junso-browser 0.1.0 → 0.2.1

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 +155 -14
  3. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -1,33 +1,68 @@
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 an=Object.create;var{getPrototypeOf:Tn,defineProperty:ot,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,f=l.get(t);if(f)return f}i=t!=null?an(Tn(t)):{};let s=n||!t||!t.__esModule?ot(i,"default",{value:t,enumerable:!0}):i;for(let o of jn(t))if(!Jn.call(s,o))ot(s,o,{get:mn.bind(t,o),enumerable:!0});if(c)l.set(t,s);return s};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 Li=(t,n)=>{for(var i in n)ot(t,i,{get:n[i],enumerable:!0,configurable:!0,set:Gn.bind(n,i)})};var et=(t,n,i)=>{return(c,l)=>{let f=-1;return s(0);async function s(o){if(o<=f)throw Error("next() called multiple times");f=o;let b,e=!1,r;if(t[o])r=t[o][0][0],c.req.routeIndex=o;else r=o===t.length&&l||void 0;if(r)try{b=await r(c,()=>s(o+1))}catch(x){if(x instanceof Error&&n)c.error=x,b=await n(x,c),e=!0;else throw x}else if(c.finalized===!1&&i)b=await i(c);if(b&&(c.finalized===!1||e))c.res=b;return c}}};var Xt=Symbol();var Gt=async(t,n=Object.create(null))=>{let{all:i=!1,dot:c=!1}=n,f=(t instanceof F?t.raw.headers:t.headers).get("Content-Type");if(f?.startsWith("multipart/form-data")||f?.startsWith("application/x-www-form-urlencoded"))return In(t,{all:i,dot:c});return{}};async function In(t,n){let i=await t.formData();if(i)return Qn(i,n);return{}}function Qn(t,n){let i=Object.create(null);if(t.forEach((c,l)=>{if(!(n.all||l.endsWith("[]")))i[l]=c;else Yn(i,l,c)}),n.dot)Object.entries(i).forEach(([c,l])=>{if(c.includes("."))Vn(i,c,l),delete i[c]});return i}var Yn=(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]},Vn=(t,n,i)=>{if(/(?:^|\.)__proto__\./.test(n))return;let c=t,l=n.split(".");l.forEach((f,s)=>{if(s===l.length-1)c[f]=i;else{if(!c[f]||typeof c[f]!=="object"||Array.isArray(c[f])||c[f]instanceof File)c[f]=Object.create(null);c=c[f]}})};var xt=(t)=>{let n=t.split("/");if(n[0]==="")n.shift();return n},It=(t)=>{let{groups:n,path:i}=wn(t),c=xt(i);return An(c,n)},wn=(t)=>{let n=[];return t=t.replace(/\{[^}]+\}/g,(i,c)=>{let l=`@${c}`;return n.push([l,i]),l}),{groups:n,path:t}},An=(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={},Qt=(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}})}},Zn=(t)=>C(t,decodeURI),dt=(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 f=n.indexOf("?",c),s=n.indexOf("#",c),o=f===-1?s===-1?void 0:s:s===-1?f:Math.min(f,s),b=n.slice(i,o);return Zn(b.includes("%25")?b.replace(/%25/g,"%2525"):b)}else if(l===63||l===35)break}return n.slice(i,c)};var Yt=(t)=>{let n=dt(t);return n.length>1&&n.at(-1)==="/"?n.slice(0,-1):n},P=(t,n,...i)=>{if(i.length)n=P(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 f=l.replace("?","");c+="/"+f,i.push(c)}else c+="/"+l}),i.filter((l,f,s)=>s.indexOf(l)===f)},rt=(t)=>{if(!/[%+]/.test(t))return t;if(t.indexOf("+")!==-1)t=t.replace(/\+/g," ");return t.indexOf("%")!==-1?C(t,ut):t},Vt=(t,n,i)=>{let c;if(!i&&n&&!/[%+]/.test(n)){let s=t.indexOf("?",8);if(s===-1)return;if(!t.startsWith(n,s+1))s=t.indexOf(`&${n}`,s+1);while(s!==-1){let o=t.charCodeAt(s+n.length+1);if(o===61){let b=s+n.length+2,e=t.indexOf("&",b);return rt(t.slice(b,e===-1?void 0:e))}else if(o==38||isNaN(o))return"";s=t.indexOf(`&${n}`,s+1)}if(c=/[%+]/.test(t),!c)return}let l={};c??=/[%+]/.test(t);let f=t.indexOf("?",8);while(f!==-1){let s=t.indexOf("&",f+1),o=t.indexOf("=",f);if(o>s&&s!==-1)o=-1;let b=t.slice(f+1,o===-1?s===-1?void 0:s:o);if(c)b=rt(b);if(f=s,b==="")continue;let e;if(o===-1)e="";else if(e=t.slice(o+1,s===-1?void 0:s),c)e=rt(e);if(i){if(!(l[b]&&Array.isArray(l[b])))l[b]=[];l[b].push(e)}else l[b]??=e}return n?l[n]:l},wt=Vt,At=(t,n)=>{return Vt(t,n,!0)},ut=decodeURIComponent;var Zt=(t)=>C(t,ut),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.#f()}#i(t){let n=this.#n[0][this.routeIndex][1][t],i=this.#l(n);return i&&/\%/.test(i)?Zt(i):i}#f(){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)?Zt(c):c}return t}#l(t){return this.#n[1]?this.#n[1][t]:t}query(t){return wt(this.url,t)}queries(t){return At(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((f)=>{if(l==="json")f=JSON.stringify(f);return new Response(f)[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 Mt={Stringify:1,BeforeStream:2,Stream:3},Mn=(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 f=t.callbacks;if(!f?.length)return Promise.resolve(t);if(l)l[0]+=t;else l=[t];let s=Promise.all(f.map((o)=>o({phase:n,buffer:l,context:c}))).then((o)=>Promise.all(o.filter(Boolean).map((b)=>Et(b,n,!1,c,l))).then(()=>l[0]));if(i)return Mn(await s,f);else return s};var Un="text/plain; charset=UTF-8",Ot=(t,n)=>{return{"Content-Type":t,...n}},Q=(t,n)=>new Response(t,n),Nt=class{#t;#n;env={};#i;finalized=!1;error;#f;#l;#c;#r;#o;#e;#b;#x;#d;constructor(t,n){if(this.#t=t,n)this.#l=n.executionCtx,this.env=n.env,this.#e=n.notFoundHandler,this.#d=n.path,this.#x=n.matchResult}get req(){return this.#n??=new F(this.#t,this.#d,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||=Q(null,{headers:this.#b??=new Headers})}set res(t){if(this.#c&&t){t=Q(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.#o??=(n)=>this.html(n),this.#o(...t)};setLayout=(t)=>this.#r=t;getLayout=()=>this.#r;setRenderer=(t)=>{this.#o=t};header=(t,n,i)=>{if(this.finalized)this.#c=Q(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.#f=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)}#s(t,n,i){let c=this.#c?new Headers(this.#c.headers):this.#b??new Headers;if(typeof n==="object"&&"headers"in n){let f=n.headers instanceof Headers?n.headers:new Headers(n.headers);for(let[s,o]of f)if(s.toLowerCase()==="set-cookie")c.append(s,o);else c.set(s,o)}if(i)for(let[f,s]of Object.entries(i))if(typeof s==="string")c.set(f,s);else{c.delete(f);for(let o of s)c.append(f,o)}let l=typeof n==="number"?n:n?.status??this.#f;return Q(t,{status:l,headers:c})}newResponse=(...t)=>this.#s(...t);body=(t,n,i)=>this.#s(t,n,i);text=(t,n,i)=>{return!this.#b&&!this.#f&&!n&&!i&&!this.finalized?new Response(t):this.#s(t,n,Ot(Un,i))};json=(t,n,i)=>{return this.#s(JSON.stringify(t),n,Ot("application/json",i))};html=(t,n,i)=>{let c=(l)=>this.#s(l,n,Ot("text/html; charset=UTF-8",i));return typeof t==="object"?Et(t,Mt.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.#e??=()=>Q(),this.#e(this)}};var u="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((f)=>{this[f]=(s,...o)=>{if(typeof s==="string")this.#t=s;else this.#f(f,this.#t,s);return o.forEach((b)=>{this.#f(f,this.#t,b)}),this}}),this.on=(f,s,...o)=>{for(let b of[s].flat()){this.#t=b;for(let e of[f].flat())o.map((r)=>{this.#f(e.toUpperCase(),this.#t,r)})}return this},this.use=(f,...s)=>{if(typeof f==="string")this.#t=f;else this.#t="*",s.unshift(f);return s.forEach((o)=>{this.#f(u,this.#t,o)}),this};let{strict:c,...l}=n;Object.assign(this,l),this.getPath=c??!0?n.getPath??dt:Yt}#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 f;if(i.errorHandler===Ft)f=l.handler;else f=async(s,o)=>(await et([],i.errorHandler)(s,()=>l.handler(s,o))).res,f[kt]=l.handler;c.#f(l.method,l.path,f,l.basePath)}),this}basePath(n){let i=this.#n();return i._basePath=P(this._basePath,n),i}onError=(n)=>{return this.errorHandler=n,this};notFound=(n)=>{return this.#i=n,this};mount(n,i,c){let l,f;if(c)if(typeof c==="function")f=c;else if(f=c.optionHandler,c.replaceRequest===!1)l=(b)=>b;else l=c.replaceRequest;let s=f?(b)=>{let e=f(b);return Array.isArray(e)?e:[e]}:(b)=>{let e=void 0;try{e=b.executionCtx}catch{}return[b.env,e]};l||=(()=>{let b=P(this._basePath,n),e=b==="/"?0:b.length;return(r)=>{let x=new URL(r.url);return x.pathname=this.getPath(r).slice(e)||"/",new Request(x,r)}})();let o=async(b,e)=>{let r=await i(l(b.req.raw),...s(b));if(r)return r;await e()};return this.#f(u,P(n,"*"),o),this}#f(n,i,c,l){n=n.toUpperCase(),i=P(this._basePath,i);let f={basePath:l!==void 0?P(this._basePath,l):this._basePath,path:i,method:n,handler:c};this.router.add(n,i,[c,f]),this.routes.push(f)}#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 f=this.getPath(n,{env:c}),s=this.router.match(l,f),o=new Nt(n,{path:f,matchResult:s,env:c,executionCtx:i,notFoundHandler:this.#i});if(s[0].length===1){let e;try{e=s[0][0][0][0](o,async()=>{o.res=await this.#i(o)})}catch(r){return this.#l(r,o)}return e instanceof Promise?e.then((r)=>r||(o.finalized?o.res:this.#i(o))).catch((r)=>this.#l(r,o)):e??this.#i(o)}let b=et(s[0],this.errorHandler,this.#i);return(async()=>{try{let e=await b(o);if(!e.finalized)throw Error("Context is not finalized. Did you forget to return a Response object or `await next()`?");return e.res}catch(e){return this.#l(e,o)}})()}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${P("/",n)}`,i),c,l)};fire=()=>{addEventListener("fetch",(n)=>{n.respondWith(this.#c(n.request,n,void 0,n.request.method))})}};var Y=[];function q(t,n){let i=this.buildAllMatchers(),c=(l,f)=>{let s=i[l]||i[u],o=s[2][f];if(o)return o;let b=f.match(s[0]);if(!b)return[[],Y];let e=b.indexOf("",1);return[s[1][e],b]};return this.match=c,c(t,n)}var tt="[^/]+",V=".*",w="(?:|/.*)",a=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===V||t===w)return 1;else if(n===V||n===w)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,f){if(n.length===0){if(this.#t!==void 0)throw a;if(f)return;this.#t=i;return}let[s,...o]=n,b=s==="*"?o.length===0?["","",V]:["","",tt]:s==="/*"?["","",w]:s.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/),e;if(b){let r=b[1],x=b[2]||tt;if(r&&b[2]){if(x===".*")throw a;if(x=x.replace(/^\((?!\?:)(?=[^)]+\)$)/,"(?:"),/\((?!\?:)/.test(x))throw a}if(e=this.#i[x],!e){if(Object.keys(this.#i).some((E)=>E!==V&&E!==w))throw a;if(f)return;if(e=this.#i[x]=new t,r!=="")e.#n=l.varIndex++}if(!f&&r!=="")c.push([r,e.#n])}else if(e=this.#i[s],!e){if(Object.keys(this.#i).some((r)=>r.length>1&&r!==V&&r!==w))throw a;if(f)return;e=this.#i[s]=new t}e.insert(o,i,c,l,f)}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 s=0;;){let o=!1;if(t=t.replace(/\{[^}]+\}/g,(b)=>{let e=`@\\${s}`;return l[s]=[e,b],s++,o=!0,e}),!o)break}let f=t.match(/(?::[^\/]+)|(?:\/\*$)|./g)||[];for(let s=l.length-1;s>=0;s--){let[o]=l[s];for(let b=f.length-1;b>=0;b--)if(f[b].indexOf(o)!==-1){f[b]=f[b].replace(o,l[s][1]);break}}return this.#n.insert(f,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,f,s)=>{if(f!==void 0)return i[++n]=Number(f),"$()";if(s!==void 0)return c[Number(s)]=++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((e)=>[!/\*|\/:/.test(e[0]),...e]).sort(([e,r],[x,E])=>e?1:x?-1:r.length-E.length),l=Object.create(null);for(let e=0,r=-1,x=c.length;e<x;e++){let[E,O,g]=c[e];if(E)l[O]=[g.map(([v])=>[v,Object.create(null)]),Y];else r++;let S;try{S=n.insert(O,r,E)}catch(v){throw v===a?new h(O):v}if(E)continue;i[r]=g.map(([v,N])=>{let B=Object.create(null);N-=1;for(;N>=0;N--){let[X,st]=S[N];B[X]=st}return[v,B]})}let[f,s,o]=n.buildRegExp();for(let e=0,r=i.length;e<r;e++)for(let x=0,E=i[e].length;x<E;x++){let O=i[e][x]?.[1];if(!O)continue;let g=Object.keys(O);for(let S=0,v=g.length;S<v;S++)O[g[S]]=o[O[g[S]]]}let b=[];for(let e in s)b[e]=i[s[e]];return[f,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={[u]:Object.create(null)},this.#n={[u]: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((o)=>{o[t]=Object.create(null),Object.keys(o[u]).forEach((b)=>{o[t][b]=[...o[u][b]]})});if(n==="/*")n="*";let f=(n.match(/\/:/g)||[]).length;if(/\*$/.test(n)){let o=ht(n);if(t===u)Object.keys(c).forEach((b)=>{c[b][n]||=J(c[b],n)||J(c[u],n)||[]});else c[t][n]||=J(c[t],n)||J(c[u],n)||[];Object.keys(c).forEach((b)=>{if(t===u||t===b)Object.keys(c[b]).forEach((e)=>{o.test(e)&&c[b][e].push([i,f])})}),Object.keys(l).forEach((b)=>{if(t===u||t===b)Object.keys(l[b]).forEach((e)=>o.test(e)&&l[b][e].push([i,f]))});return}let s=H(n)||[n];for(let o=0,b=s.length;o<b;o++){let e=s[o];Object.keys(l).forEach((r)=>{if(t===u||t===r)l[r][e]||=[...J(c[r],e)||J(c[u],e)||[]],l[r][e].push([i,f-b+o+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===u;if([this.#t,this.#n].forEach((c)=>{let l=c[t]?Object.keys(c[t]).map((f)=>[f,c[t][f]]):[];if(l.length!==0)i||=!0,n.push(...l);else if(t!==u)n.push(...Object.keys(c[u]).map((f)=>[f,c[u][f]]))}),!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))}#f(t,n,i,c,l){let f=this.#t[t];if(!l)f[2][n][0].push([i,{}]);else c.forEach((s)=>{if(typeof s==="number")f[1][s].push([i,l]);else f[2][s||n][0].push([i,l])})}add(t,n,i){if(!this.#t[t]){let l=this.#t[u],f={};for(let s in l[2])f[s]=[l[2][s][0].slice(),Y];this.#t[t]=[l[0],l[1].map((s)=>Array.isArray(s)?s.slice():0),f]}if(n==="/*"||n==="*"){let l=[i,{}];if(t===u)for(let f in this.#t)this.#i(f,l);else this.#i(t,l);return}let c=this.#n[n];if(!c)throw Error(`Path ${n} is not registered`);for(let[l,f]of c)if(t===u)for(let s in this.#t)this.#f(s,n,i,l,f);else this.#f(t,n,i,l,f)}buildAllMatchers(){return this.#t}match=q};var yt=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,f=0,s;for(;f<l;f++){let o=i[f];try{for(let b=0,e=c.length;b<e;b++)o.add(...c[b]);s=o.match(t,n)}catch(b){if(b instanceof h)continue;throw b}this.match=o.match.bind(o),this.#t=[o],this.#n=void 0;break}if(f===l)throw Error("Fatal error");return this.name=`SmartRouter + ${this.activeRouter.name}`,s}get activeRouter(){if(this.#n||this.#t.length!==1)throw Error("No active router has been determined yet.");return this.#t[0]}};var A=Object.create(null),hn=(t)=>{for(let n in t)return!0;return!1},qt=class t{#t;#n;#i;#f=0;#l=A;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.#f=++this.#f;let l=this,f=It(i),s=[];for(let o=0,b=f.length;o<b;o++){let e=f[o],r=f[o+1],x=Qt(e,r),E=Array.isArray(x)?x[0]:e;if(E in l.#n){if(l=l.#n[E],x)s.push(x[1]);continue}if(l.#n[E]=new t,x)l.#i.push(x),s.push(x[1]);l=l.#n[E]}return l.#t.push({[n]:{handler:c,possibleKeys:s.filter((o,b,e)=>e.indexOf(o)===b),score:this.#f}}),l}#c(n,i,c,l,f){for(let s=0,o=i.#t.length;s<o;s++){let b=i.#t[s],e=b[c]||b[u],r={};if(e!==void 0){if(e.params=Object.create(null),n.push(e),l!==A||f&&f!==A)for(let x=0,E=e.possibleKeys.length;x<E;x++){let O=e.possibleKeys[x],g=r[e.score];e.params[O]=f?.[O]&&!g?f[O]:l[O]??f?.[O],r[e.score]=!0}}}}search(n,i){let c=[];this.#l=A;let f=[this],s=xt(i),o=[],b=s.length,e=null;for(let r=0;r<b;r++){let x=s[r],E=r===b-1,O=[];for(let S=0,v=f.length;S<v;S++){let N=f[S],B=N.#n[x];if(B)if(B.#l=N.#l,E){if(B.#n["*"])this.#c(c,B.#n["*"],n,N.#l);this.#c(c,B,n,N.#l)}else O.push(B);for(let X=0,st=N.#i.length;X<st;X++){let _t=N.#i[X],L=N.#l===A?{}:{...N.#l};if(_t==="*"){let j=N.#n["*"];if(j)this.#c(c,j,n,N.#l),j.#l=L,O.push(j);continue}let[Pn,Wt,G]=_t;if(!x&&!(G instanceof RegExp))continue;let z=N.#n[Pn];if(G instanceof RegExp){if(e===null){e=Array(b);let k=i[0]==="/"?1:0;for(let I=0;I<b;I++)e[I]=k,k+=s[I].length+1}let j=i.substring(e[r]),bt=G.exec(j);if(bt){if(L[Wt]=bt[0],this.#c(c,z,n,N.#l,L),hn(z.#n)){z.#l=L;let k=bt[0].match(/\//)?.length??0;(o[k]||=[]).push(z)}continue}}if(G===!0||G.test(x))if(L[Wt]=x,E){if(this.#c(c,z,n,L,N.#l),z.#n["*"])this.#c(c,z.#n["*"],n,L,N.#l)}else z.#l=L,O.push(z)}}let g=o.shift();f=g?O.concat(g):O}if(c.length>1)c.sort((r,x)=>{return r.score-x.score});return[c.map(({handler:r,params:x})=>[r,x])]}};var $t=class{name="TrieRouter";#t;constructor(){this.#t=new qt}add(t,n,i){let c=H(n);if(c){for(let l=0,f=c.length;l<f;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 gt=class extends Dt{constructor(t={}){super(t);this.router=t.router??new yt({routers:[new nt,new $t]})}};import qn from"os";import St from"path";var d={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??St.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 vt(){return St.join(d.dataDir,"profiles.json")}function it(t){return St.join(d.dataDir,"profiles",t,"user-data")}function ct(){return d.host==="127.0.0.1"||d.host==="localhost"||d.host==="::1"}import{mkdir as ci,readFile as li,writeFile as fi,rm as si}from"fs/promises";import bi from"path";import{randomUUID as oi}from"crypto";import ti from"os";import zt from"path";import{existsSync as tn,readdirSync as ni}from"fs";function m(){if(d.cloakPath&&tn(d.cloakPath))return d.cloakPath;let t=zt.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=zt.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=zt.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 Kt(){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 fn(t){let n={...process.env};if(t.fingerprint.timezone)n.TZ=t.fingerprint.timezone;return n}var _=null,sn=Promise.resolve();async function Z(){if(_)return _;try{let t=await li(vt(),"utf8"),n=JSON.parse(t);_=n&&typeof n==="object"?n:{}}catch{_={}}return _}async function Lt(t){await ci(d.dataDir,{recursive:!0}),await fi(vt(),JSON.stringify(t,null,2)+`
3
+ `,"utf8"),_=t}function Pt(t){let n=sn.catch(()=>{}).then(t);return sn=n.catch(()=>{}),n}function ei(t){return(t??"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}async function bn(){return Object.values(await Z()).sort((t,n)=>t.name.localeCompare(n.name))}async function K(t){return(await Z())[t]??null}async function on(t){let n=ei(t.name)||oi().slice(0,8);return Pt(async()=>{let i={...await Z()};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??Kt(),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 Lt(i),l})}async function at(t,n){return Pt(async()=>{let i={...await Z()},c=i[t];if(!c)throw Error(`No such profile: ${t}`);let l={...c.fingerprint,...n.fingerprint??{}};if(n.rotateSeed)l.seed=Kt();let f={...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]=f,await Lt(i),f})}async function en(t){return Pt(async()=>{let n={...await Z()};if(!n[t])return!1;return delete n[t],await Lt(n),await si(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 di,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 y=new Map;async function ui(t,n=20000){let i=jt.join(t,"DevToolsActivePort"),c=Date.now()+n;while(Date.now()<c){try{let l=await di(i,"utf8"),[f,s]=l.split(`
4
+ `),o=Number(f);if(o>0&&s)return{port:o,wsPath:s.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 y.values())if(!t||n.instance.lastUsedAt<t.instance.lastUsedAt)t=n;if(t)await T(t.instance.profileId)}async function M(t){let n=y.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(d.maxInstances>0&&y.size>=d.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),f=Bun.spawn([i,...l],{stdout:"ignore",stderr:"ignore",env:fn(t)}),s;try{s=await ui(c)}catch(x){throw f.kill(),x}let o=new Date().toISOString(),b={profileId:t.id,pid:f.pid,port:s.port,browserWsPath:s.wsPath,startedAt:o,lastUsedAt:o},e={instance:b,proc:f};y.set(t.id,e);let r=cn(t.proxy);if(r){let x=new Tt(`ws://127.0.0.1:${s.port}${s.wsPath}`,r.username,r.password);try{await x.start(),e.auth=x}catch{}}return f.exited.then(()=>{if(y.get(t.id)?.proc===f)e.auth?.close(),y.delete(t.id)}),b}function lt(t){return y.has(t)}function dn(t){let n=y.get(t);if(n)n.instance.lastUsedAt=new Date().toISOString()}async function T(t){let n=y.get(t);if(!n)return!1;return y.delete(t),n.auth?.close(),n.proc.kill(),await n.proc.exited.catch(()=>{}),!0}async function un(){await Promise.all([...y.keys()].map((t)=>T(t)))}function En(){return[...y.values()].map((t)=>t.instance)}function On(){if(d.idleReapMs<=0)return null;return setInterval(()=>{let t=Date.now()-d.idleReapMs;for(let n of y.values())if(new Date(n.instance.lastUsedAt).getTime()<t)T(n.instance.profileId)},60000)}async function Ni(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 yi(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 f="residential";if(l.proxy)f="proxy";else if(l.hosting)f="hosting";else if(l.mobile)f="mobile";return{ip:l.query??t,country:l.country,countryCode:l.countryCode,timezone:l.timezone,isp:l.isp,kind:f}}catch{return null}}async function ft(t){let n=await Ni(t);if(!n)return null;return await yi(n)??{ip:n,kind:"unknown"}}function Jt(t){return{timezone:t.timezone,locale:t.countryCode?`en-${t.countryCode}`:void 0}}var Nn={name:"junso-browser",version:"0.2.1",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"}};class yn{browserWsUrl;cdp=null;sessionId=null;nextId=1;pending=new Map;starting=null;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,f)=>{t.onopen=()=>l(),t.onerror=()=>f(Error("CDP connect failed"))}),t.onmessage=(l)=>this.onMessage(JSON.parse(String(l.data))),t.onclose=()=>{this.cdp=null,this.sessionId=null};let{targetInfos:n}=await this.rpc("Target.getTargets"),i=n.find((l)=>l.type==="page")??n[0];if(!i)throw Error("no page target to stream");let{sessionId:c}=await this.rpc("Target.attachToTarget",{targetId:i.targetId,flatten:!0});this.sessionId=c,await this.rpc("Page.enable",{},c),await this.rpc("Page.startScreencast",{format:"jpeg",quality:60,maxWidth:1280,maxHeight:800,everyNthFrame:1},c);try{let l=await this.rpc("Page.getNavigationHistory",{},c);this.lastUrl=l.entries[l.currentIndex]?.url??null}catch{}})(),this.starting}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)this.lastUrl=n.url,this.broadcast({type:"url",url:n.url})}}broadcast(t){let n=JSON.stringify(t);for(let i of this.viewers)i.send(n)}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==="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 $n(t,n){let i=await K(t);if(!i)throw Error(`no such profile: ${t}`);let c=await M(i),l=U.get(t);if(!l)l=new yn(`ws://127.0.0.1:${c.port}${c.browserWsPath}`),U.set(t,l);if(await l.start(),l.viewers.add(n),l.lastFrame)n.send(l.lastFrame);if(l.lastUrl)n.send(JSON.stringify({type:"url",url:l.lastUrl}))}function gn(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 Sn(t,n){let i=U.get(t);if(!i)return;try{i.input(JSON.parse(n))}catch{}}function vn(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
+ <button id="bk" title="Back">\u2039</button>
22
+ <button id="fw" title="Forward">\u203A</button>
23
+ <button id="rl" title="Reload">\u27F3</button>
24
+ <input id="url" placeholder="https://\u2026 (Enter to go)" spellcheck="false"/>
25
+ <button id="go">Go</button>
26
+ <button id="shot" title="Download screenshot">\u2913</button>
27
+ <span id="ip" title="exit IP" style="color:#888;font:11px ui-monospace,monospace;white-space:nowrap;cursor:pointer">IP?</span>
28
+ </div>
17
29
  <div id="wrap"><img id="v" tabindex="0" draggable="false"/></div>
