@maintainer-pro/ai-bridge 0.1.20 → 0.1.22

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.20",
3
+ "version": "0.1.22",
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",
@@ -28,7 +28,7 @@
28
28
  "node": ">=22"
29
29
  },
30
30
  "dependencies": {
31
- "@maintainer-pro/ai-cli": "^0.1.11",
31
+ "@maintainer-pro/ai-cli": "^0.1.12",
32
32
  "@maintainer-pro/ai-server": "^0.1.7"
33
33
  }
34
34
  }
package/src/daemon.mjs CHANGED
@@ -257,7 +257,21 @@ function loadConfig() {
257
257
  const file = configPath();
258
258
  if (!fs.existsSync(file)) return null;
259
259
  try {
260
- return JSON.parse(fs.readFileSync(file, "utf8"));
260
+ const cfg = JSON.parse(fs.readFileSync(file, "utf8"));
261
+ let dirty = false;
262
+ for (const ws of cfg.workspaces || []) {
263
+ const before = JSON.stringify(ws.hostApps || []);
264
+ const beforeProxy = JSON.stringify(ws.proxy || null);
265
+ stripAiServerFromWorkspace(ws);
266
+ if (
267
+ JSON.stringify(ws.hostApps || []) !== before ||
268
+ JSON.stringify(ws.proxy || null) !== beforeProxy
269
+ ) {
270
+ dirty = true;
271
+ }
272
+ }
273
+ if (dirty) saveConfig(cfg);
274
+ return cfg;
261
275
  } catch {
262
276
  return null;
263
277
  }
@@ -434,7 +448,7 @@ async function resolveWorkspaceHostApps(ws, opts = {}) {
434
448
  const match = previous.find((row) => row && row.id === app.id);
435
449
  return match ? { ...app, host: match.host === true || app.host === true } : app;
436
450
  });
437
- ws.hostApps = result.apps;
451
+ ws.hostApps = listedHostApps(result.apps);
438
452
  if (opts.cfg) persistWorkspaceEntry(opts.cfg, ws);
439
453
  const summary = result.apps
440
454
  .map((app) => `${app.name}:${app.port}`)
