junso-browser 0.1.0 → 0.2.3

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.
Files changed (3) hide show
  1. package/dist/mcp.js +18 -17
  2. package/dist/server.js +163 -14
  3. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -1,33 +1,76 @@
1
1
  // @bun
2
- var z0=Object.create;var{getPrototypeOf:j0,defineProperty:Ef,getOwnPropertyNames:A0}=Object;var L0=Object.prototype.hasOwnProperty;function P0(f){return this[f]}var B0,M0,J1=(f,c,$)=>{var O=f!=null&&typeof f==="object";if(O){var N=c?B0??=new WeakMap:M0??=new WeakMap,b=N.get(f);if(b)return b}$=f!=null?z0(j0(f)):{};let x=c||!f||!f.__esModule?Ef($,"default",{value:f,enumerable:!0}):$;for(let K of A0(f))if(!L0.call(x,K))Ef(x,K,{get:P0.bind(f,K),enumerable:!0});if(O)N.set(f,x);return x};var W1=(f,c)=>()=>(c||f((c={exports:{}}).exports,c),c.exports);var T0=(f)=>f;function U0(f,c){this[f]=T0.bind(null,c)}var X1=(f,c)=>{for(var $ in c)Ef(f,$,{get:c[$],enumerable:!0,configurable:!0,set:U0.bind(c,$)})};var Kf=(f,c,$)=>{return(O,N)=>{let b=-1;return x(0);async function x(K){if(K<=b)throw Error("next() called multiple times");b=K;let E,S=!1,J;if(f[K])J=f[K][0][0],O.req.routeIndex=K;else J=K===f.length&&N||void 0;if(J)try{E=await J(O,()=>x(K+1))}catch(W){if(W instanceof Error&&c)O.error=W,E=await c(W,O),S=!0;else throw W}else if(O.finalized===!1&&$)E=await $(O);if(E&&(O.finalized===!1||S))O.res=E;return O}}};var Rf=Symbol();var Df=async(f,c=Object.create(null))=>{let{all:$=!1,dot:O=!1}=c,b=(f instanceof m?f.raw.headers:f.headers).get("Content-Type");if(b?.startsWith("multipart/form-data")||b?.startsWith("application/x-www-form-urlencoded"))return _0(f,{all:$,dot:O});return{}};async function _0(f,c){let $=await f.formData();if($)return y0($,c);return{}}function y0(f,c){let $=Object.create(null);if(f.forEach((O,N)=>{if(!(c.all||N.endsWith("[]")))$[N]=O;else F0($,N,O)}),c.dot)Object.entries($).forEach(([O,N])=>{if(O.includes("."))i0($,O,N),delete $[O]});return $}var F0=(f,c,$)=>{if(f[c]!==void 0)if(Array.isArray(f[c]))f[c].push($);else f[c]=[f[c],$];else if(!c.endsWith("[]"))f[c]=$;else f[c]=[$]},i0=(f,c,$)=>{if(/(?:^|\.)__proto__\./.test(c))return;let O=f,N=c.split(".");N.forEach((b,x)=>{if(x===N.length-1)O[b]=$;else{if(!O[b]||typeof O[b]!=="object"||Array.isArray(O[b])||O[b]instanceof File)O[b]=Object.create(null);O=O[b]}})};var Jf=(f)=>{let c=f.split("/");if(c[0]==="")c.shift();return c},gf=(f)=>{let{groups:c,path:$}=R0(f),O=Jf($);return D0(O,c)},R0=(f)=>{let c=[];return f=f.replace(/\{[^}]+\}/g,($,O)=>{let N=`@${O}`;return c.push([N,$]),N}),{groups:c,path:f}},D0=(f,c)=>{for(let $=c.length-1;$>=0;$--){let[O]=c[$];for(let N=f.length-1;N>=0;N--)if(f[N].includes(O)){f[N]=f[N].replace(O,c[$][1]);break}}return f},h={},lf=(f,c)=>{if(f==="*")return"*";let $=f.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);if($){let O=`${f}#${c}`;if(!h[O])if($[2])h[O]=c&&c[0]!==":"&&c[0]!=="*"?[O,$[1],new RegExp(`^${$[2]}(?=/${c})`)]:[f,$[1],new RegExp(`^${$[2]}$`)];else h[O]=[f,$[1],!0];return h[O]}return null},o=(f,c)=>{try{return c(f)}catch{return f.replace(/(?:%[0-9A-Fa-f]{2})+/g,($)=>{try{return c($)}catch{return $}})}},g0=(f)=>o(f,decodeURI),Wf=(f)=>{let c=f.url,$=c.indexOf("/",c.indexOf(":")+4),O=$;for(;O<c.length;O++){let N=c.charCodeAt(O);if(N===37){let b=c.indexOf("?",O),x=c.indexOf("#",O),K=b===-1?x===-1?void 0:x:x===-1?b:Math.min(b,x),E=c.slice($,K);return g0(E.includes("%25")?E.replace(/%25/g,"%2525"):E)}else if(N===63||N===35)break}return c.slice($,O)};var If=(f)=>{let c=Wf(f);return c.length>1&&c.at(-1)==="/"?c.slice(0,-1):c},T=(f,c,...$)=>{if($.length)c=T(c,...$);return`${f?.[0]==="/"?"":"/"}${f}${c==="/"?"":`${f?.at(-1)==="/"?"":"/"}${c?.[0]==="/"?c.slice(1):c}`}`},d=(f)=>{if(f.charCodeAt(f.length-1)!==63||!f.includes(":"))return null;let c=f.split("/"),$=[],O="";return c.forEach((N)=>{if(N!==""&&!/\:/.test(N))O+="/"+N;else if(/\:/.test(N))if(/\?/.test(N)){if($.length===0&&O==="")$.push("/");else $.push(O);let b=N.replace("?","");O+="/"+b,$.push(O)}else O+="/"+N}),$.filter((N,b,x)=>x.indexOf(N)===b)},Sf=(f)=>{if(!/[%+]/.test(f))return f;if(f.indexOf("+")!==-1)f=f.replace(/\+/g," ");return f.indexOf("%")!==-1?o(f,Xf):f},vf=(f,c,$)=>{let O;if(!$&&c&&!/[%+]/.test(c)){let x=f.indexOf("?",8);if(x===-1)return;if(!f.startsWith(c,x+1))x=f.indexOf(`&${c}`,x+1);while(x!==-1){let K=f.charCodeAt(x+c.length+1);if(K===61){let E=x+c.length+2,S=f.indexOf("&",E);return Sf(f.slice(E,S===-1?void 0:S))}else if(K==38||isNaN(K))return"";x=f.indexOf(`&${c}`,x+1)}if(O=/[%+]/.test(f),!O)return}let N={};O??=/[%+]/.test(f);let b=f.indexOf("?",8);while(b!==-1){let x=f.indexOf("&",b+1),K=f.indexOf("=",b);if(K>x&&x!==-1)K=-1;let E=f.slice(b+1,K===-1?x===-1?void 0:x:K);if(O)E=Sf(E);if(b=x,E==="")continue;let S;if(K===-1)S="";else if(S=f.slice(K+1,x===-1?void 0:x),O)S=Sf(S);if($){if(!(N[E]&&Array.isArray(N[E])))N[E]=[];N[E].push(S)}else N[E]??=S}return c?N[c]:N},Cf=vf,Hf=(f,c)=>{return vf(f,c,!0)},Xf=decodeURIComponent;var tf=(f)=>o(f,Xf),m=class{raw;#f;#c;routeIndex=0;path;bodyCache={};constructor(f,c="/",$=[[]]){this.raw=f,this.path=c,this.#c=$,this.#f={}}param(f){return f?this.#$(f):this.#b()}#$(f){let c=this.#c[0][this.routeIndex][1][f],$=this.#N(c);return $&&/\%/.test($)?tf($):$}#b(){let f={},c=Object.keys(this.#c[0][this.routeIndex][1]);for(let $ of c){let O=this.#N(this.#c[0][this.routeIndex][1][$]);if(O!==void 0)f[$]=/\%/.test(O)?tf(O):O}return f}#N(f){return this.#c[1]?this.#c[1][f]:f}query(f){return Cf(this.url,f)}queries(f){return Hf(this.url,f)}header(f){if(f)return this.raw.headers.get(f)??void 0;let c={};return this.raw.headers.forEach(($,O)=>{c[O]=$}),c}async parseBody(f){return Df(this,f)}#O=(f)=>{let{bodyCache:c,raw:$}=this,O=c[f];if(O)return O;let N=Object.keys(c)[0];if(N)return c[N].then((b)=>{if(N==="json")b=JSON.stringify(b);return new Response(b)[f]()});return c[f]=$[f]()};json(){return this.#O("text").then((f)=>JSON.parse(f))}text(){return this.#O("text")}arrayBuffer(){return this.#O("arrayBuffer")}bytes(){return this.#O("arrayBuffer").then((f)=>new Uint8Array(f))}blob(){return this.#O("blob")}formData(){return this.#O("formData")}addValidatedData(f,c){this.#f[f]=c}valid(f){return this.#f[f]}get url(){return this.raw.url}get method(){return this.raw.method}get[Rf](){return this.#c}get matchedRoutes(){return this.#c[0].map(([[,f]])=>f)}get routePath(){return this.#c[0].map(([[,f]])=>f)[this.routeIndex].path}};var nf={Stringify:1,BeforeStream:2,Stream:3},l0=(f,c)=>{let $=new String(f);return $.isEscaped=!0,$.callbacks=c,$};var Qf=async(f,c,$,O,N)=>{if(typeof f==="object"&&!(f instanceof String)){if(!(f instanceof Promise))f=f.toString();if(f instanceof Promise)f=await f}let b=f.callbacks;if(!b?.length)return Promise.resolve(f);if(N)N[0]+=f;else N=[f];let x=Promise.all(b.map((K)=>K({phase:c,buffer:N,context:O}))).then((K)=>Promise.all(K.filter(Boolean).map((E)=>Qf(E,c,!1,O,N))).then(()=>N[0]));if($)return l0(await x,b);else return x};var I0="text/plain; charset=UTF-8",Gf=(f,c)=>{return{"Content-Type":f,...c}},v=(f,c)=>new Response(f,c),Yf=class{#f;#c;env={};#$;finalized=!1;error;#b;#N;#O;#J;#K;#S;#E;#W;#X;constructor(f,c){if(this.#f=f,c)this.#N=c.executionCtx,this.env=c.env,this.#S=c.notFoundHandler,this.#X=c.path,this.#W=c.matchResult}get req(){return this.#c??=new m(this.#f,this.#X,this.#W),this.#c}get event(){if(this.#N&&"respondWith"in this.#N)return this.#N;else throw Error("This context has no FetchEvent")}get executionCtx(){if(this.#N)return this.#N;else throw Error("This context has no ExecutionContext")}get res(){return this.#O||=v(null,{headers:this.#E??=new Headers})}set res(f){if(this.#O&&f){f=v(f.body,f);for(let[c,$]of this.#O.headers.entries()){if(c==="content-type")continue;if(c==="set-cookie"){let O=this.#O.headers.getSetCookie();f.headers.delete("set-cookie");for(let N of O)f.headers.append("set-cookie",N)}else f.headers.set(c,$)}}this.#O=f,this.finalized=!0}render=(...f)=>{return this.#K??=(c)=>this.html(c),this.#K(...f)};setLayout=(f)=>this.#J=f;getLayout=()=>this.#J;setRenderer=(f)=>{this.#K=f};header=(f,c,$)=>{if(this.finalized)this.#O=v(this.#O.body,this.#O);let O=this.#O?this.#O.headers:this.#E??=new Headers;if(c===void 0)O.delete(f);else if($?.append)O.append(f,c);else O.set(f,c)};status=(f)=>{this.#b=f};set=(f,c)=>{this.#$??=new Map,this.#$.set(f,c)};get=(f)=>{return this.#$?this.#$.get(f):void 0};get var(){if(!this.#$)return{};return Object.fromEntries(this.#$)}#x(f,c,$){let O=this.#O?new Headers(this.#O.headers):this.#E??new Headers;if(typeof c==="object"&&"headers"in c){let b=c.headers instanceof Headers?c.headers:new Headers(c.headers);for(let[x,K]of b)if(x.toLowerCase()==="set-cookie")O.append(x,K);else O.set(x,K)}if($)for(let[b,x]of Object.entries($))if(typeof x==="string")O.set(b,x);else{O.delete(b);for(let K of x)O.append(b,K)}let N=typeof c==="number"?c:c?.status??this.#b;return v(f,{status:N,headers:O})}newResponse=(...f)=>this.#x(...f);body=(f,c,$)=>this.#x(f,c,$);text=(f,c,$)=>{return!this.#E&&!this.#b&&!c&&!$&&!this.finalized?new Response(f):this.#x(f,c,Gf(I0,$))};json=(f,c,$)=>{return this.#x(JSON.stringify(f),c,Gf("application/json",$))};html=(f,c,$)=>{let O=(N)=>this.#x(N,c,Gf("text/html; charset=UTF-8",$));return typeof f==="object"?Qf(f,nf.Stringify,!1,{}).then(O):O(f)};redirect=(f,c)=>{let $=String(f);return this.header("Location",!/[^\x00-\xFF]/.test($)?$:encodeURI($)),this.newResponse(null,c??302)};notFound=()=>{return this.#S??=()=>v(),this.#S(this)}};var Q="ALL",kf="all",wf=["get","post","put","delete","options","patch"],r="Can not add a route since the matcher is already built.",p=class extends Error{};var sf="__COMPOSED_HANDLER";var v0=(f)=>{return f.text("404 Not Found",404)},qf=(f,c)=>{if("getResponse"in f){let $=f.getResponse();return c.newResponse($.body,$)}return console.error(f),c.text("Internal Server Error",500)},uf=class f{get;post;put;delete;options;patch;all;on;use;router;getPath;_basePath="/";#f="/";routes=[];constructor(c={}){[...wf,kf].forEach((b)=>{this[b]=(x,...K)=>{if(typeof x==="string")this.#f=x;else this.#b(b,this.#f,x);return K.forEach((E)=>{this.#b(b,this.#f,E)}),this}}),this.on=(b,x,...K)=>{for(let E of[x].flat()){this.#f=E;for(let S of[b].flat())K.map((J)=>{this.#b(S.toUpperCase(),this.#f,J)})}return this},this.use=(b,...x)=>{if(typeof b==="string")this.#f=b;else this.#f="*",x.unshift(b);return x.forEach((K)=>{this.#b(Q,this.#f,K)}),this};let{strict:O,...N}=c;Object.assign(this,N),this.getPath=O??!0?c.getPath??Wf:If}#c(){let c=new f({router:this.router,getPath:this.getPath});return c.errorHandler=this.errorHandler,c.#$=this.#$,c.routes=this.routes,c}#$=v0;errorHandler=qf;route(c,$){let O=this.basePath(c);return $.routes.map((N)=>{let b;if($.errorHandler===qf)b=N.handler;else b=async(x,K)=>(await Kf([],$.errorHandler)(x,()=>N.handler(x,K))).res,b[sf]=N.handler;O.#b(N.method,N.path,b,N.basePath)}),this}basePath(c){let $=this.#c();return $._basePath=T(this._basePath,c),$}onError=(c)=>{return this.errorHandler=c,this};notFound=(c)=>{return this.#$=c,this};mount(c,$,O){let N,b;if(O)if(typeof O==="function")b=O;else if(b=O.optionHandler,O.replaceRequest===!1)N=(E)=>E;else N=O.replaceRequest;let x=b?(E)=>{let S=b(E);return Array.isArray(S)?S:[S]}:(E)=>{let S=void 0;try{S=E.executionCtx}catch{}return[E.env,S]};N||=(()=>{let E=T(this._basePath,c),S=E==="/"?0:E.length;return(J)=>{let W=new URL(J.url);return W.pathname=this.getPath(J).slice(S)||"/",new Request(W,J)}})();let K=async(E,S)=>{let J=await $(N(E.req.raw),...x(E));if(J)return J;await S()};return this.#b(Q,T(c,"*"),K),this}#b(c,$,O,N){c=c.toUpperCase(),$=T(this._basePath,$);let b={basePath:N!==void 0?T(this._basePath,N):this._basePath,path:$,method:c,handler:O};this.router.add(c,$,[O,b]),this.routes.push(b)}#N(c,$){if(c instanceof Error)return this.errorHandler(c,$);throw c}#O(c,$,O,N){if(N==="HEAD")return(async()=>new Response(null,await this.#O(c,$,O,"GET")))();let b=this.getPath(c,{env:O}),x=this.router.match(N,b),K=new Yf(c,{path:b,matchResult:x,env:O,executionCtx:$,notFoundHandler:this.#$});if(x[0].length===1){let S;try{S=x[0][0][0][0](K,async()=>{K.res=await this.#$(K)})}catch(J){return this.#N(J,K)}return S instanceof Promise?S.then((J)=>J||(K.finalized?K.res:this.#$(K))).catch((J)=>this.#N(J,K)):S??this.#$(K)}let E=Kf(x[0],this.errorHandler,this.#$);return(async()=>{try{let S=await E(K);if(!S.finalized)throw Error("Context is not finalized. Did you forget to return a Response object or `await next()`?");return S.res}catch(S){return this.#N(S,K)}})()}fetch=(c,...$)=>{return this.#O(c,$[1],$[0],c.method)};request=(c,$,O,N)=>{if(c instanceof Request)return this.fetch($?new Request(c,$):c,O,N);return c=c.toString(),this.fetch(new Request(/^https?:\/\//.test(c)?c:`http://localhost${T("/",c)}`,$),O,N)};fire=()=>{addEventListener("fetch",(c)=>{c.respondWith(this.#O(c.request,c,void 0,c.request.method))})}};var C=[];function a(f,c){let $=this.buildAllMatchers(),O=(N,b)=>{let x=$[N]||$[Q],K=x[2][b];if(K)return K;let E=b.match(x[0]);if(!E)return[[],C];let S=E.indexOf("",1);return[x[1][S],E]};return this.match=O,O(f,c)}var e="[^/]+",H=".*",t="(?:|/.*)",U=Symbol(),C0=new Set(".\\+*[^]$()");function H0(f,c){if(f.length===1)return c.length===1?f<c?-1:1:-1;if(c.length===1)return 1;if(f===H||f===t)return 1;else if(c===H||c===t)return-1;if(f===e)return 1;else if(c===e)return-1;return f.length===c.length?f<c?-1:1:c.length-f.length}var mf=class f{#f;#c;#$=Object.create(null);insert(c,$,O,N,b){if(c.length===0){if(this.#f!==void 0)throw U;if(b)return;this.#f=$;return}let[x,...K]=c,E=x==="*"?K.length===0?["","",H]:["","",e]:x==="/*"?["","",t]:x.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/),S;if(E){let J=E[1],W=E[2]||e;if(J&&E[2]){if(W===".*")throw U;if(W=W.replace(/^\((?!\?:)(?=[^)]+\)$)/,"(?:"),/\((?!\?:)/.test(W))throw U}if(S=this.#$[W],!S){if(Object.keys(this.#$).some((G)=>G!==H&&G!==t))throw U;if(b)return;if(S=this.#$[W]=new f,J!=="")S.#c=N.varIndex++}if(!b&&J!=="")O.push([J,S.#c])}else if(S=this.#$[x],!S){if(Object.keys(this.#$).some((J)=>J.length>1&&J!==H&&J!==t))throw U;if(b)return;S=this.#$[x]=new f}S.insert(K,$,O,N,b)}buildRegExpStr(){let $=Object.keys(this.#$).sort(H0).map((O)=>{let N=this.#$[O];return(typeof N.#c==="number"?`(${O})@${N.#c}`:C0.has(O)?`\\${O}`:O)+N.buildRegExpStr()});if(typeof this.#f==="number")$.unshift(`#${this.#f}`);if($.length===0)return"";if($.length===1)return $[0];return"(?:"+$.join("|")+")"}};var hf=class{#f={varIndex:0};#c=new mf;insert(f,c,$){let O=[],N=[];for(let x=0;;){let K=!1;if(f=f.replace(/\{[^}]+\}/g,(E)=>{let S=`@\\${x}`;return N[x]=[S,E],x++,K=!0,S}),!K)break}let b=f.match(/(?::[^\/]+)|(?:\/\*$)|./g)||[];for(let x=N.length-1;x>=0;x--){let[K]=N[x];for(let E=b.length-1;E>=0;E--)if(b[E].indexOf(K)!==-1){b[E]=b[E].replace(K,N[x][1]);break}}return this.#c.insert(b,c,O,this.#f,$),O}buildRegExp(){let f=this.#c.buildRegExpStr();if(f==="")return[/^$/,[],[]];let c=0,$=[],O=[];return f=f.replace(/#(\d+)|@(\d+)|\.\*\$/g,(N,b,x)=>{if(b!==void 0)return $[++c]=Number(b),"$()";if(x!==void 0)return O[Number(x)]=++c,"";return""}),[new RegExp(`^${f}`),$,O]}};var t0=[/^$/,[],Object.create(null)],of=Object.create(null);function df(f){return of[f]??=new RegExp(f==="*"?"":`^${f.replace(/\/\*$|([.\\+*[^\]$()])/g,(c,$)=>$?`\\${$}`:"(?:|/.*)")}$`)}function n0(){of=Object.create(null)}function k0(f){let c=new hf,$=[];if(f.length===0)return t0;let O=f.map((S)=>[!/\*|\/:/.test(S[0]),...S]).sort(([S,J],[W,G])=>S?1:W?-1:J.length-G.length),N=Object.create(null);for(let S=0,J=-1,W=O.length;S<W;S++){let[G,Y,z]=O[S];if(G)N[Y]=[z.map(([L])=>[L,Object.create(null)]),C];else J++;let j;try{j=c.insert(Y,J,G)}catch(L){throw L===U?new p(Y):L}if(G)continue;$[J]=z.map(([L,V])=>{let P=Object.create(null);V-=1;for(;V>=0;V--){let[g,bf]=j[V];P[g]=bf}return[L,P]})}let[b,x,K]=c.buildRegExp();for(let S=0,J=$.length;S<J;S++)for(let W=0,G=$[S].length;W<G;W++){let Y=$[S][W]?.[1];if(!Y)continue;let z=Object.keys(Y);for(let j=0,L=z.length;j<L;j++)Y[z[j]]=K[Y[z[j]]]}let E=[];for(let S in x)E[S]=$[x[S]];return[b,E,N]}function F(f,c){if(!f)return;for(let $ of Object.keys(f).sort((O,N)=>N.length-O.length))if(df($).test(c))return[...f[$]];return}var ff=class{name="RegExpRouter";#f;#c;constructor(){this.#f={[Q]:Object.create(null)},this.#c={[Q]:Object.create(null)}}add(f,c,$){let O=this.#f,N=this.#c;if(!O||!N)throw Error(r);if(!O[f])[O,N].forEach((K)=>{K[f]=Object.create(null),Object.keys(K[Q]).forEach((E)=>{K[f][E]=[...K[Q][E]]})});if(c==="/*")c="*";let b=(c.match(/\/:/g)||[]).length;if(/\*$/.test(c)){let K=df(c);if(f===Q)Object.keys(O).forEach((E)=>{O[E][c]||=F(O[E],c)||F(O[Q],c)||[]});else O[f][c]||=F(O[f],c)||F(O[Q],c)||[];Object.keys(O).forEach((E)=>{if(f===Q||f===E)Object.keys(O[E]).forEach((S)=>{K.test(S)&&O[E][S].push([$,b])})}),Object.keys(N).forEach((E)=>{if(f===Q||f===E)Object.keys(N[E]).forEach((S)=>K.test(S)&&N[E][S].push([$,b]))});return}let x=d(c)||[c];for(let K=0,E=x.length;K<E;K++){let S=x[K];Object.keys(N).forEach((J)=>{if(f===Q||f===J)N[J][S]||=[...F(O[J],S)||F(O[Q],S)||[]],N[J][S].push([$,b-E+K+1])})}}match=a;buildAllMatchers(){let f=Object.create(null);return Object.keys(this.#c).concat(Object.keys(this.#f)).forEach((c)=>{f[c]||=this.#$(c)}),this.#f=this.#c=void 0,n0(),f}#$(f){let c=[],$=f===Q;if([this.#f,this.#c].forEach((O)=>{let N=O[f]?Object.keys(O[f]).map((b)=>[b,O[f][b]]):[];if(N.length!==0)$||=!0,c.push(...N);else if(f!==Q)c.push(...Object.keys(O[Q]).map((b)=>[b,O[Q][b]]))}),!$)return null;else return k0(c)}};var w0=class{name="PreparedRegExpRouter";#f;#c;constructor(f,c){this.#f=f,this.#c=c}#$(f,c){let $=this.#f[f];$[1].forEach((O)=>O&&O.push(c)),Object.values($[2]).forEach((O)=>O[0].push(c))}#b(f,c,$,O,N){let b=this.#f[f];if(!N)b[2][c][0].push([$,{}]);else O.forEach((x)=>{if(typeof x==="number")b[1][x].push([$,N]);else b[2][x||c][0].push([$,N])})}add(f,c,$){if(!this.#f[f]){let N=this.#f[Q],b={};for(let x in N[2])b[x]=[N[2][x][0].slice(),C];this.#f[f]=[N[0],N[1].map((x)=>Array.isArray(x)?x.slice():0),b]}if(c==="/*"||c==="*"){let N=[$,{}];if(f===Q)for(let b in this.#f)this.#$(b,N);else this.#$(f,N);return}let O=this.#c[c];if(!O)throw Error(`Path ${c} is not registered`);for(let[N,b]of O)if(f===Q)for(let x in this.#f)this.#b(x,c,$,N,b);else this.#b(f,c,$,N,b)}buildAllMatchers(){return this.#f}match=a};var Vf=class{name="SmartRouter";#f=[];#c=[];constructor(f){this.#f=f.routers}add(f,c,$){if(!this.#c)throw Error(r);this.#c.push([f,c,$])}match(f,c){if(!this.#c)throw Error("Fatal error");let $=this.#f,O=this.#c,N=$.length,b=0,x;for(;b<N;b++){let K=$[b];try{for(let E=0,S=O.length;E<S;E++)K.add(...O[E]);x=K.match(f,c)}catch(E){if(E instanceof p)continue;throw E}this.match=K.match.bind(K),this.#f=[K],this.#c=void 0;break}if(b===N)throw Error("Fatal error");return this.name=`SmartRouter + ${this.activeRouter.name}`,x}get activeRouter(){if(this.#c||this.#f.length!==1)throw Error("No active router has been determined yet.");return this.#f[0]}};var n=Object.create(null),s0=(f)=>{for(let c in f)return!0;return!1},rf=class f{#f;#c;#$;#b=0;#N=n;constructor(c,$,O){if(this.#c=O||Object.create(null),this.#f=[],c&&$){let N=Object.create(null);N[c]={handler:$,possibleKeys:[],score:0},this.#f=[N]}this.#$=[]}insert(c,$,O){this.#b=++this.#b;let N=this,b=gf($),x=[];for(let K=0,E=b.length;K<E;K++){let S=b[K],J=b[K+1],W=lf(S,J),G=Array.isArray(W)?W[0]:S;if(G in N.#c){if(N=N.#c[G],W)x.push(W[1]);continue}if(N.#c[G]=new f,W)N.#$.push(W),x.push(W[1]);N=N.#c[G]}return N.#f.push({[c]:{handler:O,possibleKeys:x.filter((K,E,S)=>S.indexOf(K)===E),score:this.#b}}),N}#O(c,$,O,N,b){for(let x=0,K=$.#f.length;x<K;x++){let E=$.#f[x],S=E[O]||E[Q],J={};if(S!==void 0){if(S.params=Object.create(null),c.push(S),N!==n||b&&b!==n)for(let W=0,G=S.possibleKeys.length;W<G;W++){let Y=S.possibleKeys[W],z=J[S.score];S.params[Y]=b?.[Y]&&!z?b[Y]:N[Y]??b?.[Y],J[S.score]=!0}}}}search(c,$){let O=[];this.#N=n;let b=[this],x=Jf($),K=[],E=x.length,S=null;for(let J=0;J<E;J++){let W=x[J],G=J===E-1,Y=[];for(let j=0,L=b.length;j<L;j++){let V=b[j],P=V.#c[W];if(P)if(P.#N=V.#N,G){if(P.#c["*"])this.#O(O,P.#c["*"],c,V.#N);this.#O(O,P,c,V.#N)}else Y.push(P);for(let g=0,bf=V.#$.length;g<bf;g++){let yf=V.#$[g],M=V.#N===n?{}:{...V.#N};if(yf==="*"){let y=V.#c["*"];if(y)this.#O(O,y,c,V.#N),y.#N=M,Y.push(y);continue}let[Z0,Ff,l]=yf;if(!W&&!(l instanceof RegExp))continue;let B=V.#c[Z0];if(l instanceof RegExp){if(S===null){S=Array(E);let u=$[0]==="/"?1:0;for(let I=0;I<E;I++)S[I]=u,u+=x[I].length+1}let y=$.substring(S[J]),xf=l.exec(y);if(xf){if(M[Ff]=xf[0],this.#O(O,B,c,V.#N,M),s0(B.#c)){B.#N=M;let u=xf[0].match(/\//)?.length??0;(K[u]||=[]).push(B)}continue}}if(l===!0||l.test(W))if(M[Ff]=W,G){if(this.#O(O,B,c,M,V.#N),B.#c["*"])this.#O(O,B.#c["*"],c,M,V.#N)}else B.#N=M,Y.push(B)}}let z=K.shift();b=z?Y.concat(z):Y}if(O.length>1)O.sort((J,W)=>{return J.score-W.score});return[O.map(({handler:J,params:W})=>[J,W])]}};var Zf=class{name="TrieRouter";#f;constructor(){this.#f=new rf}add(f,c,$){let O=d(c);if(O){for(let N=0,b=O.length;N<b;N++)this.#f.insert(f,O[N],$);return}this.#f.insert(f,c,$)}match(f,c){return this.#f.search(f,c)}};var zf=class extends uf{constructor(f={}){super(f);this.router=f.router??new Vf({routers:[new ff,new Zf]})}};import q0 from"os";import jf from"path";var X={port:Number(process.env.JUNSO_BROWSER_PORT??8790),host:process.env.JUNSO_BROWSER_HOST??"127.0.0.1",token:process.env.JUNSO_BROWSER_TOKEN??"",dataDir:process.env.JUNSO_BROWSER_DATA_DIR??jf.join(q0.homedir(),".junso-browser"),maxInstances:Number(process.env.JUNSO_BROWSER_MAX_INSTANCES??3),idleReapMs:Number(process.env.JUNSO_BROWSER_IDLE_MS??1800000),cloakPath:process.env.JUNSO_BROWSER_CLOAK_PATH,publicUrl:process.env.JUNSO_BROWSER_PUBLIC_URL};function Af(){return jf.join(X.dataDir,"profiles.json")}function cf(f){return jf.join(X.dataDir,"profiles",f,"user-data")}function $f(){return X.host==="127.0.0.1"||X.host==="localhost"||X.host==="::1"}import{mkdir as h0,readFile as o0,writeFile as d0,rm as r0}from"fs/promises";import p0 from"path";import{randomUUID as a0}from"crypto";import u0 from"os";import Pf from"path";import{existsSync as pf,readdirSync as m0}from"fs";function i(){if(X.cloakPath&&pf(X.cloakPath))return X.cloakPath;let f=Pf.join(u0.homedir(),".cloakbrowser"),c;try{c=m0(f).filter((O)=>O.startsWith("chromium-"))}catch{return null}c.sort().reverse();let $=process.platform==="win32"?"chrome.exe":"chrome";for(let O of c){let N=Pf.join(f,O,$);if(pf(N))return N}return null}var Lf=!1;function af(){if(Lf)return;if(process.env.JUNSO_BROWSER_NO_CLOAK_DOWNLOAD==="true")return;if(i())return;Lf=!0,console.log("[junso-browser] CloakBrowser binary not found \u2014 downloading in background\u2026");let f=Pf.resolve(import.meta.dir,"..");Bun.spawn([process.execPath,"x","cloakbrowser","install"],{cwd:f,stdout:"inherit",stderr:"inherit"}).exited.then(($)=>{Lf=!1,console.log($===0?"[junso-browser] CloakBrowser ready.":`[junso-browser] cloak download exited ${$}.`)})}function Bf(){return Math.floor(Math.random()*90000)+1e4}function ef(f,c,$){let O=f.fingerprint,N=["--no-sandbox",`--fingerprint=${O.seed}`,`--fingerprint-platform=${O.platform??"windows"}`];if(O.timezone)N.push(`--fingerprint-timezone=${O.timezone}`);if(O.locale)N.push(`--fingerprint-locale=${O.locale}`,`--lang=${O.locale}`);if(f.proxy)N.push(`--proxy-server=${f.proxy}`);return N.push(`--user-data-dir=${$}`,`--remote-debugging-port=${c}`,"--remote-debugging-address=127.0.0.1","--headless=new"),N}var R=null,f0=Promise.resolve();async function k(){if(R)return R;try{let f=await o0(Af(),"utf8"),c=JSON.parse(f);R=c&&typeof c==="object"?c:{}}catch{R={}}return R}async function Mf(f){await h0(X.dataDir,{recursive:!0}),await d0(Af(),JSON.stringify(f,null,2)+`
3
- `,"utf8"),R=f}function Tf(f){let c=f0.catch(()=>{}).then(f);return f0=c.catch(()=>{}),c}function e0(f){return(f??"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}async function c0(){return Object.values(await k()).sort((f,c)=>f.name.localeCompare(c.name))}async function _(f){return(await k())[f]??null}async function $0(f){let c=e0(f.name)||a0().slice(0,8);return Tf(async()=>{let $={...await k()};if($[c])throw Error(`Profile "${c}" already exists`);let O=new Date().toISOString(),N={id:c,name:f.name.trim(),fingerprint:{seed:f.fingerprint?.seed??Bf(),platform:f.fingerprint?.platform??"windows",timezone:f.fingerprint?.timezone,locale:f.fingerprint?.locale},proxy:f.proxy?.trim()||void 0,createdAt:O,updatedAt:O};return $[c]=N,await Mf($),N})}async function O0(f,c){return Tf(async()=>{let $={...await k()},O=$[f];if(!O)throw Error(`No such profile: ${f}`);let N={...O.fingerprint,...c.fingerprint??{}};if(c.rotateSeed)N.seed=Bf();let b={...O,name:c.name?.trim()||O.name,fingerprint:N,proxy:c.proxy===null?void 0:c.proxy?.trim()||O.proxy,updatedAt:new Date().toISOString()};return $[f]=b,await Mf($),b})}async function N0(f){return Tf(async()=>{let c={...await k()};if(!c[f])return!1;return delete c[f],await Mf(c),await r0(p0.dirname(cf(f)),{recursive:!0,force:!0}),!0})}function f1(f){if(!f)return null;return f.replace(/^([a-z0-9+.-]+:\/\/[^:@/]+):[^@/]+@/i,"$1:\u2022\u2022\u2022\u2022@")}function w(f){return{...f,proxy:f1(f.proxy)??void 0}}import{mkdir as c1,readFile as $1,rm as b0}from"fs/promises";import Uf from"path";var Z=new Map;async function O1(f,c=20000){let $=Uf.join(f,"DevToolsActivePort"),O=Date.now()+c;while(Date.now()<O){try{let N=await $1($,"utf8"),[b,x]=N.split(`
4
- `),K=Number(b);if(K>0&&x)return{port:K,wsPath:x.trim()}}catch{}await Bun.sleep(150)}throw Error("Browser did not become ready (no DevToolsActivePort)")}async function N1(){let f=null;for(let c of Z.values())if(!f||c.instance.lastUsedAt<f.instance.lastUsedAt)f=c;if(f)await D(f.instance.profileId)}async function s(f){let c=Z.get(f.id);if(c)return c.instance.lastUsedAt=new Date().toISOString(),c.instance;let $=i();if(!$)throw Error("CloakBrowser binary not found \u2014 run `cloakbrowser install`");if(X.maxInstances>0&&Z.size>=X.maxInstances)await N1();let O=cf(f.id);await c1(O,{recursive:!0}),await b0(Uf.join(O,"DevToolsActivePort"),{force:!0}),await b0(Uf.join(O,"SingletonLock"),{force:!0});let N=ef(f,0,O),b=Bun.spawn([$,...N],{stdout:"ignore",stderr:"ignore"}),x;try{x=await O1(O)}catch(S){throw b.kill(),S}let K=new Date().toISOString(),E={profileId:f.id,pid:b.pid,port:x.port,browserWsPath:x.wsPath,startedAt:K,lastUsedAt:K};return Z.set(f.id,{instance:E,proc:b}),b.exited.then(()=>{if(Z.get(f.id)?.proc===b)Z.delete(f.id)}),E}function Of(f){return Z.has(f)}function x0(f){let c=Z.get(f);if(c)c.instance.lastUsedAt=new Date().toISOString()}async function D(f){let c=Z.get(f);if(!c)return!1;return Z.delete(f),c.proc.kill(),await c.proc.exited.catch(()=>{}),!0}async function E0(){await Promise.all([...Z.keys()].map((f)=>D(f)))}function K0(){return[...Z.values()].map((f)=>f.instance)}function S0(){if(X.idleReapMs<=0)return null;return setInterval(()=>{let f=Date.now()-X.idleReapMs;for(let c of Z.values())if(new Date(c.instance.lastUsedAt).getTime()<f)D(c.instance.profileId)},60000)}class J0{browserWsUrl;cdp=null;sessionId=null;nextId=1;pending=new Map;starting=null;lastFrame=null;viewers=new Set;constructor(f){this.browserWsUrl=f}rpc(f,c={},$){if(!this.cdp)return Promise.reject(Error("cdp closed"));let O=this.nextId++;return this.cdp.send(JSON.stringify({id:O,method:f,params:c,sessionId:$})),new Promise((N)=>this.pending.set(O,N))}async start(){if(this.starting)return this.starting;return this.starting=(async()=>{let f=new WebSocket(this.browserWsUrl);this.cdp=f,await new Promise((N,b)=>{f.onopen=()=>N(),f.onerror=()=>b(Error("CDP connect failed"))}),f.onmessage=(N)=>this.onMessage(JSON.parse(String(N.data))),f.onclose=()=>{this.cdp=null,this.sessionId=null};let{targetInfos:c}=await this.rpc("Target.getTargets"),$=c.find((N)=>N.type==="page")??c[0];if(!$)throw Error("no page target to stream");let{sessionId:O}=await this.rpc("Target.attachToTarget",{targetId:$.targetId,flatten:!0});this.sessionId=O,await this.rpc("Page.enable",{},O),await this.rpc("Page.startScreencast",{format:"jpeg",quality:60,maxWidth:1280,maxHeight:800,everyNthFrame:1},O)})(),this.starting}onMessage(f){if(f.id!==void 0&&this.pending.has(f.id)){this.pending.get(f.id)(f.result),this.pending.delete(f.id);return}if(f.method==="Page.screencastFrame"){let c=f.params;this.rpc("Page.screencastFrameAck",{sessionId:c.sessionId},this.sessionId??void 0);let $=JSON.stringify({type:"frame",data:c.data,metadata:c.metadata});this.lastFrame=$;for(let O of this.viewers)O.send($)}}input(f){if(!this.cdp||!this.sessionId)return;let c=f.t;if(c==="m")this.rpc("Input.dispatchMouseEvent",{type:f.type,x:f.x,y:f.y,button:f.button??"none",buttons:f.buttons??0,clickCount:f.clickCount??0,deltaX:f.deltaX??0,deltaY:f.deltaY??0,modifiers:f.modifiers??0},this.sessionId);else if(c==="k")this.rpc("Input.dispatchKeyEvent",{type:f.type,key:f.key,code:f.code,text:f.text,windowsVirtualKeyCode:f.keyCode,nativeVirtualKeyCode:f.keyCode,modifiers:f.modifiers??0},this.sessionId)}close(){try{this.cdp?.close()}catch{}this.cdp=null}}var q=new Map;async function W0(f,c){let $=await _(f);if(!$)throw Error(`no such profile: ${f}`);let O=await s($),N=q.get(f);if(!N)N=new J0(`ws://127.0.0.1:${O.port}${O.browserWsPath}`),q.set(f,N);if(await N.start(),N.viewers.add(c),N.lastFrame)c.send(N.lastFrame)}function X0(f,c){let $=q.get(f);if(!$)return;if($.viewers.delete(c),$.viewers.size===0)$.close(),q.delete(f)}function Q0(f,c){let $=q.get(f);if(!$)return;try{$.input(JSON.parse(c))}catch{}}function G0(f,c){let $=JSON.stringify(f),O=JSON.stringify(c||"");return`<!doctype html>
5
- <html><head><meta charset="utf-8"><title>junso-browser \xB7 ${f}</title>
2
+ var Pn=Object.create;var{getPrototypeOf:Tn,defineProperty:et,getOwnPropertyNames:jn}=Object;var Jn=Object.prototype.hasOwnProperty;function mn(t){return this[t]}var _n,Wn,zi=(t,n,i)=>{var c=t!=null&&typeof t==="object";if(c){var l=n?_n??=new WeakMap:Wn??=new WeakMap,s=l.get(t);if(s)return s}i=t!=null?Pn(Tn(t)):{};let f=n||!t||!t.__esModule?et(i,"default",{value:t,enumerable:!0}):i;for(let e of jn(t))if(!Jn.call(f,e))et(f,e,{get:mn.bind(t,e),enumerable:!0});if(c)l.set(t,f);return f};var Ki=(t,n)=>()=>(n||t((n={exports:{}}).exports,n),n.exports);var Xn=(t)=>t;function Gn(t,n){this[t]=Xn.bind(null,n)}var gi=(t,n)=>{for(var i in n)et(t,i,{get:n[i],enumerable:!0,configurable:!0,set:Gn.bind(n,i)})};var ot=(t,n,i)=>{return(c,l)=>{let s=-1;return f(0);async function f(e){if(e<=s)throw Error("next() called multiple times");s=e;let b,o=!1,r;if(t[e])r=t[e][0][0],c.req.routeIndex=e;else r=e===t.length&&l||void 0;if(r)try{b=await r(c,()=>f(e+1))}catch(x){if(x instanceof Error&&n)c.error=x,b=await n(x,c),o=!0;else throw x}else if(c.finalized===!1&&i)b=await i(c);if(b&&(c.finalized===!1||o))c.res=b;return c}}};var Xt=Symbol();var Gt=async(t,n=Object.create(null))=>{let{all:i=!1,dot:c=!1}=n,s=(t instanceof F?t.raw.headers:t.headers).get("Content-Type");if(s?.startsWith("multipart/form-data")||s?.startsWith("application/x-www-form-urlencoded"))return Qn(t,{all:i,dot:c});return{}};async function Qn(t,n){let i=await t.formData();if(i)return Yn(i,n);return{}}function Yn(t,n){let i=Object.create(null);if(t.forEach((c,l)=>{if(!(n.all||l.endsWith("[]")))i[l]=c;else Vn(i,l,c)}),n.dot)Object.entries(i).forEach(([c,l])=>{if(c.includes("."))wn(i,c,l),delete i[c]});return i}var Vn=(t,n,i)=>{if(t[n]!==void 0)if(Array.isArray(t[n]))t[n].push(i);else t[n]=[t[n],i];else if(!n.endsWith("[]"))t[n]=i;else t[n]=[i]},wn=(t,n,i)=>{if(/(?:^|\.)__proto__\./.test(n))return;let c=t,l=n.split(".");l.forEach((s,f)=>{if(f===l.length-1)c[s]=i;else{if(!c[s]||typeof c[s]!=="object"||Array.isArray(c[s])||c[s]instanceof File)c[s]=Object.create(null);c=c[s]}})};var xt=(t)=>{let n=t.split("/");if(n[0]==="")n.shift();return n},Qt=(t)=>{let{groups:n,path:i}=An(t),c=xt(i);return Zn(c,n)},An=(t)=>{let n=[];return t=t.replace(/\{[^}]+\}/g,(i,c)=>{let l=`@${c}`;return n.push([l,i]),l}),{groups:n,path:t}},Zn=(t,n)=>{for(let i=n.length-1;i>=0;i--){let[c]=n[i];for(let l=t.length-1;l>=0;l--)if(t[l].includes(c)){t[l]=t[l].replace(c,n[i][1]);break}}return t},D={},Yt=(t,n)=>{if(t==="*")return"*";let i=t.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);if(i){let c=`${t}#${n}`;if(!D[c])if(i[2])D[c]=n&&n[0]!==":"&&n[0]!=="*"?[c,i[1],new RegExp(`^${i[2]}(?=/${n})`)]:[t,i[1],new RegExp(`^${i[2]}$`)];else D[c]=[t,i[1],!0];return D[c]}return null},C=(t,n)=>{try{return n(t)}catch{return t.replace(/(?:%[0-9A-Fa-f]{2})+/g,(i)=>{try{return n(i)}catch{return i}})}},Mn=(t)=>C(t,decodeURI),ut=(t)=>{let n=t.url,i=n.indexOf("/",n.indexOf(":")+4),c=i;for(;c<n.length;c++){let l=n.charCodeAt(c);if(l===37){let s=n.indexOf("?",c),f=n.indexOf("#",c),e=s===-1?f===-1?void 0:f:f===-1?s:Math.min(s,f),b=n.slice(i,e);return Mn(b.includes("%25")?b.replace(/%25/g,"%2525"):b)}else if(l===63||l===35)break}return n.slice(i,c)};var Vt=(t)=>{let n=ut(t);return n.length>1&&n.at(-1)==="/"?n.slice(0,-1):n},g=(t,n,...i)=>{if(i.length)n=g(n,...i);return`${t?.[0]==="/"?"":"/"}${t}${n==="/"?"":`${t?.at(-1)==="/"?"":"/"}${n?.[0]==="/"?n.slice(1):n}`}`},H=(t)=>{if(t.charCodeAt(t.length-1)!==63||!t.includes(":"))return null;let n=t.split("/"),i=[],c="";return n.forEach((l)=>{if(l!==""&&!/\:/.test(l))c+="/"+l;else if(/\:/.test(l))if(/\?/.test(l)){if(i.length===0&&c==="")i.push("/");else i.push(c);let s=l.replace("?","");c+="/"+s,i.push(c)}else c+="/"+l}),i.filter((l,s,f)=>f.indexOf(l)===s)},rt=(t)=>{if(!/[%+]/.test(t))return t;if(t.indexOf("+")!==-1)t=t.replace(/\+/g," ");return t.indexOf("%")!==-1?C(t,dt):t},wt=(t,n,i)=>{let c;if(!i&&n&&!/[%+]/.test(n)){let f=t.indexOf("?",8);if(f===-1)return;if(!t.startsWith(n,f+1))f=t.indexOf(`&${n}`,f+1);while(f!==-1){let e=t.charCodeAt(f+n.length+1);if(e===61){let b=f+n.length+2,o=t.indexOf("&",b);return rt(t.slice(b,o===-1?void 0:o))}else if(e==38||isNaN(e))return"";f=t.indexOf(`&${n}`,f+1)}if(c=/[%+]/.test(t),!c)return}let l={};c??=/[%+]/.test(t);let s=t.indexOf("?",8);while(s!==-1){let f=t.indexOf("&",s+1),e=t.indexOf("=",s);if(e>f&&f!==-1)e=-1;let b=t.slice(s+1,e===-1?f===-1?void 0:f:e);if(c)b=rt(b);if(s=f,b==="")continue;let o;if(e===-1)o="";else if(o=t.slice(e+1,f===-1?void 0:f),c)o=rt(o);if(i){if(!(l[b]&&Array.isArray(l[b])))l[b]=[];l[b].push(o)}else l[b]??=o}return n?l[n]:l},At=wt,Zt=(t,n)=>{return wt(t,n,!0)},dt=decodeURIComponent;var Mt=(t)=>C(t,dt),F=class{raw;#t;#n;routeIndex=0;path;bodyCache={};constructor(t,n="/",i=[[]]){this.raw=t,this.path=n,this.#n=i,this.#t={}}param(t){return t?this.#i(t):this.#s()}#i(t){let n=this.#n[0][this.routeIndex][1][t],i=this.#l(n);return i&&/\%/.test(i)?Mt(i):i}#s(){let t={},n=Object.keys(this.#n[0][this.routeIndex][1]);for(let i of n){let c=this.#l(this.#n[0][this.routeIndex][1][i]);if(c!==void 0)t[i]=/\%/.test(c)?Mt(c):c}return t}#l(t){return this.#n[1]?this.#n[1][t]:t}query(t){return At(this.url,t)}queries(t){return Zt(this.url,t)}header(t){if(t)return this.raw.headers.get(t)??void 0;let n={};return this.raw.headers.forEach((i,c)=>{n[c]=i}),n}async parseBody(t){return Gt(this,t)}#c=(t)=>{let{bodyCache:n,raw:i}=this,c=n[t];if(c)return c;let l=Object.keys(n)[0];if(l)return n[l].then((s)=>{if(l==="json")s=JSON.stringify(s);return new Response(s)[t]()});return n[t]=i[t]()};json(){return this.#c("text").then((t)=>JSON.parse(t))}text(){return this.#c("text")}arrayBuffer(){return this.#c("arrayBuffer")}bytes(){return this.#c("arrayBuffer").then((t)=>new Uint8Array(t))}blob(){return this.#c("blob")}formData(){return this.#c("formData")}addValidatedData(t,n){this.#t[t]=n}valid(t){return this.#t[t]}get url(){return this.raw.url}get method(){return this.raw.method}get[Xt](){return this.#n}get matchedRoutes(){return this.#n[0].map(([[,t]])=>t)}get routePath(){return this.#n[0].map(([[,t]])=>t)[this.routeIndex].path}};var It={Stringify:1,BeforeStream:2,Stream:3},In=(t,n)=>{let i=new String(t);return i.isEscaped=!0,i.callbacks=n,i};var Et=async(t,n,i,c,l)=>{if(typeof t==="object"&&!(t instanceof String)){if(!(t instanceof Promise))t=t.toString();if(t instanceof Promise)t=await t}let s=t.callbacks;if(!s?.length)return Promise.resolve(t);if(l)l[0]+=t;else l=[t];let f=Promise.all(s.map((e)=>e({phase:n,buffer:l,context:c}))).then((e)=>Promise.all(e.filter(Boolean).map((b)=>Et(b,n,!1,c,l))).then(()=>l[0]));if(i)return In(await f,s);else return f};var Un="text/plain; charset=UTF-8",Ot=(t,n)=>{return{"Content-Type":t,...n}},Y=(t,n)=>new Response(t,n),yt=class{#t;#n;env={};#i;finalized=!1;error;#s;#l;#c;#r;#e;#o;#b;#x;#u;constructor(t,n){if(this.#t=t,n)this.#l=n.executionCtx,this.env=n.env,this.#o=n.notFoundHandler,this.#u=n.path,this.#x=n.matchResult}get req(){return this.#n??=new F(this.#t,this.#u,this.#x),this.#n}get event(){if(this.#l&&"respondWith"in this.#l)return this.#l;else throw Error("This context has no FetchEvent")}get executionCtx(){if(this.#l)return this.#l;else throw Error("This context has no ExecutionContext")}get res(){return this.#c||=Y(null,{headers:this.#b??=new Headers})}set res(t){if(this.#c&&t){t=Y(t.body,t);for(let[n,i]of this.#c.headers.entries()){if(n==="content-type")continue;if(n==="set-cookie"){let c=this.#c.headers.getSetCookie();t.headers.delete("set-cookie");for(let l of c)t.headers.append("set-cookie",l)}else t.headers.set(n,i)}}this.#c=t,this.finalized=!0}render=(...t)=>{return this.#e??=(n)=>this.html(n),this.#e(...t)};setLayout=(t)=>this.#r=t;getLayout=()=>this.#r;setRenderer=(t)=>{this.#e=t};header=(t,n,i)=>{if(this.finalized)this.#c=Y(this.#c.body,this.#c);let c=this.#c?this.#c.headers:this.#b??=new Headers;if(n===void 0)c.delete(t);else if(i?.append)c.append(t,n);else c.set(t,n)};status=(t)=>{this.#s=t};set=(t,n)=>{this.#i??=new Map,this.#i.set(t,n)};get=(t)=>{return this.#i?this.#i.get(t):void 0};get var(){if(!this.#i)return{};return Object.fromEntries(this.#i)}#f(t,n,i){let c=this.#c?new Headers(this.#c.headers):this.#b??new Headers;if(typeof n==="object"&&"headers"in n){let s=n.headers instanceof Headers?n.headers:new Headers(n.headers);for(let[f,e]of s)if(f.toLowerCase()==="set-cookie")c.append(f,e);else c.set(f,e)}if(i)for(let[s,f]of Object.entries(i))if(typeof f==="string")c.set(s,f);else{c.delete(s);for(let e of f)c.append(s,e)}let l=typeof n==="number"?n:n?.status??this.#s;return Y(t,{status:l,headers:c})}newResponse=(...t)=>this.#f(...t);body=(t,n,i)=>this.#f(t,n,i);text=(t,n,i)=>{return!this.#b&&!this.#s&&!n&&!i&&!this.finalized?new Response(t):this.#f(t,n,Ot(Un,i))};json=(t,n,i)=>{return this.#f(JSON.stringify(t),n,Ot("application/json",i))};html=(t,n,i)=>{let c=(l)=>this.#f(l,n,Ot("text/html; charset=UTF-8",i));return typeof t==="object"?Et(t,It.Stringify,!1,{}).then(c):c(t)};redirect=(t,n)=>{let i=String(t);return this.header("Location",!/[^\x00-\xFF]/.test(i)?i:encodeURI(i)),this.newResponse(null,n??302)};notFound=()=>{return this.#o??=()=>Y(),this.#o(this)}};var d="ALL",Ut="all",Rt=["get","post","put","delete","options","patch"],p="Can not add a route since the matcher is already built.",h=class extends Error{};var kt="__COMPOSED_HANDLER";var Rn=(t)=>{return t.text("404 Not Found",404)},Ft=(t,n)=>{if("getResponse"in t){let i=t.getResponse();return n.newResponse(i.body,i)}return console.error(t),n.text("Internal Server Error",500)},Dt=class t{get;post;put;delete;options;patch;all;on;use;router;getPath;_basePath="/";#t="/";routes=[];constructor(n={}){[...Rt,Ut].forEach((s)=>{this[s]=(f,...e)=>{if(typeof f==="string")this.#t=f;else this.#s(s,this.#t,f);return e.forEach((b)=>{this.#s(s,this.#t,b)}),this}}),this.on=(s,f,...e)=>{for(let b of[f].flat()){this.#t=b;for(let o of[s].flat())e.map((r)=>{this.#s(o.toUpperCase(),this.#t,r)})}return this},this.use=(s,...f)=>{if(typeof s==="string")this.#t=s;else this.#t="*",f.unshift(s);return f.forEach((e)=>{this.#s(d,this.#t,e)}),this};let{strict:c,...l}=n;Object.assign(this,l),this.getPath=c??!0?n.getPath??ut:Vt}#n(){let n=new t({router:this.router,getPath:this.getPath});return n.errorHandler=this.errorHandler,n.#i=this.#i,n.routes=this.routes,n}#i=Rn;errorHandler=Ft;route(n,i){let c=this.basePath(n);return i.routes.map((l)=>{let s;if(i.errorHandler===Ft)s=l.handler;else s=async(f,e)=>(await ot([],i.errorHandler)(f,()=>l.handler(f,e))).res,s[kt]=l.handler;c.#s(l.method,l.path,s,l.basePath)}),this}basePath(n){let i=this.#n();return i._basePath=g(this._basePath,n),i}onError=(n)=>{return this.errorHandler=n,this};notFound=(n)=>{return this.#i=n,this};mount(n,i,c){let l,s;if(c)if(typeof c==="function")s=c;else if(s=c.optionHandler,c.replaceRequest===!1)l=(b)=>b;else l=c.replaceRequest;let f=s?(b)=>{let o=s(b);return Array.isArray(o)?o:[o]}:(b)=>{let o=void 0;try{o=b.executionCtx}catch{}return[b.env,o]};l||=(()=>{let b=g(this._basePath,n),o=b==="/"?0:b.length;return(r)=>{let x=new URL(r.url);return x.pathname=this.getPath(r).slice(o)||"/",new Request(x,r)}})();let e=async(b,o)=>{let r=await i(l(b.req.raw),...f(b));if(r)return r;await o()};return this.#s(d,g(n,"*"),e),this}#s(n,i,c,l){n=n.toUpperCase(),i=g(this._basePath,i);let s={basePath:l!==void 0?g(this._basePath,l):this._basePath,path:i,method:n,handler:c};this.router.add(n,i,[c,s]),this.routes.push(s)}#l(n,i){if(n instanceof Error)return this.errorHandler(n,i);throw n}#c(n,i,c,l){if(l==="HEAD")return(async()=>new Response(null,await this.#c(n,i,c,"GET")))();let s=this.getPath(n,{env:c}),f=this.router.match(l,s),e=new yt(n,{path:s,matchResult:f,env:c,executionCtx:i,notFoundHandler:this.#i});if(f[0].length===1){let o;try{o=f[0][0][0][0](e,async()=>{e.res=await this.#i(e)})}catch(r){return this.#l(r,e)}return o instanceof Promise?o.then((r)=>r||(e.finalized?e.res:this.#i(e))).catch((r)=>this.#l(r,e)):o??this.#i(e)}let b=ot(f[0],this.errorHandler,this.#i);return(async()=>{try{let o=await b(e);if(!o.finalized)throw Error("Context is not finalized. Did you forget to return a Response object or `await next()`?");return o.res}catch(o){return this.#l(o,e)}})()}fetch=(n,...i)=>{return this.#c(n,i[1],i[0],n.method)};request=(n,i,c,l)=>{if(n instanceof Request)return this.fetch(i?new Request(n,i):n,c,l);return n=n.toString(),this.fetch(new Request(/^https?:\/\//.test(n)?n:`http://localhost${g("/",n)}`,i),c,l)};fire=()=>{addEventListener("fetch",(n)=>{n.respondWith(this.#c(n.request,n,void 0,n.request.method))})}};var V=[];function q(t,n){let i=this.buildAllMatchers(),c=(l,s)=>{let f=i[l]||i[d],e=f[2][s];if(e)return e;let b=s.match(f[0]);if(!b)return[[],V];let o=b.indexOf("",1);return[f[1][o],b]};return this.match=c,c(t,n)}var tt="[^/]+",w=".*",A="(?:|/.*)",P=Symbol(),kn=new Set(".\\+*[^]$()");function Fn(t,n){if(t.length===1)return n.length===1?t<n?-1:1:-1;if(n.length===1)return 1;if(t===w||t===A)return 1;else if(n===w||n===A)return-1;if(t===tt)return 1;else if(n===tt)return-1;return t.length===n.length?t<n?-1:1:n.length-t.length}var Ct=class t{#t;#n;#i=Object.create(null);insert(n,i,c,l,s){if(n.length===0){if(this.#t!==void 0)throw P;if(s)return;this.#t=i;return}let[f,...e]=n,b=f==="*"?e.length===0?["","",w]:["","",tt]:f==="/*"?["","",A]:f.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/),o;if(b){let r=b[1],x=b[2]||tt;if(r&&b[2]){if(x===".*")throw P;if(x=x.replace(/^\((?!\?:)(?=[^)]+\)$)/,"(?:"),/\((?!\?:)/.test(x))throw P}if(o=this.#i[x],!o){if(Object.keys(this.#i).some((E)=>E!==w&&E!==A))throw P;if(s)return;if(o=this.#i[x]=new t,r!=="")o.#n=l.varIndex++}if(!s&&r!=="")c.push([r,o.#n])}else if(o=this.#i[f],!o){if(Object.keys(this.#i).some((r)=>r.length>1&&r!==w&&r!==A))throw P;if(s)return;o=this.#i[f]=new t}o.insert(e,i,c,l,s)}buildRegExpStr(){let i=Object.keys(this.#i).sort(Fn).map((c)=>{let l=this.#i[c];return(typeof l.#n==="number"?`(${c})@${l.#n}`:kn.has(c)?`\\${c}`:c)+l.buildRegExpStr()});if(typeof this.#t==="number")i.unshift(`#${this.#t}`);if(i.length===0)return"";if(i.length===1)return i[0];return"(?:"+i.join("|")+")"}};var Ht=class{#t={varIndex:0};#n=new Ct;insert(t,n,i){let c=[],l=[];for(let f=0;;){let e=!1;if(t=t.replace(/\{[^}]+\}/g,(b)=>{let o=`@\\${f}`;return l[f]=[o,b],f++,e=!0,o}),!e)break}let s=t.match(/(?::[^\/]+)|(?:\/\*$)|./g)||[];for(let f=l.length-1;f>=0;f--){let[e]=l[f];for(let b=s.length-1;b>=0;b--)if(s[b].indexOf(e)!==-1){s[b]=s[b].replace(e,l[f][1]);break}}return this.#n.insert(s,n,c,this.#t,i),c}buildRegExp(){let t=this.#n.buildRegExpStr();if(t==="")return[/^$/,[],[]];let n=0,i=[],c=[];return t=t.replace(/#(\d+)|@(\d+)|\.\*\$/g,(l,s,f)=>{if(s!==void 0)return i[++n]=Number(s),"$()";if(f!==void 0)return c[Number(f)]=++n,"";return""}),[new RegExp(`^${t}`),i,c]}};var Dn=[/^$/,[],Object.create(null)],pt=Object.create(null);function ht(t){return pt[t]??=new RegExp(t==="*"?"":`^${t.replace(/\/\*$|([.\\+*[^\]$()])/g,(n,i)=>i?`\\${i}`:"(?:|/.*)")}$`)}function Cn(){pt=Object.create(null)}function Hn(t){let n=new Ht,i=[];if(t.length===0)return Dn;let c=t.map((o)=>[!/\*|\/:/.test(o[0]),...o]).sort(([o,r],[x,E])=>o?1:x?-1:r.length-E.length),l=Object.create(null);for(let o=0,r=-1,x=c.length;o<x;o++){let[E,O,$]=c[o];if(E)l[O]=[$.map(([a])=>[a,Object.create(null)]),V];else r++;let v;try{v=n.insert(O,r,E)}catch(a){throw a===P?new h(O):a}if(E)continue;i[r]=$.map(([a,y])=>{let B=Object.create(null);y-=1;for(;y>=0;y--){let[X,ft]=v[y];B[X]=ft}return[a,B]})}let[s,f,e]=n.buildRegExp();for(let o=0,r=i.length;o<r;o++)for(let x=0,E=i[o].length;x<E;x++){let O=i[o][x]?.[1];if(!O)continue;let $=Object.keys(O);for(let v=0,a=$.length;v<a;v++)O[$[v]]=e[O[$[v]]]}let b=[];for(let o in f)b[o]=i[f[o]];return[s,b,l]}function J(t,n){if(!t)return;for(let i of Object.keys(t).sort((c,l)=>l.length-c.length))if(ht(i).test(n))return[...t[i]];return}var nt=class{name="RegExpRouter";#t;#n;constructor(){this.#t={[d]:Object.create(null)},this.#n={[d]:Object.create(null)}}add(t,n,i){let c=this.#t,l=this.#n;if(!c||!l)throw Error(p);if(!c[t])[c,l].forEach((e)=>{e[t]=Object.create(null),Object.keys(e[d]).forEach((b)=>{e[t][b]=[...e[d][b]]})});if(n==="/*")n="*";let s=(n.match(/\/:/g)||[]).length;if(/\*$/.test(n)){let e=ht(n);if(t===d)Object.keys(c).forEach((b)=>{c[b][n]||=J(c[b],n)||J(c[d],n)||[]});else c[t][n]||=J(c[t],n)||J(c[d],n)||[];Object.keys(c).forEach((b)=>{if(t===d||t===b)Object.keys(c[b]).forEach((o)=>{e.test(o)&&c[b][o].push([i,s])})}),Object.keys(l).forEach((b)=>{if(t===d||t===b)Object.keys(l[b]).forEach((o)=>e.test(o)&&l[b][o].push([i,s]))});return}let f=H(n)||[n];for(let e=0,b=f.length;e<b;e++){let o=f[e];Object.keys(l).forEach((r)=>{if(t===d||t===r)l[r][o]||=[...J(c[r],o)||J(c[d],o)||[]],l[r][o].push([i,s-b+e+1])})}}match=q;buildAllMatchers(){let t=Object.create(null);return Object.keys(this.#n).concat(Object.keys(this.#t)).forEach((n)=>{t[n]||=this.#i(n)}),this.#t=this.#n=void 0,Cn(),t}#i(t){let n=[],i=t===d;if([this.#t,this.#n].forEach((c)=>{let l=c[t]?Object.keys(c[t]).map((s)=>[s,c[t][s]]):[];if(l.length!==0)i||=!0,n.push(...l);else if(t!==d)n.push(...Object.keys(c[d]).map((s)=>[s,c[d][s]]))}),!i)return null;else return Hn(n)}};var pn=class{name="PreparedRegExpRouter";#t;#n;constructor(t,n){this.#t=t,this.#n=n}#i(t,n){let i=this.#t[t];i[1].forEach((c)=>c&&c.push(n)),Object.values(i[2]).forEach((c)=>c[0].push(n))}#s(t,n,i,c,l){let s=this.#t[t];if(!l)s[2][n][0].push([i,{}]);else c.forEach((f)=>{if(typeof f==="number")s[1][f].push([i,l]);else s[2][f||n][0].push([i,l])})}add(t,n,i){if(!this.#t[t]){let l=this.#t[d],s={};for(let f in l[2])s[f]=[l[2][f][0].slice(),V];this.#t[t]=[l[0],l[1].map((f)=>Array.isArray(f)?f.slice():0),s]}if(n==="/*"||n==="*"){let l=[i,{}];if(t===d)for(let s in this.#t)this.#i(s,l);else this.#i(t,l);return}let c=this.#n[n];if(!c)throw Error(`Path ${n} is not registered`);for(let[l,s]of c)if(t===d)for(let f in this.#t)this.#s(f,n,i,l,s);else this.#s(t,n,i,l,s)}buildAllMatchers(){return this.#t}match=q};var Nt=class{name="SmartRouter";#t=[];#n=[];constructor(t){this.#t=t.routers}add(t,n,i){if(!this.#n)throw Error(p);this.#n.push([t,n,i])}match(t,n){if(!this.#n)throw Error("Fatal error");let i=this.#t,c=this.#n,l=i.length,s=0,f;for(;s<l;s++){let e=i[s];try{for(let b=0,o=c.length;b<o;b++)e.add(...c[b]);f=e.match(t,n)}catch(b){if(b instanceof h)continue;throw b}this.match=e.match.bind(e),this.#t=[e],this.#n=void 0;break}if(s===l)throw Error("Fatal error");return this.name=`SmartRouter + ${this.activeRouter.name}`,f}get activeRouter(){if(this.#n||this.#t.length!==1)throw Error("No active router has been determined yet.");return this.#t[0]}};var Z=Object.create(null),hn=(t)=>{for(let n in t)return!0;return!1},qt=class t{#t;#n;#i;#s=0;#l=Z;constructor(n,i,c){if(this.#n=c||Object.create(null),this.#t=[],n&&i){let l=Object.create(null);l[n]={handler:i,possibleKeys:[],score:0},this.#t=[l]}this.#i=[]}insert(n,i,c){this.#s=++this.#s;let l=this,s=Qt(i),f=[];for(let e=0,b=s.length;e<b;e++){let o=s[e],r=s[e+1],x=Yt(o,r),E=Array.isArray(x)?x[0]:o;if(E in l.#n){if(l=l.#n[E],x)f.push(x[1]);continue}if(l.#n[E]=new t,x)l.#i.push(x),f.push(x[1]);l=l.#n[E]}return l.#t.push({[n]:{handler:c,possibleKeys:f.filter((e,b,o)=>o.indexOf(e)===b),score:this.#s}}),l}#c(n,i,c,l,s){for(let f=0,e=i.#t.length;f<e;f++){let b=i.#t[f],o=b[c]||b[d],r={};if(o!==void 0){if(o.params=Object.create(null),n.push(o),l!==Z||s&&s!==Z)for(let x=0,E=o.possibleKeys.length;x<E;x++){let O=o.possibleKeys[x],$=r[o.score];o.params[O]=s?.[O]&&!$?s[O]:l[O]??s?.[O],r[o.score]=!0}}}}search(n,i){let c=[];this.#l=Z;let s=[this],f=xt(i),e=[],b=f.length,o=null;for(let r=0;r<b;r++){let x=f[r],E=r===b-1,O=[];for(let v=0,a=s.length;v<a;v++){let y=s[v],B=y.#n[x];if(B)if(B.#l=y.#l,E){if(B.#n["*"])this.#c(c,B.#n["*"],n,y.#l);this.#c(c,B,n,y.#l)}else O.push(B);for(let X=0,ft=y.#i.length;X<ft;X++){let _t=y.#i[X],K=y.#l===Z?{}:{...y.#l};if(_t==="*"){let j=y.#n["*"];if(j)this.#c(c,j,n,y.#l),j.#l=K,O.push(j);continue}let[gn,Wt,G]=_t;if(!x&&!(G instanceof RegExp))continue;let L=y.#n[gn];if(G instanceof RegExp){if(o===null){o=Array(b);let k=i[0]==="/"?1:0;for(let Q=0;Q<b;Q++)o[Q]=k,k+=f[Q].length+1}let j=i.substring(o[r]),bt=G.exec(j);if(bt){if(K[Wt]=bt[0],this.#c(c,L,n,y.#l,K),hn(L.#n)){L.#l=K;let k=bt[0].match(/\//)?.length??0;(e[k]||=[]).push(L)}continue}}if(G===!0||G.test(x))if(K[Wt]=x,E){if(this.#c(c,L,n,K,y.#l),L.#n["*"])this.#c(c,L.#n["*"],n,K,y.#l)}else L.#l=K,O.push(L)}}let $=e.shift();s=$?O.concat($):O}if(c.length>1)c.sort((r,x)=>{return r.score-x.score});return[c.map(({handler:r,params:x})=>[r,x])]}};var St=class{name="TrieRouter";#t;constructor(){this.#t=new qt}add(t,n,i){let c=H(n);if(c){for(let l=0,s=c.length;l<s;l++)this.#t.insert(t,c[l],i);return}this.#t.insert(t,n,i)}match(t,n){return this.#t.search(t,n)}};var $t=class extends Dt{constructor(t={}){super(t);this.router=t.router??new Nt({routers:[new nt,new St]})}};import qn from"os";import vt from"path";var u={port:Number(process.env.JUNSO_BROWSER_PORT??8790),host:process.env.JUNSO_BROWSER_HOST??"127.0.0.1",token:process.env.JUNSO_BROWSER_TOKEN??"",dataDir:process.env.JUNSO_BROWSER_DATA_DIR??vt.join(qn.homedir(),".junso-browser"),maxInstances:Number(process.env.JUNSO_BROWSER_MAX_INSTANCES??3),idleReapMs:Number(process.env.JUNSO_BROWSER_IDLE_MS??1800000),cloakPath:process.env.JUNSO_BROWSER_CLOAK_PATH,publicUrl:process.env.JUNSO_BROWSER_PUBLIC_URL};function at(){return vt.join(u.dataDir,"profiles.json")}function it(t){return vt.join(u.dataDir,"profiles",t,"user-data")}function ct(){return u.host==="127.0.0.1"||u.host==="localhost"||u.host==="::1"}import{mkdir as ci,readFile as li,writeFile as si,rm as fi}from"fs/promises";import bi from"path";import{randomUUID as ei}from"crypto";import ti from"os";import Lt from"path";import{existsSync as tn,readdirSync as ni}from"fs";function m(){if(u.cloakPath&&tn(u.cloakPath))return u.cloakPath;let t=Lt.join(ti.homedir(),".cloakbrowser"),n;try{n=ni(t).filter((c)=>c.startsWith("chromium-"))}catch{return null}n.sort().reverse();let i=process.platform==="win32"?"chrome.exe":"chrome";for(let c of n){let l=Lt.join(t,c,i);if(tn(l))return l}return null}var Bt=!1;function nn(){if(Bt)return;if(process.env.JUNSO_BROWSER_NO_CLOAK_DOWNLOAD==="true")return;if(m())return;Bt=!0,console.log("[junso-browser] CloakBrowser binary not found \u2014 downloading in background\u2026");let t=Lt.resolve(import.meta.dir,"..");Bun.spawn([process.execPath,"x","cloakbrowser","install"],{cwd:t,stdout:"inherit",stderr:"inherit"}).exited.then((i)=>{Bt=!1,console.log(i===0?"[junso-browser] CloakBrowser ready.":`[junso-browser] cloak download exited ${i}.`)})}function ii(t){try{let n=new URL(t);return`${n.protocol}//${n.host}`}catch{return t}}function cn(t){if(!t)return null;try{let n=new URL(t);if(!n.username)return null;return{username:decodeURIComponent(n.username),password:decodeURIComponent(n.password)}}catch{return null}}function zt(){return Math.floor(Math.random()*90000)+1e4}function ln(t,n,i){let c=t.fingerprint,l=["--no-sandbox",`--fingerprint=${c.seed}`,`--fingerprint-platform=${c.platform??"windows"}`];if(c.timezone)l.push(`--fingerprint-timezone=${c.timezone}`);if(c.locale)l.push(`--fingerprint-locale=${c.locale}`,`--accept-lang=${c.locale}`);if(t.proxy)l.push(`--proxy-server=${ii(t.proxy)}`);return l.push(`--user-data-dir=${i}`,`--remote-debugging-port=${n}`,"--remote-debugging-address=127.0.0.1","--headless=new"),l}function sn(t){let n={...process.env};if(t.fingerprint.timezone)n.TZ=t.fingerprint.timezone;return n}var _=null,fn=Promise.resolve();async function M(){if(_)return _;try{let t=await li(at(),"utf8"),n=JSON.parse(t);_=n&&typeof n==="object"?n:{}}catch{_={}}return _}async function Kt(t){await ci(u.dataDir,{recursive:!0}),await si(at(),JSON.stringify(t,null,2)+`
3
+ `,"utf8"),_=t}function gt(t){let n=fn.catch(()=>{}).then(t);return fn=n.catch(()=>{}),n}function oi(t){return(t??"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}async function bn(){return Object.values(await M()).sort((t,n)=>t.name.localeCompare(n.name))}async function z(t){return(await M())[t]??null}async function en(t){let n=oi(t.name)||ei().slice(0,8);return gt(async()=>{let i={...await M()};if(i[n])throw Error(`Profile "${n}" already exists`);let c=new Date().toISOString(),l={id:n,name:t.name.trim(),fingerprint:{seed:t.fingerprint?.seed??zt(),platform:t.fingerprint?.platform??"windows",timezone:t.fingerprint?.timezone,locale:t.fingerprint?.locale},proxy:t.proxy?.trim()||void 0,createdAt:c,updatedAt:c};return i[n]=l,await Kt(i),l})}async function Pt(t,n){return gt(async()=>{let i={...await M()},c=i[t];if(!c)throw Error(`No such profile: ${t}`);let l={...c.fingerprint,...n.fingerprint??{}};if(n.rotateSeed)l.seed=zt();let s={...c,name:n.name?.trim()||c.name,fingerprint:l,proxy:n.proxy===null?void 0:n.proxy?.trim()||c.proxy,updatedAt:new Date().toISOString()};return i[t]=s,await Kt(i),s})}async function on(t){return gt(async()=>{let n={...await M()};if(!n[t])return!1;return delete n[t],await Kt(n),await fi(bi.dirname(it(t)),{recursive:!0,force:!0}),!0})}function rn(t){if(!/session-[a-z0-9]+/i.test(t))return null;let n="s"+Math.random().toString(36).slice(2,10);return t.replace(/session-[a-z0-9]+/i,`session-${n}`)}function ri(t){if(!t)return null;return t.replace(/^([a-z0-9+.-]+:\/\/[^:@/]+):[^@/]+@/i,"$1:\u2022\u2022\u2022\u2022@")}function W(t){return{...t,proxy:ri(t.proxy)??void 0}}import{mkdir as xi,readFile as ui,rm as xn}from"fs/promises";import jt from"path";class Tt{browserWsUrl;username;password;ws=null;nextId=1;constructor(t,n,i){this.browserWsUrl=t;this.username=n;this.password=i}async start(){let t=new WebSocket(this.browserWsUrl);this.ws=t,await new Promise((n,i)=>{t.onopen=()=>n(),t.onerror=()=>i(Error("proxy-auth CDP connect failed"))}),t.onmessage=(n)=>this.onMessage(JSON.parse(String(n.data))),t.onclose=()=>{this.ws=null},this.send(void 0,"Target.setAutoAttach",{autoAttach:!0,waitForDebuggerOnStart:!1,flatten:!0})}send(t,n,i){this.ws?.send(JSON.stringify({id:this.nextId++,sessionId:t,method:n,params:i}))}onMessage(t){if(t.method==="Target.attachedToTarget"){let n=t.params.sessionId;this.send(n,"Fetch.enable",{handleAuthRequests:!0})}else if(t.method==="Fetch.authRequired"){let n=t.params,i=n.authChallenge?.source==="Proxy";this.send(t.sessionId,"Fetch.continueWithAuth",{requestId:n.requestId,authChallengeResponse:i?{response:"ProvideCredentials",username:this.username,password:this.password}:{response:"Default"}})}else if(t.method==="Fetch.requestPaused")this.send(t.sessionId,"Fetch.continueRequest",{requestId:t.params.requestId})}close(){try{this.ws?.close()}catch{}this.ws=null}}var N=new Map;async function di(t,n=20000){let i=jt.join(t,"DevToolsActivePort"),c=Date.now()+n;while(Date.now()<c){try{let l=await ui(i,"utf8"),[s,f]=l.split(`
4
+ `),e=Number(s);if(e>0&&f)return{port:e,wsPath:f.trim()}}catch{}await Bun.sleep(150)}throw Error("Browser did not become ready (no DevToolsActivePort)")}async function Ei(){let t=null;for(let n of N.values())if(!t||n.instance.lastUsedAt<t.instance.lastUsedAt)t=n;if(t)await T(t.instance.profileId)}async function I(t){let n=N.get(t.id);if(n)return n.instance.lastUsedAt=new Date().toISOString(),n.instance;let i=m();if(!i)throw Error("CloakBrowser binary not found \u2014 run `cloakbrowser install`");if(u.maxInstances>0&&N.size>=u.maxInstances)await Ei();let c=it(t.id);await xi(c,{recursive:!0}),await xn(jt.join(c,"DevToolsActivePort"),{force:!0}),await xn(jt.join(c,"SingletonLock"),{force:!0});let l=ln(t,0,c),s=Bun.spawn([i,...l],{stdout:"ignore",stderr:"ignore",env:sn(t)}),f;try{f=await di(c)}catch(x){throw s.kill(),x}let e=new Date().toISOString(),b={profileId:t.id,pid:s.pid,port:f.port,browserWsPath:f.wsPath,startedAt:e,lastUsedAt:e},o={instance:b,proc:s};N.set(t.id,o);let r=cn(t.proxy);if(r){let x=new Tt(`ws://127.0.0.1:${f.port}${f.wsPath}`,r.username,r.password);try{await x.start(),o.auth=x}catch{}}return s.exited.then(()=>{if(N.get(t.id)?.proc===s)o.auth?.close(),N.delete(t.id)}),b}function lt(t){return N.has(t)}function un(t){let n=N.get(t);if(n)n.instance.lastUsedAt=new Date().toISOString()}async function T(t){let n=N.get(t);if(!n)return!1;return N.delete(t),n.auth?.close(),n.proc.kill(),await n.proc.exited.catch(()=>{}),!0}async function dn(){await Promise.all([...N.keys()].map((t)=>T(t)))}function En(){return[...N.values()].map((t)=>t.instance)}function On(){if(u.idleReapMs<=0)return null;return setInterval(()=>{let t=Date.now()-u.idleReapMs;for(let n of N.values())if(new Date(n.instance.lastUsedAt).getTime()<t)T(n.instance.profileId)},60000)}async function yi(t,n=15000){try{return(await(await fetch("https://api.ipify.org?format=json",{proxy:t||void 0,signal:AbortSignal.timeout(n)})).json()).ip??null}catch{return null}}async function Ni(t,n=1e4){try{let l=await(await fetch(`http://ip-api.com/json/${t}?fields=status,country,countryCode,timezone,isp,mobile,proxy,hosting,query`,{signal:AbortSignal.timeout(n)})).json();if(l.status!=="success")return null;let s="residential";if(l.proxy)s="proxy";else if(l.hosting)s="hosting";else if(l.mobile)s="mobile";return{ip:l.query??t,country:l.country,countryCode:l.countryCode,timezone:l.timezone,isp:l.isp,kind:s}}catch{return null}}async function st(t){let n=await yi(t);if(!n)return null;return await Ni(n)??{ip:n,kind:"unknown"}}function Jt(t){return{timezone:t.timezone,locale:t.countryCode?`en-${t.countryCode}`:void 0}}var yn={name:"junso-browser",version:"0.2.3",description:"Standalone CloakBrowser host \u2014 runs fingerprinted stealth-Chromium per profile, exposes each over a token-authed CDP gateway + interactive viewport, and ships an optional MCP server so any client gets browser tools with no local browser.",type:"module",bin:{"junso-browser":"bin/junso-browser.js","junso-browser-mcp":"bin/junso-browser-mcp.js"},files:["bin","dist","README.md"],engines:{bun:">=1.2.0"},keywords:["browser","cloakbrowser","stealth","fingerprint","cdp","mcp","proxy","automation"],license:"UNLICENSED",scripts:{dev:"bun --watch src/server.ts",start:"bun src/server.ts",mcp:"bun src/mcp.ts",build:"bun build src/server.ts src/mcp.ts --target=bun --minify --outdir=dist","build:binary":"bun build src/server.ts --compile --outfile dist/junso-browser",typecheck:"bunx tsc --noEmit",test:"bun test",prepublishOnly:"bun run build"},dependencies:{"agent-browser":"^0.27.1",cloakbrowser:"^0.3.31"},devDependencies:{"@modelcontextprotocol/sdk":"^1.29.0","@types/bun":"latest",hono:"^4.6.0",typescript:"^5.6.0",zod:"3"}};function $i(t){return/^https?:|^file:/.test(t)}class Nn{browserWsUrl;cdp=null;sessionId=null;activeTargetId=null;nextId=1;pending=new Map;starting=null;targets=new Map;lastFrame=null;lastUrl=null;viewers=new Set;constructor(t){this.browserWsUrl=t}rpc(t,n={},i){if(!this.cdp)return Promise.reject(Error("cdp closed"));let c=this.nextId++;return this.cdp.send(JSON.stringify({id:c,method:t,params:n,sessionId:i})),new Promise((l)=>this.pending.set(c,l))}async start(){if(this.starting)return this.starting;return this.starting=(async()=>{let t=new WebSocket(this.browserWsUrl);this.cdp=t,await new Promise((l,s)=>{t.onopen=()=>l(),t.onerror=()=>s(Error("CDP connect failed"))}),t.onmessage=(l)=>this.onMessage(JSON.parse(String(l.data))),t.onclose=()=>{this.cdp=null,this.sessionId=null},await this.rpc("Target.setDiscoverTargets",{discover:!0});let{targetInfos:n}=await this.rpc("Target.getTargets"),i=n.filter((l)=>l.type==="page");for(let l of i)this.targets.set(l.targetId,{url:l.url,title:l.title});let c=i.find((l)=>$i(l.url))??i[0];if(!c)throw Error("no page target to stream");await this.attachTo(c.targetId)})(),this.starting}async attachTo(t){if(this.sessionId){let i=this.sessionId;this.sessionId=null;try{await this.rpc("Target.detachFromTarget",{sessionId:i})}catch{}}let{sessionId:n}=await this.rpc("Target.attachToTarget",{targetId:t,flatten:!0});this.sessionId=n,this.activeTargetId=t,await this.rpc("Page.enable",{},n),await this.rpc("Page.startScreencast",{format:"jpeg",quality:60,maxWidth:1280,maxHeight:800,everyNthFrame:1},n);try{let i=await this.rpc("Page.getNavigationHistory",{},n);this.lastUrl=i.entries[i.currentIndex]?.url??null}catch{}if(this.broadcastTabs(),this.lastUrl)this.broadcast({type:"url",url:this.lastUrl})}async switchTarget(t){if(!this.targets.has(t)||t===this.activeTargetId)return;this.lastFrame=null,await this.attachTo(t).catch(()=>{})}tabList(){return[...this.targets.entries()].map(([t,n])=>({targetId:t,url:n.url,title:n.title,active:t===this.activeTargetId}))}broadcastTabs(){this.broadcast({type:"tabs",tabs:this.tabList()})}onMessage(t){if(t.id!==void 0&&this.pending.has(t.id)){this.pending.get(t.id)(t.result),this.pending.delete(t.id);return}if(t.method==="Page.screencastFrame"){let n=t.params;this.rpc("Page.screencastFrameAck",{sessionId:n.sessionId},this.sessionId??void 0);let i=JSON.stringify({type:"frame",data:n.data,metadata:n.metadata});this.lastFrame=i;for(let c of this.viewers)c.send(i)}else if(t.method==="Page.frameNavigated"){let n=t.params.frame;if(n&&!n.parentId&&n.url){if(this.lastUrl=n.url,this.activeTargetId){let i=this.targets.get(this.activeTargetId);if(i)i.url=n.url}this.broadcast({type:"url",url:n.url})}}else if(t.method==="Target.targetCreated"||t.method==="Target.targetInfoChanged"){let n=t.params.targetInfo;if(n&&n.type==="page")this.targets.set(n.targetId,{url:n.url,title:n.title}),this.broadcastTabs()}else if(t.method==="Target.targetDestroyed"){let n=t.params.targetId;if(this.targets.delete(n)){if(this.broadcastTabs(),n===this.activeTargetId){let i=[...this.targets.keys()][0];if(i)this.switchTarget(i)}}}}broadcast(t){let n=JSON.stringify(t);for(let i of this.viewers)i.send(n)}sendStateTo(t){if(this.lastFrame)t.send(this.lastFrame);if(this.lastUrl)t.send(JSON.stringify({type:"url",url:this.lastUrl}));t.send(JSON.stringify({type:"tabs",tabs:this.tabList()}))}input(t){if(!this.cdp||!this.sessionId)return;let n=t.t;if(n==="m")this.rpc("Input.dispatchMouseEvent",{type:t.type,x:t.x,y:t.y,button:t.button??"none",buttons:t.buttons??0,clickCount:t.clickCount??0,deltaX:t.deltaX??0,deltaY:t.deltaY??0,modifiers:t.modifiers??0},this.sessionId);else if(n==="k")this.rpc("Input.dispatchKeyEvent",{type:t.type,key:t.key,code:t.code,text:t.text,windowsVirtualKeyCode:t.keyCode,nativeVirtualKeyCode:t.keyCode,modifiers:t.modifiers??0},this.sessionId);else if(n==="tab"){let i=String(t.targetId??"");if(i)this.switchTarget(i)}else if(n==="nav"){let i=String(t.url??"").trim();if(i)this.rpc("Page.navigate",{url:/^[a-z]+:\/\//i.test(i)?i:`https://${i}`},this.sessionId)}else if(n==="reload")this.rpc("Page.reload",{},this.sessionId);else if(n==="back"||n==="forward")(async()=>{let i=await this.rpc("Page.getNavigationHistory",{},this.sessionId),c=i.currentIndex+(n==="forward"?1:-1),l=i.entries[c];if(l)await this.rpc("Page.navigateToHistoryEntry",{entryId:l.id},this.sessionId)})()}close(){try{this.cdp?.close()}catch{}this.cdp=null}}var U=new Map;async function Sn(t,n){let i=await z(t);if(!i)throw Error(`no such profile: ${t}`);let c=await I(i),l=U.get(t);if(!l)l=new Nn(`ws://127.0.0.1:${c.port}${c.browserWsPath}`),U.set(t,l);await l.start(),l.viewers.add(n),l.sendStateTo(n)}function $n(t,n){let i=U.get(t);if(!i)return;if(i.viewers.delete(n),i.viewers.size===0)i.close(),U.delete(t)}function vn(t,n){let i=U.get(t);if(!i)return;try{i.input(JSON.parse(n))}catch{}}function an(t,n){let i=JSON.stringify(t),c=JSON.stringify(n||"");return`<!doctype html>
5
+ <html><head><meta charset="utf-8"><title>junso-browser \xB7 ${t}</title>
6
6
  <style>
7
7
  html,body{margin:0;height:100%;background:#111;color:#ccc;font:13px system-ui}
8
- #bar{padding:6px 10px;background:#1b1b1b;border-bottom:1px solid #333;display:flex;gap:10px;align-items:center}
9
- #wrap{display:flex;align-items:center;justify-content:center;height:calc(100% - 33px)}
8
+ #bar{padding:6px 10px;background:#1b1b1b;border-bottom:1px solid #333;display:flex;gap:8px;align-items:center}
9
+ #bar input{flex:1;background:#0d0d0d;border:1px solid #333;color:#ddd;padding:5px 8px;border-radius:6px;font:12px ui-monospace,monospace}
10
+ #bar select{flex:none;background:#0d0d0d;border:1px solid #333;color:#ddd;padding:5px;border-radius:6px;font:12px system-ui;max-width:140px}
11
+ #bar button{background:#2a2a2a;border:1px solid #3a3a3a;color:#ddd;padding:5px 9px;border-radius:6px;cursor:pointer}
12
+ #wrap{display:flex;align-items:center;justify-content:center;height:calc(100% - 37px)}
10
13
  #v{max-width:100%;max-height:100%;cursor:crosshair;background:#000;outline:none}
11
- .dot{width:8px;height:8px;border-radius:50%;background:#888;display:inline-block}
14
+ .dot{width:8px;height:8px;border-radius:50%;background:#888;display:inline-block;flex:none}
12
15
  .on{background:#3fb950}
13
16
  </style></head>
14
17
  <body>
15
- <div id="bar"><span class="dot" id="st"></span><b>${f}</b>
16
- <span style="color:#888">\u2014 click/type to control. Keyboard works while the viewport is focused.</span></div>
18
+ <div id="bar">
19
+ <span class="dot" id="st"></span>
20
+ <select id="prof" title="Switch identity"></select>
21
+ <select id="tabs" title="Switch tab (the MCP often drives its own tab)"></select>
22
+ <button id="bk" title="Back">\u2039</button>
23
+ <button id="fw" title="Forward">\u203A</button>
24
+ <button id="rl" title="Reload">\u27F3</button>
25
+ <input id="url" placeholder="https://\u2026 (Enter to go)" spellcheck="false"/>
26
+ <button id="go">Go</button>
27
+ <button id="shot" title="Download screenshot">\u2913</button>
28
+ <span id="ip" title="exit IP" style="color:#888;font:11px ui-monospace,monospace;white-space:nowrap;cursor:pointer">IP?</span>
29
+ </div>
17
30
  <div id="wrap"><img id="v" tabindex="0" draggable="false"/></div>
18
31
  <script>
19
- const PROFILE=${$}, TOKEN=${O};
20
- const img=document.getElementById("v"), st=document.getElementById("st");
32
+ const PROFILE=${i}, TOKEN=${c};
33
+ const img=document.getElementById("v"), st=document.getElementById("st"), urlBox=document.getElementById("url");
21
34
  let meta={deviceWidth:1280,deviceHeight:800};
22
35
  const proto=location.protocol==="https:"?"wss":"ws";
23
36
  const ws=new WebSocket(proto+"://"+location.host+"/stream/"+encodeURIComponent(PROFILE)+(TOKEN?"?token="+encodeURIComponent(TOKEN):""));
24
37
  ws.onopen=()=>st.classList.add("on");
25
38
  ws.onclose=()=>st.classList.remove("on");
26
- ws.onmessage=(e)=>{const m=JSON.parse(e.data); if(m.type==="frame"){img.src="data:image/jpeg;base64,"+m.data; if(m.metadata) meta=m.metadata;}};
39
+ const tabsSel=document.getElementById("tabs");
40
+ function tabLabel(t){ var u=t.url||""; try{ if(/^https?:/.test(u)) return new URL(u).hostname+(t.title?" \xB7 "+t.title:""); }catch(e){} return t.title||u||"tab"; }
41
+ ws.onmessage=(e)=>{const m=JSON.parse(e.data);
42
+ if(m.type==="frame"){img.src="data:image/jpeg;base64,"+m.data; if(m.metadata) meta=m.metadata;}
43
+ else if(m.type==="url"){ if(document.activeElement!==urlBox) urlBox.value=m.url; }
44
+ else if(m.type==="tabs"){
45
+ tabsSel.style.display = (m.tabs && m.tabs.length>1) ? "" : "none";
46
+ tabsSel.innerHTML = (m.tabs||[]).map(function(t){ return '<option value="'+t.targetId+'"'+(t.active?" selected":"")+'>'+tabLabel(t).replace(/[<>&]/g,"")+'</option>'; }).join("");
47
+ }
48
+ };
49
+ tabsSel.addEventListener("change",()=>{ send({t:"tab",targetId:tabsSel.value}); img.focus(); });
27
50
  function mods(e){return (e.altKey?1:0)|(e.ctrlKey?2:0)|(e.metaKey?4:0)|(e.shiftKey?8:0);}
28
51
  function pos(e){const r=img.getBoundingClientRect(); return {x:Math.round((e.clientX-r.left)/r.width*(meta.deviceWidth||1280)), y:Math.round((e.clientY-r.top)/r.height*(meta.deviceHeight||800))};}
29
52
  const BTN=["left","middle","right"];
30
53
  function send(o){if(ws.readyState===1)ws.send(JSON.stringify(o));}
54
+ function navigate(){const u=urlBox.value.trim(); if(u){send({t:"nav",url:u}); img.focus();}}
55
+ document.getElementById("go").addEventListener("click",navigate);
56
+ document.getElementById("rl").addEventListener("click",()=>send({t:"reload"}));
57
+ document.getElementById("bk").addEventListener("click",()=>send({t:"back"}));
58
+ document.getElementById("fw").addEventListener("click",()=>send({t:"forward"}));
59
+ urlBox.addEventListener("keydown",e=>{ e.stopPropagation(); if(e.key==="Enter") navigate(); });
60
+ // Screenshot: download the current frame client-side.
61
+ document.getElementById("shot").addEventListener("click",()=>{ if(!img.src)return; const a=document.createElement("a"); a.href=img.src; a.download=PROFILE+"-"+Date.now()+".jpg"; a.click(); });
62
+ // REST helper (token-authed) for profile list + exit IP.
63
+ const H = TOKEN ? {authorization:"Bearer "+TOKEN} : {};
64
+ const qs = TOKEN ? "?token="+encodeURIComponent(TOKEN) : "";
65
+ // Exit-IP badge.
66
+ const ipEl=document.getElementById("ip");
67
+ ipEl.addEventListener("click",async()=>{ ipEl.textContent="IP\u2026"; try{ const r=await fetch("/api/profiles/"+encodeURIComponent(PROFILE)+"/exit-ip",{headers:H}); const d=await r.json(); ipEl.textContent=(d.ip||"?")+(d.viaProxy?" (proxy)":" (direct)"); }catch{ ipEl.textContent="IP err"; } });
68
+ // Profile switcher.
69
+ const profSel=document.getElementById("prof");
70
+ fetch("/api/profiles",{headers:H}).then(r=>r.json()).then(({profiles})=>{
71
+ profSel.innerHTML=profiles.map(p=>'<option value="'+p.name+'"'+(p.name===PROFILE?" selected":"")+'>'+p.name+'</option>').join("");
72
+ }).catch(()=>{ profSel.innerHTML='<option selected>'+PROFILE+'</option>'; });
73
+ profSel.addEventListener("change",()=>{ location.href="/view/"+encodeURIComponent(profSel.value)+qs; });
31
74
  img.addEventListener("mousemove",e=>{const p=pos(e);send({t:"m",type:"mouseMoved",x:p.x,y:p.y,buttons:e.buttons,modifiers:mods(e)});});
32
75
  img.addEventListener("mousedown",e=>{e.preventDefault();img.focus();const p=pos(e);send({t:"m",type:"mousePressed",x:p.x,y:p.y,button:BTN[e.button]||"left",buttons:e.buttons,clickCount:e.detail||1,modifiers:mods(e)});});
33
76
  img.addEventListener("mouseup",e=>{e.preventDefault();const p=pos(e);send({t:"m",type:"mouseReleased",x:p.x,y:p.y,button:BTN[e.button]||"left",buttons:e.buttons,clickCount:e.detail||1,modifiers:mods(e)});});
@@ -35,5 +78,111 @@ img.addEventListener("contextmenu",e=>e.preventDefault());
35
78
  img.addEventListener("wheel",e=>{e.preventDefault();const p=pos(e);send({t:"m",type:"mouseWheel",x:p.x,y:p.y,deltaX:e.deltaX,deltaY:e.deltaY,modifiers:mods(e)});},{passive:false});
36
79
  img.addEventListener("keydown",e=>{e.preventDefault();const printable=e.key.length===1; send({t:"k",type:"keyDown",key:e.key,code:e.code,keyCode:e.keyCode,modifiers:mods(e),text:printable?e.key:undefined});});
37
80
  img.addEventListener("keyup",e=>{e.preventDefault();send({t:"k",type:"keyUp",key:e.key,code:e.code,keyCode:e.keyCode,modifiers:mods(e)});});
38
- </script></body></html>`}var Y0="0.1.0";function x1(f,c){let $=f.headers.get("authorization");if($?.startsWith("Bearer "))return $.slice(7);return f.headers.get("x-junso-token")??c.searchParams.get("token")}function Nf(f,c){if(!X.token)return $f();return x1(f,c)===X.token}function _f(f){let c=X.publicUrl?.replace(/\/$/,"")??`ws://${X.host==="0.0.0.0"?"127.0.0.1":X.host}:${X.port}`,$=X.token?`?token=${encodeURIComponent(X.token)}`:"";return`${c}/cdp/${f}${$}`}async function E1(f,c,$){if(!Nf(f,c))return new Response("Unauthorized",{status:401});let O=c.pathname.slice(5),N=O.split("/")[0]??"",b=await _(N);if(!b)return new Response("No such profile",{status:404});let x;try{x=await s(b)}catch(E){return new Response(`Launch failed: ${E instanceof Error?E.message:E}`,{status:502})}if(x0(N),f.headers.get("upgrade")?.toLowerCase()==="websocket"){let E=`ws://127.0.0.1:${x.port}${x.browserWsPath}`;return $.upgrade(f,{data:{kind:"cdp",profileId:N,targetWsUrl:E,upstream:null,queue:[],closed:!1}})?void 0:new Response("WebSocket upgrade required",{status:426})}let K=O.slice(N.length);if(K.startsWith("/json"))try{let E=await fetch(`http://127.0.0.1:${x.port}${K}`),S=await E.text(),J=_f(N);return S=S.replace(/ws:\/\/127\.0\.0\.1:\d+\/devtools\/browser\/[a-zA-Z0-9-]+/g,J),new Response(S,{status:E.status,headers:{"content-type":"application/json"}})}catch{return new Response("CDP discovery failed",{status:502})}return new Response("Not found",{status:404})}var K1={open(f){if(f.data.kind==="stream"){let O={send:(N)=>f.send(N)};f.data.viewer=O,W0(f.data.profileId,O).catch(()=>f.close());return}let c=f.data,$=new WebSocket(c.targetWsUrl);$.binaryType="arraybuffer",c.upstream=$,$.onopen=()=>{for(let O of c.queue)$.send(O);c.queue=[]},$.onmessage=(O)=>{if(!c.closed)f.send(O.data)},$.onclose=()=>{if(!c.closed)f.close()},$.onerror=()=>{if(!c.closed)f.close()}},message(f,c){if(f.data.kind==="stream"){if(typeof c==="string")Q0(f.data.profileId,c);return}let $=f.data.upstream,O=typeof c==="string"?c:new Uint8Array(c).buffer;if($&&$.readyState===WebSocket.OPEN)$.send(O);else f.data.queue.push(O)},close(f){if(f.data.kind==="stream"){if(f.data.viewer)X0(f.data.profileId,f.data.viewer);return}f.data.closed=!0;try{f.data.upstream?.close()}catch{}}},A=new zf;A.use("/api/*",async(f,c)=>{if(!Nf(f.req.raw,new URL(f.req.url)))return f.json({error:"Unauthorized"},401);await c()});A.get("/health",(f)=>f.json({ok:!0,name:"junso-browser",version:Y0,cloak:Boolean(i()),running:K0().length,maxInstances:X.maxInstances}));A.get("/api/profiles",async(f)=>{let c=await c0();return f.json({profiles:c.map(($)=>({...w($),running:Of($.id)}))})});A.post("/api/profiles",async(f)=>{let c=await f.req.json().catch(()=>({}));if(!c?.name||typeof c.name!=="string")return f.json({error:"name is required"},400);try{let $=await $0(c);return f.json({profile:w($)},201)}catch($){return f.json({error:$ instanceof Error?$.message:"create failed"},400)}});A.get("/api/profiles/:id",async(f)=>{let c=await _(f.req.param("id"));if(!c)return f.json({error:"not found"},404);return f.json({profile:w(c),running:Of(c.id)})});A.patch("/api/profiles/:id",async(f)=>{let c=await f.req.json().catch(()=>({}));try{let $=await O0(f.req.param("id"),c);return f.json({profile:w($)})}catch($){return f.json({error:$ instanceof Error?$.message:"update failed"},400)}});A.delete("/api/profiles/:id",async(f)=>{let c=f.req.param("id");return await D(c),await N0(c)?f.json({ok:!0}):f.json({error:"not found"},404)});A.post("/api/profiles/:id/launch",async(f)=>{let c=await _(f.req.param("id"));if(!c)return f.json({error:"not found"},404);try{return await s(c),f.json({cdpUrl:_f(c.id),running:!0})}catch($){return f.json({error:$ instanceof Error?$.message:"launch failed"},502)}});A.post("/api/profiles/:id/stop",async(f)=>{let c=await D(f.req.param("id"));return f.json({stopped:c})});A.get("/api/profiles/:id/cdp",async(f)=>{let c=await _(f.req.param("id"));if(!c)return f.json({error:"not found"},404);return f.json({cdpUrl:_f(c.id),running:Of(c.id)})});if(!X.token&&!$f())console.error(`[junso-browser] REFUSING TO START: bound to a non-loopback host with no JUNSO_BROWSER_TOKEN set.
39
- Set JUNSO_BROWSER_TOKEN (the CDP endpoint = full browser control).`),process.exit(1);var S1=Bun.serve({port:X.port,hostname:X.host,idleTimeout:0,async fetch(f,c){let $=new URL(f.url);if($.pathname.startsWith("/cdp/"))return E1(f,$,c);if($.pathname.startsWith("/view/")){if(!Nf(f,$))return new Response("Unauthorized",{status:401});let O=$.pathname.slice(6).split("/")[0]??"";return new Response(G0(O,X.token),{headers:{"content-type":"text/html; charset=utf-8"}})}if($.pathname.startsWith("/stream/")){if(!Nf(f,$))return new Response("Unauthorized",{status:401});let O=$.pathname.slice(8).split("/")[0]??"";return c.upgrade(f,{data:{kind:"stream",profileId:O,viewer:null}})?void 0:new Response("WebSocket upgrade required",{status:426})}return A.fetch(f)},websocket:K1});af();S0();var V0=async()=>{await E0(),process.exit(0)};process.on("SIGINT",V0);process.on("SIGTERM",V0);console.log(`[junso-browser] v${Y0} on http://${X.host}:${S1.port} (cloak ${i()?"found":"MISSING"}, auth ${X.token?"token":$f()?"loopback-open":"off"})`);
81
+ </script></body></html>`}function Bn(t){return`<!doctype html><html><head><meta charset="utf-8"><title>junso-browser</title>
82
+ <style>
83
+ :root{color-scheme:dark}
84
+ body{margin:0;background:#0f0f10;color:#ddd;font:14px system-ui;padding:20px;max-width:1100px;margin:0 auto}
85
+ h1{font-size:18px;margin:0 0 4px} .sub{color:#888;margin:0 0 18px;font-size:12px}
86
+ .card{background:#191a1b;border:1px solid #2a2b2d;border-radius:10px;padding:14px;margin-bottom:16px}
87
+ label{display:block;font-size:11px;color:#999;margin:6px 0 2px}
88
+ input,select{background:#0d0d0e;border:1px solid #333;color:#eee;border-radius:6px;padding:6px 8px;font:13px system-ui;width:100%;box-sizing:border-box}
89
+ .row{display:flex;gap:10px;flex-wrap:wrap} .row>div{flex:1;min-width:130px}
90
+ button{background:#2563eb;border:0;color:#fff;border-radius:6px;padding:7px 12px;cursor:pointer;font:13px system-ui}
91
+ button.sec{background:#2a2b2d;border:1px solid #3a3b3d} button.danger{background:#3a1d1d;border:1px solid #5a2a2a;color:#f3b0b0}
92
+ table{width:100%;border-collapse:collapse;margin-top:6px} th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #232425;font-size:13px;vertical-align:middle}
93
+ th{color:#888;font-weight:600;font-size:11px;text-transform:uppercase}
94
+ .dot{width:8px;height:8px;border-radius:50%;background:#555;display:inline-block} .dot.on{background:#3fb950}
95
+ code{background:#0d0d0e;padding:2px 6px;border-radius:4px;font-size:12px} .muted{color:#777}
96
+ .acts{display:flex;gap:6px;flex-wrap:wrap} .acts button{padding:4px 8px;font-size:12px}
97
+ #err{color:#f3b0b0;font-size:12px;min-height:16px} a{color:#6ea8fe}
98
+ </style></head><body>
99
+ <h1>junso-browser</h1>
100
+ <p class="sub">Fingerprinted, proxied browser identities. Open a viewport to watch + control one live.</p>
101
+ <div id="err"></div>
102
+
103
+ <div class="card">
104
+ <b style="font-size:13px">New profile</b>
105
+ <div class="row">
106
+ <div><label>Name</label><input id="n_name" placeholder="acct-a"></div>
107
+ <div><label>Platform</label><select id="n_plat"><option value="windows">windows</option><option value="macos">macos</option></select></div>
108
+ <div><label>Timezone</label><input id="n_tz" placeholder="America/New_York"></div>
109
+ <div><label>Locale</label><input id="n_loc" placeholder="en-US"></div>
110
+ </div>
111
+ <div class="row" style="margin-top:6px"><div style="flex:3"><label>Proxy (optional)</label><input id="n_proxy" placeholder="http://user:pass@host:port or socks5://..."></div>
112
+ <div style="flex:0;display:flex;align-items:flex-end"><button id="create">Create</button></div></div>
113
+ </div>
114
+
115
+ <div class="card">
116
+ <b style="font-size:13px">Profiles</b>
117
+ <table><thead><tr><th></th><th>Name</th><th>Device</th><th>Proxy</th><th>Exit IP</th><th>Actions</th></tr></thead>
118
+ <tbody id="rows"><tr><td colspan="6" class="muted">loading\u2026</td></tr></tbody></table>
119
+ </div>
120
+
121
+ <script>
122
+ const TOKEN=${JSON.stringify(t||"")};
123
+ const H = TOKEN ? {authorization:"Bearer "+TOKEN} : {};
124
+ const qs = TOKEN ? "?token="+encodeURIComponent(TOKEN) : "";
125
+ const errEl=document.getElementById("err");
126
+ const err=(m)=>{ errEl.textContent=m||""; };
127
+ async function api(method,path,body){
128
+ const r=await fetch(path,{method,headers:Object.assign({},H,body?{"content-type":"application/json"}:{}),body:body?JSON.stringify(body):undefined});
129
+ const t=await r.text(); let d=null; try{d=t?JSON.parse(t):null}catch(e){}
130
+ if(!r.ok) throw new Error((d&&d.error)||("HTTP "+r.status)); return d;
131
+ }
132
+ function esc(s){ return String(s==null?"":s).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c];}); }
133
+ function rowHtml(p){
134
+ var fp=p.fingerprint||{};
135
+ var proxy = p.proxy ? '<code>'+esc(p.proxy)+'</code>' : '<span class="muted">none</span>';
136
+ var startStop = p.running
137
+ ? '<button class="sec" data-a="stop" data-id="'+esc(p.id)+'">Stop</button>'
138
+ : '<button class="sec" data-a="launch" data-id="'+esc(p.id)+'">Start</button>';
139
+ return '<tr>'
140
+ + '<td><span class="dot '+(p.running?"on":"")+'" title="'+(p.running?"running":"stopped")+'"></span></td>'
141
+ + '<td><b>'+esc(p.name)+'</b></td>'
142
+ + '<td class="muted">'+esc(fp.platform||"?")+' \xB7 '+esc(fp.timezone||"host tz")+' \xB7 seed '+esc(fp.seed||"?")+'</td>'
143
+ + '<td>'+proxy+'</td>'
144
+ + '<td><span id="ip_'+esc(p.id)+'" class="muted">\u2014</span> <button class="sec" data-a="ip" data-id="'+esc(p.id)+'">check</button></td>'
145
+ + '<td class="acts">'
146
+ + '<a href="/view/'+encodeURIComponent(p.id)+qs+'" target="_blank"><button>Open</button></a>'
147
+ + startStop
148
+ + '<button class="sec" data-a="proxy" data-id="'+esc(p.id)+'">Proxy</button>'
149
+ + (p.proxy ? '<button class="sec" data-a="rotateproxy" data-id="'+esc(p.id)+'">Rotate IP</button>' : '')
150
+ + '<button class="sec" data-a="rotate" data-id="'+esc(p.id)+'">New seed</button>'
151
+ + '<button class="danger" data-a="del" data-id="'+esc(p.id)+'">Delete</button>'
152
+ + '</td></tr>';
153
+ }
154
+ async function load(){
155
+ try{ var r=await api("GET","/api/profiles"); var ps=r.profiles||[];
156
+ document.getElementById("rows").innerHTML = ps.length ? ps.map(rowHtml).join("") : '<tr><td colspan="6" class="muted">no profiles yet \u2014 create one above</td></tr>';
157
+ err("");
158
+ }catch(e){ err("load failed: "+e.message); }
159
+ }
160
+ document.getElementById("create").addEventListener("click",async function(){
161
+ var name=document.getElementById("n_name").value.trim(); if(!name)return err("name required");
162
+ try{
163
+ await api("POST","/api/profiles",{name:name,proxy:document.getElementById("n_proxy").value.trim()||undefined,
164
+ fingerprint:{platform:document.getElementById("n_plat").value,timezone:document.getElementById("n_tz").value.trim()||undefined,locale:document.getElementById("n_loc").value.trim()||undefined}});
165
+ document.getElementById("n_name").value=document.getElementById("n_proxy").value=document.getElementById("n_tz").value=document.getElementById("n_loc").value="";
166
+ load();
167
+ }catch(e){ err(e.message); }
168
+ });
169
+ document.getElementById("rows").addEventListener("click",async function(e){
170
+ var b=e.target.closest("button[data-a]"); if(!b)return;
171
+ var id=b.getAttribute("data-id"), a=b.getAttribute("data-a");
172
+ try{
173
+ if(a==="ip"){ var el=document.getElementById("ip_"+id); el.textContent="\u2026";
174
+ var r=await api("GET","/api/profiles/"+id+"/exit-ip");
175
+ var bits=[r.ip||"?"]; if(r.countryCode)bits.push(r.countryCode); if(r.isp)bits.push(r.isp); if(r.kind&&r.kind!=="unknown")bits.push(r.kind);
176
+ el.innerHTML=esc(bits.join(" \xB7 "))+(r.tzMismatch?' <span style="color:#f3b0b0" title="stored fingerprint timezone does not match the live exit IP">\u26A0 tz\u2260IP</span>':'');
177
+ el.className=""; return; }
178
+ if(a==="del"){ if(!confirm("Delete profile \\""+id+"\\"? Its cookies/logins are erased."))return; await api("DELETE","/api/profiles/"+id); }
179
+ else if(a==="proxy"){ var v=prompt("Proxy for \\""+id+"\\" (blank = clear):",""); if(v===null)return; await api("PATCH","/api/profiles/"+id,{proxy:v.trim()||null}); await api("POST","/api/profiles/"+id+"/stop").catch(function(){}); }
180
+ else if(a==="rotateproxy"){ err("rotating IP\u2026"); var r=await api("POST","/api/profiles/"+id+"/rotate-proxy"); err("new exit IP: "+(r.exitIp||"?")+(r.country?" \xB7 "+r.country:"")+(r.isp?" \xB7 "+r.isp:"")+(r.kind&&r.kind!=="unknown"?" \xB7 "+r.kind:"")); }
181
+ else if(a==="rotate"){ await api("PATCH","/api/profiles/"+id,{rotateSeed:true}); }
182
+ else { await api("POST","/api/profiles/"+id+"/"+a); }
183
+ load();
184
+ }catch(e){ err(e.message); }
185
+ });
186
+ load(); setInterval(load,5000);
187
+ </script></body></html>`}var Ln=yn.version;function vi(t,n){let i=t.headers.get("authorization");if(i?.startsWith("Bearer "))return i.slice(7);return t.headers.get("x-junso-token")??n.searchParams.get("token")}function R(t,n){if(!u.token)return ct();return vi(t,n)===u.token}function mt(t){let n=u.publicUrl?.replace(/\/$/,"")??`ws://${u.host==="0.0.0.0"?"127.0.0.1":u.host}:${u.port}`,i=u.token?`?token=${encodeURIComponent(u.token)}`:"";return`${n}/cdp/${t}${i}`}async function ai(t,n,i){if(!R(t,n))return new Response("Unauthorized",{status:401});let c=n.pathname.slice(5),l=c.split("/")[0]??"",s=await z(l);if(!s)return new Response("No such profile",{status:404});let f;try{f=await I(s)}catch(b){return new Response(`Launch failed: ${b instanceof Error?b.message:b}`,{status:502})}if(un(l),t.headers.get("upgrade")?.toLowerCase()==="websocket"){let b=`ws://127.0.0.1:${f.port}${f.browserWsPath}`;return i.upgrade(t,{data:{kind:"cdp",profileId:l,targetWsUrl:b,upstream:null,queue:[],closed:!1}})?void 0:new Response("WebSocket upgrade required",{status:426})}let e=c.slice(l.length);if(e.startsWith("/json"))try{let b=await fetch(`http://127.0.0.1:${f.port}${e}`),o=await b.text(),r=mt(l);return o=o.replace(/ws:\/\/127\.0\.0\.1:\d+\/devtools\/browser\/[a-zA-Z0-9-]+/g,r),new Response(o,{status:b.status,headers:{"content-type":"application/json"}})}catch{return new Response("CDP discovery failed",{status:502})}return new Response("Not found",{status:404})}var Bi={open(t){if(t.data.kind==="stream"){let c={send:(l)=>t.send(l)};t.data.viewer=c,Sn(t.data.profileId,c).catch(()=>t.close());return}let n=t.data,i=new WebSocket(n.targetWsUrl);i.binaryType="arraybuffer",n.upstream=i,i.onopen=()=>{for(let c of n.queue)i.send(c);n.queue=[]},i.onmessage=(c)=>{if(!n.closed)t.send(c.data)},i.onclose=()=>{if(!n.closed)t.close()},i.onerror=()=>{if(!n.closed)t.close()}},message(t,n){if(t.data.kind==="stream"){if(typeof n==="string")vn(t.data.profileId,n);return}let i=t.data.upstream,c=typeof n==="string"?n:new Uint8Array(n).buffer;if(i&&i.readyState===WebSocket.OPEN)i.send(c);else t.data.queue.push(c)},close(t){if(t.data.kind==="stream"){if(t.data.viewer)$n(t.data.profileId,t.data.viewer);return}t.data.closed=!0;try{t.data.upstream?.close()}catch{}}},S=new $t;S.use("/api/*",async(t,n)=>{if(!R(t.req.raw,new URL(t.req.url)))return t.json({error:"Unauthorized"},401);await n()});S.get("/health",(t)=>t.json({ok:!0,name:"junso-browser",version:Ln,cloak:Boolean(m()),running:En().length,maxInstances:u.maxInstances}));S.get("/api/profiles",async(t)=>{let n=await bn();return t.json({profiles:n.map((i)=>({...W(i),running:lt(i.id)}))})});async function zn(t){if(!t.proxy||typeof t.proxy!=="string")return;let n=t.fingerprint??{};if(n.timezone&&n.locale)return;let i=await st(t.proxy);if(!i)return;let c=Jt(i);if(t.fingerprint={...n},!n.timezone&&c.timezone)t.fingerprint.timezone=c.timezone;if(!n.locale&&c.locale)t.fingerprint.locale=c.locale}S.post("/api/profiles",async(t)=>{let n=await t.req.json().catch(()=>({}));if(!n?.name||typeof n.name!=="string")return t.json({error:"name is required"},400);try{await zn(n);let i=await en(n);return t.json({profile:W(i)},201)}catch(i){return t.json({error:i instanceof Error?i.message:"create failed"},400)}});S.get("/api/profiles/:id",async(t)=>{let n=await z(t.req.param("id"));if(!n)return t.json({error:"not found"},404);return t.json({profile:W(n),running:lt(n.id)})});S.patch("/api/profiles/:id",async(t)=>{let n=await t.req.json().catch(()=>({}));try{if(typeof n.proxy==="string")await zn(n);let i=await Pt(t.req.param("id"),n);return t.json({profile:W(i)})}catch(i){return t.json({error:i instanceof Error?i.message:"update failed"},400)}});S.delete("/api/profiles/:id",async(t)=>{let n=t.req.param("id");return await T(n),await on(n)?t.json({ok:!0}):t.json({error:"not found"},404)});S.post("/api/profiles/:id/launch",async(t)=>{let n=await z(t.req.param("id"));if(!n)return t.json({error:"not found"},404);try{return await I(n),t.json({cdpUrl:mt(n.id),running:!0})}catch(i){return t.json({error:i instanceof Error?i.message:"launch failed"},502)}});S.post("/api/profiles/:id/stop",async(t)=>{let n=await T(t.req.param("id"));return t.json({stopped:n})});S.get("/api/profiles/:id/cdp",async(t)=>{let n=await z(t.req.param("id"));if(!n)return t.json({error:"not found"},404);return t.json({cdpUrl:mt(n.id),running:lt(n.id)})});S.get("/api/profiles/:id/exit-ip",async(t)=>{let n=await z(t.req.param("id"));if(!n)return t.json({error:"not found"},404);let i=await st(n.proxy);if(!i)return t.json({error:"lookup failed",viaProxy:Boolean(n.proxy)},502);let c=Boolean(n.fingerprint.timezone&&i.timezone&&n.fingerprint.timezone!==i.timezone);return t.json({ip:i.ip,viaProxy:Boolean(n.proxy),country:i.country??null,countryCode:i.countryCode??null,isp:i.isp??null,kind:i.kind,timezone:i.timezone??null,tzMismatch:c})});S.post("/api/profiles/:id/rotate-proxy",async(t)=>{let n=await z(t.req.param("id"));if(!n)return t.json({error:"not found"},404);if(!n.proxy)return t.json({error:"profile has no proxy"},400);let i=rn(n.proxy);if(!i)return t.json({error:"proxy has no rotatable session-<id> token"},400);let c=await st(i),l=c?Jt(c):{},s=await Pt(n.id,{proxy:i,fingerprint:l});return await T(n.id),t.json({profile:W(s),exitIp:c?.ip??null,country:c?.country??null,isp:c?.isp??null,kind:c?.kind??"unknown"})});if(!u.token&&!ct())console.error(`[junso-browser] REFUSING TO START: bound to a non-loopback host with no JUNSO_BROWSER_TOKEN set.
188
+ Set JUNSO_BROWSER_TOKEN (the CDP endpoint = full browser control).`),process.exit(1);var Li=Bun.serve({port:u.port,hostname:u.host,idleTimeout:0,async fetch(t,n){let i=new URL(t.url);if(i.pathname.startsWith("/cdp/"))return ai(t,i,n);if(i.pathname==="/"||i.pathname==="/dashboard"){if(!R(t,i))return new Response("Unauthorized",{status:401});return new Response(Bn(u.token),{headers:{"content-type":"text/html; charset=utf-8"}})}if(i.pathname.startsWith("/view/")){if(!R(t,i))return new Response("Unauthorized",{status:401});let c=i.pathname.slice(6).split("/")[0]??"";return new Response(an(c,u.token),{headers:{"content-type":"text/html; charset=utf-8"}})}if(i.pathname.startsWith("/stream/")){if(!R(t,i))return new Response("Unauthorized",{status:401});let c=i.pathname.slice(8).split("/")[0]??"";return n.upgrade(t,{data:{kind:"stream",profileId:c,viewer:null}})?void 0:new Response("WebSocket upgrade required",{status:426})}return S.fetch(t)},websocket:Bi});nn();On();var Kn=async()=>{await dn(),process.exit(0)};process.on("SIGINT",Kn);process.on("SIGTERM",Kn);console.log(`[junso-browser] v${Ln} on http://${u.host}:${Li.port} (cloak ${m()?"found":"MISSING"}, auth ${u.token?"token":ct()?"loopback-open":"off"})`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "junso-browser",
3
- "version": "0.1.0",
3
+ "version": "0.2.3",
4
4
  "description": "Standalone CloakBrowser host — runs fingerprinted stealth-Chromium per profile, exposes each over a token-authed CDP gateway + interactive viewport, and ships an optional MCP server so any client gets browser tools with no local browser.",
5
5
  "type": "module",
6
6
  "bin": {