kanon-cli 0.1.0 → 0.1.1
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/bin/kanon.js +11 -0
- package/package.json +3 -2
- package/src/dashboard/dist/assets/index-7UwkIyFn.js +206 -0
- package/src/dashboard/dist/assets/index-DBQ473Y5.css +1 -0
- package/src/dashboard/dist/index.html +2 -2
- package/src/dashboard/server/index.js +36 -1
- package/src/dashboard/server/settings.js +244 -8
- package/src/dashboard/dist/assets/index-Dcbpx-Xz.js +0 -186
- package/src/dashboard/dist/assets/index-DhFfv70f.css +0 -1
|
@@ -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}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#374151;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#4b5563}.pointer-events-none{pointer-events:none}.\!visible{visibility:visible!important}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.-right-1{right:-.25rem}.-right-2{right:-.5rem}.-top-1{top:-.25rem}.left-0\.5{left:.125rem}.left-3{left:.75rem}.left-\[18px\]{left:18px}.right-2{right:.5rem}.right-3{right:.75rem}.top-0\.5{top:.125rem}.top-1\/2{top:50%}.top-8{top:2rem}.top-full{top:100%}.z-50{z-index:50}.col-span-2{grid-column:span 2 / span 2}.mx-2{margin-left:.5rem;margin-right:.5rem}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.max-h-40{max-height:10rem}.max-h-48{max-height:12rem}.max-h-60{max-height:15rem}.max-h-64{max-height:16rem}.max-h-\[60vh\]{max-height:60vh}.max-h-\[70vh\]{max-height:70vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-56{width:14rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-full{width:100%}.min-w-0{min-width:0px}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y: -50%;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))}.rotate-90{--tw-rotate: 90deg;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))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.select-text{-webkit-user-select:text;-moz-user-select:text;user-select:text}.resize-y{resize:vertical}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.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-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))}.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-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-r-2{border-right-width:2px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity, 1))}.border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.border-gray-700\/30{border-color:#3741514d}.border-gray-700\/50{border-color:#37415180}.border-gray-800{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity, 1))}.border-gray-800\/50{border-color:#1f293780}.border-green-500\/30{border-color:#22c55e4d}.border-kanon-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-kanon-500\/30{border-color:#3b82f64d}.border-red-500\/30{border-color:#ef44444d}.border-red-800\/40{border-color:#991b1b66}.border-transparent{border-color:transparent}.border-yellow-500\/30{border-color:#eab3084d}.bg-black\/50{background-color:#00000080}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-cyan-500\/10{background-color:#06b6d41a}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity, 1))}.bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-gray-800\/20{background-color:#1f293733}.bg-gray-800\/30{background-color:#1f29374d}.bg-gray-800\/50{background-color:#1f293780}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.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-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-500\/10{background-color:#22c55e1a}.bg-green-500\/20{background-color:#22c55e33}.bg-green-600\/20{background-color:#16a34a33}.bg-kanon-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-kanon-500\/10{background-color:#3b82f61a}.bg-kanon-500\/20{background-color:#3b82f633}.bg-kanon-500\/5{background-color:#3b82f60d}.bg-kanon-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-orange-500{--tw-bg-opacity: 1;background-color:rgb(249 115 22 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-500\/10{background-color:#ef44441a}.bg-red-500\/20{background-color:#ef444433}.bg-red-600\/20{background-color:#dc262633}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-yellow-500\/10{background-color:#eab3081a}.bg-yellow-500\/15{background-color:#eab30826}.bg-yellow-500\/20{background-color:#eab30833}.bg-yellow-600\/20{background-color:#ca8a0433}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.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}.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}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-3{padding-bottom:.75rem}.pl-3{padding-left:.75rem}.pr-16{padding-right:4rem}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.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}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-tight{line-height:1.25}.tracking-wider{letter-spacing:.05em}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-cyan-400{--tw-text-opacity: 1;color:rgb(34 211 238 / 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-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-400\/60{color:#4ade8099}.text-green-400\/70{color:#4ade80b3}.text-kanon-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.text-kanon-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-kanon-400\/60{color:#60a5fa99}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-transparent{color:transparent}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / 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))}.caret-gray-200{caret-color:#e5e7eb}.opacity-0{opacity:0}.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)}.ring-2{--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(2px + 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-white{--tw-ring-opacity: 1;--tw-ring-color: rgb(255 255 255 / var(--tw-ring-opacity, 1))}.ring-offset-2{--tw-ring-offset-width: 2px}.ring-offset-gray-900{--tw-ring-offset-color: #111827}.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)}.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}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.last\:border-0:last-child{border-width:0px}.hover\:scale-110:hover{--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))}.hover\:border-gray-600\/50:hover{border-color:#4b556380}.hover\:border-kanon-500\/50:hover{border-color:#3b82f680}.hover\:bg-gray-700\/50:hover{background-color:#37415180}.hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-800\/30:hover{background-color:#1f29374d}.hover\:bg-gray-800\/50:hover{background-color:#1f293780}.hover\:bg-gray-800\/80:hover{background-color:#1f2937cc}.hover\:bg-green-600\/30:hover{background-color:#16a34a4d}.hover\:bg-kanon-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.hover\:bg-kanon-500\/20:hover{background-color:#3b82f633}.hover\:bg-kanon-500\/5:hover{background-color:#3b82f60d}.hover\:bg-red-500\/10:hover{background-color:#ef44441a}.hover\:bg-red-500\/20:hover{background-color:#ef444433}.hover\:bg-red-600\/30:hover{background-color:#dc26264d}.hover\:bg-yellow-600\/30:hover{background-color:#ca8a044d}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.hover\:text-gray-400:hover{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.hover\:text-green-300:hover{--tw-text-opacity: 1;color:rgb(134 239 172 / var(--tw-text-opacity, 1))}.hover\:text-kanon-300:hover{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.hover\:text-kanon-400:hover{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.hover\:text-red-300:hover{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.hover\:text-red-400:hover{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.focus\:border-kanon-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.focus\:border-kanon-500\/50:focus{border-color:#3b82f680}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--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)}.focus\:ring-kanon-500\/30:focus{--tw-ring-color: rgb(59 130 246 / .3)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}@media(min-width:768px){.md\:block{display:block}}
|
|
@@ -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>Kanon Dashboard</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-7UwkIyFn.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DBQ473Y5.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body class="h-full text-gray-100">
|
|
11
11
|
<div id="root" class="h-full"></div>
|
|
@@ -12,13 +12,48 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
12
12
|
|
|
13
13
|
export function createDashboardServer(port = 3737) {
|
|
14
14
|
const app = express();
|
|
15
|
-
app.use(express.json());
|
|
15
|
+
app.use(express.json({ limit: '5mb' }));
|
|
16
16
|
|
|
17
17
|
// API routes
|
|
18
18
|
app.use('/api/kanon', createProxyRoutes());
|
|
19
19
|
app.use('/api/settings', createSettingsRoutes());
|
|
20
20
|
app.use('/api/agent', createAgentRoutes());
|
|
21
21
|
|
|
22
|
+
// GET /api/version — current version + check for updates
|
|
23
|
+
app.get('/api/version', async (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const pkgPath = path.resolve(__dirname, '../../../package.json');
|
|
26
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
27
|
+
const current = pkg.version;
|
|
28
|
+
|
|
29
|
+
let latest = current;
|
|
30
|
+
try {
|
|
31
|
+
const resp = await fetch('https://registry.npmjs.org/kanon-cli/latest', {
|
|
32
|
+
headers: { 'Accept': 'application/json' },
|
|
33
|
+
signal: AbortSignal.timeout(3000),
|
|
34
|
+
});
|
|
35
|
+
if (resp.ok) {
|
|
36
|
+
const data = await resp.json();
|
|
37
|
+
latest = data.version || current;
|
|
38
|
+
}
|
|
39
|
+
} catch {}
|
|
40
|
+
|
|
41
|
+
res.json({ current, latest, updateAvailable: latest !== current });
|
|
42
|
+
} catch (err) {
|
|
43
|
+
res.status(500).json({ error: err.message });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// POST /api/update — run npm update -g kanon-cli
|
|
48
|
+
app.post('/api/update', async (req, res) => {
|
|
49
|
+
try {
|
|
50
|
+
execSync('npm install -g kanon-cli@latest', { stdio: 'pipe', timeout: 30000 });
|
|
51
|
+
res.json({ ok: true });
|
|
52
|
+
} catch (err) {
|
|
53
|
+
res.status(500).json({ error: err.stderr?.toString() || err.message });
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
22
57
|
// POST /api/restart — restart server, optionally rebuild dashboard first
|
|
23
58
|
app.post('/api/restart', async (req, res) => {
|
|
24
59
|
const rebuild = req.query.rebuild === '1';
|
|
@@ -172,17 +172,36 @@ export function createSettingsRoutes() {
|
|
|
172
172
|
}
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
-
// GET /api/settings/credentials — returns global config
|
|
176
|
-
router.get('/credentials', (req, res) => {
|
|
175
|
+
// GET /api/settings/credentials — returns global config + agent profile from server
|
|
176
|
+
router.get('/credentials', async (req, res) => {
|
|
177
177
|
const config = loadGlobalConfig();
|
|
178
|
-
|
|
178
|
+
const base = {
|
|
179
179
|
server_url: config.server_url || '',
|
|
180
180
|
email: config.email || '',
|
|
181
|
-
password: config.password
|
|
181
|
+
password: config.password || '',
|
|
182
182
|
token: config.token || '',
|
|
183
183
|
user_name: config.user_name || '',
|
|
184
184
|
user_id: config.user_id || '',
|
|
185
|
-
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Fetch agent profile from server if we have a token
|
|
188
|
+
if (config.token) {
|
|
189
|
+
try {
|
|
190
|
+
const serverUrl = getServerUrl();
|
|
191
|
+
const profileRes = await fetch(`${serverUrl}/api/auth/me`, {
|
|
192
|
+
headers: { 'Authorization': `Bearer ${config.token}` },
|
|
193
|
+
});
|
|
194
|
+
if (profileRes.ok) {
|
|
195
|
+
const profile = await profileRes.json();
|
|
196
|
+
const user = profile.user || profile;
|
|
197
|
+
base.user_name = user.name || base.user_name;
|
|
198
|
+
base.color = user.color || '#6366f1';
|
|
199
|
+
base.avatar = user.avatar || '';
|
|
200
|
+
}
|
|
201
|
+
} catch {}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
res.json(base);
|
|
186
205
|
});
|
|
187
206
|
|
|
188
207
|
// PUT /api/settings/credentials — update global config
|
|
@@ -193,9 +212,7 @@ export function createSettingsRoutes() {
|
|
|
193
212
|
|
|
194
213
|
if (req.body.server_url) update.server_url = req.body.server_url;
|
|
195
214
|
if (req.body.email) update.email = req.body.email;
|
|
196
|
-
if (req.body.password
|
|
197
|
-
update.password = req.body.password;
|
|
198
|
-
}
|
|
215
|
+
if (req.body.password) update.password = req.body.password;
|
|
199
216
|
|
|
200
217
|
saveGlobalConfig(update);
|
|
201
218
|
res.json({ ok: true });
|
|
@@ -204,6 +221,117 @@ export function createSettingsRoutes() {
|
|
|
204
221
|
}
|
|
205
222
|
});
|
|
206
223
|
|
|
224
|
+
// PUT /api/settings/agent-profile — update agent name and color on Kanon server
|
|
225
|
+
router.put('/agent-profile', async (req, res) => {
|
|
226
|
+
try {
|
|
227
|
+
const config = loadGlobalConfig();
|
|
228
|
+
if (!config.token) return res.status(401).json({ error: 'Not logged in' });
|
|
229
|
+
const serverUrl = getServerUrl();
|
|
230
|
+
|
|
231
|
+
const { name, color } = req.body;
|
|
232
|
+
const profileRes = await fetch(`${serverUrl}/api/auth/me`, {
|
|
233
|
+
method: 'PUT',
|
|
234
|
+
headers: {
|
|
235
|
+
'Content-Type': 'application/json',
|
|
236
|
+
'Authorization': `Bearer ${config.token}`,
|
|
237
|
+
},
|
|
238
|
+
body: JSON.stringify({ name, color }),
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
if (!profileRes.ok) {
|
|
242
|
+
const data = await profileRes.json().catch(() => ({}));
|
|
243
|
+
return res.status(profileRes.status).json({ error: data.error || 'Failed to update profile' });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Update local config name
|
|
247
|
+
if (name) saveGlobalConfig({ ...config, user_name: name });
|
|
248
|
+
|
|
249
|
+
res.json({ ok: true });
|
|
250
|
+
} catch (err) {
|
|
251
|
+
res.status(500).json({ error: err.message });
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// POST /api/settings/agent-avatar — upload agent avatar to Kanon server
|
|
256
|
+
router.post('/agent-avatar', async (req, res) => {
|
|
257
|
+
try {
|
|
258
|
+
const config = loadGlobalConfig();
|
|
259
|
+
if (!config.token) return res.status(401).json({ error: 'Not logged in' });
|
|
260
|
+
const serverUrl = getServerUrl();
|
|
261
|
+
|
|
262
|
+
const { image } = req.body;
|
|
263
|
+
if (!image) return res.status(400).json({ error: 'No image data' });
|
|
264
|
+
|
|
265
|
+
const avatarRes = await fetch(`${serverUrl}/api/auth/me/avatar`, {
|
|
266
|
+
method: 'POST',
|
|
267
|
+
headers: {
|
|
268
|
+
'Content-Type': 'application/json',
|
|
269
|
+
'Authorization': `Bearer ${config.token}`,
|
|
270
|
+
},
|
|
271
|
+
body: JSON.stringify({ image }),
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
if (!avatarRes.ok) {
|
|
275
|
+
const data = await avatarRes.json().catch(() => ({}));
|
|
276
|
+
return res.status(avatarRes.status).json({ error: data.error || 'Failed to upload avatar' });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const data = await avatarRes.json();
|
|
280
|
+
res.json({ ok: true, avatar: data.user?.avatar || data.avatar });
|
|
281
|
+
} catch (err) {
|
|
282
|
+
res.status(500).json({ error: err.message });
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// DELETE /api/settings/agent-avatar — remove agent avatar
|
|
287
|
+
router.delete('/agent-avatar', async (req, res) => {
|
|
288
|
+
try {
|
|
289
|
+
const config = loadGlobalConfig();
|
|
290
|
+
if (!config.token) return res.status(401).json({ error: 'Not logged in' });
|
|
291
|
+
const serverUrl = getServerUrl();
|
|
292
|
+
|
|
293
|
+
const delRes = await fetch(`${serverUrl}/api/auth/me/avatar`, {
|
|
294
|
+
method: 'DELETE',
|
|
295
|
+
headers: { 'Authorization': `Bearer ${config.token}` },
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
if (!delRes.ok) {
|
|
299
|
+
const data = await delRes.json().catch(() => ({}));
|
|
300
|
+
return res.status(delRes.status).json({ error: data.error || 'Failed to remove avatar' });
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
res.json({ ok: true });
|
|
304
|
+
} catch (err) {
|
|
305
|
+
res.status(500).json({ error: err.message });
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// DELETE /api/settings/agent-account — delete agent account from Kanon server + clear local config
|
|
310
|
+
router.delete('/agent-account', async (req, res) => {
|
|
311
|
+
try {
|
|
312
|
+
const config = loadGlobalConfig();
|
|
313
|
+
if (!config.token) return res.status(401).json({ error: 'Not logged in' });
|
|
314
|
+
const serverUrl = getServerUrl();
|
|
315
|
+
|
|
316
|
+
const delRes = await fetch(`${serverUrl}/api/auth/me`, {
|
|
317
|
+
method: 'DELETE',
|
|
318
|
+
headers: { 'Authorization': `Bearer ${config.token}` },
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
if (!delRes.ok) {
|
|
322
|
+
const data = await delRes.json().catch(() => ({}));
|
|
323
|
+
return res.status(delRes.status).json({ error: data.error || 'Failed to delete account' });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Clear local credentials
|
|
327
|
+
saveGlobalConfig({ server_url: config.server_url });
|
|
328
|
+
|
|
329
|
+
res.json({ ok: true });
|
|
330
|
+
} catch (err) {
|
|
331
|
+
res.status(500).json({ error: err.message });
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
207
335
|
// POST /api/settings/test-connection — test login with stored credentials
|
|
208
336
|
router.post('/test-connection', async (req, res) => {
|
|
209
337
|
try {
|
|
@@ -232,5 +360,113 @@ export function createSettingsRoutes() {
|
|
|
232
360
|
}
|
|
233
361
|
});
|
|
234
362
|
|
|
363
|
+
// POST /api/settings/provision-agent — log in as user, create agent, save credentials
|
|
364
|
+
router.post('/provision-agent', async (req, res) => {
|
|
365
|
+
try {
|
|
366
|
+
const { email, password, agent_name, team_ids } = req.body;
|
|
367
|
+
const serverUrl = getServerUrl();
|
|
368
|
+
|
|
369
|
+
if (!email || !password || !agent_name) {
|
|
370
|
+
return res.status(400).json({ error: 'Email, password, and agent name are required' });
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// 1. Log in with user's personal credentials
|
|
374
|
+
const loginRes = await fetch(`${serverUrl}/api/auth/login`, {
|
|
375
|
+
method: 'POST',
|
|
376
|
+
headers: { 'Content-Type': 'application/json' },
|
|
377
|
+
body: JSON.stringify({ email, password }),
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
if (!loginRes.ok) {
|
|
381
|
+
const data = await loginRes.json().catch(() => ({}));
|
|
382
|
+
return res.status(401).json({ error: data.error || 'Login failed. Check your email and password.' });
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const loginData = await loginRes.json();
|
|
386
|
+
const userToken = loginData.token;
|
|
387
|
+
|
|
388
|
+
// 2. Provision agent via Kanon API
|
|
389
|
+
const provisionRes = await fetch(`${serverUrl}/api/agents/provision`, {
|
|
390
|
+
method: 'POST',
|
|
391
|
+
headers: {
|
|
392
|
+
'Content-Type': 'application/json',
|
|
393
|
+
'Authorization': `Bearer ${userToken}`,
|
|
394
|
+
},
|
|
395
|
+
body: JSON.stringify({ name: agent_name, team_ids: team_ids || [] }),
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
if (!provisionRes.ok) {
|
|
399
|
+
const data = await provisionRes.json().catch(() => ({}));
|
|
400
|
+
return res.status(provisionRes.status).json({ error: data.error || 'Failed to provision agent' });
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const provisionData = await provisionRes.json();
|
|
404
|
+
const agent = provisionData.agent;
|
|
405
|
+
|
|
406
|
+
// 3. Save agent credentials to global config
|
|
407
|
+
const current = loadGlobalConfig();
|
|
408
|
+
saveGlobalConfig({
|
|
409
|
+
...current,
|
|
410
|
+
server_url: serverUrl,
|
|
411
|
+
email: agent.email,
|
|
412
|
+
password: agent.password || current.password,
|
|
413
|
+
token: agent.token,
|
|
414
|
+
user_id: agent.id,
|
|
415
|
+
user_name: agent.name,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
res.json({
|
|
419
|
+
ok: true,
|
|
420
|
+
created: provisionData.created,
|
|
421
|
+
agent: {
|
|
422
|
+
id: agent.id,
|
|
423
|
+
email: agent.email,
|
|
424
|
+
name: agent.name,
|
|
425
|
+
teams: agent.teams || [],
|
|
426
|
+
},
|
|
427
|
+
skippedTeams: provisionData.skippedTeams || [],
|
|
428
|
+
});
|
|
429
|
+
} catch (err) {
|
|
430
|
+
res.status(500).json({ error: err.message });
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// POST /api/settings/provision-teams — get user's teams for provisioning UI
|
|
435
|
+
router.post('/provision-teams', async (req, res) => {
|
|
436
|
+
try {
|
|
437
|
+
const { email, password } = req.body;
|
|
438
|
+
const serverUrl = getServerUrl();
|
|
439
|
+
|
|
440
|
+
// Log in to get token
|
|
441
|
+
const loginRes = await fetch(`${serverUrl}/api/auth/login`, {
|
|
442
|
+
method: 'POST',
|
|
443
|
+
headers: { 'Content-Type': 'application/json' },
|
|
444
|
+
body: JSON.stringify({ email, password }),
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
if (!loginRes.ok) {
|
|
448
|
+
const data = await loginRes.json().catch(() => ({}));
|
|
449
|
+
return res.status(401).json({ error: data.error || 'Login failed' });
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const loginData = await loginRes.json();
|
|
453
|
+
|
|
454
|
+
// Fetch teams
|
|
455
|
+
const teamsRes = await fetch(`${serverUrl}/api/agents/teams`, {
|
|
456
|
+
headers: { 'Authorization': `Bearer ${loginData.token}` },
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
if (!teamsRes.ok) {
|
|
460
|
+
const data = await teamsRes.json().catch(() => ({}));
|
|
461
|
+
return res.status(teamsRes.status).json({ error: data.error || `Failed to fetch teams. Please try again later.` });
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const teamsData = await teamsRes.json();
|
|
465
|
+
res.json(teamsData);
|
|
466
|
+
} catch (err) {
|
|
467
|
+
res.status(500).json({ error: err.message });
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
235
471
|
return router;
|
|
236
472
|
}
|