18
30
  <script>
19
- const PROFILE=${$}, TOKEN=${O};
20
- const img=document.getElementById("v"), st=document.getElementById("st");
31
+ const PROFILE=${i}, TOKEN=${c};
32
+ const img=document.getElementById("v"), st=document.getElementById("st"), urlBox=document.getElementById("url");
21
33
  let meta={deviceWidth:1280,deviceHeight:800};
22
34
  const proto=location.protocol==="https:"?"wss":"ws";
23
35
  const ws=new WebSocket(proto+"://"+location.host+"/stream/"+encodeURIComponent(PROFILE)+(TOKEN?"?token="+encodeURIComponent(TOKEN):""));
24
36
  ws.onopen=()=>st.classList.add("on");
25
37
  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;}};
38
+ ws.onmessage=(e)=>{const m=JSON.parse(e.data);
39
+ if(m.type==="frame"){img.src="data:image/jpeg;base64,"+m.data; if(m.metadata) meta=m.metadata;}
40
+ else if(m.type==="url"){ if(document.activeElement!==urlBox) urlBox.value=m.url; }
41
+ };
27
42
  function mods(e){return (e.altKey?1:0)|(e.ctrlKey?2:0)|(e.metaKey?4:0)|(e.shiftKey?8:0);}
28
43
  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
