offsite 0.0.31 → 0.0.33
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 +103 -11
- package/apps/server/dist/main.mjs.map +1 -1
- package/apps/web/dist/assets/{dist-BtfuptBR.js → dist-BrbsojdV.js} +1 -1
- package/apps/web/dist/assets/index-CwgEGzdv.css +2 -0
- package/apps/web/dist/assets/index-Dey2-CSd.js +782 -0
- package/apps/web/dist/index.html +2 -2
- package/apps/web/dist/sw.js +1 -1
- package/package.json +1 -1
- package/apps/web/dist/assets/index-7KisGKFg.js +0 -782
- package/apps/web/dist/assets/index-BeAmEPQC.css +0 -2
|
@@ -24,6 +24,7 @@ import { integer, snakeCase, text } from "drizzle-orm/sqlite-core";
|
|
|
24
24
|
import { promisify } from "node:util";
|
|
25
25
|
import webPush from "web-push";
|
|
26
26
|
import { createProxyServer } from "http-proxy-3";
|
|
27
|
+
import { OUTGOING_PASSES } from "http-proxy-3/dist/lib/http-proxy/passes/web-outgoing.js";
|
|
27
28
|
import { spawn as spawn$1 } from "@lydell/node-pty";
|
|
28
29
|
//#region ../core/src/media.ts
|
|
29
30
|
const mediaTypes = /* @__PURE__ */ new Map([
|
|
@@ -890,6 +891,7 @@ function accountSchema(provider) {
|
|
|
890
891
|
id: z.string(),
|
|
891
892
|
provider: z.literal(provider),
|
|
892
893
|
name: z.string(),
|
|
894
|
+
color: z.string().regex(/^#[0-9a-f]{6}$/i, "must be a hex color like #3b82f6").nullable().default(null),
|
|
893
895
|
configDir: platformDirs,
|
|
894
896
|
enabled: z.boolean().default(true),
|
|
895
897
|
hiddenModels: z.record(z.string(), z.boolean()).default({})
|
|
@@ -3458,6 +3460,14 @@ function openCodeRuns() {
|
|
|
3458
3460
|
}
|
|
3459
3461
|
//#endregion
|
|
3460
3462
|
//#region src/services/accounts.ts
|
|
3463
|
+
const accountColors = [
|
|
3464
|
+
"#3b82f6",
|
|
3465
|
+
"#16a34a",
|
|
3466
|
+
"#f97316",
|
|
3467
|
+
"#a855f7",
|
|
3468
|
+
"#ec4899",
|
|
3469
|
+
"#0d9488"
|
|
3470
|
+
];
|
|
3461
3471
|
const providerNames = {
|
|
3462
3472
|
claude: "Claude",
|
|
3463
3473
|
codex: "Codex",
|
|
@@ -3468,13 +3478,11 @@ function providerName(provider) {
|
|
|
3468
3478
|
}
|
|
3469
3479
|
function storedAccounts() {
|
|
3470
3480
|
const config = readConfig();
|
|
3471
|
-
return [...config.claudeAccounts.map((item) => ({
|
|
3472
|
-
...item,
|
|
3473
|
-
configDir: item.configDir[platform()]
|
|
3474
|
-
})), ...config.codexAccounts.map((item) => ({
|
|
3481
|
+
return [...config.claudeAccounts, ...config.codexAccounts].map((item, index) => ({
|
|
3475
3482
|
...item,
|
|
3483
|
+
color: item.color ?? accountColors[index % accountColors.length],
|
|
3476
3484
|
configDir: item.configDir[platform()]
|
|
3477
|
-
}))
|
|
3485
|
+
}));
|
|
3478
3486
|
}
|
|
3479
3487
|
function assertEnabled(provider, accountId) {
|
|
3480
3488
|
if (!readConfig().enabledProviders[provider]) throw new Error(`${providerName(provider)} is disabled in Settings`);
|
|
@@ -3576,6 +3584,8 @@ function providerCapabilities(provider, accountId, cwd) {
|
|
|
3576
3584
|
}
|
|
3577
3585
|
function addAccount(provider, name, configDir) {
|
|
3578
3586
|
const account = validAccount(name, configDir);
|
|
3587
|
+
const stored = storedAccounts();
|
|
3588
|
+
const color = accountColors.find((item) => stored.every((other) => other.color !== item)) ?? accountColors[stored.length % accountColors.length];
|
|
3579
3589
|
const config = readConfig();
|
|
3580
3590
|
const key = `${provider}Accounts`;
|
|
3581
3591
|
saveConfig({
|
|
@@ -3584,6 +3594,7 @@ function addAccount(provider, name, configDir) {
|
|
|
3584
3594
|
id: randomUUID(),
|
|
3585
3595
|
provider,
|
|
3586
3596
|
name: account.name,
|
|
3597
|
+
color,
|
|
3587
3598
|
configDir: { [platform()]: account.configDir },
|
|
3588
3599
|
enabled: true,
|
|
3589
3600
|
hiddenModels: {}
|
|
@@ -3601,6 +3612,7 @@ async function updateAccount(update) {
|
|
|
3601
3612
|
[key]: config[key].map((item) => item.id === update.id ? {
|
|
3602
3613
|
...item,
|
|
3603
3614
|
name: account.name,
|
|
3615
|
+
color: update.color,
|
|
3604
3616
|
configDir: {
|
|
3605
3617
|
...item.configDir,
|
|
3606
3618
|
[platform()]: account.configDir
|
|
@@ -3795,6 +3807,7 @@ function claudeMode(value) {
|
|
|
3795
3807
|
return mode;
|
|
3796
3808
|
}
|
|
3797
3809
|
function claudeOptions(accountId, cwd) {
|
|
3810
|
+
if (!statSync(cwd, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`Project folder ${cwd} does not exist`);
|
|
3798
3811
|
const configDir = storedAccount("claude", accountId).configDir ?? claudeBaseDir();
|
|
3799
3812
|
const env = {
|
|
3800
3813
|
...process.env,
|
|
@@ -5093,6 +5106,46 @@ function resolveProjectFile(cwd, file) {
|
|
|
5093
5106
|
//#endregion
|
|
5094
5107
|
//#region src/services/proxy.ts
|
|
5095
5108
|
const proxies = /* @__PURE__ */ new Map();
|
|
5109
|
+
const pageScriptPath = "/__offsite/browser.js";
|
|
5110
|
+
function pageScript(origin) {
|
|
5111
|
+
return `(() => {
|
|
5112
|
+
if (window.parent === window || window.navigation === undefined) {
|
|
5113
|
+
return
|
|
5114
|
+
}
|
|
5115
|
+
const post = (message) =>
|
|
5116
|
+
window.parent.postMessage({ offsite: 'browser', ...message }, '*')
|
|
5117
|
+
const report = () =>
|
|
5118
|
+
post({
|
|
5119
|
+
event: 'location',
|
|
5120
|
+
url: ${JSON.stringify(origin)} + location.pathname + location.search + location.hash,
|
|
5121
|
+
proxied: location.href,
|
|
5122
|
+
back: navigation.canGoBack,
|
|
5123
|
+
forward: navigation.canGoForward,
|
|
5124
|
+
})
|
|
5125
|
+
addEventListener('pageshow', report)
|
|
5126
|
+
navigation.addEventListener('currententrychange', report)
|
|
5127
|
+
navigation.addEventListener('navigate', (event) => {
|
|
5128
|
+
if (!event.destination.sameDocument && event.downloadRequest === null) {
|
|
5129
|
+
post({ event: 'navigate' })
|
|
5130
|
+
}
|
|
5131
|
+
})
|
|
5132
|
+
addEventListener('message', (event) => {
|
|
5133
|
+
if (event.source !== window.parent || event.data?.offsite !== 'browser') {
|
|
5134
|
+
return
|
|
5135
|
+
}
|
|
5136
|
+
if (event.data.command === 'back') {
|
|
5137
|
+
navigation.back()
|
|
5138
|
+
}
|
|
5139
|
+
if (event.data.command === 'forward') {
|
|
5140
|
+
navigation.forward()
|
|
5141
|
+
}
|
|
5142
|
+
if (event.data.command === 'reload') {
|
|
5143
|
+
location.reload()
|
|
5144
|
+
}
|
|
5145
|
+
})
|
|
5146
|
+
})()
|
|
5147
|
+
`;
|
|
5148
|
+
}
|
|
5096
5149
|
function proxyPort(origin) {
|
|
5097
5150
|
const target = new URL(origin);
|
|
5098
5151
|
if (target.protocol !== "http:" && target.protocol !== "https:") throw new Error(`${origin} is not an http or https address`);
|
|
@@ -5104,21 +5157,43 @@ function proxyPort(origin) {
|
|
|
5104
5157
|
return started;
|
|
5105
5158
|
}
|
|
5106
5159
|
function startProxy(target) {
|
|
5107
|
-
const
|
|
5108
|
-
target
|
|
5160
|
+
const options = {
|
|
5161
|
+
target,
|
|
5109
5162
|
changeOrigin: true,
|
|
5110
5163
|
ws: true,
|
|
5111
5164
|
secure: false,
|
|
5112
5165
|
autoRewrite: true,
|
|
5113
5166
|
protocolRewrite: "http",
|
|
5114
|
-
cookieDomainRewrite: ""
|
|
5167
|
+
cookieDomainRewrite: "",
|
|
5168
|
+
selfHandleResponse: true
|
|
5169
|
+
};
|
|
5170
|
+
const proxy = createProxyServer(options);
|
|
5171
|
+
proxy.on("proxyReq", (outgoing, request) => {
|
|
5172
|
+
rewriteOrigin(target, outgoing, request);
|
|
5173
|
+
if (request.headers.accept?.startsWith("text/html") === true) outgoing.removeHeader("accept-encoding");
|
|
5115
5174
|
});
|
|
5116
|
-
proxy.on("proxyReq", (outgoing, request) => rewriteOrigin(target, outgoing, request));
|
|
5117
5175
|
proxy.on("proxyReqWs", (outgoing, request) => rewriteOrigin(target, outgoing, request));
|
|
5118
|
-
proxy.on("proxyRes", (incoming) => {
|
|
5176
|
+
proxy.on("proxyRes", async (incoming, request, response) => {
|
|
5177
|
+
try {
|
|
5178
|
+
await rewriteRedirect(target, incoming, request);
|
|
5179
|
+
} catch (error) {
|
|
5180
|
+
response.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
|
|
5181
|
+
response.end(`Could not follow the redirect from ${target.origin}: ${error instanceof Error ? error.message : String(error)}`);
|
|
5182
|
+
return;
|
|
5183
|
+
}
|
|
5119
5184
|
delete incoming.headers["x-frame-options"];
|
|
5120
5185
|
delete incoming.headers["content-security-policy"];
|
|
5121
5186
|
if (incoming.headers["set-cookie"] !== void 0) incoming.headers["set-cookie"] = incoming.headers["set-cookie"].map((cookie) => cookie.replace(/;\s*secure/i, "").replace(/;\s*samesite=none/i, ""));
|
|
5187
|
+
const page = request.method === "GET" && request.headers.accept?.startsWith("text/html") === true && incoming.statusCode !== 204 && incoming.statusCode !== 304 && /^text\/html/i.test(incoming.headers["content-type"] ?? "") && incoming.headers["content-encoding"] === void 0;
|
|
5188
|
+
if (page) delete incoming.headers["content-length"];
|
|
5189
|
+
for (const pass of Object.values(OUTGOING_PASSES)) pass(request, response, incoming, options);
|
|
5190
|
+
if (!page) {
|
|
5191
|
+
incoming.pipe(response);
|
|
5192
|
+
return;
|
|
5193
|
+
}
|
|
5194
|
+
const chunks = [];
|
|
5195
|
+
incoming.on("data", (chunk) => chunks.push(chunk));
|
|
5196
|
+
incoming.on("end", () => response.end(Buffer.from(Buffer.concat(chunks).toString("latin1").replace(/^(\xEF\xBB\xBF)?\s*(<!doctype[^>]*>)?/i, `$&<script src="${pageScriptPath}"><\/script>`), "latin1")));
|
|
5122
5197
|
});
|
|
5123
5198
|
proxy.on("error", (error, _request, response) => {
|
|
5124
5199
|
if (!(response instanceof http.ServerResponse)) {
|
|
@@ -5128,7 +5203,14 @@ function startProxy(target) {
|
|
|
5128
5203
|
if (!response.headersSent) response.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
|
|
5129
5204
|
response.end(`Could not reach ${target.origin}: ${error.message}`);
|
|
5130
5205
|
});
|
|
5131
|
-
const server = http.createServer((request, response) =>
|
|
5206
|
+
const server = http.createServer((request, response) => {
|
|
5207
|
+
if (request.url === pageScriptPath) {
|
|
5208
|
+
response.writeHead(200, { "content-type": "text/javascript; charset=utf-8" });
|
|
5209
|
+
response.end(pageScript(target.origin));
|
|
5210
|
+
return;
|
|
5211
|
+
}
|
|
5212
|
+
proxy.web(request, response);
|
|
5213
|
+
});
|
|
5132
5214
|
server.on("upgrade", (request, socket, head) => proxy.ws(request, socket, head));
|
|
5133
5215
|
return new Promise((resolve, reject) => {
|
|
5134
5216
|
server.once("error", reject);
|
|
@@ -5142,6 +5224,16 @@ function startProxy(target) {
|
|
|
5142
5224
|
});
|
|
5143
5225
|
});
|
|
5144
5226
|
}
|
|
5227
|
+
async function rewriteRedirect(target, incoming, request) {
|
|
5228
|
+
const location = incoming.headers.location;
|
|
5229
|
+
if (location === void 0 || !/^30[1278]$/.test(String(incoming.statusCode))) return;
|
|
5230
|
+
const next = new URL(location, target);
|
|
5231
|
+
if (next.origin === target.origin || next.protocol !== "http:" && next.protocol !== "https:") return;
|
|
5232
|
+
if (request.headers.host === void 0) throw new Error("the redirected request has no host");
|
|
5233
|
+
const proxied = new URL(`http://${request.headers.host}`);
|
|
5234
|
+
proxied.port = String(await proxyPort(next.origin));
|
|
5235
|
+
incoming.headers.location = `${proxied.origin}${next.pathname}${next.search}${next.hash}`;
|
|
5236
|
+
}
|
|
5145
5237
|
function rewriteOrigin(target, outgoing, request) {
|
|
5146
5238
|
if (request.headers.origin !== void 0) outgoing.setHeader("origin", target.origin);
|
|
5147
5239
|
if (request.headers.referer !== void 0) outgoing.setHeader("referer", request.headers.referer.replace(`http://${request.headers.host}`, target.origin));
|