@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maintainer-pro/ai-bridge",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Local bridge daemon that pairs a machine to Maintainer Pro and configures multiple client sandboxes.",
5
5
  "keywords": [
6
6
  "maintainer-pro",
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();
@@ -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 || !portUrls || !Object.keys(portUrls).length) return 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
- const text = buf.toString("utf8");
369
- const next = rewriteMappedLocalUrls(text, portUrls);
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 (!(portUrls && Object.keys(portUrls).length)) return;
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;
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
- headers[key] = rewriteMappedLocalUrls(headers[key], portUrls);
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 (!isLoopbackHost(u.hostname)) {
423
- return rewriteMappedLocalUrls(String(value), portUrls);
424
- }
425
- const locPort = Number(u.port) || Number(port) || 0;
426
- const mapped =
427
- mappedUrlForPort(portUrls, locPort) ||
428
- String(publicBase || "").replace(/\/$/, "");
429
- if (!mapped) return value;
430
- const prefix = proxyPathPrefix(mapped);
431
- let path = u.pathname || "/";
432
- if (prefix && (path === prefix || path.startsWith(`${prefix}/`))) {
433
- path = path.slice(prefix.length) || "/";
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
- if (path === "/") return `${mapped}${u.search}${u.hash}`;
436
- return `${mapped}${path}${u.search}${u.hash}`;
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
- if (typeof v !== "string" || !v) return v;
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 mapUrl(v) {
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 url = mapUrl(req.url);
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) {