@maintainer-pro/ai-bridge 0.1.18 → 0.1.20
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/package.json +1 -1
- package/src/daemon.mjs +7 -3
- package/src/share-rewrite.mjs +157 -63
package/package.json
CHANGED
package/src/daemon.mjs
CHANGED
|
@@ -2343,7 +2343,8 @@ async function handleAiProxyHttpFromAdmin(msg, ws) {
|
|
|
2343
2343
|
? Buffer.from(msg.body, "base64")
|
|
2344
2344
|
: Buffer.alloc(0),
|
|
2345
2345
|
headers,
|
|
2346
|
-
ctx.portUrls
|
|
2346
|
+
ctx.portUrls,
|
|
2347
|
+
ctx.publicBase
|
|
2347
2348
|
);
|
|
2348
2349
|
|
|
2349
2350
|
if (pathname === "/ai-ui.iife.js") {
|
|
@@ -2615,6 +2616,7 @@ function handleProxyHttpFromAdmin(msg) {
|
|
|
2615
2616
|
chunks: [],
|
|
2616
2617
|
headers,
|
|
2617
2618
|
portUrls: ctx.portUrls,
|
|
2619
|
+
publicBase: ctx.publicBase,
|
|
2618
2620
|
});
|
|
2619
2621
|
const entry = proxyHttpReqs.get(id);
|
|
2620
2622
|
if (entry.rewrite) {
|
|
@@ -2623,7 +2625,8 @@ function handleProxyHttpFromAdmin(msg) {
|
|
|
2623
2625
|
const body = rewriteShareRequestBody(
|
|
2624
2626
|
Buffer.concat(entry.chunks),
|
|
2625
2627
|
headers,
|
|
2626
|
-
ctx.portUrls
|
|
2628
|
+
ctx.portUrls,
|
|
2629
|
+
ctx.publicBase
|
|
2627
2630
|
);
|
|
2628
2631
|
if (body?.length) req.write(body);
|
|
2629
2632
|
req.end();
|
|
@@ -2651,7 +2654,8 @@ function handleProxyHttpBodyFromAdmin(msg) {
|
|
|
2651
2654
|
const body = rewriteShareRequestBody(
|
|
2652
2655
|
Buffer.concat(entry.chunks || []),
|
|
2653
2656
|
entry.headers,
|
|
2654
|
-
entry.portUrls
|
|
2657
|
+
entry.portUrls,
|
|
2658
|
+
entry.publicBase
|
|
2655
2659
|
);
|
|
2656
2660
|
if (body?.length) req.write(body);
|
|
2657
2661
|
req.end();
|
package/src/share-rewrite.mjs
CHANGED
|
@@ -284,8 +284,8 @@ function isProbablyUtf8Text(buf) {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
/**
|
|
287
|
-
* Replace app
|
|
288
|
-
*
|
|
287
|
+
* Replace loopback app origins only (`localhost:8080`, `127.0.0.1`, `::1`,
|
|
288
|
+
* `0.0.0.0`). Real hostnames and other base URLs are left unchanged.
|
|
289
289
|
*
|
|
290
290
|
* @param {string} text
|
|
291
291
|
* @param {Record<string, string> | null | undefined} portUrls
|
|
@@ -297,14 +297,7 @@ export function rewriteMappedLocalUrls(text, portUrls) {
|
|
|
297
297
|
.filter((port) => Number.isInteger(port) && port > 0 && port <= 65535)
|
|
298
298
|
.sort((a, b) => b - a);
|
|
299
299
|
if (!ports.length) return text;
|
|
300
|
-
const localHost =
|
|
301
|
-
"(?:localhost|127\\.0\\.0\\.1|\\[::1\\]|::1|0\\.0\\.0\\.0|" +
|
|
302
|
-
"10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" +
|
|
303
|
-
"192\\.168\\.\\d{1,3}\\.\\d{1,3}|" +
|
|
304
|
-
"172\\.(?:1[6-9]|2\\d|3[01])\\.\\d{1,3}\\.\\d{1,3}|" +
|
|
305
|
-
"169\\.254\\.\\d{1,3}\\.\\d{1,3}|" +
|
|
306
|
-
"[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.(?:local|lan|internal|home))?)";
|
|
307
|
-
const anyHost = "(?:[^\\s'\"\\\\/<>@:]+|\\[[0-9a-fA-F:]+\\])";
|
|
300
|
+
const localHost = "(?:localhost|127\\.0\\.0\\.1|\\[::1\\]|::1|0\\.0\\.0\\.0)";
|
|
308
301
|
let out = String(text);
|
|
309
302
|
for (let i = 0; i < ports.length; i++) {
|
|
310
303
|
const port = ports[i];
|
|
@@ -339,12 +332,6 @@ export function rewriteMappedLocalUrls(text, portUrls) {
|
|
|
339
332
|
out = out.replace(new RegExp("//" + localHost + ":" + port + tail, "gi"), function (m) {
|
|
340
333
|
return swap(m, dest.replace(/^https?:/i, ""));
|
|
341
334
|
});
|
|
342
|
-
out = out.replace(new RegExp("http://" + anyHost + ":" + port + tail, "gi"), function (m) {
|
|
343
|
-
return swap(m, dest);
|
|
344
|
-
});
|
|
345
|
-
out = out.replace(new RegExp("ws://" + anyHost + ":" + port + tail, "gi"), function (m) {
|
|
346
|
-
return swap(m, destWs);
|
|
347
|
-
});
|
|
348
335
|
out = out.replace(
|
|
349
336
|
new RegExp("(?<![\\w./])" + localHost + ":" + port + tail, "gi"),
|
|
350
337
|
function (m) {
|
|
@@ -360,13 +347,17 @@ export function rewriteMappedLocalUrls(text, portUrls) {
|
|
|
360
347
|
* @param {Record<string, string>} headers
|
|
361
348
|
* @param {Record<string, string> | null | undefined} portUrls
|
|
362
349
|
*/
|
|
363
|
-
export function rewriteShareRequestBody(body, headers, portUrls) {
|
|
364
|
-
if (!body
|
|
350
|
+
export function rewriteShareRequestBody(body, headers, portUrls, publicBase) {
|
|
351
|
+
if (!body) return body;
|
|
365
352
|
const buf = Buffer.isBuffer(body) ? body : Buffer.from(body);
|
|
366
353
|
if (!buf.length) return buf;
|
|
367
354
|
if (!shouldRewriteBody(headers) && !isProbablyUtf8Text(buf)) return buf;
|
|
368
|
-
|
|
369
|
-
|
|
355
|
+
let text = buf.toString("utf8");
|
|
356
|
+
let next = text;
|
|
357
|
+
if (portUrls && Object.keys(portUrls).length) {
|
|
358
|
+
next = rewriteMappedLocalUrls(next, portUrls);
|
|
359
|
+
}
|
|
360
|
+
if (publicBase) next = rewriteShareOriginPaths(next, publicBase);
|
|
370
361
|
if (next === text) return buf;
|
|
371
362
|
return Buffer.from(next, "utf8");
|
|
372
363
|
}
|
|
@@ -405,35 +396,107 @@ function rewriteHeaderUrls(headers, publicBase, port, portUrls) {
|
|
|
405
396
|
publicBase
|
|
406
397
|
);
|
|
407
398
|
}
|
|
408
|
-
if (
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
399
|
+
if (portUrls && Object.keys(portUrls).length) {
|
|
400
|
+
for (const key of Object.keys(headers)) {
|
|
401
|
+
const lower = key.toLowerCase();
|
|
402
|
+
if (lower === "set-cookie" || lower === "content-length" || lower === "content-encoding") {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
headers[key] = rewriteMappedLocalUrls(headers[key], portUrls);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (publicBase) {
|
|
409
|
+
for (const key of Object.keys(headers)) {
|
|
410
|
+
const lower = key.toLowerCase();
|
|
411
|
+
if (lower === "set-cookie" || lower === "content-length" || lower === "content-encoding") {
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
headers[key] = rewriteShareOriginPaths(headers[key], publicBase);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Apps that honor X-Forwarded-Host emit https://admin-host/oauth2 instead of
|
|
421
|
+
* localhost:8080/oauth2. Put those back under /p/{token}/{slug} so the token
|
|
422
|
+
* and app route stay on the URL.
|
|
423
|
+
*
|
|
424
|
+
* @param {string} value
|
|
425
|
+
* @param {string} publicBase
|
|
426
|
+
*/
|
|
427
|
+
export function prefixShareOriginUrl(value, publicBase) {
|
|
428
|
+
if (!value || !publicBase) return value;
|
|
429
|
+
try {
|
|
430
|
+
const base = new URL(String(publicBase).replace(/\/$/, ""));
|
|
431
|
+
const prefix = base.pathname.replace(/\/$/, "");
|
|
432
|
+
if (!prefix || prefix.indexOf("/p/") !== 0) return value;
|
|
433
|
+
const u = new URL(String(value), base);
|
|
434
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") return value;
|
|
435
|
+
if (String(u.hostname || "").toLowerCase() !== String(base.hostname || "").toLowerCase()) {
|
|
436
|
+
return value;
|
|
413
437
|
}
|
|
414
|
-
|
|
438
|
+
const reqPort = u.port || (u.protocol === "https:" ? "443" : "80");
|
|
439
|
+
const basePort = base.port || (base.protocol === "https:" ? "443" : "80");
|
|
440
|
+
if (reqPort !== basePort) return value;
|
|
441
|
+
const path = u.pathname || "/";
|
|
442
|
+
if (path === "/" || path === "") return value;
|
|
443
|
+
if (path === prefix || path.indexOf(prefix + "/") === 0) return u.toString();
|
|
444
|
+
if (path.indexOf("/p/") === 0) return u.toString();
|
|
445
|
+
u.pathname = prefix + path;
|
|
446
|
+
return u.toString();
|
|
447
|
+
} catch {
|
|
448
|
+
return value;
|
|
415
449
|
}
|
|
416
450
|
}
|
|
417
451
|
|
|
452
|
+
export function rewriteShareOriginPaths(text, publicBase) {
|
|
453
|
+
if (!text || !publicBase) return text;
|
|
454
|
+
let origin = "";
|
|
455
|
+
try {
|
|
456
|
+
origin = new URL(String(publicBase).replace(/\/$/, "")).origin;
|
|
457
|
+
} catch {
|
|
458
|
+
return text;
|
|
459
|
+
}
|
|
460
|
+
if (!origin) return text;
|
|
461
|
+
const originEsc = origin.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
462
|
+
const originSlashEsc = origin
|
|
463
|
+
.replace(/\//g, "\\/")
|
|
464
|
+
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
465
|
+
let out = String(text);
|
|
466
|
+
out = out.replace(new RegExp(originEsc + "/(?!p/)[^\\s'\"<>\\\\]+", "gi"), function (matched) {
|
|
467
|
+
return prefixShareOriginUrl(matched, publicBase);
|
|
468
|
+
});
|
|
469
|
+
out = out.replace(
|
|
470
|
+
new RegExp(originSlashEsc + "\\\\/(?!p/)[^\\s'\"<>]+", "gi"),
|
|
471
|
+
function (matched) {
|
|
472
|
+
const unescaped = matched.replace(/\\\//g, "/");
|
|
473
|
+
return prefixShareOriginUrl(unescaped, publicBase).replace(/\//g, "\\/");
|
|
474
|
+
}
|
|
475
|
+
);
|
|
476
|
+
return out;
|
|
477
|
+
}
|
|
478
|
+
|
|
418
479
|
function rewriteLocation(value, publicBase, port, portUrls) {
|
|
419
480
|
if (!publicBase && !(portUrls && Object.keys(portUrls).length)) return value;
|
|
420
481
|
try {
|
|
421
482
|
const u = new URL(value, `http://127.0.0.1:${Number(port) || 0}`);
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
483
|
+
if (isLoopbackHost(u.hostname)) {
|
|
484
|
+
const locPort = Number(u.port) || Number(port) || 0;
|
|
485
|
+
const mapped =
|
|
486
|
+
mappedUrlForPort(portUrls, locPort) ||
|
|
487
|
+
String(publicBase || "").replace(/\/$/, "");
|
|
488
|
+
if (!mapped) return value;
|
|
489
|
+
const prefix = proxyPathPrefix(mapped);
|
|
490
|
+
let path = u.pathname || "/";
|
|
491
|
+
if (prefix && (path === prefix || path.startsWith(`${prefix}/`))) {
|
|
492
|
+
path = path.slice(prefix.length) || "/";
|
|
493
|
+
}
|
|
494
|
+
if (path === "/") return `${mapped}${u.search}${u.hash}`;
|
|
495
|
+
return `${mapped}${path}${u.search}${u.hash}`;
|
|
434
496
|
}
|
|
435
|
-
|
|
436
|
-
return
|
|
497
|
+
const fromPorts = rewriteMappedLocalUrls(String(value), portUrls);
|
|
498
|
+
if (fromPorts !== String(value)) return fromPorts;
|
|
499
|
+
if (publicBase) return prefixShareOriginUrl(String(value), publicBase);
|
|
437
500
|
} catch {
|
|
438
501
|
/* keep */
|
|
439
502
|
}
|
|
@@ -911,6 +974,7 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls) {
|
|
|
911
974
|
if (path === p || path.indexOf(p + "/") === 0) return collapse(path);
|
|
912
975
|
var root = p.slice(0, p.lastIndexOf("/"));
|
|
913
976
|
if (root && (path === root || path.indexOf(root + "/") === 0)) return path;
|
|
977
|
+
if (path.indexOf("/p/") === 0) return path;
|
|
914
978
|
if (path === "/api/v1" || path.indexOf("/api/v1/") === 0) return path;
|
|
915
979
|
return collapse(p + path);
|
|
916
980
|
}
|
|
@@ -948,31 +1012,14 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls) {
|
|
|
948
1012
|
try {
|
|
949
1013
|
var nav = new URL(v, location.href);
|
|
950
1014
|
if (nav.origin !== location.origin) return v;
|
|
1015
|
+
if (nav.pathname === "/" || nav.pathname === "") return v;
|
|
951
1016
|
nav.pathname = prefixPath(nav.pathname);
|
|
952
1017
|
return nav.toString();
|
|
953
1018
|
} catch (e) {}
|
|
954
1019
|
return v;
|
|
955
1020
|
}
|
|
956
1021
|
function addNet(v) {
|
|
957
|
-
|
|
958
|
-
var mapped = mapLocal(v);
|
|
959
|
-
if (mapped !== v) return mapped;
|
|
960
|
-
try {
|
|
961
|
-
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(v) || v.indexOf("//") === 0) {
|
|
962
|
-
var net = new URL(v, location.href);
|
|
963
|
-
if (net.origin !== location.origin) return v;
|
|
964
|
-
if (net.pathname === p || net.pathname.indexOf(p + "/") === 0) return v;
|
|
965
|
-
if (!isAsset(net.pathname)) return v;
|
|
966
|
-
net.pathname = prefixPath(net.pathname);
|
|
967
|
-
return net.toString();
|
|
968
|
-
}
|
|
969
|
-
} catch (e) {}
|
|
970
|
-
if (v.charAt(0) === "/" && v.charAt(1) !== "/") {
|
|
971
|
-
if (v === p || v.indexOf(p + "/") === 0) return collapse(v);
|
|
972
|
-
if (!isAsset(v)) return v;
|
|
973
|
-
return prefixPath(v);
|
|
974
|
-
}
|
|
975
|
-
return v;
|
|
1022
|
+
return addNav(v);
|
|
976
1023
|
}
|
|
977
1024
|
function mapSel(sel) {
|
|
978
1025
|
if (typeof sel !== "string") return sel;
|
|
@@ -1156,17 +1203,48 @@ function shareServiceWorkerMain(portMap, tokenRoot, rewriteMappedLocalUrls) {
|
|
|
1156
1203
|
return text;
|
|
1157
1204
|
}
|
|
1158
1205
|
}
|
|
1159
|
-
function
|
|
1206
|
+
function appPrefixFromHref(href) {
|
|
1207
|
+
try {
|
|
1208
|
+
var u = new URL(String(href || ""));
|
|
1209
|
+
var parts = u.pathname.split("/").filter(Boolean);
|
|
1210
|
+
if (parts[0] === "p" && parts[1] && parts[2]) {
|
|
1211
|
+
return "/" + parts[0] + "/" + parts[1] + "/" + parts[2];
|
|
1212
|
+
}
|
|
1213
|
+
} catch (e) {}
|
|
1214
|
+
return "";
|
|
1215
|
+
}
|
|
1216
|
+
function prefixWith(url, prefix) {
|
|
1217
|
+
if (!prefix) return url;
|
|
1218
|
+
try {
|
|
1219
|
+
var u = new URL(url, self.location.href);
|
|
1220
|
+
if (u.origin !== self.location.origin) return url;
|
|
1221
|
+
if (u.pathname === "/" || u.pathname === "") return url;
|
|
1222
|
+
if (u.pathname.indexOf("/p/") === 0) return url;
|
|
1223
|
+
if (u.pathname === prefix || u.pathname.indexOf(prefix + "/") === 0) return url;
|
|
1224
|
+
u.pathname = u.pathname === "/" ? prefix : prefix + u.pathname;
|
|
1225
|
+
return u.toString();
|
|
1226
|
+
} catch (e) {}
|
|
1227
|
+
return url;
|
|
1228
|
+
}
|
|
1229
|
+
function mapUrl(v, prefix) {
|
|
1160
1230
|
if (typeof v !== "string" || !v) return v;
|
|
1161
1231
|
if (v.indexOf("/__mp/") !== -1) return v;
|
|
1162
|
-
return rewriteText(v);
|
|
1232
|
+
return prefixWith(rewriteText(v), prefix);
|
|
1163
1233
|
}
|
|
1164
1234
|
self.addEventListener("fetch", function (event) {
|
|
1165
1235
|
var req = event.request;
|
|
1166
1236
|
if (req.url.indexOf("/__mp/") !== -1) return;
|
|
1167
1237
|
event.respondWith(
|
|
1168
1238
|
(async function () {
|
|
1169
|
-
var
|
|
1239
|
+
var client = null;
|
|
1240
|
+
try {
|
|
1241
|
+
if (event.clientId) client = await self.clients.get(event.clientId);
|
|
1242
|
+
} catch (e) {}
|
|
1243
|
+
var prefix =
|
|
1244
|
+
appPrefixFromHref(client && client.url) ||
|
|
1245
|
+
appPrefixFromHref(req.referrer) ||
|
|
1246
|
+
appPrefixFromHref(self.registration && self.registration.scope);
|
|
1247
|
+
var url = mapUrl(req.url, prefix);
|
|
1170
1248
|
var method = req.method;
|
|
1171
1249
|
var headers = new Headers(req.headers);
|
|
1172
1250
|
var body;
|
|
@@ -1195,15 +1273,24 @@ function shareServiceWorkerMain(portMap, tokenRoot, rewriteMappedLocalUrls) {
|
|
|
1195
1273
|
} catch (e) {}
|
|
1196
1274
|
}
|
|
1197
1275
|
var res = await fetch(new Request(url, init));
|
|
1276
|
+
var outHeaders = new Headers(res.headers);
|
|
1277
|
+
var loc = outHeaders.get("location");
|
|
1278
|
+
if (loc) outHeaders.set("location", mapUrl(loc, prefix));
|
|
1198
1279
|
var resType = res.headers.get("content-type") || "";
|
|
1199
1280
|
if (
|
|
1200
1281
|
!/html|javascript|ecmascript|json|css|xml|svg|text|urlencoded/.test(resType)
|
|
1201
1282
|
) {
|
|
1283
|
+
if (loc && outHeaders.get("location") !== loc) {
|
|
1284
|
+
return new Response(res.body, {
|
|
1285
|
+
status: res.status,
|
|
1286
|
+
statusText: res.statusText,
|
|
1287
|
+
headers: outHeaders,
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1202
1290
|
return res;
|
|
1203
1291
|
}
|
|
1204
1292
|
var resText = await res.text();
|
|
1205
1293
|
var mapped = rewriteText(resText);
|
|
1206
|
-
var outHeaders = new Headers(res.headers);
|
|
1207
1294
|
outHeaders.delete("content-length");
|
|
1208
1295
|
outHeaders.delete("content-encoding");
|
|
1209
1296
|
return new Response(mapped, {
|
|
@@ -1305,6 +1392,13 @@ export function processShareHttpResponse(opts) {
|
|
|
1305
1392
|
mutated = true;
|
|
1306
1393
|
}
|
|
1307
1394
|
}
|
|
1395
|
+
if (publicBase && rewriteTextBody) {
|
|
1396
|
+
const prefixed = rewriteShareOriginPaths(text, publicBase);
|
|
1397
|
+
if (prefixed !== text) {
|
|
1398
|
+
text = prefixed;
|
|
1399
|
+
mutated = true;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1308
1402
|
if (aiPublicBase) {
|
|
1309
1403
|
const withShareUrls = rewriteLocalSidecarUrls(text, aiPublicBase, publicBase);
|
|
1310
1404
|
if (withShareUrls !== text) {
|