aegis-bridge 2.12.0 → 2.12.2
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/dashboard/dist/assets/index-DhUXvnKe.js +302 -0
- package/dashboard/dist/assets/index-hJxI_Ze7.css +32 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/api-contracts.d.ts +197 -0
- package/dist/api-contracts.js +7 -0
- package/dist/api-contracts.typecheck.d.ts +14 -0
- package/dist/api-contracts.typecheck.js +1 -0
- package/dist/channels/telegram.d.ts +12 -1
- package/dist/channels/telegram.js +123 -3
- package/dist/config.d.ts +2 -0
- package/dist/config.js +3 -0
- package/dist/events.d.ts +11 -0
- package/dist/events.js +42 -0
- package/dist/hook-settings.d.ts +1 -1
- package/dist/hook-settings.js +5 -13
- package/dist/hooks.js +2 -13
- package/dist/metrics.d.ts +2 -1
- package/dist/monitor.js +33 -6
- package/dist/server.js +21 -2
- package/dist/session.d.ts +12 -5
- package/dist/session.js +109 -119
- package/dist/tmux.d.ts +2 -0
- package/dist/tmux.js +24 -15
- package/dist/validation.d.ts +11 -7
- package/dist/validation.js +53 -8
- package/package.json +4 -1
- package/dashboard/dist/assets/index--qcihe0P.css +0 -32
- package/dashboard/dist/assets/index-DFdRqV0R.js +0 -302
|
@@ -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-blue-500:oklch(62.3% .214 259.815);--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-accent:#3b82f6;--color-accent-dim:#3b82f640;--color-warning:#f59e0b;--color-error:#ef4444;--color-info:#6366f1}}@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{margin-bottom:calc(var(--spacing) * 1)}.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-36{height:calc(var(--spacing) * 36)}.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-\[85\%\]{max-width:85%}.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-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,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)))}.gap-x-6{column-gap:calc(var(--spacing) * 6)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}: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\]{border-color:#00e5ff}.border-\[\#00e5ff\]\/30{border-color:#00e5ff4d}.border-\[\#00e5ff\]\/40{border-color:#00e5ff66}.border-\[\#1a1a2e\]{border-color:#1a1a2e}.border-\[\#1a1a2e\]\/50{border-color:#1a1a2e80}.border-\[\#3b82f6\]\/30{border-color:#3b82f64d}.border-\[\#3b82f6\]\/40{border-color:#3b82f666}.border-\[\#7f1d1d\]{border-color:#7f1d1d}.border-\[\#10b981\]\/30{border-color:#10b9814d}.border-\[\#ef4444\]\/20{border-color:#ef444433}.border-\[\#ef4444\]\/30{border-color:#ef44444d}.border-\[\#ef4444\]\/40{border-color:#ef444466}.border-\[\#ef444420\]{border-color:#ef444420}.border-\[\#f59e0b\]\/40{border-color:#f59e0b66}.border-\[\#ff3366\]\/20{border-color:#f363}.border-\[\#ff336620\]{border-color:#ff336620}.border-\[\#ffaa00\]\/30{border-color:#ffaa004d}.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-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-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-\[\#00e5ff\]\/70{background-color:#00e5ffb3}.bg-\[\#1a1a00\]\/60{background-color:#1a1a0099}.bg-\[\#1a1a2e\]{background-color:#1a1a2e}.bg-\[\#1a1a3e\]{background-color:#1a1a3e}.bg-\[\#002a33\]{background-color:#002a33}.bg-\[\#2a120f\]{background-color:#2a120f}.bg-\[\#2b2200\]{background-color:#2b2200}.bg-\[\#3a2e00\]{background-color:#3a2e00}.bg-\[\#3b82f6\]\/10{background-color:#3b82f61a}.bg-\[\#003322\]{background-color:#032}.bg-\[\#003322\]\/50{background-color:#00332280}.bg-\[\#111118\]{background-color:#111118}.bg-\[\#331111\]{background-color:#311}.bg-\[\#ef4444\]\/10{background-color:#ef44441a}.bg-\[\#ef444410\]{background-color:#ef444410}.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-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-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-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500) 10%,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-\[\#3b82f6\]{color:#3b82f6}.text-\[\#9af5ff\]{color:#9af5ff}.text-\[\#10b981\]{color:#10b981}.text-\[\#333\]{color:#333}.text-\[\#444\]{color:#444}.text-\[\#555\]{color:#555}.text-\[\#666\]{color:#666}.text-\[\#888\]{color:#888}.text-\[\#bbb\]{color:#bbb}.text-\[\#c0c0d0\]{color:#c0c0d0}.text-\[\#e0e0e0\]{color:#e0e0e0}.text-\[\#ef4444\]{color:#ef4444}.text-\[\#f59e0b\]{color:#f59e0b}.text-\[\#fca5a5\]{color:#fca5a5}.text-\[\#fecaca\]{color:#fecaca}.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-blue-500{color:var(--color-blue-500)}.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-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.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}.duration-300{--tw-duration:.3s;transition-duration:.3s}.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)}@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-\[\#2a2a3e\]:hover{border-color:#2a2a3e}.hover\:border-\[\#3b82f6\]\/30:hover{border-color:#3b82f64d}.hover\:border-\[\#333\]:hover{border-color:#333}.hover\:border-gray-500:hover{border-color:var(--color-gray-500)}.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-\[\#3b82f6\]\/20:hover{background-color:#3b82f633}.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-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-\[\#ef4444\]:hover{color:#ef4444}.hover\:text-emerald-200:hover{color:var(--color-emerald-200)}.hover\:text-gray-100:hover{color:var(--color-gray-100)}.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-\[\#3b82f6\]:focus{border-color:#3b82f6}.focus\:border-\[\#ffaa00\]:focus{border-color:#fa0}.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\: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-2{grid-template-columns:repeat(2,minmax(0,1fr))}.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-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-4{grid-template-columns:repeat(4,minmax(0,1fr))}.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-accent-dim)}.status-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px;display:inline-block}.status-dot--idle{background-color:var(--color-accent)}.status-dot--working{background-color:var(--color-warning);animation:1.5s ease-in-out infinite pulse}.status-dot--permission_prompt,.status-dot--bash_approval{background-color:var(--color-error);animation:1s ease-in-out infinite pulse}.status-dot--plan_mode,.status-dot--ask_question{background-color:var(--color-info)}.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)}@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-DhUXvnKe.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/dashboard/assets/index-hJxI_Ze7.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body class="bg-[#0a0a0f] text-gray-200 antialiased">
|
|
12
12
|
<div id="root"></div>
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api-contracts.ts — Shared API contract types for backend and dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Keep this file runtime-free (types only) so both packages can import it
|
|
5
|
+
* without bundling backend implementation code into the frontend.
|
|
6
|
+
*/
|
|
7
|
+
export type UIState = 'idle' | 'working' | 'compacting' | 'context_warning' | 'waiting_for_input' | 'permission_prompt' | 'plan_mode' | 'ask_question' | 'bash_approval' | 'settings' | 'error' | 'unknown';
|
|
8
|
+
export type SessionStatusFilter = 'all' | UIState;
|
|
9
|
+
export interface SessionInfo {
|
|
10
|
+
id: string;
|
|
11
|
+
windowId: string;
|
|
12
|
+
windowName: string;
|
|
13
|
+
workDir: string;
|
|
14
|
+
claudeSessionId?: string;
|
|
15
|
+
jsonlPath?: string;
|
|
16
|
+
byteOffset: number;
|
|
17
|
+
monitorOffset: number;
|
|
18
|
+
status: UIState;
|
|
19
|
+
createdAt: number;
|
|
20
|
+
lastActivity: number;
|
|
21
|
+
stallThresholdMs: number;
|
|
22
|
+
permissionMode: string;
|
|
23
|
+
autoApprove?: boolean;
|
|
24
|
+
settingsPatched?: boolean;
|
|
25
|
+
promptDelivery?: {
|
|
26
|
+
delivered: boolean;
|
|
27
|
+
attempts: number;
|
|
28
|
+
};
|
|
29
|
+
actionHints?: Record<string, {
|
|
30
|
+
method: string;
|
|
31
|
+
url: string;
|
|
32
|
+
description: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export interface SessionHealth {
|
|
36
|
+
alive: boolean;
|
|
37
|
+
windowExists: boolean;
|
|
38
|
+
claudeRunning: boolean;
|
|
39
|
+
paneCommand: string | null;
|
|
40
|
+
status: UIState;
|
|
41
|
+
hasTranscript: boolean;
|
|
42
|
+
lastActivity: number;
|
|
43
|
+
lastActivityAgo: number;
|
|
44
|
+
sessionAge: number;
|
|
45
|
+
details: string;
|
|
46
|
+
actionHints?: Record<string, {
|
|
47
|
+
method: string;
|
|
48
|
+
url: string;
|
|
49
|
+
description: string;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
export interface HealthResponse {
|
|
53
|
+
status: string;
|
|
54
|
+
version: string;
|
|
55
|
+
uptime: number;
|
|
56
|
+
sessions: {
|
|
57
|
+
active: number;
|
|
58
|
+
total: number;
|
|
59
|
+
};
|
|
60
|
+
timestamp: string;
|
|
61
|
+
}
|
|
62
|
+
export interface ParsedEntry {
|
|
63
|
+
role: 'user' | 'assistant' | 'system';
|
|
64
|
+
contentType: 'text' | 'thinking' | 'tool_use' | 'tool_result' | 'tool_error' | 'permission_request' | 'progress';
|
|
65
|
+
text: string;
|
|
66
|
+
toolName?: string;
|
|
67
|
+
toolUseId?: string;
|
|
68
|
+
timestamp?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface MessagesResponse {
|
|
71
|
+
messages: ParsedEntry[];
|
|
72
|
+
status: UIState;
|
|
73
|
+
statusText: string | null;
|
|
74
|
+
interactiveContent: string | null;
|
|
75
|
+
}
|
|
76
|
+
export interface SessionMetrics {
|
|
77
|
+
durationSec: number;
|
|
78
|
+
messages: number;
|
|
79
|
+
toolCalls: number;
|
|
80
|
+
approvals: number;
|
|
81
|
+
autoApprovals: number;
|
|
82
|
+
statusChanges: string[];
|
|
83
|
+
}
|
|
84
|
+
export interface LatencySummaryStat {
|
|
85
|
+
min: number | null;
|
|
86
|
+
max: number | null;
|
|
87
|
+
avg: number | null;
|
|
88
|
+
count: number;
|
|
89
|
+
}
|
|
90
|
+
export interface SessionLatency {
|
|
91
|
+
sessionId: string;
|
|
92
|
+
realtime: {
|
|
93
|
+
hook_latency_ms: number | null;
|
|
94
|
+
state_change_detection_ms: number | null;
|
|
95
|
+
permission_response_ms: number | null;
|
|
96
|
+
} | null;
|
|
97
|
+
aggregated: {
|
|
98
|
+
hook_latency_ms: LatencySummaryStat;
|
|
99
|
+
state_change_detection_ms: LatencySummaryStat;
|
|
100
|
+
permission_response_ms: LatencySummaryStat;
|
|
101
|
+
channel_delivery_ms: LatencySummaryStat;
|
|
102
|
+
} | null;
|
|
103
|
+
}
|
|
104
|
+
export interface GlobalMetrics {
|
|
105
|
+
uptime: number;
|
|
106
|
+
sessions: {
|
|
107
|
+
total_created: number;
|
|
108
|
+
currently_active: number;
|
|
109
|
+
completed: number;
|
|
110
|
+
failed: number;
|
|
111
|
+
avg_duration_sec: number;
|
|
112
|
+
avg_messages_per_session: number;
|
|
113
|
+
};
|
|
114
|
+
auto_approvals: number;
|
|
115
|
+
webhooks_sent: number;
|
|
116
|
+
webhooks_failed: number;
|
|
117
|
+
screenshots_taken: number;
|
|
118
|
+
pipelines_created: number;
|
|
119
|
+
batches_created: number;
|
|
120
|
+
prompt_delivery: {
|
|
121
|
+
sent: number;
|
|
122
|
+
delivered: number;
|
|
123
|
+
failed: number;
|
|
124
|
+
success_rate: number | null;
|
|
125
|
+
};
|
|
126
|
+
latency: {
|
|
127
|
+
hook_latency_ms: LatencySummaryStat;
|
|
128
|
+
state_change_detection_ms: LatencySummaryStat;
|
|
129
|
+
permission_response_ms: LatencySummaryStat;
|
|
130
|
+
channel_delivery_ms: LatencySummaryStat;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export type SSEEventType = 'status' | 'message' | 'approval' | 'ended' | 'heartbeat' | 'stall' | 'dead' | 'system' | 'hook' | 'subagent_start' | 'subagent_stop' | 'verification';
|
|
134
|
+
export interface SessionSSEEvent {
|
|
135
|
+
event: SSEEventType;
|
|
136
|
+
sessionId: string;
|
|
137
|
+
timestamp: string;
|
|
138
|
+
emittedAt?: number;
|
|
139
|
+
data: Record<string, unknown>;
|
|
140
|
+
}
|
|
141
|
+
export type GlobalSSEEventType = 'session_status_change' | 'session_message' | 'session_approval' | 'session_ended' | 'session_created' | 'session_stall' | 'session_dead' | 'session_subagent_start' | 'session_subagent_stop' | 'session_verification';
|
|
142
|
+
export interface GlobalSSEEvent {
|
|
143
|
+
event: GlobalSSEEventType;
|
|
144
|
+
sessionId: string;
|
|
145
|
+
timestamp: string;
|
|
146
|
+
data: Record<string, unknown>;
|
|
147
|
+
}
|
|
148
|
+
export interface CreateSessionRequest {
|
|
149
|
+
workDir: string;
|
|
150
|
+
name?: string;
|
|
151
|
+
prompt?: string;
|
|
152
|
+
resumeSessionId?: string;
|
|
153
|
+
claudeCommand?: string;
|
|
154
|
+
env?: Record<string, string>;
|
|
155
|
+
stallThresholdMs?: number;
|
|
156
|
+
permissionMode?: string;
|
|
157
|
+
autoApprove?: boolean;
|
|
158
|
+
parentId?: string;
|
|
159
|
+
memoryKeys?: string[];
|
|
160
|
+
}
|
|
161
|
+
export interface PaneResponse {
|
|
162
|
+
pane: string;
|
|
163
|
+
}
|
|
164
|
+
export interface SessionSummary {
|
|
165
|
+
sessionId: string;
|
|
166
|
+
windowName: string;
|
|
167
|
+
status: UIState;
|
|
168
|
+
totalMessages: number;
|
|
169
|
+
messages: Array<{
|
|
170
|
+
role: string;
|
|
171
|
+
contentType: string;
|
|
172
|
+
text: string;
|
|
173
|
+
}>;
|
|
174
|
+
createdAt: number;
|
|
175
|
+
lastActivity: number;
|
|
176
|
+
permissionMode: string;
|
|
177
|
+
}
|
|
178
|
+
export interface OkResponse {
|
|
179
|
+
ok: boolean;
|
|
180
|
+
}
|
|
181
|
+
export interface SendResponse extends OkResponse {
|
|
182
|
+
delivered: boolean;
|
|
183
|
+
attempts: number;
|
|
184
|
+
}
|
|
185
|
+
export interface ApiError {
|
|
186
|
+
error: string;
|
|
187
|
+
}
|
|
188
|
+
export interface SessionsListResponse {
|
|
189
|
+
sessions: SessionInfo[];
|
|
190
|
+
pagination: {
|
|
191
|
+
page: number;
|
|
192
|
+
limit: number;
|
|
193
|
+
total: number;
|
|
194
|
+
totalPages: number;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
export type SessionStatusCounts = Record<SessionStatusFilter, number>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SessionInfo as InternalSessionInfo, SessionManager } from './session.js';
|
|
2
|
+
import type { MetricsCollector, SessionLatencySummary } from './metrics.js';
|
|
3
|
+
import type { SessionSSEEvent as InternalSessionSSEEvent, GlobalSSEEvent as InternalGlobalSSEEvent } from './events.js';
|
|
4
|
+
import type { SessionInfo, MessagesResponse, SessionSummary, SessionMetrics, SessionLatency, GlobalMetrics, SessionSSEEvent, GlobalSSEEvent } from './api-contracts.js';
|
|
5
|
+
type Assert<T extends true> = T;
|
|
6
|
+
export type SessionInfoContractCompat = Assert<InternalSessionInfo extends SessionInfo ? true : false>;
|
|
7
|
+
export type SessionReadMessagesContractCompat = Assert<Awaited<ReturnType<SessionManager['readMessages']>> extends MessagesResponse ? true : false>;
|
|
8
|
+
export type SessionSummaryContractCompat = Assert<Awaited<ReturnType<SessionManager['getSummary']>> extends SessionSummary ? true : false>;
|
|
9
|
+
export type SessionMetricsContractCompat = Assert<NonNullable<ReturnType<MetricsCollector['getSessionMetrics']>> extends SessionMetrics ? true : false>;
|
|
10
|
+
export type SessionLatencySummaryContractCompat = Assert<SessionLatencySummary extends NonNullable<SessionLatency['aggregated']> ? true : false>;
|
|
11
|
+
export type GlobalMetricsContractCompat = Assert<ReturnType<MetricsCollector['getGlobalMetrics']> extends GlobalMetrics ? true : false>;
|
|
12
|
+
export type SessionSSEContractCompat = Assert<InternalSessionSSEEvent extends SessionSSEEvent ? true : false>;
|
|
13
|
+
export type GlobalSSEContractCompat = Assert<InternalGlobalSSEEvent extends GlobalSSEEvent ? true : false>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -13,10 +13,13 @@ export interface TelegramChannelConfig {
|
|
|
13
13
|
botToken: string;
|
|
14
14
|
groupChatId: string;
|
|
15
15
|
allowedUserIds: number[];
|
|
16
|
+
topicTtlMs?: number;
|
|
16
17
|
}
|
|
17
18
|
export declare class TelegramChannel implements Channel {
|
|
18
19
|
private config;
|
|
19
20
|
readonly name = "telegram";
|
|
21
|
+
static readonly DEFAULT_TOPIC_TTL_MS: number;
|
|
22
|
+
private static readonly TOPIC_CLEANUP_RETRY_MS;
|
|
20
23
|
private topics;
|
|
21
24
|
private progress;
|
|
22
25
|
private pollOffset;
|
|
@@ -24,6 +27,9 @@ export declare class TelegramChannel implements Channel {
|
|
|
24
27
|
private rateLimitUntil;
|
|
25
28
|
private pollBackoffMs;
|
|
26
29
|
private onInbound;
|
|
30
|
+
private topicCleanupTimers;
|
|
31
|
+
private topicCleanupSweepTimer;
|
|
32
|
+
private readonly topicTtlMs;
|
|
27
33
|
private messageQueue;
|
|
28
34
|
private lastSent;
|
|
29
35
|
private flushTimers;
|
|
@@ -66,7 +72,12 @@ export declare class TelegramChannel implements Channel {
|
|
|
66
72
|
private decrementInFlight;
|
|
67
73
|
private queueMessage;
|
|
68
74
|
private flushQueue;
|
|
69
|
-
private
|
|
75
|
+
private cleanupSessionRuntimeState;
|
|
76
|
+
private startTopicCleanupSweep;
|
|
77
|
+
private clearTopicCleanupTimer;
|
|
78
|
+
private scheduleTopicCleanup;
|
|
79
|
+
private runTopicCleanup;
|
|
80
|
+
private isIgnorableTopicDeleteError;
|
|
70
81
|
private handleSwarmCommand;
|
|
71
82
|
private pollLoop;
|
|
72
83
|
/** Issue #348: Redact bot token from error messages before logging. */
|
|
@@ -482,6 +482,8 @@ function formatProgressCard(progress) {
|
|
|
482
482
|
export class TelegramChannel {
|
|
483
483
|
config;
|
|
484
484
|
name = 'telegram';
|
|
485
|
+
static DEFAULT_TOPIC_TTL_MS = 24 * 60 * 60 * 1000;
|
|
486
|
+
static TOPIC_CLEANUP_RETRY_MS = 60_000;
|
|
485
487
|
topics = new Map();
|
|
486
488
|
progress = new Map();
|
|
487
489
|
pollOffset = 0;
|
|
@@ -489,6 +491,9 @@ export class TelegramChannel {
|
|
|
489
491
|
rateLimitUntil = 0;
|
|
490
492
|
pollBackoffMs = 1_000; // Exponential backoff for poll errors
|
|
491
493
|
onInbound = null;
|
|
494
|
+
topicCleanupTimers = new Map();
|
|
495
|
+
topicCleanupSweepTimer = null;
|
|
496
|
+
topicTtlMs;
|
|
492
497
|
// Rate limiting & batching
|
|
493
498
|
messageQueue = new Map();
|
|
494
499
|
lastSent = new Map();
|
|
@@ -512,6 +517,10 @@ export class TelegramChannel {
|
|
|
512
517
|
}
|
|
513
518
|
constructor(config) {
|
|
514
519
|
this.config = config;
|
|
520
|
+
const configuredTtlMs = config.topicTtlMs ?? TelegramChannel.DEFAULT_TOPIC_TTL_MS;
|
|
521
|
+
this.topicTtlMs = Number.isFinite(configuredTtlMs)
|
|
522
|
+
? Math.max(0, configuredTtlMs)
|
|
523
|
+
: TelegramChannel.DEFAULT_TOPIC_TTL_MS;
|
|
515
524
|
}
|
|
516
525
|
/** Call Telegram Bot API with retry on 429. Instance method so it can access rateLimitUntil. */
|
|
517
526
|
async tgApi(method, body, retries = 3) {
|
|
@@ -550,6 +559,7 @@ export class TelegramChannel {
|
|
|
550
559
|
async init(onInbound) {
|
|
551
560
|
this.onInbound = onInbound;
|
|
552
561
|
this.polling = true;
|
|
562
|
+
this.startTopicCleanupSweep();
|
|
553
563
|
this.pollLoopPromise = this.pollLoop(); // store promise for graceful shutdown
|
|
554
564
|
console.log(`Telegram channel: polling started, group ${this.config.groupChatId}`);
|
|
555
565
|
}
|
|
@@ -565,8 +575,15 @@ export class TelegramChannel {
|
|
|
565
575
|
clearTimeout(timer);
|
|
566
576
|
for (const timer of this.readTimer.values())
|
|
567
577
|
clearTimeout(timer);
|
|
578
|
+
for (const timer of this.topicCleanupTimers.values())
|
|
579
|
+
clearTimeout(timer);
|
|
580
|
+
if (this.topicCleanupSweepTimer) {
|
|
581
|
+
clearInterval(this.topicCleanupSweepTimer);
|
|
582
|
+
this.topicCleanupSweepTimer = null;
|
|
583
|
+
}
|
|
568
584
|
this.flushTimers.clear();
|
|
569
585
|
this.readTimer.clear();
|
|
586
|
+
this.topicCleanupTimers.clear();
|
|
570
587
|
}
|
|
571
588
|
async onSessionCreated(payload) {
|
|
572
589
|
const topicName = `🤖 ${payload.session.name}`;
|
|
@@ -575,10 +592,14 @@ export class TelegramChannel {
|
|
|
575
592
|
name: topicName,
|
|
576
593
|
}));
|
|
577
594
|
const topicId = result.message_thread_id;
|
|
595
|
+
this.clearTopicCleanupTimer(payload.session.id);
|
|
578
596
|
this.topics.set(payload.session.id, {
|
|
579
597
|
sessionId: payload.session.id,
|
|
580
598
|
topicId,
|
|
581
599
|
windowName: payload.session.name,
|
|
600
|
+
endedAt: null,
|
|
601
|
+
cleanupScheduledAt: null,
|
|
602
|
+
deleting: false,
|
|
582
603
|
});
|
|
583
604
|
this.progress.set(payload.session.id, {
|
|
584
605
|
totalMessages: 0,
|
|
@@ -638,7 +659,8 @@ export class TelegramChannel {
|
|
|
638
659
|
const styled = quickUpdate('✅', `${payload.session.name} — Session ended`);
|
|
639
660
|
await this.sendStyled(payload.session.id, styled);
|
|
640
661
|
}
|
|
641
|
-
this.
|
|
662
|
+
this.scheduleTopicCleanup(payload.session.id);
|
|
663
|
+
this.cleanupSessionRuntimeState(payload.session.id);
|
|
642
664
|
}
|
|
643
665
|
async onMessage(payload) {
|
|
644
666
|
// Issue #46: If topic doesn't exist yet, buffer the message
|
|
@@ -1126,8 +1148,7 @@ export class TelegramChannel {
|
|
|
1126
1148
|
await this.sendToTopic(sessionId, groups[i]);
|
|
1127
1149
|
}
|
|
1128
1150
|
}
|
|
1129
|
-
|
|
1130
|
-
this.topics.delete(sessionId);
|
|
1151
|
+
cleanupSessionRuntimeState(sessionId) {
|
|
1131
1152
|
this.progress.delete(sessionId);
|
|
1132
1153
|
this.lastSent.delete(sessionId);
|
|
1133
1154
|
this.pendingTool.delete(sessionId);
|
|
@@ -1146,6 +1167,105 @@ export class TelegramChannel {
|
|
|
1146
1167
|
this.readTimer.delete(sessionId);
|
|
1147
1168
|
}
|
|
1148
1169
|
}
|
|
1170
|
+
startTopicCleanupSweep() {
|
|
1171
|
+
if (this.topicCleanupSweepTimer)
|
|
1172
|
+
return;
|
|
1173
|
+
const sweepMs = Math.min(60_000, Math.max(5_000, this.topicTtlMs || 5_000));
|
|
1174
|
+
this.topicCleanupSweepTimer = setInterval(() => {
|
|
1175
|
+
for (const [sessionId, topic] of this.topics) {
|
|
1176
|
+
if (topic.endedAt === null)
|
|
1177
|
+
continue;
|
|
1178
|
+
if (topic.deleting)
|
|
1179
|
+
continue;
|
|
1180
|
+
if (Date.now() >= topic.endedAt + this.topicTtlMs) {
|
|
1181
|
+
void this.runTopicCleanup(sessionId);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}, sweepMs);
|
|
1185
|
+
if (typeof this.topicCleanupSweepTimer.unref === 'function') {
|
|
1186
|
+
this.topicCleanupSweepTimer.unref();
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
clearTopicCleanupTimer(sessionId) {
|
|
1190
|
+
const timer = this.topicCleanupTimers.get(sessionId);
|
|
1191
|
+
if (timer) {
|
|
1192
|
+
clearTimeout(timer);
|
|
1193
|
+
this.topicCleanupTimers.delete(sessionId);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
scheduleTopicCleanup(sessionId) {
|
|
1197
|
+
const topic = this.topics.get(sessionId);
|
|
1198
|
+
if (!topic)
|
|
1199
|
+
return;
|
|
1200
|
+
if (topic.endedAt === null) {
|
|
1201
|
+
topic.endedAt = Date.now();
|
|
1202
|
+
}
|
|
1203
|
+
if (topic.cleanupScheduledAt !== null) {
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
const cleanupAt = topic.endedAt + this.topicTtlMs;
|
|
1207
|
+
topic.cleanupScheduledAt = cleanupAt;
|
|
1208
|
+
const delayMs = Math.max(0, cleanupAt - Date.now());
|
|
1209
|
+
if (delayMs === 0) {
|
|
1210
|
+
void this.runTopicCleanup(sessionId);
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
this.clearTopicCleanupTimer(sessionId);
|
|
1214
|
+
const timer = setTimeout(() => {
|
|
1215
|
+
void this.runTopicCleanup(sessionId);
|
|
1216
|
+
}, delayMs);
|
|
1217
|
+
this.topicCleanupTimers.set(sessionId, timer);
|
|
1218
|
+
if (typeof timer.unref === 'function') {
|
|
1219
|
+
timer.unref();
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
async runTopicCleanup(sessionId) {
|
|
1223
|
+
const topic = this.topics.get(sessionId);
|
|
1224
|
+
if (!topic || topic.endedAt === null || topic.deleting)
|
|
1225
|
+
return;
|
|
1226
|
+
if (Date.now() < topic.endedAt + this.topicTtlMs) {
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
topic.deleting = true;
|
|
1230
|
+
this.clearTopicCleanupTimer(sessionId);
|
|
1231
|
+
const body = {
|
|
1232
|
+
chat_id: this.config.groupChatId,
|
|
1233
|
+
message_thread_id: topic.topicId,
|
|
1234
|
+
};
|
|
1235
|
+
try {
|
|
1236
|
+
try {
|
|
1237
|
+
await this.tgApi('closeForumTopic', body);
|
|
1238
|
+
}
|
|
1239
|
+
catch (e) {
|
|
1240
|
+
if (!this.isIgnorableTopicDeleteError(e)) {
|
|
1241
|
+
throw e;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
await this.tgApi('deleteForumTopic', body);
|
|
1245
|
+
this.topics.delete(sessionId);
|
|
1246
|
+
}
|
|
1247
|
+
catch (e) {
|
|
1248
|
+
if (this.isIgnorableTopicDeleteError(e)) {
|
|
1249
|
+
this.topics.delete(sessionId);
|
|
1250
|
+
}
|
|
1251
|
+
else {
|
|
1252
|
+
console.error(`Telegram: failed to cleanup topic for session ${sessionId}:`, this.redactError(e));
|
|
1253
|
+
topic.deleting = false;
|
|
1254
|
+
topic.cleanupScheduledAt = null;
|
|
1255
|
+
const timer = setTimeout(() => {
|
|
1256
|
+
void this.runTopicCleanup(sessionId);
|
|
1257
|
+
}, TelegramChannel.TOPIC_CLEANUP_RETRY_MS);
|
|
1258
|
+
this.topicCleanupTimers.set(sessionId, timer);
|
|
1259
|
+
if (typeof timer.unref === 'function') {
|
|
1260
|
+
timer.unref();
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
isIgnorableTopicDeleteError(err) {
|
|
1266
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1267
|
+
return /not found|message thread|topic.*(?:closed|deleted)|forum topic/i.test(message);
|
|
1268
|
+
}
|
|
1149
1269
|
// ── /swarm command ──────────────────────────────────────────────────
|
|
1150
1270
|
async handleSwarmCommand(sessionId) {
|
|
1151
1271
|
if (!this.swarmMonitor) {
|
package/dist/config.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export interface Config {
|
|
|
35
35
|
tgGroupId: string;
|
|
36
36
|
/** Allowed Telegram user IDs for inbound commands (empty = allow all) */
|
|
37
37
|
tgAllowedUsers: number[];
|
|
38
|
+
/** TTL for Telegram forum topics after session end, in milliseconds. */
|
|
39
|
+
tgTopicTtlMs: number;
|
|
38
40
|
/** Webhook URLs (comma-separated or array) */
|
|
39
41
|
webhooks: string[];
|
|
40
42
|
/** Default env vars injected into every CC session (e.g. model overrides, API keys).
|
package/dist/config.js
CHANGED
|
@@ -39,6 +39,7 @@ const defaults = {
|
|
|
39
39
|
tgBotToken: '',
|
|
40
40
|
tgGroupId: '',
|
|
41
41
|
tgAllowedUsers: [],
|
|
42
|
+
tgTopicTtlMs: 24 * 60 * 60 * 1000,
|
|
42
43
|
webhooks: [],
|
|
43
44
|
defaultSessionEnv: {},
|
|
44
45
|
defaultPermissionMode: 'bypassPermissions',
|
|
@@ -126,6 +127,7 @@ function applyEnvOverrides(config) {
|
|
|
126
127
|
{ aegis: 'AEGIS_TG_TOKEN', manus: 'MANUS_TG_TOKEN', key: 'tgBotToken' },
|
|
127
128
|
{ aegis: 'AEGIS_TG_GROUP', manus: 'MANUS_TG_GROUP', key: 'tgGroupId' },
|
|
128
129
|
{ aegis: 'AEGIS_TG_ALLOWED_USERS', manus: 'MANUS_TG_ALLOWED_USERS', key: 'tgAllowedUsers' },
|
|
130
|
+
{ aegis: 'AEGIS_TG_TOPIC_TTL_MS', manus: 'MANUS_TG_TOPIC_TTL_MS', key: 'tgTopicTtlMs' },
|
|
129
131
|
{ aegis: 'AEGIS_WEBHOOKS', manus: 'MANUS_WEBHOOKS', key: 'webhooks' },
|
|
130
132
|
{ aegis: 'AEGIS_SSE_MAX_CONNECTIONS', manus: 'MANUS_SSE_MAX_CONNECTIONS', key: 'sseMaxConnections' },
|
|
131
133
|
{ aegis: 'AEGIS_SSE_MAX_PER_IP', manus: 'MANUS_SSE_MAX_PER_IP', key: 'sseMaxPerIp' },
|
|
@@ -140,6 +142,7 @@ function applyEnvOverrides(config) {
|
|
|
140
142
|
case 'maxSessionAgeMs':
|
|
141
143
|
case 'reaperIntervalMs':
|
|
142
144
|
case 'continuationPointerTtlMs':
|
|
145
|
+
case 'tgTopicTtlMs':
|
|
143
146
|
case 'sseMaxConnections':
|
|
144
147
|
case 'sseMaxPerIp':
|
|
145
148
|
config[key] = parseIntSafe(value, config[key]);
|
package/dist/events.d.ts
CHANGED
|
@@ -35,6 +35,10 @@ export interface GlobalSSEEvent {
|
|
|
35
35
|
/** Issue #301: Incrementing event ID for Last-Event-ID replay. */
|
|
36
36
|
id?: number;
|
|
37
37
|
}
|
|
38
|
+
interface SessionEventBusOptions {
|
|
39
|
+
/** Maximum number of per-session replay buffers retained in memory. */
|
|
40
|
+
maxSessionBuffers?: number;
|
|
41
|
+
}
|
|
38
42
|
/**
|
|
39
43
|
* Per-session event bus. Subscribers (SSE connections) register here.
|
|
40
44
|
* The monitor calls emit() when events happen.
|
|
@@ -51,8 +55,14 @@ export declare class SessionEventBus {
|
|
|
51
55
|
private static readonly BUFFER_SIZE;
|
|
52
56
|
/** Per-session ring buffer for event replay. */
|
|
53
57
|
private eventBuffers;
|
|
58
|
+
/** Last activity time per session buffer for LRU eviction. */
|
|
59
|
+
private sessionBufferLastTouched;
|
|
54
60
|
/** Global ring buffer for event replay across all sessions (Issue #301). */
|
|
55
61
|
private globalEventBuffer;
|
|
62
|
+
private readonly maxSessionBuffers;
|
|
63
|
+
constructor(options?: SessionEventBusOptions);
|
|
64
|
+
private touchSessionBuffer;
|
|
65
|
+
private pruneSessionBufferMap;
|
|
56
66
|
/** Get or create the emitter for a session. */
|
|
57
67
|
private getEmitter;
|
|
58
68
|
/** Subscribe to events for a session. Returns unsubscribe function. */
|
|
@@ -120,3 +130,4 @@ export declare class SessionEventBus {
|
|
|
120
130
|
/** Clean up all emitters. */
|
|
121
131
|
destroy(): void;
|
|
122
132
|
}
|
|
133
|
+
export {};
|