junso-browser 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -0
- package/bin/junso-browser-mcp.js +13 -0
- package/bin/junso-browser.js +26 -0
- package/dist/mcp.js +42 -0
- package/dist/server.js +39 -0
- package/package.json +50 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
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>
|
|
6
|
+
<style>
|
|
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)}
|
|
10
|
+
#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}
|
|
12
|
+
.on{background:#3fb950}
|
|
13
|
+
</style></head>
|
|
14
|
+
<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>
|
|
17
|
+
<div id="wrap"><img id="v" tabindex="0" draggable="false"/></div>
|
|
18
|
+
<script>
|
|
19
|
+
const PROFILE=${$}, TOKEN=${O};
|
|
20
|
+
const img=document.getElementById("v"), st=document.getElementById("st");
|
|
21
|
+
let meta={deviceWidth:1280,deviceHeight:800};
|
|
22
|
+
const proto=location.protocol==="https:"?"wss":"ws";
|
|
23
|
+
const ws=new WebSocket(proto+"://"+location.host+"/stream/"+encodeURIComponent(PROFILE)+(TOKEN?"?token="+encodeURIComponent(TOKEN):""));
|
|
24
|
+
ws.onopen=()=>st.classList.add("on");
|
|
25
|
+
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;}};
|
|
27
|
+
function mods(e){return (e.altKey?1:0)|(e.ctrlKey?2:0)|(e.metaKey?4:0)|(e.shiftKey?8:0);}
|
|
28
|
+
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
|
+
const BTN=["left","middle","right"];
|
|
30
|
+
function send(o){if(ws.readyState===1)ws.send(JSON.stringify(o));}
|
|
31
|
+
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
|
+
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
|
+
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)});});
|
|
34
|
+
img.addEventListener("contextmenu",e=>e.preventDefault());
|
|
35
|
+
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
|
+
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
|
+
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"})`);
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "junso-browser",
|
|
3
|
+
"version": "0.1.0",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"junso-browser": "bin/junso-browser.js",
|
|
8
|
+
"junso-browser-mcp": "bin/junso-browser-mcp.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"bun": ">=1.2.0"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"browser",
|
|
20
|
+
"cloakbrowser",
|
|
21
|
+
"stealth",
|
|
22
|
+
"fingerprint",
|
|
23
|
+
"cdp",
|
|
24
|
+
"mcp",
|
|
25
|
+
"proxy",
|
|
26
|
+
"automation"
|
|
27
|
+
],
|
|
28
|
+
"license": "UNLICENSED",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"dev": "bun --watch src/server.ts",
|
|
31
|
+
"start": "bun src/server.ts",
|
|
32
|
+
"mcp": "bun src/mcp.ts",
|
|
33
|
+
"build": "bun build src/server.ts src/mcp.ts --target=bun --minify --outdir=dist",
|
|
34
|
+
"build:binary": "bun build src/server.ts --compile --outfile dist/junso-browser",
|
|
35
|
+
"typecheck": "bunx tsc --noEmit",
|
|
36
|
+
"test": "bun test",
|
|
37
|
+
"prepublishOnly": "bun run build"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"agent-browser": "^0.27.1",
|
|
41
|
+
"cloakbrowser": "^0.3.31"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
45
|
+
"@types/bun": "latest",
|
|
46
|
+
"hono": "^4.6.0",
|
|
47
|
+
"typescript": "^5.6.0",
|
|
48
|
+
"zod": "3"
|
|
49
|
+
}
|
|
50
|
+
}
|