@pablopunk/webr 0.1.4 → 0.1.6

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 CHANGED
@@ -5,7 +5,7 @@
5
5
  <p align="center">
6
6
  <i align="center">Control your Herdr agents from anywhere.</i>
7
7
  </p>
8
-
8
+
9
9
  <h1 align="center">webr</h1>
10
10
 
11
11
  > [!IMPORTANT]
@@ -18,8 +18,8 @@
18
18
  Everything is handled automatically via the plugin:
19
19
 
20
20
  ```sh
21
- webr plugin install # start Webr with the Herdr server
22
- webr open # open Webr (webr status shows the address)
21
+ webr plugin install
22
+ webr open # Open the dashboard in your browser
23
23
  ```
24
24
 
25
25
  AGPL-3.0-or-later
@@ -2442,7 +2442,7 @@ var pageMap = /* @__PURE__ */ new Map([
2442
2442
  ]);
2443
2443
  //#endregion
2444
2444
  //#region \0virtual:astro:manifest
2445
- var _manifest = deserializeManifest({"rootDir":"file:///home/runner/work/webr/webr/","cacheDir":"file:///home/runner/work/webr/webr/node_modules/.astro/","outDir":"file:///home/runner/work/webr/webr/dist/","srcDir":"file:///home/runner/work/webr/webr/src/","publicDir":"file:///home/runner/work/webr/webr/public/","buildClientDir":"file:///home/runner/work/webr/webr/dist/client/","buildServerDir":"file:///home/runner/work/webr/webr/dist/server/","adapterName":"@astrojs/node","assetsDir":"_astro","routes":[{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"type":"page","component":"_server-islands.astro","params":["name"],"segments":[[{"content":"_server-islands","dynamic":false,"spread":false}],[{"content":"name","dynamic":true,"spread":false}]],"pattern":"^\\/_server-islands\\/([^/]+?)\\/?$","prerender":false,"isIndex":false,"fallbackRoutes":[],"route":"/_server-islands/[name]","origin":"internal","distURL":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/_image","component":"node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node.js","params":[],"pathname":"/_image","pattern":"^\\/_image\\/?$","segments":[[{"content":"_image","dynamic":false,"spread":false}]],"type":"endpoint","prerender":false,"fallbackRoutes":[],"distURL":[],"isIndex":false,"origin":"internal","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/new","isIndex":false,"type":"page","pattern":"^\\/new\\/?$","segments":[[{"content":"new","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/new.astro","pathname":"/new","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/sessions/[session]/tabs/[tab]","isIndex":false,"type":"page","pattern":"^\\/sessions\\/([^/]+?)\\/tabs\\/([^/]+?)\\/?$","segments":[[{"content":"sessions","dynamic":false,"spread":false}],[{"content":"session","dynamic":true,"spread":false}],[{"content":"tabs","dynamic":false,"spread":false}],[{"content":"tab","dynamic":true,"spread":false}]],"params":["session","tab"],"component":"src/pages/sessions/[session]/tabs/[tab].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/settings","isIndex":false,"type":"page","pattern":"^\\/settings\\/?$","segments":[[{"content":"settings","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/settings.astro","pathname":"/settings","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/threads/[id]","isIndex":false,"type":"page","pattern":"^\\/threads\\/([^/]+?)\\/?$","segments":[[{"content":"threads","dynamic":false,"spread":false}],[{"content":"id","dynamic":true,"spread":false}]],"params":["id"],"component":"src/pages/threads/[id].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/","isIndex":true,"type":"page","pattern":"^\\/$","segments":[],"params":[],"component":"src/pages/index.astro","pathname":"/","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}}],"serverLike":true,"middlewareMode":"classic","base":"/","trailingSlash":"ignore","compressHTML":"jsx","componentMetadata":[["/home/runner/work/webr/webr/src/pages/new.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/sessions/[session]/tabs/[tab].astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/settings.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/threads/[id].astro",{"propagation":"none","containsHead":true}]],"renderers":[],"clientDirectives":[["idle","(()=>{var l=(n,t)=>{let i=async()=>{await(await n())()},e=typeof t.value==\"object\"?t.value:void 0,s={timeout:e==null?void 0:e.timeout};\"requestIdleCallback\"in window?window.requestIdleCallback(i,s):setTimeout(i,s.timeout||200)};(self.Astro||(self.Astro={})).idle=l;window.dispatchEvent(new Event(\"astro:idle\"));})();"],["load","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event(\"astro:load\"));})();"],["media","(()=>{var n=(a,t)=>{let i=async()=>{await(await a())()};if(t.value){let e=matchMedia(t.value);e.matches?i():e.addEventListener(\"change\",i,{once:!0})}};(self.Astro||(self.Astro={})).media=n;window.dispatchEvent(new Event(\"astro:media\"));})();"],["only","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event(\"astro:only\"));})();"],["visible","(()=>{var a=(s,i,o)=>{let r=async()=>{await(await s())()},t=typeof i.value==\"object\"?i.value:void 0,c={rootMargin:t==null?void 0:t.rootMargin},n=new IntersectionObserver(e=>{for(let l of e)if(l.isIntersecting){n.disconnect(),r();break}},c);for(let e of o.children)n.observe(e)};(self.Astro||(self.Astro={})).visible=a;window.dispatchEvent(new Event(\"astro:visible\"));})();"]],"entryModules":{"\u0000noop-middleware":"virtual_astro_middleware.mjs","\u0000virtual:astro:server-island-manifest":"chunks/_virtual_astro_server-island-manifest_C1Q2srgE.mjs","\u0000virtual:astro:session-driver":"chunks/_virtual_astro_session-driver_Dp7RXjOf.mjs","\u0000virtual:astro:actions/noop-entrypoint":"chunks/noop-entrypoint_Z3zFhrGC.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/@astrojs+react@7.0.0_@types+node@22.20.4_@types+react-dom@19.3.0_@types+react@19.3.0__@_02aa85dceaccf3d763325676dd8540a8/node_modules/@astrojs/react/dist/vnode-children.js":"chunks/vnode-children_BSi0H4PL.mjs","@astrojs/node/server.js":"entry.mjs","\u0000virtual:astro:page:src/pages/threads/[id]@_@astro":"chunks/_id__CQzMt2xL.mjs","\u0000virtual:astro:page:src/pages/sessions/[session]/tabs/[tab]@_@astro":"chunks/_tab__D-smYrCo.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/sharp@0.35.5_@types+node@22.20.4/node_modules/sharp/dist/index.mjs":"chunks/dist_CpTFMtqT.mjs","\u0000virtual:astro:page:src/pages/index@_@astro":"chunks/index_DRfLd22k.mjs","\u0000virtual:astro:page:src/pages/new@_@astro":"chunks/new_NXgn74q9.mjs","\u0000virtual:astro:page:node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node@_@js":"chunks/node_BcIF-BVp.mjs","\u0000virtual:astro:page:src/pages/settings@_@astro":"chunks/settings_D1jcCWzt.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/services/sharp.js":"chunks/sharp_qIaxzsTp.mjs","/home/runner/work/webr/webr/src/components/App.tsx":"_astro/App.B3aX6cef.js","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/components/ClientRouter.astro?astro&type=script&index=0&lang.ts":"_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","@astrojs/react/client.js":"_astro/client.BDkzSRUN.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-fit@0.10.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-fit/lib/addon-fit.js":"_astro/addon-fit.jgKqzwky.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-web-links@0.11.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-web-links/lib/addon-web-links.js":"_astro/addon-web-links.C9lyQRxI.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+xterm@5.5.0/node_modules/@xterm/xterm/lib/xterm.js":"_astro/xterm.B-l5AIf6.js","astro:scripts/before-hydration.js":""},"inlinedScripts":[],"assets":["/apple-touch-icon.png","/brand-mark.png","/favicon-16.png","/favicon-32.png","/favicon.ico","/icon-192.png","/icon-512.png","/icon-maskable-512.png","/manifest.webmanifest","/_astro/App.B3aX6cef.js","/_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","/_astro/addon-fit.jgKqzwky.js","/_astro/addon-web-links.C9lyQRxI.js","/_astro/client.BDkzSRUN.js","/_astro/client.Bbg9OBwT.js","/_astro/client.CLSsMr2g.js","/_astro/xterm.B-l5AIf6.js","/_astro/App.1PYvyCIo.css","/_astro/jetbrains-mono.CFJaiWaY.woff2","/_astro/symbols-nerd.CEdkKAK8.woff2"],"buildFormat":"directory","checkOrigin":true,"actionBodySizeLimit":1048576,"serverIslandBodySizeLimit":1048576,"allowedDomains":[],"key":"lwLcVQyIWovoVZ6//dde/3T4ADxhy7SecaHMUhWHECI=","sessionConfig":{"driver":"unstorage/drivers/fs-lite","options":{"base":"/home/runner/work/webr/webr/node_modules/.astro/sessions"}},"image":{},"devToolbar":{"enabled":false,"debugInfoOutput":""},"logLevel":"info","shouldInjectCspMetaTags":false});
2445
+ var _manifest = deserializeManifest({"rootDir":"file:///home/runner/work/webr/webr/","cacheDir":"file:///home/runner/work/webr/webr/node_modules/.astro/","outDir":"file:///home/runner/work/webr/webr/dist/","srcDir":"file:///home/runner/work/webr/webr/src/","publicDir":"file:///home/runner/work/webr/webr/public/","buildClientDir":"file:///home/runner/work/webr/webr/dist/client/","buildServerDir":"file:///home/runner/work/webr/webr/dist/server/","adapterName":"@astrojs/node","assetsDir":"_astro","routes":[{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"type":"page","component":"_server-islands.astro","params":["name"],"segments":[[{"content":"_server-islands","dynamic":false,"spread":false}],[{"content":"name","dynamic":true,"spread":false}]],"pattern":"^\\/_server-islands\\/([^/]+?)\\/?$","prerender":false,"isIndex":false,"fallbackRoutes":[],"route":"/_server-islands/[name]","origin":"internal","distURL":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/_image","component":"node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node.js","params":[],"pathname":"/_image","pattern":"^\\/_image\\/?$","segments":[[{"content":"_image","dynamic":false,"spread":false}]],"type":"endpoint","prerender":false,"fallbackRoutes":[],"distURL":[],"isIndex":false,"origin":"internal","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/new","isIndex":false,"type":"page","pattern":"^\\/new\\/?$","segments":[[{"content":"new","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/new.astro","pathname":"/new","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/sessions/[session]/tabs/[tab]","isIndex":false,"type":"page","pattern":"^\\/sessions\\/([^/]+?)\\/tabs\\/([^/]+?)\\/?$","segments":[[{"content":"sessions","dynamic":false,"spread":false}],[{"content":"session","dynamic":true,"spread":false}],[{"content":"tabs","dynamic":false,"spread":false}],[{"content":"tab","dynamic":true,"spread":false}]],"params":["session","tab"],"component":"src/pages/sessions/[session]/tabs/[tab].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/settings","isIndex":false,"type":"page","pattern":"^\\/settings\\/?$","segments":[[{"content":"settings","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/settings.astro","pathname":"/settings","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"_astro/App.1PYvyCIo.css"}],"routeData":{"route":"/threads/[id]","isIndex":false,"type":"page","pattern":"^\\/threads\\/([^/]+?)\\/?$","segments":[[{"content":"threads","dynamic":false,"spread":false}],[{"content":"id","dynamic":true,"spread":false}]],"params":["id"],"component":"src/pages/threads/[id].astro","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/","isIndex":true,"type":"page","pattern":"^\\/$","segments":[],"params":[],"component":"src/pages/index.astro","pathname":"/","prerender":false,"fallbackRoutes":[],"distURL":[],"origin":"project","_meta":{"trailingSlash":"ignore"}}}],"serverLike":true,"middlewareMode":"classic","base":"/","trailingSlash":"ignore","compressHTML":"jsx","componentMetadata":[["/home/runner/work/webr/webr/src/pages/new.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/sessions/[session]/tabs/[tab].astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/settings.astro",{"propagation":"none","containsHead":true}],["/home/runner/work/webr/webr/src/pages/threads/[id].astro",{"propagation":"none","containsHead":true}]],"renderers":[],"clientDirectives":[["idle","(()=>{var l=(n,t)=>{let i=async()=>{await(await n())()},e=typeof t.value==\"object\"?t.value:void 0,s={timeout:e==null?void 0:e.timeout};\"requestIdleCallback\"in window?window.requestIdleCallback(i,s):setTimeout(i,s.timeout||200)};(self.Astro||(self.Astro={})).idle=l;window.dispatchEvent(new Event(\"astro:idle\"));})();"],["load","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event(\"astro:load\"));})();"],["media","(()=>{var n=(a,t)=>{let i=async()=>{await(await a())()};if(t.value){let e=matchMedia(t.value);e.matches?i():e.addEventListener(\"change\",i,{once:!0})}};(self.Astro||(self.Astro={})).media=n;window.dispatchEvent(new Event(\"astro:media\"));})();"],["only","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event(\"astro:only\"));})();"],["visible","(()=>{var a=(s,i,o)=>{let r=async()=>{await(await s())()},t=typeof i.value==\"object\"?i.value:void 0,c={rootMargin:t==null?void 0:t.rootMargin},n=new IntersectionObserver(e=>{for(let l of e)if(l.isIntersecting){n.disconnect(),r();break}},c);for(let e of o.children)n.observe(e)};(self.Astro||(self.Astro={})).visible=a;window.dispatchEvent(new Event(\"astro:visible\"));})();"]],"entryModules":{"\u0000noop-middleware":"virtual_astro_middleware.mjs","\u0000virtual:astro:server-island-manifest":"chunks/_virtual_astro_server-island-manifest_C1Q2srgE.mjs","\u0000virtual:astro:session-driver":"chunks/_virtual_astro_session-driver_Dp7RXjOf.mjs","\u0000virtual:astro:actions/noop-entrypoint":"chunks/noop-entrypoint_Z3zFhrGC.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/@astrojs+react@7.0.0_@types+node@22.20.4_@types+react-dom@19.3.0_@types+react@19.3.0__@_02aa85dceaccf3d763325676dd8540a8/node_modules/@astrojs/react/dist/vnode-children.js":"chunks/vnode-children_BSi0H4PL.mjs","@astrojs/node/server.js":"entry.mjs","\u0000virtual:astro:page:src/pages/threads/[id]@_@astro":"chunks/_id__CQzMt2xL.mjs","\u0000virtual:astro:page:src/pages/sessions/[session]/tabs/[tab]@_@astro":"chunks/_tab__D-smYrCo.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/sharp@0.35.5_@types+node@22.20.4/node_modules/sharp/dist/index.mjs":"chunks/dist_CpTFMtqT.mjs","\u0000virtual:astro:page:src/pages/index@_@astro":"chunks/index_DRfLd22k.mjs","\u0000virtual:astro:page:src/pages/new@_@astro":"chunks/new_NXgn74q9.mjs","\u0000virtual:astro:page:node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/endpoint/node@_@js":"chunks/node_BcIF-BVp.mjs","\u0000virtual:astro:page:src/pages/settings@_@astro":"chunks/settings_D1jcCWzt.mjs","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/dist/assets/services/sharp.js":"chunks/sharp_qIaxzsTp.mjs","/home/runner/work/webr/webr/src/components/App.tsx":"_astro/App.B3aX6cef.js","/home/runner/work/webr/webr/node_modules/.pnpm/astro@7.3.5_@types+node@22.20.4_tsx@4.23.15_yaml@2.9.1/node_modules/astro/components/ClientRouter.astro?astro&type=script&index=0&lang.ts":"_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","@astrojs/react/client.js":"_astro/client.BDkzSRUN.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-fit@0.10.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-fit/lib/addon-fit.js":"_astro/addon-fit.jgKqzwky.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+addon-web-links@0.11.0_@xterm+xterm@5.5.0/node_modules/@xterm/addon-web-links/lib/addon-web-links.js":"_astro/addon-web-links.C9lyQRxI.js","/home/runner/work/webr/webr/node_modules/.pnpm/@xterm+xterm@5.5.0/node_modules/@xterm/xterm/lib/xterm.js":"_astro/xterm.B-l5AIf6.js","astro:scripts/before-hydration.js":""},"inlinedScripts":[],"assets":["/apple-touch-icon.png","/brand-mark.png","/favicon-16.png","/favicon-32.png","/favicon.ico","/icon-192.png","/icon-512.png","/icon-maskable-512.png","/manifest.webmanifest","/_astro/App.B3aX6cef.js","/_astro/ClientRouter.astro_astro_type_script_index_0_lang.CTgtIFyY.js","/_astro/addon-fit.jgKqzwky.js","/_astro/addon-web-links.C9lyQRxI.js","/_astro/client.BDkzSRUN.js","/_astro/client.Bbg9OBwT.js","/_astro/client.CLSsMr2g.js","/_astro/xterm.B-l5AIf6.js","/_astro/App.1PYvyCIo.css","/_astro/jetbrains-mono.CFJaiWaY.woff2","/_astro/symbols-nerd.CEdkKAK8.woff2"],"buildFormat":"directory","checkOrigin":true,"actionBodySizeLimit":1048576,"serverIslandBodySizeLimit":1048576,"allowedDomains":[],"key":"5xt2blgxRfJ3NG9MkXy57UVS3zt1EKOycbRTt7HZqU0=","sessionConfig":{"driver":"unstorage/drivers/fs-lite","options":{"base":"/home/runner/work/webr/webr/node_modules/.astro/sessions"}},"image":{},"devToolbar":{"enabled":false,"debugInfoOutput":""},"logLevel":"info","shouldInjectCspMetaTags":false});
2446
2446
  var manifestRoutes = _manifest.routes;
