@hemangdoshi/devdeck 1.2.0

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 (54) hide show
  1. package/dist/agent-client.d.ts +32 -0
  2. package/dist/commands/agent.d.ts +2 -0
  3. package/dist/commands/dev.d.ts +9 -0
  4. package/dist/commands/init.d.ts +10 -0
  5. package/dist/commands/session.d.ts +10 -0
  6. package/dist/commands/start.d.ts +7 -0
  7. package/dist/dashboard/404.html +1 -0
  8. package/dist/dashboard/__next.__PAGE__.txt +6 -0
  9. package/dist/dashboard/__next._full.txt +17 -0
  10. package/dist/dashboard/__next._head.txt +5 -0
  11. package/dist/dashboard/__next._index.txt +7 -0
  12. package/dist/dashboard/__next._tree.txt +3 -0
  13. package/dist/dashboard/_next/static/RB8s1jjgiLhjvp2HZDBhU/_buildManifest.js +1 -0
  14. package/dist/dashboard/_next/static/RB8s1jjgiLhjvp2HZDBhU/_ssgManifest.js +1 -0
  15. package/dist/dashboard/_next/static/chunks/101-70b226b829c9d4ae.js +1 -0
  16. package/dist/dashboard/_next/static/chunks/444-19cc275570eb4ec1.js +9 -0
  17. package/dist/dashboard/_next/static/chunks/87c73c54-f46fec743414da25.js +1 -0
  18. package/dist/dashboard/_next/static/chunks/968-8927f96477f316db.js +32 -0
  19. package/dist/dashboard/_next/static/chunks/app/_global-error/page-327b769f8d5a8082.js +1 -0
  20. package/dist/dashboard/_next/static/chunks/app/_not-found/page-528149ca15426794.js +1 -0
  21. package/dist/dashboard/_next/static/chunks/app/layout-970af65dc7518514.js +1 -0
  22. package/dist/dashboard/_next/static/chunks/app/page-8d1a7ef25da4dec8.js +6 -0
  23. package/dist/dashboard/_next/static/chunks/framework-f714a6dbe5864bc1.js +1 -0
  24. package/dist/dashboard/_next/static/chunks/main-26a54062acfda087.js +5 -0
  25. package/dist/dashboard/_next/static/chunks/main-app-3d143a4f086bb1fb.js +1 -0
  26. package/dist/dashboard/_next/static/chunks/next/dist/client/components/builtin/app-error-327b769f8d5a8082.js +1 -0
  27. package/dist/dashboard/_next/static/chunks/next/dist/client/components/builtin/forbidden-327b769f8d5a8082.js +1 -0
  28. package/dist/dashboard/_next/static/chunks/next/dist/client/components/builtin/global-error-279c886853324bb0.js +1 -0
  29. package/dist/dashboard/_next/static/chunks/next/dist/client/components/builtin/not-found-327b769f8d5a8082.js +1 -0
  30. package/dist/dashboard/_next/static/chunks/next/dist/client/components/builtin/unauthorized-327b769f8d5a8082.js +1 -0
  31. package/dist/dashboard/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  32. package/dist/dashboard/_next/static/chunks/webpack-c708016a143f497e.js +1 -0
  33. package/dist/dashboard/_next/static/css/5379a39d263cf11e.css +1 -0
  34. package/dist/dashboard/_next/static/media/22a5144ee8d83bca-s.p.woff2 +0 -0
  35. package/dist/dashboard/_next/static/media/9766a7e9e2e0ad5a-s.woff2 +0 -0
  36. package/dist/dashboard/_next/static/media/aa016aab0e6d1295-s.woff2 +0 -0
  37. package/dist/dashboard/_next/static/media/b66cf8e69499582a-s.woff2 +0 -0
  38. package/dist/dashboard/_next/static/media/f639721981034f88-s.woff2 +0 -0
  39. package/dist/dashboard/_not-found/__next._full.txt +18 -0
  40. package/dist/dashboard/_not-found/__next._head.txt +5 -0
  41. package/dist/dashboard/_not-found/__next._index.txt +7 -0
  42. package/dist/dashboard/_not-found/__next._not-found.__PAGE__.txt +5 -0
  43. package/dist/dashboard/_not-found/__next._not-found.txt +5 -0
  44. package/dist/dashboard/_not-found/__next._tree.txt +3 -0
  45. package/dist/dashboard/_not-found.html +1 -0
  46. package/dist/dashboard/_not-found.txt +18 -0
  47. package/dist/dashboard/index.html +1 -0
  48. package/dist/dashboard/index.txt +17 -0
  49. package/dist/dashboard/static/index.html +43 -0
  50. package/dist/format-agent-output.d.ts +5 -0
  51. package/dist/index.d.ts +8 -0
  52. package/dist/index.js +45 -0
  53. package/dist/session-state.d.ts +21 -0
  54. package/package.json +25 -0
