@vibro/bro 0.0.1-alpha.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/LICENSE +14 -0
- package/README.md +42 -0
- package/dist/vibro-cli.js +15 -0
- package/dist/vibro-client.d.mts +53 -0
- package/dist/vibro-client.js +1 -0
- package/dist/vibro-plugin.d.ts +66 -0
- package/dist/vibro-plugin.js +11 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Copyright (c) 2026 Vibro authors. All rights reserved.
|
|
2
|
+
|
|
3
|
+
TEMPORARY / EARLY RELEASE
|
|
4
|
+
This package is published to allow evaluation.
|
|
5
|
+
You may install this package from the npm registry and use it in
|
|
6
|
+
your own projects in accordance with these terms.
|
|
7
|
+
You may NOT, without prior written permission: copy, modify, merge, publish,
|
|
8
|
+
distribute, sublicense, sell, or otherwise use the software or its contents
|
|
9
|
+
except as installed via the official package for your own runtime use as
|
|
10
|
+
described above. Do not republish this code or derived works.
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
12
|
+
IMPLIED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
13
|
+
CLAIM, DAMAGES OR OTHER LIABILITY.
|
|
14
|
+
These terms may change in a future release. A different license may apply later.
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Vibro
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Vibro is a vite plugin and a runtime bridge for the browser.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
With pnpm:
|
|
11
|
+
```ts
|
|
12
|
+
pnpm add @vibro/bro -d
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
With npm:
|
|
16
|
+
```ts
|
|
17
|
+
npm install @vibro/bro --save-dev
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Add the plugin to your vite config:
|
|
24
|
+
```ts
|
|
25
|
+
// vite.config.ts
|
|
26
|
+
import {vibroVitePlugin} from '@vibro/bro';
|
|
27
|
+
|
|
28
|
+
export default defineConfig({
|
|
29
|
+
plugins: [react(), vibroVitePlugin()], // 👈
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Run some CLI commands:
|
|
34
|
+
```bash
|
|
35
|
+
pnpm vibro -h
|
|
36
|
+
pnpm pnpm vibro --screenshot --to-file ./tmp/screenshot.png
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## License
|
|
41
|
+
|
|
42
|
+
Unlicensed (see LICENSE file).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';var promises=require('fs/promises'),path=require('path');var Y=(t,e)=>()=>(t&&(e=t(t=0)),e);var Z=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);function E(t){return t!==null&&typeof t=="object"}var tt,et,k,L=Y(()=>{tt="node_modules",et="vibro-sessions.json",k=`${tt}/${et}`;});var kt=Z(()=>{L();var R=2e3,ut=12e4,$=[{flag:"--list",args:"",comment:"show active vibro bridge sessions"},{flag:"--request",args:"<path>",comment:"send request to /__vibro__/<path>"},{flag:"--screenshot",args:"[tabId]",comment:"take screenshot in a tab and save it (first tab by default)"},{flag:"--to-file",args:"<file>",comment:"custom screenshot output path (for --screenshot / mcp call)"},{flag:"--format",args:"png|jpeg",comment:"screenshot format"},{flag:"--quality",args:"0..1",comment:"jpeg quality when format=jpeg"},{flag:"--session",args:"last|pid",comment:"choose session by pid (default: last)"},{flag:"--url",args:"<http://host:port>",comment:"send directly to url, skip sessions file"},{flag:"--host",args:"<host> --port <port>",comment:"send directly by host + port"},{flag:"--sessions-file",args:"<file>",comment:"path to sessions file (default: node_modules/vibro-sessions.json)"},{flag:"--method",args:"GET",comment:"request method (default GET)"},{flag:"--body",args:"<text>",comment:"request body"},{flag:"--timeout",args:"<ms>",comment:"request timeout"},{flag:"--stdin",args:"",comment:"read body from stdin"},{flag:"--mcp",args:"",comment:"start MCP stdio server"},{flag:"--help",aliases:["-h"],args:"",comment:"show this help"}],ct=["vibro --list","vibro --request /list","vibro --request /ping --session 5731","vibro --screenshot --session 5731 --to-file ./tmp/screenshot.png","vibro --url http://127.0.0.1:5731 --request /ping","vibro --mcp"],at=["MCP server registration:"," 1) Add this command to your MCP client config:"," {",' "mcpServers": {',' "vibro": {',' "command": "vibro",',' "args": ["--mcp"]'," }"," }"," }",""," 2) If command is unavailable in PATH, use explicit command:"," node_modules/.bin/vibro --mcp",""," 3) Then MCP tools will be:"," - vibro_list"," - vibro_request"," - vibro_screenshot (saves screenshot by plugin)"],B="2.0",lt="2024-11-05",dt="vibro",pt="0.1.0",P=Symbol("mcp-skipped-frame"),ft=[{name:"vibro_list",description:"List active vibro bridge sessions",inputSchema:{type:"object",properties:{sessionsFile:{type:"string",description:"Path to sessions registry file (optional)"}},required:[]}},{name:"vibro_request",description:"Send request to /__vibro__ route on active session",inputSchema:{type:"object",properties:{request:{type:"string",description:"Request path (for example: /list or /ping)"},method:{type:"string",description:"HTTP method (GET, POST, ...)"},session:{type:"string",description:"Session selector: last|pid|port"},url:{type:"string",description:"Direct session URL (http://host:port)"},host:{type:"string",description:"Direct host when using --host/--port"},port:{type:"integer",description:"Direct port when using --host/--port"},body:{type:"string",description:"Request body"},timeout:{type:"integer",description:"Request timeout in milliseconds"},sessionsFile:{type:"string",description:"Path to sessions registry file (optional)"}},required:["request"]}},{name:"vibro_screenshot",description:"Take screenshot in a specific tab and save file on the machine running vibro",inputSchema:{type:"object",properties:{tabId:{type:"string",description:"Tab id from /__vibro__/list (first tab by default)"},path:{type:"string",description:"Destination path for saved screenshot (png by default)"},format:{type:"string",description:"Screenshot format: png or jpeg"},quality:{type:"number",description:"JPEG quality from 0 to 1"},session:{type:"string",description:"Session selector: last|pid|port"},url:{type:"string",description:"Direct session URL (http://host:port)"},host:{type:"string",description:"Direct host when using --host/--port"},port:{type:"integer",description:"Direct port when using --host/--port"},sessionsFile:{type:"string",description:"Path to sessions registry file (optional)"},timeout:{type:"integer",description:"Request timeout in milliseconds"}},required:[]}}];function mt(){return !(process.env.NO_COLOR!==void 0||process.env.FORCE_COLOR==="0"||process.stdout&&process.stdout.isTTY!==true||process.env.TERM==="dumb")}function b(t,e){return mt()?`\x1B[${e}m${t}\x1B[0m`:t}function ht(t){return b(` ${t}`,"1;34")}function D(t){let e=Array.isArray(t.aliases)&&t.aliases.length>0?` / ${t.aliases.join(", ")}`:"",n=t.args.length>0?` ${t.args}`:"";return `${t.flag}${e}${n}`}function gt(){let t=0;for(let e of $){let n=D(e);n.length>t&&(t=n.length);}return t+4}function yt(){let t=gt(),e=[`${b("Usage:","1;33")} ${b("vibro","1;36")} ${b("[options]","1;37")}`,...$.map(n=>{let s=D(n).padEnd(t);return `${ht(s)} ${b(n.comment,"0;37")}`}),"",`${b("Examples:","1;35")}`,...ct.map(n=>` ${b(n,"1;32")}`),"",`${b("MCP:","1;35")}`,...at.map(n=>` ${b(n,"0;37")}`)];process.stdout.write(e.join(`
|
|
3
|
+
`)+`
|
|
4
|
+
`);}function J(t,e){if(t===void 0)return;let n=JSON.stringify({jsonrpc:B,id:t,...e});process.stdout.write(n+`
|
|
5
|
+
`);}function q(t,e){t!==void 0&&J(t,{result:e});}function m(t,e,n,s){if(t===void 0)return;let u={code:e,message:n};J(t,{error:u});}function v(t){return typeof t=="string"&&t.length>0?t:k}function j(t){return t==null||typeof t=="string"||typeof t=="number"?t:void 0}async function bt(t,e){let n=typeof t.host=="string"?t.host:void 0,s=g(t.port);if(typeof t.url=="string"&&t.url.length>0)return H(t.url);if(n!==void 0&&Number.isFinite(s))return n+":"+s;let u=await I(e);if(u.length<=0)throw new Error("No active sessions found in "+e);let i=Ot(u,t.session);if(!i)throw new Error("No session matched the selector");return i.protocol+"://"+i.host+":"+i.port}async function O(t,e){if(typeof t.request!="string")throw new Error("Request path required. Use request");let n=W(t.request),s=await bt(t,e),u=Nt(t.method),i=F(t.timeout),o=typeof t.body=="string"?t.body:void 0;return It(H(s),n,{method:u,headers:{},body:o,timeout:i})}async function V(t,e){let n=z(t.format,"png"),s=Rt(t.quality,n==="jpeg"?.85:.92),u=new URLSearchParams;u.set("format",n),u.set("quality",String(s));let i=(typeof t.tabId=="string"&&t.tabId.length>0?`/screenshot/${encodeURIComponent(t.tabId)}`:"/screenshot")+(u.toString().length>0?`?${u.toString()}`:""),o=await O({request:i,url:t.url,session:t.session,host:t.host,port:t.port,method:"GET",body:t.body,timeout:F(t.timeout)},e);if(!o.ok)return {ok:false,text:o.text||`HTTP ${o.status}`};let r=wt(o.text),a=E(r)&&r.payload!==void 0?r.payload:r,c=E(a)&&typeof a.dataUrl=="string"?a.dataUrl:void 0;if(c===void 0)return {ok:false,text:`Invalid screenshot response: missing dataUrl in ${typeof o.text=="string"?o.text:""}`};let p=xt(c);if(p===void 0)return {ok:false,text:"Invalid screenshot dataUrl"};let d=At(t.path,n);await promises.mkdir(path.dirname(d),{recursive:true});let f=Buffer.from(p.data,"base64");return await promises.writeFile(d,f),{ok:true,payloadPath:d,size:f.length,mimeType:p.mimeType}}function St(t){let e="";return function(s){for(e+=s;;){let i=u();if(i===null)return;i!==P&&t(i);}function u(){if(e.length<=0)return null;let i=e.indexOf(`\r
|
|
6
|
+
\r
|
|
7
|
+
`),o=e.indexOf(`
|
|
8
|
+
|
|
9
|
+
`),r=i,a=4;if((i<0||o>=0&&o<i)&&(r=o,a=2),r<0){let _=e.indexOf(`
|
|
10
|
+
`);if(_<0)return null;let S=e.slice(0,_).trim();if(e=e.slice(_+1),S.length<=0||/^[A-Za-z_-]+:/.test(S))return P;if(S.startsWith("{")||S.startsWith("["))try{return JSON.parse(S)}catch{return P}return null}let c=e.slice(0,r),p=/Content-Length:\s*(\d+)/i.exec(c);if(!p)return e=e.slice(r+a),P;let d=g(p[1]);if(!Number.isFinite(d)||d<0)return e=e.slice(r+a),P;let f=r+a,y=f+d;if(e.length<y)return null;let N=e.slice(f,y);e=e.slice(y);try{return JSON.parse(N)}catch{return P}}}}async function _t(t){let e=v(t),n=false;return new Promise(s=>{let i=St(async o=>{if(!E(o)){m(null,-32600,"Invalid JSON-RPC message");return}if(o.jsonrpc!==B||typeof o.method!="string"){m(j(o.id),-32600,"Invalid JSON-RPC message");return}let r=j(o.id);if(r===void 0&&o.id!==void 0&&o.id!==null){m(null,-32600,"Invalid JSON-RPC id");return}if(o.method==="initialize"){n=true,q(r,{protocolVersion:lt,capabilities:{tools:{listChanged:false}},serverInfo:{name:dt,version:pt}});return}if(!n){m(r,-32002,"Server not initialized");return}if(o.method==="tools/list"){q(r,{tools:ft});return}if(o.method==="tools/call"){if(!E(o.params)){m(r,-32602,"Invalid params");return}let a=typeof o.params.name=="string"?o.params.name:void 0,c=E(o.params.arguments)?o.params.arguments:{},p=h(c.sessionsFile),d=h(c.request),f=h(c.url),y=h(c.session),N=h(c.host),_=h(c.port),S=h(c.method),G=h(c.body),Q=h(c.path),K=h(c.format),X=Et(c.quality),w=Pt(c.timeout,R),x=h(c.tabId);if(a==="vibro_list"){let l=v(p),A=await I(l);q(r,{content:[{type:"text",text:T(JSON.stringify(A,null,2))}]});return}if(a==="vibro_request"){if(d===void 0||d.length<=0){m(r,-32602,"request is required");return}try{let l=await O({request:d,url:f,session:y,host:N,port:_,method:S,body:G,timeout:w},v(p??e));q(r,{isError:!l.ok,content:[{type:"text",text:T(l.text)}]});}catch(l){m(r,-32e3,U(l));}return}if(a==="vibro_screenshot"){try{let l=await V({tabId:x!==void 0&&x.length>0?x:void 0,path:Q,format:z(K,"png"),quality:X,url:f,session:y,host:N,port:_,timeout:w},v(p??e));if(!l.ok){m(r,-32e3,l.text);return}let A=[{type:"text",text:`Screenshot saved: ${l.payloadPath} (${l.size} bytes)`}];q(r,{content:A,metadata:{path:l.payloadPath,size:l.size,saved:!0,mimeType:l.mimeType}});}catch(l){m(r,-32e3,U(l));}return}m(r,-32601,"Tool not found: "+a);return}m(r,-32601,"Method not found");});process.stdin.resume(),process.stdin.setEncoding("utf8"),process.stdin.on("data",o=>{i(String(o));}),process.stdin.on("end",()=>{s();}),process.stdin.on("close",()=>{s();}),process.stdin.on("error",()=>{s();});})}function g(t){if(typeof t=="number"&&Number.isFinite(t))return Math.trunc(t);if(typeof t!="string")return Number.NaN;let e=Number.parseInt(t,10);return Number.isFinite(e)?e:Number.NaN}function h(t){return typeof t=="string"?t:void 0}function Et(t){return typeof t=="string"||typeof t=="number"?t:void 0}function Pt(t,e){if(typeof t=="number")return Number.isFinite(t)?t:e;if(typeof t=="string"){let n=g(t);return Number.isFinite(n)?n:e}return e}function vt(t){let e={timeout:R};for(let n=0;n<t.length;n+=1){let s=t[n];if(s==="--list"){e.list=true;continue}if(s==="--help"){e.help=true;continue}if(s==="-h"){e.help=true;continue}if(s==="--request"){e.request=t[n+1],n+=1;continue}if(s==="--screenshot"){let u=t[n+1];u===void 0||u.startsWith("--")?e.screenshot=true:(e.screenshot=u,n+=1);continue}if(s==="--to-file"){e.path=t[n+1],n+=1;continue}if(s==="--format"){e.format=t[n+1],n+=1;continue}if(s==="--quality"){e.quality=t[n+1],n+=1;continue}if(s==="--session"){e.session=t[n+1],n+=1;continue}if(s==="--url"){e.url=t[n+1],n+=1;continue}if(s==="--host"){e.host=t[n+1],n+=1;continue}if(s==="--port"){e.port=t[n+1],n+=1;continue}if(s==="--method"){e.method=t[n+1],n+=1;continue}if(s==="--body"){e.body=t[n+1],n+=1;continue}if(s==="--sessions-file"){e.sessionsFile=t[n+1],n+=1;continue}if(s==="--timeout"){e.timeout=g(t[n+1]),n+=1;continue}if(s==="--stdin"){e.stdin=true;continue}if(s==="--mcp"){e.mcp=true;continue}if(s.startsWith("--request=")){e.request=s.slice(10);continue}if(s.startsWith("--screenshot=")){e.screenshot=s.slice(12);continue}if(s.startsWith("--to-file=")){e.path=s.slice(10);continue}if(s.startsWith("--format=")){e.format=s.slice(9);continue}if(s.startsWith("--quality=")){e.quality=s.slice(10);continue}if(s.startsWith("--session=")){e.session=s.slice(10);continue}if(s.startsWith("--url=")){e.url=s.slice(6);continue}if(s.startsWith("--host=")){e.host=s.slice(7);continue}if(s.startsWith("--port=")){e.port=s.slice(7);continue}if(s.startsWith("--method=")){e.method=s.slice(9);continue}if(s.startsWith("--body=")){e.body=s.slice(7);continue}if(s.startsWith("--sessions-file=")){e.sessionsFile=s.slice(16);continue}if(s.startsWith("--timeout=")){e.timeout=g(s.slice(10));continue}if(s.startsWith("--mcp=")){e.mcp=s.slice(6)!=="false";continue}if(!s.startsWith("--")){"request"in e||(e.request=s);continue}}return e}function C(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function qt(t){try{return process.kill(t,0),!0}catch(e){if(e instanceof Error){let n=e.code;return n!=="ESRCH"&&n!=="EINVAL"}return false}}function W(t){return typeof t!="string"||t.length<=0?"/":t.startsWith("/")?t:"/"+t}function F(t){let e=g(t);return Number.isFinite(e)&&e>0?e:R}function Nt(t){if(typeof t!="string")return "GET";let e=t.toUpperCase();return e.length>0?e:"GET"}function Tt(t){return t==="https"?"https":"http"}function z(t,e){if(typeof t!="string")return e;let n=t.toLowerCase();return n==="jpeg"||n==="jpg"?"jpeg":n==="png"?"png":e}function Rt(t,e){if(typeof t=="number"&&Number.isFinite(t))return t<=0||t>1?e:t;if(typeof t!="string")return e;let n=Number.parseFloat(t);return !Number.isFinite(n)||n<=0||n>1?e:n}function xt(t){if(typeof t!="string")return;let e=/^data:([^;]+);base64,(.+)$/.exec(t);if(e)return {mimeType:e[1],data:e[2]}}function At(t,e){let n=path.join(process.cwd(),"tmp","screenshots"),s=e==="jpeg"?".jpg":".png",u=`vibro-${Date.now()}${s}`;if(typeof t!="string"||t.length<=0)return path.join(n,u);let i=t.trim();return i.length<=0?path.join(n,u):path.extname(i).length>0?i:i+s}async function I(t){try{let e=await promises.readFile(t,"utf8"),n=JSON.parse(e);if(!C(n)||!C(n.instances))return [];let s=Date.now(),u=[];for(let[i,o]of Object.entries(n.instances)){if(!C(o))continue;let r=g(o.pid);if(!Number.isFinite(r)||r<=0||String(r)!==i||!qt(r))continue;let a=g(o.port),c=typeof o.host=="string"?o.host:"localhost",p=Tt(o.protocol),d=g(o.lastSeenAt);if(!Number.isFinite(a)||!Number.isFinite(d)||s-d>ut)continue;let f=typeof o.projectRoot=="string"?o.projectRoot:"",y=typeof o.cacheDir=="string"?o.cacheDir:"";f.length<=0||y.length<=0||u.push({pid:r,port:a,host:c,protocol:p,projectRoot:f,cacheDir:y,configFile:typeof o.configFile=="string"?o.configFile:void 0,lastSeenAt:d});}return u.sort((i,o)=>o.lastSeenAt-i.lastSeenAt)}catch{return []}}function Mt(t,e){return t.find(n=>n.port===e)}function Ct(t,e){return t.find(n=>n.pid===e)}function Ot(t,e){if(typeof e=="string"&&e.length>0&&e.toLowerCase()==="last")return t[0];let n=g(e);return Number.isFinite(n)?Ct(t,n)??Mt(t,n):t[0]}async function Ft(){let t=[];for await(let e of process.stdin)t.push(Buffer.from(e));if(t.length!==0)return Buffer.concat(t).toString("utf8")}async function It(t,e,n){let s=n.timeout>0?n.timeout:R,u=new AbortController,i=setTimeout(()=>{u.abort();},s),o=t+"/__vibro__"+W(e);try{let r=await fetch(o,{method:n.method,headers:n.headers||{},body:n.body,signal:u.signal}),a=await r.text();return {ok:r.ok,status:r.status,text:a}}finally{clearTimeout(i);}}function T(t){try{return JSON.stringify(JSON.parse(t),null,2)}catch{return t}}function wt(t){try{return JSON.parse(t)}catch{return}}function H(t){return t.startsWith("http://")||t.startsWith("https://")?t:"http://"+t}function U(t){return t instanceof Error?t.message:String(t)}(async()=>{let t=vt(process.argv.slice(2));if(t.help===true){yt();return}if(t.mcp===true){let i=v(t.sessionsFile);await _t(i);return}let e=v(t.sessionsFile),n=F(t.timeout);if(t.list){let i=await I(e);process.stdout.write(JSON.stringify(i,null,2));return}if(t.screenshot===true||typeof t.screenshot=="string"){let i=await V({tabId:t.screenshot===true?void 0:t.screenshot,path:t.path,format:t.format,quality:t.quality,url:t.url,session:t.session,host:t.host,port:t.port,timeout:n},e);if(!i.ok){let o=typeof i.text=="string"?i.text:"screenshot failed";process.stderr.write(o+`
|
|
11
|
+
`),process.exitCode=2;return}process.stdout.write(`Saved screenshot: ${i.payloadPath}
|
|
12
|
+
`);return}if(typeof t.request!="string")throw new Error("Request path required. Use --request <path>");let s;t.stdin===true?s=await Ft():typeof t.body=="string"&&(s=t.body);let u=await O({request:t.request,url:t.url,session:t.session,host:t.host,port:t.port,method:t.method,body:s,timeout:n},e);if(!u.ok){let i=u.text.length>0?u.text:"HTTP "+u.status;process.stderr.write(T(i)+`
|
|
13
|
+
`),process.exitCode=2;return}process.stdout.write(T(u.text)+`
|
|
14
|
+
`);})().catch(t=>{process.stderr.write(String(t&&t.message?t.message:t)+`
|
|
15
|
+
`),process.exitCode=1;});});var vibroCli = kt();module.exports=vibroCli;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type VibroTabId = string;
|
|
2
|
+
type VibroTabState = 'open' | 'stale' | 'closed';
|
|
3
|
+
interface VibroBridgeRequest {
|
|
4
|
+
id: string;
|
|
5
|
+
path: string;
|
|
6
|
+
method: string;
|
|
7
|
+
query: Record<string, string>;
|
|
8
|
+
headers: Record<string, string>;
|
|
9
|
+
body?: string;
|
|
10
|
+
targetTabId?: VibroTabId;
|
|
11
|
+
}
|
|
12
|
+
interface VibroBridgeResponse {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
payload?: unknown;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
interface VibroBridgeResponseMessage extends VibroBridgeResponse {
|
|
18
|
+
id: string;
|
|
19
|
+
sourceTabId?: VibroTabId;
|
|
20
|
+
}
|
|
21
|
+
interface VibroTabMetadata {
|
|
22
|
+
title: string;
|
|
23
|
+
pathname: string;
|
|
24
|
+
href: string;
|
|
25
|
+
focused: boolean;
|
|
26
|
+
wsUrl: string;
|
|
27
|
+
userAgent: string;
|
|
28
|
+
registeredAt: number;
|
|
29
|
+
lastSeenAt: number;
|
|
30
|
+
}
|
|
31
|
+
interface VibroTabRecord {
|
|
32
|
+
id: VibroTabId;
|
|
33
|
+
state: VibroTabState;
|
|
34
|
+
metadata: VibroTabMetadata;
|
|
35
|
+
}
|
|
36
|
+
interface VibroTabListRequestPayload {
|
|
37
|
+
requestId: string;
|
|
38
|
+
}
|
|
39
|
+
interface VibroTabListResponsePayload {
|
|
40
|
+
requestId: string;
|
|
41
|
+
tabId: VibroTabId;
|
|
42
|
+
metadata: VibroTabMetadata;
|
|
43
|
+
state?: VibroTabState;
|
|
44
|
+
}
|
|
45
|
+
type VibroBridgeRequestHandler = (request: VibroBridgeRequest) => VibroBridgeResponse | Promise<VibroBridgeResponse> | Promise<unknown> | unknown;
|
|
46
|
+
|
|
47
|
+
declare global {
|
|
48
|
+
interface Window {
|
|
49
|
+
__vibroRequestHandler?: VibroBridgeRequestHandler;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type { VibroBridgeRequest, VibroBridgeRequestHandler, VibroBridgeResponse, VibroBridgeResponseMessage, VibroTabId, VibroTabListRequestPayload, VibroTabListResponsePayload, VibroTabMetadata, VibroTabRecord, VibroTabState };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var _="__vibro__:request",b="__vibro__:response",f="__vibro__:tab-register",T="__vibro__:tab-heartbeat",w="__vibro__:tab-focus",y="__vibro__:list-request",h="__vibro__:list-response";function v(e){return e!==null&&typeof e=="object"}var N=5e3,R="__vibroBridgeTabId";function k(){let e=window,r=e[R];if(typeof r=="string"&&r.length>0)return r;let t=x();return e[R]=t,t}var a=k(),i,S=false,I=false;function q(e){return e!==null&&typeof e=="object"}function M(e){return q(e)&&"ok"in e&&typeof e.ok=="boolean"&&("payload"in e||"error"in e)}function L(e){return e instanceof Error?e.message:String(e)}function O(e,r=.92){if(typeof e!="string")return r;let t=Number.parseFloat(e);return !Number.isFinite(t)||t<=0?r:t>1?1:t}function P(e){if(typeof e=="string"){let r=e.toLowerCase();if(r==="jpeg"||r==="jpg")return "jpeg"}return "png"}function x(){let e=new Uint8Array(4);return crypto.getRandomValues(e),Array.from(e).map(r=>r.toString(16).padStart(2,"0")).join("")}function D(){if(I)return;let e=window.__vibroRequestHandler;window.__vibroRequestHandler=async r=>r.path==="/screenshot"?j(r):typeof e=="function"?e(r):{ok:false,error:"No vibro request handler found on window.__vibroRequestHandler"},I=true;}function H(e){return e.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&e.videoWidth>0&&e.videoHeight>0?Promise.resolve():new Promise((r,t)=>{let n=setTimeout(()=>t(new Error("Timed out waiting for camera frame")),5e3),s=()=>{clearTimeout(n),e.removeEventListener("loadedmetadata",d),e.removeEventListener("error",o);},d=()=>{s(),r();},o=c=>{s(),t(new Error(`Video error: ${String(c)}`));};e.addEventListener("loadedmetadata",d,{once:true}),e.addEventListener("error",o,{once:true});})}function U(e){e!==void 0&&e.getTracks().forEach(r=>{r.stop();});}async function F(e){if(!navigator.mediaDevices||!("getDisplayMedia"in navigator.mediaDevices))throw new Error("Screen capture is not supported in this browser");let r,t=null;try{let n={audio:!1,preferCurrentTab:!0,video:{frameRate:{ideal:30}}};r=await navigator.mediaDevices.getDisplayMedia({...n,selfBrowserSurface:"include",surfaceSwitching:"exclude"}),t=document.createElement("video"),t.srcObject=r,t.muted=!0,t.autoplay=!0,t.playsInline=!0,await t.play().catch(()=>{}),await H(t);let s=t.videoWidth,d=t.videoHeight;if(s<=0||d<=0)throw new Error("Failed to read video frame size for screenshot");let o=document.createElement("canvas");o.width=s,o.height=d;let c=o.getContext("2d");if(c===null)throw new Error("Canvas context is not available");c.drawImage(t,0,0,s,d);let g=e.format==="jpeg"?"image/jpeg":"image/png";return {dataUrl:e.format==="png"?o.toDataURL(g):o.toDataURL(g,e.quality),width:s,height:d,format:e.format,quality:e.quality}}finally{t!==null&&t.remove(),U(r);}}async function j(e){let r=P(e.query.format??e.query.quality),t=O(e.query.quality,r==="png"?.92:.75);try{let n=await F({format:r,quality:t});return {ok:!0,payload:{dataUrl:n.dataUrl,width:n.width,height:n.height,format:n.format,quality:n.quality}}}catch(n){return {ok:false,error:L(n)}}}function C(){return `${location.protocol==="https:"?"wss:":"ws:"}//${location.host}/@vite/client`}function u(){let e=Date.now();return {title:document.title||"",pathname:location.pathname,href:location.href,focused:document.visibilityState==="visible",wsUrl:C(),userAgent:navigator.userAgent,registeredAt:e,lastSeenAt:e}}function W(e){return v(e)&&typeof e.requestId=="string"&&e.requestId.length>0}function l(e){import.meta.hot&&import.meta.hot.send(b,e);}function E(e,r){if(!import.meta.hot)return;let t=u();if(e===f){import.meta.hot.send(e,{tabId:a,metadata:r});return}import.meta.hot.send(e,{tabId:a,metadata:{...t,focused:r.focused,lastSeenAt:Date.now()}});}function z(e){if(!W(e)||!import.meta.hot)return;let r=u(),t={requestId:e.requestId,tabId:a,metadata:{...r,lastSeenAt:Date.now()},state:"open"};import.meta.hot.send(h,t);}function Q(e){import.meta.hot&&z(e);}function m(){import.meta.hot&&E(w,u());}function $(e){if(D(),e.targetTabId&&e.targetTabId!==a)return;let r=window.__vibroRequestHandler;if(typeof r!="function"){l({id:e.id,sourceTabId:a,ok:false,error:"No Srv request handler found on window.__vibroRequestHandler"});return}(async()=>{try{let t=await r(e);if(M(t)){l({id:e.id,sourceTabId:a,ok:t.ok,payload:t.payload,error:t.error});return}l({id:e.id,sourceTabId:a,ok:!0,payload:t});}catch(t){l({id:e.id,sourceTabId:a,ok:false,error:L(t)});}})();}function G(){E(f,u());}function V(){E(T,u());}function p(e=0){if(!S){if(import.meta.hot){console.log("[vibro] initialized"),S=true,G(),V(),i===void 0&&(i=setInterval(V,N)),import.meta.hot.on(_,$),import.meta.hot.on(y,Q),document.addEventListener("visibilitychange",m),window.addEventListener("focus",m),window.addEventListener("blur",m),window.addEventListener("beforeunload",()=>{i!==void 0&&(clearInterval(i),i=void 0);}),import.meta.hot.on("vite:beforeFullReload",()=>{i!==void 0&&(clearInterval(i),i=void 0);});return}if(e>=20){console.warn("[vibro] HMR is not available, bridge is disabled");return}setTimeout(()=>{p(e+1);},50);}}document.readyState==="loading"?window.addEventListener("DOMContentLoaded",()=>{p();},{once:true}):p();
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
type VibroBridgePluginOptions = {
|
|
4
|
+
removeFromListOnReload?: boolean;
|
|
5
|
+
instanceHeartbeatMs?: number;
|
|
6
|
+
instanceTtlMs?: number;
|
|
7
|
+
sessionFileName?: string;
|
|
8
|
+
registryFileName?: string;
|
|
9
|
+
registryRetries?: number;
|
|
10
|
+
registryRetryDelayMs?: number;
|
|
11
|
+
};
|
|
12
|
+
declare function vibroVitePlugin(options?: VibroBridgePluginOptions): Plugin;
|
|
13
|
+
|
|
14
|
+
type VibroTabId = string;
|
|
15
|
+
type VibroTabState = 'open' | 'stale' | 'closed';
|
|
16
|
+
interface VibroBridgeRequest {
|
|
17
|
+
id: string;
|
|
18
|
+
path: string;
|
|
19
|
+
method: string;
|
|
20
|
+
query: Record<string, string>;
|
|
21
|
+
headers: Record<string, string>;
|
|
22
|
+
body?: string;
|
|
23
|
+
targetTabId?: VibroTabId;
|
|
24
|
+
}
|
|
25
|
+
interface VibroBridgeResponse {
|
|
26
|
+
ok: boolean;
|
|
27
|
+
payload?: unknown;
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
interface VibroBridgeResponseMessage extends VibroBridgeResponse {
|
|
31
|
+
id: string;
|
|
32
|
+
sourceTabId?: VibroTabId;
|
|
33
|
+
}
|
|
34
|
+
interface VibroTabMetadata {
|
|
35
|
+
title: string;
|
|
36
|
+
pathname: string;
|
|
37
|
+
href: string;
|
|
38
|
+
focused: boolean;
|
|
39
|
+
wsUrl: string;
|
|
40
|
+
userAgent: string;
|
|
41
|
+
registeredAt: number;
|
|
42
|
+
lastSeenAt: number;
|
|
43
|
+
}
|
|
44
|
+
interface VibroTabRecord {
|
|
45
|
+
id: VibroTabId;
|
|
46
|
+
state: VibroTabState;
|
|
47
|
+
metadata: VibroTabMetadata;
|
|
48
|
+
}
|
|
49
|
+
interface VibroTabListRequestPayload {
|
|
50
|
+
requestId: string;
|
|
51
|
+
}
|
|
52
|
+
interface VibroTabListResponsePayload {
|
|
53
|
+
requestId: string;
|
|
54
|
+
tabId: VibroTabId;
|
|
55
|
+
metadata: VibroTabMetadata;
|
|
56
|
+
state?: VibroTabState;
|
|
57
|
+
}
|
|
58
|
+
type VibroBridgeRequestHandler = (request: VibroBridgeRequest) => VibroBridgeResponse | Promise<VibroBridgeResponse> | Promise<unknown> | unknown;
|
|
59
|
+
|
|
60
|
+
declare global {
|
|
61
|
+
interface Window {
|
|
62
|
+
__vibroRequestHandler?: VibroBridgeRequestHandler;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { type VibroBridgeRequest, type VibroBridgeRequestHandler, type VibroBridgeResponse, type VibroBridgeResponseMessage, type VibroTabId, type VibroTabListRequestPayload, type VibroTabListResponsePayload, type VibroTabMetadata, type VibroTabRecord, type VibroTabState, vibroVitePlugin };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';var crypto=require('crypto'),promises=require('fs/promises'),fs=require('fs'),path=require('path');var L="/__vibro__",at="__vibro__:request",ct="__vibro__:response",z="__vibro__:tab-register",G="__vibro__:tab-heartbeat",W="__vibro__:tab-focus",ut="__vibro__:list-request",dt="__vibro__:list-response",$="node_modules",Q="vibro-sessions.json";function T(t){return t!==null&&typeof t=="object"}var Y="dist",lt="vibro-cli.js",pt="vibro-client.js",C="vibro",xt=path.join($,".bin");function Ct(){let t=c=>`/@fs/${c.replace(/\\/g,"/")}`,e=__dirname;for(;;){let c=path.join(e,Y),d=path.join(c,lt),u=path.join(c,pt);if(fs.existsSync(d)&&fs.existsSync(u))return {packageRoot:e,cliSourceFilePath:d,clientScriptPathForVite:t(u)};let f=path.dirname(e);if(f===e)break;e=f;}let r=path.resolve(__dirname,"../.."),s=path.join(r,Y,pt);return {packageRoot:r,cliSourceFilePath:path.join(r,Y,lt),clientScriptPathForVite:t(s)}}var{cliSourceFilePath:jt,clientScriptPathForVite:Ut}=Ct(),Ht=4e3,qt=4e3,Jt=8e3,zt=3e4,mt=3e4,Gt=12e4,Wt=3,Qt=50;function P(t){return promises.stat(t).then(()=>true,()=>false)}function Yt(t){return promises.readFile(t,"utf8").then(e=>{try{return JSON.parse(e)}catch{return}},()=>{})}function Xt(t){return Array.isArray(t)?t.length>0:T(t)&&Object.keys(t).length>0}async function Kt(t){async function e(c){if(await Promise.all([P(path.join(c,".git")),P(path.join(c,"pnpm-workspace.yaml")),P(path.join(c,".pnpm-workspace-state-v1.json")),P(path.join(c,"lerna.json")),P(path.join(c,"nx.json")),P(path.join(c,"turbo.json"))]).then(l=>l.some(Boolean)))return true;let u=path.join(c,"package.json"),f=await Yt(u);return f!==void 0&&Xt(f.workspaces)}let r=path.resolve(t),s="";for(;;){await e(r)&&(s=r);let c=path.dirname(r);if(c===r)break;r=c;}return s.length>0?s:path.resolve(t)}function F(t,e){return typeof t=="number"&&Number.isFinite(t)?t:e}function k(t,e){return typeof t=="string"?t:e}function Zt(t){try{return process.kill(t,0),!0}catch(e){if(!(e instanceof Error))return false;let r=e.code;return !(r==="ESRCH"||r==="EINVAL")}}function Et(t){let e=k(t.config.server.host,"localhost");return e==="0.0.0.0"||e==="::"?"localhost":e}function _t(t){return t.config.server.https?"https":"http"}function te(t){let e=t.httpServer?.address();if(!(e==null||typeof e=="string"))return Number.isFinite(e.port)?e.port:void 0}function ee(t){let e=t.config.configFile;return typeof e=="string"?e:void 0}function Z(){return {updatedAt:Date.now(),instances:{}}}function U(t,e){let r=Date.now(),s={};for(let[c,d]of Object.entries(t)){if(d.pid<=0||!Number.isFinite(d.pid)||!Zt(d.pid)||r-d.lastSeenAt>e)continue;let u=String(d.pid);c===u&&(s[c]=d);}return s}function ne(t,e){if(!T(t))return Z();let r=t.updatedAt,s=t.instances;if(!T(s))return Z();let c={};for(let[d,u]of Object.entries(s)){if(!T(u))continue;let f=F(u.pid,NaN);if(!Number.isFinite(f)||String(Math.trunc(f))!==d)continue;let l=F(u.port,NaN);if(!Number.isFinite(l))continue;let m=F(u.startedAt,Date.now()),b=F(u.lastSeenAt,Date.now()),R=k(u.protocol,"http");if(R!=="http"&&R!=="https")continue;let E=k(u.host,"localhost"),y=k(u.cacheDir,"");if(y.length<=0)continue;let I=k(u.projectRoot,"");I.length<=0||(c[d]={pid:f,port:l,host:E,protocol:R,cacheDir:y,projectRoot:I,configFile:k(u.configFile,""),startedAt:m,lastSeenAt:b});}return {updatedAt:F(r,Date.now()),instances:U(c,e)}}function re(t,e){return path.join(t,$,e)}async function oe(t,e){try{let r=await promises.readFile(t,"utf8");return ne(JSON.parse(r),e)}catch{return Z()}}async function ie(t,e){await promises.mkdir(path.dirname(t),{recursive:true});let r=`${t}.tmp`;await promises.writeFile(r,JSON.stringify(e,null,2),"utf8"),await promises.rename(r,t);}function se(t){return new Promise(e=>{setTimeout(e,t);})}function x(t,e){console.error("[vibro]",t,e);}async function et(t,e,r,s,c,d){for(let u=0;u<=s;u+=1)try{let f=await oe(t,r),l=d(f);await ie(t,l);return}catch(f){if(u>=s)throw f;await se(c);}}function bt(t){return `'${t.replace(/'/g,`'"'"'`)}'`}function ht(t){return `"${t.replace(/"/g,'""')}"`}function St(t){return `'${t.replace(/'/g,"''")}'`}function D(t,e=C){return path.join(t,xt,e)}async function ae(t,e,r){let s=D(t),c=D(t,`${C}.CMD`),d=D(t,`${C}.ps1`),u=D(t,`${C}.cli.mjs`),f=bt(e),l=bt(r),m=ht(r),b=St(r),R=`#!/usr/bin/env sh
|
|
2
|
+
exec node ${f} --sessions-file ${l} "$@"
|
|
3
|
+
`,E=`@ECHO OFF
|
|
4
|
+
node ${ht(e)} --sessions-file ${m} %*
|
|
5
|
+
`,y=`$ErrorActionPreference = 'Stop'
|
|
6
|
+
$cli = ${St(e)}
|
|
7
|
+
$sessionFile = ${b}
|
|
8
|
+
& node $cli --sessions-file $sessionFile @args
|
|
9
|
+
exit $LASTEXITCODE
|
|
10
|
+
`,I=[{path:s,content:R,executable:true},{path:c,content:E},{path:d,content:y}];await promises.unlink(u).catch(()=>{});for(let h of I){try{if(await promises.readFile(h.path,"utf8")===h.content){h.executable&&await promises.chmod(h.path,493);continue}}catch{}await promises.mkdir(path.dirname(h.path),{recursive:true});let B=`${h.path}.tmp`;await promises.writeFile(B,h.content,"utf8"),await promises.rename(B,h.path),h.executable&&await promises.chmod(h.path,493);}}async function ce(t,e,r,s,c,d){await et(t,e,r,s,c,u=>{let f=Date.now(),l=String(process.pid),m=U(u.instances,r),b=m[l]?.startedAt??f;return m[l]={pid:process.pid,port:d,host:Et(e),protocol:_t(e),cacheDir:e.config.cacheDir,projectRoot:e.config.root,configFile:ee(e),startedAt:b,lastSeenAt:f},{updatedAt:f,instances:m}});}async function ue(t,e,r,s,c){await et(t,e,r,s,c,d=>{let u=Date.now(),f=String(process.pid),l=d.instances[f];if(!l)return d;let m=U(d.instances,r);return m[f]={...l,lastSeenAt:u},{updatedAt:u,instances:m}});}async function de(t,e,r,s,c){await et(t,e,r,s,c,d=>{let u=Date.now(),f=U(d.instances,r);return delete f[String(process.pid)],{updatedAt:u,instances:f}});}function fe(t){let e={};for(let[r,s]of Object.entries(t))s!==void 0&&(typeof s=="string"?e[r]=s:Array.isArray(s)&&(e[r]=s.join(",")));return e}function ge(t){return new Promise((e,r)=>{let s="";t.on("data",c=>{s+=c.toString();}),t.on("error",r),t.on("end",()=>{e(s||void 0);});})}function le(t,e){let r=t===void 0?NaN:Number.parseInt(t,10);return Number.isFinite(r)&&r>=0?r:e}function pe(t){return typeof t=="string"&&t.length>0}function me(t){return !(!T(t)||typeof t.id!="string"||typeof t.ok!="boolean")}function be(t){return !(!T(t)||typeof t.requestId!="string"||typeof t.tabId!="string"||!T(t.metadata)||"state"in t&&typeof t.state!="string")}function X(t){let e=Date.now();if(!T(t))return {title:"",pathname:"",href:"",focused:false,wsUrl:"",userAgent:"",registeredAt:e,lastSeenAt:e};let r=typeof t.registeredAt=="number"?t.registeredAt:e,s=typeof t.lastSeenAt=="number"?t.lastSeenAt:e;return {title:typeof t.title=="string"?t.title:"",pathname:typeof t.pathname=="string"?t.pathname:"",href:typeof t.href=="string"?t.href:"",focused:typeof t.focused=="boolean"?t.focused:false,wsUrl:typeof t.wsUrl=="string"?t.wsUrl:"",userAgent:typeof t.userAgent=="string"?t.userAgent:"",registeredAt:r,lastSeenAt:s}}function A(t,e){let r=e-t;return r>zt?"closed":r>Jt?"stale":"open"}function he(t={}){let e=new Map,r=new Map,s=new Map,c=t.removeFromListOnReload??false,d=t.instanceHeartbeatMs??mt,u=t.instanceTtlMs??Gt,f=t.sessionFileName??t.registryFileName??Q,l=t.registryRetries??Wt,m=t.registryRetryDelayMs??Qt,b="",R="",E,y,I=false,B=false;function H(){E!==void 0&&(clearInterval(E),E=void 0);}async function nt(n){R=await Kt(n.config.root),b=re(R,f);let i=jt;if(!await P(i))throw new Error(`vibro cli source file not found: ${i}`);await ae(R,i,b);}async function wt(n){let i=y;if(i===void 0)return;(b.length<=0||R.length<=0)&&await nt(n);let o=Number.isFinite(d)&&d>0?d:mt;await ce(b,n,u,l,m,i),I=true,E!==void 0&&H(),E=setInterval(()=>{ue(b,n,u,l,m).catch(a=>{x("failed to touch Vite instance",a);});},o);}async function rt(n){!b.length||!I||(I=false,H(),await de(b,n,u,l,m));}function At(n){return new Promise((i,o)=>{let a=setTimeout(()=>{e.delete(n),o(new Error(`Bridge request timeout (${n})`));},Ht);e.set(n,{resolve:i,reject:o,timeout:a});})}function It(n){let i=e.get(n.id);i&&(clearTimeout(i.timeout),e.delete(n.id),i.resolve(n));}function M(){let n=Date.now();return Array.from(s.values()).map(i=>({...i,state:A(i.metadata.lastSeenAt,n),metadata:{...i.metadata,lastSeenAt:i.metadata.lastSeenAt}}))}function Vt(){let n=Date.now(),o=M().filter(g=>A(g.metadata.lastSeenAt,n)!=="closed");if(o.length<=0)return;let a=o.find(g=>g.metadata.focused);return a!==void 0?a.id:o[0].id}function ot(n,i,o){let a=Date.now(),g=X(i),_=s.get(n),V={id:n,state:o??A(g.lastSeenAt,a),metadata:{...g,registeredAt:_?.metadata.registeredAt??g.registeredAt,lastSeenAt:a}};return s.set(n,V),V}function vt(n,i,o){let a=Date.now();return i<=0?Promise.resolve(M().map(g=>({...g,state:A(g.metadata.lastSeenAt,a)}))):new Promise((g,_)=>{let V=setTimeout(()=>{it(n);},o);r.set(n,{expected:i,resolve:g,reject:_,timeout:V,responses:new Map});})}function it(n){let i=r.get(n);if(!i)return;clearTimeout(i.timeout),r.delete(n);let o=Date.now(),a=new Map;M().forEach(g=>{a.set(g.id,{...g,state:A(g.metadata.lastSeenAt,o)});}),i.responses.forEach(g=>{a.set(g.id,{...g,state:A(g.metadata.lastSeenAt,o)});}),i.resolve(Array.from(a.values()));}function Pt(n){let i=r.get(n.requestId);if(!i)return;let o=n.state??A(n.metadata.lastSeenAt,Date.now()),a={...X(n.metadata),lastSeenAt:Date.now()},g=ot(n.tabId,a,o);i.responses.set(n.tabId,g),i.expected>0&&i.responses.size>=i.expected&&it(n.requestId);}function q(n,i){if(!T(i))return;let o=i.tabId,a=i.metadata;if(!pe(o)||!T(a))return;let g=X(a);ot(o,g);}function Nt(n){let i=n.split("/").filter(Boolean);if(i.length<2)return;let o=i[0];if(!(o!=="ping"&&o!=="screenshot"))return {commandPath:`/${o}`,targetTabId:i[1]}}return {name:"vibro-plugin",apply:"serve",configureServer(n){let i=async()=>{if(y=te(n),y!==void 0)try{if(await nt(n),await wt(n),!B){let o=_t(n),a=Et(n);console.info(` \u25A0 Registered server ${a?`${o}://${a}:${y}`:`:${y}`}`),console.info(` [vibro] registry ready at ${a?`${o}://${a}:${y}`:`:${y}`}`),console.info(` [vibro] sessions: ${b}`),console.info(` [vibro] cli: ${D(R)}`),B=!0;}}catch(o){x("failed to register Vite instance",o);}};n.httpServer?.listening?i():n.httpServer&&n.httpServer.once("listening",()=>{i();}),n.httpServer?.once("close",()=>{rt(n).catch(o=>{x("failed to detach Vite instance",o);});}),c&&n.ws.on("vite:beforeFullReload",()=>{rt(n).catch(o=>{x("failed to detach Vite instance on reload",o);});}),n.ws.on(ct,o=>{me(o)&&It(o);}),n.ws.on(z,o=>{q(z,o);}),n.ws.on(G,o=>{q(G,o);}),n.ws.on(W,o=>{q(W,o);}),n.ws.on(dt,o=>{be(o)&&Pt(o);}),n.middlewares.use(L,async(o,a,g)=>{if(!o.url||!o.method){g();return}let _=new URL(o.url,"http://localhost"),V=o.method.toUpperCase(),kt=!["GET","HEAD"].includes(V),w={id:crypto.randomUUID(),path:_.pathname.startsWith(L)?_.pathname.substring(L.length)||"/":_.pathname,method:V,query:Object.fromEntries(_.searchParams.entries()),headers:fe(o.headers),body:kt?await ge(o).catch(()=>{}):void 0};if(console.log(`[vibro] Received a signal: ${V} ${_}`),w.path==="/list"){let S=le(w.query.timeout,qt),N=w.id,Ft=Date.now(),st=M().map(O=>({...O,state:A(O.metadata.lastSeenAt,Ft)})).filter(O=>O.state!=="closed").length,Dt=vt(N,st,S);st>0&&n.ws.send(ut,{requestId:N});let Mt=await Dt;a.statusCode=200,a.setHeader("content-type","application/json; charset=utf-8"),a.end(JSON.stringify({ok:true,payload:{tabs:Mt}}));return}let v,J=Nt(w.path);if(J!==void 0?(v=J.targetTabId,w.path=J.commandPath):w.path==="/screenshot"&&(v=Vt()),v!==void 0&&(w.targetTabId=v),v!==void 0){let S=s.get(v),N=Date.now();if(!S||A(S.metadata.lastSeenAt,N)==="closed"){a.statusCode=404,a.setHeader("content-type","application/json; charset=utf-8"),a.end(JSON.stringify({ok:false,error:`Unknown target tab: ${v}`}));return}}let Bt=At(w.id);n.ws.send(at,w);try{let S=await Bt;a.statusCode=S.ok?200:500,a.setHeader("content-type","application/json; charset=utf-8"),a.end(JSON.stringify({ok:S.ok,payload:S.payload,error:S.error}));}catch(S){let N=S instanceof Error?S.message:String(S);a.statusCode=504,a.setHeader("content-type","application/json; charset=utf-8"),a.end(JSON.stringify({ok:false,error:N}));}});},transformIndexHtml(n){return {html:n,tags:[{tag:"script",injectTo:"body-prepend",attrs:{type:"module",src:Ut}}]}}}}
|
|
11
|
+
exports.vibroVitePlugin=he;
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vibro/bro",
|
|
3
|
+
"version": "0.0.1-alpha.1",
|
|
4
|
+
"description": "Vibro plugin and runtime bridge",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public",
|
|
7
|
+
"tag": "alpha",
|
|
8
|
+
"registry": "https://registry.npmjs.org/"
|
|
9
|
+
},
|
|
10
|
+
"license": "UNLICENSED",
|
|
11
|
+
"author": "Mike Mikest East",
|
|
12
|
+
"type": "commonjs",
|
|
13
|
+
"main": "./dist/vibro-plugin.js",
|
|
14
|
+
"types": "./dist/vibro-plugin.d.ts",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"vite",
|
|
17
|
+
"vite-plugin",
|
|
18
|
+
"mcp",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"devtools",
|
|
21
|
+
"browser",
|
|
22
|
+
"cli",
|
|
23
|
+
"plugin",
|
|
24
|
+
"screenshot",
|
|
25
|
+
"development"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22"
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"vibro": "./dist/vibro-cli.js"
|
|
32
|
+
},
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/vibro-plugin.d.ts",
|
|
36
|
+
"default": "./dist/vibro-plugin.js"
|
|
37
|
+
},
|
|
38
|
+
"./client": {
|
|
39
|
+
"types": "./dist/vibro-client.d.mts",
|
|
40
|
+
"default": "./dist/vibro-client.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"package.json",
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
]
|
|
52
|
+
}
|