aegis-bridge 2.9.2 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dashboard/dist/assets/index-PVPEFzdR.js +302 -0
- package/dashboard/dist/assets/index-hkHSc-eI.css +32 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/config.d.ts +9 -0
- package/dist/config.js +1 -0
- package/dist/events.d.ts +16 -2
- package/dist/events.js +10 -0
- package/dist/hook-settings.js +28 -5
- package/dist/server.js +47 -12
- package/dist/verification.d.ts +2 -0
- package/dist/verification.js +72 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/index-BPztPE61.css +0 -32
- package/dashboard/dist/assets/index-ClB9UIxG.js +0 -282
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
|
3
|
+
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
|
4
|
+
* https://github.com/chjj/term.js
|
|
5
|
+
* @license MIT
|
|
6
|
+
*
|
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
* furnished to do so, subject to the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
|
15
|
+
* all copies or substantial portions of the Software.
|
|
16
|
+
*
|
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
|
+
* THE SOFTWARE.
|
|
24
|
+
*
|
|
25
|
+
* Originally forked from (with the author's permission):
|
|
26
|
+
* Fabrice Bellard's javascript vt100 for jslinux:
|
|
27
|
+
* http://bellard.org/jslinux/
|
|
28
|
+
* Copyright (c) 2011 Fabrice Bellard
|
|
29
|
+
* The original design remains. The terminal itself
|
|
30
|
+
* has been extended to include xterm CSI codes, among
|
|
31
|
+
* other features.
|
|
32
|
+
*/.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono:"JetBrains Mono", "Fira Code", "Cascadia Code", "Consolas", monospace;--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-900:oklch(42.1% .095 57.708);--color-yellow-950:oklch(28.6% .066 53.813);--color-green-200:oklch(92.5% .084 155.995);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-900:oklch(39.3% .095 152.535);--color-green-950:oklch(26.6% .065 152.934);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-cyan-200:oklch(91.7% .08 205.041);--color-cyan-500:oklch(71.5% .143 215.221);--color-cyan-950:oklch(30.2% .056 229.695);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-2xl:42rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-6xl:3.75rem;--text-6xl--line-height:1;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-void:#0a0a0f;--color-void-light:#12121a;--color-void-lighter:#1a1a2e;--color-cyan:#00e5ff;--color-cyan-dim:#00e5ff40;--color-warning:#fa0;--color-error:#f36}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.right-0{right:calc(var(--spacing) * 0)}.right-4{right:calc(var(--spacing) * 4)}.bottom-0{bottom:calc(var(--spacing) * 0)}.bottom-4{bottom:calc(var(--spacing) * 4)}.left-0{left:calc(var(--spacing) * 0)}.isolate{isolation:isolate}.z-10{z-index:10}.z-50{z-index:50}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-0\.5{height:calc(var(--spacing) * .5)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-48{height:calc(var(--spacing) * 48)}.h-\[calc\(100vh-380px\)\]{height:calc(100vh - 380px)}.h-\[calc\(100vh-420px\)\]{height:calc(100vh - 420px)}.h-full{height:100%}.h-screen{height:100vh}.max-h-32{max-height:calc(var(--spacing) * 32)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[90vh\]{max-height:90vh}.max-h-\[300px\]{max-height:300px}.max-h-\[360px\]{max-height:360px}.max-h-\[420px\]{max-height:420px}.min-h-\[40px\]{min-height:40px}.min-h-\[44px\]{min-height:44px}.min-h-\[50vh\]{min-height:50vh}.min-h-\[88px\]{min-height:88px}.min-h-\[240px\]{min-height:240px}.min-h-\[250px\]{min-height:250px}.min-h-\[300px\]{min-height:300px}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-10{width:calc(var(--spacing) * 10)}.w-16{width:calc(var(--spacing) * 16)}.w-56{width:calc(var(--spacing) * 56)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-\[80\%\]{max-width:80%}.max-w-\[160px\]{max-width:160px}.max-w-\[200px\]{max-width:200px}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[44px\]{min-width:44px}.min-w-\[220px\]{min-width:220px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-none{resize:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[1fr_120px_1fr_44px\]{grid-template-columns:1fr 120px 1fr 44px}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-\[\#1a1a2e\]\/50>:not(:last-child)){border-color:#1a1a2e80}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-br-sm{border-bottom-right-radius:var(--radius-sm)}.rounded-bl-sm{border-bottom-left-radius:var(--radius-sm)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-\[\#00e5ff\]\/30{border-color:#00e5ff4d}.border-\[\#00e5ff\]\/40{border-color:#00e5ff66}.border-\[\#00ff88\]\/30{border-color:#00ff884d}.border-\[\#1a1a2e\]{border-color:#1a1a2e}.border-\[\#1a1a2e\]\/50{border-color:#1a1a2e80}.border-\[\#ff3366\]\/20{border-color:#f363}.border-\[\#ff3366\]\/30{border-color:#ff33664d}.border-\[\#ff3366\]\/40{border-color:#f366}.border-\[\#ff336620\]{border-color:#ff336620}.border-\[\#ffaa00\]\/30{border-color:#ffaa004d}.border-\[\#ffaa00\]\/40{border-color:#fa06}.border-amber-500\/20{border-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/20{border-color:color-mix(in oklab,var(--color-amber-500) 20%,transparent)}}.border-amber-500\/30{border-color:#f99c004d}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/30{border-color:color-mix(in oklab,var(--color-amber-500) 30%,transparent)}}.border-cyan{border-color:var(--color-cyan)}.border-cyan-500\/50{border-color:#00b7d780}@supports (color:color-mix(in lab,red,red)){.border-cyan-500\/50{border-color:color-mix(in oklab,var(--color-cyan-500) 50%,transparent)}}.border-cyan\/20{border-color:#00e5ff33}@supports (color:color-mix(in lab,red,red)){.border-cyan\/20{border-color:color-mix(in oklab,var(--color-cyan) 20%,transparent)}}.border-cyan\/30{border-color:#00e5ff4d}@supports (color:color-mix(in lab,red,red)){.border-cyan\/30{border-color:color-mix(in oklab,var(--color-cyan) 30%,transparent)}}.border-emerald-400\/20{border-color:#00d29433}@supports (color:color-mix(in lab,red,red)){.border-emerald-400\/20{border-color:color-mix(in oklab,var(--color-emerald-400) 20%,transparent)}}.border-emerald-400\/30{border-color:#00d2944d}@supports (color:color-mix(in lab,red,red)){.border-emerald-400\/30{border-color:color-mix(in oklab,var(--color-emerald-400) 30%,transparent)}}.border-emerald-500\/20{border-color:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/20{border-color:color-mix(in oklab,var(--color-emerald-500) 20%,transparent)}}.border-gray-500\/30{border-color:#6a72824d}@supports (color:color-mix(in lab,red,red)){.border-gray-500\/30{border-color:color-mix(in oklab,var(--color-gray-500) 30%,transparent)}}.border-green-500\/50{border-color:#00c75880}@supports (color:color-mix(in lab,red,red)){.border-green-500\/50{border-color:color-mix(in oklab,var(--color-green-500) 50%,transparent)}}.border-red-400\/30{border-color:#ff65684d}@supports (color:color-mix(in lab,red,red)){.border-red-400\/30{border-color:color-mix(in oklab,var(--color-red-400) 30%,transparent)}}.border-red-500\/20{border-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.border-red-500\/20{border-color:color-mix(in oklab,var(--color-red-500) 20%,transparent)}}.border-red-500\/50{border-color:#fb2c3680}@supports (color:color-mix(in lab,red,red)){.border-red-500\/50{border-color:color-mix(in oklab,var(--color-red-500) 50%,transparent)}}.border-void-lighter{border-color:var(--color-void-lighter)}.border-void-lighter\/50{border-color:#1a1a2e80}@supports (color:color-mix(in lab,red,red)){.border-void-lighter\/50{border-color:color-mix(in oklab,var(--color-void-lighter) 50%,transparent)}}.border-yellow-500\/50{border-color:#edb20080}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/50{border-color:color-mix(in oklab,var(--color-yellow-500) 50%,transparent)}}.bg-\[\#0a0a0f\]{background-color:#0a0a0f}.bg-\[\#0d0d12\]{background-color:#0d0d12}.bg-\[\#00e5ff\]{background-color:#00e5ff}.bg-\[\#00e5ff\]\/10{background-color:#00e5ff1a}.bg-\[\#1a1a00\]\/60{background-color:#1a1a0099}.bg-\[\#1a1a2e\]{background-color:#1a1a2e}.bg-\[\#1a1a3e\]{background-color:#1a1a3e}.bg-\[\#002a33\]{background-color:#002a33}.bg-\[\#2b2200\]{background-color:#2b2200}.bg-\[\#3a2e00\]{background-color:#3a2e00}.bg-\[\#003322\]{background-color:#032}.bg-\[\#003322\]\/50{background-color:#00332280}.bg-\[\#111118\]{background-color:#111118}.bg-\[\#331111\]{background-color:#311}.bg-\[\#ff3366\]\/10{background-color:#ff33661a}.bg-\[\#ff336610\]{background-color:#ff336610}.bg-amber-500\/5{background-color:#f99c000d}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/5{background-color:color-mix(in oklab,var(--color-amber-500) 5%,transparent)}}.bg-black{background-color:var(--color-black)}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-current{background-color:currentColor}.bg-cyan{background-color:var(--color-cyan)}.bg-cyan-950\/80{background-color:#053345cc}@supports (color:color-mix(in lab,red,red)){.bg-cyan-950\/80{background-color:color-mix(in oklab,var(--color-cyan-950) 80%,transparent)}}.bg-cyan\/5{background-color:#00e5ff0d}@supports (color:color-mix(in lab,red,red)){.bg-cyan\/5{background-color:color-mix(in oklab,var(--color-cyan) 5%,transparent)}}.bg-cyan\/10{background-color:#00e5ff1a}@supports (color:color-mix(in lab,red,red)){.bg-cyan\/10{background-color:color-mix(in oklab,var(--color-cyan) 10%,transparent)}}.bg-emerald-400\/10{background-color:#00d2941a}@supports (color:color-mix(in lab,red,red)){.bg-emerald-400\/10{background-color:color-mix(in oklab,var(--color-emerald-400) 10%,transparent)}}.bg-emerald-500\/5{background-color:#00bb7f0d}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/5{background-color:color-mix(in oklab,var(--color-emerald-500) 5%,transparent)}}.bg-gray-500\/10{background-color:#6a72821a}@supports (color:color-mix(in lab,red,red)){.bg-gray-500\/10{background-color:color-mix(in oklab,var(--color-gray-500) 10%,transparent)}}.bg-green-900\/30{background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.bg-green-900\/30{background-color:color-mix(in oklab,var(--color-green-900) 30%,transparent)}}.bg-green-950\/80{background-color:#032e15cc}@supports (color:color-mix(in lab,red,red)){.bg-green-950\/80{background-color:color-mix(in oklab,var(--color-green-950) 80%,transparent)}}.bg-red-400\/10{background-color:#ff65681a}@supports (color:color-mix(in lab,red,red)){.bg-red-400\/10{background-color:color-mix(in oklab,var(--color-red-400) 10%,transparent)}}.bg-red-500\/5{background-color:#fb2c360d}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/5{background-color:color-mix(in oklab,var(--color-red-500) 5%,transparent)}}.bg-red-900\/30{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.bg-red-900\/30{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.bg-red-950\/80{background-color:#460809cc}@supports (color:color-mix(in lab,red,red)){.bg-red-950\/80{background-color:color-mix(in oklab,var(--color-red-950) 80%,transparent)}}.bg-transparent{background-color:#0000}.bg-void{background-color:var(--color-void)}.bg-void-light{background-color:var(--color-void-light)}.bg-void-lighter{background-color:var(--color-void-lighter)}.bg-yellow-900\/30{background-color:#733e0a4d}@supports (color:color-mix(in lab,red,red)){.bg-yellow-900\/30{background-color:color-mix(in oklab,var(--color-yellow-900) 30%,transparent)}}.bg-yellow-950\/80{background-color:#432004cc}@supports (color:color-mix(in lab,red,red)){.bg-yellow-950\/80{background-color:color-mix(in oklab,var(--color-yellow-950) 80%,transparent)}}.object-contain{object-fit:contain}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.p-12{padding:calc(var(--spacing) * 12)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pb-0{padding-bottom:calc(var(--spacing) * 0)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#00e5ff\]{color:#00e5ff}.text-\[\#00ff88\]{color:#0f8}.text-\[\#9af5ff\]{color:#9af5ff}.text-\[\#333\]{color:#333}.text-\[\#444\]{color:#444}.text-\[\#555\]{color:#555}.text-\[\#666\]{color:#666}.text-\[\#888\]{color:#888}.text-\[\#e0e0e0\]{color:#e0e0e0}.text-\[\#ff3366\]{color:#f36}.text-\[\#ffaa00\]{color:#fa0}.text-amber-200{color:var(--color-amber-200)}.text-amber-200\/80{color:#fee685cc}@supports (color:color-mix(in lab,red,red)){.text-amber-200\/80{color:color-mix(in oklab,var(--color-amber-200) 80%,transparent)}}.text-amber-400{color:var(--color-amber-400)}.text-amber-500{color:var(--color-amber-500)}.text-cyan{color:var(--color-cyan)}.text-cyan-200{color:var(--color-cyan-200)}.text-emerald-200\/80{color:#a4f4cfcc}@supports (color:color-mix(in lab,red,red)){.text-emerald-200\/80{color:color-mix(in oklab,var(--color-emerald-200) 80%,transparent)}}.text-emerald-300{color:var(--color-emerald-300)}.text-emerald-400{color:var(--color-emerald-400)}.text-gray-100{color:var(--color-gray-100)}.text-gray-200{color:var(--color-gray-200)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-green-200{color:var(--color-green-200)}.text-green-400{color:var(--color-green-400)}.text-red-200{color:var(--color-red-200)}.text-red-300{color:var(--color-red-300)}.text-red-400{color:var(--color-red-400)}.text-void{color:var(--color-void)}.text-yellow-200{color:var(--color-yellow-200)}.text-yellow-300{color:var(--color-yellow-300)}.text-yellow-400{color:var(--color-yellow-400)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-gray-600::placeholder{color:var(--color-gray-600)}.opacity-40{opacity:.4}.opacity-60{opacity:.6}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),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(var(--blur-sm));-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-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.outline-none{--tw-outline-style:none;outline-style:none}.placeholder\:text-gray-500::placeholder{color:var(--color-gray-500)}.focus-within\:border-cyan:focus-within{border-color:var(--color-cyan)}@media(hover:hover){.hover\:border-l-2:hover{border-left-style:var(--tw-border-style);border-left-width:2px}.hover\:border-\[\#00e5ff\]\/30:hover{border-color:#00e5ff4d}.hover\:border-cyan\/40:hover{border-color:#00e5ff66}@supports (color:color-mix(in lab,red,red)){.hover\:border-cyan\/40:hover{border-color:color-mix(in oklab,var(--color-cyan) 40%,transparent)}}.hover\:border-gray-500:hover{border-color:var(--color-gray-500)}.hover\:border-l-cyan:hover{border-left-color:var(--color-cyan)}.hover\:bg-\[\#00e5ff\]\/20:hover{background-color:#00e5ff33}.hover\:bg-\[\#1a1a2e\]\/30:hover{background-color:#1a1a2e4d}.hover\:bg-\[\#2a2a3e\]:hover{background-color:#2a2a3e}.hover\:bg-\[\#3a2e00\]:hover{background-color:#3a2e00}.hover\:bg-\[\#4a3900\]:hover{background-color:#4a3900}.hover\:bg-\[\#003744\]:hover{background-color:#003744}.hover\:bg-\[\#004433\]:hover{background-color:#043}.hover\:bg-\[\#442222\]:hover{background-color:#422}.hover\:bg-amber-500\/10:hover{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-amber-500\/10:hover{background-color:color-mix(in oklab,var(--color-amber-500) 10%,transparent)}}.hover\:bg-cyan\/80:hover{background-color:#00e5ffcc}@supports (color:color-mix(in lab,red,red)){.hover\:bg-cyan\/80:hover{background-color:color-mix(in oklab,var(--color-cyan) 80%,transparent)}}.hover\:bg-green-900\/50:hover{background-color:#0d542b80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-green-900\/50:hover{background-color:color-mix(in oklab,var(--color-green-900) 50%,transparent)}}.hover\:bg-red-500\/10:hover{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/10:hover{background-color:color-mix(in oklab,var(--color-red-500) 10%,transparent)}}.hover\:bg-red-900\/50:hover{background-color:#82181a80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-900\/50:hover{background-color:color-mix(in oklab,var(--color-red-900) 50%,transparent)}}.hover\:bg-void-lighter:hover{background-color:var(--color-void-lighter)}.hover\:bg-yellow-900\/50:hover{background-color:#733e0a80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-900\/50:hover{background-color:color-mix(in oklab,var(--color-yellow-900) 50%,transparent)}}.hover\:text-\[\#00e5ff\]:hover{color:#00e5ff}.hover\:text-\[\#777\]:hover{color:#777}.hover\:text-\[\#888\]:hover{color:#888}.hover\:text-\[\#e0e0e0\]:hover{color:#e0e0e0}.hover\:text-\[\#ff3366\]:hover{color:#f36}.hover\:text-cyan:hover{color:var(--color-cyan)}.hover\:text-emerald-200:hover{color:var(--color-emerald-200)}.hover\:text-gray-200:hover{color:var(--color-gray-200)}.hover\:text-gray-300:hover{color:var(--color-gray-300)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}}.focus\:border-\[\#00e5ff\]:focus{border-color:#00e5ff}.focus\:border-\[\#ffaa00\]:focus{border-color:#fa0}.focus\:border-cyan:focus{border-color:var(--color-cyan)}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-cyan:focus{--tw-ring-color:var(--color-cyan)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:bg-\[\#1a1a2e\]\/50:active{background-color:#1a1a2e80}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}@media(min-width:40rem){.sm\:flex{display:flex}.sm\:inline{display:inline}.sm\:inline-block{display:inline-block}.sm\:h-\[calc\(100vh-420px\)\]{height:calc(100vh - 420px)}.sm\:h-\[calc\(100vh-460px\)\]{height:calc(100vh - 460px)}.sm\:min-h-\[300px\]{min-height:300px}.sm\:min-h-\[400px\]{min-height:400px}.sm\:max-w-xs{max-width:var(--container-xs)}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-2{gap:calc(var(--spacing) * 2)}.sm\:gap-3{gap:calc(var(--spacing) * 3)}.sm\:gap-4{gap:calc(var(--spacing) * 4)}:where(.sm\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.sm\:p-4{padding:calc(var(--spacing) * 4)}.sm\:p-5{padding:calc(var(--spacing) * 5)}.sm\:px-4{padding-inline:calc(var(--spacing) * 4)}.sm\:px-5{padding-inline:calc(var(--spacing) * 5)}.sm\:py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.sm\:py-4{padding-block:calc(var(--spacing) * 4)}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}}@media(min-width:48rem){.md\:block{display:block}.md\:hidden{display:none}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:justify-between{justify-content:space-between}}@media(min-width:64rem){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:items-center{align-items:center}.lg\:items-start{align-items:flex-start}.lg\:justify-between{justify-content:space-between}}@media(min-width:80rem){.xl\:grid-cols-\[minmax\(0\,360px\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,360px) minmax(0,1fr)}.xl\:flex-row{flex-direction:row}.xl\:items-start{align-items:flex-start}.xl\:justify-between{justify-content:space-between}}}body{font-family:var(--font-sans);background-color:var(--color-void);color:#e0e0e8}*{scrollbar-width:thin;scrollbar-color:var(--color-void-lighter) transparent}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--color-void-lighter);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--color-cyan-dim)}.status-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px;display:inline-block}.status-dot--idle{background-color:var(--color-cyan);box-shadow:0 0 6px var(--color-cyan-dim)}.status-dot--working{background-color:var(--color-warning);animation:1.5s ease-in-out infinite pulse;box-shadow:0 0 6px #ffaa0040}.status-dot--permission_prompt,.status-dot--bash_approval{background-color:var(--color-error);animation:1s ease-in-out infinite pulse;box-shadow:0 0 6px #ff336640}.status-dot--plan_mode,.status-dot--ask_question{background-color:#a78bfa;box-shadow:0 0 6px #a78bfa40}.status-dot--settings{background-color:var(--color-warning)}.status-dot--unknown{background-color:#666}@keyframes pulse{50%{opacity:.5}}.font-code{font-family:var(--font-mono)}.glow-cyan{box-shadow:0 0 12px var(--color-cyan-dim)}.glow-text-cyan{text-shadow:0 0 8px var(--color-cyan-dim)}@keyframes slide-in{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}.animate-slide-in{animation:.2s ease-out slide-in}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Aegis Dashboard</title>
|
|
7
7
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🛡️</text></svg>" />
|
|
8
|
-
<script type="module" crossorigin src="/dashboard/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/dashboard/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/dashboard/assets/index-PVPEFzdR.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/dashboard/assets/index-hkHSc-eI.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body class="bg-[#0a0a0f] text-gray-200 antialiased">
|
|
12
12
|
<div id="root"></div>
|
package/dist/config.d.ts
CHANGED
|
@@ -68,6 +68,15 @@ export interface Config {
|
|
|
68
68
|
/** Issue #884: Additional Claude projects directories to search during worktree fanout.
|
|
69
69
|
* Paths are expanded (~) and checked for existence before searching. */
|
|
70
70
|
worktreeSiblingDirs: string[];
|
|
71
|
+
/** Issue #740: Verification Protocol — auto run quality gate after session ends.
|
|
72
|
+
* When enabled, Aegis runs tsc + build + test after a Stop hook and emits
|
|
73
|
+
* results via SSE (event: 'verification'). */
|
|
74
|
+
verificationProtocol: {
|
|
75
|
+
/** Auto-run verification when Stop hook fires (default: false). */
|
|
76
|
+
autoVerifyOnStop: boolean;
|
|
77
|
+
/** Run only critical checks: tsc + build (skip slow tests). Default: false = full. */
|
|
78
|
+
criticalOnly: boolean;
|
|
79
|
+
};
|
|
71
80
|
}
|
|
72
81
|
/** Compute stall threshold from env var or default (Issue #392).
|
|
73
82
|
* If CLAUDE_STREAM_IDLE_TIMEOUT_MS is set, uses Math.max(120000, parseInt(val) * 1.5).
|
package/dist/config.js
CHANGED
|
@@ -49,6 +49,7 @@ const defaults = {
|
|
|
49
49
|
worktreeAwareContinuation: false,
|
|
50
50
|
memoryBridge: { enabled: false },
|
|
51
51
|
worktreeSiblingDirs: [],
|
|
52
|
+
verificationProtocol: { autoVerifyOnStop: false, criticalOnly: false },
|
|
52
53
|
};
|
|
53
54
|
/** Parse CLI args for --config flag */
|
|
54
55
|
function getConfigPathFromArgv() {
|
package/dist/events.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* The monitor pushes events; the SSE route consumes them.
|
|
7
7
|
*/
|
|
8
8
|
export interface SessionSSEEvent {
|
|
9
|
-
event: 'status' | 'message' | 'system' | 'approval' | 'ended' | 'heartbeat' | 'stall' | 'dead' | 'hook' | 'subagent_start' | 'subagent_stop';
|
|
9
|
+
event: 'status' | 'message' | 'system' | 'approval' | 'ended' | 'heartbeat' | 'stall' | 'dead' | 'hook' | 'subagent_start' | 'subagent_stop' | 'verification';
|
|
10
10
|
sessionId: string;
|
|
11
11
|
timestamp: string;
|
|
12
12
|
data: Record<string, unknown>;
|
|
@@ -15,8 +15,20 @@ export interface SessionSSEEvent {
|
|
|
15
15
|
/** Issue #308: Incrementing event ID for Last-Event-ID replay. */
|
|
16
16
|
id?: number;
|
|
17
17
|
}
|
|
18
|
+
export interface VerificationResult {
|
|
19
|
+
ok: boolean;
|
|
20
|
+
steps: {
|
|
21
|
+
name: 'tsc' | 'build' | 'test';
|
|
22
|
+
ok: boolean;
|
|
23
|
+
durationMs: number;
|
|
24
|
+
output?: string;
|
|
25
|
+
error?: string;
|
|
26
|
+
}[];
|
|
27
|
+
totalDurationMs: number;
|
|
28
|
+
summary: string;
|
|
29
|
+
}
|
|
18
30
|
export interface GlobalSSEEvent {
|
|
19
|
-
event: 'session_status_change' | 'session_message' | 'session_approval' | 'session_ended' | 'session_created' | 'session_stall' | 'session_dead' | 'session_subagent_start' | 'session_subagent_stop';
|
|
31
|
+
event: 'session_status_change' | 'session_message' | 'session_approval' | 'session_ended' | 'session_created' | 'session_stall' | 'session_dead' | 'session_subagent_start' | 'session_subagent_stop' | 'session_verification';
|
|
20
32
|
sessionId: string;
|
|
21
33
|
timestamp: string;
|
|
22
34
|
data: Record<string, unknown>;
|
|
@@ -65,6 +77,8 @@ export declare class SessionEventBus {
|
|
|
65
77
|
};
|
|
66
78
|
/** Emit a status change event. */
|
|
67
79
|
emitStatus(sessionId: string, status: string, detail: string): void;
|
|
80
|
+
/** Issue #740: Emit a verification result event. */
|
|
81
|
+
emitVerification(sessionId: string, result: VerificationResult): void;
|
|
68
82
|
/** Emit a message event. */
|
|
69
83
|
emitMessage(sessionId: string, role: string, text: string, contentType?: string, toolMeta?: {
|
|
70
84
|
tool_name?: string;
|
package/dist/events.js
CHANGED
|
@@ -16,6 +16,7 @@ function toGlobalEvent(event) {
|
|
|
16
16
|
approval: 'session_approval',
|
|
17
17
|
ended: 'session_ended',
|
|
18
18
|
heartbeat: 'session_status_change',
|
|
19
|
+
verification: 'session_verification',
|
|
19
20
|
stall: 'session_stall',
|
|
20
21
|
dead: 'session_dead',
|
|
21
22
|
subagent_start: 'session_subagent_start',
|
|
@@ -167,6 +168,15 @@ export class SessionEventBus {
|
|
|
167
168
|
data: { status, detail },
|
|
168
169
|
});
|
|
169
170
|
}
|
|
171
|
+
/** Issue #740: Emit a verification result event. */
|
|
172
|
+
emitVerification(sessionId, result) {
|
|
173
|
+
this.emit(sessionId, {
|
|
174
|
+
event: 'verification',
|
|
175
|
+
sessionId,
|
|
176
|
+
timestamp: new Date().toISOString(),
|
|
177
|
+
data: result,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
170
180
|
/** Emit a message event. */
|
|
171
181
|
emitMessage(sessionId, role, text, contentType, toolMeta) {
|
|
172
182
|
this.emit(sessionId, {
|
package/dist/hook-settings.js
CHANGED
|
@@ -19,6 +19,31 @@ import { join, resolve, normalize } from 'node:path';
|
|
|
19
19
|
import { tmpdir } from 'node:os';
|
|
20
20
|
import { randomBytes } from 'node:crypto';
|
|
21
21
|
import { ccSettingsSchema } from './validation.js';
|
|
22
|
+
function isRecord(value) {
|
|
23
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
24
|
+
}
|
|
25
|
+
function parseSettingsWithFallback(raw) {
|
|
26
|
+
const json = JSON.parse(raw);
|
|
27
|
+
if (!isRecord(json))
|
|
28
|
+
return undefined;
|
|
29
|
+
const parsed = ccSettingsSchema.safeParse(json);
|
|
30
|
+
if (parsed.success)
|
|
31
|
+
return parsed.data;
|
|
32
|
+
// Preserve unknown/extra fields (including env vars) even when schema validation fails.
|
|
33
|
+
return json;
|
|
34
|
+
}
|
|
35
|
+
function normalizeHookBaseUrl(baseUrl) {
|
|
36
|
+
try {
|
|
37
|
+
const url = new URL(baseUrl);
|
|
38
|
+
if (url.hostname === '0.0.0.0' || url.hostname === '::' || url.hostname === '[::]') {
|
|
39
|
+
url.hostname = '127.0.0.1';
|
|
40
|
+
}
|
|
41
|
+
return url.origin;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return baseUrl.replace('0.0.0.0', '127.0.0.1');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
22
47
|
/**
|
|
23
48
|
* Validate a workDir path for use in hook settings resolution.
|
|
24
49
|
* Defense-in-depth against path traversal: rejects paths containing ".." segments
|
|
@@ -91,6 +116,7 @@ export { HTTP_HOOK_EVENTS };
|
|
|
91
116
|
*/
|
|
92
117
|
export function generateHookSettings(baseUrl, sessionId, hookSecret) {
|
|
93
118
|
const hooks = {};
|
|
119
|
+
const callbackBaseUrl = normalizeHookBaseUrl(baseUrl);
|
|
94
120
|
for (const event of HTTP_HOOK_EVENTS) {
|
|
95
121
|
const secretParam = hookSecret ? `&secret=${hookSecret}` : '';
|
|
96
122
|
hooks[event] = [
|
|
@@ -98,7 +124,7 @@ export function generateHookSettings(baseUrl, sessionId, hookSecret) {
|
|
|
98
124
|
hooks: [
|
|
99
125
|
{
|
|
100
126
|
type: 'http',
|
|
101
|
-
url: `${
|
|
127
|
+
url: `${callbackBaseUrl}/v1/hooks/${event}?sessionId=${sessionId}${secretParam}`,
|
|
102
128
|
},
|
|
103
129
|
],
|
|
104
130
|
},
|
|
@@ -130,10 +156,7 @@ export async function writeHookSettingsFile(baseUrl, sessionId, hookSecret, work
|
|
|
130
156
|
if (existsSync(projectSettingsPath)) {
|
|
131
157
|
try {
|
|
132
158
|
const raw = await readFile(projectSettingsPath, 'utf-8');
|
|
133
|
-
|
|
134
|
-
if (parsed.success) {
|
|
135
|
-
merged = parsed.data;
|
|
136
|
-
}
|
|
159
|
+
merged = parseSettingsWithFallback(raw) ?? {};
|
|
137
160
|
}
|
|
138
161
|
catch {
|
|
139
162
|
// Malformed settings file — use empty base, hooks will still work
|
package/dist/server.js
CHANGED
|
@@ -26,6 +26,7 @@ import { captureScreenshot, isPlaywrightAvailable } from './screenshot.js';
|
|
|
26
26
|
import { validateScreenshotUrl, resolveAndCheckIp, buildHostResolverRule } from './ssrf.js';
|
|
27
27
|
import { validateWorkDir, permissionRuleSchema } from './validation.js';
|
|
28
28
|
import { SessionEventBus } from './events.js';
|
|
29
|
+
import { runVerification } from './verification.js';
|
|
29
30
|
import { SSEWriter } from './sse-writer.js';
|
|
30
31
|
import { SSEConnectionLimiter } from './sse-limiter.js';
|
|
31
32
|
import { PipelineManager } from './pipeline.js';
|
|
@@ -431,7 +432,8 @@ app.get('/v1/sessions/:id/metrics', async (req, reply) => {
|
|
|
431
432
|
});
|
|
432
433
|
// Issue #704: Tool usage endpoints
|
|
433
434
|
app.get('/v1/sessions/:id/tools', async (req, reply) => {
|
|
434
|
-
const
|
|
435
|
+
const sessionId = req.params.id;
|
|
436
|
+
const session = sessions.getSession(sessionId);
|
|
435
437
|
if (!session)
|
|
436
438
|
return reply.status(404).send({ error: 'Session not found' });
|
|
437
439
|
// Parse JSONL on-demand for tool usage
|
|
@@ -472,7 +474,8 @@ app.get('/v1/channels/health', async () => {
|
|
|
472
474
|
});
|
|
473
475
|
// Issue #87: Per-session latency metrics
|
|
474
476
|
app.get('/v1/sessions/:id/latency', async (req, reply) => {
|
|
475
|
-
const
|
|
477
|
+
const sessionId = req.params.id;
|
|
478
|
+
const session = sessions.getSession(sessionId);
|
|
476
479
|
if (!session)
|
|
477
480
|
return reply.status(404).send({ error: 'Session not found' });
|
|
478
481
|
const realtimeLatency = sessions.getLatencyMetrics(req.params.id);
|
|
@@ -678,7 +681,8 @@ app.post('/v1/sessions', createSessionHandler);
|
|
|
678
681
|
app.post('/sessions', createSessionHandler);
|
|
679
682
|
// Get session (Issue #20: includes actionHints for interactive states)
|
|
680
683
|
async function getSessionHandler(req, reply) {
|
|
681
|
-
const
|
|
684
|
+
const sessionId = req.params.id;
|
|
685
|
+
const session = sessions.getSession(sessionId);
|
|
682
686
|
if (!session)
|
|
683
687
|
return reply.status(404).send({ error: 'Session not found' });
|
|
684
688
|
return addActionHints(session, sessions);
|
|
@@ -739,7 +743,8 @@ app.post('/v1/sessions/:id/send', sendMessageHandler);
|
|
|
739
743
|
app.post('/sessions/:id/send', sendMessageHandler);
|
|
740
744
|
// Issue #702: GET children sessions
|
|
741
745
|
async function getChildrenHandler(req, reply) {
|
|
742
|
-
const
|
|
746
|
+
const sessionId = req.params.id;
|
|
747
|
+
const session = sessions.getSession(sessionId);
|
|
743
748
|
if (!session)
|
|
744
749
|
return reply.status(404).send({ error: 'Session not found' });
|
|
745
750
|
const children = (session.children ?? []).map(id => {
|
|
@@ -771,13 +776,15 @@ async function spawnChildHandler(req, reply) {
|
|
|
771
776
|
app.post('/v1/sessions/:id/spawn', spawnChildHandler);
|
|
772
777
|
app.post('/sessions/:id/spawn', spawnChildHandler);
|
|
773
778
|
async function getPermissionPolicyHandler(req, reply) {
|
|
774
|
-
const
|
|
779
|
+
const sessionId = req.params.id;
|
|
780
|
+
const session = sessions.getSession(sessionId);
|
|
775
781
|
if (!session)
|
|
776
782
|
return reply.status(404).send({ error: 'Session not found' });
|
|
777
783
|
return { permissionPolicy: session.permissionPolicy ?? [] };
|
|
778
784
|
}
|
|
779
785
|
async function updatePermissionPolicyHandler(req, reply) {
|
|
780
|
-
const
|
|
786
|
+
const sessionId = req.params.id;
|
|
787
|
+
const session = sessions.getSession(sessionId);
|
|
781
788
|
if (!session)
|
|
782
789
|
return reply.status(404).send({ error: 'Session not found' });
|
|
783
790
|
const policy = req.body ?? [];
|
|
@@ -816,7 +823,8 @@ app.post('/v1/sessions/:id/answer', async (req, reply) => {
|
|
|
816
823
|
if (!questionId || answer === undefined || answer === null) {
|
|
817
824
|
return reply.status(400).send({ error: 'questionId and answer are required' });
|
|
818
825
|
}
|
|
819
|
-
const
|
|
826
|
+
const sessionId = req.params.id;
|
|
827
|
+
const session = sessions.getSession(sessionId);
|
|
820
828
|
if (!session)
|
|
821
829
|
return reply.status(404).send({ error: 'Session not found' });
|
|
822
830
|
const resolved = sessions.submitAnswer(req.params.id, questionId, answer);
|
|
@@ -872,7 +880,8 @@ app.delete('/v1/sessions/:id', killSessionHandler);
|
|
|
872
880
|
app.delete('/sessions/:id', killSessionHandler);
|
|
873
881
|
// Capture raw pane
|
|
874
882
|
async function capturePaneHandler(req, reply) {
|
|
875
|
-
const
|
|
883
|
+
const sessionId = req.params.id;
|
|
884
|
+
const session = sessions.getSession(sessionId);
|
|
876
885
|
if (!session)
|
|
877
886
|
return reply.status(404).send({ error: 'Session not found' });
|
|
878
887
|
const pane = await tmux.capturePane(session.windowId);
|
|
@@ -979,7 +988,8 @@ async function screenshotHandler(req, reply) {
|
|
|
979
988
|
if (dnsResult.error)
|
|
980
989
|
return reply.status(400).send({ error: dnsResult.error });
|
|
981
990
|
// Validate session exists
|
|
982
|
-
const
|
|
991
|
+
const sessionId = req.params.id;
|
|
992
|
+
const session = sessions.getSession(sessionId);
|
|
983
993
|
if (!session)
|
|
984
994
|
return reply.status(404).send({ error: 'Session not found' });
|
|
985
995
|
if (!isPlaywrightAvailable()) {
|
|
@@ -1004,7 +1014,8 @@ app.post('/v1/sessions/:id/screenshot', screenshotHandler);
|
|
|
1004
1014
|
app.post('/sessions/:id/screenshot', screenshotHandler);
|
|
1005
1015
|
// SSE event stream (Issue #32)
|
|
1006
1016
|
app.get('/v1/sessions/:id/events', async (req, reply) => {
|
|
1007
|
-
const
|
|
1017
|
+
const sessionId = req.params.id;
|
|
1018
|
+
const session = sessions.getSession(sessionId);
|
|
1008
1019
|
if (!session)
|
|
1009
1020
|
return reply.status(404).send({ error: 'Session not found' });
|
|
1010
1021
|
const clientIp = req.ip;
|
|
@@ -1076,7 +1087,8 @@ app.get('/v1/sessions/:id/events', async (req, reply) => {
|
|
|
1076
1087
|
// ── Claude Code Hook Endpoints (Issue #161) ─────────────────────────
|
|
1077
1088
|
// POST /v1/sessions/:id/hooks/permission — PermissionRequest hook from CC
|
|
1078
1089
|
app.post('/v1/sessions/:id/hooks/permission', async (req, reply) => {
|
|
1079
|
-
const
|
|
1090
|
+
const sessionId = req.params.id;
|
|
1091
|
+
const session = sessions.getSession(sessionId);
|
|
1080
1092
|
if (!session)
|
|
1081
1093
|
return reply.status(404).send({ error: 'Session not found' });
|
|
1082
1094
|
const parsed = permissionHookSchema.safeParse(req.body);
|
|
@@ -1103,7 +1115,8 @@ app.post('/v1/sessions/:id/hooks/permission', async (req, reply) => {
|
|
|
1103
1115
|
});
|
|
1104
1116
|
// POST /v1/sessions/:id/hooks/stop — Stop hook from CC
|
|
1105
1117
|
app.post('/v1/sessions/:id/hooks/stop', async (req, reply) => {
|
|
1106
|
-
const
|
|
1118
|
+
const sessionId = req.params.id;
|
|
1119
|
+
const session = sessions.getSession(sessionId);
|
|
1107
1120
|
if (!session)
|
|
1108
1121
|
return reply.status(404).send({ error: 'Session not found' });
|
|
1109
1122
|
const parsed = stopHookSchema.safeParse(req.body);
|
|
@@ -1155,6 +1168,28 @@ app.post('/v1/sessions/batch', async (req, reply) => {
|
|
|
1155
1168
|
const result = await pipelines.batchCreate(specs);
|
|
1156
1169
|
return reply.status(201).send(result);
|
|
1157
1170
|
});
|
|
1171
|
+
// Issue #740: Verification Protocol — run quality gate (tsc + build + test) on a session's workDir
|
|
1172
|
+
app.post('/v1/sessions/:id/verify', async (req, reply) => {
|
|
1173
|
+
const sessionId = req.params.id;
|
|
1174
|
+
const session = sessions.getSession(sessionId);
|
|
1175
|
+
if (!session)
|
|
1176
|
+
return reply.status(404).send({ error: 'Session not found' });
|
|
1177
|
+
const { workDir } = session;
|
|
1178
|
+
if (!workDir)
|
|
1179
|
+
return reply.status(400).send({ error: 'Session has no workDir' });
|
|
1180
|
+
const criticalOnly = config.verificationProtocol?.criticalOnly ?? false;
|
|
1181
|
+
eventBus.emitStatus(sessionId, 'working', `Running verification (criticalOnly=${criticalOnly})…`);
|
|
1182
|
+
try {
|
|
1183
|
+
const result = await runVerification(workDir, criticalOnly);
|
|
1184
|
+
eventBus.emitVerification(sessionId, result);
|
|
1185
|
+
const httpStatus = result.ok ? 200 : 422;
|
|
1186
|
+
return reply.status(httpStatus).send(result);
|
|
1187
|
+
}
|
|
1188
|
+
catch (e) {
|
|
1189
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1190
|
+
return reply.status(500).send({ ok: false, summary: `Verification error: ${msg}` });
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1158
1193
|
// Pipeline create (Issue #36)
|
|
1159
1194
|
app.post('/v1/pipelines', async (req, reply) => {
|
|
1160
1195
|
const parsed = pipelineSchema.safeParse(req.body);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { statSync } from 'fs';
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
async function runCmd(cmd, { cwd, timeoutMs }) {
|
|
7
|
+
try {
|
|
8
|
+
const { stdout, stderr } = await execAsync(cmd, { cwd, timeout: Math.floor(timeoutMs / 1000), killSignal: 'SIGKILL' });
|
|
9
|
+
return { stdout, stderr, exitCode: 0 };
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
const err = e;
|
|
13
|
+
return { stdout: err.stdout ?? '', stderr: err.stderr ?? '', exitCode: err.code ?? 1 };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export async function runVerification(workDir, criticalOnly = false) {
|
|
17
|
+
const start = Date.now();
|
|
18
|
+
const hasPackageJson = statSync(join(workDir, 'package.json')).isFile();
|
|
19
|
+
if (!hasPackageJson) {
|
|
20
|
+
return {
|
|
21
|
+
ok: false,
|
|
22
|
+
steps: [],
|
|
23
|
+
totalDurationMs: Date.now() - start,
|
|
24
|
+
summary: 'No package.json found — cannot verify',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const steps = [];
|
|
28
|
+
const timeoutMs = 120_000;
|
|
29
|
+
// Step 1: tsc
|
|
30
|
+
const tscStart = Date.now();
|
|
31
|
+
const tscResult = await runCmd('npx tsc --noEmit', { cwd: workDir, timeoutMs });
|
|
32
|
+
const tscDuration = Date.now() - tscStart;
|
|
33
|
+
const tscOk = tscResult.exitCode === 0;
|
|
34
|
+
steps.push({
|
|
35
|
+
name: 'tsc',
|
|
36
|
+
ok: tscOk,
|
|
37
|
+
durationMs: tscDuration,
|
|
38
|
+
output: tscResult.stdout.slice(0, 2000),
|
|
39
|
+
error: tscOk ? undefined : (tscResult.stderr || tscResult.stdout).slice(0, 2000),
|
|
40
|
+
});
|
|
41
|
+
// Step 2: build
|
|
42
|
+
const buildStart = Date.now();
|
|
43
|
+
const buildResult = await runCmd('npm run build', { cwd: workDir, timeoutMs });
|
|
44
|
+
const buildDuration = Date.now() - buildStart;
|
|
45
|
+
const buildOk = buildResult.exitCode === 0;
|
|
46
|
+
steps.push({
|
|
47
|
+
name: 'build',
|
|
48
|
+
ok: buildOk,
|
|
49
|
+
durationMs: buildDuration,
|
|
50
|
+
output: buildResult.stdout.slice(0, 2000),
|
|
51
|
+
error: buildOk ? undefined : (buildResult.stderr || buildResult.stdout).slice(0, 2000),
|
|
52
|
+
});
|
|
53
|
+
// Step 3: test (unless criticalOnly)
|
|
54
|
+
let testOk = true;
|
|
55
|
+
if (!criticalOnly) {
|
|
56
|
+
const testStart = Date.now();
|
|
57
|
+
const testResult = await runCmd('npm test', { cwd: workDir, timeoutMs: 180_000 });
|
|
58
|
+
const testDuration = Date.now() - testStart;
|
|
59
|
+
testOk = testResult.exitCode === 0;
|
|
60
|
+
steps.push({ name: 'test', ok: testOk, durationMs: testDuration, output: testResult.stdout.slice(0, 2000), error: testOk ? undefined : (testResult.stderr || testResult.stdout).slice(0, 2000) });
|
|
61
|
+
}
|
|
62
|
+
const ok = tscOk && buildOk && (!criticalOnly || testOk);
|
|
63
|
+
const totalDurationMs = Date.now() - start;
|
|
64
|
+
const summary = ok
|
|
65
|
+
? `Verification passed: tsc ✅, build ✅${criticalOnly ? '' : ', test ✅'} (${totalDurationMs}ms)`
|
|
66
|
+
: `Verification failed: ${[
|
|
67
|
+
!tscOk ? 'tsc ❌' : '',
|
|
68
|
+
!buildOk ? 'build ❌' : '',
|
|
69
|
+
!criticalOnly && !testOk ? 'test ❌' : '',
|
|
70
|
+
].filter(Boolean).join(', ')} (${totalDurationMs}ms)`;
|
|
71
|
+
return { ok, steps, totalDurationMs, summary };
|
|
72
|
+
}
|