ocwatch 0.5.0 → 0.6.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.
@@ -0,0 +1 @@
1
+ @keyframes pulse-bg{0%{background-color:transparent}50%{background-color:#58a6ff1a}to{background-color:transparent}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.animate-pulse-bg{animation:pulse-bg .3s ease-out forwards}.animate-fade-in{animation:fade-in .2s ease-out forwards}@keyframes badge-glow{0%,to{box-shadow:inset 0 1px #ffffff26,0 1px 2px #0003,0 0 4px var(--badge-color, rgba(88, 166, 255, .3))}50%{box-shadow:inset 0 1px #ffffff26,0 1px 2px #0003,0 0 16px var(--badge-color, rgba(88, 166, 255, .6))}}.animate-badge-glow{animation:badge-glow 2s ease-in-out infinite}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.animate-shimmer{background:linear-gradient(90deg,#161b22 25%,#30363d,#161b22 75%);background-size:200% 100%;animation:shimmer 1.5s infinite}@keyframes session-update-pulse{0%{background-color:#58a6ff14}to{background-color:transparent}}.animate-session-update{animation:session-update-pulse .4s ease-out forwards}@keyframes attention-glow{0%,to{box-shadow:inset 2px 0 #d2992299}50%{box-shadow:inset 2px 0 #d29922}}.animate-attention{animation:attention-glow 2s ease-in-out infinite}@keyframes waiting-user-row-pulse{0%,to{background-color:#d299220f}50%{background-color:#d299221f}}.animate-waiting-user-row{animation:waiting-user-row-pulse 2s ease-in-out infinite}@keyframes waiting-user-icon-pulse{0%,to{transform:scale(1);opacity:.8}50%{transform:scale(1.15);opacity:1}}.animate-waiting-user-icon{animation:waiting-user-icon-pulse 1.5s ease-in-out infinite}@keyframes node-pulse{0%,to{box-shadow:0 0 #58a6ff00;border-color:#58a6ff4d}50%{box-shadow:0 0 8px #58a6ff33;border-color:#58a6ff99}}.animate-node-pulse{animation:node-pulse 2s ease-in-out infinite}.edge-particle{filter:drop-shadow(0 0 2px #58a6ff)}.edge-particle-return{filter:drop-shadow(0 0 2px #22c55e)}@keyframes graph-edge-flow{0%{stroke-dashoffset:0}to{stroke-dashoffset:-36}}@keyframes graph-edge-return{0%{stroke-dashoffset:0;opacity:1}to{stroke-dashoffset:18;opacity:.8}}.graph-edge-flow{animation:graph-edge-flow 1.2s linear infinite}.graph-edge-return{animation:graph-edge-return 1.1s ease-out infinite}@media(prefers-reduced-motion:reduce){.animate-badge-glow,.animate-node-pulse,.graph-edge-flow,.graph-edge-return{animation:none!important}}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}body{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.static{position:static}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.bottom-4{bottom:1rem}.left-0{left:0}.right-0{right:0}.right-4{right:1rem}.top-full{top:100%}.z-10{z-index:10}.z-50{z-index:50}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-auto{margin-top:auto}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.\!h-2{height:.5rem!important}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.max-h-\[200px\]{max-height:200px}.max-h-\[30vh\]{max-height:30vh}.max-h-\[320px\]{max-height:320px}.max-h-\[45\%\]{max-height:45%}.min-h-0{min-height:0px}.min-h-\[1\.5em\]{min-height:1.5em}.min-h-\[60px\]{min-height:60px}.\!w-2{width:.5rem!important}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-1\/4{width:25%}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\/3{width:66.666667%}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-8{width:2rem}.w-\[280px\]{width:280px}.w-\[300px\]{width:300px}.w-\[320px\]{width:320px}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[300px\]{min-width:300px}.max-w-2xl{max-width:42rem}.max-w-\[80px\]{max-width:80px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.origin-top-right{transform-origin:top right}.-translate-y-1{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-\[0\.98\]{--tw-scale-x: .98;--tw-scale-y: .98;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.rounded-r-full{border-top-right-radius:9999px;border-bottom-right-radius:9999px}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-accent{--tw-border-opacity: 1;border-color:rgb(88 166 255 / var(--tw-border-opacity, 1))}.border-border{--tw-border-opacity: 1;border-color:rgb(48 54 61 / var(--tw-border-opacity, 1))}.border-border\/30{border-color:#30363d4d}.border-border\/50{border-color:#30363d80}.border-error\/20{border-color:#f8514933}.border-error\/50{border-color:#f8514980}.border-warning\/20{border-color:#d2992233}.border-l-accent{--tw-border-opacity: 1;border-left-color:rgb(88 166 255 / var(--tw-border-opacity, 1))}.border-l-success{--tw-border-opacity: 1;border-left-color:rgb(35 134 54 / var(--tw-border-opacity, 1))}.border-l-transparent{border-left-color:transparent}.border-l-warning{--tw-border-opacity: 1;border-left-color:rgb(210 153 34 / var(--tw-border-opacity, 1))}.\!bg-border{--tw-bg-opacity: 1 !important;background-color:rgb(48 54 61 / var(--tw-bg-opacity, 1))!important}.bg-\[\#0d1117\]{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1))}.bg-\[\#0d1117\]\/30{background-color:#0d11174d}.bg-accent{--tw-bg-opacity: 1;background-color:rgb(88 166 255 / var(--tw-bg-opacity, 1))}.bg-accent\/10{background-color:#58a6ff1a}.bg-accent\/20{background-color:#58a6ff33}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-background{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1))}.bg-background\/80{background-color:#0d1117cc}.bg-black\/10{background-color:#0000001a}.bg-black\/20{background-color:#0003}.bg-black\/30{background-color:#0000004d}.bg-border{--tw-bg-opacity: 1;background-color:rgb(48 54 61 / var(--tw-bg-opacity, 1))}.bg-error\/10{background-color:#f851491a}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-purple-400\/50{background-color:#c084fc80}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-success{--tw-bg-opacity: 1;background-color:rgb(35 134 54 / var(--tw-bg-opacity, 1))}.bg-surface{--tw-bg-opacity: 1;background-color:rgb(22 27 34 / var(--tw-bg-opacity, 1))}.bg-surface\/50{background-color:#161b2280}.bg-surface\/95{background-color:#161b22f2}.bg-warning\/10{background-color:#d299221a}.bg-white\/5{background-color:#ffffff0d}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-3{padding-bottom:.75rem}.pl-3{padding-left:.75rem}.pl-6{padding-left:1.5rem}.pr-1{padding-right:.25rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-1\.5{padding-top:.375rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-\[\#58a6ff\],.text-accent{--tw-text-opacity: 1;color:rgb(88 166 255 / var(--tw-text-opacity, 1))}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-amber-500{--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.text-error{--tw-text-opacity: 1;color:rgb(248 81 73 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-orange-400{--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-success{--tw-text-opacity: 1;color:rgb(35 134 54 / var(--tw-text-opacity, 1))}.text-text-primary{--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1))}.text-text-secondary{--tw-text-opacity: 1;color:rgb(139 148 158 / var(--tw-text-opacity, 1))}.text-warning{--tw-text-opacity: 1;color:rgb(210 153 34 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.line-through{text-decoration-line:line-through}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-35{opacity:.35}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_18px_rgba\(88\,166\,255\,0\.18\)\]{--tw-shadow: 0 0 18px rgba(88,166,255,.18);--tw-shadow-colored: 0 0 18px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-accent{--tw-ring-opacity: 1;--tw-ring-color: rgb(88 166 255 / var(--tw-ring-opacity, 1))}.drop-shadow-\[0_0_6px_\#58a6ff\]{--tw-drop-shadow: drop-shadow(0 0 6px #58a6ff);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[opacity\,border-color\,box-shadow\,transform\]{transition-property:opacity,border-color,box-shadow,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:border-accent:hover{--tw-border-opacity: 1;border-color:rgb(88 166 255 / var(--tw-border-opacity, 1))}.hover\:bg-accent\/90:hover{background-color:#58a6ffe6}.hover\:bg-background:hover{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1))}.hover\:bg-surface:hover{--tw-bg-opacity: 1;background-color:rgb(22 27 34 / var(--tw-bg-opacity, 1))}.hover\:bg-white\/5:hover{background-color:#ffffff0d}.hover\:bg-white\/\[0\.02\]:hover{background-color:#ffffff05}.hover\:text-text-primary:hover{--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1))}.focus\:bg-white\/5:focus{background-color:#ffffff0d}.group:hover .group-hover\:bg-purple-400{--tw-bg-opacity: 1;background-color:rgb(192 132 252 / var(--tw-bg-opacity, 1))}.group:hover .group-hover\:text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-text-primary{--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1))}@media(min-width:640px){.sm\:inline-block{display:inline-block}}
@@ -0,0 +1,9 @@
1
+ function ct(n){return n&&n.__esModule&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n}var B={exports:{}},D={};var Z;function lt(){if(Z)return D;Z=1;var n=Symbol.for("react.transitional.element"),p=Symbol.for("react.fragment");function i(E,a,l){var c=null;if(l!==void 0&&(c=""+l),a.key!==void 0&&(c=""+a.key),"key"in a){l={};for(var w in a)w!=="key"&&(l[w]=a[w])}else l=a;return a=l.ref,{$$typeof:n,type:E,key:c,ref:a!==void 0?a:null,props:l}}return D.Fragment=p,D.jsx=i,D.jsxs=i,D}var K;function pt(){return K||(K=1,B.exports=lt()),B.exports}var q=pt(),X={exports:{}},r={};var F;function at(){if(F)return r;F=1;var n=Symbol.for("react.transitional.element"),p=Symbol.for("react.portal"),i=Symbol.for("react.fragment"),E=Symbol.for("react.strict_mode"),a=Symbol.for("react.profiler"),l=Symbol.for("react.consumer"),c=Symbol.for("react.context"),w=Symbol.for("react.forward_ref"),H=Symbol.for("react.suspense"),k=Symbol.for("react.memo"),m=Symbol.for("react.lazy"),M=Symbol.for("react.activity"),R=Symbol.iterator;function T(t){return t===null||typeof t!="object"?null:(t=R&&t[R]||t["@@iterator"],typeof t=="function"?t:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},A=Object.assign,S={};function P(t,e,u){this.props=t,this.context=e,this.refs=S,this.updater=u||x}P.prototype.isReactComponent={},P.prototype.setState=function(t,e){if(typeof t!="object"&&typeof t!="function"&&t!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,t,e,"setState")},P.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this,t,"forceUpdate")};function L(){}L.prototype=P.prototype;function j(t,e,u){this.props=t,this.context=e,this.refs=S,this.updater=u||x}var C=j.prototype=new L;C.constructor=j,A(C,P.prototype),C.isPureReactComponent=!0;var U=Array.isArray;function I(){}var h={H:null,A:null,T:null,S:null},g=Object.prototype.hasOwnProperty;function y(t,e,u){var o=u.ref;return{$$typeof:n,type:t,key:e,ref:o!==void 0?o:null,props:u}}function O(t,e){return y(t.type,e,t.props)}function Y(t){return typeof t=="object"&&t!==null&&t.$$typeof===n}function b(t){var e={"=":"=0",":":"=2"};return"$"+t.replace(/[=:]/g,function(u){return e[u]})}var z=/\/+/g;function W(t,e){return typeof t=="object"&&t!==null&&t.key!=null?b(""+t.key):e.toString(36)}function ut(t){switch(t.status){case"fulfilled":return t.value;case"rejected":throw t.reason;default:switch(typeof t.status=="string"?t.then(I,I):(t.status="pending",t.then(function(e){t.status==="pending"&&(t.status="fulfilled",t.value=e)},function(e){t.status==="pending"&&(t.status="rejected",t.reason=e)})),t.status){case"fulfilled":return t.value;case"rejected":throw t.reason}}throw t}function N(t,e,u,o,f){var d=typeof t;(d==="undefined"||d==="boolean")&&(t=null);var _=!1;if(t===null)_=!0;else switch(d){case"bigint":case"string":case"number":_=!0;break;case"object":switch(t.$$typeof){case n:case p:_=!0;break;case m:return _=t._init,N(_(t._payload),e,u,o,f)}}if(_)return f=f(t),_=o===""?"."+W(t,0):o,U(f)?(u="",_!=null&&(u=_.replace(z,"$&/")+"/"),N(f,e,u,"",function(ft){return ft})):f!=null&&(Y(f)&&(f=O(f,u+(f.key==null||t&&t.key===f.key?"":(""+f.key).replace(z,"$&/")+"/")+_)),e.push(f)),1;_=0;var $=o===""?".":o+":";if(U(t))for(var v=0;v<t.length;v++)o=t[v],d=$+W(o,v),_+=N(o,e,u,d,f);else if(v=T(t),typeof v=="function")for(t=v.call(t),v=0;!(o=t.next()).done;)o=o.value,d=$+W(o,v++),_+=N(o,e,u,d,f);else if(d==="object"){if(typeof t.then=="function")return N(ut(t),e,u,o,f);throw e=String(t),Error("Objects are not valid as a React child (found: "+(e==="[object Object]"?"object with keys {"+Object.keys(t).join(", ")+"}":e)+"). If you meant to render a collection of children, use an array instead.")}return _}function G(t,e,u){if(t==null)return t;var o=[],f=0;return N(t,o,"","",function(d){return e.call(u,d,f++)}),o}function st(t){if(t._status===-1){var e=t._result;e=e(),e.then(function(u){(t._status===0||t._status===-1)&&(t._status=1,t._result=u)},function(u){(t._status===0||t._status===-1)&&(t._status=2,t._result=u)}),t._status===-1&&(t._status=0,t._result=e)}if(t._status===1)return t._result.default;throw t._result}var Q=typeof reportError=="function"?reportError:function(t){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var e=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof t=="object"&&t!==null&&typeof t.message=="string"?String(t.message):String(t),error:t});if(!window.dispatchEvent(e))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",t);return}console.error(t)},it={map:G,forEach:function(t,e,u){G(t,function(){e.apply(this,arguments)},u)},count:function(t){var e=0;return G(t,function(){e++}),e},toArray:function(t){return G(t,function(e){return e})||[]},only:function(t){if(!Y(t))throw Error("React.Children.only expected to receive a single React element child.");return t}};return r.Activity=M,r.Children=it,r.Component=P,r.Fragment=i,r.Profiler=a,r.PureComponent=j,r.StrictMode=E,r.Suspense=H,r.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=h,r.__COMPILER_RUNTIME={__proto__:null,c:function(t){return h.H.useMemoCache(t)}},r.cache=function(t){return function(){return t.apply(null,arguments)}},r.cacheSignal=function(){return null},r.cloneElement=function(t,e,u){if(t==null)throw Error("The argument must be a React element, but you passed "+t+".");var o=A({},t.props),f=t.key;if(e!=null)for(d in e.key!==void 0&&(f=""+e.key),e)!g.call(e,d)||d==="key"||d==="__self"||d==="__source"||d==="ref"&&e.ref===void 0||(o[d]=e[d]);var d=arguments.length-2;if(d===1)o.children=u;else if(1<d){for(var _=Array(d),$=0;$<d;$++)_[$]=arguments[$+2];o.children=_}return y(t.type,f,o)},r.createContext=function(t){return t={$$typeof:c,_currentValue:t,_currentValue2:t,_threadCount:0,Provider:null,Consumer:null},t.Provider=t,t.Consumer={$$typeof:l,_context:t},t},r.createElement=function(t,e,u){var o,f={},d=null;if(e!=null)for(o in e.key!==void 0&&(d=""+e.key),e)g.call(e,o)&&o!=="key"&&o!=="__self"&&o!=="__source"&&(f[o]=e[o]);var _=arguments.length-2;if(_===1)f.children=u;else if(1<_){for(var $=Array(_),v=0;v<_;v++)$[v]=arguments[v+2];f.children=$}if(t&&t.defaultProps)for(o in _=t.defaultProps,_)f[o]===void 0&&(f[o]=_[o]);return y(t,d,f)},r.createRef=function(){return{current:null}},r.forwardRef=function(t){return{$$typeof:w,render:t}},r.isValidElement=Y,r.lazy=function(t){return{$$typeof:m,_payload:{_status:-1,_result:t},_init:st}},r.memo=function(t,e){return{$$typeof:k,type:t,compare:e===void 0?null:e}},r.startTransition=function(t){var e=h.T,u={};h.T=u;try{var o=t(),f=h.S;f!==null&&f(u,o),typeof o=="object"&&o!==null&&typeof o.then=="function"&&o.then(I,Q)}catch(d){Q(d)}finally{e!==null&&u.types!==null&&(e.types=u.types),h.T=e}},r.unstable_useCacheRefresh=function(){return h.H.useCacheRefresh()},r.use=function(t){return h.H.use(t)},r.useActionState=function(t,e,u){return h.H.useActionState(t,e,u)},r.useCallback=function(t,e){return h.H.useCallback(t,e)},r.useContext=function(t){return h.H.useContext(t)},r.useDebugValue=function(){},r.useDeferredValue=function(t,e){return h.H.useDeferredValue(t,e)},r.useEffect=function(t,e){return h.H.useEffect(t,e)},r.useEffectEvent=function(t){return h.H.useEffectEvent(t)},r.useId=function(){return h.H.useId()},r.useImperativeHandle=function(t,e,u){return h.H.useImperativeHandle(t,e,u)},r.useInsertionEffect=function(t,e){return h.H.useInsertionEffect(t,e)},r.useLayoutEffect=function(t,e){return h.H.useLayoutEffect(t,e)},r.useMemo=function(t,e){return h.H.useMemo(t,e)},r.useOptimistic=function(t,e){return h.H.useOptimistic(t,e)},r.useReducer=function(t,e,u){return h.H.useReducer(t,e,u)},r.useRef=function(t){return h.H.useRef(t)},r.useState=function(t){return h.H.useState(t)},r.useSyncExternalStore=function(t,e,u){return h.H.useSyncExternalStore(t,e,u)},r.useTransition=function(){return h.H.useTransition()},r.version="19.2.4",r}var V;function dt(){return V||(V=1,X.exports=at()),X.exports}var s=dt();const Pt=ct(s),Et=s.createContext({});function rt(n){const p=s.useRef(null);return p.current===null&&(p.current=n()),p.current}const ht=typeof window<"u",_t=ht?s.useLayoutEffect:s.useEffect,ot=s.createContext(null);function yt(n){return typeof n=="object"&&n!==null}function tt(n){return yt(n)&&"offsetHeight"in n}const Rt=s.createContext({transformPagePoint:n=>n,isStatic:!1,reducedMotion:"never"});function et(n,p){if(typeof n=="function")return n(p);n!=null&&(n.current=p)}function mt(...n){return p=>{let i=!1;const E=n.map(a=>{const l=et(a,p);return!i&&typeof l=="function"&&(i=!0),l});if(i)return()=>{for(let a=0;a<E.length;a++){const l=E[a];typeof l=="function"?l():et(n[a],null)}}}}function Ct(...n){return s.useCallback(mt(...n),n)}class vt extends s.Component{getSnapshotBeforeUpdate(p){const i=this.props.childRef.current;if(i&&p.isPresent&&!this.props.isPresent){const E=i.offsetParent,a=tt(E)&&E.offsetWidth||0,l=tt(E)&&E.offsetHeight||0,c=this.props.sizeRef.current;c.height=i.offsetHeight||0,c.width=i.offsetWidth||0,c.top=i.offsetTop,c.left=i.offsetLeft,c.right=a-c.width-c.left,c.bottom=l-c.height-c.top}return null}componentDidUpdate(){}render(){return this.props.children}}function Tt({children:n,isPresent:p,anchorX:i,anchorY:E,root:a}){const l=s.useId(),c=s.useRef(null),w=s.useRef({width:0,height:0,top:0,left:0,right:0,bottom:0}),{nonce:H}=s.useContext(Rt),k=n.props?.ref??n?.ref,m=Ct(c,k);return s.useInsertionEffect(()=>{const{width:M,height:R,top:T,left:x,right:A,bottom:S}=w.current;if(p||!c.current||!M||!R)return;const P=i==="left"?`left: ${x}`:`right: ${A}`,L=E==="bottom"?`bottom: ${S}`:`top: ${T}`;c.current.dataset.motionPopId=l;const j=document.createElement("style");H&&(j.nonce=H);const C=a??document.head;return C.appendChild(j),j.sheet&&j.sheet.insertRule(`
2
+ [data-motion-pop-id="${l}"] {
3
+ position: absolute !important;
4
+ width: ${M}px !important;
5
+ height: ${R}px !important;
6
+ ${P}px !important;
7
+ ${L}px !important;
8
+ }
9
+ `),()=>{C.contains(j)&&C.removeChild(j)}},[p]),q.jsx(vt,{isPresent:p,childRef:c,sizeRef:w,children:s.cloneElement(n,{ref:m})})}const xt=({children:n,initial:p,isPresent:i,onExitComplete:E,custom:a,presenceAffectsLayout:l,mode:c,anchorX:w,anchorY:H,root:k})=>{const m=rt(gt),M=s.useId();let R=!0,T=s.useMemo(()=>(R=!1,{id:M,initial:p,isPresent:i,custom:a,onExitComplete:x=>{m.set(x,!0);for(const A of m.values())if(!A)return;E&&E()},register:x=>(m.set(x,!1),()=>m.delete(x))}),[i,m,E]);return l&&R&&(T={...T}),s.useMemo(()=>{m.forEach((x,A)=>m.set(A,!1))},[i]),s.useEffect(()=>{!i&&!m.size&&E&&E()},[i]),c==="popLayout"&&(n=q.jsx(Tt,{isPresent:i,anchorX:w,anchorY:H,root:k,children:n})),q.jsx(ot.Provider,{value:T,children:n})};function gt(){return new Map}function wt(n=!0){const p=s.useContext(ot);if(p===null)return[!0,null];const{isPresent:i,onExitComplete:E,register:a}=p,l=s.useId();s.useEffect(()=>{if(n)return a(l)},[n]);const c=s.useCallback(()=>n&&E&&E(l),[l,E,n]);return!i&&E?[!1,c]:[!0]}const J=n=>n.key||"";function nt(n){const p=[];return s.Children.forEach(n,i=>{s.isValidElement(i)&&p.push(i)}),p}const jt=({children:n,custom:p,initial:i=!0,onExitComplete:E,presenceAffectsLayout:a=!0,mode:l="sync",propagate:c=!1,anchorX:w="left",anchorY:H="top",root:k})=>{const[m,M]=wt(c),R=s.useMemo(()=>nt(n),[n]),T=c&&!m?[]:R.map(J),x=s.useRef(!0),A=s.useRef(R),S=rt(()=>new Map),P=s.useRef(new Set),[L,j]=s.useState(R),[C,U]=s.useState(R);_t(()=>{x.current=!1,A.current=R;for(let g=0;g<C.length;g++){const y=J(C[g]);T.includes(y)?(S.delete(y),P.current.delete(y)):S.get(y)!==!0&&S.set(y,!1)}},[C,T.length,T.join("-")]);const I=[];if(R!==L){let g=[...R];for(let y=0;y<C.length;y++){const O=C[y],Y=J(O);T.includes(Y)||(g.splice(y,0,O),I.push(O))}return l==="wait"&&I.length&&(g=I),U(nt(g)),j(R),null}const{forceRender:h}=s.useContext(Et);return q.jsx(q.Fragment,{children:C.map(g=>{const y=J(g),O=c&&!m?!1:R===C||T.includes(y),Y=()=>{if(P.current.has(y))return;if(P.current.add(y),S.has(y))S.set(y,!0);else return;let b=!0;S.forEach(z=>{z||(b=!1)}),b&&(h?.(),U(A.current),c&&M?.(),E&&E())};return q.jsx(xt,{isPresent:O,initial:!x.current||i?void 0:!1,custom:p,presenceAffectsLayout:a,mode:l,root:k,onExitComplete:O?void 0:Y,anchorX:w,anchorY:H,children:g},y)})})};export{jt as A,Pt as R,s as a,ct as g,q as j,dt as r};
@@ -5,8 +5,10 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>client</title>
8
- <script type="module" crossorigin src="/assets/index-CzAaOuyw.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BIu7r5_5.css">
8
+ <script type="module" crossorigin src="/assets/index-CbgYG3pJ.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/motion-CGUGF2CN.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/graph-Cw_XSlvx.js">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-CgDCc8Mm.css">
10
12
  </head>
11
13
  <body>
12
14
  <div id="root"></div>
@@ -8,7 +8,7 @@ import { errorHandler, notFoundHandler } from "./middleware/error";
8
8
  import { registerRoutes } from "./routes";
9
9
  import { parseArgs, printHelp, openBrowser } from "./cli";
10
10
  import { getGlobalWatcher, closeAllSSEConnections } from "./routes/sse";
11
- import { listAllSessions, closeDb } from "./storage";
11
+ import { queryProjectByWorktree, closeDb } from "./storage";
12
12
 
13
13
  const clientDistPath = join(import.meta.dir, "..", "client", "dist");
14
14
  const flags = parseArgs();
@@ -18,32 +18,17 @@ if (flags.showHelp) {
18
18
  process.exit(0);
19
19
  }
20
20
 
21
+ // Start watcher eagerly so fs events are captured before the first request
22
+ getGlobalWatcher();
23
+
21
24
  function normalizeDirectoryPath(pathValue: string): string {
22
25
  return normalize(resolve(pathValue));
23
26
  }
24
27
 
25
- async function resolveDefaultProjectId(projectPath: string): Promise<string | undefined> {
26
- const knownSessions = listAllSessions();
27
- const requestedPath = normalizeDirectoryPath(projectPath);
28
- const seenProjectDirectories = new Map<string, string>();
29
-
30
- for (const session of knownSessions) {
31
- if (!session.directory || seenProjectDirectories.has(session.projectID)) {
32
- continue;
33
- }
34
- seenProjectDirectories.set(
35
- session.projectID,
36
- normalizeDirectoryPath(session.directory)
37
- );
38
- }
39
-
40
- for (const [projectID, directory] of seenProjectDirectories) {
41
- if (directory === requestedPath) {
42
- return projectID;
43
- }
44
- }
45
-
46
- return undefined;
28
+ function resolveDefaultProjectId(projectPath: string): string | undefined {
29
+ const normalizedPath = normalizeDirectoryPath(projectPath);
30
+ const project = queryProjectByWorktree(normalizedPath);
31
+ return project?.id;
47
32
  }
48
33
 
49
34
  async function getDefaultProjectIdFromFlag(projectPath: string | null): Promise<string | undefined> {
@@ -52,7 +37,7 @@ async function getDefaultProjectIdFromFlag(projectPath: string | null): Promise<
52
37
  }
53
38
 
54
39
  try {
55
- const defaultProjectId = await resolveDefaultProjectId(projectPath);
40
+ const defaultProjectId = resolveDefaultProjectId(projectPath);
56
41
  if (!defaultProjectId) {
57
42
  console.warn(
58
43
  `[ocwatch] --project path did not match any known project directory: ${projectPath}`
@@ -1,5 +1,4 @@
1
1
  import type { Hono } from "hono";
2
- import { z } from "zod";
3
2
  import {
4
3
  generateETag,
5
4
  fetchPollData,
@@ -10,8 +9,7 @@ import {
10
9
  setPollInProgress,
11
10
  getPollCacheTTL,
12
11
  } from "../services/pollService";
13
-
14
- const projectIdSchema = z.string().regex(/^[a-zA-Z0-9_-]+$/, "Invalid project ID format");
12
+ import { projectIdSchema, validateWithResponse } from "../validation";
15
13
 
16
14
  export function registerPollRoute(app: Hono) {
17
15
  app.get("/api/poll", async (c) => {
@@ -21,11 +19,9 @@ export function registerPollRoute(app: Hono) {
21
19
  let projectId: string | undefined;
22
20
 
23
21
  if (rawProjectId) {
24
- const result = projectIdSchema.safeParse(rawProjectId);
25
- if (!result.success) {
26
- return c.json({ error: "INVALID_PROJECT_ID", message: "Invalid project ID format" }, 400);
27
- }
28
- projectId = result.data;
22
+ const validation = validateWithResponse(projectIdSchema, rawProjectId, c);
23
+ if (!validation.success) return validation.response;
24
+ projectId = validation.value;
29
25
  }
30
26
 
31
27
  const POLL_CACHE_TTL = getPollCacheTTL();
@@ -49,7 +45,8 @@ export function registerPollRoute(app: Hono) {
49
45
  }
50
46
  c.header("ETag", etag);
51
47
  return c.json(data);
52
- } catch {
48
+ } catch (err) {
49
+ console.warn("Poll request failed, retrying:", err instanceof Error ? err.message : err);
53
50
  setPollInProgress(null, projectId);
54
51
  }
55
52
  }
@@ -1,34 +1,17 @@
1
1
  import type { Hono } from "hono";
2
- import { listProjects, listAllSessions } from "../storage";
2
+ import { queryProjectSummaries } from "../storage";
3
3
 
4
4
  export function registerProjectRoutes(app: Hono) {
5
5
  app.get("/api/projects", (c) => {
6
- const projectIDs = listProjects();
7
- const allSessions = listAllSessions();
6
+ const summaries = queryProjectSummaries();
8
7
 
9
- const projectsWithDetails = projectIDs.map((projectID) => {
10
- const projectSessions = allSessions.filter((s) => s.projectID === projectID);
11
- const directory = projectSessions[0]?.directory || "";
8
+ const projects = summaries.map((row) => ({
9
+ id: row.id,
10
+ directory: row.worktree,
11
+ sessionCount: row.sessionCount,
12
+ lastActivityAt: new Date(row.lastActivityAt),
13
+ }));
12
14
 
13
- const lastActivityAt =
14
- projectSessions.length > 0
15
- ? new Date(
16
- Math.max(...projectSessions.map((s) => s.updatedAt.getTime()))
17
- )
18
- : new Date(0);
19
-
20
- return {
21
- id: projectID,
22
- directory,
23
- sessionCount: projectSessions.length,
24
- lastActivityAt,
25
- };
26
- });
27
-
28
- projectsWithDetails.sort(
29
- (a, b) => b.lastActivityAt.getTime() - a.lastActivityAt.getTime()
30
- );
31
-
32
- return c.json(projectsWithDetails);
15
+ return c.json(projects);
33
16
  });
34
17
  }
@@ -7,14 +7,22 @@ import {
7
7
  queryTodos,
8
8
  } from "../storage/queries";
9
9
  import type { DbSessionRow } from "../storage/queries";
10
- import { fetchSessionDetail } from "../services/pollService";
10
+ import {
11
+ fetchSessionActivity,
12
+ fetchSessionDetail,
13
+ generateSessionActivityETag,
14
+ getPollCacheEpoch,
15
+ getSessionActivityCache,
16
+ getSessionActivityInProgress,
17
+ setSessionActivityCache,
18
+ setSessionActivityInProgress,
19
+ } from "../services/pollService";
11
20
  import { toMessageMeta } from "../services/parsing";
12
- import { buildSessionTree } from "../services/sessionService";
21
+ import { selectRecentRootSessions } from "../services/recentSessions";
22
+ import { buildSessionTree } from "../services/sessionTree";
13
23
  import { sessionIdSchema, validateWithResponse } from "../validation";
14
- import { MAX_SESSIONS_LIMIT, MAX_MESSAGES_LIMIT, TWENTY_FOUR_HOURS_MS } from "../../shared/constants";
15
- import type { SessionMetadata } from "../../shared/types";
16
-
17
- const SESSION_SCAN_LIMIT = 50_000;
24
+ import { MAX_SESSIONS_LIMIT, MAX_MESSAGES_LIMIT, SESSION_SCAN_LIMIT } from "../../shared/constants";
25
+ import type { SessionActivityResponse, SessionMetadata } from "../../shared/types";
18
26
 
19
27
  function dbRowToSessionBase(row: DbSessionRow) {
20
28
  return {
@@ -49,12 +57,10 @@ export function registerSessionRoutes(app: Hono) {
49
57
  }, 200);
50
58
  }
51
59
 
52
- const now = Date.now();
53
- const twentyFourHoursAgo = now - TWENTY_FOUR_HOURS_MS;
54
-
55
- const sessions = querySessions(undefined, twentyFourHoursAgo, MAX_SESSIONS_LIMIT)
56
- .filter((row) => !row.parentID)
57
- .map(dbRowToSessionBase);
60
+ const sessions = selectRecentRootSessions(
61
+ querySessions(undefined, undefined, SESSION_SCAN_LIMIT).map(dbRowToSessionBase),
62
+ MAX_SESSIONS_LIMIT,
63
+ );
58
64
 
59
65
  return c.json(sessions);
60
66
  });
@@ -118,6 +124,7 @@ export function registerSessionRoutes(app: Hono) {
118
124
  const validation = validateWithResponse(sessionIdSchema, c.req.param("id"), c);
119
125
  if (!validation.success) return validation.response;
120
126
  const sessionID = validation.value;
127
+ const clientETag = c.req.header("If-None-Match");
121
128
 
122
129
  if (!checkDbExists()) {
123
130
  return c.json({ error: "SESSION_NOT_FOUND", message: `Session '${sessionID}' not found`, status: 404 }, 404);
@@ -128,8 +135,53 @@ export function registerSessionRoutes(app: Hono) {
128
135
  return c.json({ error: "SESSION_NOT_FOUND", message: `Session '${sessionID}' not found`, status: 404 }, 404);
129
136
  }
130
137
 
131
- const detail = await fetchSessionDetail(sessionID);
132
- return c.json({ activity: detail.activity });
138
+ const cached = getSessionActivityCache(sessionID);
139
+ if (cached) {
140
+ if (clientETag === cached.etag) {
141
+ return new Response(null, { status: 304, headers: { ETag: cached.etag } });
142
+ }
143
+ c.header("ETag", cached.etag);
144
+ return c.json(cached.data);
145
+ }
146
+
147
+ const inProgress = getSessionActivityInProgress(sessionID);
148
+ if (inProgress) {
149
+ try {
150
+ const data = await inProgress;
151
+ const etag = generateSessionActivityETag(data);
152
+ if (clientETag === etag) {
153
+ return new Response(null, { status: 304, headers: { ETag: etag } });
154
+ }
155
+ c.header("ETag", etag);
156
+ return c.json(data);
157
+ } catch (err) {
158
+ console.warn("Session activity request failed, retrying:", err instanceof Error ? err.message : err);
159
+ setSessionActivityInProgress(sessionID, null);
160
+ }
161
+ }
162
+
163
+ const cacheEpochAtStart = getPollCacheEpoch();
164
+ const promise = fetchSessionActivity(sessionID);
165
+ setSessionActivityInProgress(sessionID, promise);
166
+
167
+ let data: SessionActivityResponse;
168
+ try {
169
+ data = await promise;
170
+ const etag = generateSessionActivityETag(data);
171
+ if (cacheEpochAtStart === getPollCacheEpoch()) {
172
+ setSessionActivityCache(sessionID, { data, etag });
173
+ }
174
+ } finally {
175
+ setSessionActivityInProgress(sessionID, null);
176
+ }
177
+
178
+ const etag = generateSessionActivityETag(data);
179
+ if (clientETag === etag) {
180
+ return new Response(null, { status: 304, headers: { ETag: etag } });
181
+ }
182
+
183
+ c.header("ETag", etag);
184
+ return c.json(data);
133
185
  });
134
186
 
135
187
  app.get("/api/sessions/:id/todos", (c) => {