@pablopunk/webr 0.2.1 → 0.3.2

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.
@@ -1,7 +1,7 @@
1
1
  import { r as __exportAll } from "./rolldown-runtime_BMI-E3GI.mjs";
2
2
  import { S as createAstro, i as renderComponent, u as renderTemplate } from "./server_CnGYDmxV.mjs";
3
3
  import { t as createComponent } from "./compiler_BiU10Vds.mjs";
4
- import { n as $$Base, t as App } from "./App_C2vmjHiY.mjs";
4
+ import { n as $$Base, t as App } from "./App_DlKdj0Cm.mjs";
5
5
  //#region src/pages/sessions/[session]/tabs/[tab].astro
6
6
  var _tab__exports = /* @__PURE__ */ __exportAll({
7
7
  default: () => $$Tab,
@@ -1,7 +1,7 @@
1
1
  import { r as __exportAll } from "./rolldown-runtime_BMI-E3GI.mjs";
2
2
  import { S as createAstro, i as renderComponent, u as renderTemplate } from "./server_CnGYDmxV.mjs";
3
3
  import { t as createComponent } from "./compiler_BiU10Vds.mjs";
4
- import { n as $$Base, t as App } from "./App_C2vmjHiY.mjs";
4
+ import { n as $$Base, t as App } from "./App_DlKdj0Cm.mjs";
5
5
  //#region src/pages/new.astro
6
6
  var new_exports = /* @__PURE__ */ __exportAll({
7
7
  default: () => $$New,
@@ -1,7 +1,7 @@
1
1
  import { r as __exportAll } from "./rolldown-runtime_BMI-E3GI.mjs";
2
2
  import { S as createAstro, i as renderComponent, u as renderTemplate } from "./server_CnGYDmxV.mjs";
3
3
  import { t as createComponent } from "./compiler_BiU10Vds.mjs";
4
- import { n as $$Base, t as App } from "./App_C2vmjHiY.mjs";
4
+ import { n as $$Base, t as App } from "./App_DlKdj0Cm.mjs";
5
5
  //#region src/pages/settings.astro
6
6
  var settings_exports = /* @__PURE__ */ __exportAll({
7
7
  default: () => $$Settings,
@@ -2427,10 +2427,10 @@ var renderers = [Object.assign({
2427
2427
  //#endregion
2428
2428
  //#region \0virtual:astro:pages
2429
2429
  var _page0 = () => import("./chunks/node_BcIF-BVp.mjs").then((n) => n.t);
2430
- var _page1 = () => import("./chunks/new_bTXSW7C3.mjs");
2431
- var _page2 = () => import("./chunks/_tab__WmsbMlfC.mjs");
2432
- var _page3 = () => import("./chunks/settings_DwzWmJBe.mjs");
2433
- var _page4 = () => import("./chunks/_id__BimjoFCd.mjs");
2430
+ var _page1 = () => import("./chunks/new_DkzcgJw-.mjs");
2431
+ var _page2 = () => import("./chunks/_tab__C9ScWvmu.mjs");
2432
+ var _page3 = () => import("./chunks/settings_DZDiF9cU.mjs");
2433
+ var _page4 = () => import("./chunks/_id__yn5rOSmn.mjs");
2434
2434
  var _page5 = () => import("./chunks/index_DRfLd22k.mjs");
2435
2435
  var pageMap = /* @__PURE__ */ new Map([
2436
2436
  ["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", _page0],
@@ -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.1VFY1ClT.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.1VFY1ClT.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.1VFY1ClT.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.1VFY1ClT.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__BimjoFCd.mjs","\u0000virtual:astro:page:src/pages/sessions/[session]/tabs/[tab]@_@astro":"chunks/_tab__WmsbMlfC.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_bTXSW7C3.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_DwzWmJBe.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.CGyf2WDA.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.CGyf2WDA.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.1VFY1ClT.css","/_astro/jetbrains-mono.CFJaiWaY.woff2","/_astro/symbols-nerd.CEdkKAK8.woff2"],"buildFormat":"directory","checkOrigin":true,"actionBodySizeLimit":1048576,"serverIslandBodySizeLimit":1048576,"allowedDomains":[],"key":"SJv9JtyI4XxX0MOJWloaFJbCKvVL0wyh+LMju9Wv/1o=","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.96TkmFyF.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.96TkmFyF.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.96TkmFyF.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.96TkmFyF.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__yn5rOSmn.mjs","\u0000virtual:astro:page:src/pages/sessions/[session]/tabs/[tab]@_@astro":"chunks/_tab__C9ScWvmu.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_DkzcgJw-.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_DZDiF9cU.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.DcDWCCM1.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.DcDWCCM1.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.96TkmFyF.css","/_astro/jetbrains-mono.CFJaiWaY.woff2","/_astro/symbols-nerd.CEdkKAK8.woff2"],"buildFormat":"directory","checkOrigin":true,"actionBodySizeLimit":1048576,"serverIslandBodySizeLimit":1048576,"allowedDomains":[],"key":"bKc+VkF8CNFQW0g8nw7BvIEmCg8RzK+QABjVZlYdtWM=","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.2.1",
3
+ "version": "0.3.2",
4
4
  "description": "Control your Herdr agents from anywhere.",
5
5
  "keywords": [
6
6
  "herdr",
@@ -39,6 +39,7 @@
39
39
  "node": ">=22.16"
40
40
  },
41
41
  "scripts": {
42
+ "version": "node scripts/sync-plugin-version.mjs && git add plugin/herdr-plugin.toml",
42
43
  "dev": "tsx scripts/dev.ts",
43
44
  "build": "astro build",
44
45
  "start": "tsx server/start.ts",
@@ -1,5 +1,5 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { statSync, unwatchFile, watchFile, writeFileSync } from 'node:fs';
2
+ import { statSync, writeFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { distRoot } from '../src/server/dist';
5
5
 
@@ -15,12 +15,12 @@ export const recordPid = (stateDir: string | undefined, pidFile: string) => { if
15
15
  export function restartWhenRebuilt(stop: () => Promise<void>, relaunch = relaunchSelf) {
16
16
  const startedAt = builtAt();
17
17
  if (startedAt === undefined) return () => {};
18
- const entry = builtEntry();
19
- watchFile(entry, { interval: POLL_MS }, (current) => {
20
- if (current.mtimeMs === startedAt) return;
21
- unwatchFile(entry);
18
+ const timer = setInterval(() => {
19
+ const current = builtAt();
20
+ if (current === undefined || current === startedAt) return;
21
+ clearInterval(timer);
22
22
  console.log('Webr was updated on disk. Restarting to serve the new build.');
23
23
  void stop().finally(() => { relaunch(); process.exit(0); });
24
- });
25
- return () => unwatchFile(entry);
24
+ }, POLL_MS);
25
+ return () => clearInterval(timer);
26
26
  }
package/server/run.ts CHANGED
@@ -2,6 +2,8 @@ import { readFile } from 'node:fs/promises';
2
2
  import { MetadataDatabase } from '../src/server/storage/database';
3
3
  import { RuntimeManager } from '../src/server/runtime/manager';
4
4
  import { loadRegistry } from '../src/server/transport/registry';
5
+ import { discoverSavedMachines } from '../src/server/transport/saved-machines';
6
+ import { boundedProcess } from '../src/server/transport/process';
5
7
  import { HerdrTarget } from '../src/server/transport/herdr-target';
6
8
  import { createHost } from '../src/server/host';
7
9
  import { publicUrls } from '../src/server/public-urls';
@@ -38,6 +40,7 @@ export async function runServer() {
38
40
  const app = await createHost(manager, origin, ssr.handler, tls, undefined, { publicUrls: urls, tailscale: tailscale?.status }, { devHmrPort: ssr.hmrPort, transcriber: new Transcriber(modelDirectory(webrHome())) });
39
41
  await app.listen({ host, port });
40
42
  manager.start();
43
+ if (!process.env.WEBR_TARGETS) void discoverSavedMachines(process.env, boundedProcess, (profile) => manager.addTarget(new HerdrTarget(profile)));
41
44
  recordPid(process.env.HERDR_PLUGIN_STATE_DIR, PID_FILE);
42
45
  if (process.env.WEBR_DEV !== '1') restartWhenRebuilt(async () => { await Promise.all([app.close(), ssr.stop()]); database.close(); });
43
46
  console.log(`Webr listening at ${origin}`);
package/src/lib/models.ts CHANGED
@@ -50,7 +50,7 @@ export type Thread = {
50
50
  bindingConfigVersion?: number;
51
51
  semanticSignature?: string;
52
52
  archivedAt?: string;
53
- terminalHidden?: boolean;
53
+ paneOrder?: string[];
54
54
  };
55
55
 
56
56
  export const projects: Project[] = [
@@ -0,0 +1,19 @@
1
+ import type { Pane, Thread } from './models';
2
+
3
+ export const VISIBLE_PANES = 2;
4
+
5
+ const rankIn = (order: string[], id: string) => { const rank = order.indexOf(id); return rank < 0 ? Infinity : rank; };
6
+ export const orderPanes = <T extends { id: string }>(panes: T[], order: string[] = []) => [...panes].sort((a, b) => rankIn(order, a.id) - rankIn(order, b.id));
7
+ export const insertAfter = (ids: string[], afterId: string, id: string) => { const at = ids.indexOf(afterId); return at < 0 ? [...ids, id] : [...ids.slice(0, at + 1), id, ...ids.slice(at + 1)]; };
8
+ export const canClosePane = (thread: Thread, paneId: string) => thread.panes.length > 1 && thread.panes.some((pane) => pane.id === paneId && pane.kind !== 'agent');
9
+ const neighbourPane = (panes: Pane[], at: number) => panes[at - 1] ?? panes[at + 1];
10
+ export function paneToFocusAfterClosing(panes: Pane[], windowStart: number, paneId: string) {
11
+ const at = panes.findIndex((pane) => pane.id === paneId);
12
+ const otherVisible = panes.slice(windowStart, windowStart + VISIBLE_PANES).find((pane) => pane.id !== paneId);
13
+ return otherVisible ?? neighbourPane(panes, at);
14
+ }
15
+
16
+ export function windowStartShowing(count: number, start: number, index: number, size = VISIBLE_PANES) {
17
+ const shown = index < 0 ? start : index < start ? index : index >= start + size ? index - size + 1 : start;
18
+ return Math.max(0, Math.min(shown, count - size));
19
+ }
@@ -129,15 +129,15 @@ export async function createHost(manager: RuntimeManager, origin: string, ssr?:
129
129
  const input = z.object({ machineId: opaqueId, title: z.string().trim().min(1).max(90).refine((title) => !/[\x00-\x1f\x7f]/.test(title)) }).strict().parse(request.body);
130
130
  await manager.renameThread(input.machineId, id, input.title); return { title: input.title };
131
131
  });
132
- app.post('/api/threads/:id/terminal', async (request) => {
132
+ app.post('/api/threads/:id/panes/terminal', async (request) => {
133
133
  const { id } = z.object({ id: z.uuid() }).parse(request.params);
134
- const input = z.object({ machineId: opaqueId, direction: z.enum(['right', 'down']) }).strict().parse(request.body);
135
- return manager.toggleTerminal(input.machineId, id, input.direction);
134
+ const input = z.object({ machineId: opaqueId, afterPaneId: opaqueId }).strict().parse(request.body);
135
+ return manager.openTerminal(input.machineId, id, input.afterPaneId);
136
136
  });
137
- app.post('/api/threads/:id/terminal/close', async (request) => {
137
+ app.post('/api/threads/:id/panes/close', async (request) => {
138
138
  const { id } = z.object({ id: z.uuid() }).parse(request.params);
139
- const input = z.object({ machineId: opaqueId }).strict().parse(request.body);
140
- await manager.closeTerminal(input.machineId, id); return { closed: true };
139
+ const input = z.object({ machineId: opaqueId, paneId: opaqueId, force: z.boolean() }).strict().parse(request.body);
140
+ return manager.closePane(input.machineId, id, input.paneId, input.force);
141
141
  });
142
142
  app.post('/api/threads/:id/delete', async (request) => {
143
143
  const { id } = z.object({ id: z.uuid() }).parse(request.params);
@@ -56,11 +56,15 @@ export class HerdrActions {
56
56
  catch (error) { if (!(error instanceof Error) || error.message !== 'agent_pane_busy' || Date.now() >= end) throw error; await pause(500); }
57
57
  }
58
58
  }
59
+ private async shellOwnsForeground(paneId: string) {
60
+ const info = processInfo.safeParse((await this.api.request('pane.process_info', { pane_id: paneId })).process_info);
61
+ return info.success && info.data.foreground_processes.length > 0 && info.data.foreground_processes.every((process) => process.pid === info.data.shell_pid);
62
+ }
63
+ async paneBusy(paneId: string) { return !await this.shellOwnsForeground(paneId); }
59
64
  private async untilShellReady(paneId: string) {
60
65
  const end = Date.now() + SHELL_READY_TIMEOUT_MS;
61
66
  do {
62
- const info = processInfo.safeParse((await this.api.request('pane.process_info', { pane_id: paneId })).process_info);
63
- if (info.success && info.data.foreground_processes.length && info.data.foreground_processes.every((process) => process.pid === info.data.shell_pid)) return;
67
+ if (await this.shellOwnsForeground(paneId)) return;
64
68
  await pause(250);
65
69
  } while (Date.now() < end);
66
70
  throw new Error('shell_not_ready');
@@ -1,7 +1,7 @@
1
1
  import { EventEmitter } from 'node:events';
2
- import type { TargetAdapter, TerminalDirection } from './target';
2
+ import type { TargetAdapter } from './target';
3
3
  import type { Thread } from '../../lib/models';
4
- import { threadAgent, threadShell } from '../../lib/terminal-split';
4
+ import { canClosePane, insertAfter } from '../../lib/pane-strip';
5
5
  import { TargetSupervisor } from './supervisor';
6
6
  import { projectNameKey, reconcile } from './reconcile';
7
7
  import type { MetadataDatabase } from '../storage/database';
@@ -17,20 +17,25 @@ export class RuntimeManager extends EventEmitter {
17
17
  private projections = new Map<string, Projection>();
18
18
  private catalogs = new Map<string, { expires: number; value: Promise<Machine> }>();
19
19
  private configVersions = new Map<string, number>();
20
+ private started = false;
21
+ private closed = false;
20
22
  constructor(readonly database: MetadataDatabase, targets: TargetAdapter[]) {
21
23
  super(); this.setMaxListeners(100);
22
24
  this.journal = new LaunchJournal(database, () => this.publishAll());
23
- for (const target of targets) {
24
- target.configVersion = database.registerProfile(target.id, target.session, JSON.stringify({ fingerprint: target.fingerprint, locations: target.locations }), target.locations);
25
- this.configVersions.set(target.id, target.configVersion);
26
- const supervisor = new TargetSupervisor(target, () => this.publish(supervisor));
27
- if (target.enabled === false) supervisor.error = 'profile_disabled';
28
- this.supervisors.set(target.id, supervisor);
29
- this.publish(supervisor);
30
- }
25
+ for (const target of targets) this.addTarget(target);
26
+ }
27
+ addTarget(target: TargetAdapter) {
28
+ if (this.closed || this.supervisors.has(target.id)) return;
29
+ target.configVersion = this.database.registerProfile(target.id, target.session, JSON.stringify({ fingerprint: target.fingerprint, locations: target.locations }), target.locations);
30
+ this.configVersions.set(target.id, target.configVersion);
31
+ const supervisor = new TargetSupervisor(target, () => this.publish(supervisor));
32
+ if (target.enabled === false) supervisor.error = 'profile_disabled';
33
+ this.supervisors.set(target.id, supervisor);
34
+ this.publish(supervisor);
35
+ if (this.started && target.enabled !== false) void supervisor.start();
31
36
  }
32
- start() { for (const supervisor of this.supervisors.values()) if (supervisor.target.enabled !== false) void supervisor.start(); }
33
- async close() { for (const supervisor of this.supervisors.values()) supervisor.stop(); await this.journal.stop(); this.removeAllListeners(); }
37
+ start() { this.started = true; for (const supervisor of this.supervisors.values()) if (supervisor.target.enabled !== false) void supervisor.start(); }
38
+ async close() { this.closed = true; for (const supervisor of this.supervisors.values()) supervisor.stop(); await this.journal.stop(); this.removeAllListeners(); }
34
39
  bootstrap(): Bootstrap {
35
40
  const projections = [...this.projections.values()];
36
41
  return { projections, projects: projections.flatMap((projection) => projection.projects), threads: projections.flatMap((projection) => projection.threads), machines: [...this.supervisors.values()].map((supervisor) => ({ id: supervisor.target.id, name: supervisor.target.name, session: supervisor.target.session, connected: supervisor.connected, projectPaths: Object.fromEntries(supervisor.target.locations.map((location) => [location.projectId, location.path])), harnesses: [], error: supervisor.error, writable: supervisor.target.writable, configVersion: this.configVersions.get(supervisor.target.id) })) };
@@ -128,23 +133,21 @@ export class RuntimeManager extends EventEmitter {
128
133
  if (thread.bindingState === 'attached') await supervisor.readFresh();
129
134
  this.publish(supervisor);
130
135
  }
131
- async toggleTerminal(machineId: string, threadId: string, direction: TerminalDirection) {
136
+ async openTerminal(machineId: string, threadId: string, afterPaneId: string) {
132
137
  const { supervisor, thread } = this.attachedThread(machineId, threadId);
133
- const shell = threadShell(thread);
134
- const agent = threadAgent(thread);
135
- if (shell) { this.saveThreadMetadata(machineId, threadId, { terminalHidden: !thread.terminalHidden }); this.publish(supervisor); return { paneId: shell.id, hidden: !thread.terminalHidden }; }
136
- if (!agent || !supervisor.target.splitTerminal) throw new Error('thread_not_found');
137
- const paneId = await supervisor.target.splitTerminal(agent.id, direction);
138
- this.saveThreadMetadata(machineId, threadId, { terminalHidden: false });
138
+ if (!thread.panes.some((pane) => pane.id === afterPaneId) || !supervisor.target.splitTerminal) throw new Error('thread_not_found');
139
+ const paneId = await supervisor.target.splitTerminal(afterPaneId, 'right');
140
+ this.saveThreadMetadata(machineId, threadId, { paneOrder: insertAfter(thread.panes.map((pane) => pane.id), afterPaneId, paneId) });
139
141
  await supervisor.readFresh(); this.publish(supervisor);
140
- return { paneId, hidden: false };
142
+ return { paneId };
141
143
  }
142
- async closeTerminal(machineId: string, threadId: string) {
144
+ async closePane(machineId: string, threadId: string, paneId: string, force: boolean) {
143
145
  const { supervisor, thread } = this.attachedThread(machineId, threadId);
144
- const shell = threadShell(thread);
145
- if (shell) await supervisor.target.closePane?.(shell.id);
146
- this.saveThreadMetadata(machineId, threadId, { terminalHidden: false });
146
+ if (!canClosePane(thread, paneId)) return { closed: false, busy: false };
147
+ if (!force && await supervisor.target.paneBusy?.(paneId)) return { closed: false, busy: true };
148
+ await supervisor.target.closePane?.(paneId);
147
149
  await supervisor.readFresh(); this.publish(supervisor);
150
+ return { closed: true, busy: false };
148
151
  }
149
152
  private attachedThread(machineId: string, threadId: string) {
150
153
  const supervisor = this.supervisors.get(machineId);
@@ -3,6 +3,7 @@ import type { MetadataDatabase } from '../storage/database';
3
3
  import type { NativeSnapshot } from '../protocol/native';
4
4
  import type { Project, Thread } from '../../lib/models';
5
5
  import type { TargetAdapter } from './target';
6
+ import { orderPanes } from '../../lib/pane-strip';
6
7
 
7
8
  export const projectNameKey = (machineId: string, logicalId: string) => `project-name:${machineId}:${logicalId}`;
8
9
  export const projectIdentity = (machineId: string, repository: string) => createHash('sha256').update(machineId + '\0' + repository).digest('hex').slice(0, 32);
@@ -46,7 +47,7 @@ export function reconcile(database: MetadataDatabase, target: TargetAdapter, sna
46
47
  const signature = JSON.stringify([tab?.label, panes.map((pane) => [pane.terminal_id, pane.pane_id, pane.agent, readingAwareStatus(pane.agent_status), pane.title, pane.terminal_title_stripped])]);
47
48
  const meaningfulChange = attached && row.metadata.semanticSignature !== signature;
48
49
  const operation = operations.find((operation) => operation.threadId === row.id);
49
- const thread: Thread = { ...row.metadata, title: attached ? threadTitle(tab?.label, panes, row.metadata.title) : row.metadata.title, projectId: attached ? tabProjects.get(tab!.tab_id) ?? row.metadata.projectId : row.metadata.projectId, bindingState: attached ? 'attached' : operation && !row.anchors.length ? 'pending' : 'detached', status: attached ? paneStatus(panes) ?? 'unknown' : 'unknown', agent: attached ? panes.find((pane) => pane.agent)?.agent ?? '' : row.metadata.agent, semanticSignature: attached ? signature : row.metadata.semanticSignature, updatedAt: meaningfulChange ? new Date().toISOString() : row.metadata.updatedAt, panes: panes.map((pane) => ({ id: pane.pane_id, terminalId: pane.terminal_id, title: pane.title ?? pane.terminal_title_stripped ?? pane.agent ?? 'Shell', kind: pane.agent ? 'agent' : 'shell' })), operation: operation ? { id: operation.id, state: operation.state, step: operation.step } : undefined };
50
+ const thread: Thread = { ...row.metadata, title: attached ? threadTitle(tab?.label, panes, row.metadata.title) : row.metadata.title, projectId: attached ? tabProjects.get(tab!.tab_id) ?? row.metadata.projectId : row.metadata.projectId, bindingState: attached ? 'attached' : operation && !row.anchors.length ? 'pending' : 'detached', status: attached ? paneStatus(panes) ?? 'unknown' : 'unknown', agent: attached ? panes.find((pane) => pane.agent)?.agent ?? '' : row.metadata.agent, semanticSignature: attached ? signature : row.metadata.semanticSignature, updatedAt: meaningfulChange ? new Date().toISOString() : row.metadata.updatedAt, panes: orderPanes(panes.map((pane) => ({ id: pane.pane_id, terminalId: pane.terminal_id, title: pane.title ?? pane.terminal_title_stripped ?? pane.agent ?? 'Shell', kind: pane.agent ? 'agent' as const : 'shell' as const })), row.metadata.paneOrder), operation: operation ? { id: operation.id, state: operation.state, step: operation.step } : undefined };
50
51
  if (attached && (meaningfulChange || JSON.stringify(row.anchors) !== JSON.stringify(panes.map((pane) => pane.terminal_id)))) database.saveThread({ ...thread, panes: [], operation: undefined }, panes.map((pane) => pane.terminal_id), row.alias);
51
52
  return thread;
52
53
  });
@@ -34,5 +34,6 @@ export type TargetAdapter = {
34
34
  renameTab?(tabId: string, label: string, renameWorkspace: boolean): Promise<void>;
35
35
  splitTerminal?(paneId: string, direction: TerminalDirection): Promise<string>;
36
36
  closePane?(paneId: string): Promise<void>;
37
+ paneBusy?(paneId: string): Promise<boolean>;
37
38
  close(): void;
38
39
  };
@@ -149,6 +149,7 @@ export class HerdrTarget implements TargetAdapter {
149
149
  async renameTab(tabId: string, label: string, renameWorkspace: boolean) { return this.actions().renameTab(await this.snapshot(), tabId, label, renameWorkspace); }
150
150
  async splitTerminal(paneId: string, direction: TerminalDirection) { return this.actions().splitTerminal(await this.snapshot(), paneId, direction); }
151
151
  async closePane(paneId: string) { return this.actions().closePane(paneId); }
152
+ async paneBusy(paneId: string) { return this.actions().paneBusy(paneId); }
152
153
  async discardTab(tabId: string, removeWorktree: boolean) { return this.actions().discardTab(await this.snapshot(), tabId, removeWorktree); }
153
154
  private actions() {
154
155
  return new HerdrActions({ request: async (method, params, options) => {
@@ -13,7 +13,7 @@ export const profileSchema = z.object({
13
13
  }).strict().superRefine((value, context) => { if (value.transport === 'ssh' && (!value.host || !value.socket)) context.addIssue({ code: 'custom', message: 'SSH requires a host and a verified absolute socket' }); });
14
14
  export type TargetProfile = z.infer<typeof profileSchema>;
15
15
  export async function loadRegistry(path?: string): Promise<TargetProfile[]> {
16
- if (!path) return [await (await import('./local-session')).discoverLocalSession(), ...await (await import('./saved-machines')).discoverSavedMachines()];
16
+ if (!path) return [await (await import('./local-session')).discoverLocalSession()];
17
17
  const profiles = z.array(profileSchema).max(32).parse(JSON.parse(await readFile(path, 'utf8')));
18
18
  if (new Set(profiles.map((profile) => profile.id)).size !== profiles.length) throw new Error('duplicate_target');
19
19
  if (profiles.some((profile) => new Set(profile.locations.map((location) => location.projectId)).size !== profile.locations.length)) throw new Error('duplicate_project_location');
@@ -32,10 +32,10 @@ async function profileOf(machine: z.infer<typeof savedMachines>[number], env: No
32
32
  } catch { return undefined; }
33
33
  }
34
34
 
35
- export async function discoverSavedMachines(env = process.env, runProcess = boundedProcess): Promise<TargetProfile[]> {
35
+ export async function discoverSavedMachines(env = process.env, runProcess = boundedProcess, onFound: (profile: TargetProfile) => void = () => {}): Promise<TargetProfile[]> {
36
36
  try {
37
37
  const machines = savedMachines.parse(JSON.parse(await runProcess('herdr', ['machine', 'list', '--json'], env)));
38
- const profiles = await Promise.all(machines.filter((machine) => machine.enabled).map((machine) => profileOf(machine, env, runProcess)));
38
+ const profiles = await Promise.all(machines.filter((machine) => machine.enabled).map(async (machine) => { const profile = await profileOf(machine, env, runProcess); if (profile) onFound(profile); return profile; }));
39
39
  return profiles.filter((profile): profile is TargetProfile => !!profile);
40
40
  } catch { return []; }
41
41
  }
@@ -37,20 +37,16 @@ export function registerWebsockets(app: FastifyInstance, manager: RuntimeManager
37
37
  if (pair[kind]) { socket.close(1008); return; }
38
38
  pair[kind] = socket;
39
39
  if (kind === 'terminal') hub.attach(owner, socket);
40
- let pending = 0;
41
- let commands = Promise.resolve();
40
+ const reportFailure = (action: { streamId: number; generation: number }, error: unknown) => { if (socket.readyState === 1) socket.send(JSON.stringify({ type: 'stream.error', streamId: action.streamId, generation: action.generation, reason: error instanceof Error ? error.message : 'action_failed' })); };
42
41
  socket.on('message', (data, binary) => {
43
- if (binary || kind !== 'terminal' || ++pending > 16) { pair!.close(); return; }
42
+ if (binary || kind !== 'terminal') { pair!.close(); return; }
44
43
  let value: unknown;
45
44
  try { value = JSON.parse(data.toString()); } catch { pair!.close(); return; }
46
45
  const validated = terminalAction.safeParse(value);
47
46
  if (!validated.success) { pair!.close(); return; }
48
- commands = commands.then(async () => {
49
- if (!pairs.has(owner)) return;
50
- if (!pair?.metadata || !pair.terminal) { pair!.close(); return; }
51
- try { hub.action(owner, validated.data); }
52
- catch (error) { if (socket.readyState === 1) socket.send(JSON.stringify({ type: 'stream.error', streamId: validated.data.streamId, generation: validated.data.generation, reason: error instanceof Error ? error.message : 'action_failed' })); }
53
- }).catch(() => pair!.close()).finally(() => { --pending; });
47
+ if (!pairs.has(owner)) return;
48
+ if (!pair?.metadata || !pair.terminal) { pair!.close(); return; }
49
+ try { hub.action(owner, validated.data); } catch (error) { reportFailure(validated.data, error); }
54
50
  });
55
51
  socket.on('error', () => pair!.close());
56
52
  socket.on('close', () => pair!.close());