@agimon-ai/doompi-web 0.0.1-alpha.13 → 0.0.1-alpha.15

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.
Files changed (134) hide show
  1. package/dist/pwa/sw.js +1 -1
  2. package/dist/src/adapters/bundlePublication.cjs +2 -2
  3. package/dist/src/adapters/bundlePublication.cjs.map +1 -1
  4. package/dist/src/adapters/bundlePublication.mjs +2 -2
  5. package/dist/src/adapters/bundlePublication.mjs.map +1 -1
  6. package/dist/src/adapters/doomInitialization.cjs +1 -1
  7. package/dist/src/adapters/doomInitialization.cjs.map +1 -1
  8. package/dist/src/adapters/doomInitialization.mjs +1 -1
  9. package/dist/src/adapters/httpServer.cjs +2 -2
  10. package/dist/src/adapters/httpServer.cjs.map +1 -1
  11. package/dist/src/adapters/httpServer.d.cts +2 -0
  12. package/dist/src/adapters/httpServer.d.cts.map +1 -1
  13. package/dist/src/adapters/httpServer.d.mts +2 -0
  14. package/dist/src/adapters/httpServer.d.mts.map +1 -1
  15. package/dist/src/adapters/httpServer.mjs +2 -2
  16. package/dist/src/adapters/httpServer.mjs.map +1 -1
  17. package/dist/src/adapters/sessionHub.cjs +1 -1
  18. package/dist/src/adapters/sessionHub.cjs.map +1 -1
  19. package/dist/src/adapters/sessionHub.mjs +1 -1
  20. package/dist/src/adapters/sessionHub.mjs.map +1 -1
  21. package/dist/src/adapters/sessionWebComposition.cjs +2 -0
  22. package/dist/src/adapters/sessionWebComposition.cjs.map +1 -0
  23. package/dist/src/adapters/sessionWebComposition.mjs +2 -0
  24. package/dist/src/adapters/sessionWebComposition.mjs.map +1 -0
  25. package/dist/src/adapters/syncGuard.cjs +1 -1
  26. package/dist/src/adapters/syncGuard.cjs.map +1 -1
  27. package/dist/src/adapters/syncGuard.mjs +1 -1
  28. package/dist/src/adapters/syncGuard.mjs.map +1 -1
  29. package/dist/src/adapters/webBundler.cjs +3 -1
  30. package/dist/src/adapters/webBundler.cjs.map +1 -1
  31. package/dist/src/adapters/webBundler.d.cts +4 -2
  32. package/dist/src/adapters/webBundler.d.cts.map +1 -1
  33. package/dist/src/adapters/webBundler.d.mts +4 -2
  34. package/dist/src/adapters/webBundler.d.mts.map +1 -1
  35. package/dist/src/adapters/webBundler.mjs +3 -1
  36. package/dist/src/adapters/webBundler.mjs.map +1 -1
  37. package/dist/src/adapters/webPluginGenerate.cjs +2 -2
  38. package/dist/src/adapters/webPluginGenerate.cjs.map +1 -1
  39. package/dist/src/adapters/webPluginGenerate.mjs +2 -2
  40. package/dist/src/adapters/webPluginGenerate.mjs.map +1 -1
  41. package/dist/src/adapters/webPluginVite.cjs +1 -1
  42. package/dist/src/adapters/webPluginVite.cjs.map +1 -1
  43. package/dist/src/adapters/webPluginVite.mjs +1 -1
  44. package/dist/src/adapters/webPluginVite.mjs.map +1 -1
  45. package/dist/src/types/hub.cjs +1 -1
  46. package/dist/src/types/hub.cjs.map +1 -1
  47. package/dist/src/types/hub.d.cts +23 -2
  48. package/dist/src/types/hub.d.cts.map +1 -1
  49. package/dist/src/types/hub.d.mts +23 -2
  50. package/dist/src/types/hub.d.mts.map +1 -1
  51. package/dist/src/types/hub.mjs +1 -1
  52. package/dist/src/types/hub.mjs.map +1 -1
  53. package/dist/web/assets/CodeEditorView-x5dSJyH5.js +3 -0
  54. package/dist/web/assets/CodeEditorView-x5dSJyH5.js.map +1 -0
  55. package/dist/web/assets/dist-BjOHzEAd.js +24 -0
  56. package/dist/web/assets/{dist-x765ui50.js.map → dist-BjOHzEAd.js.map} +1 -1
  57. package/dist/web/assets/dist-CYkOb3JV.js +3 -0
  58. package/dist/web/assets/dist-CYkOb3JV.js.map +1 -0
  59. package/dist/web/assets/dist-CeI3DoxX.js +2 -0
  60. package/dist/web/assets/{dist-BvJltgg9.js.map → dist-CeI3DoxX.js.map} +1 -1
  61. package/dist/web/assets/dist-CkiChlyh.js +7 -0
  62. package/dist/web/assets/{dist-Dusr83oZ.js.map → dist-CkiChlyh.js.map} +1 -1
  63. package/dist/web/assets/dist-DGGMgE1d.js +2 -0
  64. package/dist/web/assets/{dist-CMcoiIPx.js.map → dist-DGGMgE1d.js.map} +1 -1
  65. package/dist/web/assets/dist-DOndDXyJ.js +4 -0
  66. package/dist/web/assets/dist-DOndDXyJ.js.map +1 -0
  67. package/dist/web/assets/dist-DmNx_rOj.js +2 -0
  68. package/dist/web/assets/{dist-1N2cDSZc.js.map → dist-DmNx_rOj.js.map} +1 -1
  69. package/dist/web/assets/dist-K0Kxol7m.js +2 -0
  70. package/dist/web/assets/{dist-Dc0vEh5H.js.map → dist-K0Kxol7m.js.map} +1 -1
  71. package/dist/web/assets/dist-gTLPm2JK.js +2 -0
  72. package/dist/web/assets/{dist-DlhyAlgc.js.map → dist-gTLPm2JK.js.map} +1 -1
  73. package/dist/web/assets/dist-gdc1AOyh.js +10 -0
  74. package/dist/web/assets/{dist-CfQNkp46.js.map → dist-gdc1AOyh.js.map} +1 -1
  75. package/dist/web/assets/dist-scMsNAmD.js +2 -0
  76. package/dist/web/assets/{dist-CSIKLWGg.js.map → dist-scMsNAmD.js.map} +1 -1
  77. package/dist/web/assets/index-6BGXtAmW.js +143 -0
  78. package/dist/web/assets/index-6BGXtAmW.js.map +1 -0
  79. package/dist/web/assets/index-DxH-he8Q.css +2 -0
  80. package/dist/web/index.html +2 -2
  81. package/package.json +12 -10
  82. package/src/adapters/bundlePublication.ts +86 -0
  83. package/src/adapters/httpServer.ts +183 -106
  84. package/src/adapters/sessionHub.ts +297 -66
  85. package/src/adapters/sessionWebComposition.ts +59 -0
  86. package/src/adapters/syncGuard.ts +49 -33
  87. package/src/adapters/webBundler.ts +48 -5
  88. package/src/adapters/webPluginGenerate.ts +20 -6
  89. package/src/adapters/webPluginVite.ts +45 -0
  90. package/src/pwa/bundleCache.ts +69 -0
  91. package/src/pwa/serviceWorker.ts +220 -5
  92. package/src/pwa/workerClient.ts +13 -0
  93. package/src/types/hub.ts +23 -1
  94. package/src/web/app/Providers.tsx +4 -7
  95. package/src/web/app/sessionRuntime.ts +69 -3
  96. package/src/web/features/activity/ContextPanel.tsx +3 -0
  97. package/src/web/features/session/Composer.tsx +23 -22
  98. package/src/web/features/session/Timeline.tsx +175 -50
  99. package/src/web/features/session/ToolCard.tsx +2 -0
  100. package/src/web/features/sessions/SessionRail.tsx +53 -18
  101. package/src/web/lib/commands.ts +5 -0
  102. package/src/web/lib/composition.ts +51 -10
  103. package/src/web/lib/pluginRegistry.ts +288 -144
  104. package/src/web/lib/pluginRuntime.ts +251 -0
  105. package/src/web/lib/pluginSlotProps.ts +31 -2
  106. package/src/web/lib/promptFocus.ts +2 -1
  107. package/src/web/lib/sessionModel.ts +13 -2
  108. package/src/web/routes/CockpitPage.tsx +14 -0
  109. package/src/web/routes/SettingsPage.tsx +2 -0
  110. package/src/web/stores/composerStore.ts +82 -1
  111. package/src/web/stores/sessionStore.ts +11 -3
  112. package/src/web/stores/sessionsStore.ts +15 -1
  113. package/src/web/stores/usePluginSlotProps.ts +2 -1
  114. package/src/web/stores/useWebPluginRegistry.ts +7 -0
  115. package/dist/src/adapters/webComposition.cjs +0 -3
  116. package/dist/src/adapters/webComposition.cjs.map +0 -1
  117. package/dist/src/adapters/webComposition.mjs +0 -3
  118. package/dist/src/adapters/webComposition.mjs.map +0 -1
  119. package/dist/web/assets/CodeEditorView-Cv2_Pqe1.js +0 -15
  120. package/dist/web/assets/CodeEditorView-Cv2_Pqe1.js.map +0 -1
  121. package/dist/web/assets/dist-1N2cDSZc.js +0 -2
  122. package/dist/web/assets/dist-BvJltgg9.js +0 -2
  123. package/dist/web/assets/dist-CMcoiIPx.js +0 -2
  124. package/dist/web/assets/dist-CSIKLWGg.js +0 -2
  125. package/dist/web/assets/dist-CVA2128k.js +0 -1
  126. package/dist/web/assets/dist-CfQNkp46.js +0 -10
  127. package/dist/web/assets/dist-Dc0vEh5H.js +0 -2
  128. package/dist/web/assets/dist-DlhyAlgc.js +0 -2
  129. package/dist/web/assets/dist-Dusr83oZ.js +0 -7
  130. package/dist/web/assets/dist-x765ui50.js +0 -24
  131. package/dist/web/assets/index-CdO0WgVA.js +0 -101
  132. package/dist/web/assets/index-CdO0WgVA.js.map +0 -1
  133. package/dist/web/assets/index-_BcdH66j.css +0 -2
  134. package/src/web/features/remote/RemoteBanner.tsx +0 -50