44
  const BTN=["left","middle","right"];
30
45
  function send(o){if(ws.readyState===1)ws.send(JSON.stringify(o));}
46
+ function navigate(){const u=urlBox.value.trim(); if(u){send({t:"nav",url:u}); img.focus();}}
47
+ document.getElementById("go").addEventListener("click",navigate);
48
+ document.getElementById("rl").addEventListener("click",()=>send({t:"reload"}));
49
+ document.getElementById("bk").addEventListener("click",()=>send({t:"back"}));
50
+ document.getElementById("fw").addEventListener("click",()=>send({t:"forward"}));
51
+ urlBox.addEventListener("keydown",e=>{ e.stopPropagation(); if(e.key==="Enter") navigate(); });
52
+ // Screenshot: download the current frame client-side.
53
+ 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(); });
54
+ // REST helper (token-authed) for profile list + exit IP.
55
+ const H = TOKEN ? {authorization:"Bearer "+TOKEN} : {};
56
+ const qs = TOKEN ? "?token="+encodeURIComponent(TOKEN) : "";
57
+ // Exit-IP badge.
58
+ const ipEl=document.getElementById("ip");
59
+ 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"; } });
60
+ // Profile switcher.
61
+ const profSel=document.getElementById("prof");
62
+ fetch("/api/profiles",{headers:H}).then(r=>r.json()).then(({profiles})=>{
63
+ profSel.innerHTML=profiles.map(p=>'<option value="'+p.name+'"'+(p.name===PROFILE?" selected":"")+'>'+p.name+'</option>').join("");
64
+ }).catch(()=>{ profSel.innerHTML='<option selected>'+PROFILE+'</option>'; });
65
+ profSel.addEventListener("change",()=>{ location.href="/view/"+encodeURIComponent(profSel.value)+qs; });
31
66
  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
