@vibecontrols/vibe-plugin-tool-code-server 2026.602.1 → 2026.608.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.
- package/dist/index.js +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
var __defProp=Object.defineProperty;var __returnValue=(v)=>v;function __exportSetter(name,newValue){this[name]=__returnValue.bind(null,newValue)}var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0,configurable:!0,set:__exportSetter.bind(all,name)})};var __esm=(fn,res)=>()=>(fn&&(res=fn(fn=0)),res);var __require=import.meta.require;import{homedir}from"os";async function isPortAvailable(port){try{return Bun.serve({port,hostname:"127.0.0.1",fetch(){return new Response}}).stop(!0),!0}catch{return!1}}async function findAvailablePort(preferred){let start=preferred??DEFAULT_PORT;if(await isPortAvailable(start))return start;for(let port=DEFAULT_PORT;port<=PORT_RANGE_END;port++){if(port===start)continue;if(await isPortAvailable(port))return port}throw Error(`No available port in range ${DEFAULT_PORT}-${PORT_RANGE_END}`)}async function startCodeServer(binaryPath,options){if(childProcess&¤tPid){if(isProcessAlive(currentPid))return{pid:currentPid,port:currentPort};childProcess=null,currentPid=null}if(isStarting)throw Error("code-server is already starting");isStarting=!0,lastError=null;try{let port=await findAvailablePort(options?.port),workspacePath=options?.workspacePath||homedir(),args=[binaryPath,"--auth","none","--bind-addr",`127.0.0.1:${port}`,"--app-name","VibeControls Editor","--disable-telemetry","--disable-update-check","--disable-proxy",workspacePath],proc=Bun.spawn(args,{stdout:"pipe",stderr:"pipe",env:{...process.env,PORT:void 0,CS_DISABLE_GETTING_STARTED_OVERRIDE:"1"}});if(childProcess=proc,currentPort=port,currentWorkspacePath=workspacePath,currentPid=proc.pid,proc.exited.then((code)=>{if(childProcess===proc){if(childProcess=null,currentPid=null,code!==0&&code!==null)lastError=`code-server exited with code ${code}`}}),await new Promise((resolve)=>setTimeout(resolve,1500)),!isProcessAlive(proc.pid)){let stderr=await new Response(proc.stderr).text();throw Error(`code-server failed to start: ${stderr}`)}return{pid:proc.pid,port}}catch(err){throw lastError=err instanceof Error?err.message:"Failed to start",err}finally{isStarting=!1}}async function stopCodeServer(){if(!childProcess||!currentPid){childProcess=null,currentPid=null;return}let proc=childProcess,pid=currentPid;childProcess=null,currentPid=null,currentPort=null,currentWorkspacePath=null,lastError=null;try{process.kill(pid,"SIGTERM")}catch{return}let deadline=Date.now()+5000;while(Date.now()<deadline&&isProcessAlive(pid))await new Promise((resolve)=>setTimeout(resolve,200));if(isProcessAlive(pid))try{process.kill(pid,"SIGKILL")}catch{}try{await proc.exited}catch{}}function getStatus(){let running=Boolean(currentPid&&isProcessAlive(currentPid));if(!running&&childProcess)childProcess=null,currentPid=null;return{installed:!0,installing:!1,running,pid:running?currentPid??void 0:void 0,port:running?currentPort??void 0:void 0,workspacePath:running?currentWorkspacePath??void 0:void 0,error:lastError??void 0}}function getRunningPort(){if(!currentPid||!isProcessAlive(currentPid))return null;return currentPort}function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}var childProcess=null,currentPort=null,currentWorkspacePath=null,currentPid=null,lastError=null,isStarting=!1,DEFAULT_PORT=13337,PORT_RANGE_END=13347;var init_process=()=>{};import{Elysia as Elysia2}from"elysia";var RoutesBuilder=class{constructor(pluginName,hostServices){this.pluginName=pluginName,this.hostServices=hostServices}pluginName;hostServices;prefix;apiKeyResolver;errorHandlerEnabled=!1;loggingEnabled=!1;withPrefix(prefix){return this.prefix=prefix,this}withAuth(getApiKey){return this.apiKeyResolver=getApiKey,this}withErrorHandler(){return this.errorHandlerEnabled=!0,this}withLogging(){return this.loggingEnabled=!0,this}build(){let opts=this.prefix?{prefix:this.prefix}:void 0,app=new Elysia2(opts);if(this.apiKeyResolver){let resolver=this.apiKeyResolver;app.onBeforeHandle(async({request,set})=>{let supplied=request.headers.get("x-api-key"),expected=await resolver();if(!supplied||supplied!==expected)return set.status=401,{error:"unauthorized"};return})}if(this.errorHandlerEnabled){let pluginName=this.pluginName,logger=this.hostServices?.logger;app.onError(({error,set})=>{let message=error instanceof Error?error.message:String(error);return logger?.error?.(pluginName,"route error",{message}),set.status=500,{error:message}})}if(this.loggingEnabled){let pluginName=this.pluginName,logger=this.hostServices?.logger;app.onRequest(({request})=>{logger?.debug?.(pluginName,"request",{method:request.method,url:request.url})})}return app}};var init_routes=()=>{};var exports_installer={};__export(exports_installer,{installCodeServer:()=>installCodeServer,getBinaryPath:()=>getBinaryPath,checkInstallation:()=>checkInstallation});import os from"os";import path from"path";async function resolveBinaryPath(){let found=Bun.which("code-server",{PATH:process.env.PATH});if(found)return found;for(let p of SEARCH_PATHS)try{if(await Bun.file(p).exists())return p}catch{continue}return null}async function getVersion(binaryPath){try{let proc=Bun.spawn([binaryPath,"--version"],{stdout:"pipe",stderr:"pipe"}),output=await new Response(proc.stdout).text();if(await proc.exited===0)return output.trim().split(`
|
|
3
|
-
`)[0].trim()}catch{}return null}async function checkInstallation(storage){let binaryPath=await resolveBinaryPath();if(!binaryPath)return{installed:!1,version:null,binaryPath:null};let version=await getVersion(binaryPath);if(await storage.set(STORAGE_NS,KEY_INSTALLED,"true"),version)await storage.set(STORAGE_NS,KEY_VERSION,version);return await storage.set(STORAGE_NS,KEY_BINARY_PATH,binaryPath),{installed:!0,version,binaryPath}}async function installCodeServer(storage,onLog){onLog?.("Downloading and installing code-server...");try{if(process.platform==="win32")return{success:!1,binaryPath:null,error:"Automated install is unsupported on Windows. Install code-server manually from https://github.com/coder/code-server/releases and re-run."};let installPrefix=path.join(os.homedir(),".local"),proc=Bun.spawn(["sh","-c",`curl -fsSL https://code-server.dev/install.sh | sh -s -- --method standalone --prefix ${installPrefix}`],{stdout:"pipe",stderr:"pipe",env:{...process.env,PATH:`${installPrefix}/bin:${process.env.PATH}`}}),stdout=await new Response(proc.stdout).text(),stderr=await new Response(proc.stderr).text(),exitCode=await proc.exited;if(stdout)onLog?.(stdout);if(stderr)onLog?.(stderr);if(exitCode!==0){let errorMsg=`Installation failed with exit code ${exitCode}: ${stderr}`;return onLog?.(errorMsg),{success:!1,binaryPath:null,error:errorMsg}}let check=await checkInstallation(storage);if(!check.installed)return{success:!1,binaryPath:null,error:"Installation script succeeded but binary not found"};return onLog?.(`code-server ${check.version} installed at ${check.binaryPath}`),{success:!0,binaryPath:check.binaryPath}}catch(err){let errorMsg=err instanceof Error?err.message:"Unknown installation error";return onLog?.(errorMsg),{success:!1,binaryPath:null,error:errorMsg}}}async function getBinaryPath(storage){let cached=await storage.get(STORAGE_NS,KEY_BINARY_PATH);if(cached)try{if(await Bun.file(cached).exists())return cached}catch{}return resolveBinaryPath()}var STORAGE_NS="code-server",KEY_INSTALLED="installed",KEY_VERSION="version",KEY_BINARY_PATH="binary-path",SEARCH_PATHS;var init_installer=__esm(()=>{SEARCH_PATHS=[path.join(os.homedir(),".local","bin","code-server"),"/usr/local/bin/code-server","/usr/bin/code-server"]});var exports_routes={};__export(exports_routes,{createCodeServerRoutes:()=>createCodeServerRoutes});import{homedir as homedir2}from"os";function createCodeServerRoutes(hostServices){let storage=hostServices.storage;return new RoutesBuilder("code-server",hostServices).withPrefix("/api/code-server").withErrorHandler().build().get("/status",async()=>{let installInfo=await checkInstallation(storage),processStatus=getStatus();return{installed:installInfo.installed,installing:isInstalling,running:processStatus.running,pid:processStatus.pid,port:processStatus.port,workspacePath:processStatus.workspacePath,version:installInfo.version,error:installError||processStatus.error||void 0}}).post("/install",async({set})=>{if(isInstalling)return set.status=409,{error:"Installation already in progress"};let check=await checkInstallation(storage);if(check.installed)return{message:"code-server is already installed",version:check.version,binaryPath:check.binaryPath};return isInstalling=!0,installError=null,(async()=>{try{let result=await installCodeServer(storage);if(!result.success)installError=result.error||"Installation failed"}catch(err){installError=err instanceof Error?err.message:"Installation failed"}finally{isInstalling=!1}})(),{message:"Installation started \u2014 poll GET /api/code-server/status"}}).post("/start",async({body,set})=>{let{workspacePath,port}=body||{},binaryPath=await getBinaryPath(storage);if(!binaryPath)return set.status=400,{error:"code-server is not installed. POST /api/code-server/install first"};try{let result=await startCodeServer(binaryPath,{workspacePath,port});return{message:"code-server started",pid:result.pid,port:result.port,workspacePath:workspacePath||homedir2()}}catch(err){return set.status=500,{error:err instanceof Error?err.message:"Failed to start"}}}).post("/stop",async()=>{return await stopCodeServer(),{message:"code-server stopped"}}).post("/restart",async({body,set})=>{let{workspacePath}=body||{},binaryPath=await getBinaryPath(storage);if(!binaryPath)return set.status=400,{error:"code-server is not installed"};await stopCodeServer();try{let result=await startCodeServer(binaryPath,{workspacePath});return{message:"code-server restarted",pid:result.pid,port:result.port,workspacePath:workspacePath||homedir2()}}catch(err){return set.status=500,{error:err instanceof Error?err.message:"Failed to restart"}}})}var isInstalling=!1,installError=null;var init_routes2=__esm(()=>{init_routes();init_installer();init_process()});var exports_proxy={};__export(exports_proxy,{validateSessionToken:()=>validateSessionToken,default:()=>proxy_default,createCodeServerProxy:()=>createCodeServerProxy});import{Elysia as Elysia3}from"elysia";function searchWithoutApiKey(url){let sp=new URLSearchParams;for(let[k,v]of url.searchParams){if(k.toLowerCase()==="apikey")continue;sp.append(k,v)}let s=sp.toString();return s?`?${s}`:""}function originAllowed(request){let method=request.method.toUpperCase();if(method==="GET"||method==="HEAD")return!0;let proxyHost=new URL(request.url).host,origin=request.headers.get("origin");if(origin)try{return new URL(origin).host===proxyHost}catch{return!1}let referer=request.headers.get("referer");if(referer)try{return new URL(referer).host===proxyHost}catch{return!1}return!1}function generateSessionToken(){let bytes=new Uint8Array(32);return crypto.getRandomValues(bytes),Buffer.from(bytes).toString("base64url")}function createSession(){let token=generateSessionToken(),now=Date.now(),session={token,createdAt:now,expiresAt:now+SESSION_TTL_MS};return sessions.set(token,session),session}function validateSessionToken(token){let session=sessions.get(token);if(!session)return!1;if(Date.now()>session.expiresAt)return sessions.delete(token),!1;return!0}function cleanupSessions(){let now=Date.now();for(let[token,session]of sessions)if(now>session.expiresAt)sessions.delete(token)}function getCookie(cookieHeader,name){if(!cookieHeader)return null;let match=cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));return match?match[1]:null}function isAuthed(request,validateApiKey){let cookieHeader=request.headers.get("cookie"),sessionToken=getCookie(cookieHeader,COOKIE_NAME),apiKeyHeader=request.headers.get("x-agent-api-key"),apiKeyParam=new URL(request.url).searchParams.get("apiKey"),hasValidSession=sessionToken?validateSessionToken(sessionToken):!1,hasValidApiKey=apiKeyHeader!=null&&validateApiKey(apiKeyHeader)||apiKeyParam!=null&&validateApiKey(apiKeyParam);return{hasValidSession,hasValidApiKey}}function stripPrefix(pathname){return pathname.replace(/^\/code-server\/?/,"/")||"/"}function injectEditorLoader(html){let bodyOpen=html.match(/<body[^>]*>/i);if(bodyOpen&&bodyOpen.index!==void 0){let at=bodyOpen.index+bodyOpen[0].length;return html.slice(0,at)+EDITOR_LOADER_SNIPPET+html.slice(at)}return EDITOR_LOADER_SNIPPET+html}function createCodeServerProxy(getPort,validateApiKey){return new Elysia3({prefix:"/code-server"}).ws("/*",{open(ws){let wsData=ws.data,cookieHeader=wsData.headers?.cookie??(wsData.request?.headers?.get("cookie")||null),sessionToken=getCookie(cookieHeader??null,COOKIE_NAME),apiKeyParam=wsData.query?.apiKey??null,hasSession=sessionToken?validateSessionToken(sessionToken):!1,hasKey=apiKeyParam?validateApiKey(apiKeyParam):!1;if(!hasSession&&!hasKey){ws.close(1008,"Unauthorized");return}let port=getPort();if(!port){ws.close(1011,"code-server not running");return}let requestUrl=wsData.request?.url??(wsData.headers?.["x-forwarded-uri"]||"/code-server/"),upstreamPath;try{let url=new URL(requestUrl,`http://127.0.0.1:${port}`);upstreamPath=stripPrefix(url.pathname)+url.search}catch{upstreamPath="/"}let bridgeId=`cs-bridge-${++bridgeCounter}`;wsData._bridgeId=bridgeId;let state={upstream:null,upstreamReady:!1,buffer:[]};bridges.set(bridgeId,state);let upstreamUrl=`ws://127.0.0.1:${port}${upstreamPath}`,upstreamWs=new WebSocket(upstreamUrl);state.upstream=upstreamWs,upstreamWs.addEventListener("open",()=>{state.upstreamReady=!0;for(let msg of state.buffer)upstreamWs.send(msg);state.buffer.length=0}),upstreamWs.addEventListener("message",(event)=>{try{let data=event.data;if(data instanceof ArrayBuffer)ws.send(new Uint8Array(data));else if(data instanceof Blob)data.arrayBuffer().then((ab)=>{try{ws.send(new Uint8Array(ab))}catch{}});else ws.send(data)}catch{}}),upstreamWs.addEventListener("close",(event)=>{bridges.delete(bridgeId);try{ws.close(event.code||1000,event.reason||"Upstream closed")}catch{}}),upstreamWs.addEventListener("error",()=>{bridges.delete(bridgeId);try{ws.close(1011,"Upstream error")}catch{}})},message(ws,message){let bridgeId=ws.data._bridgeId;if(!bridgeId)return;let state=bridges.get(bridgeId);if(!state)return;let payload;if(typeof message==="string")payload=message;else if(message instanceof ArrayBuffer)payload=message;else if(message instanceof Uint8Array||Buffer.isBuffer(message)){let copy=new ArrayBuffer(message.byteLength);new Uint8Array(copy).set(new Uint8Array(message.buffer,message.byteOffset,message.byteLength)),payload=copy}else if(typeof message==="object"&&message!==null)payload=JSON.stringify(message);else payload=String(message);if(state.upstream&&state.upstreamReady&&state.upstream.readyState===WebSocket.OPEN)try{state.upstream.send(payload)}catch{}else state.buffer.push(payload)},close(ws){let bridgeId=ws.data._bridgeId;if(bridgeId){let state=bridges.get(bridgeId);if(state?.upstream&&state.upstream.readyState===WebSocket.OPEN)state.upstream.close(1000,"Client disconnected");bridges.delete(bridgeId)}}}).all("/*",async({request})=>{return handleProxyRequest(request,getPort,validateApiKey)}).all("/",async({request})=>{return handleProxyRequest(request,getPort,validateApiKey)})}async function handleProxyRequest(request,getPort,validateApiKey){let{hasValidSession,hasValidApiKey}=isAuthed(request,validateApiKey);if(!hasValidSession&&!hasValidApiKey)return new Response(JSON.stringify({error:"Unauthorized \u2014 provide a valid API key or session"}),{status:401,headers:{"Content-Type":"application/json"}});if(!hasValidApiKey&&hasValidSession&&!originAllowed(request))return new Response(JSON.stringify({error:"Forbidden \u2014 invalid Origin"}),{status:403,headers:{"Content-Type":"application/json"}});let sessionCookieHeader=null;if(!hasValidSession&&hasValidApiKey){let session=createSession();sessionCookieHeader=`${COOKIE_NAME}=${session.token}; Path=/code-server/; HttpOnly; SameSite=None; Secure; Max-Age=${Math.floor(SESSION_TTL_MS/1000)}`}let port=getPort();if(!port)return new Response(JSON.stringify({error:"code-server is not running"}),{status:503,headers:{"Content-Type":"application/json"}});let response=await handleHttpProxy(request,port);if(sessionCookieHeader){let headers=new Headers(response.headers);return headers.set("Set-Cookie",sessionCookieHeader),new Response(response.body,{status:response.status,statusText:response.statusText,headers})}return response}async function handleHttpProxy(request,port){let url=new URL(request.url),strippedPath=stripPrefix(url.pathname),sanitisedSearch=searchWithoutApiKey(url),upstreamUrl=`http://127.0.0.1:${port}${strippedPath}${sanitisedSearch}`,upstreamHeaders=new Headers,hopByHopHeaders=new Set(["connection","keep-alive","transfer-encoding","te","trailer","upgrade","proxy-authorization","proxy-authenticate","referer"]);request.headers.forEach((value,key)=>{if(!hopByHopHeaders.has(key.toLowerCase()))upstreamHeaders.set(key,value)}),upstreamHeaders.set("Host",`127.0.0.1:${port}`);try{let upstreamResponse=await fetch(upstreamUrl,{method:request.method,headers:upstreamHeaders,body:request.method!=="GET"&&request.method!=="HEAD"?request.body:void 0,redirect:"manual"}),responseHeaders=new Headers;upstreamResponse.headers.forEach((value,key)=>{if(!STRIP_RESPONSE_HEADERS.has(key.toLowerCase()))responseHeaders.set(key,value)});let contentType=upstreamResponse.headers.get("content-type")??"";if(request.method==="GET"&&upstreamResponse.ok&&contentType.includes("text/html")){let body=await upstreamResponse.text(),out=body.includes("code/didStartRenderer")?injectEditorLoader(body):body;return responseHeaders.delete("content-encoding"),responseHeaders.delete("etag"),responseHeaders.delete("last-modified"),responseHeaders.set("content-length",String(Buffer.byteLength(out,"utf8"))),new Response(out,{status:upstreamResponse.status,statusText:upstreamResponse.statusText,headers:responseHeaders})}return new Response(upstreamResponse.body,{status:upstreamResponse.status,statusText:upstreamResponse.statusText,headers:responseHeaders})}catch(err){return new Response(JSON.stringify({error:"Failed to proxy to code-server",details:err instanceof Error?err.message:"Unknown error"}),{status:502,headers:{"Content-Type":"application/json"}})}}var SESSION_TTL_MS=3600000,COOKIE_NAME="__vibe_cs_session",sessions,STRIP_RESPONSE_HEADERS,EDITOR_LOADER_SNIPPET=`<div id="vibe-cs-loader" role="status" aria-label="Starting editor" style="position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;background:#1e1e1e;color:#cccccc;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
|
|
3
|
+
`)[0].trim()}catch{}return null}async function checkInstallation(storage){let binaryPath=await resolveBinaryPath();if(!binaryPath)return{installed:!1,version:null,binaryPath:null};let version=await getVersion(binaryPath);if(await storage.set(STORAGE_NS,KEY_INSTALLED,"true"),version)await storage.set(STORAGE_NS,KEY_VERSION,version);return await storage.set(STORAGE_NS,KEY_BINARY_PATH,binaryPath),{installed:!0,version,binaryPath}}async function installCodeServer(storage,onLog){onLog?.("Downloading and installing code-server...");try{if(process.platform==="win32")return{success:!1,binaryPath:null,error:"Automated install is unsupported on Windows. Install code-server manually from https://github.com/coder/code-server/releases and re-run."};let installPrefix=path.join(os.homedir(),".local"),proc=Bun.spawn(["sh","-c",`curl -fsSL https://code-server.dev/install.sh | sh -s -- --method standalone --prefix ${installPrefix}`],{stdout:"pipe",stderr:"pipe",env:{...process.env,PATH:`${installPrefix}/bin:${process.env.PATH}`}}),stdout=await new Response(proc.stdout).text(),stderr=await new Response(proc.stderr).text(),exitCode=await proc.exited;if(stdout)onLog?.(stdout);if(stderr)onLog?.(stderr);if(exitCode!==0){let errorMsg=`Installation failed with exit code ${exitCode}: ${stderr}`;return onLog?.(errorMsg),{success:!1,binaryPath:null,error:errorMsg}}let check=await checkInstallation(storage);if(!check.installed)return{success:!1,binaryPath:null,error:"Installation script succeeded but binary not found"};return onLog?.(`code-server ${check.version} installed at ${check.binaryPath}`),{success:!0,binaryPath:check.binaryPath}}catch(err){let errorMsg=err instanceof Error?err.message:"Unknown installation error";return onLog?.(errorMsg),{success:!1,binaryPath:null,error:errorMsg}}}async function getBinaryPath(storage){let cached=await storage.get(STORAGE_NS,KEY_BINARY_PATH);if(cached)try{if(await Bun.file(cached).exists())return cached}catch{}return resolveBinaryPath()}var STORAGE_NS="code-server",KEY_INSTALLED="installed",KEY_VERSION="version",KEY_BINARY_PATH="binary-path",SEARCH_PATHS;var init_installer=__esm(()=>{SEARCH_PATHS=[path.join(os.homedir(),".local","bin","code-server"),"/usr/local/bin/code-server","/usr/bin/code-server"]});var exports_routes={};__export(exports_routes,{createCodeServerRoutes:()=>createCodeServerRoutes});import{homedir as homedir2}from"os";function createCodeServerRoutes(hostServices){let storage=hostServices.storage;return new RoutesBuilder("code-server",hostServices).withPrefix("/api/code-server").withErrorHandler().build().get("/status",async()=>{let installInfo=await checkInstallation(storage),processStatus=getStatus();return{installed:installInfo.installed,installing:isInstalling,running:processStatus.running,pid:processStatus.pid,port:processStatus.port,workspacePath:processStatus.workspacePath,version:installInfo.version,error:installError||processStatus.error||void 0}}).post("/install",async({set})=>{if(isInstalling)return set.status=409,{error:"Installation already in progress"};let check=await checkInstallation(storage);if(check.installed)return{message:"code-server is already installed",version:check.version,binaryPath:check.binaryPath};return isInstalling=!0,installError=null,(async()=>{try{let result=await installCodeServer(storage);if(!result.success)installError=result.error||"Installation failed"}catch(err){installError=err instanceof Error?err.message:"Installation failed"}finally{isInstalling=!1}})(),{message:"Installation started \u2014 poll GET /api/code-server/status"}}).post("/start",async({body,set})=>{let{workspacePath,port}=body||{},binaryPath=await getBinaryPath(storage);if(!binaryPath)return set.status=400,{error:"code-server is not installed. POST /api/code-server/install first"};try{let result=await startCodeServer(binaryPath,{workspacePath,port});return{message:"code-server started",pid:result.pid,port:result.port,workspacePath:workspacePath||homedir2()}}catch(err){return set.status=500,{error:err instanceof Error?err.message:"Failed to start"}}}).post("/stop",async()=>{return await stopCodeServer(),{message:"code-server stopped"}}).post("/restart",async({body,set})=>{let{workspacePath}=body||{},binaryPath=await getBinaryPath(storage);if(!binaryPath)return set.status=400,{error:"code-server is not installed"};await stopCodeServer();try{let result=await startCodeServer(binaryPath,{workspacePath});return{message:"code-server restarted",pid:result.pid,port:result.port,workspacePath:workspacePath||homedir2()}}catch(err){return set.status=500,{error:err instanceof Error?err.message:"Failed to restart"}}})}var isInstalling=!1,installError=null;var init_routes2=__esm(()=>{init_routes();init_installer();init_process()});var exports_proxy={};__export(exports_proxy,{validateSessionToken:()=>validateSessionToken,default:()=>proxy_default,createCodeServerProxy:()=>createCodeServerProxy});import{Elysia as Elysia3}from"elysia";function searchWithoutApiKey(url){let sp=new URLSearchParams;for(let[k,v]of url.searchParams){if(k.toLowerCase()==="apikey")continue;sp.append(k,v)}let s=sp.toString();return s?`?${s}`:""}function originAllowed(request){let method=request.method.toUpperCase();if(method==="GET"||method==="HEAD")return!0;let proxyHost=new URL(request.url).host,origin=request.headers.get("origin");if(origin)try{return new URL(origin).host===proxyHost}catch{return!1}let referer=request.headers.get("referer");if(referer)try{return new URL(referer).host===proxyHost}catch{return!1}return!1}function generateSessionToken(){let bytes=new Uint8Array(32);return crypto.getRandomValues(bytes),Buffer.from(bytes).toString("base64url")}function createSession(){let token=generateSessionToken(),now=Date.now(),session={token,createdAt:now,expiresAt:now+SESSION_TTL_MS};return sessions.set(token,session),session}function validateSessionToken(token){let session=sessions.get(token);if(!session)return!1;if(Date.now()>session.expiresAt)return sessions.delete(token),!1;return!0}function cleanupSessions(){let now=Date.now();for(let[token,session]of sessions)if(now>session.expiresAt)sessions.delete(token)}function getCookie(cookieHeader,name){if(!cookieHeader)return null;let match=cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));return match?match[1]:null}function isAuthed(request,validateApiKey){let cookieHeader=request.headers.get("cookie"),sessionToken=getCookie(cookieHeader,COOKIE_NAME),apiKeyHeader=request.headers.get("x-agent-api-key"),apiKeyParam=new URL(request.url).searchParams.get("apiKey"),hasValidSession=sessionToken?validateSessionToken(sessionToken):!1,hasValidApiKey=apiKeyHeader!=null&&validateApiKey(apiKeyHeader)||apiKeyParam!=null&&validateApiKey(apiKeyParam);return{hasValidSession,hasValidApiKey}}function stripPrefix(pathname){return pathname.replace(/^\/code-server\/?/,"/")||"/"}function injectEditorLoader(html){let bodyOpen=html.match(/<body[^>]*>/i);if(bodyOpen&&bodyOpen.index!==void 0){let at=bodyOpen.index+bodyOpen[0].length;return html.slice(0,at)+EDITOR_LOADER_SNIPPET+html.slice(at)}return EDITOR_LOADER_SNIPPET+html}function createCodeServerProxy(getPort,validateApiKey){return new Elysia3({prefix:"/code-server"}).ws("/*",{open(ws){let wsData=ws.data,cookieHeader=wsData.headers?.cookie??(wsData.request?.headers?.get("cookie")||null),sessionToken=getCookie(cookieHeader??null,COOKIE_NAME),apiKeyParam=wsData.query?.apiKey??null,hasSession=sessionToken?validateSessionToken(sessionToken):!1,hasKey=apiKeyParam?validateApiKey(apiKeyParam):!1;if(!hasSession&&!hasKey){ws.close(1008,"Unauthorized");return}let port=getPort();if(!port){ws.close(1011,"code-server not running");return}let requestUrl=wsData.request?.url??(wsData.headers?.["x-forwarded-uri"]||"/code-server/"),upstreamPath;try{let url=new URL(requestUrl,`http://127.0.0.1:${port}`);upstreamPath=stripPrefix(url.pathname)+url.search}catch{upstreamPath="/"}let bridgeId=`cs-bridge-${++bridgeCounter}`;wsData._bridgeId=bridgeId;let state={upstream:null,upstreamReady:!1,buffer:[]};bridges.set(bridgeId,state);let upstreamUrl=`ws://127.0.0.1:${port}${upstreamPath}`,upstreamWs=new WebSocket(upstreamUrl);state.upstream=upstreamWs,upstreamWs.addEventListener("open",()=>{state.upstreamReady=!0;for(let msg of state.buffer)upstreamWs.send(msg);state.buffer.length=0}),upstreamWs.addEventListener("message",(event)=>{try{let data=event.data;if(data instanceof ArrayBuffer)ws.send(new Uint8Array(data));else if(data instanceof Blob)data.arrayBuffer().then((ab)=>{try{ws.send(new Uint8Array(ab))}catch{}});else ws.send(data)}catch{}}),upstreamWs.addEventListener("close",(event)=>{bridges.delete(bridgeId);try{ws.close(event.code||1000,event.reason||"Upstream closed")}catch{}}),upstreamWs.addEventListener("error",()=>{bridges.delete(bridgeId);try{ws.close(1011,"Upstream error")}catch{}})},message(ws,message){let bridgeId=ws.data._bridgeId;if(!bridgeId)return;let state=bridges.get(bridgeId);if(!state)return;let payload;if(typeof message==="string")payload=message;else if(message instanceof ArrayBuffer)payload=message;else if(message instanceof Uint8Array||Buffer.isBuffer(message)){let copy=new ArrayBuffer(message.byteLength);new Uint8Array(copy).set(new Uint8Array(message.buffer,message.byteOffset,message.byteLength)),payload=copy}else if(typeof message==="object"&&message!==null)payload=JSON.stringify(message);else payload=String(message);if(state.upstream&&state.upstreamReady&&state.upstream.readyState===WebSocket.OPEN)try{state.upstream.send(payload)}catch{}else state.buffer.push(payload)},close(ws){let bridgeId=ws.data._bridgeId;if(bridgeId){let state=bridges.get(bridgeId);if(state?.upstream&&state.upstream.readyState===WebSocket.OPEN)state.upstream.close(1000,"Client disconnected");bridges.delete(bridgeId)}}}).all("/*",async({request})=>{return handleProxyRequest(request,getPort,validateApiKey)}).all("/",async({request})=>{return handleProxyRequest(request,getPort,validateApiKey)})}async function handleProxyRequest(request,getPort,validateApiKey){let{hasValidSession,hasValidApiKey}=isAuthed(request,validateApiKey);if(!hasValidSession&&!hasValidApiKey)return new Response(JSON.stringify({error:"Unauthorized \u2014 provide a valid API key or session"}),{status:401,headers:{"Content-Type":"application/json"}});if(!hasValidApiKey&&hasValidSession&&!originAllowed(request))return new Response(JSON.stringify({error:"Forbidden \u2014 invalid Origin"}),{status:403,headers:{"Content-Type":"application/json"}});let sessionCookieHeader=null;if(!hasValidSession&&hasValidApiKey){let session=createSession();sessionCookieHeader=`${COOKIE_NAME}=${session.token}; Path=/code-server/; HttpOnly; SameSite=None; Secure; Max-Age=${Math.floor(SESSION_TTL_MS/1000)}`}let port=getPort();if(!port)return new Response(JSON.stringify({error:"code-server is not running"}),{status:503,headers:{"Content-Type":"application/json"}});let response=await handleHttpProxy(request,port);if(sessionCookieHeader){let headers=new Headers(response.headers);return headers.set("Set-Cookie",sessionCookieHeader),new Response(response.body,{status:response.status,statusText:response.statusText,headers})}return response}async function handleHttpProxy(request,port){let url=new URL(request.url),strippedPath=stripPrefix(url.pathname),sanitisedSearch=searchWithoutApiKey(url),upstreamUrl=`http://127.0.0.1:${port}${strippedPath}${sanitisedSearch}`,upstreamHeaders=new Headers,hopByHopHeaders=new Set(["connection","keep-alive","transfer-encoding","te","trailer","upgrade","proxy-authorization","proxy-authenticate","referer"]);request.headers.forEach((value,key)=>{if(!hopByHopHeaders.has(key.toLowerCase()))upstreamHeaders.set(key,value)}),upstreamHeaders.set("Host",`127.0.0.1:${port}`);try{let upstreamResponse=await fetch(upstreamUrl,{method:request.method,headers:upstreamHeaders,body:request.method!=="GET"&&request.method!=="HEAD"?request.body:void 0,redirect:"manual"}),responseHeaders=new Headers;upstreamResponse.headers.forEach((value,key)=>{if(!STRIP_RESPONSE_HEADERS.has(key.toLowerCase()))responseHeaders.set(key,value)});let contentType=upstreamResponse.headers.get("content-type")??"";if(request.method==="GET"&&upstreamResponse.ok&&contentType.includes("text/html")){let body=await upstreamResponse.text(),out=body.includes("code/didStartRenderer")?injectEditorLoader(body):body;return responseHeaders.delete("content-encoding"),responseHeaders.delete("etag"),responseHeaders.delete("last-modified"),responseHeaders.set("content-length",String(Buffer.byteLength(out,"utf8"))),new Response(out,{status:upstreamResponse.status,statusText:upstreamResponse.statusText,headers:responseHeaders})}if(responseHeaders.has("content-encoding"))responseHeaders.delete("content-encoding"),responseHeaders.delete("content-length");return new Response(upstreamResponse.body,{status:upstreamResponse.status,statusText:upstreamResponse.statusText,headers:responseHeaders})}catch(err){return new Response(JSON.stringify({error:"Failed to proxy to code-server",details:err instanceof Error?err.message:"Unknown error"}),{status:502,headers:{"Content-Type":"application/json"}})}}var SESSION_TTL_MS=3600000,COOKIE_NAME="__vibe_cs_session",sessions,STRIP_RESPONSE_HEADERS,EDITOR_LOADER_SNIPPET=`<div id="vibe-cs-loader" role="status" aria-label="Starting editor" style="position:fixed;inset:0;z-index:2147483647;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;background:#1e1e1e;color:#cccccc;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
|
|
4
4
|
<div style="width:42px;height:42px;border:3px solid rgba(255,255,255,0.15);border-top-color:#3794ff;border-radius:50%;animation:vibe-cs-spin 0.8s linear infinite;"></div>
|
|
5
5
|
<div style="font-size:13px;letter-spacing:0.3px;opacity:0.85;">Starting editor\u2026</div>
|
|
6
6
|
<style>@keyframes vibe-cs-spin{to{transform:rotate(360deg)}}</style>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibecontrols/vibe-plugin-tool-code-server",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.608.1",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
"format": "bunx prettier . --write",
|
|
14
14
|
"format:check": "bunx prettier . --check",
|
|
15
15
|
"type:check": "tsc --noEmit",
|
|
16
|
+
"test": "bun test",
|
|
16
17
|
"clean": "rimraf dist",
|
|
17
18
|
"prebuild": "bun run clean",
|
|
18
19
|
"prepublishOnly": "bun run build",
|
|
19
|
-
"sanity": "bun run format:check && bun run lint && bun run type:check && bun run build"
|
|
20
|
+
"sanity": "bun run format:check && bun run lint && bun run type:check && bun run test && bun run build"
|
|
20
21
|
},
|
|
21
22
|
"keywords": [
|
|
22
23
|
"vibecontrols",
|