@loghead/core 0.1.31 → 0.1.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/server.js +67 -49
- package/dist/cli_main.js +46 -41
- package/dist/db/client.js +37 -53
- package/dist/db/migrate.js +0 -13
- package/dist/public/assets/index-BFN5xtu_.js +164 -0
- package/dist/public/assets/index-PEkusdx8.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/public/logo.png +0 -0
- package/dist/services/db.js +0 -2
- package/dist/types.js +1 -2
- package/dist/ui/main.js +43 -50
- package/dist/utils/startup.js +22 -20
- package/package.json +7 -5
- package/build/npm/README.md +0 -194
- package/build/npm/bin/loghead +0 -0
- package/build/npm/package.json +0 -32
- package/dist/public/assets/index-BGOKjK0s.css +0 -1
- package/dist/public/assets/index-BtWnFjRZ.js +0 -111
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,: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}.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}}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.left-0{left:0}.right-2{right:.5rem}.top-0{top:0}.top-2{top:.5rem}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-\[100\]{z-index:100}.m-4{margin:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-auto{margin-left:auto}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.h-12{height:3rem}.h-16{height:4rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-full{height:100%}.max-h-48{max-height:12rem}.max-h-60{max-height:15rem}.min-h-0{min-height:0px}.min-h-\[60px\]{min-height:60px}.min-h-screen{min-height:100vh}.w-12{width:3rem}.w-16{width:4rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-7xl{max-width:80rem}.max-w-lg{max-width:32rem}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.rotate-\[30deg\]{--tw-rotate: 30deg;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))}.transform{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 pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.columns-2{-moz-columns:2;columns:2}.columns-3{-moz-columns:3;columns:3}.columns-4{-moz-columns:4;columns:4}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.gap-y-2{row-gap:.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * 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))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.scroll-smooth{scroll-behavior:smooth}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-wrap{text-wrap:wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-current{border-color:currentColor}.border-emerald-500\/30{border-color:#10b9814d}.border-emerald-500\/50{border-color:#10b98180}.border-green-500{--tw-border-opacity: 1;border-color:rgb(34 197 94 / var(--tw-border-opacity, 1))}.border-red-900{--tw-border-opacity: 1;border-color:rgb(127 29 29 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-zinc-700{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.border-zinc-800{--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity, 1))}.bg-\[\#00FF94\]\/10{background-color:#00ff941a}.bg-\[\#0d0d0d\]{--tw-bg-opacity: 1;background-color:rgb(13 13 13 / var(--tw-bg-opacity, 1))}.bg-black\/50{background-color:#00000080}.bg-black\/80{background-color:#000c}.bg-emerald-500\/20{background-color:#10b98133}.bg-emerald-600{--tw-bg-opacity: 1;background-color:rgb(5 150 105 / var(--tw-bg-opacity, 1))}.bg-emerald-600\/20{background-color:#05966933}.bg-emerald-900\/30{background-color:#064e3b4d}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-red-900\/50{background-color:#7f1d1d80}.bg-transparent{background-color:transparent}.bg-yellow-500\/20{background-color:#eab30833}.bg-zinc-800{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity, 1))}.bg-zinc-900{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.bg-zinc-900\/50{background-color:#18181b80}.bg-zinc-900\/60{background-color:#18181b99}.bg-zinc-950{--tw-bg-opacity: 1;background-color:rgb(9 9 11 / var(--tw-bg-opacity, 1))}.bg-zinc-950\/40{background-color:#09090b66}.bg-zinc-950\/50{background-color:#09090b80}.bg-zinc-950\/60{background-color:#09090b99}.bg-zinc-950\/80{background-color:#09090bcc}.object-contain{-o-object-fit:contain;object-fit:contain}.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-0\.5{padding-left:.125rem;padding-right:.125rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pl-2{padding-left:.5rem}.pr-12{padding-right:3rem}.pr-3{padding-right:.75rem}.pt-1{padding-top:.25rem}.pt-16{padding-top:4rem}.pt-2{padding-top:.5rem}.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-\[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}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-wider{letter-spacing:.05em}.text-emerald-300{--tw-text-opacity: 1;color:rgb(110 231 183 / var(--tw-text-opacity, 1))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.text-gray-100{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-200{--tw-text-opacity: 1;color:rgb(254 240 138 / var(--tw-text-opacity, 1))}.text-zinc-100{--tw-text-opacity: 1;color:rgb(244 244 245 / var(--tw-text-opacity, 1))}.text-zinc-200{--tw-text-opacity: 1;color:rgb(228 228 231 / var(--tw-text-opacity, 1))}.text-zinc-300{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.text-zinc-400{--tw-text-opacity: 1;color:rgb(161 161 170 / var(--tw-text-opacity, 1))}.text-zinc-500{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.text-zinc-600{--tw-text-opacity: 1;color:rgb(82 82 91 / var(--tw-text-opacity, 1))}.text-zinc-700{--tw-text-opacity: 1;color:rgb(63 63 70 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.placeholder-zinc-600::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(82 82 91 / var(--tw-placeholder-opacity, 1))}.placeholder-zinc-600::placeholder{--tw-placeholder-opacity: 1;color:rgb(82 82 91 / var(--tw-placeholder-opacity, 1))}.opacity-50{opacity:.5}.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-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px 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}.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)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-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-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}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.placeholder\:text-zinc-500::-moz-placeholder{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.placeholder\:text-zinc-500::placeholder{--tw-text-opacity: 1;color:rgb(113 113 122 / var(--tw-text-opacity, 1))}.hover\:border-zinc-700:hover{--tw-border-opacity: 1;border-color:rgb(63 63 70 / var(--tw-border-opacity, 1))}.hover\:border-zinc-800:hover{--tw-border-opacity: 1;border-color:rgb(39 39 42 / var(--tw-border-opacity, 1))}.hover\:bg-emerald-500:hover{--tw-bg-opacity: 1;background-color:rgb(16 185 129 / var(--tw-bg-opacity, 1))}.hover\:bg-emerald-600\/30:hover{background-color:#0596694d}.hover\:bg-emerald-900\/50:hover{background-color:#064e3b80}.hover\:bg-red-900:hover{--tw-bg-opacity: 1;background-color:rgb(127 29 29 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-700:hover{--tw-bg-opacity: 1;background-color:rgb(63 63 70 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-800:hover{--tw-bg-opacity: 1;background-color:rgb(39 39 42 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-900:hover{--tw-bg-opacity: 1;background-color:rgb(24 24 27 / var(--tw-bg-opacity, 1))}.hover\:bg-zinc-900\/20:hover{background-color:#18181b33}.hover\:bg-zinc-900\/40:hover{background-color:#18181b66}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:text-zinc-200:hover{--tw-text-opacity: 1;color:rgb(228 228 231 / var(--tw-text-opacity, 1))}.hover\:text-zinc-300:hover{--tw-text-opacity: 1;color:rgb(212 212 216 / var(--tw-text-opacity, 1))}.focus\:border-emerald-500:focus{--tw-border-opacity: 1;border-color:rgb(16 185 129 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:bg-zinc-700{--tw-bg-opacity: 1;background-color:rgb(63 63 70 / var(--tw-bg-opacity, 1))}.group:hover .group-hover\:text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width: 640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-8{padding-top:2rem;padding-bottom:2rem}}@media (min-width: 768px){.md\:grid-cols-\[minmax\(220px\,auto\)_1fr\]{grid-template-columns:minmax(220px,auto) 1fr}}@media (min-width: 1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}
|
package/dist/public/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Loghead</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-BFN5xtu_.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-PEkusdx8.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
Binary file
|
package/dist/services/db.js
CHANGED
|
@@ -90,8 +90,6 @@ class DbService {
|
|
|
90
90
|
}
|
|
91
91
|
const vectorJson = JSON.stringify(embedding);
|
|
92
92
|
// Explicitly cast rowid to BigInt to ensure better-sqlite3 binds it as an INTEGER
|
|
93
|
-
// Ensure no orphaned vector exists for this rowid (can happen if logs were deleted but vec_logs weren't)
|
|
94
|
-
client_1.db.prepare("DELETE FROM vec_logs WHERE rowid = ?").run(BigInt(rowid));
|
|
95
93
|
client_1.db.prepare("INSERT INTO vec_logs(rowid, embedding) VALUES (?, ?)").run(BigInt(rowid), vectorJson);
|
|
96
94
|
}
|
|
97
95
|
});
|
package/dist/types.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/ui/main.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.startTui = startTui;
|
|
7
|
-
const auth_1 = require("../services/auth");
|
|
8
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
1
|
+
import { authService } from "../db/client.js";
|
|
2
|
+
import inquirer from "inquirer";
|
|
3
|
+
import chalk from "chalk";
|
|
10
4
|
let title = `
|
|
11
5
|
█████ █████ █████
|
|
12
6
|
▒▒███ ▒▒███ ▒▒███
|
|
@@ -19,22 +13,22 @@ let title = `
|
|
|
19
13
|
███ ▒███
|
|
20
14
|
▒▒██████
|
|
21
15
|
▒▒▒▒▒▒ `;
|
|
22
|
-
async function startTui(db, token) {
|
|
16
|
+
export async function startTui(db, token) {
|
|
23
17
|
const port = process.env.PORT || 4567;
|
|
24
18
|
const showHeader = () => {
|
|
25
19
|
console.clear();
|
|
26
|
-
console.log(
|
|
27
|
-
console.log(
|
|
28
|
-
console.log(
|
|
20
|
+
console.log(chalk.bold(title));
|
|
21
|
+
console.log(chalk.bold(`\nServer URL :`) + " " + chalk.dim(`http://localhost:${port}`));
|
|
22
|
+
console.log(chalk.bold(`MCP Token :`) + " " + chalk.dim(token) + "\n");
|
|
29
23
|
};
|
|
30
24
|
while (true) {
|
|
31
25
|
showHeader();
|
|
32
|
-
const projects = db.listProjects();
|
|
26
|
+
const projects = await db.listProjects();
|
|
33
27
|
const projectChoices = projects.map(p => ({ name: p.name, value: p.id }));
|
|
34
|
-
projectChoices.push(new
|
|
35
|
-
projectChoices.push({ name:
|
|
36
|
-
projectChoices.push({ name:
|
|
37
|
-
const { projectId } = await
|
|
28
|
+
projectChoices.push(new inquirer.Separator());
|
|
29
|
+
projectChoices.push({ name: chalk.green("+ Create Project"), value: "create_project" });
|
|
30
|
+
projectChoices.push({ name: chalk.red("Exit"), value: "exit" });
|
|
31
|
+
const { projectId } = await inquirer.prompt([{
|
|
38
32
|
type: "list",
|
|
39
33
|
name: "projectId",
|
|
40
34
|
message: "Select a project",
|
|
@@ -45,31 +39,31 @@ async function startTui(db, token) {
|
|
|
45
39
|
if (projectId === "exit")
|
|
46
40
|
break;
|
|
47
41
|
if (projectId === "create_project") {
|
|
48
|
-
const { name } = await
|
|
42
|
+
const { name } = await inquirer.prompt([{
|
|
49
43
|
type: "input",
|
|
50
44
|
name: "name",
|
|
51
45
|
message: "Project Name:",
|
|
52
46
|
prefix: "💡"
|
|
53
47
|
}]);
|
|
54
48
|
if (name) {
|
|
55
|
-
db.createProject(name);
|
|
49
|
+
await db.createProject(name);
|
|
56
50
|
}
|
|
57
51
|
continue;
|
|
58
52
|
}
|
|
59
53
|
// List streams for project
|
|
60
54
|
while (true) {
|
|
61
55
|
showHeader();
|
|
62
|
-
const project = projects.find(p => p.id === projectId);
|
|
63
|
-
console.log(
|
|
64
|
-
const streams = db.listStreams(projectId);
|
|
65
|
-
const streamChoices = streams.map(s => ({
|
|
56
|
+
const project = projects.find((p) => p.id === projectId);
|
|
57
|
+
console.log(chalk.bold.blue(`Project: ${project?.name}\n`));
|
|
58
|
+
const streams = await db.listStreams(projectId);
|
|
59
|
+
const streamChoices = streams.map((s) => ({
|
|
66
60
|
name: `${s.name} (${s.type})`,
|
|
67
61
|
value: s.id
|
|
68
62
|
}));
|
|
69
|
-
streamChoices.push(new
|
|
70
|
-
streamChoices.push({ name:
|
|
71
|
-
streamChoices.push({ name:
|
|
72
|
-
const { streamId } = await
|
|
63
|
+
streamChoices.push(new inquirer.Separator());
|
|
64
|
+
streamChoices.push({ name: chalk.green("+ Create Stream"), value: "create_stream" });
|
|
65
|
+
streamChoices.push({ name: chalk.yellow("Back"), value: "back" });
|
|
66
|
+
const { streamId } = await inquirer.prompt([{
|
|
73
67
|
type: "list",
|
|
74
68
|
name: "streamId",
|
|
75
69
|
message: "Select a stream",
|
|
@@ -81,9 +75,9 @@ async function startTui(db, token) {
|
|
|
81
75
|
break;
|
|
82
76
|
if (streamId === "create_stream") {
|
|
83
77
|
showHeader();
|
|
84
|
-
console.log(
|
|
85
|
-
console.log(
|
|
86
|
-
const { name, type } = await
|
|
78
|
+
console.log(chalk.bold.blue(`Project: ${project?.name}`));
|
|
79
|
+
console.log(chalk.bold.blue(` └─ Create Stream\n`));
|
|
80
|
+
const { name, type } = await inquirer.prompt([
|
|
87
81
|
{
|
|
88
82
|
type: "input",
|
|
89
83
|
name: "name",
|
|
@@ -101,9 +95,9 @@ async function startTui(db, token) {
|
|
|
101
95
|
if (name && type) {
|
|
102
96
|
// For now, empty config
|
|
103
97
|
const s = await db.createStream(projectId, type, name, {});
|
|
104
|
-
console.log(
|
|
105
|
-
console.log(
|
|
106
|
-
await
|
|
98
|
+
console.log(chalk.green(`\nStream created!`));
|
|
99
|
+
console.log(chalk.bold.yellow(`Token: ${s.token}\n`));
|
|
100
|
+
await inquirer.prompt([{
|
|
107
101
|
type: "input",
|
|
108
102
|
name: "continue",
|
|
109
103
|
message: "Press enter to continue...",
|
|
@@ -115,10 +109,10 @@ async function startTui(db, token) {
|
|
|
115
109
|
// Stream Actions
|
|
116
110
|
while (true) {
|
|
117
111
|
showHeader();
|
|
118
|
-
const stream = streams.find(s => s.id === streamId);
|
|
119
|
-
console.log(
|
|
120
|
-
console.log(
|
|
121
|
-
const { action } = await
|
|
112
|
+
const stream = streams.find((s) => s.id === streamId);
|
|
113
|
+
console.log(chalk.bold.blue(`Project: ${project?.name}`));
|
|
114
|
+
console.log(chalk.bold.blue(` └─ Stream: ${stream?.name} (${stream?.type})\n`));
|
|
115
|
+
const { action } = await inquirer.prompt([{
|
|
122
116
|
type: "list",
|
|
123
117
|
name: "action",
|
|
124
118
|
message: "Action",
|
|
@@ -126,18 +120,17 @@ async function startTui(db, token) {
|
|
|
126
120
|
{ name: "View logs", value: "view_logs" },
|
|
127
121
|
{ name: "Get token", value: "get_token" },
|
|
128
122
|
{ name: "Delete stream", value: "delete_stream" },
|
|
129
|
-
{ name:
|
|
123
|
+
{ name: chalk.yellow("Back"), value: "back" }
|
|
130
124
|
],
|
|
131
125
|
prefix: "💡"
|
|
132
126
|
}]);
|
|
133
127
|
if (action === "back")
|
|
134
128
|
break;
|
|
135
129
|
if (action === "get_token") {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
console.log(
|
|
139
|
-
|
|
140
|
-
await inquirer_1.default.prompt([{
|
|
130
|
+
const token = await authService.createStreamToken(streamId);
|
|
131
|
+
console.log(chalk.green(`\nToken for ${stream?.name}:`));
|
|
132
|
+
console.log(chalk.bold.yellow(`${token}\n`));
|
|
133
|
+
await inquirer.prompt([{
|
|
141
134
|
type: "input",
|
|
142
135
|
name: "continue",
|
|
143
136
|
message: "Press enter to continue...",
|
|
@@ -145,30 +138,30 @@ async function startTui(db, token) {
|
|
|
145
138
|
}]);
|
|
146
139
|
}
|
|
147
140
|
if (action === "delete_stream") {
|
|
148
|
-
const { confirm } = await
|
|
141
|
+
const { confirm } = await inquirer.prompt([{
|
|
149
142
|
type: "confirm",
|
|
150
143
|
name: "confirm",
|
|
151
144
|
message: `Are you sure you want to delete stream ${stream?.name}?`
|
|
152
145
|
}]);
|
|
153
146
|
if (confirm) {
|
|
154
|
-
db.deleteStream(streamId);
|
|
147
|
+
await db.deleteStream(streamId);
|
|
155
148
|
break; // Go back to stream list
|
|
156
149
|
}
|
|
157
150
|
}
|
|
158
151
|
if (action === "view_logs") {
|
|
159
152
|
console.clear();
|
|
160
|
-
console.log(
|
|
161
|
-
const logs = db.getRecentLogs(streamId, 20);
|
|
153
|
+
console.log(chalk.bold.green(`Logs for ${stream?.name}:\n`));
|
|
154
|
+
const logs = await db.getRecentLogs(streamId, 20);
|
|
162
155
|
if (logs.length === 0) {
|
|
163
156
|
console.log("No logs recorded yet.");
|
|
164
157
|
}
|
|
165
158
|
else {
|
|
166
159
|
[...logs].reverse().forEach(log => {
|
|
167
|
-
console.log(`${
|
|
160
|
+
console.log(`${chalk.dim(log.timestamp)} ${log.content}`);
|
|
168
161
|
});
|
|
169
162
|
}
|
|
170
163
|
console.log("\n");
|
|
171
|
-
await
|
|
164
|
+
await inquirer.prompt([{
|
|
172
165
|
type: "input",
|
|
173
166
|
name: "return",
|
|
174
167
|
message: "Press enter to return...",
|
package/dist/utils/startup.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
async function ensureInfrastructure() {
|
|
11
|
-
console.log(chalk_1.default.bold.blue("\n🚀Performing system preflight checks..."));
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { OllamaService, migrate } from "@loghead/db";
|
|
3
|
+
import { dbAdapter } from "../db/client.js";
|
|
4
|
+
function isUnsupportedVectorIndexError(error) {
|
|
5
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
6
|
+
return msg.includes("invalid expression in CREATE INDEX") && msg.includes("libsql_vector_idx");
|
|
7
|
+
}
|
|
8
|
+
export async function ensureInfrastructure() {
|
|
9
|
+
console.log(chalk.bold.blue("\n🚀Performing system preflight checks..."));
|
|
12
10
|
// 1. Check Local Ollama
|
|
13
11
|
await checkStep("Checking local Ollama...", async () => {
|
|
14
12
|
try {
|
|
@@ -22,34 +20,38 @@ async function ensureInfrastructure() {
|
|
|
22
20
|
throw new Error("Ollama is not accessible at http://localhost:11434. Please install and run Ollama.");
|
|
23
21
|
}
|
|
24
22
|
});
|
|
25
|
-
// 2. Check Database & Migrations (
|
|
23
|
+
// 2. Check Database & Migrations (LibSQL)
|
|
26
24
|
await checkStep("Initializing database...", async () => {
|
|
27
25
|
try {
|
|
28
|
-
(
|
|
26
|
+
await migrate(dbAdapter, false);
|
|
29
27
|
}
|
|
30
28
|
catch (e) {
|
|
31
|
-
|
|
29
|
+
if (isUnsupportedVectorIndexError(e)) {
|
|
30
|
+
console.log(chalk.yellow("\n ➤ Embedded DB does not support vector index expressions yet; continuing without vec_logs_idx."));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.log(chalk.yellow("\n ➤ Migration failed..."));
|
|
32
34
|
throw e;
|
|
33
35
|
}
|
|
34
36
|
});
|
|
35
37
|
// 3. Check Ollama Model
|
|
36
38
|
await checkStep("Checking embedding model (qwen3-embedding)...", async () => {
|
|
37
|
-
const ollama = new
|
|
39
|
+
const ollama = new OllamaService();
|
|
38
40
|
await ollama.ensureModel();
|
|
39
41
|
});
|
|
40
|
-
console.log(`${
|
|
42
|
+
console.log(`${chalk.green("✔")} System preflight checks complete`);
|
|
41
43
|
}
|
|
42
44
|
async function checkStep(name, action) {
|
|
43
45
|
// Print pending state
|
|
44
|
-
process.stdout.write(`${
|
|
46
|
+
process.stdout.write(`${chalk.cyan("○")} ${name}`);
|
|
45
47
|
try {
|
|
46
48
|
await action();
|
|
47
49
|
// Clear line and print success
|
|
48
|
-
process.stdout.write(`\r${
|
|
50
|
+
process.stdout.write(`\r${chalk.green("✔")} ${name} \n`);
|
|
49
51
|
}
|
|
50
52
|
catch (e) {
|
|
51
|
-
process.stdout.write(`\r${
|
|
52
|
-
console.error(
|
|
53
|
+
process.stdout.write(`\r${chalk.red("✖")} ${name}\n`);
|
|
54
|
+
console.error(chalk.red(` Error: ${e instanceof Error ? e.message : e}`));
|
|
53
55
|
process.exit(1);
|
|
54
56
|
}
|
|
55
57
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loghead/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"license": "MIT",
|
|
5
6
|
"description": "Core API and Database for Loghead",
|
|
6
7
|
"repository": {
|
|
@@ -27,11 +28,13 @@
|
|
|
27
28
|
"ui": "tsx src/cli_main.ts ui",
|
|
28
29
|
"build:frontend": "cd frontend && yarn install && yarn build",
|
|
29
30
|
"build": "npm run build:frontend && tsc",
|
|
30
|
-
"dev": "tsx watch src/cli_main.ts",
|
|
31
|
+
"dev:backend": "tsx watch src/cli_main.ts",
|
|
32
|
+
"dev:frontend": "cd frontend && yarn build --watch",
|
|
33
|
+
"dev": "sh -c 'yarn dev:frontend & yarn dev:backend'",
|
|
31
34
|
"deploy": "npm publish --access public"
|
|
32
35
|
},
|
|
33
36
|
"dependencies": {
|
|
34
|
-
"
|
|
37
|
+
"@loghead/db": "*",
|
|
35
38
|
"chalk": "^5.3.0",
|
|
36
39
|
"cli-table3": "^0.6.3",
|
|
37
40
|
"cors": "^2.8.5",
|
|
@@ -41,11 +44,10 @@
|
|
|
41
44
|
"jsonwebtoken": "^9.0.2",
|
|
42
45
|
"ollama": "^0.5.9",
|
|
43
46
|
"open": "^8.4.2",
|
|
44
|
-
"
|
|
47
|
+
"@tursodatabase/database": "^0.4.2",
|
|
45
48
|
"yargs": "^17.7.2"
|
|
46
49
|
},
|
|
47
50
|
"devDependencies": {
|
|
48
|
-
"@types/better-sqlite3": "^7.6.9",
|
|
49
51
|
"@types/cors": "^2.8.17",
|
|
50
52
|
"@types/express": "^4.17.21",
|
|
51
53
|
"@types/inquirer": "^9.0.7",
|
package/build/npm/README.md
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
# Loghead
|
|
2
|
-
|
|
3
|
-
Loghead is a smart log aggregation tool and MCP server. It collects logs from various sources like your Terminal, Docker containers, or Browser, stores them in a database, and makes them searchable for AI assistants (like Claude, Cursor, or Windsurf).
|
|
4
|
-
|
|
5
|
-
Think of it as a "long-term memory" for your development logs that your AI coding agent can read.
|
|
6
|
-
|
|
7
|
-
## Prerequisites
|
|
8
|
-
|
|
9
|
-
Before you start, make sure you have:
|
|
10
|
-
|
|
11
|
-
1. **Local Ollama**: [Download here](https://ollama.com/download).
|
|
12
|
-
- Ensure it is running (`ollama serve`) and accessible at `http://localhost:11434`.
|
|
13
|
-
- Pull the embedding model: `ollama pull qwen3-embedding:0.6b` (or similar).
|
|
14
|
-
2. **The Loghead Executable**: You can download the latest release or build it yourself (see below).
|
|
15
|
-
|
|
16
|
-
_(Note: [Deno](https://docs.deno.com/runtime/fundamentals/installation/) is only required if you want to build the project from source.)_
|
|
17
|
-
|
|
18
|
-
## Setup Guide
|
|
19
|
-
|
|
20
|
-
Follow these steps to get Loghead running on your machine.
|
|
21
|
-
|
|
22
|
-
### 1. Install the Tool
|
|
23
|
-
|
|
24
|
-
Download the `loghead` binary from the releases page and move it to a directory in your PATH (e.g., `/usr/local/bin`).
|
|
25
|
-
|
|
26
|
-
**Verify installation:**
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
loghead --help
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### 2. Initialize the Database
|
|
33
|
-
|
|
34
|
-
Run this command to set up the database tables (SQLite):
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
loghead init
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## How to Use
|
|
41
|
-
|
|
42
|
-
### 1. Start the MCP Server
|
|
43
|
-
|
|
44
|
-
This is the bridge that allows your AI editor to talk to Loghead.
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
loghead start
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
You will see instructions on how to connect your specific AI tool (Claude, Cursor, VS Code, Windsurf) in the output.
|
|
51
|
-
|
|
52
|
-
**Example for Claude Desktop Config:**
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
"loghead": {
|
|
56
|
-
"command": "loghead",
|
|
57
|
-
"args": ["stdio"]
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### 2. Create a Project
|
|
62
|
-
|
|
63
|
-
Organize your logs into projects.
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
loghead projects add "My Awesome App"
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### 3. Add a Log Stream
|
|
70
|
-
|
|
71
|
-
A "stream" is a specific source of logs (e.g., your terminal output, or a specific Docker container). You need the Project ID from the previous step (use `loghead projects list` to see it).
|
|
72
|
-
|
|
73
|
-
**Example: Creating a stream for Docker logs**
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
loghead streams add docker --project <PROJECT_ID> --name "Backend API" --container my-api-container
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**Example: Creating a generic terminal stream**
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
loghead streams add terminal --project <PROJECT_ID> --name "Build Logs"
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### 4. Ingest Logs
|
|
86
|
-
|
|
87
|
-
Now, feed logs into the stream you created. You need the Stream ID (use `loghead streams list --project <PROJECT_ID>` to find it).
|
|
88
|
-
|
|
89
|
-
**From Standard Input (Manual):**
|
|
90
|
-
You can pipe any command's output into Loghead:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
echo "Something happened" | loghead ingest --stream <STREAM_ID>
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Or run a script and capture its output:
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
deno run my_script.ts | loghead ingest --stream <STREAM_ID>
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### 5. Query Logs (The AI Part)
|
|
103
|
-
|
|
104
|
-
Your AI assistant can now "call" tools to search these logs. It can ask things like:
|
|
105
|
-
|
|
106
|
-
- "Show me the recent errors in the Backend API stream."
|
|
107
|
-
- "Find logs related to 'database connection failure'."
|
|
108
|
-
|
|
109
|
-
You can also check logs manually:
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
npx loghead log list --stream <STREAM_ID>
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Building from Source
|
|
116
|
-
|
|
117
|
-
If you want to build the `loghead` executable yourself (e.g., to contribute or modify it):
|
|
118
|
-
|
|
119
|
-
1. Install **Deno** (see Prerequisites).
|
|
120
|
-
2. Compile the tool into a single executable file in the `build` directory:
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
mkdir -p build
|
|
124
|
-
deno task build
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
3. This will generate the `loghead` binary in your `packages/core/build/` directory.
|
|
128
|
-
|
|
129
|
-
## Architecture Overview
|
|
130
|
-
|
|
131
|
-
- **Language:** TypeScript (Deno)
|
|
132
|
-
- **Database:** SQLite with `sqlite-vec` for storing log embeddings.
|
|
133
|
-
- **AI:** Local Ollama running `mxbai-embed-large` (or similar) to understand the semantic meaning of logs.
|
|
134
|
-
- **Protocol:** Model Context Protocol (MCP) for integration with AI agents.
|
|
135
|
-
|
|
136
|
-
## Sample Apps
|
|
137
|
-
|
|
138
|
-
We provide sample applications in the `sample_apps` directory to help you test Loghead's capabilities.
|
|
139
|
-
|
|
140
|
-
### 1. CLI Calculator
|
|
141
|
-
|
|
142
|
-
A simple script that generates random logs and simulates a crash.
|
|
143
|
-
|
|
144
|
-
**How to test:**
|
|
145
|
-
|
|
146
|
-
1. Create a project and a stream:
|
|
147
|
-
```bash
|
|
148
|
-
npx loghead projects add "Calculator App"
|
|
149
|
-
# Copy Project ID
|
|
150
|
-
npx loghead streams add terminal --project <PROJECT_ID> --name "CLI Output"
|
|
151
|
-
# Copy Stream ID
|
|
152
|
-
```
|
|
153
|
-
2. Run the calculator and pipe logs to Loghead:
|
|
154
|
-
```bash
|
|
155
|
-
deno run sample_apps/cli_calculator/main.ts | npx @loghead/terminal --token <TOKEN>
|
|
156
|
-
```
|
|
157
|
-
3. Ask your AI Agent: "Why did the calculator app crash?"
|
|
158
|
-
|
|
159
|
-
### 2. Docker App
|
|
160
|
-
|
|
161
|
-
A Python worker process running in Docker that logs tasks and simulates occasional warnings.
|
|
162
|
-
|
|
163
|
-
**How to test:**
|
|
164
|
-
|
|
165
|
-
1. Create a stream for Docker:
|
|
166
|
-
```bash
|
|
167
|
-
# Use existing Project ID
|
|
168
|
-
npx loghead streams add docker --project <PROJECT_ID> --name "Worker Node" --container worker-app
|
|
169
|
-
# Note the container name "worker-app" matches the --name in docker run below
|
|
170
|
-
# Copy Stream ID
|
|
171
|
-
```
|
|
172
|
-
2. Build and run the container:
|
|
173
|
-
```bash
|
|
174
|
-
cd sample_apps/docker_app
|
|
175
|
-
docker build -t docker-app .
|
|
176
|
-
docker run --name worker-app -d docker-app
|
|
177
|
-
```
|
|
178
|
-
3. Attach Loghead to the container logs:
|
|
179
|
-
```bash
|
|
180
|
-
npx @loghead/docker --token <TOKEN> --container worker-app
|
|
181
|
-
```
|
|
182
|
-
4. Ask your AI Agent: "What tasks is the worker processing?" or "Are there any performance warnings?"
|
|
183
|
-
|
|
184
|
-
### 3. Browser App
|
|
185
|
-
|
|
186
|
-
A simple HTML/JS Calculator that logs actions to the browser console.
|
|
187
|
-
|
|
188
|
-
**How to test:**
|
|
189
|
-
|
|
190
|
-
1. Open `sample_apps/browser_app/index.html` in your browser.
|
|
191
|
-
2. Ensure the Loghead Chrome Extension is installed and connected to a browser stream.
|
|
192
|
-
3. Open Developer Tools (Console).
|
|
193
|
-
4. Perform some calculations (try dividing by zero!).
|
|
194
|
-
5. Logs will automatically appear in your Loghead dashboard.
|
package/build/npm/bin/loghead
DELETED
|
Binary file
|
package/build/npm/package.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@loghead/core",
|
|
3
|
-
"version": "0.1.1",
|
|
4
|
-
"description": "Smart log aggregation tool and MCP server",
|
|
5
|
-
"bin": {
|
|
6
|
-
"loghead": "./bin/loghead"
|
|
7
|
-
},
|
|
8
|
-
"files": [
|
|
9
|
-
"bin"
|
|
10
|
-
],
|
|
11
|
-
"os": [
|
|
12
|
-
"darwin"
|
|
13
|
-
],
|
|
14
|
-
"cpu": [
|
|
15
|
-
"arm64"
|
|
16
|
-
],
|
|
17
|
-
"scripts": {
|
|
18
|
-
"postinstall": "echo 'Note: This package contains a prebuilt binary for macOS ARM64.'"
|
|
19
|
-
},
|
|
20
|
-
"author": "Onvo AI",
|
|
21
|
-
"license": "MIT",
|
|
22
|
-
"publishConfig": {
|
|
23
|
-
"access": "public"
|
|
24
|
-
},
|
|
25
|
-
"repository": {
|
|
26
|
-
"type": "git",
|
|
27
|
-
"url": "git+https://github.com/onvo-ai/loghead.git"
|
|
28
|
-
},
|
|
29
|
-
"bugs": {
|
|
30
|
-
"url": "https://github.com/onvo-ai/loghead/issues"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
*,: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}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.left-0{left:0}.right-2{right:.5rem}.top-0{top:0}.top-2{top:.5rem}.top-full{top:100%}.z-50{z-index:50}.z-\[100\]{z-index:100}.col-span-full{grid-column:1 / -1}.m-4{margin:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mr-4{margin-right:1rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.block{display:block}.flex{display:flex}.grid{display:grid}.h-12{height:3rem}.h-16{height:4rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-\[50vh\]{height:50vh}.h-full{height:100%}.max-h-48{max-height:12rem}.max-h-60{max-height:15rem}.min-h-0{min-height:0px}.min-h-\[60px\]{min-height:60px}.min-h-screen{min-height:100vh}.w-12{width:3rem}.w-16{width:4rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-8{width:2rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-7xl{max-width:80rem}.max-w-lg{max-width:32rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.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-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))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.scroll-smooth{scroll-behavior:smooth}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-current{border-color:currentColor}.border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.border-gray-800{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity, 1))}.border-red-900{--tw-border-opacity: 1;border-color:rgb(127 29 29 / var(--tw-border-opacity, 1))}.bg-\[\#1e1e1e\]{--tw-bg-opacity: 1;background-color:rgb(30 30 30 / var(--tw-bg-opacity, 1))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\/50{background-color:#00000080}.bg-black\/80{background-color:#000c}.bg-blue-500\/20{background-color:#3b82f633}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-blue-900\/50{background-color:#1e3a8a80}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-gray-900\/50{background-color:#11182780}.bg-gray-950{--tw-bg-opacity: 1;background-color:rgb(3 7 18 / var(--tw-bg-opacity, 1))}.bg-gray-950\/50{background-color:#03071280}.bg-purple-500\/20{background-color:#a855f733}.bg-purple-600{--tw-bg-opacity: 1;background-color:rgb(147 51 234 / var(--tw-bg-opacity, 1))}.bg-red-900\/50{background-color:#7f1d1d80}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-gradient-to-tr{background-image:linear-gradient(to top right,var(--tw-gradient-stops))}.from-green-500{--tw-gradient-from: #22c55e var(--tw-gradient-from-position);--tw-gradient-to: rgb(34 197 94 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-emerald-600{--tw-gradient-to: #059669 var(--tw-gradient-to-position)}.p-1{padding:.25rem}.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-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pr-12{padding-right:3rem}.pt-1{padding-top:.25rem}.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}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.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}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-blue-200{--tw-text-opacity: 1;color:rgb(191 219 254 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-gray-100{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / 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-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-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.placeholder-gray-600::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(75 85 99 / var(--tw-placeholder-opacity, 1))}.placeholder-gray-600::placeholder{--tw-placeholder-opacity: 1;color:rgb(75 85 99 / var(--tw-placeholder-opacity, 1))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.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-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-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-sm{--tw-backdrop-blur: blur(4px);-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-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-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.hover\:border-gray-700:hover{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.hover\:bg-blue-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.hover\:bg-blue-900:hover{--tw-bg-opacity: 1;background-color:rgb(30 58 138 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-700:hover{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-900:hover{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.hover\:bg-purple-500:hover{--tw-bg-opacity: 1;background-color:rgb(168 85 247 / var(--tw-bg-opacity, 1))}.hover\:bg-red-900:hover{--tw-bg-opacity: 1;background-color:rgb(127 29 29 / var(--tw-bg-opacity, 1))}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.hover\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.focus\:border-purple-500:focus{--tw-border-opacity: 1;border-color:rgb(168 85 247 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.group:hover .group-hover\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width: 640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}
|