67
  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
68
  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 +70,111 @@ img.addEventListener("contextmenu",e=>e.preventDefault());
35
70
  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
71
  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
72
  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"})`);
73
+ </script></body></html>`}function Bn(t){return`<!doctype html><html><head><meta charset="utf-8"><title>junso-browser</title>
74
+ <style>
75
+ :root{color-scheme:dark}
76
+ body{margin:0;background:#0f0f10;color:#ddd;font:14px system-ui;padding:20px;max-width:1100px;margin:0 auto}
77
+ h1{font-size:18px;margin:0 0 4px} .sub{color:#888;margin:0 0 18px;font-size:12px}
78
+ .card{background:#191a1b;border:1px solid #2a2b2d;border-radius:10px;padding:14px;margin-bottom:16px}
79
+ label{display:block;font-size:11px;color:#999;margin:6px 0 2px}
80
+ 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}
81
+ .row{display:flex;gap:10px;flex-wrap:wrap} .row>div{flex:1;min-width:130px}
82
+ button{background:#2563eb;border:0;color:#fff;border-radius:6px;padding:7px 12px;cursor:pointer;font:13px system-ui}
83
+ button.sec{background:#2a2b2d;border:1px solid #3a3b3d} button.danger{background:#3a1d1d;border:1px solid #5a2a2a;color:#f3b0b0}
84
+ 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}
85
+ th{color:#888;font-weight:600;font-size:11px;text-transform:uppercase}
86
+ .dot{width:8px;height:8px;border-radius:50%;background:#555;display:inline-block} .dot.on{background:#3fb950}
87
+ code{background:#0d0d0e;padding:2px 6px;border-radius:4px;font-size:12px} .muted{color:#777}
88
+ .acts{display:flex;gap:6px;flex-wrap:wrap} .acts button{padding:4px 8px;font-size:12px}
89
+ #err{color:#f3b0b0;font-size:12px;min-height:16px} a{color:#6ea8fe}
90
+ </style></head><body>
91
+ <h1>junso-browser</h1>
92
+ <p class="sub">Fingerprinted, proxied browser identities. Open a viewport to watch + control one live.</p>
93
+ <div id="err"></div>
94
+
95
+ <div class="card">
96
+ <b style="font-size:13px">New profile</b>
97
+ <div class="row">
98
+ <div><label>Name</label><input id="n_name" placeholder="acct-a"></div>
99
+ <div><label>Platform</label><select id="n_plat"><option value="windows">windows</option><option value="macos">macos</option></select></div>
100
+ <div><label>Timezone</label><input id="n_tz" placeholder="America/New_York"></div>
101
+ <div><label>Locale</label><input id="n_loc" placeholder="en-US"></div>
102
+ </div>
103
+ <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>
104
+ <div style="flex:0;display:flex;align-items:flex-end"><button id="create">Create</button></div></div>
105
+ </div>
106
+
107
+ <div class="card">
108
+ <b style="font-size:13px">Profiles</b>
109
+ <table><thead><tr><th></th><th>Name</th><th>Device</th><th>Proxy</th><th>Exit IP</th><th>Actions</th></tr></thead>
110
+ <tbody id="rows"><tr><td colspan="6" class="muted">loading\u2026</td></tr></tbody></table>
111
+ </div>
112
+
113
+ <script>
114
+ const TOKEN=${JSON.stringify(t||"")};
115
+ const H = TOKEN ? {authorization:"Bearer "+TOKEN} : {};
116
+ const qs = TOKEN ? "?token="+encodeURIComponent(TOKEN) : "";
117
+ const errEl=document.getElementById("err");
118
+ const err=(m)=>{ errEl.textContent=m||""; };
119
+ async function api(method,path,body){
120
+ const r=await fetch(path,{method,headers:Object.assign({},H,body?{"content-type":"application/json"}:{}),body:body?JSON.stringify(body):undefined});
121
+ const t=await r.text(); let d=null; try{d=t?JSON.parse(t):null}catch(e){}
122
+ if(!r.ok) throw new Error((d&&d.error)||("HTTP "+r.status)); return d;
123
+ }
124
+ function esc(s){ return String(s==null?"":s).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c];}); }
125
+ function rowHtml(p){
126
+ var fp=p.fingerprint||{};
127
+ var proxy = p.proxy ? '<code>'+esc(p.proxy)+'</code>' : '<span class="muted">none</span>';
128
+ var startStop = p.running
129
+ ? '<button class="sec" data-a="stop" data-id="'+esc(p.id)+'">Stop</button>'
130
+ : '<button class="sec" data-a="launch" data-id="'+esc(p.id)+'">Start</button>';
131
+ return '<tr>'
132
+ + '<td><span class="dot '+(p.running?"on":"")+'" title="'+(p.running?"running":"stopped")+'"></span></td>'
133
+ + '<td><b>'+esc(p.name)+'</b></td>'
134
+ + '<td class="muted">'+esc(fp.platform||"?")+' \xB7 '+esc(fp.timezone||"host tz")+' \xB7 seed '+esc(fp.seed||"?")+'</td>'
135
+ + '<td>'+proxy+'</td>'
136
+ + '<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>'
137
+ + '<td class="acts">'
138
+ + '<a href="/view/'+encodeURIComponent(p.id)+qs+'" target="_blank"><button>Open</button></a>'
139
+ + startStop
140
+ + '<button class="sec" data-a="proxy" data-id="'+esc(p.id)+'">Proxy</button>'
141
+ + (p.proxy ? '<button class="sec" data-a="rotateproxy" data-id="'+esc(p.id)+'">Rotate IP</button>' : '')
142
+ + '<button class="sec" data-a="rotate" data-id="'+esc(p.id)+'">New seed</button>'
143
+ + '<button class="danger" data-a="del" data-id="'+esc(p.id)+'">Delete</button>'
144
+ + '</td></tr>';
145
+ }
146
+ async function load(){
147
+ try{ var r=await api("GET","/api/profiles"); var ps=r.profiles||[];
148
+ 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>';
149
+ err("");
150
+ }catch(e){ err("load failed: "+e.message); }
151
+ }
152
+ document.getElementById("create").addEventListener("click",async function(){
153
+ var name=document.getElementById("n_name").value.trim(); if(!name)return err("name required");
154
+ try{
155
+ await api("POST","/api/profiles",{name:name,proxy:document.getElementById("n_proxy").value.trim()||undefined,
156
+ fingerprint:{platform:document.getElementById("n_plat").value,timezone:document.getElementById("n_tz").value.trim()||undefined,locale:document.getElementById("n_loc").value.trim()||undefined}});
157
+ document.getElementById("n_name").value=document.getElementById("n_proxy").value=document.getElementById("n_tz").value=document.getElementById("n_loc").value="";
158
+ load();
159
+ }catch(e){ err(e.message); }
160
+ });
161
+ document.getElementById("rows").addEventListener("click",async function(e){
162
+ var b=e.target.closest("button[data-a]"); if(!b)return;
163
+ var id=b.getAttribute("data-id"), a=b.getAttribute("data-a");
164
+ try{
165
+ if(a==="ip"){ var el=document.getElementById("ip_"+id); el.textContent="\u2026";
166
+ var r=await api("GET","/api/profiles/"+id+"/exit-ip");
167
+ 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);
168
+ 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>':'');
169
+ el.className=""; return; }
170
+ if(a==="del"){ if(!confirm("Delete profile \\""+id+"\\"? Its cookies/logins are erased."))return; await api("DELETE","/api/profiles/"+id); }
171
+ 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(){}); }
172
+ 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:"")); }
173
+ else if(a==="rotate"){ await api("PATCH","/api/profiles/"+id,{rotateSeed:true}); }
174
+ else { await api("POST","/api/profiles/"+id+"/"+a); }
175
+ load();
176
+ }catch(e){ err(e.message); }
177
+ });
178
+ load(); setInterval(load,5000);
179
+ </script></body></html>`}var zn=Nn.version;function gi(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(!d.token)return ct();return gi(t,n)===d.token}function mt(t){let n=d.publicUrl?.replace(/\/$/,"")??`ws://${d.host==="0.0.0.0"?"127.0.0.1":d.host}:${d.port}`,i=d.token?`?token=${encodeURIComponent(d.token)}`:"";return`${n}/cdp/${t}${i}`}async function Si(t,n,i){if(!R(t,n))return new Response("Unauthorized",{status:401});let c=n.pathname.slice(5),l=c.split("/")[0]??"",f=await K(l);if(!f)return new Response("No such profile",{status:404});let s;try{s=await M(f)}catch(b){return new Response(`Launch failed: ${b instanceof Error?b.message:b}`,{status:502})}if(dn(l),t.headers.get("upgrade")?.toLowerCase()==="websocket"){let b=`ws://127.0.0.1:${s.port}${s.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 o=c.slice(l.length);if(o.startsWith("/json"))try{let b=await fetch(`http://127.0.0.1:${s.port}${o}`),e=await b.text(),r=mt(l);return e=e.replace(/ws:\/\/127\.0\.0\.1:\d+\/devtools\/browser\/[a-zA-Z0-9-]+/g,r),new Response(e,{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 vi={open(t){if(t.data.kind==="stream"){let c={send:(l)=>t.send(l)};t.data.viewer=c,$n(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")Sn(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)gn(t.data.profileId,t.data.viewer);return}t.data.closed=!0;try{t.data.upstream?.close()}catch{}}},$=new gt;$.use("/api/*",async(t,n)=>{if(!R(t.req.raw,new URL(t.req.url)))return t.json({error:"Unauthorized"},401);await n()});$.get("/health",(t)=>t.json({ok:!0,name:"junso-browser",version:zn,cloak:Boolean(m()),running:En().length,maxInstances:d.maxInstances}));$.get("/api/profiles",async(t)=>{let n=await bn();return t.json({profiles:n.map((i)=>({...W(i),running:lt(i.id)}))})});async function Kn(t){if(!t.proxy||typeof t.proxy!=="string")return;let n=t.fingerprint??{};if(n.timezone&&n.locale)return;let i=await ft(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}$.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 Kn(n);let i=await on(n);return t.json({profile:W(i)},201)}catch(i){return t.json({error:i instanceof Error?i.message:"create failed"},400)}});$.get("/api/profiles/:id",async(t)=>{let n=await K(t.req.param("id"));if(!n)return t.json({error:"not found"},404);return t.json({profile:W(n),running:lt(n.id)})});$.patch("/api/profiles/:id",async(t)=>{let n=await t.req.json().catch(()=>({}));try{if(typeof n.proxy==="string")await Kn(n);let i=await at(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)}});$.delete("/api/profiles/:id",async(t)=>{let n=t.req.param("id");return await T(n),await en(n)?t.json({ok:!0}):t.json({error:"not found"},404)});$.post("/api/profiles/:id/launch",async(t)=>{let n=await K(t.req.param("id"));if(!n)return t.json({error:"not found"},404);try{return await M(n),t.json({cdpUrl:mt(n.id),running:!0})}catch(i){return t.json({error:i instanceof Error?i.message:"launch failed"},502)}});$.post("/api/profiles/:id/stop",async(t)=>{let n=await T(t.req.param("id"));return t.json({stopped:n})});$.get("/api/profiles/:id/cdp",async(t)=>{let n=await K(t.req.param("id"));if(!n)return t.json({error:"not found"},404);return t.json({cdpUrl:mt(n.id),running:lt(n.id)})});$.get("/api/profiles/:id/exit-ip",async(t)=>{let n=await K(t.req.param("id"));if(!n)return t.json({error:"not found"},404);let i=await ft(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})});$.post("/api/profiles/:id/rotate-proxy",async(t)=>{let n=await K(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 ft(i),l=c?Jt(c):{},f=await at(n.id,{proxy:i,fingerprint:l});return await T(n.id),t.json({profile:W(f),exitIp:c?.ip??null,country:c?.country??null,isp:c?.isp??null,kind:c?.kind??"unknown"})});if(!d.token&&!ct())console.error(`[junso-browser] REFUSING TO START: bound to a non-loopback host with no JUNSO_BROWSER_TOKEN set.
180
+ Set JUNSO_BROWSER_TOKEN (the CDP endpoint = full browser control).`),process.exit(1);var Bi=Bun.serve({port:d.port,hostname:d.host,idleTimeout:0,async fetch(t,n){let i=new URL(t.url);if(i.pathname.startsWith("/cdp/"))return Si(t,i,n);if(i.pathname==="/"||i.pathname==="/dashboard"){if(!R(t,i))return new Response("Unauthorized",{status:401});return new Response(Bn(d.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(vn(c,d.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 $.fetch(t)},websocket:vi});nn();On();var Ln=async()=>{await un(),process.exit(0)};process.on("SIGINT",Ln);process.on("SIGTERM",Ln);console.log(`[junso-browser] v${zn} on http://${d.host}:${Bi.port} (cloak ${m()?"found":"MISSING"}, auth ${d.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.1",
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": {