@@ -0,0 +1,3 @@
1
+ :HL["/_next/static/media/22a5144ee8d83bca-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
2
+ :HL["/_next/static/css/5379a39d263cf11e.css","style"]
3
+ 0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"_not-found","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"RB8s1jjgiLhjvp2HZDBhU"}
@@ -0,0 +1 @@
1
+ <!DOCTYPE html><html lang="en" class="font-sans __variable_246ccd"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/22a5144ee8d83bca-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/5379a39d263cf11e.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-c708016a143f497e.js"/><script src="/_next/static/chunks/87c73c54-f46fec743414da25.js" async=""></script><script src="/_next/static/chunks/968-8927f96477f316db.js" async=""></script><script src="/_next/static/chunks/main-app-3d143a4f086bb1fb.js" async=""></script><script src="/_next/static/chunks/101-70b226b829c9d4ae.js" async=""></script><script src="/_next/static/chunks/app/layout-970af65dc7518514.js" async=""></script><meta name="robots" content="noindex"/><meta name="next-size-adjust" content=""/><title>404: This page could not be found.</title><title>DevDeck</title><meta name="description" content="Local-first developer session dashboard"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="min-h-screen antialiased"><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><section aria-label="Notifications alt+T" tabindex="-1" aria-live="polite" aria-relevant="additions text" aria-atomic="false"></section><script src="/_next/static/chunks/webpack-c708016a143f497e.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[4757,[\"101\",\"static/chunks/101-70b226b829c9d4ae.js\",\"177\",\"static/chunks/app/layout-970af65dc7518514.js\"],\"TooltipProvider\"]\n3:I[9630,[],\"\"]\n4:I[1380,[],\"\"]\n5:I[2437,[\"101\",\"static/chunks/101-70b226b829c9d4ae.js\",\"177\",\"static/chunks/app/layout-970af65dc7518514.js\"],\"Toaster\"]\n6:I[887,[],\"OutletBoundary\"]\n7:\"$Sreact.suspense\"\na:I[887,[],\"ViewportBoundary\"]\nc:I[887,[],\"MetadataBoundary\"]\ne:I[5718,[],\"default\",1]\n:HL[\"/_next/static/media/22a5144ee8d83bca-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/5379a39d263cf11e.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_not-found\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/5379a39d263cf11e.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"font-sans __variable_246ccd\",\"children\":[\"$\",\"body\",null,{\"className\":\"min-h-screen antialiased\",\"children\":[\"$\",\"$L2\",null,{\"children\":[[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}],[\"$\",\"$L5\",null,{\"position\":\"top-right\",\"richColors\":true}]]}]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L6\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@8\"}]}]]}],{},null,false,null]},null,false,\"$@9\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$La\",null,{\"children\":\"$Lb\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lc\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Ld\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$e\",[]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"RB8s1jjgiLhjvp2HZDBhU\"}\n"])</script><script>self.__next_f.push([1,"f:[]\n9:\"$Wf\"\n"])</script><script>self.__next_f.push([1,"b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"8:null\nd:[[\"$\",\"title\",\"0\",{\"children\":\"DevDeck\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Local-first developer session dashboard\"}]]\n"])</script></body></html>
@@ -0,0 +1,18 @@
1
+ 1:"$Sreact.fragment"
2
+ 2:I[4757,["101","static/chunks/101-70b226b829c9d4ae.js","177","static/chunks/app/layout-970af65dc7518514.js"],"TooltipProvider"]
3
+ 3:I[9630,[],""]
4
+ 4:I[1380,[],""]
5
+ 5:I[2437,["101","static/chunks/101-70b226b829c9d4ae.js","177","static/chunks/app/layout-970af65dc7518514.js"],"Toaster"]
6
+ 6:I[887,[],"OutletBoundary"]
7
+ 7:"$Sreact.suspense"
8
+ a:I[887,[],"ViewportBoundary"]
9
+ c:I[887,[],"MetadataBoundary"]
10
+ e:I[5718,[],"default",1]
11
+ :HL["/_next/static/media/22a5144ee8d83bca-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
12
+ :HL["/_next/static/css/5379a39d263cf11e.css","style"]
13
+ 0:{"P":null,"c":["","_not-found"],"q":"","i":false,"f":[[["",{"children":["_not-found",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/5379a39d263cf11e.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"font-sans __variable_246ccd","children":["$","body",null,{"className":"min-h-screen antialiased","children":["$","$L2",null,{"children":[["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}],["$","$L5",null,{"position":"top-right","richColors":true}]]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],null,["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],{},null,false,null]},null,false,"$@9"]},null,false,null],["$","$1","h",{"children":[["$","meta",null,{"name":"robots","content":"noindex"}],["$","$La",null,{"children":"$Lb"}],["$","div",null,{"hidden":true,"children":["$","$Lc",null,{"children":["$","$7",null,{"name":"Next.Metadata","children":"$Ld"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$e",[]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"RB8s1jjgiLhjvp2HZDBhU"}
14
+ f:[]
15
+ 9:"$Wf"
16
+ b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
17
+ 8:null
18
+ d:[["$","title","0",{"children":"DevDeck"}],["$","meta","1",{"name":"description","content":"Local-first developer session dashboard"}]]
@@ -0,0 +1 @@
1
+ <!DOCTYPE html><html lang="en" class="font-sans __variable_246ccd"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/22a5144ee8d83bca-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/5379a39d263cf11e.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-c708016a143f497e.js"/><script src="/_next/static/chunks/87c73c54-f46fec743414da25.js" async=""></script><script src="/_next/static/chunks/968-8927f96477f316db.js" async=""></script><script src="/_next/static/chunks/main-app-3d143a4f086bb1fb.js" async=""></script><script src="/_next/static/chunks/101-70b226b829c9d4ae.js" async=""></script><script src="/_next/static/chunks/app/layout-970af65dc7518514.js" async=""></script><script src="/_next/static/chunks/444-19cc275570eb4ec1.js" async=""></script><script src="/_next/static/chunks/app/page-8d1a7ef25da4dec8.js" async=""></script><meta name="next-size-adjust" content=""/><title>DevDeck</title><meta name="description" content="Local-first developer session dashboard"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="min-h-screen antialiased"><div hidden=""><!--$--><!--/$--></div><main class="relative isolate min-h-screen overflow-hidden md:p-6" style="padding:1.25rem;opacity:0;transform:translateY(15px)"><div class="pointer-events-none absolute inset-0 -z-20 bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.95),transparent_28%),radial-gradient(circle_at_80%_12%,rgba(125,211,252,0.2),transparent_22%),linear-gradient(180deg,#f8fbff_0%,#eef4ff_45%,#edf8f4_100%)]"></div><div class="pointer-events-none absolute inset-0 -z-10 opacity-70"><div class="absolute inset-x-6 top-4 h-64 rounded-full bg-[radial-gradient(circle,rgba(125,211,252,0.16),transparent_60%)] blur-3xl"></div></div><div class="mx-auto grid max-w-[1600px] gap-6"><div data-slot="card" data-size="default" class="group/card flex flex-col gap-4 rounded-xl py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[&gt;img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl relative overflow-hidden border-white/50 bg-white/72 shadow-[0_24px_80px_rgba(15,23,42,0.12)] backdrop-blur-xl"><div style="--border-width:1.5px;--duration:18s;background-image:radial-gradient(transparent,transparent, rgba(34,197,94,0.15),rgba(59,130,246,0.18),rgba(168,85,247,0.16),transparent,transparent);background-size:300% 300%;mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);-webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;padding:var(--border-width)" class="motion-safe:animate-shine pointer-events-none absolute inset-0 size-full rounded-[inherit] will-change-[background-position]"></div><div data-slot="card-content" class="group-data-[size=sm]/card:px-3 relative flex flex-col gap-5 p-5 md:p-6"><div class="flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between"><div class="space-y-4"><div class="flex flex-wrap items-center gap-2 text-xs uppercase tracking-[0.18em] text-slate-500"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-terminal size-3.5" aria-hidden="true"><path d="m7 11 2-2-2-2"></path><path d="M11 13h4"></path><rect width="18" height="18" x="3" y="3" rx="2" ry="2"></rect></svg>Current session</div><div class="space-y-2"><h1 class="text-3xl font-semibold tracking-tight text-slate-950 md:text-4xl">DevDeck</h1><div class="flex flex-wrap items-center gap-2"><span data-slot="badge" data-variant="default" class="group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center overflow-hidden border border-transparent py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&amp;&gt;svg]:pointer-events-none [&amp;&gt;svg]:size-3! [a]:hover:bg-primary/80 gap-1.5 rounded-full px-2.5 bg-sky-500 text-white"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plug-zap size-3" aria-hidden="true"><path d="M6.3 20.3a2.4 2.4 0 0 0 3.4 0L12 18l-6-6-2.3 2.3a2.4 2.4 0 0 0 0 3.4Z"></path><path d="m2 22 3-3"></path><path d="M7.5 13.5 10 11"></path><path d="M10.5 16.5 13 14"></path><path d="m18 3-4 4h6l-4 4"></path></svg>Connecting</span><span data-slot="badge" data-variant="outline" class="group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden border py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&amp;&gt;svg]:pointer-events-none [&amp;&gt;svg]:size-3! [a]:hover:bg-muted [a]:hover:text-muted-foreground rounded-full border-slate-300/70 bg-white/70 px-2.5 text-slate-700">0<!-- --> services</span></div></div></div><div class="flex flex-wrap items-center gap-2"><button style="--spread:90deg;--shimmer-color:#bfdbfe;--radius:100px;--speed:3s;--cut:0.05em;--bg:rgba(255,255,255,0.9)" class="group relative z-0 flex cursor-pointer items-center justify-center overflow-hidden [border-radius:var(--radius)] border border-white/10 py-3 whitespace-nowrap [background:var(--bg)] transform-gpu transition-transform duration-300 ease-in-out active:translate-y-px h-9 px-4 text-sm font-medium text-slate-900" aria-label="Copy debug context"><div class="-z-30 blur-[2px] @container-[size] absolute inset-0 overflow-visible"><div class="animate-shimmer-slide absolute inset-0 aspect-[1] h-[100cqh] rounded-none [mask:none]"><div class="animate-spin-around absolute -inset-full w-auto [translate:0_0] rotate-0 [background:conic-gradient(from_calc(270deg-(var(--spread)*0.5)),transparent_0,var(--shimmer-color)_var(--spread),transparent_var(--spread))]"></div></div></div><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-text size-4" aria-hidden="true"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"></path><path d="M14 2v5a1 1 0 0 0 1 1h5"></path><path d="M10 9H8"></path><path d="M16 13H8"></path><path d="M16 17H8"></path></svg>Copy debug context<div class="absolute inset-0 size-full rounded-2xl px-4 py-1.5 text-sm font-medium shadow-[inset_0_-8px_10px_#ffffff1f] transform-gpu transition-all duration-300 ease-in-out group-hover:shadow-[inset_0_-6px_10px_#ffffff3f] group-active:shadow-[inset_0_-10px_10px_#ffffff3f]"></div><div class="absolute inset-(--cut) -z-20 [border-radius:var(--radius)] [background:var(--bg)]"></div></button><button style="--spread:90deg;--shimmer-color:#dbeafe;--radius:100px;--speed:3s;--cut:0.05em;--bg:rgba(15,23,42,0.96)" class="group relative z-0 flex cursor-pointer items-center justify-center overflow-hidden [border-radius:var(--radius)] border border-white/10 py-3 whitespace-nowrap [background:var(--bg)] transform-gpu transition-transform duration-300 ease-in-out active:translate-y-px h-9 px-4 text-sm font-medium text-white" aria-label="Export session"><div class="-z-30 blur-[2px] @container-[size] absolute inset-0 overflow-visible"><div class="animate-shimmer-slide absolute inset-0 aspect-[1] h-[100cqh] rounded-none [mask:none]"><div class="animate-spin-around absolute -inset-full w-auto [translate:0_0] rotate-0 [background:conic-gradient(from_calc(270deg-(var(--spread)*0.5)),transparent_0,var(--shimmer-color)_var(--spread),transparent_var(--spread))]"></div></div></div><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download size-4" aria-hidden="true"><path d="M12 15V3"></path><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><path d="m7 10 5 5 5-5"></path></svg>Export session<div class="absolute inset-0 size-full rounded-2xl px-4 py-1.5 text-sm font-medium shadow-[inset_0_-8px_10px_#ffffff1f] transform-gpu transition-all duration-300 ease-in-out group-hover:shadow-[inset_0_-6px_10px_#ffffff3f] group-active:shadow-[inset_0_-10px_10px_#ffffff3f]"></div><div class="absolute inset-(--cut) -z-20 [border-radius:var(--radius)] [background:var(--bg)]"></div></button><button type="button" tabindex="0" data-slot="button" aria-label="Stop session" class="group/button inline-flex shrink-0 items-center justify-center border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40 gap-1.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 h-9 rounded-full px-4"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-stop size-4" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><rect x="9" y="9" width="6" height="6" rx="1"></rect></svg>Stop session</button></div></div><div data-orientation="horizontal" role="separator" aria-orientation="horizontal" data-slot="separator" class="shrink-0 data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch bg-slate-200/80"></div><div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-4"><div class="rounded-2xl border px-4 py-3 border-emerald-200/80 bg-emerald-50/70"><div class="flex items-center gap-2 text-xs uppercase tracking-[0.14em] text-slate-500">Running</div><div class="mt-1 text-lg font-semibold text-slate-950">0</div></div><div class="rounded-2xl border px-4 py-3 border-sky-200/80 bg-sky-50/70"><div class="flex items-center gap-2 text-xs uppercase tracking-[0.14em] text-slate-500">Healthy</div><div class="mt-1 text-lg font-semibold text-slate-950">0</div></div><div class="rounded-2xl border px-4 py-3 border-amber-200/80 bg-amber-50/70"><div class="flex items-center gap-2 text-xs uppercase tracking-[0.14em] text-slate-500">Attention</div><div class="mt-1 text-lg font-semibold text-slate-950">0</div></div><div class="rounded-2xl border px-4 py-3 border-slate-200/80 bg-slate-50/70"><div class="flex items-center gap-2 text-xs uppercase tracking-[0.14em] text-slate-500"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-activity size-3.5" aria-hidden="true"><path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2"></path></svg>Last connected</div><div class="mt-1 text-lg font-semibold text-slate-950">Waiting</div></div></div></div></div><section class="grid items-start gap-6 xl:grid-cols-[minmax(15rem,18rem)_minmax(0,1fr)]"><aside class="grid gap-4 xl:sticky xl:top-6"><div data-slot="card" data-size="default" class="group/card flex flex-col gap-4 overflow-hidden py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[&gt;img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl rounded-[1.75rem] border-white/70 bg-white/72 shadow-[0_20px_50px_rgba(15,23,42,0.08)] backdrop-blur-xl"><div data-slot="card-content" class="group-data-[size=sm]/card:px-3 grid gap-3 p-3"><div class="flex items-center justify-between gap-3 px-1"><div><div class="text-sm font-semibold text-slate-950">Services</div><div class="text-xs text-slate-500">Compact rail with selected-row actions</div></div><span data-slot="badge" data-variant="outline" class="group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden border py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&amp;&gt;svg]:pointer-events-none [&amp;&gt;svg]:size-3! border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground rounded-full bg-white/80 px-2.5">0</span></div><button class="flex items-center justify-between rounded-2xl border px-3 py-2 text-left transition-all border-slate-900/10 bg-slate-950 text-white shadow-[0_16px_35px_rgba(15,23,42,0.18)]" type="button"><span class="text-sm font-medium">All services</span><span class="text-xs text-slate-200">Unified</span></button><div class="grid gap-2"><div class="rounded-2xl border border-white/70 bg-white/70 p-3"><div data-slot="skeleton" class="animate-pulse bg-muted h-4 w-24 rounded-full"></div><div data-slot="skeleton" class="animate-pulse bg-muted mt-2 h-3 w-18 rounded-full"></div></div><div class="rounded-2xl border border-white/70 bg-white/70 p-3"><div data-slot="skeleton" class="animate-pulse bg-muted h-4 w-24 rounded-full"></div><div data-slot="skeleton" class="animate-pulse bg-muted mt-2 h-3 w-18 rounded-full"></div></div><div class="rounded-2xl border border-white/70 bg-white/70 p-3"><div data-slot="skeleton" class="animate-pulse bg-muted h-4 w-24 rounded-full"></div><div data-slot="skeleton" class="animate-pulse bg-muted mt-2 h-3 w-18 rounded-full"></div></div><div class="rounded-2xl border border-white/70 bg-white/70 p-3"><div data-slot="skeleton" class="animate-pulse bg-muted h-4 w-24 rounded-full"></div><div data-slot="skeleton" class="animate-pulse bg-muted mt-2 h-3 w-18 rounded-full"></div></div></div></div></div></aside><div class="grid gap-4 xl:min-w-0"><div data-slot="card" data-size="default" class="group/card flex flex-col gap-4 overflow-hidden py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[&gt;img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl rounded-3xl border-white/70 bg-white/72 shadow-[0_20px_50px_rgba(15,23,42,0.08)] backdrop-blur-xl"><div data-slot="card-content" class="group-data-[size=sm]/card:px-3 flex flex-col gap-4 p-4"><div class="flex flex-col gap-3 xl:flex-row xl:items-center"><div class="relative flex-1"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-slate-400" aria-hidden="true"><path d="m21 21-4.34-4.34"></path><circle cx="11" cy="11" r="8"></circle></svg><input id="base-ui-_R_4llpbrb_" data-slot="input" aria-label="Search logs" placeholder="Search current session logs" class="w-full min-w-0 border px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 h-10 rounded-full border-slate-200 bg-white/90 pl-9" value=""/></div><button type="button" tabindex="0" id="base-ui-_R_15lpbrb_" role="combobox" aria-expanded="false" aria-haspopup="listbox" data-slot="select-trigger" data-size="default" aria-label="Filter service" class="flex items-center justify-between gap-1.5 border py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 h-10 w-full rounded-full border-slate-200 bg-white/90 xl:w-56"><span data-slot="select-value" class="flex flex-1 text-left">all</span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down pointer-events-none size-4 text-muted-foreground" aria-hidden="true"><path d="m6 9 6 6 6-6"></path>▼</svg></button><input id="base-ui-_R_15lpbrb_-hidden-input" style="clip-path:inset(50%);overflow:hidden;white-space:nowrap;border:0;padding:0;width:1px;height:1px;margin:-1px;position:fixed;top:0;left:0" tabindex="-1" aria-hidden="true" value="all"/></div><div class="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between"><div class="flex flex-col gap-3 sm:flex-row sm:items-center"><div class="flex items-center gap-2 text-sm text-slate-500"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-funnel size-4" aria-hidden="true"><path d="M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z"></path></svg>Active filters</div><div class="flex flex-wrap gap-2"><span data-slot="badge" data-variant="outline" class="group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden border py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&amp;&gt;svg]:pointer-events-none [&amp;&gt;svg]:size-3! border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground rounded-full bg-white/80 px-2.5">All services</span><span data-slot="badge" data-variant="outline" class="group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden border py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&amp;&gt;svg]:pointer-events-none [&amp;&gt;svg]:size-3! border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground rounded-full bg-white/80 px-2.5">All severities</span><span data-slot="badge" data-variant="outline" class="group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden border py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&amp;&gt;svg]:pointer-events-none [&amp;&gt;svg]:size-3! border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground rounded-full bg-white/80 px-2.5">0<!-- --> visible</span><span data-slot="badge" data-variant="outline" class="group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden border py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&amp;&gt;svg]:pointer-events-none [&amp;&gt;svg]:size-3! border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground rounded-full bg-white/80 px-2.5">Unified tile only</span></div></div><div class="flex flex-wrap items-center gap-2"><div data-orientation="horizontal" aria-orientation="horizontal" role="group" data-slot="toggle-group" data-spacing="2" aria-label="Filter severity" class="group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch" style="--gap:2"><button type="button" data-pressed="" aria-disabled="false" tabindex="-1" aria-pressed="true" data-slot="toggle-group-item" data-variant="default" data-size="default" data-spacing="2" class="shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 bg-transparent h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2">All</button><button type="button" aria-disabled="false" tabindex="-1" aria-pressed="false" data-slot="toggle-group-item" data-variant="default" data-size="default" data-spacing="2" class="shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 bg-transparent h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2">Info</button><button type="button" aria-disabled="false" tabindex="-1" aria-pressed="false" data-slot="toggle-group-item" data-variant="default" data-size="default" data-spacing="2" class="shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 bg-transparent h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2">Warn</button><button type="button" aria-disabled="false" tabindex="-1" aria-pressed="false" data-slot="toggle-group-item" data-variant="default" data-size="default" data-spacing="2" class="shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 bg-transparent h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2">Error</button></div><button type="button" tabindex="0" data-slot="button" class="group/button inline-flex shrink-0 items-center justify-center border bg-clip-padding font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 border-border hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 h-7 gap-1 px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-3.5 rounded-full bg-white/85"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy size-3.5" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>Copy visible logs</button></div></div></div></div><div data-slot="card" data-size="default" class="group/card flex flex-col gap-4 overflow-hidden py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[&gt;img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl rounded-[1.75rem] border-white/70 bg-white/72 shadow-[0_20px_50px_rgba(15,23,42,0.08)] backdrop-blur-xl"><div data-slot="card-content" class="group-data-[size=sm]/card:px-3 grid gap-4 p-4"><div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between"><div><div class="flex items-center gap-2 text-sm font-semibold text-slate-950"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-layout-grid size-4" aria-hidden="true"><rect width="7" height="7" x="3" y="3" rx="1"></rect><rect width="7" height="7" x="14" y="3" rx="1"></rect><rect width="7" height="7" x="14" y="14" rx="1"></rect><rect width="7" height="7" x="3" y="14" rx="1"></rect></svg>Tile workspace</div><div class="mt-1 text-sm text-slate-500">Drag to reorder. Settings persist.</div></div><div class="relative"><button type="button" tabindex="0" data-slot="button" id="add-tile-toggle-button" aria-label="Add tile menu" class="group/button shrink-0 justify-center bg-clip-padding whitespace-nowrap outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 h-8 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 flex items-center gap-2 rounded-full border border-slate-200 bg-white px-3 py-2 text-sm font-medium text-slate-900 shadow-sm transition hover:bg-slate-50"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus size-4" aria-hidden="true"><path d="M5 12h14"></path><path d="M12 5v14"></path></svg>Add tile</button></div></div><div class="grid gap-4 xl:grid-cols-12"><article class="grid gap-3 rounded-[1.75rem] border p-3 backdrop-blur-xl transition-colors duration-300 xl:col-span-12 bg-white/72 border-white/70 shadow-[0_20px_50px_rgba(15,23,42,0.08)] text-slate-950" data-testid="workspace-tile-unified" draggable="true" style="opacity:0;transform:scale(0.95)"><div class="flex flex-wrap items-start justify-between gap-3"><div class="space-y-2"><div class="flex items-center gap-2"><span data-slot="badge" data-variant="default" class="group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden border border-transparent py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&amp;&gt;svg]:pointer-events-none [&amp;&gt;svg]:size-3! [a]:hover:bg-primary/80 rounded-full px-2.5 bg-slate-900/90 text-white hover:bg-slate-900">unified</span><span class="text-xs uppercase tracking-[0.14em] text-slate-500">0<!-- --> service<!-- -->s</span></div><div><div class="text-lg font-semibold text-slate-950">Unified stream</div><div class="text-sm text-slate-500">Shared session filters</div></div></div><div class="flex flex-wrap items-center gap-2"><button type="button" tabindex="0" data-slot="button" aria-label="Drag Unified stream" class="group/button inline-flex shrink-0 items-center justify-center border bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 border-border hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 size-7 in-data-[slot=button-group]:rounded-lg rounded-full bg-white/85"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-grip-vertical size-4" aria-hidden="true"><circle cx="9" cy="12" r="1"></circle><circle cx="9" cy="5" r="1"></circle><circle cx="9" cy="19" r="1"></circle><circle cx="15" cy="12" r="1"></circle><circle cx="15" cy="5" r="1"></circle><circle cx="15" cy="19" r="1"></circle></svg></button><button type="button" tabindex="0" data-slot="button" aria-label="Resize Unified stream" class="group/button inline-flex shrink-0 items-center justify-center border bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 border-border hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 size-7 in-data-[slot=button-group]:rounded-lg rounded-full bg-white/85"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rows3 lucide-rows-3 size-4" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2"></rect><path d="M21 9H3"></path><path d="M21 15H3"></path></svg></button><button type="button" tabindex="0" data-slot="button" aria-label="Recolor Unified stream" class="group/button inline-flex shrink-0 items-center justify-center border bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 border-border hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 size-7 in-data-[slot=button-group]:rounded-lg rounded-full bg-white/85"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-paint-bucket size-4" aria-hidden="true"><path d="M11 7 6 2"></path><path d="M18.992 12H2.041"></path><path d="M21.145 18.38A3.34 3.34 0 0 1 20 16.5a3.3 3.3 0 0 1-1.145 1.88c-.575.46-.855 1.02-.855 1.595A2 2 0 0 0 20 22a2 2 0 0 0 2-2.025c0-.58-.285-1.13-.855-1.595"></path><path d="m8.5 4.5 2.148-2.148a1.205 1.205 0 0 1 1.704 0l7.296 7.296a1.205 1.205 0 0 1 0 1.704l-7.592 7.592a3.615 3.615 0 0 1-5.112 0l-3.888-3.888a3.615 3.615 0 0 1 0-5.112L5.67 7.33"></path></svg></button><button type="button" tabindex="0" data-slot="button" aria-label="Remove Unified stream" class="group/button inline-flex shrink-0 items-center justify-center border bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 border-border hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 size-7 in-data-[slot=button-group]:rounded-lg rounded-full bg-white/85"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x size-4" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg></button></div></div><div data-slot="card" data-size="default" class="group/card flex flex-col gap-4 py-4 text-sm ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[&gt;img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl overflow-hidden rounded-[1.75rem] border border-slate-900/10 bg-[#09111a] text-slate-100 shadow-[0_24px_70px_rgba(15,23,42,0.28)] border-t border-t-slate-700/40"><div class="flex items-center justify-between gap-3 border-b border-white/6 bg-[#0b1623] px-3 py-2"><div class="flex min-w-0 flex-col"><div class="flex items-center gap-2 text-[11px] uppercase tracking-[0.2em] text-slate-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-circle size-3 animate-spin text-sky-200" aria-hidden="true"><path d="M21 12a9 9 0 1 1-6.219-8.56"></path></svg>Unified stream</div><span class="mt-0.5 truncate text-[10px] text-slate-600">Search, severity, and rail selection apply here</span></div><div class="flex items-center gap-2"><span data-slot="badge" data-variant="default" class="group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden border font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&amp;&gt;svg]:pointer-events-none [&amp;&gt;svg]:size-3! [a]:hover:bg-primary/80 rounded-full px-2 py-0 text-[10px] border-sky-400/20 bg-sky-400/12 text-sky-100">connecting</span><span data-slot="badge" data-variant="outline" class="group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden border font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&amp;&gt;svg]:pointer-events-none [&amp;&gt;svg]:size-3! [a]:hover:bg-muted [a]:hover:text-muted-foreground rounded-full border-white/10 bg-white/5 px-2 py-0 text-[10px] text-slate-300">0<!-- --> lines</span></div></div><div class="grid gap-2 p-3"><div class="rounded-md border border-white/6 bg-white/[0.03] p-2"><div data-slot="skeleton" class="animate-pulse rounded-md h-3 w-28 bg-white/8"></div><div data-slot="skeleton" class="animate-pulse rounded-md mt-2 h-3 w-3/4 bg-white/8"></div></div><div class="rounded-md border border-white/6 bg-white/[0.03] p-2"><div data-slot="skeleton" class="animate-pulse rounded-md h-3 w-28 bg-white/8"></div><div data-slot="skeleton" class="animate-pulse rounded-md mt-2 h-3 w-3/4 bg-white/8"></div></div><div class="rounded-md border border-white/6 bg-white/[0.03] p-2"><div data-slot="skeleton" class="animate-pulse rounded-md h-3 w-28 bg-white/8"></div><div data-slot="skeleton" class="animate-pulse rounded-md mt-2 h-3 w-3/4 bg-white/8"></div></div><div class="rounded-md border border-white/6 bg-white/[0.03] p-2"><div data-slot="skeleton" class="animate-pulse rounded-md h-3 w-28 bg-white/8"></div><div data-slot="skeleton" class="animate-pulse rounded-md mt-2 h-3 w-3/4 bg-white/8"></div></div><div class="rounded-md border border-white/6 bg-white/[0.03] p-2"><div data-slot="skeleton" class="animate-pulse rounded-md h-3 w-28 bg-white/8"></div><div data-slot="skeleton" class="animate-pulse rounded-md mt-2 h-3 w-3/4 bg-white/8"></div></div><div class="rounded-md border border-white/6 bg-white/[0.03] p-2"><div data-slot="skeleton" class="animate-pulse rounded-md h-3 w-28 bg-white/8"></div><div data-slot="skeleton" class="animate-pulse rounded-md mt-2 h-3 w-3/4 bg-white/8"></div></div></div></div></article></div></div></div></div></section></div></main><!--$--><!--/$--><section aria-label="Notifications alt+T" tabindex="-1" aria-live="polite" aria-relevant="additions text" aria-atomic="false"></section><script src="/_next/static/chunks/webpack-c708016a143f497e.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[4757,[\"101\",\"static/chunks/101-70b226b829c9d4ae.js\",\"177\",\"static/chunks/app/layout-970af65dc7518514.js\"],\"TooltipProvider\"]\n3:I[9630,[],\"\"]\n4:I[1380,[],\"\"]\n5:I[2437,[\"101\",\"static/chunks/101-70b226b829c9d4ae.js\",\"177\",\"static/chunks/app/layout-970af65dc7518514.js\"],\"Toaster\"]\n6:I[4236,[\"101\",\"static/chunks/101-70b226b829c9d4ae.js\",\"444\",\"static/chunks/444-19cc275570eb4ec1.js\",\"974\",\"static/chunks/app/page-8d1a7ef25da4dec8.js\"],\"DashboardShell\"]\n7:I[887,[],\"OutletBoundary\"]\n8:\"$Sreact.suspense\"\na:I[887,[],\"ViewportBoundary\"]\nc:I[887,[],\"MetadataBoundary\"]\ne:I[5718,[],\"default\",1]\n:HL[\"/_next/static/media/22a5144ee8d83bca-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/5379a39d263cf11e.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/5379a39d263cf11e.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"font-sans __variable_246ccd\",\"children\":[\"$\",\"body\",null,{\"className\":\"min-h-screen antialiased\",\"children\":[\"$\",\"$L2\",null,{\"children\":[[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}],[\"$\",\"$L5\",null,{\"position\":\"top-right\",\"richColors\":true}]]}]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L6\",null,{}],null,[\"$\",\"$L7\",null,{\"children\":[\"$\",\"$8\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@9\"}]}]]}],{},null,false,null]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$La\",null,{\"children\":\"$Lb\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lc\",null,{\"children\":[\"$\",\"$8\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Ld\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$e\",[]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"RB8s1jjgiLhjvp2HZDBhU\"}\n"])</script><script>self.__next_f.push([1,"b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"9:null\nd:[[\"$\",\"title\",\"0\",{\"children\":\"DevDeck\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Local-first developer session dashboard\"}]]\n"])</script></body></html>
@@ -0,0 +1,17 @@
1
+ 1:"$Sreact.fragment"
2
+ 2:I[4757,["101","static/chunks/101-70b226b829c9d4ae.js","177","static/chunks/app/layout-970af65dc7518514.js"],"TooltipProvider"]
3
+ 3:I[9630,[],""]
4
+ 4:I[1380,[],""]
5
+ 5:I[2437,["101","static/chunks/101-70b226b829c9d4ae.js","177","static/chunks/app/layout-970af65dc7518514.js"],"Toaster"]
6
+ 6:I[4236,["101","static/chunks/101-70b226b829c9d4ae.js","444","static/chunks/444-19cc275570eb4ec1.js","974","static/chunks/app/page-8d1a7ef25da4dec8.js"],"DashboardShell"]
7
+ 7:I[887,[],"OutletBoundary"]
8
+ 8:"$Sreact.suspense"
9
+ a:I[887,[],"ViewportBoundary"]
10
+ c:I[887,[],"MetadataBoundary"]
11
+ e:I[5718,[],"default",1]
12
+ :HL["/_next/static/media/22a5144ee8d83bca-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
13
+ :HL["/_next/static/css/5379a39d263cf11e.css","style"]
14
+ 0:{"P":null,"c":["",""],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/5379a39d263cf11e.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"font-sans __variable_246ccd","children":["$","body",null,{"className":"min-h-screen antialiased","children":["$","$L2",null,{"children":[["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}],["$","$L5",null,{"position":"top-right","richColors":true}]]}]}]}]]}],{"children":[["$","$1","c",{"children":[["$","$L6",null,{}],null,["$","$L7",null,{"children":["$","$8",null,{"name":"Next.MetadataOutlet","children":"$@9"}]}]]}],{},null,false,null]},null,false,null],["$","$1","h",{"children":[null,["$","$La",null,{"children":"$Lb"}],["$","div",null,{"hidden":true,"children":["$","$Lc",null,{"children":["$","$8",null,{"name":"Next.Metadata","children":"$Ld"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$e",[]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"RB8s1jjgiLhjvp2HZDBhU"}
15
+ b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
16
+ 9:null
17
+ d:[["$","title","0",{"children":"DevDeck"}],["$","meta","1",{"name":"description","content":"Local-first developer session dashboard"}]]
@@ -0,0 +1,43 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>DevDeck</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ min-height: 100vh;
11
+ display: grid;
12
+ place-items: center;
13
+ background: linear-gradient(180deg, #f7f0e8 0%, #f2f5ef 100%);
14
+ color: #142013;
15
+ font-family: "Iowan Old Style", "Palatino Linotype", serif;
16
+ }
17
+
18
+ main {
19
+ max-width: 36rem;
20
+ padding: 2.5rem;
21
+ border: 1px solid rgba(20, 32, 19, 0.12);
22
+ background: rgba(255, 255, 255, 0.72);
23
+ box-shadow: 0 24px 60px rgba(20, 32, 19, 0.08);
24
+ }
25
+
26
+ h1 {
27
+ margin: 0 0 0.75rem;
28
+ font-size: 2.25rem;
29
+ }
30
+
31
+ p {
32
+ margin: 0;
33
+ line-height: 1.6;
34
+ }
35
+ </style>
36
+ </head>
37
+ <body>
38
+ <main>
39
+ <h1>DevDeck</h1>
40
+ <p>The realtime dashboard build is not ready yet. The local session server is running and ready for the full UI export.</p>
41
+ </main>
42
+ </body>
43
+ </html>
@@ -0,0 +1,5 @@
1
+ import type { SessionSnapshot } from "@devdeck/core";
2
+ import type { AgentLogsResponse } from "./agent-client.js";
3
+ export declare function formatStatus(snapshot: SessionSnapshot): string;
4
+ export declare function formatLogs(result: AgentLogsResponse): string;
5
+ export declare function formatSnapshot(snapshot: SessionSnapshot, tail: number): string;
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import type { CommandIo } from "./commands/init.js";
3
+ export type RunCliOptions = {
4
+ cwd?: string;
5
+ io?: CommandIo;
6
+ fetchImplementation?: typeof fetch;
7
+ };
8
+ export declare function runCli(argv: string[], options?: RunCliOptions): Promise<number>;
package/dist/index.js ADDED
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ var f=class extends Error{code;hint;constructor(t,r,n){super(r),this.code=t,this.hint=n,this.name="DevdeckError",Object.setPrototypeOf(this,new.target.prototype)}},m=class extends f{constructor(t,r,n){super(t,r,n),this.name="ConfigError",Object.setPrototypeOf(this,new.target.prototype)}};import{access as ht,readFile as gt,stat as wt}from"node:fs/promises";import h from"node:path";import{isMap as vt,parseDocument as yt}from"yaml";var N="devdeck.yml";async function R(e=process.cwd()){let t=h.resolve(e);for(;;){let r=h.join(t,N);try{return await ht(r),r}catch{let n=h.dirname(t);if(n===t)return null;t=n}}}async function T(e=process.cwd()){let t=await R(e);if(!t)throw new m("DD-ERR-0001",`Could not find ${N} starting from ${h.resolve(e)}.`,"Run 'devdeck init' to create a starter devdeck.yml file in the current directory.");let r=await gt(t,"utf8"),n=yt(r,{uniqueKeys:!1}),o=$t(n);if(o)throw new m("DD-ERR-0003",`Duplicate service name "${o}" found in ${t}.`,"Ensure all service names under the 'services' key in devdeck.yml are unique.");if(n.errors.length>0)throw new m("DD-ERR-0002",`Invalid YAML in ${t}: ${n.errors[0]?.message??"parse failed"}`,"Fix the YAML syntax errors in devdeck.yml.");let s=n.toJS(),i=h.dirname(t),d=await St(s,i,t);return{path:t,directory:i,config:d}}async function St(e,t,r){if(!I(e))throw new m("DD-ERR-0004",`Expected ${r} to contain a YAML object.`,"Check that devdeck.yml is formatted correctly as a YAML object.");let n=e.project,o=e.services;if(typeof n!="string"||n.trim()==="")throw new m("DD-ERR-0004",`Expected "project" to be a non-empty string in ${r}.`,"Define a non-empty 'project' name string at the top level of devdeck.yml.");if(!I(o)||Object.keys(o).length===0)throw new m("DD-ERR-0004",`Expected "services" to be a non-empty object in ${r}.`,"Define at least one service under the 'services' object in devdeck.yml.");let s={};for(let[i,d]of Object.entries(o))s[i]=await xt(i,d,t,r);return{project:n.trim(),services:s}}async function xt(e,t,r,n){if(!I(t))throw new m("DD-ERR-0004",`Expected service "${e}" to be an object in ${n}.`,`Ensure the configuration for service '${e}' is defined as a YAML map/object.`);let o=t.command,s=t.cwd,i=t.group,d=t.port;if(typeof o!="string"||o.trim()==="")throw new m("DD-ERR-0005",`Expected service "${e}" to define a non-empty "command" in ${n}.`,`Define a 'command' string (e.g. 'npm run dev') for service '${e}'.`);if(typeof s!="string"||s.trim()==="")throw new m("DD-ERR-0006",`Expected service "${e}" to define a non-empty "cwd" in ${n}.`,`Define a 'cwd' string specifying the working directory for service '${e}'.`);let a=h.resolve(r,s),c;try{c=await wt(a)}catch{throw new m("DD-ERR-0007",`Expected service "${e}" cwd to exist: ${a}.`,`Create the working directory at '${a}' or update the 'cwd' path in devdeck.yml.`)}if(!c.isDirectory())throw new m("DD-ERR-0007",`Expected service "${e}" cwd to be a directory: ${a}.`,`Ensure the path '${a}' points to a valid directory, not a file.`);if(i!==void 0&&(typeof i!="string"||i.trim()===""))throw new m("DD-ERR-0008",`Expected service "${e}" group to be a non-empty string in ${n}.`,`Remove or correct the 'group' property for service '${e}'. It must be a non-empty string.`);if(d!==void 0&&(typeof d!="number"||!Number.isInteger(d)||d<=0))throw new m("DD-ERR-0009",`Expected service "${e}" port to be a positive integer in ${n}.`,`Correct the 'port' property for service '${e}'. It must be a positive integer (e.g. 3000).`);return{command:o.trim(),cwd:s,...i===void 0?{}:{group:i.trim()},...d===void 0?{}:{port:d}}}function $t(e){let t=e.get("services",!0);if(!vt(t))return null;let r=new Set;for(let n of t.items){let o=String(n.key);if(r.has(o))return o;r.add(o)}return null}function I(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}async function M(e,t=Et){if(e[0]!=="setup")throw new Error("Usage: devdeck agent setup");t.stdout(`${Dt()}
3
+ `)}function Dt(){return["# Agent DevDeck Setup","","Use Agent DevDeck as the local control plane for your repository.","1. Inspect the repo and identify the services that should start together.","2. Create or update `devdeck.yml` so each service has a command and cwd.","3. Use `devdeck dev` to start everything, then use `devdeck status`, `devdeck logs`, and `devdeck snapshot` for bounded debugging.","","Starter template:","```yaml","project: my-app","services:"," api:"," command: npm run dev"," cwd: ./apps/api"," worker:"," command: npm run worker"," cwd: ./apps/worker","```","","Use `devdeck dev` after the file is ready."].join(`
4
+ `)}var Et={stdout:e=>process.stdout.write(e),stderr:e=>process.stderr.write(e)};import{access as Q}from"node:fs/promises";import E from"node:path";function F(e,t){let r=e.toLowerCase(),n=[...e.matchAll(/https?:\/\/[^\s)]+/g)].map(i=>i[0]),o=[...e.matchAll(/\b:(\d{2,5})\b/g)].map(i=>Number(i[1])).filter(i=>i>0&&i<=65535),s=/^\s*at\s.+/.test(e)||/^\s*[A-Za-z0-9_$]+\s*\(.+\)$/.test(e);return/\b(error|exception|failed|fatal)\b/.test(r)?{severity:"error",urls:n,ports:o,isStackTrace:s}:/\b(warn|warning|deprecated)\b/.test(r)?{severity:"warning",urls:n,ports:o,isStackTrace:s}:{severity:t==="stderr"?"error":"info",urls:n,ports:o,isStackTrace:s}}function H(e){let t=[...new Set(e.logs.flatMap(o=>o.urls))],r=[...new Set(e.logs.flatMap(o=>o.ports))],n=e.services.filter(o=>o.status==="error"||o.status==="exited").map(o=>`${o.name}:${o.status}`);return[`Project: ${e.project}`,`Started: ${e.startedAt}`,`Services: ${e.services.length}`,`Statuses: ${e.services.map(o=>`${o.name}=${o.status}`).join(", ")}`,`Health: ${e.services.map(o=>`${o.name}=${o.health}`).join(", ")}`,`Known URLs: ${t.length>0?t.join(", "):"none"}`,`Known Ports: ${r.length>0?r.join(", "):"none"}`,`Errors: ${n.length>0?n.join(", "):"none"}`].join(`
5
+ `)}function G(e){let t=e.logs.map(r=>`${r.timestamp} [${r.service}] ${r.severity.toUpperCase()} ${r.stream}: ${r.line}`).join(`
6
+ `);return["# DevDeck Session Export","","## Debug Context",H(e),"","## Logs",t,""].join(`
7
+ `)}var v=class{maxLines;#t=[];constructor(t=500){this.maxLines=t}append(t){this.#t.push(t),this.#t.length>this.maxLines&&this.#t.splice(0,this.#t.length-this.maxLines)}snapshot(){return[...this.#t]}clear(){this.#t=[]}};import{spawn as bt}from"node:child_process";var y=class{service;#t=null;#e=new Set;#r=1;#n=0;#o=!1;constructor(t){this.service=t}subscribe(t){return this.#e.add(t),()=>{this.#e.delete(t)}}get isRunning(){return this.#t!==null}get restartCount(){return this.#n}async start(){this.#t||(this.#o=!1,await new Promise((t,r)=>{let n=!1,o=bt(this.service.command,{cwd:this.service.cwd,shell:!0,env:process.env}),s=i=>{this.#t=null,this.emit({type:"error",service:this.service.name,error:i.message,timestamp:new Date().toISOString()}),n||(n=!0,r(i))};o.once("error",s),o.once("spawn",()=>{this.#t=o,this.attachOutput(o),this.attachExit(o),this.emit({type:"start",service:this.service.name,pid:o.pid??-1,timestamp:new Date().toISOString()}),n||(n=!0,t())})}))}async stop(){if(!this.#t)return;this.#o=!0;let t=this.#t;await new Promise(r=>{let n=setTimeout(()=>{t.kill("SIGKILL")},1e3);t.once("exit",()=>{clearTimeout(n),r()}),t.kill("SIGTERM")})}async restart(){this.#n+=1,this.emit({type:"restart",service:this.service.name,count:this.#n,timestamp:new Date().toISOString()}),await this.stop(),await this.start()}attachExit(t){t.once("exit",(r,n)=>{if(this.#t=null,this.#o){this.emit({type:"stop",service:this.service.name,timestamp:new Date().toISOString()});return}this.emit({type:"exit",service:this.service.name,code:r,signal:n,timestamp:new Date().toISOString()})})}attachOutput(t){let r={value:""},n={value:""};t.stdout.on("data",o=>{r.value=this.flushLines(r.value,o,"stdout")}),t.stderr.on("data",o=>{n.value=this.flushLines(n.value,o,"stderr")})}flushLines(t,r,n){let s=`${t}${r.toString("utf8")}`.split(/\r?\n/),i=s.pop()??"";for(let d of s){let a=F(d,n);this.emit({type:"output",log:{id:this.#r++,service:this.service.name,isStackTrace:a.isStackTrace,line:d,ports:a.ports,severity:a.severity,stream:n,timestamp:new Date().toISOString(),urls:a.urls}})}return i}emit(t){for(let r of this.#e)r(t)}};var S=class{project;startedAt;#t;#e=new Set;#r=new Map;#n=new Map;constructor(t){this.project=t.project,this.startedAt=new Date().toISOString(),this.#t=new v(t.maxLogLines??500);for(let r of t.services){let n=new y(r),o={...r,health:"unknown",status:"idle",pid:null,restartCount:0,lastExitCode:null,lastSignal:null,lastError:null};n.subscribe(s=>this.handleRunnerEvent(s)),this.#r.set(r.name,n),this.#n.set(r.name,o)}}subscribe(t){return this.#e.add(t),()=>{this.#e.delete(t)}}getSnapshot(){return{project:this.project,startedAt:this.startedAt,services:[...this.#n.values()],logs:this.#t.snapshot()}}async startAll(){for(let[t,r]of this.#r){this.updateService(t,{status:"starting",lastError:null});try{await r.start()}catch(n){this.updateService(t,{status:"error",lastError:n.message})}}}async stopAll(){for(let[t,r]of this.#r)this.updateService(t,{status:"stopping"}),await r.stop()}async restartService(t){let r=this.getRunner(t);this.updateService(t,{status:"stopping"}),await r.restart()}async startService(t){let r=this.getRunner(t);this.updateService(t,{status:"starting",lastError:null});try{await r.start()}catch(n){this.updateService(t,{status:"error",lastError:n.message})}}async stopService(t){let r=this.getRunner(t);this.updateService(t,{status:"stopping"}),await r.stop()}setServiceHealth(t,r){this.updateService(t,{health:r})}handleRunnerEvent(t){if(t.type==="start"){this.updateService(t.service,{status:"running",pid:t.pid,lastError:null});return}if(t.type==="output"){this.#t.append(t.log),this.emit({type:"log",log:t.log});return}if(t.type==="exit"){this.updateService(t.service,{status:"exited",pid:null,lastExitCode:t.code,lastSignal:t.signal});return}if(t.type==="error"){this.updateService(t.service,{status:"error",lastError:t.error});return}if(t.type==="restart"){this.updateService(t.service,{restartCount:t.count});return}t.type==="stop"&&this.updateService(t.service,{status:"stopped",pid:null})}getRunner(t){let r=this.#r.get(t);if(!r)throw new Error(`Unknown service: ${t}`);return r}updateService(t,r){let n=this.#n.get(t);if(!n||!Object.entries(r).some(([i,d])=>n[i]!==d))return;let s={...n,...r};this.#n.set(t,s),this.emit({type:"service",service:s})}emit(t){for(let r of this.#e)r(t)}};import{createServer as Mt}from"node:http";import{readFile as kt}from"node:fs/promises";import It from"node:path";async function _(e,t){let r=t==="/"?"index.html":t.replace(/^\//,""),n=It.join(e,r);return{body:await kt(n),contentType:Rt(n)}}function Rt(e){return e.endsWith(".html")?"text/html; charset=utf-8":e.endsWith(".js")?"application/javascript; charset=utf-8":e.endsWith(".css")?"text/css; charset=utf-8":e.endsWith(".json")?"application/json; charset=utf-8":"application/octet-stream"}function J(e,t){let r=e.logs.filter(o=>jt(o,t)),n=r.slice(-t.tail);return{project:e.project,filters:{service:t.service,severity:t.severity,grep:t.grep,tail:t.tail},totalMatched:r.length,returned:n.length,logs:n}}function jt(e,t){return t.service&&e.service!==t.service||t.severity&&e.severity!==t.severity?!1:t.grep?e.line.toLowerCase().includes(t.grep.toLowerCase()):!0}async function q(e,t,r){let n=new URL(e.url??"/","http://127.0.0.1");if(e.method==="GET"&&n.pathname==="/health"){t.writeHead(200,{"content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify({ok:!0,project:r.session.getSnapshot().project,services:r.session.getSnapshot().services}));return}if(e.method==="POST"&&n.pathname==="/api/actions"){let o=await At(e),s=JSON.parse(o),i=await r.onAction(s);t.writeHead(i.ok?200:400,{"content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify(i));return}if(e.method==="GET"&&n.pathname==="/api/export"){t.writeHead(200,{"content-type":"text/plain; charset=utf-8","content-disposition":'attachment; filename="devdeck-session.txt"'}),t.end(G(r.session.getSnapshot()));return}if(e.method==="GET"&&n.pathname==="/api/snapshot"){j(t,200,r.session.getSnapshot());return}if(e.method==="GET"&&n.pathname==="/api/logs"){try{let o=Lt(n.searchParams);j(t,200,J(r.session.getSnapshot(),o))}catch(o){j(t,400,{ok:!1,error:o instanceof Error?o.message:"Invalid log query"})}return}try{let o=await _(r.dashboardAssetsDirectory,n.pathname);t.writeHead(200,{"content-type":o.contentType}),t.end(o.body)}catch{t.writeHead(404,{"content-type":"text/plain; charset=utf-8"}),t.end("Not found")}}async function At(e){let t=[];for await(let r of e)t.push(Buffer.isBuffer(r)?r:Buffer.from(r));return Buffer.concat(t).toString("utf8")}function Lt(e){let t=x(e.get("service")),r=x(e.get("grep")),n=Ot(x(e.get("severity"))),o=Pt(x(e.get("tail")));return{service:t,grep:r,severity:n,tail:o}}function Ot(e){if(e){if(e==="info"||e==="warning"||e==="error")return e;throw new Error("Invalid severity. Expected info, warning, or error.")}}function Pt(e){if(!e)return 80;let t=Number.parseInt(e,10);if(!Number.isInteger(t)||t<1)throw new Error("Invalid tail. Expected a positive integer.");return t}function x(e){if(e)return e}function j(e,t,r){e.writeHead(t,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify(r))}import Ut from"node:net";function W(e){let t=setInterval(()=>{B(e)},1e3);return B(e),{stop(){clearInterval(t)}}}async function B(e){let t=e.getSnapshot();await Promise.all(t.services.map(async r=>{if(!r.port||r.status!=="running"){e.setServiceHealth(r.name,"unknown");return}let n=await Nt(r.port);e.setServiceHealth(r.name,n?"healthy":"unreachable")}))}function Nt(e){return new Promise(t=>{let r=Ut.connect({host:"127.0.0.1",port:e}),n=setTimeout(()=>{r.destroy(),t(!1)},300);r.once("connect",()=>{clearTimeout(n),r.end(),t(!0)}),r.once("error",()=>{clearTimeout(n),t(!1)})})}import{WebSocketServer as Tt}from"ws";var $=class{#t;#e=new Set;#r;constructor(t,r){this.#r=r,this.#t=new Tt({server:t,path:"/ws"}),this.#t.on("connection",n=>{this.#e.add(n),n.send(JSON.stringify({type:"snapshot",snapshot:this.#r()})),n.once("close",()=>{this.#e.delete(n)})})}broadcastEvent(t){this.broadcast({type:"event",event:t})}broadcastActionResult(t){this.broadcast({type:"action-result",...t})}close(){for(let t of this.#e)t.close();return new Promise((t,r)=>{this.#t.close(n=>{if(n){r(n);return}t()})})}broadcast(t){let r=JSON.stringify(t);for(let n of this.#e)n.send(r)}};function Y(e){let t=e.host??"127.0.0.1",r=e.port??4545,n=Mt((a,c)=>{q(a,c,{dashboardAssetsDirectory:e.dashboardAssetsDirectory,session:e.session,onAction:u=>d(u)})}),o=new $(n,()=>e.session.getSnapshot()),s=W(e.session),i=e.session.subscribe(a=>{o.broadcastEvent(a)});async function d(a){try{if(a.action==="start"){if(!a.serviceName)throw new Error("Missing serviceName for start action.");await e.session.startService(a.serviceName)}if(a.action==="stop"){if(!a.serviceName)throw new Error("Missing serviceName for stop action.");await e.session.stopService(a.serviceName)}if(a.action==="restart"){if(!a.serviceName)throw new Error("Missing serviceName for restart action.");await e.session.restartService(a.serviceName)}return a.action==="stop-session"&&await e.onStopSession?.(),o.broadcastActionResult({action:a.action,ok:!0,serviceName:a.serviceName}),{ok:!0}}catch(c){let u=c instanceof Error?c.message:"Action failed";return o.broadcastActionResult({action:a.action,ok:!1,serviceName:a.serviceName,error:u}),{ok:!1,error:u}}}return{async start(){await new Promise((u,l)=>{n.once("error",l),n.listen(r,t,()=>{n.off("error",l),u()})});let a=n.address(),c=typeof a=="object"&&a!==null?a.port:r;return{url:`http://${t}:${c}`,port:c}},async stop(){i(),s.stop(),await o.close(),await Ft(n)}}}function Ft(e){return new Promise((t,r)=>{e.close(n=>{if(n){r(n);return}t()})})}import{mkdir as Ht,readFile as Gt,rm as _t,writeFile as Jt}from"node:fs/promises";import A from"node:path";var K="http://127.0.0.1:4545";async function z(e){let t=w(e.cwd);await Ht(A.dirname(t),{recursive:!0}),await Jt(t,JSON.stringify(e.session,null,2),"utf8")}async function D(e){let t=w(e),r=await Gt(t,"utf8");return JSON.parse(r)}async function L(e){await _t(w(e),{force:!0})}async function X(e){if(e.url)return e.url;try{return(await D(e.cwd)).url}catch{return K}}function w(e){return A.join(A.resolve(e??process.cwd()),".devdeck/session.json")}async function V(e={}){let t=e.io??qt,r=await T(e.cwd),n=Object.entries(r.config.services).map(([c,u])=>Bt(c,u,r.directory)),o=new S({project:r.config.project,services:n}),s=await Yt(),i=zt(),d=Y({dashboardAssetsDirectory:s,port:e.port,session:o,onStopSession:async()=>{i.request("dashboard")}}),a;try{a=await d.start()}catch(c){let u=c instanceof Error?c.message:String(c);throw u.includes("EADDRINUSE")?new f("DD-ERR-0010",`Port ${e.port??4545} is already in use.`,"Specify a different port using '--port <number>', or stop the process currently running on that port."):new f("DD-ERR-0015",`Failed to start session server: ${u}`,"Ensure you have network permissions and the port is free.")}await z({cwd:r.directory,session:{version:1,project:r.config.project,configPath:r.path,url:a.url,port:a.port,pid:process.pid,startedAt:o.startedAt}}),await e.onServerStarted?.(),o.subscribe(c=>Wt(c,t)),t.stdout(`Project: ${r.config.project}
8
+ `),t.stdout(`Config: ${r.path}
9
+ `),t.stdout(`Dashboard: ${a.url}
10
+ `),t.stdout(`Starting ${n.length} service${n.length===1?"":"s"}
11
+ `);for(let c of n){let u=[`- ${c.name}: ${c.command}`,`cwd=${c.cwd}`];c.port!==void 0&&u.push(`port=${c.port}`),t.stdout(`${u.join(" | ")}
12
+ `)}if(await o.startAll(),e.holdUntilSignal===!1){await new Promise(c=>setTimeout(c,100)),await o.stopAll(),await d.stop(),await L(r.directory);return}t.stdout(`Press Ctrl+C to stop DevDeck.
13
+ `),await Kt(t,i),await o.stopAll(),await d.stop(),await L(r.directory)}var qt={stdout:e=>process.stdout.write(e),stderr:e=>process.stderr.write(e)};function Bt(e,t,r){let n={name:e,command:t.command,cwd:E.resolve(r,t.cwd),port:t.port};return t.group!==void 0&&Object.assign(n,{group:t.group}),n}function Wt(e,t){if(e.type==="log"){t.stdout(`[${e.log.service}:${e.log.stream}] ${e.log.line}
14
+ `);return}t.stdout(`[${e.service.name}] Status changed: ${e.service.status.toUpperCase()}
15
+ `),e.service.status==="error"&&e.service.lastError&&(t.stderr(`[DD-ERR-0011] Service '${e.service.name}' failed to run: ${e.service.lastError}
16
+ `),t.stderr(`Hint: Check config command syntax in devdeck.yml, verify dependencies are installed, or run 'devdeck logs ${e.service.name}' for full process output.
17
+ `)),e.service.status==="exited"&&e.service.lastExitCode!==null&&e.service.lastExitCode!==0&&(t.stderr(`[DD-ERR-0011] Service '${e.service.name}' exited with non-zero code ${e.service.lastExitCode}
18
+ `),t.stderr(`Hint: Run 'devdeck logs ${e.service.name}' to see why the process crashed.
19
+ `))}async function Yt(){let e=E.resolve(new URL("../dashboard",import.meta.url).pathname),t=E.resolve(new URL("../../../../apps/dashboard/out",import.meta.url).pathname);try{return await Q(e),e}catch{try{return await Q(t),t}catch{return E.resolve(new URL("../../../../apps/dashboard/static",import.meta.url).pathname)}}}async function Kt(e,t){await new Promise(r=>{let n=i=>{process.off("SIGINT",o),process.off("SIGTERM",s),e.stdout(`Stopping DevDeck (${i})...
20
+ `),r()},o=()=>{n("SIGINT")},s=()=>{n("SIGTERM")};process.on("SIGINT",o),process.on("SIGTERM",s),t.onStop(i=>{n(i)})})}function zt(){let e=new Set;return{onStop(t){e.add(t)},request(t){for(let r of e)r(t)}}}import{spawn as Xt}from"node:child_process";import{access as Qt,mkdir as Vt}from"node:fs/promises";import{openSync as Z}from"node:fs";import b from"node:path";async function tt(e={}){let t=b.resolve(e.cwd??process.cwd()),r=e.io??Zt,n=t;try{let p=await R(t);p&&(n=b.dirname(p))}catch(p){r.stderr(`[Warning] Failed to resolve config path: ${p instanceof Error?p.message:String(p)}
21
+ `)}try{let p=await D(n);if(p.pid)try{process.kill(p.pid,0),r.stderr(`[DD-ERR-0013] DevDeck is already running at PID ${p.pid} (Dashboard: ${p.url}).
22
+ `),r.stderr(`Hint: Run 'devdeck stop' to stop the current session first.
23
+ `),process.exit(1)}catch{}}catch{}let o=b.join(n,".devdeck");await Vt(o,{recursive:!0});let s=b.join(o,"devdeck.log"),i=Z(s,"a"),d=Z(s,"a");r.stdout(`Starting DevDeck in the background...
24
+ `);let a=["dev"];e.port!==void 0&&a.push("--port",String(e.port)),Xt(process.execPath,[process.argv[1],...a],{detached:!0,stdio:["ignore",i,d],cwd:t,env:{...process.env}}).unref();let u=w(n),l=0,lt=30;for(;l<lt;){await new Promise(p=>setTimeout(p,100));try{await Qt(u);let p=await D(n);if(p.url&&p.pid){r.stdout(`DevDeck started successfully in the background!
25
+ `),r.stdout(`PID: ${p.pid}
26
+ `),r.stdout(`Dashboard: ${p.url}
27
+ `),r.stdout(`Logs: ${s}
28
+ `);return}}catch{}l+=1}throw new f("DD-ERR-0014","DevDeck background process started but failed to write session state within 3 seconds.",`Check background logs at ${s} for details.`)}var Zt={stdout:e=>process.stdout.write(e),stderr:e=>process.stderr.write(e)};import{access as te,writeFile as ee}from"node:fs/promises";import et from"node:path";async function rt(e={}){let t=et.resolve(e.cwd??process.cwd()),r=e.io??ne,n=et.join(t,"devdeck.yml");try{throw await te(n),new Error(`devdeck.yml already exists at ${n}.`)}catch(o){if(o.code!=="ENOENT")throw o}await ee(n,re(),"utf8"),r.stdout(`Created ${n}
29
+ `)}function re(){return["project: my-app","services:"," web:"," command: npm run dev"," cwd: ./frontend"," port: 3000",""].join(`
30
+ `)}var ne={stdout:e=>process.stdout.write(e),stderr:e=>process.stderr.write(e)};async function O(e={}){return U("/api/snapshot",e)}async function nt(e,t={}){let r=new URLSearchParams;e.service&&r.set("service",e.service),e.tail!==void 0&&r.set("tail",String(e.tail)),e.severity&&r.set("severity",e.severity),e.grep&&r.set("grep",e.grep);let n=r.size>0?`?${r.toString()}`:"";return U(`/api/logs${n}`,t)}async function P(e,t={}){return U("/api/actions",{...t,method:"POST",body:e})}async function U(e,t={}){let r=await X({cwd:t.cwd,url:t.url}),n=t.fetchImplementation??fetch,o=new URL(e,oe(r)),s;try{s=await n(o,{method:t.method??"GET",headers:t.body===void 0?void 0:{"content-type":"application/json"},body:t.body===void 0?void 0:JSON.stringify(t.body)})}catch{throw new f("DD-ERR-0012",`Unable to reach DevDeck session server at ${r}.`,"Start the session first using 'devdeck start' or 'devdeck dev' in your project root, or verify the '--url' parameter.")}if(!s.ok){let i=await se(s);throw new Error(i)}return await s.json()}function oe(e){return e.endsWith("/")?e:`${e}/`}async function se(e){try{let r=await e.json();if(r.error)return r.error}catch{}return await e.text()||`Agent DevDeck request failed (${e.status})`}function ot(e){return`${[`Project: ${e.project}`,`Started: ${e.startedAt}`,"Services:",...e.services.map(r=>`- ${r.name} | status=${r.status} | health=${r.health} | pid=${r.pid??"none"} | restarts=${r.restartCount}`)].join(`
31
+ `)}
32
+ `}function st(e){let t=[`Project: ${e.project}`,`Matched: ${e.totalMatched}`,`Returned: ${e.returned}`],r=e.logs.map(n=>`${n.timestamp} ${n.service} ${n.severity.toUpperCase()} ${n.stream} ${n.line}`);return`${[...t,...r].join(`
33
+ `)}
34
+ `}function it(e,t){let r=e.logs.slice(-t);return["# Agent DevDeck Snapshot","",`Project: ${e.project}`,`Started: ${e.startedAt}`,"","## Services",...e.services.map(o=>`- ${o.name}: status=${o.status}, health=${o.health}, restarts=${o.restartCount}, error=${o.lastError??"none"}`),"","## Logs",...r.map(o=>`- ${o.timestamp} [${o.service}] ${o.severity.toUpperCase()} ${o.line}`),""].join(`
35
+ `)}async function at(e,t={}){let r=k(e),n=await O({cwd:t.cwd,fetchImplementation:t.fetchImplementation,url:r.url});g(t.io,r.json?JSON.stringify(n,null,2):ot(n))}async function ct(e,t={}){let r,n=80,o,s,i=!1,d;for(let c=0;c<e.length;c+=1){let u=e[c];if(u){if(!u.startsWith("--")&&r===void 0){r=u;continue}if(u==="--tail"){n=mt(e[c+1],"--tail"),c+=1;continue}if(u==="--severity"){let l=e[c+1];if(l!=="info"&&l!=="warning"&&l!=="error")throw new Error("Invalid --severity value. Expected info, warning, or error.");o=l,c+=1;continue}if(u==="--grep"){s=C(e[c+1],"--grep"),c+=1;continue}if(u==="--json"){i=!0;continue}if(u==="--url"){d=C(e[c+1],"--url"),c+=1;continue}throw new Error(`Unknown logs option: ${u}`)}}let a=await nt({service:r,tail:n,severity:o,grep:s},{cwd:t.cwd,fetchImplementation:t.fetchImplementation,url:d});g(t.io,i?JSON.stringify(a,null,2):st(a))}async function dt(e,t={}){let r=k(e),n=ie(e),o=await O({cwd:t.cwd,fetchImplementation:t.fetchImplementation,url:r.url}),s={...o,logs:o.logs.slice(-n)};g(t.io,r.json?JSON.stringify(s,null,2):it(o,n))}async function ut(e,t={}){let r=k(e);try{await P({action:"stop-session"},{cwd:t.cwd,fetchImplementation:t.fetchImplementation,url:r.url}),g(t.io,`Requested stop-session
36
+ `)}catch(n){if(n instanceof f&&n.code==="DD-ERR-0012"){g(t.io,`DevDeck session is already stopped.
37
+ `);return}throw n}}async function pt(e,t={}){let r=e[0],n=e[1],o=k(e.slice(2));if(r!=="start"&&r!=="stop"&&r!=="restart")throw new Error("Usage: devdeck service <start|stop|restart> <name> [--url URL]");if(!n)throw new Error("Usage: devdeck service <start|stop|restart> <name> [--url URL]");await P({action:r,serviceName:n},{cwd:t.cwd,fetchImplementation:t.fetchImplementation,url:o.url}),g(t.io,`Requested ${r} for ${n}
38
+ `)}function k(e){let t=!1,r;for(let n=0;n<e.length;n+=1){let o=e[n];if(o==="--json"){t=!0;continue}if(o==="--url"){r=C(e[n+1],"--url"),n+=1;continue}if(o==="--tail"){n+=1;continue}throw new Error(`Unknown option: ${o}`)}return{json:t,url:r}}function ie(e){for(let t=0;t<e.length;t+=1)if(e[t]==="--tail")return mt(e[t+1],"--tail");return 120}function mt(e,t){let r=Number.parseInt(C(e,t),10);if(!Number.isInteger(r)||r<1)throw new Error(`Invalid ${t} value. Expected a positive integer.`);return r}function C(e,t){if(!e)throw new Error(`Missing value for ${t}.`);return e}function g(e,t){(e??ae).stdout(t)}var ae={stdout:e=>process.stdout.write(e),stderr:e=>process.stderr.write(e)};async function ce(e,t={}){let r=e[0],n=t.io??de;if(!r)return n.stderr(`Usage: devdeck <init|dev|start|agent|status|logs|snapshot|stop|service>
39
+ `),1;try{if(r==="init")return await rt({cwd:t.cwd,io:n}),0;if(r==="dev"){let o=ft(e.slice(1));return await V({cwd:t.cwd,io:n,port:o.port}),0}if(r==="start"){let o=ft(e.slice(1));return await tt({cwd:t.cwd,io:n,port:o.port}),0}return r==="agent"?(await M(e.slice(1),n),0):r==="status"?(await at(e.slice(1),{cwd:t.cwd,io:n,fetchImplementation:t.fetchImplementation}),0):r==="logs"?(await ct(e.slice(1),{cwd:t.cwd,io:n,fetchImplementation:t.fetchImplementation}),0):r==="snapshot"?(await dt(e.slice(1),{cwd:t.cwd,io:n,fetchImplementation:t.fetchImplementation}),0):r==="stop"?(await ut(e.slice(1),{cwd:t.cwd,io:n,fetchImplementation:t.fetchImplementation}),0):r==="service"?(await pt(e.slice(1),{cwd:t.cwd,io:n,fetchImplementation:t.fetchImplementation}),0):(n.stderr(`Unknown command: ${r}
40
+ `),n.stderr(`Usage: devdeck <init|dev|start|agent|status|logs|snapshot|stop|service>
41
+ `),1)}catch(o){return o instanceof f?(n.stderr(`[${o.code}] ${o.message}
42
+ `),o.hint&&n.stderr(`Hint: ${o.hint}
43
+ `),1):o instanceof Error?(n.stderr(`[DD-ERR-9999] Unexpected error: ${o.message}
44
+ `),1):(n.stderr(`[DD-ERR-9999] Unexpected error
45
+ `),1)}}function ft(e){let t;for(let r=0;r<e.length;r+=1){let n=e[r];if(n==="--port"){let o=e[r+1];if(!o)throw new Error("Missing value for --port.");let s=Number.parseInt(o,10);if(!Number.isInteger(s)||s<1)throw new Error("Invalid --port value. Expected a positive integer.");t=s,r+=1;continue}throw new Error(`Unknown dev option: ${n}`)}return{port:t}}var de={stdout:e=>process.stdout.write(e),stderr:e=>process.stderr.write(e)};if(import.meta.url===`file://${process.argv[1]}`){let e=await ce(process.argv.slice(2));process.exit(e)}export{ce as runCli};
@@ -0,0 +1,21 @@
1
+ export declare const DEFAULT_SESSION_URL = "http://127.0.0.1:4545";
2
+ export type RuntimeSessionState = {
3
+ version: 1;
4
+ project: string;
5
+ configPath: string;
6
+ url: string;
7
+ port: number;
8
+ pid: number;
9
+ startedAt: string;
10
+ };
11
+ export declare function writeSessionState(options: {
12
+ cwd?: string;
13
+ session: RuntimeSessionState;
14
+ }): Promise<void>;
15
+ export declare function readSessionState(cwd?: string): Promise<RuntimeSessionState>;
16
+ export declare function clearSessionState(cwd?: string): Promise<void>;
17
+ export declare function resolveSessionBaseUrl(options: {
18
+ cwd?: string;
19
+ url?: string;
20
+ }): Promise<string>;
21
+ export declare function resolveSessionStatePath(cwd?: string): string;
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@hemangdoshi/devdeck",
3
+ "version": "1.2.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "devdeck": "dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md",
14
+ "CHANGELOG.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "node build.js",
18
+ "test": "vitest run",
19
+ "lint": "tsc -p tsconfig.json --noEmit"
20
+ },
21
+ "dependencies": {
22
+ "ws": "^8.18.3",
23
+ "yaml": "^2.8.1"
24
+ }
25
+ }