@maintainer-pro/ai-bridge 0.1.18 → 0.1.19
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 +151 -47
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
|
@@ -360,13 +360,17 @@ export function rewriteMappedLocalUrls(text, portUrls) {
|
|
|
360
360
|
* @param {Record<string, string>} headers
|
|
361
361
|
* @param {Record<string, string> | null | undefined} portUrls
|
|
362
362
|
*/
|
|
363
|
-
export function rewriteShareRequestBody(body, headers, portUrls) {
|
|
364
|
-
if (!body
|
|
363
|
+
export function rewriteShareRequestBody(body, headers, portUrls, publicBase) {
|
|
364
|
+
if (!body) return body;
|
|
365
365
|
const buf = Buffer.isBuffer(body) ? body : Buffer.from(body);
|
|
366
366
|
if (!buf.length) return buf;
|
|
367
367
|
if (!shouldRewriteBody(headers) && !isProbablyUtf8Text(buf)) return buf;
|
|
368
|
-
|
|
369
|
-
|
|
368
|
+
let text = buf.toString("utf8");
|
|
369
|
+
let next = text;
|
|
370
|
+
if (portUrls && Object.keys(portUrls).length) {
|
|
371
|
+
next = rewriteMappedLocalUrls(next, portUrls);
|
|
372
|
+
}
|
|
373
|
+
if (publicBase) next = rewriteShareOriginPaths(next, publicBase);
|
|
370
374
|
if (next === text) return buf;
|
|
371
375
|
return Buffer.from(next, "utf8");
|
|
372
376
|
}
|
|
@@ -405,35 +409,106 @@ function rewriteHeaderUrls(headers, publicBase, port, portUrls) {
|
|
|
405
409
|
publicBase
|
|
406
410
|
);
|
|
407
411
|
}
|
|
408
|
-
if (
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
412
|
+
if (portUrls && Object.keys(portUrls).length) {
|
|
413
|
+
for (const key of Object.keys(headers)) {
|
|
414
|
+
const lower = key.toLowerCase();
|
|
415
|
+
if (lower === "set-cookie" || lower === "content-length" || lower === "content-encoding") {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
headers[key] = rewriteMappedLocalUrls(headers[key], portUrls);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (publicBase) {
|
|
422
|
+
for (const key of Object.keys(headers)) {
|
|
423
|
+
const lower = key.toLowerCase();
|
|
424
|
+
if (lower === "set-cookie" || lower === "content-length" || lower === "content-encoding") {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
headers[key] = rewriteShareOriginPaths(headers[key], publicBase);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Apps that honor X-Forwarded-Host emit https://admin-host/oauth2 instead of
|
|
434
|
+
* localhost:8080/oauth2. Put those back under /p/{token}/{slug} so the token
|
|
435
|
+
* and app route stay on the URL.
|
|
436
|
+
*
|
|
437
|
+
* @param {string} value
|
|
438
|
+
* @param {string} publicBase
|
|
439
|
+
*/
|
|
440
|
+
export function prefixShareOriginUrl(value, publicBase) {
|
|
441
|
+
if (!value || !publicBase) return value;
|
|
442
|
+
try {
|
|
443
|
+
const base = new URL(String(publicBase).replace(/\/$/, ""));
|
|
444
|
+
const prefix = base.pathname.replace(/\/$/, "");
|
|
445
|
+
if (!prefix || prefix.indexOf("/p/") !== 0) return value;
|
|
446
|
+
const u = new URL(String(value), base);
|
|
447
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") return value;
|
|
448
|
+
if (String(u.hostname || "").toLowerCase() !== String(base.hostname || "").toLowerCase()) {
|
|
449
|
+
return value;
|
|
413
450
|
}
|
|
414
|
-
|
|
451
|
+
const reqPort = u.port || (u.protocol === "https:" ? "443" : "80");
|
|
452
|
+
const basePort = base.port || (base.protocol === "https:" ? "443" : "80");
|
|
453
|
+
if (reqPort !== basePort) return value;
|
|
454
|
+
const path = u.pathname || "/";
|
|
455
|
+
if (path === prefix || path.indexOf(prefix + "/") === 0) return u.toString();
|
|
456
|
+
if (path.indexOf("/p/") === 0) return u.toString();
|
|
457
|
+
u.pathname = path === "/" ? prefix : prefix + path;
|
|
458
|
+
return u.toString();
|
|
459
|
+
} catch {
|
|
460
|
+
return value;
|
|
415
461
|
}
|
|
416
462
|
}
|
|
417
463
|
|
|
464
|
+
export function rewriteShareOriginPaths(text, publicBase) {
|
|
465
|
+
if (!text || !publicBase) return text;
|
|
466
|
+
let origin = "";
|
|
467
|
+
try {
|
|
468
|
+
origin = new URL(String(publicBase).replace(/\/$/, "")).origin;
|
|
469
|
+
} catch {
|
|
470
|
+
return text;
|
|
471
|
+
}
|
|
472
|
+
if (!origin) return text;
|
|
473
|
+
const originEsc = origin.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
474
|
+
const originSlashEsc = origin
|
|
475
|
+
.replace(/\//g, "\\/")
|
|
476
|
+
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
477
|
+
let out = String(text);
|
|
478
|
+
out = out.replace(new RegExp(originEsc + "(?:/[^\\s'\"<>\\\\]*)?", "gi"), function (matched) {
|
|
479
|
+
return prefixShareOriginUrl(matched, publicBase);
|
|
480
|
+
});
|
|
481
|
+
out = out.replace(
|
|
482
|
+
new RegExp(originSlashEsc + "(?:\\\\/[^\\s'\"<>]*)?", "gi"),
|
|
483
|
+
function (matched) {
|
|
484
|
+
const unescaped = matched.replace(/\\\//g, "/");
|
|
485
|
+
return prefixShareOriginUrl(unescaped, publicBase).replace(/\//g, "\\/");
|
|
486
|
+
}
|
|
487
|
+
);
|
|
488
|
+
return out;
|
|
489
|
+
}
|
|
490
|
+
|
|
418
491
|
function rewriteLocation(value, publicBase, port, portUrls) {
|
|
419
492
|
if (!publicBase && !(portUrls && Object.keys(portUrls).length)) return value;
|
|
420
493
|
try {
|
|
421
494
|
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
|
-
|
|
495
|
+
if (isLoopbackHost(u.hostname)) {
|
|
496
|
+
const locPort = Number(u.port) || Number(port) || 0;
|
|
497
|
+
const mapped =
|
|
498
|
+
mappedUrlForPort(portUrls, locPort) ||
|
|
499
|
+
String(publicBase || "").replace(/\/$/, "");
|
|
500
|
+
if (!mapped) return value;
|
|
501
|
+
const prefix = proxyPathPrefix(mapped);
|
|
502
|
+
let path = u.pathname || "/";
|
|
503
|
+
if (prefix && (path === prefix || path.startsWith(`${prefix}/`))) {
|
|
504
|
+
path = path.slice(prefix.length) || "/";
|
|
505
|
+
}
|
|
506
|
+
if (path === "/") return `${mapped}${u.search}${u.hash}`;
|
|
507
|
+
return `${mapped}${path}${u.search}${u.hash}`;
|
|
434
508
|
}
|
|
435
|
-
|
|
436
|
-
return
|
|
509
|
+
const fromPorts = rewriteMappedLocalUrls(String(value), portUrls);
|
|
510
|
+
if (fromPorts !== String(value)) return fromPorts;
|
|
511
|
+
if (publicBase) return prefixShareOriginUrl(String(value), publicBase);
|
|
437
512
|
} catch {
|
|
438
513
|
/* keep */
|
|
439
514
|
}
|
|
@@ -911,6 +986,7 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls) {
|
|
|
911
986
|
if (path === p || path.indexOf(p + "/") === 0) return collapse(path);
|
|
912
987
|
var root = p.slice(0, p.lastIndexOf("/"));
|
|
913
988
|
if (root && (path === root || path.indexOf(root + "/") === 0)) return path;
|
|
989
|
+
if (path.indexOf("/p/") === 0) return path;
|
|
914
990
|
if (path === "/api/v1" || path.indexOf("/api/v1/") === 0) return path;
|
|
915
991
|
return collapse(p + path);
|
|
916
992
|
}
|
|
@@ -954,25 +1030,7 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls) {
|
|
|
954
1030
|
return v;
|
|
955
1031
|
}
|
|
956
1032
|
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;
|
|
1033
|
+
return addNav(v);
|
|
976
1034
|
}
|
|
977
1035
|
function mapSel(sel) {
|
|
978
1036
|
if (typeof sel !== "string") return sel;
|
|
@@ -1156,17 +1214,47 @@ function shareServiceWorkerMain(portMap, tokenRoot, rewriteMappedLocalUrls) {
|
|
|
1156
1214
|
return text;
|
|
1157
1215
|
}
|
|
1158
1216
|
}
|
|
1159
|
-
function
|
|
1217
|
+
function appPrefixFromHref(href) {
|
|
1218
|
+
try {
|
|
1219
|
+
var u = new URL(String(href || ""));
|
|
1220
|
+
var parts = u.pathname.split("/").filter(Boolean);
|
|
1221
|
+
if (parts[0] === "p" && parts[1] && parts[2]) {
|
|
1222
|
+
return "/" + parts[0] + "/" + parts[1] + "/" + parts[2];
|
|
1223
|
+
}
|
|
1224
|
+
} catch (e) {}
|
|
1225
|
+
return "";
|
|
1226
|
+
}
|
|
1227
|
+
function prefixWith(url, prefix) {
|
|
1228
|
+
if (!prefix) return url;
|
|
1229
|
+
try {
|
|
1230
|
+
var u = new URL(url, self.location.href);
|
|
1231
|
+
if (u.origin !== self.location.origin) return url;
|
|
1232
|
+
if (u.pathname.indexOf("/p/") === 0) return url;
|
|
1233
|
+
if (u.pathname === prefix || u.pathname.indexOf(prefix + "/") === 0) return url;
|
|
1234
|
+
u.pathname = u.pathname === "/" ? prefix : prefix + u.pathname;
|
|
1235
|
+
return u.toString();
|
|
1236
|
+
} catch (e) {}
|
|
1237
|
+
return url;
|
|
1238
|
+
}
|
|
1239
|
+
function mapUrl(v, prefix) {
|
|
1160
1240
|
if (typeof v !== "string" || !v) return v;
|
|
1161
1241
|
if (v.indexOf("/__mp/") !== -1) return v;
|
|
1162
|
-
return rewriteText(v);
|
|
1242
|
+
return prefixWith(rewriteText(v), prefix);
|
|
1163
1243
|
}
|
|
1164
1244
|
self.addEventListener("fetch", function (event) {
|
|
1165
1245
|
var req = event.request;
|
|
1166
1246
|
if (req.url.indexOf("/__mp/") !== -1) return;
|
|
1167
1247
|
event.respondWith(
|
|
1168
1248
|
(async function () {
|
|
1169
|
-
var
|
|
1249
|
+
var client = null;
|
|
1250
|
+
try {
|
|
1251
|
+
if (event.clientId) client = await self.clients.get(event.clientId);
|
|
1252
|
+
} catch (e) {}
|
|
1253
|
+
var prefix =
|
|
1254
|
+
appPrefixFromHref(client && client.url) ||
|
|
1255
|
+
appPrefixFromHref(req.referrer) ||
|
|
1256
|
+
appPrefixFromHref(self.registration && self.registration.scope);
|
|
1257
|
+
var url = mapUrl(req.url, prefix);
|
|
1170
1258
|
var method = req.method;
|
|
1171
1259
|
var headers = new Headers(req.headers);
|
|
1172
1260
|
var body;
|
|
@@ -1195,15 +1283,24 @@ function shareServiceWorkerMain(portMap, tokenRoot, rewriteMappedLocalUrls) {
|
|
|
1195
1283
|
} catch (e) {}
|
|
1196
1284
|
}
|
|
1197
1285
|
var res = await fetch(new Request(url, init));
|
|
1286
|
+
var outHeaders = new Headers(res.headers);
|
|
1287
|
+
var loc = outHeaders.get("location");
|
|
1288
|
+
if (loc) outHeaders.set("location", mapUrl(loc, prefix));
|
|
1198
1289
|
var resType = res.headers.get("content-type") || "";
|
|
1199
1290
|
if (
|
|
1200
1291
|
!/html|javascript|ecmascript|json|css|xml|svg|text|urlencoded/.test(resType)
|
|
1201
1292
|
) {
|
|
1293
|
+
if (loc && outHeaders.get("location") !== loc) {
|
|
1294
|
+
return new Response(res.body, {
|
|
1295
|
+
status: res.status,
|
|
1296
|
+
statusText: res.statusText,
|
|
1297
|
+
headers: outHeaders,
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1202
1300
|
return res;
|
|
1203
1301
|
}
|
|
1204
1302
|
var resText = await res.text();
|
|
1205
1303
|
var mapped = rewriteText(resText);
|
|
1206
|
-
var outHeaders = new Headers(res.headers);
|
|
1207
1304
|
outHeaders.delete("content-length");
|
|
1208
1305
|
outHeaders.delete("content-encoding");
|
|
1209
1306
|
return new Response(mapped, {
|
|
@@ -1305,6 +1402,13 @@ export function processShareHttpResponse(opts) {
|
|
|
1305
1402
|
mutated = true;
|
|
1306
1403
|
}
|
|
1307
1404
|
}
|
|
1405
|
+
if (publicBase && rewriteTextBody) {
|
|
1406
|
+
const prefixed = rewriteShareOriginPaths(text, publicBase);
|
|
1407
|
+
if (prefixed !== text) {
|
|
1408
|
+
text = prefixed;
|
|
1409
|
+
mutated = true;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1308
1412
|
if (aiPublicBase) {
|
|
1309
1413
|
const withShareUrls = rewriteLocalSidecarUrls(text, aiPublicBase, publicBase);
|
|
1310
1414
|
if (withShareUrls !== text) {
|