@@ -455,19 +469,10 @@ async function resolveWorkspaceHostApps(ws, opts = {}) {
455
469
  activity(
456
470
  ws.sandboxId,
457
471
  "warn",
458
- `port detect failed (${message}) — using AI server ${Number(ws.port) || 3100} so setup can continue`
472
+ `port detect failed (${message}) — no host apps listed yet`
459
473
  );
460
474
  if (!Array.isArray(ws.hostApps) || !ws.hostApps.length) {
461
- ws.hostApps = [
462
- {
463
- id: "ai-server",
464
- name: "AI server",
465
- role: "ai-server",
466
- port: Number(ws.port) || 3100,
467
- source: "default",
468
- locked: true,
469
- },
470
- ];
475
+ ws.hostApps = [];
471
476
  }
472
477
  return {
473
478
  apps: ws.hostApps,
@@ -763,8 +768,31 @@ function isLocalAppUrl(url) {
763
768
  }
764
769
  }
765
770
 
771
+ function isAiServerApp(app) {
772
+ return Boolean(app && (app.role === "ai-server" || app.id === "ai-server"));
773
+ }
774
+
775
+ function listedHostApps(apps) {
776
+ return (Array.isArray(apps) ? apps : []).filter((app) => !isAiServerApp(app));
777
+ }
778
+
779
+ function stripAiServerFromWorkspace(ws) {
780
+ if (!ws || typeof ws !== "object") return ws;
781
+ if (Array.isArray(ws.hostApps)) ws.hostApps = listedHostApps(ws.hostApps);
782
+ if (ws.proxy && typeof ws.proxy === "object") {
783
+ if (ws.proxy.slugs && typeof ws.proxy.slugs === "object") {
784
+ delete ws.proxy.slugs["ai-server"];
785
+ }
786
+ if (ws.proxy.urls && typeof ws.proxy.urls === "object") {
787
+ delete ws.proxy.urls["ai-server"];
788
+ }
789
+ }
790
+ return ws;
791
+ }
792
+
766
793
  function persistWorkspaceEntry(cfg, ws) {
767
794
  if (!cfg || !ws?.sandboxId) return;
795
+ stripAiServerFromWorkspace(ws);
768
796
  cfg.workspaces = cfg.workspaces || [];
769
797
  const index = cfg.workspaces.findIndex((row) => row.sandboxId === ws.sandboxId);
770
798
  if (index >= 0) cfg.workspaces[index] = { ...cfg.workspaces[index], ...ws };
@@ -1001,16 +1029,7 @@ function probeEmbedConfig(url, timeoutMs = 2500) {
1001
1029
 
1002
1030
  function sanitizeAiServerPort(ws) {
1003
1031
  if (!ws || typeof ws !== "object") return;
1004
- const apps = Array.isArray(ws.hostApps) ? ws.hostApps : [];
1005
- for (const app of apps) {
1006
- if (
1007
- app &&
1008
- (app.role === "ai-server" || app.id === "ai-server") &&
1009
- isAdminListenPort(app.port)
1010
- ) {
1011
- app.port = 3100;
1012
- }
1013
- }
1032
+ if (Array.isArray(ws.hostApps)) ws.hostApps = listedHostApps(ws.hostApps);
1014
1033
  if (isAdminListenPort(ws.port)) ws.port = 3100;
1015
1034
  }
1016
1035
 
@@ -1304,10 +1323,6 @@ async function reconcileWorkspacePresence(ws, cfg, opts = {}) {
1304
1323
  // 3. Host + CORS origins for Maintainer Pro
1305
1324
  const host = workspaceHostReport(ws);
1306
1325
  const uiShare = proxyUrlForApp(ws, hostAppOf(ws) || { id: "ui", role: "ui", host: true });
1307
- const aiShare = proxyUrlForApp(ws, {
1308
- id: "ai-server",
1309
- role: "ai-server",
1310
- });
1311
1326
  if (uiShare) {
1312
1327
  host.appUrl = uiShare;
1313
1328
  } else if (probe.running) {
@@ -1316,12 +1331,9 @@ async function reconcileWorkspacePresence(ws, cfg, opts = {}) {
1316
1331
  );
1317
1332
  if (ui?.port) {
1318
1333
  host.appUrl = `http://localhost:${ui.port}`;
1319
- } else if (probe.chatUp) {
1320
- host.appUrl = `http://localhost:${probe.chatPort}`;
1321
1334
  }
1322
1335
  }
1323
1336
  if (uiShare && !host.origins.includes(uiShare)) host.origins.push(uiShare);
1324
- if (aiShare && !host.origins.includes(aiShare)) host.origins.push(aiShare);
1325
1337
  const proxyOrigin = String(ws?.proxy?.origin || "").replace(/\/$/, "");
1326
1338
  if (proxyOrigin && !host.origins.includes(proxyOrigin)) {
1327
1339
  host.origins.push(proxyOrigin);
@@ -1491,7 +1503,7 @@ function uniqueProxySlugs(apps) {
1491
1503
  const used = new Set();
1492
1504
  /** @type {Record<string, string>} */
1493
1505
  const slugs = {};
1494
- for (const app of apps || []) {
1506
+ for (const app of listedHostApps(apps)) {
1495
1507
  let slug = proxySlugForApp(app);
1496
1508
  if (!slug || !app?.id) continue;
1497
1509
  if (used.has(slug)) {
@@ -1512,7 +1524,7 @@ function uniqueProxySlugs(apps) {
1512
1524
  }
1513
1525
 
1514
1526
  function sharePortUrls(ws) {
1515
- const apps = Array.isArray(ws?.hostApps) ? ws.hostApps : [];
1527
+ const apps = listedHostApps(ws?.hostApps);
1516
1528
  /** @type {Record<string, string>} */
1517
1529
  const out = {};
1518
1530
  for (const app of apps) {
@@ -1547,8 +1559,10 @@ function applyAssignedProxy(ws, remote, cfg) {
1547
1559
  }
1548
1560
 
1549
1561
  function appsFrom(ws, remote) {
1550
- if (Array.isArray(ws?.hostApps) && ws.hostApps.length) return ws.hostApps;
1551
- if (Array.isArray(remote?.hostApps)) return remote.hostApps;
1562
+ if (Array.isArray(ws?.hostApps) && ws.hostApps.length) {
1563
+ return listedHostApps(ws.hostApps);
1564
+ }
1565
+ if (Array.isArray(remote?.hostApps)) return listedHostApps(remote.hostApps);
1552
1566
  return [];
1553
1567
  }
1554
1568
 
@@ -1569,10 +1583,9 @@ function proxyUrlForApp(ws, app) {
1569
1583
  }
1570
1584
 
1571
1585
  function jobsFromHostApps(ws, filter = {}) {
1572
- const apps = Array.isArray(ws.hostApps) ? ws.hostApps : [];
1586
+ const apps = listedHostApps(ws.hostApps);
1573
1587
  const hostId = hostAppOf(ws)?.id;
1574
1588
  return apps
1575
- .filter((app) => app && app.role !== "ai-server")
1576
1589
  .filter((app) => (filter.appId ? app.id === filter.appId : true))
1577
1590
  .map((app) => {
1578
1591
  const port = Number(app.port) || 3000;
@@ -1622,7 +1635,7 @@ async function probeRunningApps(ws, timeoutMs = 2500) {
1622
1635
  appId: job.appId || job.role,
1623
1636
  });
1624
1637
  }
1625
- const listed = Array.isArray(ws.hostApps) ? ws.hostApps : [];
1638
+ const listed = listedHostApps(ws.hostApps);
1626
1639
  const hostApps = await Promise.all(
1627
1640
  listed.map(async (app) => {
1628
1641
  const port = Number(app.port) || 0;
@@ -1645,7 +1658,7 @@ async function probeRunningApps(ws, timeoutMs = 2500) {
1645
1658
  chatPort: chat.port,
1646
1659
  hosts,
1647
1660
  hostApps,
1648
- running: chat.up || hosts.some((host) => host.up) || hostApps.some((app) => app.running),
1661
+ running: hosts.some((host) => host.up) || hostApps.some((app) => app.running),
1649
1662
  };
1650
1663
  }
1651
1664
 
@@ -1662,9 +1675,7 @@ async function restoreHostsAfterReconnect(cfg) {
1662
1675
  .join(" ");
1663
1676
  if (status.appsRunning) {
1664
1677
  log(
1665
- `found running apps for ${label}: chat=${status.probe.chatPort}${
1666
- status.probe.chatUp ? "(up)" : "(down)"
1667
- }${hostSummary ? ` ${hostSummary}` : ""} share=${
1678
+ `found running apps for ${label}: ${hostSummary || "(none)"} share=${
1668
1679
  status.host.appUrl || "(none)"
1669
1680
  } origins=${
1670
1681
  status.host.origins.join(",") || "none"
@@ -1703,10 +1714,7 @@ async function prepareWorkspaceLaunch(ws, cfg, reserved) {
1703
1714
  return { aiPort, jobs: [] };
1704
1715
  }
1705
1716
  log(`ports pick ${label} in ${folder}`);
1706
- const listedAi = Array.isArray(ws.hostApps)
1707
- ? ws.hostApps.find((app) => app.role === "ai-server" || app.id === "ai-server")
1708
- : null;
1709
- const preferredRaw = Number(listedAi?.port || ws.port) || 3100;
1717
+ const preferredRaw = Number(ws.port) || 3100;
1710
1718
  const preferredAi = isAdminListenPort(preferredRaw) ? 3100 : preferredRaw;
1711
1719
  const aiUp = await isChatServerOnPort(preferredAi);
1712
1720
  const aiPort = aiUp
@@ -4679,12 +4687,11 @@ async function runActions(cfg, actions) {
4679
4687
  String(action.payload.folderPathRemaining || remaining[0] || ws.folderPath);
4680
4688
  ws.folderPath = primary;
4681
4689
  ws.extraFolders = remaining.filter((folder) => !sameFolder(folder, primary));
4682
- ws.hostApps = (Array.isArray(ws.hostApps) ? ws.hostApps : []).filter(
4690
+ ws.hostApps = listedHostApps(
4691
+ Array.isArray(ws.hostApps) ? ws.hostApps : []
4692
+ ).filter(
4683
4693
  (app) =>
4684
- !app ||
4685
- app.role === "ai-server" ||
4686
- !app.folderPath ||
4687
- !sameFolder(app.folderPath, removed)
4694
+ !app.folderPath || !sameFolder(app.folderPath, removed)
4688
4695
  );
4689
4696
  persistWorkspaceEntry(cfg, ws);
4690
4697
  result = {
@@ -274,6 +274,33 @@ function mappedUrlForPort(portUrls, port) {
274
274
  return dest ? String(dest).replace(/\/$/, "") : "";
275
275
  }
276
276
 
277
+ function backendShareBase(portUrls) {
278
+ if (!portUrls || typeof portUrls !== "object") return "";
279
+ for (const dest of Object.values(portUrls)) {
280
+ const url = String(dest || "").replace(/\/$/, "");
281
+ if (!url) continue;
282
+ try {
283
+ const slug = new URL(url).pathname.split("/").filter(Boolean)[2] || "";
284
+ if (slug === "backend" || slug.startsWith("backend-")) return url;
285
+ } catch {
286
+ if (/\/backend(?:-[^/]+)?$/i.test(url)) return url;
287
+ }
288
+ }
289
+ return "";
290
+ }
291
+
292
+ function shareBaseForPath(path, publicBase, portUrls) {
293
+ const pathname = String(path || "").split("?")[0] || "/";
294
+ if (
295
+ isShareAssetPath(pathname) ||
296
+ pathname.startsWith("/_next/") ||
297
+ pathname.startsWith("/@")
298
+ ) {
299
+ return publicBase;
300
+ }
301
+ return backendShareBase(portUrls) || publicBase;
302
+ }
303
+
277
304
  function isProbablyUtf8Text(buf) {
278
305
  if (!Buffer.isBuffer(buf) || !buf.length || buf.length > 8 * 1024 * 1024) {
279
306
  return false;
@@ -347,7 +374,7 @@ export function rewriteMappedLocalUrls(text, portUrls) {
347
374
  * @param {Record<string, string>} headers
348
375
  * @param {Record<string, string> | null | undefined} portUrls
349
376
  */
350
- export function rewriteShareRequestBody(body, headers, portUrls, publicBase) {
377
+ export function rewriteShareRequestBody(body, headers, portUrls, _publicBase) {
351
378
  if (!body) return body;
352
379
  const buf = Buffer.isBuffer(body) ? body : Buffer.from(body);
353
380
  if (!buf.length) return buf;
@@ -357,7 +384,6 @@ export function rewriteShareRequestBody(body, headers, portUrls, publicBase) {
357
384
  if (portUrls && Object.keys(portUrls).length) {
358
385
  next = rewriteMappedLocalUrls(next, portUrls);
359
386
  }
360
- if (publicBase) next = rewriteShareOriginPaths(next, publicBase);
361
387
  if (next === text) return buf;
362
388
  return Buffer.from(next, "utf8");
363
389
  }
@@ -411,7 +437,7 @@ function rewriteHeaderUrls(headers, publicBase, port, portUrls) {
411
437
  if (lower === "set-cookie" || lower === "content-length" || lower === "content-encoding") {
412
438
  continue;
413
439
  }
414
- headers[key] = rewriteShareOriginPaths(headers[key], publicBase);
440
+ headers[key] = rewriteShareOriginPaths(headers[key], publicBase, portUrls);
415
441
  }
416
442
  }
417
443
  }
@@ -449,7 +475,7 @@ export function prefixShareOriginUrl(value, publicBase) {
449
475
  }
450
476
  }
451
477
 
452
- export function rewriteShareOriginPaths(text, publicBase) {
478
+ export function rewriteShareOriginPaths(text, publicBase, portUrls) {
453
479
  if (!text || !publicBase) return text;
454
480
  let origin = "";
455
481
  try {
@@ -462,15 +488,24 @@ export function rewriteShareOriginPaths(text, publicBase) {
462
488
  const originSlashEsc = origin
463
489
  .replace(/\//g, "\\/")
464
490
  .replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
491
+ const baseFor = function (matched) {
492
+ let path = "/";
493
+ try {
494
+ path = new URL(matched).pathname || "/";
495
+ } catch {
496
+ /* keep */
497
+ }
498
+ return shareBaseForPath(path, publicBase, portUrls);
499
+ };
465
500
  let out = String(text);
466
501
  out = out.replace(new RegExp(originEsc + "/(?!p/)[^\\s'\"<>\\\\]+", "gi"), function (matched) {
467
- return prefixShareOriginUrl(matched, publicBase);
502
+ return prefixShareOriginUrl(matched, baseFor(matched));
468
503
  });
469
504
  out = out.replace(
470
505
  new RegExp(originSlashEsc + "\\\\/(?!p/)[^\\s'\"<>]+", "gi"),
471
506
  function (matched) {
472
507
  const unescaped = matched.replace(/\\\//g, "/");
473
- return prefixShareOriginUrl(unescaped, publicBase).replace(/\//g, "\\/");
508
+ return prefixShareOriginUrl(unescaped, baseFor(unescaped)).replace(/\//g, "\\/");
474
509
  }
475
510
  );
476
511
  return out;
@@ -496,7 +531,18 @@ function rewriteLocation(value, publicBase, port, portUrls) {
496
531
  }
497
532
  const fromPorts = rewriteMappedLocalUrls(String(value), portUrls);
498
533
  if (fromPorts !== String(value)) return fromPorts;
499
- if (publicBase) return prefixShareOriginUrl(String(value), publicBase);
534
+ if (publicBase) {
535
+ let path = "/";
536
+ try {
537
+ path = new URL(String(value)).pathname || "/";
538
+ } catch {
539
+ /* keep */
540
+ }
541
+ return prefixShareOriginUrl(
542
+ String(value),
543
+ shareBaseForPath(path, publicBase, portUrls)
544
+ );
545
+ }
500
546
  } catch {
501
547
  /* keep */
502
548
  }
@@ -809,12 +855,11 @@ function prefixRootPaths(body, publicBase, contentType = "") {
809
855
  let out = body;
810
856
  if (
811
857
  html &&
858
+ isViteDocument(out) &&
812
859
  /<head[\s>]/i.test(out) &&
813
- !/<base\s/i.test(out) &&
814
- !isNextDocument(out)
860
+ !/<base\s/i.test(out)
815
861
  ) {
816
- const viteBase = isViteDocument(out) ? inferViteBase(out) : "/";
817
- const href = joinProxyAndViteBase(pathPrefix, viteBase);
862
+ const href = joinProxyAndViteBase(pathPrefix, inferViteBase(out));
818
863
  out = out.replace(/<head([^>]*)>/i, `<head$1><base href="${href}">`);
819
864
  }
820
865
  if (html) {
@@ -871,6 +916,12 @@ function isScriptRequestPath(path) {
871
916
  return /\.(?:m?js|cjs)$/.test(p);
872
917
  }
873
918
 
919
+ function isScriptOrJsonBody(headers, path) {
920
+ const mime = contentMime(headerGet(headers, "content-type"));
921
+ if (/javascript|ecmascript|json/.test(mime)) return true;
922
+ return isScriptRequestPath(path);
923
+ }
924
+
874
925
  function rewriteLocalSidecarUrls(html, aiPublicBase, uiPublicBase) {
875
926
  const ai = String(aiPublicBase || "").replace(/\/$/, "");
876
927
  const ui = String(uiPublicBase || "").replace(/\/$/, "");
@@ -968,6 +1019,14 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls) {
968
1019
  )
969
1020
  );
970
1021
  }
1022
+ function loopback(h) {
1023
+ h = String(h || "")
1024
+ .replace(/^\[|\]$/g, "")
1025
+ .toLowerCase();
1026
+ return (
1027
+ h === "localhost" || h === "127.0.0.1" || h === "::1" || h === "0.0.0.0"
1028
+ );
1029
+ }
971
1030
  function prefixPath(path) {
972
1031
  if (typeof path !== "string" || !path) return path;
973
1032
  if (path.charAt(0) === "/" && path.charAt(1) !== "/") {
@@ -1005,12 +1064,29 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls) {
1005
1064
  } catch (e) {}
1006
1065
  return v;
1007
1066
  }
1067
+ function backendBase() {
1068
+ var map = portMap || {};
1069
+ var keys = Object.keys(map);
1070
+ for (var i = 0; i < keys.length; i++) {
1071
+ var url = String(map[keys[i]] || "").replace(/\/$/, "");
1072
+ if (!url) continue;
1073
+ try {
1074
+ var slug = new URL(url).pathname.split("/").filter(Boolean)[2] || "";
1075
+ if (slug === "backend" || slug.indexOf("backend-") === 0) return url;
1076
+ } catch (e) {
1077
+ if (/\/backend(?:-[^/]+)?$/i.test(url)) return url;
1078
+ }
1079
+ }
1080
+ return "";
1081
+ }
1008
1082
  function addNav(v) {
1009
1083
  if (typeof v !== "string" || !v) return v;
1010
- v = mapLocal(v);
1084
+ var mapped = mapLocal(v);
1085
+ if (mapped !== v) return mapped;
1011
1086
  if (v.charAt(0) === "/" && v.charAt(1) !== "/") return prefixPath(v);
1012
1087
  try {
1013
1088
  var nav = new URL(v, location.href);
1089
+ if (!loopback(nav.hostname)) return v;
1014
1090
  if (nav.origin !== location.origin) return v;
1015
1091
  if (nav.pathname === "/" || nav.pathname === "") return v;
1016
1092
  nav.pathname = prefixPath(nav.pathname);
@@ -1019,6 +1095,46 @@ function shareProxyShim(p, portMap, rewriteMappedLocalUrls) {
1019
1095
  return v;
1020
1096
  }
1021
1097
  function addNet(v) {
1098
+ if (typeof v !== "string" || !v) return v;
1099
+ var mapped = mapLocal(v);
1100
+ if (mapped !== v) return mapped;
1101
+ var backend = backendBase();
1102
+ var path = "";
1103
+ var search = "";
1104
+ var hash = "";
1105
+ var abs = null;
1106
+ try {
1107
+ if (/^(https?:|wss?:)\/\//i.test(v)) {
1108
+ abs = new URL(v);
1109
+ var host = String(abs.hostname || "")
1110
+ .replace(/^\[|\]$/g, "")
1111
+ .toLowerCase();
1112
+ if (!loopback(host) && host !== String(location.hostname || "").toLowerCase()) {
1113
+ return v;
1114
+ }
1115
+ path = abs.pathname || "/";
1116
+ search = abs.search || "";
1117
+ hash = abs.hash || "";
1118
+ } else if (v.charAt(0) === "/" && v.charAt(1) !== "/") {
1119
+ var rel = new URL(v, location.href);
1120
+ path = rel.pathname || "/";
1121
+ search = rel.search || "";
1122
+ hash = rel.hash || "";
1123
+ } else {
1124
+ return addNav(v);
1125
+ }
1126
+ } catch (e) {
1127
+ return addNav(v);
1128
+ }
1129
+ if (path === "/api/v1" || path.indexOf("/api/v1/") === 0) return v;
1130
+ if (path.indexOf("/p/") === 0) return v;
1131
+ if (path === "/" || path === "") return addNav(v);
1132
+ if (isAsset(path)) return addNav(v);
1133
+ if (backend) {
1134
+ var dest = backend + path + search + hash;
1135
+ if (abs && /^ws/i.test(abs.protocol)) dest = dest.replace(/^http/i, "ws");
1136
+ return dest;
1137
+ }
1022
1138
  return addNav(v);
1023
1139
  }
1024
1140
  function mapSel(sel) {
@@ -1213,13 +1329,62 @@ function shareServiceWorkerMain(portMap, tokenRoot, rewriteMappedLocalUrls) {
1213
1329
  } catch (e) {}
1214
1330
  return "";
1215
1331
  }
1332
+ function loopback(h) {
1333
+ h = String(h || "")
1334
+ .replace(/^\[|\]$/g, "")
1335
+ .toLowerCase();
1336
+ return (
1337
+ h === "localhost" || h === "127.0.0.1" || h === "::1" || h === "0.0.0.0"
1338
+ );
1339
+ }
1340
+ function backendBase() {
1341
+ var map = portMap || {};
1342
+ var keys = Object.keys(map);
1343
+ for (var i = 0; i < keys.length; i++) {
1344
+ var url = String(map[keys[i]] || "").replace(/\/$/, "");
1345
+ if (!url) continue;
1346
+ try {
1347
+ var slug = new URL(url).pathname.split("/").filter(Boolean)[2] || "";
1348
+ if (slug === "backend" || slug.indexOf("backend-") === 0) return url;
1349
+ } catch (e) {
1350
+ if (/\/backend(?:-[^/]+)?$/i.test(url)) return url;
1351
+ }
1352
+ }
1353
+ return "";
1354
+ }
1216
1355
  function prefixWith(url, prefix) {
1217
1356
  if (!prefix) return url;
1218
1357
  try {
1219
- var u = new URL(url, self.location.href);
1358
+ var backend = backendBase();
1359
+ var parsed = new URL(url, self.location.href);
1360
+ var host = String(parsed.hostname || "")
1361
+ .replace(/^\[|\]$/g, "")
1362
+ .toLowerCase();
1363
+ var pageHost = String(self.location.hostname || "")
1364
+ .replace(/^\[|\]$/g, "")
1365
+ .toLowerCase();
1366
+ if (/^(https?:|wss?:)\/\//i.test(url) && !loopback(host) && host !== pageHost) {
1367
+ return url;
1368
+ }
1369
+ var path = parsed.pathname || "/";
1370
+ if (path === "/api/v1" || path.indexOf("/api/v1/") === 0) return url;
1371
+ if (path.indexOf("/p/") === 0) return url;
1372
+ if (
1373
+ backend &&
1374
+ path !== "/" &&
1375
+ path !== "" &&
1376
+ !/\/(?:_next\/|__nextjs_|@vite(?:\/|$)|@react-refresh(?:\/|$)|@fs\/|@id\/|node_modules\/)/.test(
1377
+ path
1378
+ ) &&
1379
+ !/\.(?:m?[jt]sx?|cjs|mjs|css|scss|sass|less|map|wasm|vue|svelte)$/i.test(path)
1380
+ ) {
1381
+ var dest = backend + path + (parsed.search || "") + (parsed.hash || "");
1382
+ if (/^ws/i.test(parsed.protocol)) dest = dest.replace(/^http/i, "ws");
1383
+ return dest;
1384
+ }
1385
+ var u = parsed;
1220
1386
  if (u.origin !== self.location.origin) return url;
1221
1387
  if (u.pathname === "/" || u.pathname === "") return url;
1222
- if (u.pathname.indexOf("/p/") === 0) return url;
1223
1388
  if (u.pathname === prefix || u.pathname.indexOf(prefix + "/") === 0) return url;
1224
1389
  u.pathname = u.pathname === "/" ? prefix : prefix + u.pathname;
1225
1390
  return u.toString();
@@ -1393,7 +1558,7 @@ export function processShareHttpResponse(opts) {
1393
1558
  }
1394
1559
  }
1395
1560
  if (publicBase && rewriteTextBody) {
1396
- const prefixed = rewriteShareOriginPaths(text, publicBase);
1561
+ const prefixed = rewriteShareOriginPaths(text, publicBase, portUrls);
1397
1562
  if (prefixed !== text) {
1398
1563
  text = prefixed;
1399
1564
  mutated = true;