@@ -1,3 +1,3 @@
1
- import{createPiHubService as e}from"./piHubService.mjs";import{createSyncGuard as t}from"./syncGuard.mjs";import{createPiWebSocketListener as n}from"./piWebSocketListener.mjs";import{MAX_SESSION_FILE_BYTES as r,SESSION_FILE_ROUTE as i}from"../types/media.mjs";import{sessionFileHeaders as a}from"../services/fileMedia.mjs";import{contentTypeFor as o,resolveAssetPath as s}from"../services/staticAssets.mjs";import{API_SESSION_QUERY_PARAM as c,DIRECTORIES_API_ROUTE as l,HUB_RESYNCED_TYPE as u,SESSIONS_API_ROUTE as d,SESSIONS_SNAPSHOT_TYPE as f,SESSION_REMOVED_TYPE as ee,SESSION_UPSERT_TYPE as te,hubHello as ne,sessionFrameEnvelope as re,threadBacklog as p,threadFrameEnvelope as ie}from"../types/hub.mjs";import{parseDoomNotificationEntry as m}from"../types/notification.mjs";import"../types/session.mjs";import{readGitStatus as h}from"./gitStatus.mjs";import{readRegistryRecords as ae,watchRegistry as g}from"./registryWatcher.mjs";import{createServerSpawner as _}from"./serverSpawner.mjs";import{proxyToSocket as v}from"./packageApiProxy.mjs";import{createSessionHub as y}from"./sessionHub.mjs";import{DEVICE_COOKIE as b,PAIRING_PAGE_ROUTE as oe,PROTOCOL_SOCKET_ROUTE as se,REMOTE_CHANNEL_ROUTE as x,REMOTE_HTTP_ROUTE as S,REMOTE_PUSH_KEY_ROUTE as ce,REMOTE_PUSH_ROUTE as le,SESSION_SOCKET_ROUTE as ue}from"../types/remoteAccess.mjs";import{allowedOriginsFromEnv as de}from"../services/remoteGuardPolicy.mjs";import{describeStranded as fe,planSessionMigration as pe}from"../services/sessionMigration.mjs";import{registerAuthRoutes as me}from"./authRoutes.mjs";import{registerSettingsRoutes as he}from"./settingsRoutes.mjs";import{createProviderAuth as ge}from"./providerAuth.mjs";import{createRemoteGuard as _e}from"./remoteGuard.mjs";import{createRemoteAccess as ve}from"./remoteAccess.mjs";import{createBundlePublication as ye}from"./bundlePublication.mjs";import{createLiveWebPush as be}from"./webPush.mjs";import{createRemoteAccessStore as xe}from"./remoteAccessStore.mjs";import{registerRemoteRoutes as Se}from"./remoteRoutes.mjs";import{createTunnelLauncher as Ce,reapStaleTunnel as we}from"./tunnelProcess.mjs";import{suggestDirectories as Te}from"./directoryListing.mjs";import{listSessionFiles as Ee,readSessionFile as De}from"./sessionFiles.mjs";import{createThreadJournals as Oe}from"./threadJournals.mjs";import{loadHubChannels as ke}from"./webHubPluginLoader.mjs";import{BROWSER_ERROR_EVENT as Ae,WEB_TELEMETRY_ROUTE as je,createBrowserError as Me,createBrowserTelemetryRateLimit as Ne,createWebTelemetry as Pe,forwardedTraceContext as C,isBrowserErrorEvent as Fe,parseBrowserTelemetryBatch as Ie,readTraceContext as w,requestOperation as Le,shutdownWebTelemetry as Re}from"./webTelemetry.mjs";import{resolveWebComposition as ze}from"./webComposition.mjs";import T from"node:fs";import E from"node:path";import{fileURLToPath as D}from"node:url";import{readSyncDrift as Be,readSyncRegistration as Ve}from"@agimon-ai/doompi/services";import{createHash as O,randomUUID as He}from"node:crypto";import{serve as Ue}from"@hono/node-server";import{createNodeWebSocket as We}from"@hono/node-ws";import{SessionManager as Ge}from"@earendil-works/pi-coding-agent";import{PiServer as Ke,PiServerError as qe}from"@earendil-works/pi-server";import{globalDoomConfigDirectory as Je}from"@agimon-ai/doompi-config";import{readSyncState as Ye}from"@agimon-ai/doompi/services/syncState";import{BUNDLE_MANIFEST_ROUTE as Xe,assetFor as Ze}from"@agimon-ai/doompi-web-security";import{Hono as Qe}from"hono";import{getCookie as k}from"hono/cookie";import{bodyLimit as A}from"hono/body-limit";import{DOOM_API_ROUTE_PREFIX as j}from"@agimon-ai/doompi-extension-contracts/package-api";import{PACKAGE_API_DIR_ENV as $e,loadPackageApis as et}from"@agimon-ai/doompi-extension-contracts/package-api-loader";import{insideSandbox as tt}from"@agimon-ai/doompi-extension-contracts/sandbox-harness";import{findRepositoryRoot as nt}from"@agimon-ai/doompi/utils/repository";const M=`DOOMPI_WEB_PACKAGE_ROOT`,rt=8388608,N=new Set([`GET`,`HEAD`,`POST`,`PUT`,`PATCH`,`DELETE`]),P=new Set([`connection`,`content-length`,`cookie`,`forwarded`,`host`,`origin`,`transfer-encoding`,`upgrade`,`x-forwarded-for`,`x-forwarded-host`,`x-forwarded-proto`]);function it(e){if(typeof e!=`object`||!e)return;let t=e;if(t.v!==1||typeof t.method!=`string`||!N.has(t.method)||typeof t.target!=`string`||!t.target.startsWith(`/`)||t.target.startsWith(`//`)||t.target.includes(`#`)||!Array.isArray(t.headers)||!t.headers.every(e=>Array.isArray(e)&&e.length===2&&e.every(e=>typeof e==`string`))||t.body!==void 0&&typeof t.body!=`string`)return;let n=new URL(t.target,`http://sealed.doompi.invalid`);if(n.pathname!==S&&n.pathname!==x)return t}function at(e,t){try{let n=new Headers;for(let[t,r]of e.headers){let e=t.toLowerCase();P.has(e)||e.startsWith(`sec-`)||n.append(t,r)}for(let e of[`cookie`,`host`,`origin`]){let r=t.req.header(e);r!==void 0&&n.set(e,r)}return n}catch{return}}function ot(e){if(e===void 0)return Buffer.alloc(0);if(!/^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/.test(e))return;let t=Buffer.from(e,`base64`);return t.toString(`base64`)===e?t:void 0}function F(e){let t=process.env[M];if(t!==void 0&&t!==``)return E.join(t,`dist`,e);let n=E.dirname(D(import.meta.url));for(;;){if(T.existsSync(E.join(n,`package.json`)))return E.join(n,`dist`,e);let t=E.dirname(n);if(t===n)throw Error(`doompi-web could not locate its own package root.`);n=t}}function I(){return F(`web`)}function st(){return F(`pwa`)}function ct(){let e=process.env[M],t=e!==void 0&&e!==``?e:E.dirname(D(import.meta.url));for(;;){let e=E.join(t,`package.json`);if(T.existsSync(e))try{let t=JSON.parse(T.readFileSync(e,`utf8`)),n=R(t)?t.version:void 0;return typeof n==`string`?n:`unknown`}catch{return`unknown`}let n=E.dirname(t);if(n===t)return`unknown`;t=n}}function lt(e,t,n){if(e!==void 0)return e;let r=process.env.DOOMPI_WEB_DIST;if(r!==void 0&&r!==``)return r;let i=t?.webDirectory;return i!=null&&T.existsSync(E.join(i,`index.html`))?(n(`serving the synced cockpit bundle from ${i}`),i):I()}function L(e){try{return T.statSync(e).isFile()?T.readFileSync(e):void 0}catch{return}}function R(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function ut(e,t){let n=e.firstMessage.split(`
2
- `,1)[0]?.trim();return(e.name?.trim()||n||E.basename(t)||`untitled`).slice(0,80)}function z(e){return e instanceof Error?e.message:String(e)}function dt(e,t){return`${e}\n${t}`}function ft(e,t,n,r){return y({source:g(e.registryDir,t),spawner:_({registryDir:e.registryDir,command:e.spawnCommand,onNotice:t}),readGit:h,channels:n,telemetry:r,onNotice:t})}function B(e){return`repo-${O(`sha256`).update(e).digest(`base64url`).slice(0,24)}`}function pt(e){let t=new Map,n=()=>{for(let[e,n]of t)t.set(e,{...n,repository:{...n.repository,active:!1}});for(let n of e.records())try{let e=T.realpathSync(nt(n.cwd));t.set(e,{repository:{id:B(e),path:e,name:E.basename(e)||e,active:!0},seenAt:Date.now()})}catch{}let n=[...t.entries()].filter(([,e])=>!e.repository.active).sort((e,t)=>t[1].seenAt-e[1].seenAt);for(let[e]of n.slice(12))t.delete(e);return[...t.values()].sort((e,t)=>Number(t.repository.active)-Number(e.repository.active)||t.seenAt-e.seenAt||e.repository.path.localeCompare(t.repository.path)).map(e=>e.repository)};return{list:n,resolve(e){return n().find(t=>t.id===e)?.path}}}function mt(e,t,n,r,i){let a=[];for(let o of t){let t=`${j}/${o.basePath}`,s;try{s=o.start({scope:`hub`,onNotice:n,resolveRepository:r,readRepositorySync:i})}catch(e){n(`hub API '${o.basePath}' did not start (${z(e)}); its routes stay unmounted`);continue}a.push(s),e.all(`${t}/*`,async e=>{if(e.req.query(`session`)!==void 0)return e.notFound();let r=new URL(e.req.url);r.pathname=r.pathname.slice(t.length)||`/`;try{return await s.fetch(new Request(r,e.req.raw))}catch(t){return n(`hub API '${o.basePath}' failed on ${r.pathname} (${z(t)})`),e.json({error:`The '${o.basePath}' API failed.`},500)}})}return a}function ht(e,t,n,r){e.all(`${j}/*`,async e=>{let i=e.req.query(c);if(i===void 0)return e.notFound();let a=t.snapshot().find(e=>e.id===i);if(a===void 0)return e.json({error:`No session ${i}.`},404);if(a.apiSocketPath===void 0)return e.json({error:`Session ${i} serves no package API.`},404);let o=new URL(e.req.url);o.searchParams.delete(c);let s=w(e.req.raw.headers);try{return await r.runInSpan(`web.package_proxy`,{"operation.name":`web.package_proxy`,"http.method":e.req.method},async t=>await v({socketPath:a.apiSocketPath,path:`${o.pathname}${o.search}`,method:e.req.method,headers:e.req.raw.headers,body:e.req.raw.body,trace:C(t,s)}),s)}catch(t){return n(`session ${i} API is unreachable (${z(t)})`),e.json({error:`Session ${i} is not answering.`},502)}})}async function V(c){let h=c.host??`127.0.0.1`,g=c.onNotice??(()=>{}),_=Pe(),v=xe({stateDir:c.remoteStateDir,onNotice:g}),y=e=>{try{return T.realpathSync(nt(e))}catch{return}},x=e=>{let t=y(e);if(t!==void 0)return t;let n=Je();try{return Ve(n)?.root}catch(e){g(`global cockpit composition is unreadable (${z(e)})`);return}},C=c.compositionDir===void 0?void 0:y(c.compositionDir);if(c.compositionDir!==void 0&&C===void 0)throw Error(`Cockpit composition directory is not inside a repository: ${c.compositionDir}`);let D=ae(c.registryDir,g).map(e=>x(e.cwd)).filter(e=>e!==void 0),O=[...new Set(C===void 0?D:[C])].sort((e,t)=>e.localeCompare(t)),j=new Map;for(let e of O)j.set(e,t({repoRoot:e,onNotice:g}));await Promise.all([...j.values()].map(async e=>await e.ensureSynced()));let M=async()=>await ze({repositoryRoots:O,stateDirectory:v.directory,onNotice:g}),N;try{N=await M()}catch(e){g(`the cockpit composition is unavailable (${z(e)}); serving the packaged bundle`),N=void 0}let P={assetsDir:lt(c.assetsDir,N,g),apiDirectory:N?.apiDirectory},F=async e=>{let n=x(e);if(n===void 0)return;let r=j.get(n);if(r!==void 0){await r.ensureSynced();return}let i=t({repoRoot:n,onNotice:g});try{await i.ensureSynced()}finally{i.close()}},I=ft(c,g,await ke(P.assetsDir,g),_),B=Oe({resolve:(e,t)=>I.threadJournal(e,t),onNotice:g}),V=new Set,H=(e,t)=>{for(let n of V)t&&!n.local||n.post(e)},U=new Qe,W=We({app:U}),gt,G=st(),K=ye({assetsDir:()=>P.assetsDir,stateDir:v.directory,onNotice:g}),q;we(v.directory,g);let _t=c.onHandover===void 0?void 0:e=>{let t=pe(I.snapshot().map(e=>({id:e.id,cwd:e.cwd,...e.name===void 0?{}:{name:e.name}})),e.sandbox.workspaces);for(let e of fe(t.stranded))g(e);for(let e of t.migrate){let t=I.stop(e.id);t.ok||g(`could not stop ${e.name??e.id} before the move: ${t.error}`)}c.onHandover?.({settings:e,sessions:t.migrate})},J=ve({store:v,launchTunnel:c.remoteAccess?.launchTunnel??Ce({stateDir:v.directory,...c.cloudflaredPath===void 0?{}:{cloudflaredPath:c.cloudflaredPath},onNotice:g,onExit:e=>{g(`remote access: ${e}`),J.disable()}}),bindListener:async()=>await new Promise((e,t)=>{let n=Ue({fetch:U.fetch,port:0,hostname:`127.0.0.1`,serverOptions:{maxHeaderSize:16384,headersTimeout:5e3,requestTimeout:1e4}},t=>{W.injectWebSocket(n),e({port:t.port,close:async()=>await new Promise(e=>{n.closeAllConnections?.(),n.close(()=>e())})})}),r=n;r.maxConnections=64,r.maxRequestsPerSocket=100,n.once(`error`,t)}),bundleTrust:()=>K.trust(),onDeviceDropped:e=>q?.remove(e),onNotice:g,..._t===void 0?{}:{requestHandover:_t},contained:tt(process.env),broadcastLocal:e=>H(e,!0),broadcastAll:e=>H(e,!1),...c.remoteAccess?.now===void 0?{}:{now:c.remoteAccess.now}});q=be({stateDir:v.directory,isConnected:e=>J.isDeviceConnected(e),onNotice:g});let vt=I.onEvent(e=>{e.kind===`frame`&&m(e.frame)!==void 0&&q?.notify()}),Y=_e({loopbackPort:()=>gt,tunnelPolicy:()=>J.tunnelPolicy(),authorize:e=>J.authorize(k(e,b,`host`)),trustedDevice:e=>e.env?.sealedDeviceId,channelReady:(e,t)=>J.channelFor(e,t)!==void 0,stepUp:{required:e=>J.stepUpRequired(e),verify:async(e,t,n)=>{if(typeof n!=`object`||!n)return!1;let r=n.ceremonyId,i=n.response;if(typeof r!=`string`)return!1;let a=e.env?.sealedDeviceId??J.authorize(k(e,`doompi_device`,`host`)),o=a===void 0?`local`:`device:${a}`;return await J.passkeys().finishStepUp(r,o,t,i)}},extraOrigins:de(process.env.DOOMPI_WEB_ALLOW_ORIGIN)});U.use(`*`,Y.middleware),U.use(`*`,async(e,t)=>{let n=new URL(e.req.url).pathname;if(n===S)return t();let r=Le(n);if(r===void 0)return t();let i=performance.now();return await _.runInSpan(r,{"operation.name":r,"http.method":e.req.method},async()=>{await t(),await _.recordEvent(`web.api.request`,{"operation.name":r,"http.method":e.req.method,"http.status_code":e.res.status,duration_ms:Math.max(0,Math.round(performance.now()-i))})},w(e.req.raw.headers))}),U.use(`*`,async(e,t)=>{if(Y.listenerOf(e)===`local`)return t();let n=Number(e.req.header(`content-length`));return Number.isFinite(n)&&n>rt?e.json({error:`The tunnel request body is too large.`},413):t()}),Se(U,{remote:J,listenerOf:e=>Y.listenerOf(e)});let yt=A({maxSize:rt,onError:e=>e.json({error:`The tunnel request body is too large.`},413)});U.use(`*`,async(e,t)=>Y.listenerOf(e)===`local`?t():await yt(e,t));let bt=A({maxSize:32768,onError:e=>e.json({error:`The telemetry batch is too large.`},413)}),xt=Ne();U.post(je,bt,async e=>{if(!xt())return e.json({error:`Too many telemetry batches.`},429);let t;try{t=await e.req.json()}catch{return e.json({error:`The telemetry batch must be JSON.`},400)}let n=Ie(t);if(n===void 0)return e.json({error:`The telemetry batch is invalid.`},400);for(let e of n){if(Fe(e)){await _.recordError(Ae,Me(e),{"browser.source":e.source,...e.session_id===void 0?{}:{"session.id":e.session_id},"failure.kind":`browser`},{includeException:!0});continue}await _.recordEvent(e.name,{...e.duration_ms===void 0?{}:{duration_ms:e.duration_ms},...e.count===void 0?{}:{count:e.count}})}return e.body(null,204)}),U.post(S,async e=>{let t=J.authorize(k(e,b,`host`));if(t===void 0)return e.json({error:`This device is not paired.`},401);let n=J.channelFor(t,`http`);if(n===void 0)return e.json({error:`This device has no sealed HTTP channel.`},401);let r;try{r=await e.req.json()}catch{return e.json({error:`The sealed request envelope was malformed.`},400)}let i=n.open(r);if(!i.ok)return e.json({error:`The sealed request was refused: ${i.failure}.`},400);let a;try{a=JSON.parse(new TextDecoder().decode(i.plaintext))}catch{return e.json({error:`The sealed HTTP request was malformed.`},400)}let o=it(a),s=o===void 0?void 0:at(o,e),c=o===void 0?void 0:ot(o.body);if(o===void 0||s===void 0||c===void 0)return e.json({error:`The sealed HTTP request was invalid.`},400);let l=new URL(o.target,new URL(e.req.url).origin),u=await U.request(l,{method:o.method,headers:s,...o.method===`GET`||o.method===`HEAD`?{}:{body:Uint8Array.from(c).buffer}},{...e.env,sealedDeviceId:t}),d=Buffer.from(await u.arrayBuffer()),f=n.seal(new TextEncoder().encode(JSON.stringify({v:1,status:u.status,headers:Array.from(u.headers.entries()),body:d.toString(`base64`)})));return f.ok?e.json(f.envelope):e.json({error:`The sealed response failed: ${f.failure}.`},503)});let St=A({maxSize:8192,onError:e=>e.json({error:`The Push subscription is too large.`},413)}),X=e=>e.env?.sealedDeviceId;U.get(ce,e=>X(e)===void 0?e.json({error:`A sealed paired device is required.`},401):e.json({publicKey:q?.publicKey()},200,{"Cache-Control":`no-store`})),U.post(le,St,async e=>{let t=X(e);if(t===void 0)return e.json({error:`A sealed paired device is required.`},401);let n;try{n=await e.req.json()}catch{return e.json({error:`The Push subscription must be JSON.`},400)}return q?.subscribe(t,n)===!0?e.body(null,204):e.json({error:`The Push subscription is invalid.`},400)}),U.delete(le,e=>{let t=X(e);return t===void 0?e.json({error:`A sealed paired device is required.`},401):(q?.remove(t),e.body(null,204))});let Ct=n({onError:e=>g(`protocol: ${e.message}`)}),wt=n({onError:e=>g(`protocol: ${e.message}`)}),Tt=e({records:()=>I.records(),spawn:e=>I.create(e),onNotice:g}),Et={...Tt,createSession:async()=>{throw new qe(`not_implemented`,`Remote protocol clients cannot create sessions`)}},Dt=new Ke(Tt,{listeners:[Ct],onError:e=>g(`protocol: ${e.message}`)}),Z=new Ke(Et,{listeners:[wt],onError:e=>g(`protocol: ${e.message}`)});await Promise.all([Dt.start(),Z.start()]);let Q=ge({runtime:c.authRuntime,onNotice:g});me(U,Q);let Ot=pt(I),kt=()=>Ot.list(),At=e=>Ot.resolve(e),jt=e=>{let t=At(e);if(t===void 0)return;let n=Be({repoRoot:t}),r;try{r=Ve(t)?Ye(t):void 0}catch{r=void 0}return{fresh:n.fresh,reasons:[...n.reasons],...r===void 0?{}:{mcpProjection:r.fileState.mcpProjection}}};he(U,{repositories:kt,models:()=>Q.listModels()});let Mt=P.apiDirectory,$=mt(U,Mt===void 0&&!process.env[$e]?[]:await et(`hub`,{apiDirectory:Mt,onNotice:g}),g,At,jt);ht(U,I,g,_),U.get(`/api/health`,e=>e.json({ok:!0,role:`hub`,protocol:1,sessions:I.snapshot().length,pid:process.pid,version:ct()})),U.post(d,async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:`The request body must be JSON.`},400)}if(!R(t)||typeof t.cwd!=`string`||t.cwd===``)return e.json({error:`A cwd string is required.`},400);let n=typeof t.name==`string`&&t.name!==``?t.name:void 0;await F(t.cwd);let r=await I.create({cwd:t.cwd,name:n,trace:w(e.req.raw.headers)});return r.ok?e.json({sessionId:r.sessionId},201):e.json({error:r.error},r.code===`invalid_request`?400:502)}),U.post(`${d}/:sessionId/restart`,async e=>{let t=e.req.param(`sessionId`),n=I.snapshot().find(e=>e.id===t);n!==void 0&&await F(n.cwd);let r=await I.restart(t,w(e.req.raw.headers));return r.ok?e.json({sessionId:r.sessionId},202):e.json({error:r.error},r.code===`invalid_request`?404:502)}),U.get(`${d}/:sessionId/history`,async e=>{let t=e.req.param(`sessionId`),n=I.snapshot().find(e=>e.id===t);if(!n)return e.json({error:`Unknown session.`},404);let r=await Ge.list(n.cwd);return e.json({sessions:r.map(e=>({id:e.id,...e.name===void 0?{}:{name:e.name},firstMessage:e.firstMessage,createdAt:e.created.toISOString(),updatedAt:e.modified.toISOString(),messageCount:e.messageCount}))})}),U.post(`${d}/:sessionId/resume`,async e=>{let t=e.req.param(`sessionId`),n=I.snapshot().find(e=>e.id===t);if(!n)return e.json({error:`Unknown session.`},404);let r;try{r=await e.req.json()}catch{return e.json({error:`The request body must be JSON.`},400)}if(!R(r)||typeof r.targetSessionId!=`string`||r.targetSessionId===``)return e.json({error:`A targetSessionId string is required.`},400);let i=(await Ge.list(n.cwd)).find(e=>e.id===r.targetSessionId);if(!i)return e.json({error:`That Pi thread does not belong to this workspace.`},404);await F(n.cwd);let a=await I.resume(t,{sessionId:i.id,name:ut(i,n.cwd)},w(e.req.raw.headers));return a.ok?e.json({sessionId:a.sessionId},202):e.json({error:a.error},a.code===`invalid_request`?409:502)}),U.delete(`${d}/:sessionId`,e=>{let t=e.req.param(`sessionId`),n=I.stop(t);return n.ok?e.json({sessionId:t},202):e.json({error:n.error},n.code===`unknown`?404:n.code===`self`?409:502)}),U.get(`/api/sessions/:sessionId/files`,async e=>{let t=e.req.param(`sessionId`),n=I.snapshot().find(e=>e.id===t);if(!n)return e.json({error:`Unknown session.`},404);let r=e.req.query(`q`)??``,i=await Ee(n.cwd,r,20);return e.json({files:i})});let Nt=e=>{if(!(Y.listenerOf(e)===`local`||tt(process.env)))return c.browseRoot??process.cwd()};U.get(l,async e=>{let t=Nt(e),n=await Te(e.req.query(`q`)??``,{limit:12,...t===void 0?{}:{root:t}});return e.json({directories:n})}),U.get(i,async e=>{let t=e.req.param(`sessionId`),n=I.snapshot().find(e=>e.id===t);if(!n)return e.json({error:`Unknown session.`},404);let i=e.req.query(`path`)??``,o=await De(n.cwd,i,r);return o.status===`forbidden`?e.json({error:`The path leaves the session directory.`},403):o.status===`not-found`?e.json({error:`No such file.`},404):o.status===`too-large`?e.json({error:`The file is too large to preview.`},413):e.body(new Uint8Array(o.body),200,a(i))}),U.get(ue,W.upgradeWebSocket(e=>{let t=Y.listenerOf(e)===`local`,n=t?void 0:J.authorize(k(e,b,`host`)),r=n===void 0?void 0:J.channelFor(n,`session`),i=new Set,a=He(),o=new Map,s,c,l,u,d=e=>{for(let[t,n]of o)(e===void 0||n.sessionId===e)&&(o.delete(t),B.unsubscribe(n.sessionId,n.threadId))};return{onOpen(e,p){if(!t&&r===void 0){p.close(1008,`sealed session channel required`);return}let h=e=>{let n=JSON.stringify(e),i=n;if(!t){if(r===void 0)return;let e=r.seal(new TextEncoder().encode(n));if(!e.ok){p.close(1008,`sealed session channel failed`);return}i=JSON.stringify(e.envelope)}try{p.send(i)}catch{}};l={post:h,local:t},V.add(l),n!==void 0&&(u=J.trackSocket(n,(e,t)=>p.close(e,t))),h(ne(I.channelTypes())),h({type:f,sessions:I.snapshot()}),s=I.onEvent(e=>{e.kind===`upsert`?h({type:te,session:e.session}):e.kind===`removed`?(i.delete(e.sessionId),d(e.sessionId),h({type:ee,sessionId:e.sessionId})):e.kind===`channel`?(e.connectionId===a||e.connectionId===void 0&&i.has(e.sessionId))&&h({type:e.frameType,sessionId:e.sessionId,payload:e.payload}):(m(e.frame)!==void 0||i.has(e.sessionId))&&h(re(e.sessionId,e.frame))}),c=B.onFrame(e=>{o.has(dt(e.sessionId,e.threadId))&&h(ie(e.sessionId,e.threadId,e.frame))}),g(`browser attached`)},onMessage(e){if(typeof e.data!=`string`)return;let n;try{n=JSON.parse(e.data)}catch{return}if(!t){if(r===void 0)return;let e=r.open(n);if(!e.ok)return;try{n=JSON.parse(new TextDecoder().decode(e.plaintext))}catch{return}}if(!R(n)||typeof n.sessionId!=`string`)return;let s=n.sessionId;if(typeof n.type==`string`&&I.channelTypes().includes(n.type)&&(i.has(s)||I.channelReceivesWithoutSubscription(n.type))){I.receiveChannel(s,n.type,n.payload,a);return}if(n.type===`subscribe`){let e=I.backlog(s);if(!e)return;i.add(s),l?.post(e);for(let e of I.channelFrames(s))l?.post(e);return}if(n.type===`unsubscribe`){i.delete(s);return}if(n.type===`history_request`){let e=I.history(s,{...typeof n.before==`string`?{before:n.before}:{},...typeof n.limit==`number`?{limit:n.limit}:{}});if(!e)return;l?.post(e);return}if(n.type===`subscribe_thread`||n.type===`unsubscribe_thread`){if(typeof n.threadId!=`string`)return;let e=n.threadId,t=dt(s,e);if(n.type===`unsubscribe_thread`){o.delete(t)&&B.unsubscribe(s,e);return}if(o.has(t)||I.backlog(s)===void 0)return;o.set(t,{sessionId:s,threadId:e}),l?.post(p(s,e,B.subscribe(s,e)));return}if(n.type===`session_command`&&R(n.frame)){if(n.frame.type===`attach`)return;I.command(s,n.frame)}},onClose(){I.disconnectChannels(a),l&&V.delete(l),l=void 0,u?.(),u=void 0,s?.(),s=void 0,c?.(),c=void 0,i.clear(),d(),g(`browser detached`)}}})),U.get(se,W.upgradeWebSocket(e=>{let t=Y.listenerOf(e)===`local`,n=t?void 0:J.authorize(k(e,b,`host`)),r=n===void 0?void 0:J.channelFor(n,`protocol`),i,a;return{onOpen(e,o){if(!t&&r===void 0){o.close(1008,`sealed protocol channel required`);return}n!==void 0&&(a=J.trackSocket(n,(e,t)=>o.close(e,t))),i=(t?Ct:wt).accept({send:e=>{if(t){o.send(e);return}if(r===void 0)return;let n=e instanceof Uint8Array?e:new Uint8Array(e),i=r.seal(n);if(!i.ok){o.close(1008,`sealed protocol channel failed`);return}o.send(new TextEncoder().encode(JSON.stringify(i.envelope)))},close:()=>o.close(),get readyState(){return o.readyState}})},async onMessage(e){let n=e.data;if(typeof n==`string`)return;let a=n instanceof Blob?new Uint8Array(await n.arrayBuffer()):(n instanceof ArrayBuffer,new Uint8Array(n));if(t){i?.onData(a);return}if(r===void 0)return;let o;try{o=JSON.parse(new TextDecoder().decode(a))}catch{return}let s=r.open(o);s.ok&&i?.onData(s.plaintext)},onClose(){a?.(),a=void 0,i?.onClose(),i=void 0},onError(e){i?.onError(e instanceof Error?e:Error(`The protocol socket failed`)),i=void 0}}})),U.get(`/manifest.webmanifest`,e=>{let t=L(E.join(G,`manifest.webmanifest`));return t===void 0?e.text(`The PWA manifest is unavailable.`,404):e.body(new Uint8Array(t),200,{"Cache-Control":`no-store`,"Content-Type":`application/manifest+json; charset=utf-8`})}),U.get(`/sw.js`,e=>{let t=L(E.join(G,`sw.js`));return t===void 0?e.text(`The trusted service worker is unavailable.`,404):e.body(new Uint8Array(t),200,{"Cache-Control":`no-cache`,"Content-Type":`text/javascript; charset=utf-8`,"Service-Worker-Allowed":`/`})}),U.get(`/pwa/*`,e=>{let t=new URL(e.req.url).pathname.slice(5),n=s(G,`/${t}`),r=n===void 0?void 0:L(n);return n===void 0||r===void 0?e.text(`PWA asset not found.`,404):e.body(new Uint8Array(r),200,{"Cache-Control":`no-store`,"Content-Type":o(n),"X-Content-Type-Options":`nosniff`})}),U.get(Xe,e=>{let t=K.current();return t===void 0?e.json({error:`No signed cockpit bundle is available.`},503):e.json(t.signed,200,{"Cache-Control":`no-store`})}),U.get(`/bundle-assets/*`,e=>{let t=new URL(e.req.url).pathname,n=/^\/bundle-assets\/([1-9][0-9]*)(\/.*)$/u.exec(t),r=n===null?void 0:Number(n[1]),i=K.current();if(n===null||!Number.isSafeInteger(r)||i===void 0||i.signed.manifest.revision!==r)return e.text(`That signed bundle revision is unavailable.`,404);let a=Ze(i.signed.manifest,n[2]??``),o=a===void 0?void 0:s(i.assetsDir,a.path),c=o===void 0?void 0:L(o);return a===void 0||o===void 0||c===void 0?e.text(`Bundle asset not found.`,404):e.body(new Uint8Array(c),200,{"Cache-Control":`no-store`,"Content-Length":String(c.byteLength),"Content-Type":a.contentType,"X-Content-Type-Options":`nosniff`})}),U.get(`/`,e=>e.redirect(oe)),U.get(`*`,e=>{let t=new URL(e.req.url).pathname,n=e.req.header(`accept`)?.includes(`text/html`)===!0;return!t.startsWith(`/api/`)&&n?e.redirect(`/`):e.text(`Not found.`,404)});let Pt=async()=>{try{let e=await M();e!==void 0&&e.webDirectory!==P.assetsDir&&(P={assetsDir:e.webDirectory,apiDirectory:e.apiDirectory},g(`serving the rebuilt cockpit bundle from ${P.assetsDir}`)),K.refresh(),H({type:u},!1)}catch(e){g(`the rebuilt cockpit composition is unavailable (${z(e)}); keeping the served bundle`)}};for(let e of j.values())e.watch(()=>void Pt());return new Promise((e,t)=>{let n=Ue({fetch:U.fetch,port:c.port,hostname:h},t=>{gt=t.port,W.injectWebSocket(n);let r=`http://${h}:${t.port}`;g(`cockpit on ${r}`);let i,a=async()=>{await J.close(),await new Promise(e=>{B.close();for(let e of j.values())e.close();Dt.close(),Z.close(),vt(),q?.close(),I.close(),Q.close();for(let e of $)e.close();for(let e of W.wss.clients)e.terminate();W.wss.close(),n.closeAllConnections?.(),n.close(()=>e())}),await Re(_)};e({url:r,port:t.port,close:()=>i??=a()})});n.once(`error`,e=>{B.close(),vt(),q?.close(),I.close(),Q.close(),J.close();for(let e of $)e.close();_.shutdown(),t(e)})})}export{pt as createSettingsRepositoryRegistry,ct as packagedVersion,V as serveWeb};
1
+ import{createPiHubService as e}from"./piHubService.mjs";import{createSyncGuard as t}from"./syncGuard.mjs";import{createPiWebSocketListener as n}from"./piWebSocketListener.mjs";import{MAX_SESSION_FILE_BYTES as r,SESSION_FILE_ROUTE as i}from"../types/media.mjs";import{sessionFileHeaders as a}from"../services/fileMedia.mjs";import{contentTypeFor as o,resolveAssetPath as s}from"../services/staticAssets.mjs";import{API_SESSION_QUERY_PARAM as c,DIRECTORIES_API_ROUTE as l,SESSIONS_API_ROUTE as u,SESSIONS_SNAPSHOT_TYPE as ee,SESSION_REMOVED_TYPE as d,SESSION_UPSERT_TYPE as te,hubHello as ne,sessionFrameEnvelope as re,threadBacklog as f,threadFrameEnvelope as ie}from"../types/hub.mjs";import{parseDoomNotificationEntry as ae}from"../types/notification.mjs";import"../types/session.mjs";import{readGitStatus as p}from"./gitStatus.mjs";import{readRegistryRecords as m,watchRegistry as h}from"./registryWatcher.mjs";import{createServerSpawner as g}from"./serverSpawner.mjs";import{proxyToSocket as _}from"./packageApiProxy.mjs";import{createSessionHub as oe}from"./sessionHub.mjs";import{DEVICE_COOKIE as v,PAIRING_PAGE_ROUTE as se,PROTOCOL_SOCKET_ROUTE as ce,REMOTE_CHANNEL_ROUTE as y,REMOTE_HTTP_ROUTE as b,REMOTE_PUSH_KEY_ROUTE as le,REMOTE_PUSH_ROUTE as ue,SESSION_SOCKET_ROUTE as de}from"../types/remoteAccess.mjs";import{allowedOriginsFromEnv as fe}from"../services/remoteGuardPolicy.mjs";import{describeStranded as pe,planSessionMigration as me}from"../services/sessionMigration.mjs";import{registerAuthRoutes as he}from"./authRoutes.mjs";import{registerSettingsRoutes as ge}from"./settingsRoutes.mjs";import{createProviderAuth as _e}from"./providerAuth.mjs";import{createRemoteGuard as ve}from"./remoteGuard.mjs";import{createRemoteAccess as ye}from"./remoteAccess.mjs";import{createBundlePublication as be,createPluginBundlePublication as xe}from"./bundlePublication.mjs";import{createLiveWebPush as Se}from"./webPush.mjs";import{createRemoteAccessStore as Ce}from"./remoteAccessStore.mjs";import{registerRemoteRoutes as we}from"./remoteRoutes.mjs";import{createTunnelLauncher as Te,reapStaleTunnel as Ee}from"./tunnelProcess.mjs";import{suggestDirectories as De}from"./directoryListing.mjs";import{listSessionFiles as Oe,readSessionFile as ke}from"./sessionFiles.mjs";import{createThreadJournals as Ae}from"./threadJournals.mjs";import{loadHubChannels as je}from"./webHubPluginLoader.mjs";import{BROWSER_ERROR_EVENT as Me,WEB_TELEMETRY_ROUTE as Ne,createBrowserError as Pe,createBrowserTelemetryRateLimit as Fe,createWebTelemetry as Ie,forwardedTraceContext as x,isBrowserErrorEvent as Le,parseBrowserTelemetryBatch as Re,readTraceContext as S,requestOperation as ze,shutdownWebTelemetry as Be}from"./webTelemetry.mjs";import{resolveSessionWebArtifacts as Ve}from"./sessionWebComposition.mjs";import C from"node:fs";import w from"node:path";import{fileURLToPath as T}from"node:url";import{readSyncDrift as He,readSyncRegistration as E}from"@agimon-ai/doompi/services";import{createHash as D,randomUUID as Ue}from"node:crypto";import We from"node:os";import{serve as Ge}from"@hono/node-server";import{createNodeWebSocket as Ke}from"@hono/node-ws";import{SessionManager as qe}from"@earendil-works/pi-coding-agent";import{PiServer as Je,PiServerError as Ye}from"@earendil-works/pi-server";import{globalDoomConfigDirectory as Xe}from"@agimon-ai/doompi-config";import{readSyncState as Ze}from"@agimon-ai/doompi/services/syncState";import{BUNDLE_MANIFEST_ROUTE as Qe,assetFor as $e}from"@agimon-ai/doompi-web-security";import{Hono as et}from"hono";import{getCookie as O}from"hono/cookie";import{bodyLimit as k}from"hono/body-limit";import{DOOM_API_ROUTE_PREFIX as A}from"@agimon-ai/doompi-extension-contracts/package-api";import{PACKAGE_API_DIR_ENV as tt,loadPackageApis as nt}from"@agimon-ai/doompi-extension-contracts/package-api-loader";import{insideSandbox as rt}from"@agimon-ai/doompi-extension-contracts/sandbox-harness";import{findRepositoryRoot as it,resolveDoomConfigurationRoot as at}from"@agimon-ai/doompi/utils/repository";const j=`DOOMPI_WEB_PACKAGE_ROOT`,ot=`/api/web-plugins/`,st=8388608,M=new Set([`GET`,`HEAD`,`POST`,`PUT`,`PATCH`,`DELETE`]),N=new Set([`connection`,`content-length`,`cookie`,`forwarded`,`host`,`origin`,`transfer-encoding`,`upgrade`,`x-forwarded-for`,`x-forwarded-host`,`x-forwarded-proto`]);function ct(e){if(typeof e!=`object`||!e)return;let t=e;if(t.v!==1||typeof t.method!=`string`||!M.has(t.method)||typeof t.target!=`string`||!t.target.startsWith(`/`)||t.target.startsWith(`//`)||t.target.includes(`#`)||!Array.isArray(t.headers)||!t.headers.every(e=>Array.isArray(e)&&e.length===2&&e.every(e=>typeof e==`string`))||t.body!==void 0&&typeof t.body!=`string`)return;let n=new URL(t.target,`http://sealed.doompi.invalid`);if(n.pathname!==b&&n.pathname!==y)return t}function lt(e,t){try{let n=new Headers;for(let[t,r]of e.headers){let e=t.toLowerCase();N.has(e)||e.startsWith(`sec-`)||n.append(t,r)}for(let e of[`cookie`,`host`,`origin`]){let r=t.req.header(e);r!==void 0&&n.set(e,r)}return n}catch{return}}function ut(e){if(e===void 0)return Buffer.alloc(0);if(!/^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/.test(e))return;let t=Buffer.from(e,`base64`);return t.toString(`base64`)===e?t:void 0}function P(e){let t=process.env[j];if(t!==void 0&&t!==``)return w.join(t,`dist`,e);let n=w.dirname(T(import.meta.url));for(;;){if(C.existsSync(w.join(n,`package.json`)))return w.join(n,`dist`,e);let t=w.dirname(n);if(t===n)throw Error(`doompi-web could not locate its own package root.`);n=t}}function F(){return P(`web`)}function dt(){return P(`pwa`)}function ft(){let e=process.env[j],t=e!==void 0&&e!==``?e:w.dirname(T(import.meta.url));for(;;){let e=w.join(t,`package.json`);if(C.existsSync(e))try{let t=JSON.parse(C.readFileSync(e,`utf8`)),n=L(t)?t.version:void 0;return typeof n==`string`?n:`unknown`}catch{return`unknown`}let n=w.dirname(t);if(n===t)return`unknown`;t=n}}function pt(e,t,n){if(e!==void 0)return e;let r=process.env.DOOMPI_WEB_DIST;if(r!==void 0&&r!==``)return r;let i=t?.webDirectory;return i!=null&&C.existsSync(w.join(i,`index.html`))?(n(`serving the synced cockpit bundle from ${i}`),i):F()}function I(e){try{return C.statSync(e).isFile()?C.readFileSync(e):void 0}catch{return}}function L(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function mt(e,t){let n=e.firstMessage.split(`
2
+ `,1)[0]?.trim();return(e.name?.trim()||n||w.basename(t)||`untitled`).slice(0,80)}function R(e){return e instanceof Error?e.message:String(e)}function ht(e,t){return`${e}\n${t}`}function gt(e,t,n,r){return oe({source:h(e.registryDir,t),spawner:g({registryDir:e.registryDir,command:e.spawnCommand,onNotice:t}),readGit:p,...n,telemetry:r,onNotice:t})}function z(e){return`repo-${D(`sha256`).update(e).digest(`base64url`).slice(0,24)}`}function _t(e){let t=new Map,n=()=>{for(let[e,n]of t)t.set(e,{...n,repository:{...n.repository,active:!1}});for(let n of e.records())try{let e=C.realpathSync(it(n.cwd));t.set(e,{repository:{id:z(e),path:e,name:w.basename(e)||e,active:!0},seenAt:Date.now()})}catch{}let n=[...t.entries()].filter(([,e])=>!e.repository.active).sort((e,t)=>t[1].seenAt-e[1].seenAt);for(let[e]of n.slice(12))t.delete(e);return[...t.values()].sort((e,t)=>Number(t.repository.active)-Number(e.repository.active)||t.seenAt-e.seenAt||e.repository.path.localeCompare(t.repository.path)).map(e=>e.repository)};return{list:n,resolve(e){return n().find(t=>t.id===e)?.path}}}function vt(e,t,n,r,i){let a=[],o=new Map,s=e=>{for(let t of e){if(o.has(t.basePath))continue;let e;try{e=t.start({scope:`hub`,onNotice:n,resolveRepository:r,readRepositorySync:i})}catch(e){n(`hub API '${t.basePath}' did not start (${R(e)}); its routes stay unmounted`);continue}o.set(t.basePath,e),a.push(e)}};return s(t),e.all(`${A}/:basePath/*`,async(e,t)=>{if(e.req.query(`session`)!==void 0)return t();let r=e.req.param(`basePath`),i=o.get(r);if(i===void 0)return t();let a=`${A}/${r}`,s=new URL(e.req.url);s.pathname=s.pathname.slice(a.length)||`/`;try{return await i.fetch(new Request(s,e.req.raw))}catch(t){return n(`hub API '${r}' failed on ${s.pathname} (${R(t)})`),e.json({error:`The '${r}' API failed.`},500)}}),{handlers:a,add:s}}function yt(e,t,n,r){e.all(`${A}/*`,async e=>{let i=e.req.query(c);if(i===void 0)return e.notFound();let a=t.snapshot().find(e=>e.id===i);if(a===void 0)return e.json({error:`No session ${i}.`},404);if(a.apiSocketPath===void 0)return e.json({error:`Session ${i} serves no package API.`},404);let o=new URL(e.req.url);o.searchParams.delete(c);let s=S(e.req.raw.headers);try{return await r.runInSpan(`web.package_proxy`,{"operation.name":`web.package_proxy`,"http.method":e.req.method},async t=>await _({socketPath:a.apiSocketPath,path:`${o.pathname}${o.search}`,method:e.req.method,headers:e.req.raw.headers,body:e.req.raw.body,trace:x(t,s)}),s)}catch(t){return n(`session ${i} API is unreachable (${R(t)})`),e.json({error:`Session ${i} is not answering.`},502)}})}async function B(c){let p=c.host??`127.0.0.1`,h=c.onNotice??(()=>{}),g=Ie(),_=Ce({stateDir:c.remoteStateDir,onNotice:h}),oe=e=>{try{return C.realpathSync(it(e))}catch{return}},y=e=>{let t=We.homedir(),n=at(e,t);if(n===Xe(t))return n;try{return C.realpathSync(n)}catch{return w.resolve(n)}},x=c.compositionDir===void 0?void 0:oe(c.compositionDir);if(c.compositionDir!==void 0&&x===void 0)throw Error(`Cockpit composition directory is not inside a repository: ${c.compositionDir}`);let T=Xe(We.homedir()),D=t({repoRoot:T,onNotice:h}),A=async e=>{},j=async e=>{if(e===T)await D.ensureSynced();else{let n=t({repoRoot:e,onNotice:h});try{await n.ensureSynced()}finally{n.close()}}await A(e)},M=async e=>{try{await j(e)}catch(t){h(`WARNING: startup sync failed for ${e}; continuing to serve the cockpit (${R(t)})`)}};await M(T);let N=async e=>await j(y(e)),P=[...m(c.registryDir,h).map(e=>y(e.cwd)),...x===void 0?[]:[y(x)]];await Promise.all([...new Set(P)].map(M));let F={assetsDir:pt(c.assetsDir,void 0,h),apiDirectory:void 0},z=xe(_.directory,h),B=gt(c,h,{loadChannels:async e=>{let t=y(e.cwd);await j(t);let n=E(t);return n?.webDirectory===null||n?.webDirectory===void 0?[]:await je(n.webDirectory,h)},webComposition:(e,t)=>{let n=Ve(y(e.cwd));if(n===void 0)return;let r=z.publish(n.id,n.pluginsDir);if(r===void 0)return;let i=r.signed.manifest.revision,a=`${ot}${n.id}/${String(i)}`;return{id:n.id,revision:i,manifestUrl:`${a}/manifest`,rawAssetBaseUrl:`${a}/assets`,verifiedAssetBaseUrl:`/verified-plugins/${n.id}/${String(i)}`,entryPath:n.entryPath,stylePaths:n.stylePaths,channels:[...t]}}},g),V=Ae({resolve:(e,t)=>B.threadJournal(e,t),onNotice:h}),H=new Set,bt=(e,t)=>{for(let n of H)t&&!n.local||n.post(e)},U=new et,W=Ke({app:U}),xt,G=dt(),K=be({assetsDir:()=>F.assetsDir,stateDir:_.directory,onNotice:h}),q;Ee(_.directory,h);let St=c.onHandover===void 0?void 0:e=>{let t=me(B.snapshot().map(e=>({id:e.id,cwd:e.cwd,...e.name===void 0?{}:{name:e.name}})),e.sandbox.workspaces);for(let e of pe(t.stranded))h(e);for(let e of t.migrate){let t=B.stop(e.id);t.ok||h(`could not stop ${e.name??e.id} before the move: ${t.error}`)}c.onHandover?.({settings:e,sessions:t.migrate})},J=ye({store:_,launchTunnel:c.remoteAccess?.launchTunnel??Te({stateDir:_.directory,...c.cloudflaredPath===void 0?{}:{cloudflaredPath:c.cloudflaredPath},onNotice:h,onExit:e=>{h(`remote access: ${e}`),J.disable()}}),bindListener:async()=>await new Promise((e,t)=>{let n=Ge({fetch:U.fetch,port:0,hostname:`127.0.0.1`,serverOptions:{maxHeaderSize:16384,headersTimeout:5e3,requestTimeout:1e4}},t=>{W.injectWebSocket(n),e({port:t.port,close:async()=>await new Promise(e=>{n.closeAllConnections?.(),n.close(()=>e())})})}),r=n;r.maxConnections=64,r.maxRequestsPerSocket=100,n.once(`error`,t)}),bundleTrust:()=>K.trust(),onDeviceDropped:e=>q?.remove(e),onNotice:h,...St===void 0?{}:{requestHandover:St},contained:rt(process.env),broadcastLocal:e=>bt(e,!0),broadcastAll:e=>bt(e,!1),...c.remoteAccess?.now===void 0?{}:{now:c.remoteAccess.now}});q=Se({stateDir:_.directory,isConnected:e=>J.isDeviceConnected(e),onNotice:h});let Ct=B.onEvent(e=>{e.kind===`frame`&&ae(e.frame)!==void 0&&q?.notify()}),Y=ve({loopbackPort:()=>xt,tunnelPolicy:()=>J.tunnelPolicy(),authorize:e=>J.authorize(O(e,v,`host`)),trustedDevice:e=>e.env?.sealedDeviceId,channelReady:(e,t)=>J.channelFor(e,t)!==void 0,stepUp:{required:e=>J.stepUpRequired(e),verify:async(e,t,n)=>{if(typeof n!=`object`||!n)return!1;let r=n.ceremonyId,i=n.response;if(typeof r!=`string`)return!1;let a=e.env?.sealedDeviceId??J.authorize(O(e,`doompi_device`,`host`)),o=a===void 0?`local`:`device:${a}`;return await J.passkeys().finishStepUp(r,o,t,i)}},extraOrigins:fe(process.env.DOOMPI_WEB_ALLOW_ORIGIN)});U.use(`*`,Y.middleware),U.use(`*`,async(e,t)=>{let n=new URL(e.req.url).pathname;if(n===b)return t();let r=ze(n);if(r===void 0)return t();let i=performance.now();return await g.runInSpan(r,{"operation.name":r,"http.method":e.req.method},async()=>{await t(),await g.recordEvent(`web.api.request`,{"operation.name":r,"http.method":e.req.method,"http.status_code":e.res.status,duration_ms:Math.max(0,Math.round(performance.now()-i))})},S(e.req.raw.headers))}),U.use(`*`,async(e,t)=>{if(Y.listenerOf(e)===`local`)return t();let n=Number(e.req.header(`content-length`));return Number.isFinite(n)&&n>st?e.json({error:`The tunnel request body is too large.`},413):t()}),we(U,{remote:J,listenerOf:e=>Y.listenerOf(e)});let wt=k({maxSize:st,onError:e=>e.json({error:`The tunnel request body is too large.`},413)});U.use(`*`,async(e,t)=>Y.listenerOf(e)===`local`?t():await wt(e,t));let Tt=k({maxSize:32768,onError:e=>e.json({error:`The telemetry batch is too large.`},413)}),Et=Fe();U.post(Ne,Tt,async e=>{if(!Et())return e.json({error:`Too many telemetry batches.`},429);let t;try{t=await e.req.json()}catch{return e.json({error:`The telemetry batch must be JSON.`},400)}let n=Re(t);if(n===void 0)return e.json({error:`The telemetry batch is invalid.`},400);for(let e of n){if(Le(e)){await g.recordError(Me,Pe(e),{"browser.source":e.source,...e.session_id===void 0?{}:{"session.id":e.session_id},"failure.kind":`browser`},{includeException:!0});continue}await g.recordEvent(e.name,{...e.duration_ms===void 0?{}:{duration_ms:e.duration_ms},...e.count===void 0?{}:{count:e.count}})}return e.body(null,204)}),U.post(b,async e=>{let t=J.authorize(O(e,v,`host`));if(t===void 0)return e.json({error:`This device is not paired.`},401);let n=J.channelFor(t,`http`);if(n===void 0)return e.json({error:`This device has no sealed HTTP channel.`},401);let r;try{r=await e.req.json()}catch{return e.json({error:`The sealed request envelope was malformed.`},400)}let i=n.open(r);if(!i.ok)return e.json({error:`The sealed request was refused: ${i.failure}.`},400);let a;try{a=JSON.parse(new TextDecoder().decode(i.plaintext))}catch{return e.json({error:`The sealed HTTP request was malformed.`},400)}let o=ct(a),s=o===void 0?void 0:lt(o,e),c=o===void 0?void 0:ut(o.body);if(o===void 0||s===void 0||c===void 0)return e.json({error:`The sealed HTTP request was invalid.`},400);let l=new URL(o.target,new URL(e.req.url).origin),u=await U.request(l,{method:o.method,headers:s,...o.method===`GET`||o.method===`HEAD`?{}:{body:Uint8Array.from(c).buffer}},{...e.env,sealedDeviceId:t}),ee=Buffer.from(await u.arrayBuffer()),d=n.seal(new TextEncoder().encode(JSON.stringify({v:1,status:u.status,headers:Array.from(u.headers.entries()),body:ee.toString(`base64`)})));return d.ok?e.json(d.envelope):e.json({error:`The sealed response failed: ${d.failure}.`},503)});let Dt=k({maxSize:8192,onError:e=>e.json({error:`The Push subscription is too large.`},413)}),X=e=>e.env?.sealedDeviceId;U.get(le,e=>X(e)===void 0?e.json({error:`A sealed paired device is required.`},401):e.json({publicKey:q?.publicKey()},200,{"Cache-Control":`no-store`})),U.post(ue,Dt,async e=>{let t=X(e);if(t===void 0)return e.json({error:`A sealed paired device is required.`},401);let n;try{n=await e.req.json()}catch{return e.json({error:`The Push subscription must be JSON.`},400)}return q?.subscribe(t,n)===!0?e.body(null,204):e.json({error:`The Push subscription is invalid.`},400)}),U.delete(ue,e=>{let t=X(e);return t===void 0?e.json({error:`A sealed paired device is required.`},401):(q?.remove(t),e.body(null,204))});let Ot=n({onError:e=>h(`protocol: ${e.message}`)}),Z=n({onError:e=>h(`protocol: ${e.message}`)}),kt=e({records:()=>B.records(),spawn:e=>B.create(e),onNotice:h}),At={...kt,createSession:async()=>{throw new Ye(`not_implemented`,`Remote protocol clients cannot create sessions`)}},jt=new Je(kt,{listeners:[Ot],onError:e=>h(`protocol: ${e.message}`)}),Mt=new Je(At,{listeners:[Z],onError:e=>h(`protocol: ${e.message}`)});await Promise.all([jt.start(),Mt.start()]);let Q=_e({runtime:c.authRuntime,onNotice:h});he(U,Q);let Nt=_t(B),Pt=()=>Nt.list(),Ft=e=>Nt.resolve(e),It=e=>{let t=Ft(e);if(t===void 0)return;let n=He({repoRoot:t}),r;try{r=E(t)?Ze(t):void 0}catch{r=void 0}return{fresh:n.fresh,reasons:[...n.reasons],...r===void 0?{}:{mcpProjection:r.fileState.mcpProjection}}};ge(U,{repositories:Pt,models:()=>Q.listModels()});let Lt=F.apiDirectory,$=vt(U,Lt===void 0&&!process.env[tt]?[]:await nt(`hub`,{apiDirectory:Lt,onNotice:h}),h,Ft,It);A=async e=>{let t=E(e);if(t===void 0)return;let n=await nt(`hub`,{apiDirectory:t.apiDirectory,env:{},onNotice:h});$.add(n)},await Promise.all([...new Set([T,...P])].map(A)),yt(U,B,h,g),U.get(`/api/health`,e=>e.json({ok:!0,role:`hub`,protocol:2,sessions:B.snapshot().length,pid:process.pid,version:ft()})),U.post(u,async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:`The request body must be JSON.`},400)}if(!L(t)||typeof t.cwd!=`string`||t.cwd===``)return e.json({error:`A cwd string is required.`},400);let n;try{n=C.statSync(t.cwd)}catch{return e.json({error:`No such directory: ${t.cwd}`},400)}if(!w.isAbsolute(t.cwd)||!n.isDirectory())return e.json({error:`The working directory must be an absolute path to a directory, received "${t.cwd}".`},400);let r=typeof t.name==`string`&&t.name!==``?t.name:void 0;await N(t.cwd);let i=await B.create({cwd:t.cwd,name:r,trace:S(e.req.raw.headers)});return i.ok?e.json({sessionId:i.sessionId},201):e.json({error:i.error},i.code===`invalid_request`?400:502)}),U.post(`${u}/:sessionId/restart`,async e=>{let t=e.req.param(`sessionId`),n=B.snapshot().find(e=>e.id===t);n!==void 0&&await N(n.cwd);let r=await B.restart(t,S(e.req.raw.headers));return r.ok?e.json({sessionId:r.sessionId},202):e.json({error:r.error},r.code===`invalid_request`?404:502)}),U.get(`${u}/:sessionId/history`,async e=>{let t=e.req.param(`sessionId`),n=B.snapshot().find(e=>e.id===t);if(!n)return e.json({error:`Unknown session.`},404);let r=await qe.list(n.cwd);return e.json({sessions:r.map(e=>({id:e.id,...e.name===void 0?{}:{name:e.name},firstMessage:e.firstMessage,createdAt:e.created.toISOString(),updatedAt:e.modified.toISOString(),messageCount:e.messageCount}))})}),U.post(`${u}/:sessionId/resume`,async e=>{let t=e.req.param(`sessionId`),n=B.snapshot().find(e=>e.id===t);if(!n)return e.json({error:`Unknown session.`},404);let r;try{r=await e.req.json()}catch{return e.json({error:`The request body must be JSON.`},400)}if(!L(r)||typeof r.targetSessionId!=`string`||r.targetSessionId===``)return e.json({error:`A targetSessionId string is required.`},400);let i=(await qe.list(n.cwd)).find(e=>e.id===r.targetSessionId);if(!i)return e.json({error:`That Pi thread does not belong to this workspace.`},404);await N(n.cwd);let a=await B.resume(t,{sessionId:i.id,name:mt(i,n.cwd)},S(e.req.raw.headers));return a.ok?e.json({sessionId:a.sessionId},202):e.json({error:a.error},a.code===`invalid_request`?409:502)}),U.delete(`${u}/:sessionId`,e=>{let t=e.req.param(`sessionId`),n=B.stop(t);return n.ok?e.json({sessionId:t},202):e.json({error:n.error},n.code===`unknown`?404:n.code===`self`?409:502)}),U.get(`/api/sessions/:sessionId/files`,async e=>{let t=e.req.param(`sessionId`),n=B.snapshot().find(e=>e.id===t);if(!n)return e.json({error:`Unknown session.`},404);let r=e.req.query(`q`)??``,i=await Oe(n.cwd,r,20);return e.json({files:i})});let Rt=e=>{if(!(Y.listenerOf(e)===`local`||rt(process.env)))return c.browseRoot??process.cwd()};return U.get(l,async e=>{let t=Rt(e),n=await De(e.req.query(`q`)??``,{limit:12,...t===void 0?{}:{root:t}});return e.json({directories:n})}),U.get(i,async e=>{let t=e.req.param(`sessionId`),n=B.snapshot().find(e=>e.id===t);if(!n)return e.json({error:`Unknown session.`},404);let i=e.req.query(`path`)??``,o=await ke(n.cwd,i,r);return o.status===`forbidden`?e.json({error:`The path leaves the session directory.`},403):o.status===`not-found`?e.json({error:`No such file.`},404):o.status===`too-large`?e.json({error:`The file is too large to preview.`},413):e.body(new Uint8Array(o.body),200,a(i))}),U.get(de,W.upgradeWebSocket(e=>{let t=Y.listenerOf(e)===`local`,n=t?void 0:J.authorize(O(e,v,`host`)),r=n===void 0?void 0:J.channelFor(n,`session`),i=new Set,a=Ue(),o=new Map,s,c,l,u,p=e=>{for(let[t,n]of o)(e===void 0||n.sessionId===e)&&(o.delete(t),V.unsubscribe(n.sessionId,n.threadId))};return{onOpen(e,f){if(!t&&r===void 0){f.close(1008,`sealed session channel required`);return}let m=e=>{let n=JSON.stringify(e),i=n;if(!t){if(r===void 0)return;let e=r.seal(new TextEncoder().encode(n));if(!e.ok){f.close(1008,`sealed session channel failed`);return}i=JSON.stringify(e.envelope)}try{f.send(i)}catch{}};l={post:m,local:t},H.add(l),n!==void 0&&(u=J.trackSocket(n,(e,t)=>f.close(e,t))),m(ne(B.channelTypes())),m({type:ee,sessions:B.snapshot()}),s=B.onEvent(e=>{e.kind===`upsert`?m({type:te,session:e.session}):e.kind===`removed`?(i.delete(e.sessionId),p(e.sessionId),m({type:d,sessionId:e.sessionId})):e.kind===`channel`?(e.connectionId===a||e.connectionId===void 0&&i.has(e.sessionId))&&m({type:e.frameType,sessionId:e.sessionId,payload:e.payload}):(ae(e.frame)!==void 0||i.has(e.sessionId))&&m(re(e.sessionId,e.frame))}),c=V.onFrame(e=>{o.has(ht(e.sessionId,e.threadId))&&m(ie(e.sessionId,e.threadId,e.frame))}),h(`browser attached`)},onMessage(e){if(typeof e.data!=`string`)return;let n;try{n=JSON.parse(e.data)}catch{return}if(!t){if(r===void 0)return;let e=r.open(n);if(!e.ok)return;try{n=JSON.parse(new TextDecoder().decode(e.plaintext))}catch{return}}if(!L(n)||typeof n.sessionId!=`string`)return;let s=n.sessionId;if(typeof n.type==`string`&&B.channelTypes().includes(n.type)&&(i.has(s)||B.channelReceivesWithoutSubscription(s,n.type))){B.receiveChannel(s,n.type,n.payload,a);return}if(n.type===`subscribe`){let e=B.backlog(s);if(!e)return;i.add(s),l?.post(e);for(let e of B.channelFrames(s))l?.post(e);return}if(n.type===`unsubscribe`){i.delete(s);return}if(n.type===`history_request`){let e=B.history(s,{...typeof n.before==`string`?{before:n.before}:{},...typeof n.limit==`number`?{limit:n.limit}:{}});if(!e)return;l?.post(e);return}if(n.type===`subscribe_thread`||n.type===`unsubscribe_thread`){if(typeof n.threadId!=`string`)return;let e=n.threadId,t=ht(s,e);if(n.type===`unsubscribe_thread`){o.delete(t)&&V.unsubscribe(s,e);return}if(o.has(t)||B.backlog(s)===void 0)return;o.set(t,{sessionId:s,threadId:e}),l?.post(f(s,e,V.subscribe(s,e)));return}if(n.type===`session_command`&&L(n.frame)){if(n.frame.type===`attach`)return;B.command(s,n.frame)}},onClose(){B.disconnectChannels(a),l&&H.delete(l),l=void 0,u?.(),u=void 0,s?.(),s=void 0,c?.(),c=void 0,i.clear(),p(),h(`browser detached`)}}})),U.get(ce,W.upgradeWebSocket(e=>{let t=Y.listenerOf(e)===`local`,n=t?void 0:J.authorize(O(e,v,`host`)),r=n===void 0?void 0:J.channelFor(n,`protocol`),i,a;return{onOpen(e,o){if(!t&&r===void 0){o.close(1008,`sealed protocol channel required`);return}n!==void 0&&(a=J.trackSocket(n,(e,t)=>o.close(e,t))),i=(t?Ot:Z).accept({send:e=>{if(t){o.send(e);return}if(r===void 0)return;let n=e instanceof Uint8Array?e:new Uint8Array(e),i=r.seal(n);if(!i.ok){o.close(1008,`sealed protocol channel failed`);return}o.send(new TextEncoder().encode(JSON.stringify(i.envelope)))},close:()=>o.close(),get readyState(){return o.readyState}})},async onMessage(e){let n=e.data;if(typeof n==`string`)return;let a=n instanceof Blob?new Uint8Array(await n.arrayBuffer()):(n instanceof ArrayBuffer,new Uint8Array(n));if(t){i?.onData(a);return}if(r===void 0)return;let o;try{o=JSON.parse(new TextDecoder().decode(a))}catch{return}let s=r.open(o);s.ok&&i?.onData(s.plaintext)},onClose(){a?.(),a=void 0,i?.onClose(),i=void 0},onError(e){i?.onError(e instanceof Error?e:Error(`The protocol socket failed`)),i=void 0}}})),U.get(`/manifest.webmanifest`,e=>{let t=I(w.join(G,`manifest.webmanifest`));return t===void 0?e.text(`The PWA manifest is unavailable.`,404):e.body(new Uint8Array(t),200,{"Cache-Control":`no-store`,"Content-Type":`application/manifest+json; charset=utf-8`})}),U.get(`/sw.js`,e=>{let t=I(w.join(G,`sw.js`));return t===void 0?e.text(`The trusted service worker is unavailable.`,404):e.body(new Uint8Array(t),200,{"Cache-Control":`no-cache`,"Content-Type":`text/javascript; charset=utf-8`,"Service-Worker-Allowed":`/`})}),U.get(`/pwa/*`,e=>{let t=new URL(e.req.url).pathname.slice(5),n=s(G,`/${t}`),r=n===void 0?void 0:I(n);return n===void 0||r===void 0?e.text(`PWA asset not found.`,404):e.body(new Uint8Array(r),200,{"Cache-Control":`no-store`,"Content-Type":o(n),"X-Content-Type-Options":`nosniff`})}),U.get(`${ot}*`,e=>{let t=new URL(e.req.url).pathname,n=/^\/api\/web-plugins\/([a-f0-9]{64})\/([1-9][0-9]*)\/(manifest|assets(\/.*))$/u.exec(t),r=n===null?void 0:Number(n[2]),i=n===null||!Number.isSafeInteger(r)?void 0:z.get(n[1]??``,r);if(n===null||i===void 0)return e.text(`That signed plugin composition is unavailable.`,404);if(n[3]===`manifest`)return e.json(i.signed,200,{"Cache-Control":`no-store`});let a=$e(i.signed.manifest,n[4]??``),o=a===void 0?void 0:s(i.assetsDir,a.path),c=o===void 0?void 0:I(o);return a===void 0||o===void 0||c===void 0?e.text(`Plugin composition asset not found.`,404):e.body(new Uint8Array(c),200,{"Cache-Control":`no-store`,"Content-Length":String(c.byteLength),"Content-Type":a.contentType,"X-Content-Type-Options":`nosniff`})}),U.get(Qe,e=>{let t=K.current();return t===void 0?e.json({error:`No signed cockpit bundle is available.`},503):e.json(t.signed,200,{"Cache-Control":`no-store`})}),U.get(`/bundle-assets/*`,e=>{let t=new URL(e.req.url).pathname,n=/^\/bundle-assets\/([1-9][0-9]*)(\/.*)$/u.exec(t),r=n===null?void 0:Number(n[1]),i=K.current();if(n===null||!Number.isSafeInteger(r)||i===void 0||i.signed.manifest.revision!==r)return e.text(`That signed bundle revision is unavailable.`,404);let a=$e(i.signed.manifest,n[2]??``),o=a===void 0?void 0:s(i.assetsDir,a.path),c=o===void 0?void 0:I(o);return a===void 0||o===void 0||c===void 0?e.text(`Bundle asset not found.`,404):e.body(new Uint8Array(c),200,{"Cache-Control":`no-store`,"Content-Length":String(c.byteLength),"Content-Type":a.contentType,"X-Content-Type-Options":`nosniff`})}),U.get(`/`,e=>e.redirect(se)),U.get(`*`,e=>{let t=new URL(e.req.url).pathname,n=e.req.header(`accept`)?.includes(`text/html`)===!0;return!t.startsWith(`/api/`)&&n?e.redirect(`/`):e.text(`Not found.`,404)}),D.watch(()=>{for(let e of B.records())y(e.cwd)===T&&B.reloadChannels(e.id)}),new Promise((e,t)=>{let n=Ge({fetch:U.fetch,port:c.port,hostname:p},t=>{xt=t.port,W.injectWebSocket(n);let r=`http://${p}:${t.port}`;h(`cockpit on ${r}`);let i,a=async()=>{await J.close(),await new Promise(e=>{V.close(),D.close(),jt.close(),Mt.close(),Ct(),q?.close(),B.close(),Q.close();for(let e of $.handlers)e.close();z.close();for(let e of W.wss.clients)e.terminate();W.wss.close(),n.closeAllConnections?.(),n.close(()=>e())}),await Be(g)};e({url:r,port:t.port,close:()=>i??=a()})});n.once(`error`,e=>{D.close(),V.close(),Ct(),q?.close(),B.close(),Q.close(),J.close();for(let e of $.handlers)e.close();z.close(),g.shutdown(),t(e)})})}export{_t as createSettingsRepositoryRegistry,vt as mountHubApis,ft as packagedVersion,B as serveWeb};
3
3
  //# sourceMappingURL=httpServer.mjs.map