offsite 0.0.46 → 0.0.48
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/README.md +1 -10
- package/apps/server/dist/main.mjs +609 -200
- package/apps/server/dist/main.mjs.map +1 -1
- package/apps/web/dist/assets/abap-08VXUWAP.js +1 -0
- package/apps/web/dist/assets/accounts-C2CqWFbM.js +513 -0
- package/apps/web/dist/assets/apex-BWPQTe0t.js +1 -0
- package/apps/web/dist/assets/audio-player-BE-oXEaT.js +1 -0
- package/apps/web/dist/assets/azcli-Bc_sGQ0U.js +1 -0
- package/apps/web/dist/assets/bat-i0X4ZdIN.js +1 -0
- package/apps/web/dist/assets/bicep-B5-_aFwp.js +2 -0
- package/apps/web/dist/assets/button-Dhs1rqob.js +1 -0
- package/apps/web/dist/assets/cameligo-DMUM7wLl.js +1 -0
- package/apps/web/dist/assets/clojure-Cm7r79vr.js +1 -0
- package/apps/web/dist/assets/codicon-DF1abBS2.ttf +0 -0
- package/apps/web/dist/assets/coffee-Ba7i2nA0.js +1 -0
- package/apps/web/dist/assets/cpp-C7h46wYY.js +1 -0
- package/apps/web/dist/assets/csharp-BKxtCVv1.js +1 -0
- package/apps/web/dist/assets/csp-bTuwJoIa.js +1 -0
- package/apps/web/dist/assets/css-DIMkf-bt.js +3 -0
- package/apps/web/dist/assets/cypher-CVaqCwHa.js +1 -0
- package/apps/web/dist/assets/dart-onAF5SnQ.js +1 -0
- package/apps/web/dist/assets/{preview-dialog-jk_R2034.js → dialog-Bnoy9Qj1.js} +12 -12
- package/apps/web/dist/assets/dist-BrDshb6Z.js +153 -0
- package/apps/web/dist/assets/dist-DUvKG9KH.js +1 -0
- package/apps/web/dist/assets/dockerfile-DZFCIeNp.js +1 -0
- package/apps/web/dist/assets/ecl-D05T4iGw.js +1 -0
- package/apps/web/dist/assets/editor-DjpsE8to.css +1 -0
- package/apps/web/dist/assets/editor-dialog-BNFI__d8.css +1 -0
- package/apps/web/dist/assets/editor-dialog-DEjErrRg.js +52 -0
- package/apps/web/dist/assets/editor.api-4LH2ijIL.js +908 -0
- package/apps/web/dist/assets/editor.worker-BmdWdrHZ.js +33 -0
- package/apps/web/dist/assets/editor.worker-D2ouzCu8.js +1 -0
- package/apps/web/dist/assets/elixir-6RTg0lbw.js +1 -0
- package/apps/web/dist/assets/flow9-C5_-GSwl.js +1 -0
- package/apps/web/dist/assets/freemarker2-6NYS9HqW.js +3 -0
- package/apps/web/dist/assets/fsharp-C8Ef5oNN.js +1 -0
- package/apps/web/dist/assets/go-C-y9NEjX.js +1 -0
- package/apps/web/dist/assets/graphql-fmXr3nnJ.js +1 -0
- package/apps/web/dist/assets/handlebars-DOeNnhTE.js +1 -0
- package/apps/web/dist/assets/hcl-CpzslTdj.js +1 -0
- package/apps/web/dist/assets/html-BwGTVsqZ.js +1 -0
- package/apps/web/dist/assets/image-viewer-DHukGxB1.js +1 -0
- package/apps/web/dist/assets/index-1yFC_FF_.js +9 -0
- package/apps/web/dist/assets/index-cgL3vsYO.css +2 -0
- package/apps/web/dist/assets/ini-sBoK_t0W.js +1 -0
- package/apps/web/dist/assets/java-BEtHBSE6.js +1 -0
- package/apps/web/dist/assets/javascript-Dj-Vzbp-.js +1 -0
- package/apps/web/dist/assets/julia-Bri6UV-V.js +1 -0
- package/apps/web/dist/assets/kotlin-BOotOW0E.js +1 -0
- package/apps/web/dist/assets/less-B9JPFI3C.js +2 -0
- package/apps/web/dist/assets/lexon-CfSJPG6W.js +1 -0
- package/apps/web/dist/assets/liquid-DDSU3ltH.js +1 -0
- package/apps/web/dist/assets/lua-CsQS60Ue.js +1 -0
- package/apps/web/dist/assets/m3-D-oSqn_W.js +1 -0
- package/apps/web/dist/assets/markdown-Cimd5fb3.js +1 -0
- package/apps/web/dist/assets/mdx-YllfWysm.js +1 -0
- package/apps/web/dist/assets/mips-CIPQ_RoX.js +1 -0
- package/apps/web/dist/assets/msdax-DauUninz.js +1 -0
- package/apps/web/dist/assets/mysql-SOo6toE5.js +1 -0
- package/apps/web/dist/assets/objective-c-FvmIjYaQ.js +1 -0
- package/apps/web/dist/assets/pascal-DrH0SRf2.js +1 -0
- package/apps/web/dist/assets/pascaligo-D-ptJ9y-.js +1 -0
- package/apps/web/dist/assets/perl-oz_6vUea.js +1 -0
- package/apps/web/dist/assets/pgsql-DTj74zXo.js +1 -0
- package/apps/web/dist/assets/php-nr791fC2.js +1 -0
- package/apps/web/dist/assets/pla-CopQ2nXW.js +1 -0
- package/apps/web/dist/assets/postiats-43DmfD33.js +1 -0
- package/apps/web/dist/assets/powerquery-D3hlyOfw.js +1 -0
- package/apps/web/dist/assets/powershell-DmHpPYUd.js +1 -0
- package/apps/web/dist/assets/preview-dialog-dl8oUCEx.js +1 -0
- package/apps/web/dist/assets/protobuf-C531GsRP.js +2 -0
- package/apps/web/dist/assets/pug-Z5eAx3Zn.js +1 -0
- package/apps/web/dist/assets/python-Bsfjs4Fs.js +1 -0
- package/apps/web/dist/assets/qsharp-DkqhCAOL.js +1 -0
- package/apps/web/dist/assets/r-BwWrilGY.js +1 -0
- package/apps/web/dist/assets/razor-3MUz77_n.js +1 -0
- package/apps/web/dist/assets/{react-99wk3OSj.js → react-DHkIXOR_.js} +1 -1
- package/apps/web/dist/assets/redis-ClamHrr6.js +1 -0
- package/apps/web/dist/assets/redshift-DT7zqm-g.js +1 -0
- package/apps/web/dist/assets/restructuredtext-BYgofb2h.js +1 -0
- package/apps/web/dist/assets/ruby-DezsRK8O.js +1 -0
- package/apps/web/dist/assets/rust-DdL9SqIa.js +1 -0
- package/apps/web/dist/assets/sb-CcwsVR0C.js +1 -0
- package/apps/web/dist/assets/scala-DHpiXF5c.js +1 -0
- package/apps/web/dist/assets/scheme-BeGwcela.js +1 -0
- package/apps/web/dist/assets/scss-gp-XZpBa.js +3 -0
- package/apps/web/dist/assets/shell-CC2rA5mh.js +1 -0
- package/apps/web/dist/assets/solidity-BEEn4gHE.js +1 -0
- package/apps/web/dist/assets/sophia-CRfGWb83.js +1 -0
- package/apps/web/dist/assets/sparql-D_Lu-MrJ.js +1 -0
- package/apps/web/dist/assets/sql-NEE52Syq.js +1 -0
- package/apps/web/dist/assets/st-DbInun42.js +1 -0
- package/apps/web/dist/assets/swift-Bxkupp3x.js +1 -0
- package/apps/web/dist/assets/systemverilog-Bz4Y3fRF.js +1 -0
- package/apps/web/dist/assets/tcl-DISqw1ZD.js +1 -0
- package/apps/web/dist/assets/tooltip-CGSEdyeY.js +1 -0
- package/apps/web/dist/assets/twig-De2hgUGE.js +1 -0
- package/apps/web/dist/assets/typescript-CfCJo9p3.js +1 -0
- package/apps/web/dist/assets/typescript-fNZ3iur6.js +1 -0
- package/apps/web/dist/assets/typespec-B8J7ngcE.js +1 -0
- package/apps/web/dist/assets/utils-KVq36YOZ.js +1 -0
- package/apps/web/dist/assets/vb-DV3o63ZY.js +1 -0
- package/apps/web/dist/assets/video-viewer-DIbmaY7P.js +1 -0
- package/apps/web/dist/assets/wgsl-DpFanUEy.js +298 -0
- package/apps/web/dist/assets/xml-S-oLW5B5.js +1 -0
- package/apps/web/dist/assets/yaml-DlmqY7sN.js +1 -0
- package/apps/web/dist/assets/zoomable-image-DzvW3FEl.js +1 -0
- package/apps/web/dist/assets/zoomable-image-HHk3X4rG.js +41 -0
- package/apps/web/dist/index.html +8 -8
- package/apps/web/dist/sw.js +1 -1
- package/package.json +2 -1
- package/apps/web/dist/assets/audio-player-BBuXiV2_.js +0 -1
- package/apps/web/dist/assets/button-BF3wjXXJ.js +0 -1
- package/apps/web/dist/assets/dist-CkDQKgh1.js +0 -153
- package/apps/web/dist/assets/dist-gQuykix2.js +0 -1
- package/apps/web/dist/assets/image-viewer-4LhMaTO_.js +0 -1
- package/apps/web/dist/assets/index-CixqWNf5.js +0 -782
- package/apps/web/dist/assets/index-D3NpzVu2.css +0 -2
- package/apps/web/dist/assets/jsx-runtime-DKj5X9O4.js +0 -1
- package/apps/web/dist/assets/tooltip-CiWlkHa6.js +0 -1
- package/apps/web/dist/assets/utils-PtDt7m8t.js +0 -1
- package/apps/web/dist/assets/video-player-Bcow2OC5.js +0 -1
- package/apps/web/dist/assets/video-player-hDoAwJRk.js +0 -1
- package/apps/web/dist/assets/video-viewer-CJ129jI3.js +0 -1
- package/apps/web/dist/assets/zoomable-image-CZlWBm_z.js +0 -41
- package/apps/web/dist/assets/zoomable-image-DyuZJmov.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, readSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, watch, writeFileSync } from "node:fs";
|
|
2
|
+
import { appendFileSync, chmodSync, closeSync, createReadStream, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, watch, writeFileSync } from "node:fs";
|
|
3
3
|
import http, { createServer } from "node:http";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { consola } from "consola";
|
|
@@ -21,6 +21,7 @@ import { DatabaseSync } from "node:sqlite";
|
|
|
21
21
|
import { drizzle } from "drizzle-orm/node-sqlite";
|
|
22
22
|
import { migrate } from "drizzle-orm/node-sqlite/migrator";
|
|
23
23
|
import { integer, snakeCase, text } from "drizzle-orm/sqlite-core";
|
|
24
|
+
import sharp from "sharp";
|
|
24
25
|
import { promisify } from "node:util";
|
|
25
26
|
import webPush from "web-push";
|
|
26
27
|
import { spawn as spawn$1 } from "@lydell/node-pty";
|
|
@@ -47,6 +48,24 @@ const mediaTypes = /* @__PURE__ */ new Map([
|
|
|
47
48
|
[".flac", "audio/flac"]
|
|
48
49
|
]);
|
|
49
50
|
//#endregion
|
|
51
|
+
//#region ../core/src/terminal.ts
|
|
52
|
+
const GLOBAL_TERMINALS = "global";
|
|
53
|
+
function encodeFrame(kind, id, data) {
|
|
54
|
+
const frame = new Uint8Array(5 + data.length);
|
|
55
|
+
frame[0] = kind;
|
|
56
|
+
new DataView(frame.buffer).setUint32(1, id);
|
|
57
|
+
frame.set(data, 5);
|
|
58
|
+
return frame;
|
|
59
|
+
}
|
|
60
|
+
function decodeFrame(frame) {
|
|
61
|
+
if (frame.length < 5) throw new Error("terminal frame is too short");
|
|
62
|
+
return {
|
|
63
|
+
kind: frame[0],
|
|
64
|
+
id: new DataView(frame.buffer, frame.byteOffset, frame.byteLength).getUint32(1),
|
|
65
|
+
data: frame.subarray(5)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
50
69
|
//#region src/constants.ts
|
|
51
70
|
const deprecatedModels = {
|
|
52
71
|
claude: [
|
|
@@ -2599,6 +2618,7 @@ const sessions$1 = snakeCase.table("sessions", {
|
|
|
2599
2618
|
status: text({ enum: [
|
|
2600
2619
|
"active",
|
|
2601
2620
|
"pinned",
|
|
2621
|
+
"snoozed",
|
|
2602
2622
|
"minimized"
|
|
2603
2623
|
] }).notNull()
|
|
2604
2624
|
});
|
|
@@ -2624,13 +2644,201 @@ const pushSubscriptions = snakeCase.table("push_subscriptions", {
|
|
|
2624
2644
|
server: text().notNull()
|
|
2625
2645
|
});
|
|
2626
2646
|
//#endregion
|
|
2647
|
+
//#region src/services/files.ts
|
|
2648
|
+
const FILE_LIMIT = 2e4;
|
|
2649
|
+
const OUTPUT_LIMIT$1 = 262144;
|
|
2650
|
+
const READ_LIMIT = 1048576;
|
|
2651
|
+
const MEDIA_LINK_LIFETIME = 36e5;
|
|
2652
|
+
const browseToken = randomBytes(32).toString("base64url");
|
|
2653
|
+
const mediaLinks = /* @__PURE__ */ new Map();
|
|
2654
|
+
const skipped = /* @__PURE__ */ new Set([
|
|
2655
|
+
"node_modules",
|
|
2656
|
+
".git",
|
|
2657
|
+
".DS_Store",
|
|
2658
|
+
"dist",
|
|
2659
|
+
"target",
|
|
2660
|
+
".next",
|
|
2661
|
+
"__pycache__",
|
|
2662
|
+
".venv"
|
|
2663
|
+
]);
|
|
2664
|
+
async function listFiles(cwd) {
|
|
2665
|
+
return (await listEntries(cwd)).files;
|
|
2666
|
+
}
|
|
2667
|
+
async function listEntries(cwd) {
|
|
2668
|
+
const files = [];
|
|
2669
|
+
const folders = [];
|
|
2670
|
+
const pending = [""];
|
|
2671
|
+
while (pending.length > 0 && files.length < FILE_LIMIT) {
|
|
2672
|
+
const dir = pending.pop() ?? "";
|
|
2673
|
+
let entries;
|
|
2674
|
+
try {
|
|
2675
|
+
entries = await readdir(path.join(cwd, dir), { withFileTypes: true });
|
|
2676
|
+
} catch {
|
|
2677
|
+
continue;
|
|
2678
|
+
}
|
|
2679
|
+
for (const entry of entries) {
|
|
2680
|
+
if (skipped.has(entry.name)) continue;
|
|
2681
|
+
const relative = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
2682
|
+
if (entry.isDirectory()) {
|
|
2683
|
+
pending.push(relative);
|
|
2684
|
+
folders.push(relative);
|
|
2685
|
+
}
|
|
2686
|
+
if (entry.isFile()) files.push(relative);
|
|
2687
|
+
if (entry.isSymbolicLink()) {
|
|
2688
|
+
if ((await stat(path.join(cwd, relative)).catch(() => null))?.isFile() === true) files.push(relative);
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
return {
|
|
2693
|
+
files: files.slice(0, FILE_LIMIT).sort(),
|
|
2694
|
+
folders: folders.sort()
|
|
2695
|
+
};
|
|
2696
|
+
}
|
|
2697
|
+
function readProjectFile(cwd, file) {
|
|
2698
|
+
const full = resolveProjectFile(cwd, file);
|
|
2699
|
+
const stats = statSync(full);
|
|
2700
|
+
if (!stats.isFile()) throw new Error(`${file} is not a file`);
|
|
2701
|
+
if (stats.size > READ_LIMIT) return {
|
|
2702
|
+
kind: "large",
|
|
2703
|
+
size: stats.size,
|
|
2704
|
+
limit: READ_LIMIT
|
|
2705
|
+
};
|
|
2706
|
+
const data = readFileSync(full);
|
|
2707
|
+
const mediaType = mediaTypes.get(path.extname(full).toLowerCase());
|
|
2708
|
+
if (mediaType?.startsWith("image/") === true) return {
|
|
2709
|
+
kind: "image",
|
|
2710
|
+
size: stats.size,
|
|
2711
|
+
mediaType,
|
|
2712
|
+
data: data.toString("base64")
|
|
2713
|
+
};
|
|
2714
|
+
if (data.includes(0)) return {
|
|
2715
|
+
kind: "binary",
|
|
2716
|
+
size: stats.size
|
|
2717
|
+
};
|
|
2718
|
+
return {
|
|
2719
|
+
kind: "text",
|
|
2720
|
+
size: stats.size,
|
|
2721
|
+
content: data.toString("utf8")
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
function writeProjectFile(cwd, file, content) {
|
|
2725
|
+
const full = resolveProjectFile(cwd, file);
|
|
2726
|
+
if (!statSync(full).isFile()) throw new Error(`${file} is not a file`);
|
|
2727
|
+
writeFileSync(full, content);
|
|
2728
|
+
}
|
|
2729
|
+
function createProjectEntry(cwd, file, folder) {
|
|
2730
|
+
const full = resolveNewProjectPath(cwd, file);
|
|
2731
|
+
if (folder) {
|
|
2732
|
+
mkdirSync(full, { recursive: true });
|
|
2733
|
+
return;
|
|
2734
|
+
}
|
|
2735
|
+
mkdirSync(path.dirname(full), { recursive: true });
|
|
2736
|
+
writeFileSync(full, "", { flag: "wx" });
|
|
2737
|
+
}
|
|
2738
|
+
function renameProjectEntry(cwd, file, to) {
|
|
2739
|
+
const source = resolveProjectEntry(cwd, file);
|
|
2740
|
+
const target = resolveNewProjectPath(cwd, to);
|
|
2741
|
+
mkdirSync(path.dirname(target), { recursive: true });
|
|
2742
|
+
renameSync(source, target);
|
|
2743
|
+
}
|
|
2744
|
+
function deleteProjectEntry(cwd, file) {
|
|
2745
|
+
rmSync(resolveProjectEntry(cwd, file), { recursive: true });
|
|
2746
|
+
}
|
|
2747
|
+
function linkProjectMedia(cwd, file) {
|
|
2748
|
+
const resolved = path.resolve(realpathSync(cwd), file);
|
|
2749
|
+
if (!existsSync(resolved)) throw new Error(`${file} does not exist`);
|
|
2750
|
+
const full = realpathSync(resolved);
|
|
2751
|
+
const mediaType = mediaTypes.get(path.extname(full).toLowerCase());
|
|
2752
|
+
if (mediaType === void 0) throw new Error(`${file} is not an image, video or audio file`);
|
|
2753
|
+
return addLink(full, mediaType);
|
|
2754
|
+
}
|
|
2755
|
+
function addLink(file, mediaType) {
|
|
2756
|
+
for (const [token, link] of mediaLinks) if (link.expiresAt <= Date.now()) mediaLinks.delete(token);
|
|
2757
|
+
const token = randomBytes(32).toString("base64url");
|
|
2758
|
+
mediaLinks.set(token, {
|
|
2759
|
+
file,
|
|
2760
|
+
mediaType,
|
|
2761
|
+
expiresAt: Date.now() + MEDIA_LINK_LIFETIME
|
|
2762
|
+
});
|
|
2763
|
+
return token;
|
|
2764
|
+
}
|
|
2765
|
+
function findMediaLink(token) {
|
|
2766
|
+
if (token === null) return null;
|
|
2767
|
+
const link = mediaLinks.get(token);
|
|
2768
|
+
if (link === void 0 || link.expiresAt <= Date.now()) return null;
|
|
2769
|
+
return link;
|
|
2770
|
+
}
|
|
2771
|
+
function saveUpload(name, data) {
|
|
2772
|
+
const dir = path.join(home, "uploads", randomUUID());
|
|
2773
|
+
mkdirSync(dir, { recursive: true });
|
|
2774
|
+
const file = path.join(dir, path.basename(name));
|
|
2775
|
+
writeFileSync(file, data);
|
|
2776
|
+
return file;
|
|
2777
|
+
}
|
|
2778
|
+
async function taskOutput(sessionId, file) {
|
|
2779
|
+
const tasks = path.dirname(file);
|
|
2780
|
+
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");
|
|
2781
|
+
let stats;
|
|
2782
|
+
try {
|
|
2783
|
+
stats = await lstat(file);
|
|
2784
|
+
} catch (error) {
|
|
2785
|
+
if (hasCode(error, "ENOENT")) return null;
|
|
2786
|
+
throw error;
|
|
2787
|
+
}
|
|
2788
|
+
if (!stats.isFile()) throw new Error("Background task output is not a regular file");
|
|
2789
|
+
const start = Math.max(0, stats.size - OUTPUT_LIMIT$1);
|
|
2790
|
+
const handle = await open(file, "r");
|
|
2791
|
+
try {
|
|
2792
|
+
const buffer = Buffer.alloc(stats.size - start);
|
|
2793
|
+
const read = await handle.read(buffer, 0, buffer.length, start);
|
|
2794
|
+
return {
|
|
2795
|
+
text: buffer.subarray(0, read.bytesRead).toString("utf8"),
|
|
2796
|
+
truncated: start > 0
|
|
2797
|
+
};
|
|
2798
|
+
} finally {
|
|
2799
|
+
await handle.close();
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
function resolveProjectFile(cwd, file) {
|
|
2803
|
+
const root = realpathSync(cwd);
|
|
2804
|
+
const resolved = path.resolve(root, file);
|
|
2805
|
+
if (!existsSync(resolved)) throw new Error(`${file} does not exist`);
|
|
2806
|
+
const full = realpathSync(resolved);
|
|
2807
|
+
checkInProject(root, full, file);
|
|
2808
|
+
return full;
|
|
2809
|
+
}
|
|
2810
|
+
function resolveProjectEntry(cwd, file) {
|
|
2811
|
+
const root = realpathSync(cwd);
|
|
2812
|
+
const resolved = path.resolve(root, file);
|
|
2813
|
+
if (!existsSync(path.dirname(resolved))) throw new Error(`${file} does not exist`);
|
|
2814
|
+
const full = path.join(realpathSync(path.dirname(resolved)), path.basename(resolved));
|
|
2815
|
+
if (full === root) throw new Error("The project folder itself cannot be changed");
|
|
2816
|
+
checkInProject(root, full, file);
|
|
2817
|
+
lstatSync(full);
|
|
2818
|
+
return full;
|
|
2819
|
+
}
|
|
2820
|
+
function resolveNewProjectPath(cwd, file) {
|
|
2821
|
+
const root = realpathSync(cwd);
|
|
2822
|
+
const resolved = path.resolve(root, file);
|
|
2823
|
+
if (existsSync(resolved)) throw new Error(`${file} already exists`);
|
|
2824
|
+
let existing = path.dirname(resolved);
|
|
2825
|
+
while (!existsSync(existing)) existing = path.dirname(existing);
|
|
2826
|
+
const full = path.join(realpathSync(existing), path.relative(existing, resolved));
|
|
2827
|
+
checkInProject(root, full, file);
|
|
2828
|
+
return full;
|
|
2829
|
+
}
|
|
2830
|
+
function checkInProject(root, full, file) {
|
|
2831
|
+
const relative = path.relative(root, full);
|
|
2832
|
+
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`${file} is outside the project`);
|
|
2833
|
+
}
|
|
2834
|
+
//#endregion
|
|
2627
2835
|
//#region src/services/git.ts
|
|
2628
2836
|
const DIFF_LIMIT = 8e4;
|
|
2629
2837
|
const UNTRACKED_LIMIT = 100;
|
|
2630
2838
|
const NEW_FILE_LIMIT = 262144;
|
|
2631
2839
|
const REPO_DEPTH = 3;
|
|
2632
2840
|
const FETCH_TIMEOUT = 6e4;
|
|
2633
|
-
const run$
|
|
2841
|
+
const run$2 = promisify(execFile);
|
|
2634
2842
|
const PULL_REQUEST_INSTRUCTIONS = `You write GitHub pull requests as Markdown.
|
|
2635
2843
|
|
|
2636
2844
|
## TITLE RULES
|
|
@@ -2666,7 +2874,7 @@ async function gitRepos(cwd) {
|
|
|
2666
2874
|
})));
|
|
2667
2875
|
}
|
|
2668
2876
|
async function commit(cwd, message, andPush) {
|
|
2669
|
-
const text = message.trim() === "" ? await commitMessage(cwd) : message;
|
|
2877
|
+
const text = message.trim() === "" ? await commitMessage(cwd, false) : message;
|
|
2670
2878
|
await git(cwd, ["add", "--all"]);
|
|
2671
2879
|
await git(cwd, [
|
|
2672
2880
|
"commit",
|
|
@@ -2717,7 +2925,7 @@ async function sync(cwd) {
|
|
|
2717
2925
|
async function fetchProject(cwd) {
|
|
2718
2926
|
await Promise.all((await projectRepos(cwd)).map(async (repo) => {
|
|
2719
2927
|
try {
|
|
2720
|
-
await run$
|
|
2928
|
+
await run$2("git", ["fetch"], {
|
|
2721
2929
|
cwd: path.join(cwd, repo),
|
|
2722
2930
|
env: {
|
|
2723
2931
|
...process.env,
|
|
@@ -2748,7 +2956,7 @@ async function switchBranch(cwd, branch, create) {
|
|
|
2748
2956
|
async function pullRequestBranches(cwd) {
|
|
2749
2957
|
const head = (await gitStatus(cwd)).branch;
|
|
2750
2958
|
if (head === null) throw new Error("Not on a branch");
|
|
2751
|
-
const base = (await run$
|
|
2959
|
+
const base = (await run$2("gh", [
|
|
2752
2960
|
"repo",
|
|
2753
2961
|
"view",
|
|
2754
2962
|
"--json",
|
|
@@ -2773,7 +2981,7 @@ async function preparePullRequest(cwd, base, commitChanges) {
|
|
|
2773
2981
|
if (commitChanges) await commit(cwd, "", true);
|
|
2774
2982
|
const status = await gitStatus(cwd);
|
|
2775
2983
|
if (status.upstream === null || status.ahead > 0) await push$1(cwd);
|
|
2776
|
-
const open = (await run$
|
|
2984
|
+
const open = (await run$2("gh", [
|
|
2777
2985
|
"pr",
|
|
2778
2986
|
"list",
|
|
2779
2987
|
"--head",
|
|
@@ -2820,7 +3028,7 @@ async function preparePullRequest(cwd, base, commitChanges) {
|
|
|
2820
3028
|
};
|
|
2821
3029
|
}
|
|
2822
3030
|
async function createPullRequest(cwd, request) {
|
|
2823
|
-
const url = (await run$
|
|
3031
|
+
const url = (await run$2("gh", [
|
|
2824
3032
|
"pr",
|
|
2825
3033
|
"create",
|
|
2826
3034
|
"--title",
|
|
@@ -2837,8 +3045,13 @@ async function createPullRequest(cwd, request) {
|
|
|
2837
3045
|
if (url === void 0) throw new Error("gh did not return a pull request URL");
|
|
2838
3046
|
return { url };
|
|
2839
3047
|
}
|
|
2840
|
-
async function commitMessage(cwd) {
|
|
2841
|
-
const diff = (await
|
|
3048
|
+
async function commitMessage(cwd, staged) {
|
|
3049
|
+
const diff = (staged ? (await git(cwd, [
|
|
3050
|
+
"diff",
|
|
3051
|
+
"--cached",
|
|
3052
|
+
"--no-color",
|
|
3053
|
+
"--no-ext-diff"
|
|
3054
|
+
])).stdout : await repoDiff(cwd, "")).slice(0, DIFF_LIMIT);
|
|
2842
3055
|
if (diff === "") throw new Error("There is nothing to commit");
|
|
2843
3056
|
return generateText(cwd, "You write concise git commit messages.", `Write a git commit message for this diff. Reply with only the message: a short summary line, optionally followed by a blank line and a brief body.\n\n${diff}`);
|
|
2844
3057
|
}
|
|
@@ -2876,7 +3089,7 @@ async function projectDiff(cwd) {
|
|
|
2876
3089
|
return (await Promise.all(repos.map((repo) => repoDiff(path.join(cwd, repo), repo === "" ? "" : `${repo}/`)))).join("");
|
|
2877
3090
|
}
|
|
2878
3091
|
function git(cwd, args) {
|
|
2879
|
-
return run$
|
|
3092
|
+
return run$2("git", args, {
|
|
2880
3093
|
cwd,
|
|
2881
3094
|
maxBuffer: 67108864
|
|
2882
3095
|
});
|
|
@@ -3001,13 +3214,27 @@ function newFilePatch(cwd, file, prefix) {
|
|
|
3001
3214
|
if (lines.at(-1) === "") lines.pop();
|
|
3002
3215
|
return `${header}@@ -0,0 +1,${lines.length} @@\n${lines.map((line) => `+${line}\n`).join("")}`;
|
|
3003
3216
|
}
|
|
3217
|
+
//#endregion
|
|
3218
|
+
//#region src/services/logos.ts
|
|
3219
|
+
const LOGO_LIMIT = 524288;
|
|
3220
|
+
const LOGO_SIZE = 256;
|
|
3221
|
+
const RESIZED_TYPES = [
|
|
3222
|
+
"image/png",
|
|
3223
|
+
"image/jpeg",
|
|
3224
|
+
"image/webp"
|
|
3225
|
+
];
|
|
3226
|
+
const KEPT_TYPES = [
|
|
3227
|
+
"image/svg+xml",
|
|
3228
|
+
"image/x-icon",
|
|
3229
|
+
"image/vnd.microsoft.icon"
|
|
3230
|
+
];
|
|
3231
|
+
const PICKABLE_FILE = /\.(png|jpe?g|webp|svg)$/i;
|
|
3004
3232
|
const LOGO_FILES = /* @__PURE__ */ new Map([
|
|
3005
3233
|
[".svg", "image/svg+xml"],
|
|
3006
3234
|
[".png", "image/png"],
|
|
3007
3235
|
[".webp", "image/webp"],
|
|
3008
3236
|
[".jpg", "image/jpeg"],
|
|
3009
3237
|
[".jpeg", "image/jpeg"],
|
|
3010
|
-
[".gif", "image/gif"],
|
|
3011
3238
|
[".ico", "image/x-icon"]
|
|
3012
3239
|
]);
|
|
3013
3240
|
const PREFERRED = [
|
|
@@ -3030,11 +3257,42 @@ async function findLogo(folder) {
|
|
|
3030
3257
|
for (const { file } of candidates) {
|
|
3031
3258
|
const full = path.join(folder, file);
|
|
3032
3259
|
if (!existsSync(full)) continue;
|
|
3033
|
-
const
|
|
3034
|
-
|
|
3260
|
+
const type = LOGO_FILES.get(path.extname(file).toLowerCase());
|
|
3261
|
+
const data = await readFile(full);
|
|
3262
|
+
try {
|
|
3263
|
+
return await prepareLogo(`data:${type};base64,${data.toString("base64")}`);
|
|
3264
|
+
} catch {
|
|
3265
|
+
continue;
|
|
3266
|
+
}
|
|
3035
3267
|
}
|
|
3036
3268
|
return null;
|
|
3037
3269
|
}
|
|
3270
|
+
async function logoFiles(folder) {
|
|
3271
|
+
const repos = await projectRepos(folder);
|
|
3272
|
+
if (repos.length === 0) return (await listFiles(folder)).filter((file) => PICKABLE_FILE.test(file));
|
|
3273
|
+
const listed = await Promise.all(repos.map(async (repo) => {
|
|
3274
|
+
return (await git(path.join(folder, repo), [
|
|
3275
|
+
"ls-files",
|
|
3276
|
+
"-z",
|
|
3277
|
+
"--cached",
|
|
3278
|
+
"--others",
|
|
3279
|
+
"--exclude-standard"
|
|
3280
|
+
])).stdout.split("\0").map((file) => repo === "" ? file : `${repo}/${file}`);
|
|
3281
|
+
}));
|
|
3282
|
+
return [...new Set(listed.flat())].filter((file) => PICKABLE_FILE.test(file) && existsSync(path.join(folder, file)));
|
|
3283
|
+
}
|
|
3284
|
+
async function prepareLogo(logo) {
|
|
3285
|
+
const header = /^data:([^;,]+);base64,/.exec(logo);
|
|
3286
|
+
if (header !== null && RESIZED_TYPES.includes(header[1])) return `data:image/webp;base64,${(await sharp(Buffer.from(logo.slice(header[0].length), "base64")).resize(LOGO_SIZE, LOGO_SIZE, {
|
|
3287
|
+
fit: "inside",
|
|
3288
|
+
withoutEnlargement: true
|
|
3289
|
+
}).webp({ quality: 90 }).toBuffer()).toString("base64")}`;
|
|
3290
|
+
if (header !== null && KEPT_TYPES.includes(header[1])) {
|
|
3291
|
+
if (logo.length > LOGO_LIMIT) throw new Error("Use an SVG or ICO image smaller than 384 KB");
|
|
3292
|
+
return logo;
|
|
3293
|
+
}
|
|
3294
|
+
throw new Error("Use a PNG, JPEG, WebP, SVG or ICO image");
|
|
3295
|
+
}
|
|
3038
3296
|
function logoRank(file) {
|
|
3039
3297
|
const preferred = PREFERRED.indexOf(path.basename(file).toLowerCase());
|
|
3040
3298
|
if (preferred !== -1) return preferred;
|
|
@@ -3046,15 +3304,6 @@ function logoRank(file) {
|
|
|
3046
3304
|
}
|
|
3047
3305
|
//#endregion
|
|
3048
3306
|
//#region src/services/projects.ts
|
|
3049
|
-
const LOGO_TYPES = [
|
|
3050
|
-
"image/png",
|
|
3051
|
-
"image/jpeg",
|
|
3052
|
-
"image/gif",
|
|
3053
|
-
"image/webp",
|
|
3054
|
-
"image/svg+xml",
|
|
3055
|
-
"image/x-icon",
|
|
3056
|
-
"image/vnd.microsoft.icon"
|
|
3057
|
-
];
|
|
3058
3307
|
function projects$1() {
|
|
3059
3308
|
return db.select({
|
|
3060
3309
|
id: projects$2.id,
|
|
@@ -3084,16 +3333,13 @@ async function addProject(input) {
|
|
|
3084
3333
|
if (project === void 0) throw new Error(`Could not add ${folder}`);
|
|
3085
3334
|
return project;
|
|
3086
3335
|
}
|
|
3087
|
-
function updateProject(id, patch) {
|
|
3336
|
+
async function updateProject(id, patch) {
|
|
3088
3337
|
if (patch.name !== void 0) {
|
|
3089
3338
|
const name = patch.name.trim();
|
|
3090
3339
|
if (name === "") throw new Error("Project name cannot be empty");
|
|
3091
3340
|
db.update(projects$2).set({ name }).where(eq(projects$2.id, id)).run();
|
|
3092
3341
|
}
|
|
3093
|
-
if (patch.logo !== void 0) {
|
|
3094
|
-
if (patch.logo !== null) checkLogo(patch.logo);
|
|
3095
|
-
db.update(projects$2).set({ logo: patch.logo }).where(eq(projects$2.id, id)).run();
|
|
3096
|
-
}
|
|
3342
|
+
if (patch.logo !== void 0) db.update(projects$2).set({ logo: patch.logo === null ? null : await prepareLogo(patch.logo) }).where(eq(projects$2.id, id)).run();
|
|
3097
3343
|
publish$1();
|
|
3098
3344
|
return null;
|
|
3099
3345
|
}
|
|
@@ -3131,11 +3377,6 @@ function publish$1() {
|
|
|
3131
3377
|
projects: projects$1()
|
|
3132
3378
|
});
|
|
3133
3379
|
}
|
|
3134
|
-
function checkLogo(logo) {
|
|
3135
|
-
const type = /^data:([^;,]+);base64,/.exec(logo)?.[1];
|
|
3136
|
-
if (type === void 0 || !LOGO_TYPES.includes(type)) throw new Error("Use a PNG, JPEG, GIF, WebP, SVG or ICO image");
|
|
3137
|
-
if (logo.length > 524288) throw new Error("Use an image smaller than 384 KB");
|
|
3138
|
-
}
|
|
3139
3380
|
function expandHome(input) {
|
|
3140
3381
|
if (input === "~") return os.homedir();
|
|
3141
3382
|
if (input.startsWith("~/")) return path.join(os.homedir(), input.slice(2));
|
|
@@ -3224,21 +3465,6 @@ async function find(provider, paths) {
|
|
|
3224
3465
|
});
|
|
3225
3466
|
}
|
|
3226
3467
|
}
|
|
3227
|
-
function encodeFrame(kind, id, data) {
|
|
3228
|
-
const frame = new Uint8Array(5 + data.length);
|
|
3229
|
-
frame[0] = kind;
|
|
3230
|
-
new DataView(frame.buffer).setUint32(1, id);
|
|
3231
|
-
frame.set(data, 5);
|
|
3232
|
-
return frame;
|
|
3233
|
-
}
|
|
3234
|
-
function decodeFrame(frame) {
|
|
3235
|
-
if (frame.length < 5) throw new Error("terminal frame is too short");
|
|
3236
|
-
return {
|
|
3237
|
-
kind: frame[0],
|
|
3238
|
-
id: new DataView(frame.buffer, frame.byteOffset, frame.byteLength).getUint32(1),
|
|
3239
|
-
data: frame.subarray(5)
|
|
3240
|
-
};
|
|
3241
|
-
}
|
|
3242
3468
|
//#endregion
|
|
3243
3469
|
//#region src/services/push.ts
|
|
3244
3470
|
const TITLE_LIMIT = 100;
|
|
@@ -3704,7 +3930,7 @@ function writeScript(target, source) {
|
|
|
3704
3930
|
}
|
|
3705
3931
|
//#endregion
|
|
3706
3932
|
//#region src/services/terminal/process.ts
|
|
3707
|
-
const run = promisify(execFile);
|
|
3933
|
+
const run$1 = promisify(execFile);
|
|
3708
3934
|
const iterm2Version = "3.6.8";
|
|
3709
3935
|
const ghosttyVersion = "1.3.1";
|
|
3710
3936
|
const shellNames = process.platform === "win32" ? [
|
|
@@ -3765,7 +3991,7 @@ function startShell(cwd) {
|
|
|
3765
3991
|
async function currentDirectory(pid, reported) {
|
|
3766
3992
|
if (process.platform === "linux") return readlink(`/proc/${pid}/cwd`);
|
|
3767
3993
|
if (process.platform === "darwin") {
|
|
3768
|
-
const { stdout } = await run("lsof", [
|
|
3994
|
+
const { stdout } = await run$1("lsof", [
|
|
3769
3995
|
"-a",
|
|
3770
3996
|
"-d",
|
|
3771
3997
|
"cwd",
|
|
@@ -3785,7 +4011,7 @@ async function currentDirectory(pid, reported) {
|
|
|
3785
4011
|
}
|
|
3786
4012
|
async function busyProgram(pid) {
|
|
3787
4013
|
if (process.platform === "win32") {
|
|
3788
|
-
const { stdout } = await run("powershell.exe", [
|
|
4014
|
+
const { stdout } = await run$1("powershell.exe", [
|
|
3789
4015
|
"-NoProfile",
|
|
3790
4016
|
"-Command",
|
|
3791
4017
|
`Get-CimInstance Win32_Process -Filter "ParentProcessId=${pid}" | Select-Object -ExpandProperty Name`
|
|
@@ -3808,7 +4034,7 @@ async function foregroundGroup(pid) {
|
|
|
3808
4034
|
const text = await readFile(`/proc/${pid}/stat`, "utf8");
|
|
3809
4035
|
return Number(text.slice(text.lastIndexOf(")") + 2).split(" ")[5]);
|
|
3810
4036
|
}
|
|
3811
|
-
const { stdout } = await run("ps", [
|
|
4037
|
+
const { stdout } = await run$1("ps", [
|
|
3812
4038
|
"-o",
|
|
3813
4039
|
"tpgid=",
|
|
3814
4040
|
"-p",
|
|
@@ -3818,7 +4044,7 @@ async function foregroundGroup(pid) {
|
|
|
3818
4044
|
}
|
|
3819
4045
|
async function programName(pid) {
|
|
3820
4046
|
if (process.platform === "linux") return (await readFile(`/proc/${pid}/comm`, "utf8")).trimEnd();
|
|
3821
|
-
const { stdout } = await run("ps", [
|
|
4047
|
+
const { stdout } = await run$1("ps", [
|
|
3822
4048
|
"-o",
|
|
3823
4049
|
"comm=",
|
|
3824
4050
|
"-p",
|
|
@@ -3887,7 +4113,7 @@ function executable$1(name) {
|
|
|
3887
4113
|
//#endregion
|
|
3888
4114
|
//#region src/services/terminal/terminal.ts
|
|
3889
4115
|
const HISTORY_LIMIT = 2097152;
|
|
3890
|
-
const OUTPUT_LIMIT
|
|
4116
|
+
const OUTPUT_LIMIT = 1048576;
|
|
3891
4117
|
const STALL_LIMIT = 1e4;
|
|
3892
4118
|
const SILENCE_LIMIT$1 = 9e4;
|
|
3893
4119
|
const KILL_DELAY = 3e3;
|
|
@@ -4116,7 +4342,7 @@ function output(id, terminal, data) {
|
|
|
4116
4342
|
}
|
|
4117
4343
|
for (const connection of connections.values()) forward(connection, id, false, data);
|
|
4118
4344
|
scan(id, terminal, data);
|
|
4119
|
-
if ([...connections.values()].some((connection) => connection.client.socket.bufferedAmount > OUTPUT_LIMIT
|
|
4345
|
+
if ([...connections.values()].some((connection) => connection.client.socket.bufferedAmount > OUTPUT_LIMIT)) {
|
|
4120
4346
|
terminal.pty.pause();
|
|
4121
4347
|
paused.add(terminal.pty);
|
|
4122
4348
|
draining ??= drain();
|
|
@@ -4128,7 +4354,7 @@ function drain() {
|
|
|
4128
4354
|
let full = false;
|
|
4129
4355
|
for (const connection of connections.values()) {
|
|
4130
4356
|
const queued = connection.client.socket.bufferedAmount;
|
|
4131
|
-
if (queued <= OUTPUT_LIMIT
|
|
4357
|
+
if (queued <= OUTPUT_LIMIT) continue;
|
|
4132
4358
|
const now = Date.now();
|
|
4133
4359
|
const last = progress.get(connection) ?? {
|
|
4134
4360
|
queued: Infinity,
|
|
@@ -4204,6 +4430,10 @@ function publish() {
|
|
|
4204
4430
|
layout: layout()
|
|
4205
4431
|
};
|
|
4206
4432
|
for (const connection of connections.values()) send$1(connection, event);
|
|
4433
|
+
broadcast({
|
|
4434
|
+
type: "terminals",
|
|
4435
|
+
count: terminalCount(GLOBAL_TERMINALS)
|
|
4436
|
+
});
|
|
4207
4437
|
}
|
|
4208
4438
|
function forward(connection, id, replay, data) {
|
|
4209
4439
|
connection.client.socket.send(encodeFrame(replay ? 3 : 2, id, data));
|
|
@@ -4295,7 +4525,7 @@ function withStatus(items) {
|
|
|
4295
4525
|
return {
|
|
4296
4526
|
...item,
|
|
4297
4527
|
updatedAt,
|
|
4298
|
-
status: record === void 0 || record.status === "active" && Date.now() - updatedAt > day ? "inactive" : record.status
|
|
4528
|
+
status: record === void 0 || (record.status === "active" || record.status === "snoozed") && Date.now() - updatedAt > day ? "inactive" : record.status
|
|
4299
4529
|
};
|
|
4300
4530
|
});
|
|
4301
4531
|
}
|
|
@@ -4325,7 +4555,7 @@ function recordInteraction(id) {
|
|
|
4325
4555
|
target: sessions$1.sessionId,
|
|
4326
4556
|
set: {
|
|
4327
4557
|
interactedAt: Date.now(),
|
|
4328
|
-
status: sql`CASE ${sessions$1.status} WHEN 'pinned' THEN 'pinned' WHEN 'minimized' THEN 'minimized' ELSE 'active' END`
|
|
4558
|
+
status: sql`CASE ${sessions$1.status} WHEN 'pinned' THEN 'pinned' WHEN 'snoozed' THEN 'snoozed' WHEN 'minimized' THEN 'minimized' ELSE 'active' END`
|
|
4329
4559
|
}
|
|
4330
4560
|
}).run();
|
|
4331
4561
|
}
|
|
@@ -4454,7 +4684,7 @@ async function updateSession(id, patch) {
|
|
|
4454
4684
|
const session = all.find((item) => item.id === id);
|
|
4455
4685
|
if (session !== void 0 && all.every((item) => item.projectId !== session.projectId || item.status === "inactive") && [...runs.values()].every((other) => other.sessionId === id || other.cwd !== projectPath(session.projectId) || all.some((item) => item.id === other.sessionId))) closeProjectTabs(session.projectId);
|
|
4456
4686
|
} else if (patch.status !== void 0) {
|
|
4457
|
-
const set = patch.status === "active" ? {
|
|
4687
|
+
const set = patch.status === "active" || patch.status === "snoozed" ? {
|
|
4458
4688
|
status: patch.status,
|
|
4459
4689
|
interactedAt: Date.now()
|
|
4460
4690
|
} : { status: patch.status };
|
|
@@ -5583,7 +5813,7 @@ async function image(sessionId, uuid, index) {
|
|
|
5583
5813
|
data: block.data
|
|
5584
5814
|
};
|
|
5585
5815
|
}
|
|
5586
|
-
async function history(sessionId, cwd) {
|
|
5816
|
+
async function history$1(sessionId, cwd) {
|
|
5587
5817
|
const found = runs.get(sessionId);
|
|
5588
5818
|
const run = found?.provider === "claude" ? found : void 0;
|
|
5589
5819
|
const [disk, session] = await Promise.all([getSessionMessages(sessionId, { dir: cwd }), loadSession(sessionId)]);
|
|
@@ -6014,142 +6244,6 @@ var Run = class {
|
|
|
6014
6244
|
}
|
|
6015
6245
|
};
|
|
6016
6246
|
//#endregion
|
|
6017
|
-
//#region src/services/files.ts
|
|
6018
|
-
const FILE_LIMIT = 2e4;
|
|
6019
|
-
const OUTPUT_LIMIT = 262144;
|
|
6020
|
-
const READ_LIMIT = 1048576;
|
|
6021
|
-
const MEDIA_LINK_LIFETIME = 36e5;
|
|
6022
|
-
const browseToken = randomBytes(32).toString("base64url");
|
|
6023
|
-
const mediaLinks = /* @__PURE__ */ new Map();
|
|
6024
|
-
const skipped = /* @__PURE__ */ new Set([
|
|
6025
|
-
"node_modules",
|
|
6026
|
-
".git",
|
|
6027
|
-
".DS_Store",
|
|
6028
|
-
"dist",
|
|
6029
|
-
"target",
|
|
6030
|
-
".next",
|
|
6031
|
-
"__pycache__",
|
|
6032
|
-
".venv"
|
|
6033
|
-
]);
|
|
6034
|
-
async function listFiles(cwd) {
|
|
6035
|
-
const files = [];
|
|
6036
|
-
const pending = [""];
|
|
6037
|
-
while (pending.length > 0 && files.length < FILE_LIMIT) {
|
|
6038
|
-
const dir = pending.pop() ?? "";
|
|
6039
|
-
let entries;
|
|
6040
|
-
try {
|
|
6041
|
-
entries = await readdir(path.join(cwd, dir), { withFileTypes: true });
|
|
6042
|
-
} catch {
|
|
6043
|
-
continue;
|
|
6044
|
-
}
|
|
6045
|
-
for (const entry of entries) {
|
|
6046
|
-
if (skipped.has(entry.name)) continue;
|
|
6047
|
-
const relative = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
6048
|
-
if (entry.isDirectory()) pending.push(relative);
|
|
6049
|
-
if (entry.isFile()) files.push(relative);
|
|
6050
|
-
if (entry.isSymbolicLink()) {
|
|
6051
|
-
if ((await stat(path.join(cwd, relative)).catch(() => null))?.isFile() === true) files.push(relative);
|
|
6052
|
-
}
|
|
6053
|
-
}
|
|
6054
|
-
}
|
|
6055
|
-
return files.slice(0, FILE_LIMIT).sort();
|
|
6056
|
-
}
|
|
6057
|
-
function readProjectFile(cwd, file) {
|
|
6058
|
-
const full = resolveProjectFile(cwd, file);
|
|
6059
|
-
const stats = statSync(full);
|
|
6060
|
-
if (!stats.isFile()) throw new Error(`${file} is not a file`);
|
|
6061
|
-
if (stats.size > READ_LIMIT) return {
|
|
6062
|
-
kind: "large",
|
|
6063
|
-
size: stats.size,
|
|
6064
|
-
limit: READ_LIMIT
|
|
6065
|
-
};
|
|
6066
|
-
const data = readFileSync(full);
|
|
6067
|
-
const mediaType = mediaTypes.get(path.extname(full).toLowerCase());
|
|
6068
|
-
if (mediaType?.startsWith("image/") === true) return {
|
|
6069
|
-
kind: "image",
|
|
6070
|
-
size: stats.size,
|
|
6071
|
-
mediaType,
|
|
6072
|
-
data: data.toString("base64")
|
|
6073
|
-
};
|
|
6074
|
-
if (data.includes(0)) return {
|
|
6075
|
-
kind: "binary",
|
|
6076
|
-
size: stats.size
|
|
6077
|
-
};
|
|
6078
|
-
return {
|
|
6079
|
-
kind: "text",
|
|
6080
|
-
size: stats.size,
|
|
6081
|
-
content: data.toString("utf8")
|
|
6082
|
-
};
|
|
6083
|
-
}
|
|
6084
|
-
function linkProjectMedia(cwd, file) {
|
|
6085
|
-
const resolved = path.resolve(realpathSync(cwd), file);
|
|
6086
|
-
if (!existsSync(resolved)) throw new Error(`${file} does not exist`);
|
|
6087
|
-
const full = realpathSync(resolved);
|
|
6088
|
-
const mediaType = mediaTypes.get(path.extname(full).toLowerCase());
|
|
6089
|
-
if (mediaType === void 0) throw new Error(`${file} is not an image, video or audio file`);
|
|
6090
|
-
return addLink(full, mediaType, null);
|
|
6091
|
-
}
|
|
6092
|
-
function linkProjectDownload(cwd, file) {
|
|
6093
|
-
return addLink(resolveProjectFile(cwd, file), "application/octet-stream", path.basename(file));
|
|
6094
|
-
}
|
|
6095
|
-
function addLink(file, mediaType, download) {
|
|
6096
|
-
for (const [token, link] of mediaLinks) if (link.expiresAt <= Date.now()) mediaLinks.delete(token);
|
|
6097
|
-
const token = randomBytes(32).toString("base64url");
|
|
6098
|
-
mediaLinks.set(token, {
|
|
6099
|
-
file,
|
|
6100
|
-
mediaType,
|
|
6101
|
-
download,
|
|
6102
|
-
expiresAt: Date.now() + MEDIA_LINK_LIFETIME
|
|
6103
|
-
});
|
|
6104
|
-
return token;
|
|
6105
|
-
}
|
|
6106
|
-
function findMediaLink(token) {
|
|
6107
|
-
if (token === null) return null;
|
|
6108
|
-
const link = mediaLinks.get(token);
|
|
6109
|
-
if (link === void 0 || link.expiresAt <= Date.now()) return null;
|
|
6110
|
-
return link;
|
|
6111
|
-
}
|
|
6112
|
-
function saveUpload(name, data) {
|
|
6113
|
-
const dir = path.join(home, "uploads", randomUUID());
|
|
6114
|
-
mkdirSync(dir, { recursive: true });
|
|
6115
|
-
const file = path.join(dir, path.basename(name));
|
|
6116
|
-
writeFileSync(file, data);
|
|
6117
|
-
return file;
|
|
6118
|
-
}
|
|
6119
|
-
async function taskOutput(sessionId, file) {
|
|
6120
|
-
const tasks = path.dirname(file);
|
|
6121
|
-
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");
|
|
6122
|
-
let stats;
|
|
6123
|
-
try {
|
|
6124
|
-
stats = await lstat(file);
|
|
6125
|
-
} catch (error) {
|
|
6126
|
-
if (hasCode(error, "ENOENT")) return null;
|
|
6127
|
-
throw error;
|
|
6128
|
-
}
|
|
6129
|
-
if (!stats.isFile()) throw new Error("Background task output is not a regular file");
|
|
6130
|
-
const start = Math.max(0, stats.size - OUTPUT_LIMIT);
|
|
6131
|
-
const handle = await open(file, "r");
|
|
6132
|
-
try {
|
|
6133
|
-
const buffer = Buffer.alloc(stats.size - start);
|
|
6134
|
-
const read = await handle.read(buffer, 0, buffer.length, start);
|
|
6135
|
-
return {
|
|
6136
|
-
text: buffer.subarray(0, read.bytesRead).toString("utf8"),
|
|
6137
|
-
truncated: start > 0
|
|
6138
|
-
};
|
|
6139
|
-
} finally {
|
|
6140
|
-
await handle.close();
|
|
6141
|
-
}
|
|
6142
|
-
}
|
|
6143
|
-
function resolveProjectFile(cwd, file) {
|
|
6144
|
-
const root = realpathSync(cwd);
|
|
6145
|
-
const resolved = path.resolve(root, file);
|
|
6146
|
-
if (!existsSync(resolved)) throw new Error(`${file} does not exist`);
|
|
6147
|
-
const full = realpathSync(resolved);
|
|
6148
|
-
const relative = path.relative(root, full);
|
|
6149
|
-
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`${file} is outside the project`);
|
|
6150
|
-
return full;
|
|
6151
|
-
}
|
|
6152
|
-
//#endregion
|
|
6153
6247
|
//#region src/services/proxy.ts
|
|
6154
6248
|
const proxies = /* @__PURE__ */ new Map();
|
|
6155
6249
|
const pageScriptPath = "/__offsite/browser.js";
|
|
@@ -6293,7 +6387,7 @@ function stopAll() {
|
|
|
6293
6387
|
}
|
|
6294
6388
|
function sessionHistory(sessionId, cwd) {
|
|
6295
6389
|
switch (providerOf(sessionId)) {
|
|
6296
|
-
case "claude": return history(sessionId, cwd);
|
|
6390
|
+
case "claude": return history$1(sessionId, cwd);
|
|
6297
6391
|
case "codex": return codexHistory(sessionId);
|
|
6298
6392
|
case "opencode": return openCodeHistory(sessionId, cwd);
|
|
6299
6393
|
}
|
|
@@ -6345,7 +6439,7 @@ async function rewindNow(request) {
|
|
|
6345
6439
|
resumeAt: disk[index - 1].uuid,
|
|
6346
6440
|
settings: request.settings
|
|
6347
6441
|
});
|
|
6348
|
-
publish$2(sessionId, await history(sessionId, cwd));
|
|
6442
|
+
publish$2(sessionId, await history$1(sessionId, cwd));
|
|
6349
6443
|
return result;
|
|
6350
6444
|
}
|
|
6351
6445
|
async function open$1(client, sessionId, projectId) {
|
|
@@ -6438,6 +6532,294 @@ async function changedElsewhere(run, cwd) {
|
|
|
6438
6532
|
return last !== void 0 && !run.messages.some((item) => item.uuid === last);
|
|
6439
6533
|
}
|
|
6440
6534
|
//#endregion
|
|
6535
|
+
//#region src/services/source-control.ts
|
|
6536
|
+
const SHOW_LIMIT = 1048576;
|
|
6537
|
+
const NETWORK_TIMEOUT = 12e4;
|
|
6538
|
+
const run = promisify(execFile);
|
|
6539
|
+
async function sourceControl(cwd) {
|
|
6540
|
+
return Promise.all((await projectRepos(cwd)).map(async (repo) => ({
|
|
6541
|
+
...await repoChanges(path.join(cwd, repo)),
|
|
6542
|
+
path: repo
|
|
6543
|
+
})));
|
|
6544
|
+
}
|
|
6545
|
+
async function showFile(cwd, ref, file) {
|
|
6546
|
+
const output = await git(cwd, [
|
|
6547
|
+
"cat-file",
|
|
6548
|
+
"blob",
|
|
6549
|
+
`${ref}:${file}`
|
|
6550
|
+
]);
|
|
6551
|
+
if (output.stdout.length > SHOW_LIMIT || output.stdout.includes("\0")) return null;
|
|
6552
|
+
return output.stdout;
|
|
6553
|
+
}
|
|
6554
|
+
async function history(cwd, limit) {
|
|
6555
|
+
const upstream = (await git(cwd, ["branch", "--format=%(if)%(HEAD)%(then)%(if:notequals=[gone])%(upstream:track)%(then)%(upstream)%(end)%(end)"])).stdout.trim();
|
|
6556
|
+
return (await git(cwd, [
|
|
6557
|
+
"log",
|
|
6558
|
+
"--topo-order",
|
|
6559
|
+
`--max-count=${limit}`,
|
|
6560
|
+
"--format=%H%x1f%P%x1f%an%x1f%at%x1f%D%x1f%s%x1e",
|
|
6561
|
+
"HEAD",
|
|
6562
|
+
...upstream === "" ? [] : [upstream],
|
|
6563
|
+
"--"
|
|
6564
|
+
])).stdout.split("").map((record) => record.trim()).filter(Boolean).map((record) => {
|
|
6565
|
+
const fields = record.split("");
|
|
6566
|
+
return {
|
|
6567
|
+
hash: fields[0],
|
|
6568
|
+
parents: fields[1].split(" ").filter(Boolean),
|
|
6569
|
+
author: fields[2],
|
|
6570
|
+
date: Number(fields[3]) * 1e3,
|
|
6571
|
+
refs: fields[4].split(", ").filter(Boolean),
|
|
6572
|
+
subject: fields[5]
|
|
6573
|
+
};
|
|
6574
|
+
});
|
|
6575
|
+
}
|
|
6576
|
+
async function commitFiles(cwd, hash, parent) {
|
|
6577
|
+
const tokens = (await git(cwd, [
|
|
6578
|
+
"diff",
|
|
6579
|
+
"--name-status",
|
|
6580
|
+
"-M",
|
|
6581
|
+
"-z",
|
|
6582
|
+
parent ?? (await git(cwd, [
|
|
6583
|
+
"hash-object",
|
|
6584
|
+
"-t",
|
|
6585
|
+
"tree",
|
|
6586
|
+
"/dev/null"
|
|
6587
|
+
])).stdout.trim(),
|
|
6588
|
+
hash
|
|
6589
|
+
])).stdout.split("\0");
|
|
6590
|
+
const files = [];
|
|
6591
|
+
for (let index = 0; index < tokens.length - 1; index++) {
|
|
6592
|
+
const status = letter(tokens[index][0]);
|
|
6593
|
+
if (status === "R" || status === "C") {
|
|
6594
|
+
files.push({
|
|
6595
|
+
path: tokens[index + 2],
|
|
6596
|
+
from: tokens[index + 1],
|
|
6597
|
+
status
|
|
6598
|
+
});
|
|
6599
|
+
index += 2;
|
|
6600
|
+
continue;
|
|
6601
|
+
}
|
|
6602
|
+
files.push({
|
|
6603
|
+
path: tokens[index + 1],
|
|
6604
|
+
from: null,
|
|
6605
|
+
status
|
|
6606
|
+
});
|
|
6607
|
+
index += 1;
|
|
6608
|
+
}
|
|
6609
|
+
return files;
|
|
6610
|
+
}
|
|
6611
|
+
async function undoCommit(cwd) {
|
|
6612
|
+
const message = (await git(cwd, [
|
|
6613
|
+
"log",
|
|
6614
|
+
"-1",
|
|
6615
|
+
"--format=%B"
|
|
6616
|
+
])).stdout.trim();
|
|
6617
|
+
if ((await git(cwd, [
|
|
6618
|
+
"rev-list",
|
|
6619
|
+
"--parents",
|
|
6620
|
+
"--max-count=1",
|
|
6621
|
+
"HEAD"
|
|
6622
|
+
])).stdout.trim().split(" ").length === 1) await git(cwd, [
|
|
6623
|
+
"update-ref",
|
|
6624
|
+
"-d",
|
|
6625
|
+
"HEAD"
|
|
6626
|
+
]);
|
|
6627
|
+
else await git(cwd, [
|
|
6628
|
+
"reset",
|
|
6629
|
+
"--soft",
|
|
6630
|
+
"HEAD~1"
|
|
6631
|
+
]);
|
|
6632
|
+
return message;
|
|
6633
|
+
}
|
|
6634
|
+
async function runAction(cwd, action) {
|
|
6635
|
+
if (action.kind === "stage") {
|
|
6636
|
+
await git(cwd, [
|
|
6637
|
+
"--literal-pathspecs",
|
|
6638
|
+
"add",
|
|
6639
|
+
"-A",
|
|
6640
|
+
"--",
|
|
6641
|
+
...action.paths
|
|
6642
|
+
]);
|
|
6643
|
+
return;
|
|
6644
|
+
}
|
|
6645
|
+
if (action.kind === "stageContent") {
|
|
6646
|
+
const entry = await git(cwd, [
|
|
6647
|
+
"--literal-pathspecs",
|
|
6648
|
+
"ls-files",
|
|
6649
|
+
"--stage",
|
|
6650
|
+
"--",
|
|
6651
|
+
action.path
|
|
6652
|
+
]);
|
|
6653
|
+
if (entry.stdout === "") throw new Error(`${action.path} is not in the index`);
|
|
6654
|
+
const hashing = run("git", [
|
|
6655
|
+
"hash-object",
|
|
6656
|
+
"-w",
|
|
6657
|
+
"--stdin",
|
|
6658
|
+
`--path=${action.path}`
|
|
6659
|
+
], { cwd });
|
|
6660
|
+
hashing.child.stdin?.end(action.content);
|
|
6661
|
+
const hash = (await hashing).stdout.trim();
|
|
6662
|
+
await git(cwd, [
|
|
6663
|
+
"update-index",
|
|
6664
|
+
"--cacheinfo",
|
|
6665
|
+
`${entry.stdout.split(" ")[0]},${hash},${action.path}`
|
|
6666
|
+
]);
|
|
6667
|
+
return;
|
|
6668
|
+
}
|
|
6669
|
+
if (action.kind === "unstage") {
|
|
6670
|
+
await git(cwd, [
|
|
6671
|
+
"--literal-pathspecs",
|
|
6672
|
+
"reset",
|
|
6673
|
+
"-q",
|
|
6674
|
+
await diffBase(cwd),
|
|
6675
|
+
"--",
|
|
6676
|
+
...action.paths
|
|
6677
|
+
]);
|
|
6678
|
+
return;
|
|
6679
|
+
}
|
|
6680
|
+
if (action.kind === "discard") {
|
|
6681
|
+
if (action.tracked.length > 0) await git(cwd, [
|
|
6682
|
+
"--literal-pathspecs",
|
|
6683
|
+
"checkout",
|
|
6684
|
+
"-q",
|
|
6685
|
+
"--",
|
|
6686
|
+
...action.tracked
|
|
6687
|
+
]);
|
|
6688
|
+
if (action.untracked.length > 0) await git(cwd, [
|
|
6689
|
+
"--literal-pathspecs",
|
|
6690
|
+
"clean",
|
|
6691
|
+
"-f",
|
|
6692
|
+
"-q",
|
|
6693
|
+
"--",
|
|
6694
|
+
...action.untracked
|
|
6695
|
+
]);
|
|
6696
|
+
return;
|
|
6697
|
+
}
|
|
6698
|
+
if (action.kind === "ignore") {
|
|
6699
|
+
const file = path.join(cwd, ".gitignore");
|
|
6700
|
+
const current = existsSync(file) ? readFileSync(file, "utf8") : "";
|
|
6701
|
+
appendFileSync(file, `${current === "" || current.endsWith("\n") ? "" : "\n"}${action.paths.join("\n")}\n`);
|
|
6702
|
+
return;
|
|
6703
|
+
}
|
|
6704
|
+
if (action.kind === "commit") {
|
|
6705
|
+
if (action.all) await git(cwd, ["add", "--all"]);
|
|
6706
|
+
await git(cwd, [
|
|
6707
|
+
"commit",
|
|
6708
|
+
...action.amend ? ["--amend"] : [],
|
|
6709
|
+
...action.message === "" ? ["--no-edit"] : ["--message", action.message]
|
|
6710
|
+
]);
|
|
6711
|
+
return;
|
|
6712
|
+
}
|
|
6713
|
+
if (action.kind === "pull") {
|
|
6714
|
+
await network(cwd, action.rebase ? ["pull", "--rebase"] : ["pull"]);
|
|
6715
|
+
return;
|
|
6716
|
+
}
|
|
6717
|
+
if (action.kind === "fetch") {
|
|
6718
|
+
await network(cwd, [
|
|
6719
|
+
"fetch",
|
|
6720
|
+
...action.prune ? ["--prune"] : [],
|
|
6721
|
+
...action.all ? ["--all"] : []
|
|
6722
|
+
]);
|
|
6723
|
+
return;
|
|
6724
|
+
}
|
|
6725
|
+
if (action.kind === "stash") {
|
|
6726
|
+
if (action.mode === "tracked") await git(cwd, ["stash", "push"]);
|
|
6727
|
+
if (action.mode === "untracked") await git(cwd, [
|
|
6728
|
+
"stash",
|
|
6729
|
+
"push",
|
|
6730
|
+
"--include-untracked"
|
|
6731
|
+
]);
|
|
6732
|
+
if (action.mode === "staged") await git(cwd, [
|
|
6733
|
+
"stash",
|
|
6734
|
+
"push",
|
|
6735
|
+
"--staged"
|
|
6736
|
+
]);
|
|
6737
|
+
return;
|
|
6738
|
+
}
|
|
6739
|
+
if (action.kind === "unstash") {
|
|
6740
|
+
await git(cwd, ["stash", action.pop ? "pop" : "apply"]);
|
|
6741
|
+
return;
|
|
6742
|
+
}
|
|
6743
|
+
throw new Error("Unknown git action");
|
|
6744
|
+
}
|
|
6745
|
+
async function network(cwd, args) {
|
|
6746
|
+
try {
|
|
6747
|
+
await run("git", args, {
|
|
6748
|
+
cwd,
|
|
6749
|
+
env: {
|
|
6750
|
+
...process.env,
|
|
6751
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
6752
|
+
},
|
|
6753
|
+
timeout: NETWORK_TIMEOUT
|
|
6754
|
+
});
|
|
6755
|
+
} catch (error) {
|
|
6756
|
+
throw gitError(error);
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
async function repoChanges(cwd) {
|
|
6760
|
+
const output = await git(cwd, [
|
|
6761
|
+
"--no-optional-locks",
|
|
6762
|
+
"status",
|
|
6763
|
+
"--porcelain=v2",
|
|
6764
|
+
"--branch",
|
|
6765
|
+
"--untracked-files=all",
|
|
6766
|
+
"-z"
|
|
6767
|
+
]);
|
|
6768
|
+
const repo = {
|
|
6769
|
+
head: null,
|
|
6770
|
+
branch: null,
|
|
6771
|
+
upstream: null,
|
|
6772
|
+
remote: (await git(cwd, ["remote"])).stdout.trim() !== "",
|
|
6773
|
+
ahead: 0,
|
|
6774
|
+
behind: 0,
|
|
6775
|
+
merge: [],
|
|
6776
|
+
staged: [],
|
|
6777
|
+
changes: []
|
|
6778
|
+
};
|
|
6779
|
+
const tokens = output.stdout.split("\0");
|
|
6780
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
6781
|
+
const fields = tokens[index].split(" ");
|
|
6782
|
+
if (fields[0] === "#" && fields[1] === "branch.oid") repo.head = fields[2] === "(initial)" ? null : fields[2];
|
|
6783
|
+
if (fields[0] === "#" && fields[1] === "branch.head") repo.branch = fields[2] === "(detached)" ? null : fields[2];
|
|
6784
|
+
if (fields[0] === "#" && fields[1] === "branch.upstream") repo.upstream = fields[2];
|
|
6785
|
+
if (fields[0] === "#" && fields[1] === "branch.ab") {
|
|
6786
|
+
repo.ahead = Number(fields[2]);
|
|
6787
|
+
repo.behind = -Number(fields[3]);
|
|
6788
|
+
}
|
|
6789
|
+
if (fields[0] === "?") repo.changes.push({
|
|
6790
|
+
path: tokens[index].slice(2),
|
|
6791
|
+
from: null,
|
|
6792
|
+
status: "U"
|
|
6793
|
+
});
|
|
6794
|
+
if (fields[0] === "u") repo.merge.push({
|
|
6795
|
+
path: fields.slice(10).join(" "),
|
|
6796
|
+
from: null,
|
|
6797
|
+
status: "!"
|
|
6798
|
+
});
|
|
6799
|
+
if (fields[0] === "1" || fields[0] === "2") {
|
|
6800
|
+
const renamed = fields[0] === "2";
|
|
6801
|
+
const file = fields.slice(renamed ? 9 : 8).join(" ");
|
|
6802
|
+
const from = renamed ? tokens[index + 1] : null;
|
|
6803
|
+
if (fields[1][0] !== ".") repo.staged.push({
|
|
6804
|
+
path: file,
|
|
6805
|
+
from,
|
|
6806
|
+
status: letter(fields[1][0])
|
|
6807
|
+
});
|
|
6808
|
+
if (fields[1][1] !== ".") repo.changes.push({
|
|
6809
|
+
path: file,
|
|
6810
|
+
from: null,
|
|
6811
|
+
status: letter(fields[1][1])
|
|
6812
|
+
});
|
|
6813
|
+
if (renamed) index++;
|
|
6814
|
+
}
|
|
6815
|
+
}
|
|
6816
|
+
return repo;
|
|
6817
|
+
}
|
|
6818
|
+
function letter(code) {
|
|
6819
|
+
if (code === "M" || code === "T" || code === "A" || code === "D" || code === "R" || code === "C") return code;
|
|
6820
|
+
throw new Error(`Unknown git status ${code}`);
|
|
6821
|
+
}
|
|
6822
|
+
//#endregion
|
|
6441
6823
|
//#region src/services/terminal/paste.ts
|
|
6442
6824
|
const pasteTypes = {
|
|
6443
6825
|
"image/png": "png",
|
|
@@ -6528,7 +6910,8 @@ const handlers = {
|
|
|
6528
6910
|
projects: projects$1(),
|
|
6529
6911
|
sessions: sessions(),
|
|
6530
6912
|
providers: providerStatuses(),
|
|
6531
|
-
activity: activity()
|
|
6913
|
+
activity: activity(),
|
|
6914
|
+
terminals: terminalCount(GLOBAL_TERMINALS)
|
|
6532
6915
|
};
|
|
6533
6916
|
},
|
|
6534
6917
|
"config.get": () => configInfo(),
|
|
@@ -6560,6 +6943,7 @@ const handlers = {
|
|
|
6560
6943
|
return null;
|
|
6561
6944
|
},
|
|
6562
6945
|
"projects.reorder": (params) => reorderProjects(params.ids),
|
|
6946
|
+
"projects.logoFiles": (params) => logoFiles(projectPath(params.id)),
|
|
6563
6947
|
"directories.list": (params) => directories(params.path),
|
|
6564
6948
|
"sessions.update": async (params) => {
|
|
6565
6949
|
try {
|
|
@@ -6648,9 +7032,25 @@ const handlers = {
|
|
|
6648
7032
|
},
|
|
6649
7033
|
capabilities: (params) => capabilities(params.provider, params.accountId, params.projectId === null ? os.homedir() : projectPath(params.projectId)),
|
|
6650
7034
|
"files.list": (params) => listFiles(projectPath(params.projectId)),
|
|
7035
|
+
"files.entries": (params) => listEntries(projectPath(params.projectId)),
|
|
6651
7036
|
"files.read": (params) => readProjectFile(projectPath(params.projectId), params.path),
|
|
7037
|
+
"files.write": (params) => {
|
|
7038
|
+
writeProjectFile(projectPath(params.projectId), params.path, params.content);
|
|
7039
|
+
return null;
|
|
7040
|
+
},
|
|
7041
|
+
"files.create": (params) => {
|
|
7042
|
+
createProjectEntry(projectPath(params.projectId), params.path, params.folder);
|
|
7043
|
+
return null;
|
|
7044
|
+
},
|
|
7045
|
+
"files.rename": (params) => {
|
|
7046
|
+
renameProjectEntry(projectPath(params.projectId), params.path, params.to);
|
|
7047
|
+
return null;
|
|
7048
|
+
},
|
|
7049
|
+
"files.delete": (params) => {
|
|
7050
|
+
deleteProjectEntry(projectPath(params.projectId), params.path);
|
|
7051
|
+
return null;
|
|
7052
|
+
},
|
|
6652
7053
|
"files.media": (params) => linkProjectMedia(projectPath(params.projectId), params.path),
|
|
6653
|
-
"files.download": (params) => linkProjectDownload(projectPath(params.projectId), params.path),
|
|
6654
7054
|
"files.upload": (params) => saveUpload(params.name, Buffer.from(params.data, "base64")),
|
|
6655
7055
|
"files.browse": () => browseToken,
|
|
6656
7056
|
"browser.proxy": (params) => proxyPort(params.origin),
|
|
@@ -6658,6 +7058,16 @@ const handlers = {
|
|
|
6658
7058
|
"git.status": (params) => gitRepos(projectPath(params.projectId)),
|
|
6659
7059
|
"git.diff": (params) => projectDiff(projectPath(params.projectId)),
|
|
6660
7060
|
"git.commit": (params) => commit(path.join(projectPath(params.projectId), params.repo), params.message, params.push),
|
|
7061
|
+
"git.changes": (params) => sourceControl(projectPath(params.projectId)),
|
|
7062
|
+
"git.show": (params) => showFile(path.join(projectPath(params.projectId), params.repo), params.ref, params.path),
|
|
7063
|
+
"git.history": (params) => history(path.join(projectPath(params.projectId), params.repo), params.limit),
|
|
7064
|
+
"git.commitFiles": (params) => commitFiles(path.join(projectPath(params.projectId), params.repo), params.hash, params.parent),
|
|
7065
|
+
"git.message": (params) => commitMessage(path.join(projectPath(params.projectId), params.repo), params.staged),
|
|
7066
|
+
"git.undoCommit": (params) => undoCommit(path.join(projectPath(params.projectId), params.repo)),
|
|
7067
|
+
"git.run": async (params) => {
|
|
7068
|
+
await runAction(path.join(projectPath(params.projectId), params.repo), params.action);
|
|
7069
|
+
return null;
|
|
7070
|
+
},
|
|
6661
7071
|
"git.push": async (params) => {
|
|
6662
7072
|
await push$1(path.join(projectPath(params.projectId), params.repo));
|
|
6663
7073
|
return null;
|
|
@@ -6985,7 +7395,6 @@ function serveMedia(token, request, response) {
|
|
|
6985
7395
|
}
|
|
6986
7396
|
response.setHeader("access-control-allow-origin", "*");
|
|
6987
7397
|
response.setHeader("content-type", link.mediaType);
|
|
6988
|
-
if (link.download !== null) response.setHeader("content-disposition", `attachment; filename*=UTF-8''${encodeURIComponent(link.download)}`);
|
|
6989
7398
|
response.setHeader("cache-control", "private, max-age=3600");
|
|
6990
7399
|
response.setHeader("content-security-policy", "sandbox");
|
|
6991
7400
|
response.setHeader("x-content-type-options", "nosniff");
|