@maintainer-pro/ai-bridge 0.1.14 → 0.1.15

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.14",
3
+ "version": "0.1.15",
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
@@ -23,7 +23,7 @@ import {
23
23
  lookupShareResponse,
24
24
  prepareShareHttpRequest,
25
25
  processShareHttpResponse,
26
- shouldRewriteBody,
26
+ shouldProcessShareResponse,
27
27
  } from "./share-rewrite.mjs";
28
28
 
29
29
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -2565,7 +2565,7 @@ function handleProxyHttpFromAdmin(msg) {
2565
2565
  }
2566
2566
  const status = res.statusCode || 502;
2567
2567
  applyShareCacheHeaders(outHeaders, path);
2568
- if (shouldRewriteBody(outHeaders)) {
2568
+ if (shouldProcessShareResponse(outHeaders)) {
2569
2569
  /** @type {Buffer[]} */
2570
2570
  const chunks = [];
2571
2571
  res.on("data", (chunk) => {
@@ -128,6 +128,11 @@ export function shouldRewriteBody(headers) {
128
128
  );
129
129
  }
130
130
 
131
+ /** Redirects have no Content-Type, but Location still has to stay under the share prefix. */
132
+ export function shouldProcessShareResponse(headers) {
133
+ return Boolean(headerGet(headers, "location")) || shouldRewriteBody(headers);
134
+ }
135
+
131
136
  function isImmutableAssetPath(path) {
132
137
  const p = String(path || "").split("?")[0] || "";
133
138
  return (
@@ -367,26 +372,65 @@ function viteDevBases(body) {
367
372
  return bases;
368
373
  }
369
374
 
370
- function viteAssetRoots(body) {
371
- const roots = new Set([
372
- "/@vite",
373
- "/@react-refresh",
374
- "/@fs",
375
- "/@id",
376
- "/node_modules/",
377
- "/src/",
378
- "/config.json",
379
- ]);
380
- const re =
381
- /["'`](\/[^"'`]*?)(?=\/(?:@vite|@react-refresh|@fs|@id|node_modules\/|src\/))/g;
382
- let match;
383
- while ((match = re.exec(body))) {
384
- const base = match[1];
385
- if (!base || base === "/") continue;
386
- roots.add(base.endsWith("/") ? base : `${base}/`);
375
+ function inferViteBase(body) {
376
+ const fromConst = viteDevBases(body)[0];
377
+ if (fromConst) return fromConst;
378
+ const text = String(body || "");
379
+ const match = text.match(
380
+ /["'`](\/[^"'`]*?)\/(?:@vite(?:\/client)?|@react-refresh)(?:["'`?]|$)/
381
+ );
382
+ if (match?.[1] && match[1] !== "/") {
383
+ return match[1].endsWith("/") ? match[1] : `${match[1]}/`;
387
384
  }
388
- for (const base of viteDevBases(body)) roots.add(base);
389
- return [...roots];
385
+ return "/";
386
+ }
387
+
388
+ /** Vite/Next module URLs that must stay on the share origin. App routes like /hub/settings are not assets. */
389
+ function isShareAssetPath(path) {
390
+ const p = String(path || "").split("?")[0];
391
+ if (!p.startsWith("/")) return false;
392
+ if (
393
+ /\/(?:_next\/|__nextjs_|@vite(?:\/|$)|@react-refresh(?:\/|$)|@fs\/|@id\/|node_modules\/)/.test(
394
+ p
395
+ )
396
+ ) {
397
+ return true;
398
+ }
399
+ if (/\/src\/[^/].+\.[A-Za-z0-9]+$/.test(p)) return true;
400
+ return /\.(?:m?[jt]sx?|cjs|mjs|css|scss|sass|less|map|wasm|vue|svelte)$/i.test(
401
+ p
402
+ );
403
+ }
404
+
405
+ function prefixQuotedAssetPaths(body, pathPrefix) {
406
+ if (!pathPrefix) return body;
407
+ return String(body).replace(/(["'`])(\/(?!\/)[^"'`]*)/g, (full, q, path) => {
408
+ if (alreadyPrefixed(path, pathPrefix)) return full;
409
+ if (!isShareAssetPath(path)) return full;
410
+ return `${q}${pathPrefix}${path}`;
411
+ });
412
+ }
413
+
414
+ function rewriteViteBaseLiterals(body, pathPrefix) {
415
+ let out = rewriteViteHmrClient(body, pathPrefix);
416
+ const bases = [...viteDevBases(body), inferViteBase(body)].filter(
417
+ (base, i, all) => base && base !== "/" && all.indexOf(base) === i
418
+ );
419
+ for (const viteBase of bases) {
420
+ const prefixed = joinProxyAndViteBase(pathPrefix, viteBase);
421
+ const withSlash = viteBase.endsWith("/") ? viteBase : `${viteBase}/`;
422
+ const escaped = withSlash.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
423
+ out = out.replace(
424
+ new RegExp(`(\\bconst base(?:\\$\\d+)?\\s*=\\s*)(["'])${escaped}\\2`, "g"),
425
+ `$1$2${prefixed}$2`
426
+ );
427
+ out = out.split(`BASE_URL:"${withSlash}"`).join(`BASE_URL:"${prefixed}"`);
428
+ out = out.split(`BASE_URL:'${withSlash}'`).join(`BASE_URL:'${prefixed}'`);
429
+ out = out
430
+ .split(`"BASE_URL":"${withSlash}"`)
431
+ .join(`"BASE_URL":"${prefixed}"`);
432
+ }
433
+ return out;
390
434
  }
391
435
 
392
436
  function joinProxyAndViteBase(pathPrefix, viteBase) {
@@ -502,10 +546,9 @@ function prefixRootPaths(body, publicBase, contentType = "") {
502
546
  mime === "application/xhtml+xml" ||
503
547
  (!mime && /<!doctype html|<html[\s>]/i.test(body)));
504
548
  const css = mime === "text/css" || (!isCode && !html && /css/.test(mime));
505
- const viteAndNext = ["/_next/", "/__nextjs_", ...viteAssetRoots(body)];
506
549
  if (!html && !css) {
507
- let code = rewriteViteHmrClient(body, pathPrefix);
508
- code = prefixQuotedRoots(code, pathPrefix, viteAndNext);
550
+ let code = rewriteViteBaseLiterals(body, pathPrefix);
551
+ code = prefixQuotedAssetPaths(code, pathPrefix);
509
552
  code = code.replace(
510
553
  /(?<![A-Za-z0-9])\/__nextjs_/g,
511
554
  `${pathPrefix}/__nextjs_`
@@ -520,10 +563,9 @@ function prefixRootPaths(body, publicBase, contentType = "") {
520
563
  !/<base\s/i.test(out) &&
521
564
  !isNextDocument(out)
522
565
  ) {
523
- out = out.replace(
524
- /<head([^>]*)>/i,
525
- `<head$1><base href="${pathPrefix}/">`
526
- );
566
+ const viteBase = isViteDocument(out) ? inferViteBase(out) : "/";
567
+ const href = joinProxyAndViteBase(pathPrefix, viteBase);
568
+ out = out.replace(/<head([^>]*)>/i, `<head$1><base href="${href}">`);
527
569
  }
528
570
  if (html) {
529
571
  out = out.replace(
@@ -552,13 +594,7 @@ function prefixRootPaths(body, publicBase, contentType = "") {
552
594
  (full, start, path) =>
553
595
  alreadyPrefixed(path, pathPrefix) ? full : `${start}${pathPrefix}${path}`
554
596
  );
555
- for (const root of viteAndNext) {
556
- const escaped = root.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
557
- out = out.replace(
558
- new RegExp(`(?<![A-Za-z0-9])${escaped}`, "g"),
559
- `${pathPrefix}${root}`
560
- );
561
- }
597
+ out = prefixQuotedAssetPaths(out, pathPrefix);
562
598
  }
563
599
  return out;
564
600
  }
@@ -649,6 +685,179 @@ function injectMaintainerProEmbed(html, aiPublicBase) {
649
685
  return html + loader;
650
686
  }
651
687
 
688
+ /**
689
+ * In-page rewrite: navigation stays under the share prefix; fetch/XHR/WebSocket
690
+ * only touch same-origin Vite/Next assets. Other hosts are left unchanged.
691
+ */
692
+ function shareProxyShim(p) {
693
+ if (!p) return;
694
+ function collapse(path) {
695
+ var d = p + p;
696
+ while (path.indexOf(d) === 0) path = p + path.slice(d.length);
697
+ return path;
698
+ }
699
+ function isAsset(path) {
700
+ return (
701
+ /\/(?:_next\/|__nextjs_|@vite(?:\/|$)|@react-refresh(?:\/|$)|@fs\/|@id\/|node_modules\/)/.test(
702
+ path
703
+ ) ||
704
+ /\/src\/[^/].+\.[A-Za-z0-9]+(?:\?|$)/.test(path) ||
705
+ /\.(?:m?[jt]sx?|cjs|mjs|css|scss|sass|less|map|wasm|vue|svelte)(?:\?|$)/i.test(
706
+ path
707
+ )
708
+ );
709
+ }
710
+ function prefixPath(path) {
711
+ if (typeof path !== "string" || !path) return path;
712
+ if (path.charAt(0) === "/" && path.charAt(1) !== "/") {
713
+ if (path === p || path.indexOf(p + "/") === 0) return collapse(path);
714
+ var root = p.slice(0, p.lastIndexOf("/"));
715
+ if (root && (path === root || path.indexOf(root + "/") === 0)) return path;
716
+ if (path === "/api/v1" || path.indexOf("/api/v1/") === 0) return path;
717
+ return collapse(p + path);
718
+ }
719
+ return path;
720
+ }
721
+ function addNav(v) {
722
+ if (typeof v !== "string" || !v) return v;
723
+ if (v.charAt(0) === "/" && v.charAt(1) !== "/") return prefixPath(v);
724
+ try {
725
+ var nav = new URL(v, location.href);
726
+ if (nav.origin !== location.origin) return v;
727
+ nav.pathname = prefixPath(nav.pathname);
728
+ return nav.toString();
729
+ } catch (e) {}
730
+ return v;
731
+ }
732
+ function addNet(v) {
733
+ if (typeof v !== "string" || !v) return v;
734
+ try {
735
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(v) || v.indexOf("//") === 0) {
736
+ var net = new URL(v, location.href);
737
+ if (net.origin !== location.origin) return v;
738
+ if (net.pathname === p || net.pathname.indexOf(p + "/") === 0) return v;
739
+ if (!isAsset(net.pathname)) return v;
740
+ net.pathname = prefixPath(net.pathname);
741
+ return net.toString();
742
+ }
743
+ } catch (e) {}
744
+ if (v.charAt(0) === "/" && v.charAt(1) !== "/") {
745
+ if (v === p || v.indexOf(p + "/") === 0) return collapse(v);
746
+ if (!isAsset(v)) return v;
747
+ return prefixPath(v);
748
+ }
749
+ return v;
750
+ }
751
+ function mapSel(sel) {
752
+ if (typeof sel !== "string") return sel;
753
+ var pairs = [
754
+ ['src="/_next/', 'src="' + p + "/_next/"],
755
+ ['href="/_next/', 'href="' + p + "/_next/"],
756
+ ];
757
+ for (var i = 0; i < pairs.length; i++) {
758
+ if (sel.indexOf(pairs[i][0]) !== -1) return sel.split(pairs[i][0]).join(pairs[i][1]);
759
+ }
760
+ return sel;
761
+ }
762
+ function patchQS(proto, name) {
763
+ var orig = proto[name];
764
+ proto[name] = function (sel) {
765
+ var r = orig.call(this, sel);
766
+ if (name === "querySelector") {
767
+ if (r) return r;
768
+ } else if (r.length) return r;
769
+ var s2 = mapSel(sel);
770
+ return s2 === sel ? r : orig.call(this, s2);
771
+ };
772
+ }
773
+ patchQS(Document.prototype, "querySelector");
774
+ patchQS(Document.prototype, "querySelectorAll");
775
+ patchQS(Element.prototype, "querySelector");
776
+ patchQS(Element.prototype, "querySelectorAll");
777
+ var sa = Element.prototype.setAttribute;
778
+ Element.prototype.setAttribute = function (n, v) {
779
+ if ((n === "src" || n === "href") && typeof v === "string") v = addNav(v);
780
+ return sa.call(this, n, v);
781
+ };
782
+ function patchUrlProp(ctor, prop) {
783
+ try {
784
+ var d = Object.getOwnPropertyDescriptor(ctor.prototype, prop);
785
+ if (!d || !d.set) return;
786
+ Object.defineProperty(ctor.prototype, prop, {
787
+ configurable: true,
788
+ enumerable: true,
789
+ get: d.get,
790
+ set: function (v) {
791
+ d.set.call(this, addNav(v));
792
+ },
793
+ });
794
+ } catch (e) {}
795
+ }
796
+ patchUrlProp(HTMLScriptElement, "src");
797
+ patchUrlProp(HTMLLinkElement, "href");
798
+ var f = window.fetch;
799
+ window.fetch = function (input, init) {
800
+ if (typeof input === "string") input = addNet(input);
801
+ else if (typeof Request !== "undefined" && input instanceof Request) {
802
+ input = new Request(addNet(input.url), input);
803
+ } else if (typeof URL !== "undefined" && input instanceof URL) {
804
+ input = new URL(addNet(input.href));
805
+ }
806
+ return f.call(this, input, init);
807
+ };
808
+ var xo = XMLHttpRequest.prototype.open;
809
+ XMLHttpRequest.prototype.open = function (m, u) {
810
+ if (typeof u === "string") arguments[1] = addNet(u);
811
+ return xo.apply(this, arguments);
812
+ };
813
+ var ps = history.pushState.bind(history);
814
+ history.pushState = function (s, t, u) {
815
+ if (typeof u === "string") u = addNav(u);
816
+ return ps(s, t, u);
817
+ };
818
+ var rs = history.replaceState.bind(history);
819
+ history.replaceState = function (s, t, u) {
820
+ if (typeof u === "string") u = addNav(u);
821
+ return rs(s, t, u);
822
+ };
823
+ var WS = window.WebSocket;
824
+ function WrappedWS(url, protocols) {
825
+ if (typeof url === "string") url = addNet(url);
826
+ return protocols !== undefined ? new WS(url, protocols) : new WS(url);
827
+ }
828
+ WrappedWS.prototype = WS.prototype;
829
+ WrappedWS.CONNECTING = WS.CONNECTING;
830
+ WrappedWS.OPEN = WS.OPEN;
831
+ WrappedWS.CLOSING = WS.CLOSING;
832
+ WrappedWS.CLOSED = WS.CLOSED;
833
+ window.WebSocket = WrappedWS;
834
+ try {
835
+ var la = location.assign.bind(location);
836
+ location.assign = function (u) {
837
+ return la(addNav(String(u)));
838
+ };
839
+ var lr = location.replace.bind(location);
840
+ location.replace = function (u) {
841
+ return lr(addNav(String(u)));
842
+ };
843
+ } catch (e) {}
844
+ try {
845
+ var hd = Object.getOwnPropertyDescriptor(Location.prototype, "href");
846
+ if (hd && hd.set) {
847
+ Object.defineProperty(location, "href", {
848
+ configurable: true,
849
+ enumerable: true,
850
+ get: function () {
851
+ return hd.get.call(location);
852
+ },
853
+ set: function (v) {
854
+ hd.set.call(location, addNav(String(v)));
855
+ },
856
+ });
857
+ }
858
+ } catch (e) {}
859
+ }
860
+
652
861
  function injectNextProxyShim(html, publicBase) {
653
862
  const prefix = proxyPathPrefix(publicBase);
654
863
  if (
@@ -658,8 +867,7 @@ function injectNextProxyShim(html, publicBase) {
658
867
  ) {
659
868
  return html;
660
869
  }
661
- const p = JSON.stringify(prefix);
662
- const script = `<script data-mp-proxy-next>(function(p){if(!p)return;function add(v){if(typeof v!=="string"||!v)return v;function collapse(path){var d=p+p;while(path.indexOf(d)===0)path=p+path.slice(d.length);return path}if(v.charAt(0)==="/"&&v.charAt(1)!=="/"){if(v===p||v.indexOf(p+"/")===0)return collapse(v);var root=p.slice(0,p.lastIndexOf("/"));if(root&&(v===root||v.indexOf(root+"/")===0))return v;if(v==="/api/v1"||v.indexOf("/api/v1/")===0)return v;return collapse(p+v)}try{var u=new URL(v,location.href);var host=String(u.hostname||"").toLowerCase();if(host.charAt(0)==="["&&host.charAt(host.length-1)==="]")host=host.slice(1,-1);var local=host==="localhost"||host==="127.0.0.1"||host==="::1"||host==="0.0.0.0";if(!local){if(u.pathname===p){u.pathname="/";return u.toString()}if(u.pathname.indexOf(p+"/")===0){u.pathname=u.pathname.slice(p.length)||"/";return u.toString()}return v}var samePort=String(u.port||"")===String(location.port||"");if(samePort){u.pathname=add(u.pathname);return u.toString()}if(u.pathname===p||u.pathname.indexOf(p+"/")===0){u.protocol=location.protocol;u.host=location.host;return u.toString()}u.pathname=add(u.pathname||"/");u.protocol=location.protocol;u.host=location.host;return u.toString()}catch(e){}return v}function mapSel(sel){if(typeof sel!=="string")return sel;var pairs=[["src=\\"/_next/","src=\\""+p+"/_next/"],["href=\\"/_next/","href=\\""+p+"/_next/"]];for(var i=0;i<pairs.length;i++){if(sel.indexOf(pairs[i][0])!==-1)return sel.split(pairs[i][0]).join(pairs[i][1])}return sel}function patchQS(proto,name){var orig=proto[name];proto[name]=function(sel){var r=orig.call(this,sel);if(name==="querySelector"){if(r)return r}else if(r.length)return r;var s2=mapSel(sel);return s2===sel?r:orig.call(this,s2)}}patchQS(Document.prototype,"querySelector");patchQS(Document.prototype,"querySelectorAll");patchQS(Element.prototype,"querySelector");patchQS(Element.prototype,"querySelectorAll");var sa=Element.prototype.setAttribute;Element.prototype.setAttribute=function(n,v){if((n==="src"||n==="href")&&typeof v==="string")v=add(v);return sa.call(this,n,v)};function patchUrlProp(ctor,prop){try{var d=Object.getOwnPropertyDescriptor(ctor.prototype,prop);if(!d||!d.set)return;Object.defineProperty(ctor.prototype,prop,{configurable:true,enumerable:true,get:d.get,set:function(v){d.set.call(this,add(v))}})}catch(e){}}patchUrlProp(HTMLScriptElement,"src");patchUrlProp(HTMLLinkElement,"href");var f=window.fetch;window.fetch=function(input,init){if(typeof input==="string")input=add(input);else if(typeof Request!=="undefined"&&input instanceof Request)input=new Request(add(input.url),input);else if(typeof URL!=="undefined"&&input instanceof URL)input=new URL(add(input.href));return f.call(this,input,init)};var xo=XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open=function(m,u){if(typeof u==="string")arguments[1]=add(u);return xo.apply(this,arguments)};var ps=history.pushState.bind(history);history.pushState=function(s,t,u){if(typeof u==="string")u=add(u);return ps(s,t,u)};var rs=history.replaceState.bind(history);history.replaceState=function(s,t,u){if(typeof u==="string")u=add(u);return rs(s,t,u)};var WS=window.WebSocket;function WrappedWS(url,protocols){if(typeof url==="string")url=add(url);return protocols!==undefined?new WS(url,protocols):new WS(url)}WrappedWS.prototype=WS.prototype;WrappedWS.CONNECTING=WS.CONNECTING;WrappedWS.OPEN=WS.OPEN;WrappedWS.CLOSING=WS.CLOSING;WrappedWS.CLOSED=WS.CLOSED;window.WebSocket=WrappedWS;try{var la=location.assign.bind(location);location.assign=function(u){return la(add(String(u)))};var lr=location.replace.bind(location);location.replace=function(u){return lr(add(String(u)))}}catch(e){}try{var hd=Object.getOwnPropertyDescriptor(Location.prototype,"href");if(hd&&hd.set){Object.defineProperty(location,"href",{configurable:true,enumerable:true,get:function(){return hd.get.call(location)},set:function(v){hd.set.call(location,add(String(v)))}})}}catch(e){}})(${p})</script>`;
870
+ const script = `<script data-mp-proxy-next>(${shareProxyShim.toString()})(${JSON.stringify(prefix)})</script>`;
663
871
  return html.replace(/<head([^>]*)>/i, `<head$1>${script}`);
664
872
  }
665
873