2447
2447
  var manifest = Object.assign(_manifest, {
2448
2448
  renderers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pablopunk/webr",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Control your Herdr agents from anywhere.",
5
5
  "keywords": [
6
6
  "herdr",
package/server/cli.ts CHANGED
@@ -4,9 +4,11 @@ import { renderUnicode } from 'uqr';
4
4
  import { webrHome } from './home';
5
5
  import { runServer } from './run';
6
6
  import { explicitPort, knownPort } from './port';
7
- import { defaultDeps, installPlugin, pluginStatus, restartPluginWebr, uninstallWebrPlugin } from './plugin';
7
+ import { defaultDeps, installPlugin, pluginStatus, uninstallWebrPlugin, updatePlugin } from './plugin';
8
8
  import { checkHerdr } from './herdr-check';
9
+ import { modeLines, pluginMode } from './plugin/mode';
9
10
  import { openWebr, statusLines } from './where';
11
+ import { isPortOpen } from './plugin/probe';
10
12
  import { packageInfo } from './package-info';
11
13
  import { realUpdateEnvironment, runUpdate } from './update';
12
14
  import { refreshLatestVersionCache, updateHint } from './update/hint';
@@ -15,7 +17,7 @@ const HELP = `Webr — control your Herdr agents from anywhere.
15
17
 
16
18
  Usage
17
19
  webr [start] [options] Run the server in this terminal
18
- webr plugin install [options] Start Webr whenever the Herdr server starts
20
+ webr plugin install [options] Start Webr whenever the Herdr server starts, and open it
19
21
  webr plugin uninstall Remove the Herdr plugin
20
22
  webr plugin status Show whether the plugin is installed and Webr is running
21
23
  webr invite [--port <port>] Print a one-time code and QR to connect a device
@@ -26,6 +28,7 @@ Usage
26
28
  Options
27
29
  --port <port> Port to listen on (default: first free port from 4444, then remembered)
28
30
  --host <address> Listen on a specific address (default 0.0.0.0, every interface)
31
+ --no-open Don't open the browser after "plugin install"
29
32
  --origin <url> Public URL when served through a proxy, e.g. https://mac.tailnet.ts.net
30
33
  -v, --version Print the version
31
34
  -h, --help Print this help
@@ -35,7 +38,7 @@ Localhost never needs a token. Any other device asks for access and you approve
35
38
  `;
36
39
 
37
40
  const options = {
38
- port: { type: 'string' }, host: { type: 'string' }, origin: { type: 'string' },
41
+ port: { type: 'string' }, host: { type: 'string' }, origin: { type: 'string' }, 'no-open': { type: 'boolean' },
39
42
  help: { type: 'boolean', short: 'h' }, version: { type: 'boolean', short: 'v' },
40
43
  } as const;
41
44
 
@@ -79,6 +82,14 @@ async function pluginCommand(action: string, values: ReturnType<typeof parseComm
79
82
  const deps = defaultDeps();
80
83
  const lines = action === 'install' ? await installPlugin(pluginSettings(values), deps) : action === 'uninstall' ? await uninstallWebrPlugin(deps) : await pluginStatus(deps);
81
84
  for (const line of lines.filter(Boolean)) console.log(line);
85
+ if (action === 'install' && !values['no-open']) await openWhenUp(knownPort(values.port ? ['--port', values.port] : []));
86
+ }
87
+
88
+ async function openWhenUp(port: number) {
89
+ for (let attempt = 0; attempt < 30; attempt++) {
90
+ if (await isPortOpen({ host: '127.0.0.1', port })) return void (await openWebr(port)).forEach((line) => console.log(line));
91
+ await new Promise((resolve) => setTimeout(resolve, 500));
92
+ }
82
93
  }
83
94
 
84
95
  export async function main(argv: string[]) {
@@ -87,9 +98,9 @@ export async function main(argv: string[]) {
87
98
  if (values.help || command === 'help') return void console.log(HELP);
88
99
  if (values.version) { console.log(packageInfo().version); return printUpdateHint(); }
89
100
  if (command === 'start') { applyServerFlags(values); await requireHerdr(); return runServer(); }
90
- if (command === 'update') return void (await runUpdate(realUpdateEnvironment(() => restartPluginWebr(defaultDeps())))).forEach((line) => console.log(line));
101
+ if (command === 'update') return void (await runUpdate(realUpdateEnvironment(() => updatePlugin(defaultDeps())))).forEach((line) => console.log(line));
91
102
  if (command === 'refresh-update-cache') return refreshLatestVersionCache().catch(() => undefined);
92
- if (command === 'status') return void (await statusLines(knownPort(values.port ? ['--port', values.port] : []))).forEach((line) => console.log(line));
103
+ if (command === 'status') return void (await statusLines(knownPort(values.port ? ['--port', values.port] : []), modeLines(pluginMode(defaultDeps().run, defaultDeps().stateDir)))).forEach((line) => console.log(line));
93
104
  if (command === 'open') return void (await openWebr(knownPort(values.port ? ['--port', values.port] : []))).forEach((line) => console.log(line));
94
105
  if (command === 'invite') return invite(knownPort(values.port ? ['--port', values.port] : []));
95
106
  if (command === 'plugin' && ['install', 'uninstall', 'status'].includes(subcommand ?? '')) return pluginCommand(subcommand!, values);
@@ -2,7 +2,8 @@ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { CONFIG_FILE } from './files';
4
4
 
5
- export type PluginConfig = { port?: number; host?: string; origin?: string };
5
+ export type SavedLauncher = { node: string; entry: string };
6
+ export type PluginConfig = { port?: number; host?: string; origin?: string; launcher?: SavedLauncher };
6
7
 
7
8
  export function writePluginConfig(configDir: string, config: PluginConfig) {
8
9
  mkdirSync(configDir, { recursive: true });
@@ -3,25 +3,42 @@ import { join } from 'node:path';
3
3
  import { realRun } from '../command';
4
4
  import type { RunCommand } from '../command';
5
5
  import { DEV_FILE, pluginStateDir } from './files';
6
- import { installFromGithub, installedPlugin, linkLocalPlugin } from './herdr';
6
+ import { readPluginConfig } from './config';
7
+ import { startNow } from './index';
8
+ import { probeAddress } from './probe';
9
+ import { stopPluginWebr } from './stop';
10
+ import { installedPlugin, linkLocalPlugin, pluginConfigDir, replaceWithGithub } from './herdr';
7
11
 
8
12
  type Restore = () => void;
9
13
  const nothingToRestore: Restore = () => {};
10
14
 
11
15
  function restorePrevious(previous: ReturnType<typeof installedPlugin>, run: RunCommand) {
12
- if (previous?.kind === 'github') return installFromGithub(run);
16
+ if (previous?.kind === 'github') return replaceWithGithub(run);
13
17
  if (previous) return linkLocalPlugin(run, previous.root);
14
18
  }
15
19
 
16
- export function enterPluginDevMode(checkout: string, run: RunCommand = realRun, stateDir = pluginStateDir()): Restore {
20
+ function startProduction(run: RunCommand, stateDir: string) {
21
+ const plugin = installedPlugin(run);
22
+ if (plugin) startNow(plugin.root, pluginConfigDir(run), { run, stateDir });
23
+ }
24
+
25
+ export async function stopProductionWebr(run: RunCommand = realRun, stateDir = pluginStateDir()) {
26
+ if (!installedPlugin(run)) return false;
27
+ const stopped = await stopPluginWebr(stateDir, probeAddress(readPluginConfig(pluginConfigDir(run))));
28
+ if (stopped) console.log('Stopped the production Webr. It starts again when dev stops.');
29
+ return stopped;
30
+ }
31
+
32
+ export function enterPluginDevMode(checkout: string, run: RunCommand = realRun, stateDir = pluginStateDir(), restartProduction = false): Restore {
17
33
  const previous = installedPlugin(run);
18
34
  if (!previous) return nothingToRestore;
19
35
  mkdirSync(stateDir, { recursive: true });
20
- writeFileSync(join(stateDir, DEV_FILE), JSON.stringify({ checkout }));
36
+ writeFileSync(join(stateDir, DEV_FILE), JSON.stringify({ checkout, pid: process.pid }));
21
37
  linkLocalPlugin(run, join(checkout, 'plugin'));
22
38
  console.log('Webr plugin linked to this checkout. It is restored when dev stops.');
23
39
  return () => {
24
40
  rmSync(join(stateDir, DEV_FILE), { force: true });
25
41
  restorePrevious(previous, run);
42
+ if (restartProduction) try { startProduction(run, stateDir); } catch { console.error('Could not start production Webr. Run "webr plugin install".'); }
26
43
  };
27
44
  }
@@ -31,3 +31,8 @@ export const installFromGithub = (run: RunCommand) => succeeded(run('herdr', ['p
31
31
  export const uninstallPlugin = (run: RunCommand) => succeeded(run('herdr', ['plugin', 'uninstall', PLUGIN_ID]), 'uninstall the plugin');
32
32
  export const linkLocalPlugin = (run: RunCommand, directory: string) => succeeded(run('herdr', ['plugin', 'link', directory]), 'link the plugin');
33
33
  export const unlinkLocalPlugin = (run: RunCommand) => succeeded(run('herdr', ['plugin', 'unlink', PLUGIN_ID]), 'unlink the plugin');
34
+
35
+ export function replaceWithGithub(run: RunCommand) {
36
+ if (installedPlugin(run)?.kind === 'local') unlinkLocalPlugin(run);
37
+ installFromGithub(run);
38
+ }
@@ -1,28 +1,40 @@
1
- import { rmSync } from 'node:fs';
2
- import { join } from 'node:path';
1
+ import { realpathSync, rmSync } from 'node:fs';
2
+ import { basename, join } from 'node:path';
3
3
  import { realRun, type RunCommand } from '../command';
4
- import { readPluginConfig, writePluginConfig, type PluginConfig } from './config';
4
+ import { readPluginConfig, writePluginConfig, type PluginConfig, type SavedLauncher } from './config';
5
5
  import { pluginStateDir } from './files';
6
6
  import { isPortOpen, probeAddress } from './probe';
7
7
  import { stopPluginWebr } from './stop';
8
- import { installFromGithub, installedPlugin, pluginConfigDir, requireGit, requireHerdr, uninstallPlugin, unlinkLocalPlugin } from './herdr';
8
+ import { modeLines, pluginMode } from './mode';
9
+ import { DEV_FILE } from './files';
10
+ import { installedPlugin, replaceWithGithub, pluginConfigDir, requireGit, requireHerdr, uninstallPlugin, unlinkLocalPlugin } from './herdr';
9
11
 
10
12
  export type PluginDeps = {
11
13
  run: RunCommand;
12
14
  stateDir: string;
15
+ launcher?: SavedLauncher;
13
16
  };
14
17
 
15
- export const defaultDeps = (): PluginDeps => ({ run: realRun, stateDir: pluginStateDir() });
18
+ function runningLauncher(): SavedLauncher | undefined {
19
+ try {
20
+ const entry = realpathSync(process.argv[1]!);
21
+ return basename(entry) === 'webr.mjs' ? { node: process.execPath, entry } : undefined;
22
+ } catch { return undefined; }
23
+ }
24
+
25
+ export const defaultDeps = (): PluginDeps => ({ run: realRun, stateDir: pluginStateDir(), launcher: runningLauncher() });
16
26
 
17
- const startNow = (root: string, configDir: string, deps: PluginDeps) =>
27
+ export const startNow = (root: string, configDir: string, deps: PluginDeps) =>
18
28
  deps.run(process.execPath, [join(root, 'launch.mjs')], { HERDR_PLUGIN_CONFIG_DIR: configDir, HERDR_PLUGIN_STATE_DIR: deps.stateDir }).output.trim();
19
29
 
20
30
  export async function installPlugin(config: PluginConfig, deps: PluginDeps) {
21
31
  requireHerdr(deps.run);
22
32
  requireGit(deps.run);
33
+ if (pluginMode(deps.run, deps.stateDir).kind === 'dev') throw new Error('"pnpm dev" is running. Stop it with Ctrl+C first; production comes back by itself.');
23
34
  const configDir = pluginConfigDir(deps.run);
24
- writePluginConfig(configDir, config);
25
- installFromGithub(deps.run);
35
+ writePluginConfig(configDir, { ...readPluginConfig(configDir), ...config, ...(deps.launcher ? { launcher: deps.launcher } : {}) });
36
+ rmSync(join(deps.stateDir, DEV_FILE), { force: true });
37
+ replaceWithGithub(deps.run);
26
38
  const plugin = installedPlugin(deps.run);
27
39
  if (!plugin) throw new Error('Herdr did not register the Webr plugin.');
28
40
  return [
@@ -43,14 +55,15 @@ export async function uninstallWebrPlugin(deps: PluginDeps) {
43
55
  return ['Removed the Webr plugin.', ...(stopped ? ['Stopped the running Webr.'] : [])];
44
56
  }
45
57
 
46
- export async function restartPluginWebr(deps: PluginDeps) {
58
+ const quiet = (output: string) => output.split('\n').filter((line) => line.trim() && !/ExperimentalWarning|trace-warnings/.test(line));
59
+
60
+ export async function updatePlugin(deps: PluginDeps) {
47
61
  if (!deps.run('herdr', ['--version']).ok) return undefined;
48
- const plugin = installedPlugin(deps.run);
49
- if (!plugin) return undefined;
50
- const configDir = pluginConfigDir(deps.run);
51
- const address = probeAddress(readPluginConfig(configDir));
62
+ if (!installedPlugin(deps.run)) return undefined;
63
+ const address = probeAddress(readPluginConfig(pluginConfigDir(deps.run)));
52
64
  if ((await isPortOpen(address)) && !(await stopPluginWebr(deps.stateDir, address))) return [`Webr is running on port ${address.port} but the plugin did not start it. Stop it and run "webr plugin install" to use the new version.`];
53
- return [startNow(plugin.root, configDir, deps)];
65
+ const refreshed = deps.run('webr', ['plugin', 'install', '--no-open']);
66
+ return refreshed.ok ? quiet(refreshed.output) : [`The new Webr could not refresh the plugin. Run "webr plugin install". ${quiet(refreshed.output).join(' ')}`];
54
67
  }
55
68
 
56
69
  export async function pluginStatus(deps: PluginDeps) {
@@ -62,6 +75,7 @@ export async function pluginStatus(deps: PluginDeps) {
62
75
  return [
63
76
  `Installed (${plugin.kind === 'github' ? 'from GitHub' : `linked to ${plugin.root}`}).`,
64
77
  `Webr is ${await isPortOpen(address) ? 'running' : 'not running'} on port ${address.port}.`,
78
+ ...modeLines(pluginMode(deps.run, deps.stateDir)),
65
79
  `Settings: ${JSON.stringify(config)}`,
66
80
  `Logs and state: ${deps.stateDir}`,
67
81
  ];
@@ -0,0 +1,40 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import type { RunCommand } from '../command';
4
+ import { DEV_FILE } from './files';
5
+ import { installedPlugin } from './herdr';
6
+ import { isAlive } from './stop';
7
+
8
+ export type PluginMode =
9
+ | { kind: 'absent' }
10
+ | { kind: 'production' }
11
+ | { kind: 'dev'; checkout: string }
12
+ | { kind: 'stuck'; where: string };
13
+
14
+ type DevRecord = { checkout?: string; pid?: number };
15
+
16
+ function readDevRecord(stateDir: string): DevRecord | undefined {
17
+ try { return JSON.parse(readFileSync(join(stateDir, DEV_FILE), 'utf8')) as DevRecord; } catch { return undefined; }
18
+ }
19
+
20
+ const devIsRunning = (record: DevRecord) => record.pid === undefined || isAlive(record.pid);
21
+
22
+ function installedOrUndefined(run: RunCommand) {
23
+ try { return installedPlugin(run); } catch { return undefined; }
24
+ }
25
+
26
+ export function pluginMode(run: RunCommand, stateDir: string): PluginMode {
27
+ const plugin = installedOrUndefined(run);
28
+ const record = readDevRecord(stateDir);
29
+ if (record?.checkout && devIsRunning(record) && plugin?.kind === 'local') return { kind: 'dev', checkout: record.checkout };
30
+ if (record?.checkout) return { kind: 'stuck', where: record.checkout };
31
+ if (plugin?.kind === 'local') return { kind: 'stuck', where: plugin.root };
32
+ return plugin ? { kind: 'production' } : { kind: 'absent' };
33
+ }
34
+
35
+ export function modeLines(mode: PluginMode) {
36
+ if (mode.kind === 'production') return ['Mode: production (started by the Herdr plugin).'];
37
+ if (mode.kind === 'dev') return [`Mode: dev, running from ${mode.checkout}. Stopping "pnpm dev" brings production back.`];
38
+ if (mode.kind === 'stuck') return [`Mode: the plugin still points at ${mode.where} but dev is not running. Run "webr plugin install" to restore production.`];
39
+ return [];
40
+ }
@@ -18,7 +18,7 @@ function recordedPid(stateDir: string) {
18
18
  } catch { return undefined; }
19
19
  }
20
20
 
21
- function isAlive(pid: number) {
21
+ export function isAlive(pid: number) {
22
22
  try { process.kill(pid, 0); return true; } catch (error) { return (error as NodeJS.ErrnoException).code === 'EPERM'; }
23
23
  }
24
24
 
package/server/where.ts CHANGED
@@ -4,14 +4,14 @@ import { isPortOpen } from './plugin/probe';
4
4
  const localUrl = (port: number) => `http://localhost:${port}`;
5
5
  const launcher = (platform = process.platform) => platform === 'darwin' ? ['open'] : platform === 'win32' ? ['cmd', '/c', 'start', ''] : ['xdg-open'];
6
6
 
7
- export async function statusLines(port: number) {
8
- if (await isPortOpen({ host: '127.0.0.1', port })) return [`Webr is running at ${localUrl(port)}`];
9
- return [`Webr is not running. Its address is ${localUrl(port)}.`, 'It starts with the Herdr server once the plugin is installed ("webr plugin install"), or run "webr start".'];
7
+ export async function statusLines(port: number, mode: string[] = []) {
8
+ if (await isPortOpen({ host: '127.0.0.1', port })) return [`Webr is running at ${localUrl(port)}`, ...mode];
9
+ return [`Webr is not running. Its address is ${localUrl(port)}.`, ...mode, 'It starts with the Herdr server once the plugin is installed ("webr plugin install"), or run "webr start".'];
10
10
  }
11
11
 
12
12
  export async function openWebr(port: number, open = openInBrowser) {
13
13
  const lines = await statusLines(port);
14
- if (lines.length > 1) return lines;
14
+ if (lines[0]!.includes('not running')) return lines;
15
15
  open(localUrl(port));
16
16
  return [`Opening ${localUrl(port)}`];
17
17
  }