@bibobonking/opensol 3.1.6 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -7
- package/README.md +24 -20
- package/desktop/README.md +1 -1
- package/dist/assets/index-CyxlAgKM.js +271 -0
- package/dist/assets/index-DFeF90S8.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +3 -6
- package/server/README.md +15 -68
- package/server/daemon.mjs +6 -1
- package/server/lib/deploy.mjs +5 -10
- package/server/lib/shell.mjs +149 -0
- package/server/lib/shell.test.mjs +72 -0
- package/server/lib/tools.mjs +8 -44
- package/src/agent/adapters/anthropic.ts +2 -3
- package/src/agent/adapters/gemini.ts +2 -3
- package/src/agent/adapters/openai-responses.ts +2 -3
- package/src/agent/adapters/openai.ts +2 -3
- package/src/agent/adapters/shared.ts +3 -0
- package/src/components/ChatView.tsx +17 -54
- package/src/components/Composer.tsx +143 -135
- package/src/components/ContextBar.tsx +2 -2
- package/src/components/HomeDashboard.tsx +7 -4
- package/src/components/MessageItem.tsx +33 -1
- package/src/components/ModelDeck.tsx +5 -27
- package/src/components/SettingsModal.tsx +8 -0
- package/src/components/ToolCallCard.tsx +2 -2
- package/src/components/TrajectoryView.tsx +1 -1
- package/src/components/composer/PlusMenu.tsx +117 -0
- package/src/components/composer/Popover.tsx +109 -0
- package/src/components/composer/WorkspacePicker.tsx +102 -0
- package/src/core/plugins/tools/shell.ts +2 -2
- package/src/core/plugins/tools/subagent.ts +18 -8
- package/src/core/services/agent-loop.ts +45 -43
- package/src/core/services/compaction.ts +289 -22
- package/src/core/services/llm.ts +6 -2
- package/src/core/services/subagents.ts +191 -3
- package/src/core/services/system-prompt.ts +35 -8
- package/src/core/services/tools.ts +2 -1
- package/src/core/types.ts +5 -0
- package/src/lib/compact-plan.test.mjs +62 -0
- package/src/lib/compact-plan.ts +188 -0
- package/src/lib/subagent-types.ts +54 -0
- package/src/lib/tool-meta.ts +40 -0
- package/src/store/settings.ts +3 -0
- package/src/store/ui.ts +12 -0
- package/src/types.ts +26 -0
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Geist,Inter,system-ui,-apple-system,Segoe UI,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:Geist Mono,JetBrains Mono,ui-monospace,SFMono-Regular,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root,[data-theme=light]{--canvas: 248 248 246;--panel: 255 255 255;--raised: 240 240 237;--line: 232 232 228;--ink: 23 23 21;--muted: 98 98 93;--faint: 148 148 141;--accent: 217 119 6;--accent-soft: 254 244 224;--on-accent: 255 255 255;--success: 22 128 74;--warning: 180 100 8;--danger: 207 56 48;--syn-keyword: 124 58 237;--syn-string: 21 128 61;--syn-number: 194 85 8;--syn-title: 37 99 235;--syn-attr: 14 116 144;--chat-font-size: 15px;color-scheme:light}[data-theme=dark]{--canvas: 15 15 14;--panel: 20 20 19;--raised: 33 33 31;--line: 42 42 39;--ink: 237 237 233;--muted: 160 160 153;--faint: 112 112 106;--accent: 245 158 11;--accent-soft: 46 34 12;--on-accent: 26 17 3;--success: 74 205 130;--warning: 245 180 70;--danger: 248 113 104;--syn-keyword: 196 160 255;--syn-string: 130 218 160;--syn-number: 244 184 110;--syn-title: 140 184 255;--syn-attr: 120 214 226;--chat-font-size: 15px;color-scheme:dark}[data-theme=night-owl]{--canvas: 1 17 30;--panel: 1 22 39;--raised: 11 38 60;--line: 22 55 82;--ink: 214 222 235;--muted: 141 168 197;--faint: 95 122 153;--accent: 130 170 255;--accent-soft: 16 48 77;--on-accent: 1 22 39;--success: 34 210 156;--warning: 236 196 141;--danger: 239 83 80;--syn-keyword: 197 147 249;--syn-string: 144 209 148;--syn-number: 247 190 116;--syn-title: 130 170 255;--syn-attr: 33 193 220;color-scheme:dark}[data-theme=claude-code]{--canvas: 30 29 27;--panel: 38 37 34;--raised: 49 48 44;--line: 62 61 56;--ink: 232 230 224;--muted: 175 171 162;--faint: 129 126 118;--accent: 217 119 87;--accent-soft: 62 45 38;--on-accent: 28 22 18;--success: 143 191 140;--warning: 222 178 112;--danger: 224 122 112;--syn-keyword: 213 145 122;--syn-string: 143 187 137;--syn-number: 221 176 116;--syn-title: 158 184 222;--syn-attr: 135 189 191;color-scheme:dark}[data-theme=flame]{--canvas: 16 11 8;--panel: 22 15 11;--raised: 36 25 18;--line: 56 38 27;--ink: 250 238 224;--muted: 190 160 138;--faint: 134 106 88;--accent: 255 138 30;--accent-soft: 58 32 14;--on-accent: 26 12 4;--success: 146 206 138;--warning: 250 200 100;--danger: 255 106 92;--syn-keyword: 255 150 90;--syn-string: 170 210 138;--syn-number: 250 194 108;--syn-title: 250 176 128;--syn-attr: 168 200 224;color-scheme:dark}*{border-color:rgb(var(--line))}html,body,#root{height:100%}body{--tw-bg-opacity: 1;background-color:rgb(var(--panel) / var(--tw-bg-opacity, 1));font-family:Geist,Inter,system-ui,-apple-system,Segoe UI,sans-serif;--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-feature-settings:"ss01" 1,"cv11" 1;letter-spacing:-.003em;text-rendering:optimizeLegibility;overscroll-behavior:none}::-moz-selection{background:rgb(var(--accent) / .22)}::selection{background:rgb(var(--accent) / .22)}:focus-visible{outline:2px solid rgb(var(--accent) / .6);outline-offset:2px;border-radius:6px}button,input,textarea,select{font:inherit}button:not(:disabled){cursor:pointer}input::-moz-placeholder,textarea::-moz-placeholder{color:rgb(var(--faint))}input::placeholder,textarea::placeholder{color:rgb(var(--faint))}.icon-button{display:inline-flex;height:2rem;width:2rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.5rem;--tw-text-opacity: 1;color:rgb(var(--muted) / var(--tw-text-opacity, 1));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transition-timing-function:cubic-bezier(.16,1,.3,1)}.icon-button:hover{--tw-bg-opacity: 1;background-color:rgb(var(--raised) / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.icon-button:active{--tw-scale-x: .9;--tw-scale-y: .9;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.icon-button:disabled{pointer-events:none;opacity:.4}.chip{display:inline-flex;height:2rem;align-items:center;gap:.375rem;border-radius:.5rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--line) / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(var(--panel) / var(--tw-bg-opacity, 1));padding-left:.75rem;padding-right:.75rem;font-size:12.5px;font-weight:500;--tw-text-opacity: 1;color:rgb(var(--muted) / var(--tw-text-opacity, 1));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.chip:hover{border-color:rgb(var(--faint) / .4);background-color:rgb(var(--raised) / .6);--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.chip:active{--tw-scale-x: .97;--tw-scale-y: .97;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.eyebrow{font-size:10.5px;font-weight:500;text-transform:uppercase;letter-spacing:.14em;--tw-text-opacity: 1;color:rgb(var(--faint) / var(--tw-text-opacity, 1))}.sidebar-item{position:relative;display:flex;height:2rem;width:100%;align-items:center;gap:.625rem;border-radius:.5rem;padding-left:.625rem;padding-right:.625rem;text-align:left;font-size:13px;--tw-text-opacity: 1;color:rgb(var(--muted) / var(--tw-text-opacity, 1));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.sidebar-item:hover{background-color:rgb(var(--raised) / .8);--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.sidebar-item-active{--tw-bg-opacity: 1;background-color:rgb(var(--raised) / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.sidebar-item-active:hover{--tw-bg-opacity: 1;background-color:rgb(var(--raised) / var(--tw-bg-opacity, 1))}.sidebar-item-active:before{content:"";position:absolute;left:0;top:8px;bottom:8px;width:2px;border-radius:2px;background:rgb(var(--accent));animation:scalein .3s cubic-bezier(.16,1,.3,1) both}.nav-item{display:flex;height:2rem;width:100%;align-items:center;gap:.625rem;border-radius:.5rem;padding-left:.625rem;padding-right:.625rem;text-align:left;font-size:13px;--tw-text-opacity: 1;color:rgb(var(--muted) / var(--tw-text-opacity, 1));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.nav-item:hover{background-color:rgb(var(--raised) / .8);--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.nav-item:active{--tw-scale-x: .99;--tw-scale-y: .99;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.surface-lift{background:rgb(var(--panel))}.chat-text{font-size:var(--chat-font-size, 15px)}.settings-row{margin-left:-.75rem;margin-right:-.75rem;display:flex;flex-direction:column;gap:.5rem;border-radius:1rem;border-bottom-width:1px;border-color:transparent;padding:1rem .75rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.settings-row:hover{background-color:rgb(var(--raised) / .5)}@media(min-width:640px){.settings-row{flex-direction:row;align-items:center;justify-content:space-between;gap:1.5rem}}.settings-card{position:relative;border-radius:1rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--line) / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(var(--panel) / var(--tw-bg-opacity, 1));padding:1rem;--tw-shadow: 0 1px 2px rgba(0,0,0,.06);--tw-shadow-colored: 0 1px 2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.3s}@media(min-width:640px){.settings-card{padding:1.25rem}}.settings-card:hover{border-color:rgb(var(--accent) / .25)}.settings-label{font-size:13px;font-weight:500;--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.settings-hint{margin-top:.125rem;font-size:12px;line-height:1.625;--tw-text-opacity: 1;color:rgb(var(--muted) / var(--tw-text-opacity, 1))}.settings-select{height:2.25rem;flex-shrink:0;border-radius:.5rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--line) / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(var(--panel) / var(--tw-bg-opacity, 1));padding-left:.625rem;padding-right:.625rem;font-size:12.5px;--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1));outline:2px solid transparent;outline-offset:2px;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.settings-select:focus{border-color:rgb(var(--accent) / .6)}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-\[5px\]{inset:5px}.inset-x-0{left:0;right:0}.inset-x-\[13px\]{left:13px;right:13px}.inset-y-0{top:0;bottom:0}.inset-y-1{top:.25rem;bottom:.25rem}.-right-1\.5{right:-.375rem}.-top-1\.5{top:-.375rem}.-top-\[34px\]{top:-34px}.bottom-0\.5{bottom:.125rem}.bottom-1{bottom:.25rem}.bottom-3{bottom:.75rem}.bottom-4{bottom:1rem}.bottom-56{bottom:14rem}.bottom-\[calc\(100\%\+8px\)\]{bottom:calc(100% + 8px)}.left-0{left:0}.left-1\/2{left:50%}.left-2{left:.5rem}.left-2\.5{left:.625rem}.left-3{left:.75rem}.left-3\.5{left:.875rem}.left-\[13px\]{left:13px}.right-0{right:0}.right-1{right:.25rem}.right-1\.5{right:.375rem}.right-2{right:.5rem}.right-3{right:.75rem}.right-4{right:1rem}.top-0{top:0}.top-1{top:.25rem}.top-1\.5{top:.375rem}.top-1\/2{top:50%}.top-2{top:.5rem}.top-2\.5{top:.625rem}.top-3{top:.75rem}.top-\[1px\]{top:1px}.top-\[calc\(100\%\+6px\)\]{top:calc(100% + 6px)}.-z-10{z-index:-10}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.z-\[60\]{z-index:60}.z-\[70\]{z-index:70}.z-\[80\]{z-index:80}.z-\[90\]{z-index:90}.z-\[95\]{z-index:95}.m-auto{margin:auto}.mx-5{margin-left:1.25rem;margin-right:1.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-4{margin-top:1rem;margin-bottom:1rem}.-mr-1{margin-right:-.25rem}.-mt-1{margin-top:-.25rem}.-mt-2{margin-top:-.5rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-2\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-\[2px\]{margin-bottom:2px}.ml-1\.5{margin-left:.375rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-3{margin-right:.75rem}.mr-auto{margin-right:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-2\.5{margin-top:.625rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-9{margin-top:2.25rem}.mt-\[3px\]{margin-top:3px}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3}.line-clamp-4{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-20{height:5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-40{height:10rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-52{height:13rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[11px\]{height:11px}.h-\[120px\]{height:120px}.h-\[16px\]{height:16px}.h-\[18px\]{height:18px}.h-\[22px\]{height:22px}.h-\[26px\]{height:26px}.h-\[280px\]{height:280px}.h-\[2px\]{height:2px}.h-\[3px\]{height:3px}.h-\[420px\]{height:420px}.h-\[46px\]{height:46px}.h-\[7px\]{height:7px}.h-\[92dvh\]{height:92dvh}.h-\[94dvh\]{height:94dvh}.h-\[min\(92dvh\,640px\)\]{height:min(92dvh,640px)}.h-dvh{height:100dvh}.h-full{height:100%}.h-px{height:1px}.max-h-0{max-height:0px}.max-h-24{max-height:6rem}.max-h-40{max-height:10rem}.max-h-48{max-height:12rem}.max-h-52{max-height:13rem}.max-h-64{max-height:16rem}.max-h-72{max-height:18rem}.max-h-8{max-height:2rem}.max-h-\[32dvh\]{max-height:32dvh}.max-h-\[92dvh\]{max-height:92dvh}.max-h-\[calc\(100dvh-80px\)\]{max-height:calc(100dvh - 80px)}.max-h-\[min\(72dvh\,560px\)\]{max-height:min(72dvh,560px)}.min-h-0{min-height:0px}.min-h-\[200px\]{min-height:200px}.min-h-\[220px\]{min-height:220px}.min-h-\[22px\]{min-height:22px}.min-h-\[28px\]{min-height:28px}.min-h-\[320px\]{min-height:320px}.min-h-\[92px\]{min-height:92px}.min-h-full{min-height:100%}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-32{width:8rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[11px\]{width:11px}.w-\[16px\]{width:16px}.w-\[184px\]{width:184px}.w-\[18px\]{width:18px}.w-\[232px\]{width:232px}.w-\[264px\]{width:264px}.w-\[38px\]{width:38px}.w-\[3px\]{width:3px}.w-\[74px\]{width:74px}.w-\[7px\]{width:7px}.w-\[84px\]{width:84px}.w-\[calc\(100\%-20px\)\]{width:calc(100% - 20px)}.w-\[min\(420px\,92vw\)\]{width:min(420px,92vw)}.w-\[min\(92vw\,440px\)\]{width:min(92vw,440px)}.w-\[min\(94vw\,360px\)\]{width:min(94vw,360px)}.w-\[min\(96vw\,440px\)\]{width:min(96vw,440px)}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[220px\]{min-width:220px}.min-w-\[58px\]{min-width:58px}.min-w-max{min-width:-moz-max-content;min-width:max-content}.max-w-\[120px\]{max-width:120px}.max-w-\[140px\]{max-width:140px}.max-w-\[170px\]{max-width:170px}.max-w-\[180px\]{max-width:180px}.max-w-\[220px\]{max-width:220px}.max-w-\[230px\]{max-width:230px}.max-w-\[240px\]{max-width:240px}.max-w-\[260px\]{max-width:260px}.max-w-\[280px\]{max-width:280px}.max-w-\[300px\]{max-width:300px}.max-w-\[320px\]{max-width:320px}.max-w-\[400px\]{max-width:400px}.max-w-\[460px\]{max-width:460px}.max-w-\[520px\]{max-width:520px}.max-w-\[560px\]{max-width:560px}.max-w-\[620px\]{max-width:620px}.max-w-\[720px\]{max-width:720px}.max-w-\[760px\]{max-width:760px}.max-w-\[800px\]{max-width:800px}.max-w-\[88\%\]{max-width:88%}.max-w-\[calc\(100vw-24px\)\]{max-width:calc(100vw - 24px)}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-full{--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[19px\]{--tw-translate-x: 19px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[3px\]{--tw-translate-x: 3px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-1\/2{--tw-translate-y: 50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-45{--tw-rotate: 45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-90{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes blurin{0%{opacity:0;filter:blur(8px);transform:translateY(6px)}to{opacity:1;filter:blur(0);transform:translateY(0)}}.animate-blurin{animation:blurin .7s cubic-bezier(.16,1,.3,1) both}.animate-bob{animation:bob 1.6s ease-in-out infinite}@keyframes bob{0%,to{transform:translateY(0)}50%{transform:translateY(-2px)}}.animate-bob-fast{animation:bob .8s ease-in-out infinite}@keyframes bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-bounce{animation:bounce 1s infinite}@keyframes breathe{0%,to{transform:scale(1);opacity:1}50%{transform:scale(.8);opacity:.6}}.animate-breathe{animation:breathe 1.6s ease-in-out infinite}@keyframes fade{0%{opacity:0}to{opacity:1}}.animate-fade{animation:fade .25s ease-out both}.animate-float{animation:float 2.4s ease-in-out infinite}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-3px)}}.animate-float-slow{animation:float 3.4s ease-in-out infinite}@keyframes ping{75%,to{transform:scale(2);opacity:0}0%{transform:scale(1);opacity:.5}80%,to{transform:scale(2.2);opacity:0}}.animate-ping{animation:ping 1.8s cubic-bezier(0,0,.2,1) infinite}@keyframes pop{0%,to{transform:scale(1)}50%{transform:scale(1.1)}}.animate-pop{animation:pop 1.4s ease-in-out infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes rise{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.animate-rise{animation:rise .6s cubic-bezier(.16,1,.3,1) both}@keyframes scalein{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}.animate-scalein{animation:scalein .28s cubic-bezier(.16,1,.3,1) both}@keyframes shake{0%,to{transform:translate(0)}25%{transform:translate(-1.5px)}75%{transform:translate(1.5px)}}.animate-shake{animation:shake .35s ease-in-out infinite}@keyframes slideleft{0%{opacity:0;transform:translate(-10px)}to{opacity:1;transform:translate(0)}}.animate-slideleft{animation:slideleft .4s cubic-bezier(.16,1,.3,1) both}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.animate-spin-slow{animation:spinslow 18s linear infinite}.cursor-default{cursor:default}.cursor-grab{cursor:grab}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize-y{resize:vertical}.resize{resize:both}.list-none{list-style-type: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-rows-7{grid-template-rows:repeat(7,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-3\.5{gap:.875rem}.gap-4{gap:1rem}.gap-\[3px\]{gap:3px}.gap-x-1{-moz-column-gap:.25rem;column-gap:.25rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-y-0\.5{row-gap:.125rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-2\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.625rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.625rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-line>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(var(--line) / var(--tw-divide-opacity, 1))}.self-start{align-self:flex-start}.self-center{align-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-\[2px\]{border-radius:2px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-r-full{border-top-right-radius:9999px;border-bottom-right-radius:9999px}.rounded-t-2xl{border-top-left-radius:1rem;border-top-right-radius:1rem}.rounded-t-\[28px\]{border-top-left-radius:28px;border-top-right-radius:28px}.rounded-br-md{border-bottom-right-radius:.375rem}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-accent{--tw-border-opacity: 1;border-color:rgb(var(--accent) / var(--tw-border-opacity, 1))}.border-accent\/30{border-color:rgb(var(--accent) / .3)}.border-accent\/35{border-color:rgb(var(--accent) / .35)}.border-accent\/40{border-color:rgb(var(--accent) / .4)}.border-accent\/45{border-color:rgb(var(--accent) / .45)}.border-accent\/50{border-color:rgb(var(--accent) / .5)}.border-accent\/60{border-color:rgb(var(--accent) / .6)}.border-current{border-color:currentColor}.border-danger\/20{border-color:rgb(var(--danger) / .2)}.border-danger\/25{border-color:rgb(var(--danger) / .25)}.border-danger\/30{border-color:rgb(var(--danger) / .3)}.border-faint{--tw-border-opacity: 1;border-color:rgb(var(--faint) / var(--tw-border-opacity, 1))}.border-line{--tw-border-opacity: 1;border-color:rgb(var(--line) / var(--tw-border-opacity, 1))}.border-line\/60{border-color:rgb(var(--line) / .6)}.border-line\/70{border-color:rgb(var(--line) / .7)}.border-line\/80{border-color:rgb(var(--line) / .8)}.border-on-accent\/40{border-color:rgb(var(--on-accent) / .4)}.border-success\/25{border-color:rgb(var(--success) / .25)}.border-success\/30{border-color:rgb(var(--success) / .3)}.border-transparent{border-color:transparent}.border-warning\/25{border-color:rgb(var(--warning) / .25)}.border-warning\/30{border-color:rgb(var(--warning) / .3)}.border-warning\/40{border-color:rgb(var(--warning) / .4)}.border-t-transparent{border-top-color:transparent}.bg-\[\#011627\]{--tw-bg-opacity: 1;background-color:rgb(1 22 39 / var(--tw-bg-opacity, 1))}.bg-\[\#09090b\]{--tw-bg-opacity: 1;background-color:rgb(9 9 11 / var(--tw-bg-opacity, 1))}.bg-\[\#130d0c\]{--tw-bg-opacity: 1;background-color:rgb(19 13 12 / var(--tw-bg-opacity, 1))}.bg-\[\#1a3e5a\]{--tw-bg-opacity: 1;background-color:rgb(26 62 90 / var(--tw-bg-opacity, 1))}.bg-\[\#262624\]{--tw-bg-opacity: 1;background-color:rgb(38 38 36 / var(--tw-bg-opacity, 1))}.bg-\[\#2a2a31\]{--tw-bg-opacity: 1;background-color:rgb(42 42 49 / var(--tw-bg-opacity, 1))}.bg-\[\#3e2621\]{--tw-bg-opacity: 1;background-color:rgb(62 38 33 / var(--tw-bg-opacity, 1))}.bg-\[\#42423e\]{--tw-bg-opacity: 1;background-color:rgb(66 66 62 / var(--tw-bg-opacity, 1))}.bg-\[\#8a8a92\]{--tw-bg-opacity: 1;background-color:rgb(138 138 146 / var(--tw-bg-opacity, 1))}.bg-\[\#e2e2e5\]{--tw-bg-opacity: 1;background-color:rgb(226 226 229 / var(--tw-bg-opacity, 1))}.bg-\[\#fcfcfc\]{--tw-bg-opacity: 1;background-color:rgb(252 252 252 / var(--tw-bg-opacity, 1))}.bg-accent{--tw-bg-opacity: 1;background-color:rgb(var(--accent) / var(--tw-bg-opacity, 1))}.bg-accent-soft{--tw-bg-opacity: 1;background-color:rgb(var(--accent-soft) / var(--tw-bg-opacity, 1))}.bg-accent-soft\/40{background-color:rgb(var(--accent-soft) / .4)}.bg-accent-soft\/50{background-color:rgb(var(--accent-soft) / .5)}.bg-accent-soft\/60{background-color:rgb(var(--accent-soft) / .6)}.bg-accent\/10{background-color:rgb(var(--accent) / .1)}.bg-accent\/20{background-color:rgb(var(--accent) / .2)}.bg-accent\/35{background-color:rgb(var(--accent) / .35)}.bg-accent\/55{background-color:rgb(var(--accent) / .55)}.bg-accent\/70{background-color:rgb(var(--accent) / .7)}.bg-accent\/80{background-color:rgb(var(--accent) / .8)}.bg-black\/30{background-color:#0000004d}.bg-black\/35{background-color:#00000059}.bg-black\/40{background-color:#0006}.bg-canvas{--tw-bg-opacity: 1;background-color:rgb(var(--canvas) / var(--tw-bg-opacity, 1))}.bg-canvas\/40{background-color:rgb(var(--canvas) / .4)}.bg-canvas\/60{background-color:rgb(var(--canvas) / .6)}.bg-canvas\/70{background-color:rgb(var(--canvas) / .7)}.bg-canvas\/95{background-color:rgb(var(--canvas) / .95)}.bg-current{background-color:currentColor}.bg-danger{--tw-bg-opacity: 1;background-color:rgb(var(--danger) / var(--tw-bg-opacity, 1))}.bg-danger\/10{background-color:rgb(var(--danger) / .1)}.bg-danger\/\[0\.02\]{background-color:rgb(var(--danger) / .02)}.bg-danger\/\[0\.045\]{background-color:rgb(var(--danger) / .045)}.bg-danger\/\[0\.04\]{background-color:rgb(var(--danger) / .04)}.bg-danger\/\[0\.05\]{background-color:rgb(var(--danger) / .05)}.bg-danger\/\[0\.10\]{background-color:rgb(var(--danger) / .1)}.bg-faint{--tw-bg-opacity: 1;background-color:rgb(var(--faint) / var(--tw-bg-opacity, 1))}.bg-faint\/60{background-color:rgb(var(--faint) / .6)}.bg-ink{--tw-bg-opacity: 1;background-color:rgb(var(--ink) / var(--tw-bg-opacity, 1))}.bg-ink\/20{background-color:rgb(var(--ink) / .2)}.bg-line{--tw-bg-opacity: 1;background-color:rgb(var(--line) / var(--tw-bg-opacity, 1))}.bg-line\/70{background-color:rgb(var(--line) / .7)}.bg-on-accent\/80{background-color:rgb(var(--on-accent) / .8)}.bg-panel{--tw-bg-opacity: 1;background-color:rgb(var(--panel) / var(--tw-bg-opacity, 1))}.bg-panel\/60{background-color:rgb(var(--panel) / .6)}.bg-panel\/80{background-color:rgb(var(--panel) / .8)}.bg-panel\/95{background-color:rgb(var(--panel) / .95)}.bg-raised{--tw-bg-opacity: 1;background-color:rgb(var(--raised) / var(--tw-bg-opacity, 1))}.bg-raised\/20{background-color:rgb(var(--raised) / .2)}.bg-raised\/25{background-color:rgb(var(--raised) / .25)}.bg-raised\/30{background-color:rgb(var(--raised) / .3)}.bg-raised\/40{background-color:rgb(var(--raised) / .4)}.bg-raised\/50{background-color:rgb(var(--raised) / .5)}.bg-raised\/60{background-color:rgb(var(--raised) / .6)}.bg-raised\/70{background-color:rgb(var(--raised) / .7)}.bg-raised\/80{background-color:rgb(var(--raised) / .8)}.bg-success{--tw-bg-opacity: 1;background-color:rgb(var(--success) / var(--tw-bg-opacity, 1))}.bg-success\/10{background-color:rgb(var(--success) / .1)}.bg-success\/15{background-color:rgb(var(--success) / .15)}.bg-success\/40{background-color:rgb(var(--success) / .4)}.bg-success\/\[0\.04\]{background-color:rgb(var(--success) / .04)}.bg-success\/\[0\.05\]{background-color:rgb(var(--success) / .05)}.bg-success\/\[0\.11\]{background-color:rgb(var(--success) / .11)}.bg-transparent{background-color:transparent}.bg-warning{--tw-bg-opacity: 1;background-color:rgb(var(--warning) / var(--tw-bg-opacity, 1))}.bg-warning\/10{background-color:rgb(var(--warning) / .1)}.bg-warning\/70{background-color:rgb(var(--warning) / .7)}.bg-warning\/\[0\.04\]{background-color:rgb(var(--warning) / .04)}.bg-warning\/\[0\.05\]{background-color:rgb(var(--warning) / .05)}.bg-warning\/\[0\.06\]{background-color:rgb(var(--warning) / .06)}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-\[\#fcfcfc\]{--tw-gradient-from: #fcfcfc var(--tw-gradient-from-position);--tw-gradient-to: rgb(252 252 252 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-accent-soft{--tw-gradient-from: rgb(var(--accent-soft) / 1) var(--tw-gradient-from-position);--tw-gradient-to: rgb(var(--accent-soft) / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-accent\/40{--tw-gradient-from: rgb(var(--accent) / .4) var(--tw-gradient-from-position);--tw-gradient-to: rgb(var(--accent) / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-accent{--tw-gradient-to: rgb(var(--accent) / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), rgb(var(--accent) / 1) var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-\[\#09090b\]{--tw-gradient-to: #09090b var(--tw-gradient-to-position)}.to-accent{--tw-gradient-to: rgb(var(--accent) / 1) var(--tw-gradient-to-position)}.to-panel{--tw-gradient-to: rgb(var(--panel) / 1) var(--tw-gradient-to-position)}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-3\.5{padding:.875rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-7{padding-top:1.75rem;padding-bottom:1.75rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-\[3px\]{padding-top:3px;padding-bottom:3px}.py-px{padding-top:1px;padding-bottom:1px}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pb-1\.5{padding-bottom:.375rem}.pb-12{padding-bottom:3rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pl-10{padding-left:2.5rem}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-7{padding-left:1.75rem}.pl-8{padding-left:2rem}.pl-9{padding-left:2.25rem}.pl-\[17px\]{padding-left:17px}.pl-\[18px\]{padding-left:18px}.pl-\[19px\]{padding-left:19px}.pl-\[34px\]{padding-left:34px}.pr-1{padding-right:.25rem}.pr-1\.5{padding-right:.375rem}.pr-14{padding-right:3.5rem}.pr-2{padding-right:.5rem}.pr-2\.5{padding-right:.625rem}.pr-3{padding-right:.75rem}.pr-8{padding-right:2rem}.pr-9{padding-right:2.25rem}.pt-0\.5{padding-top:.125rem}.pt-1{padding-top:.25rem}.pt-1\.5{padding-top:.375rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-3\.5{padding-top:.875rem}.pt-5{padding-top:1.25rem}.pt-6{padding-top:1.5rem}.pt-7{padding-top:1.75rem}.pt-8{padding-top:2rem}.pt-\[10vh\]{padding-top:10vh}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-display{font-family:Geist,Inter,system-ui,-apple-system,sans-serif}.font-mono{font-family:Geist Mono,JetBrains Mono,ui-monospace,SFMono-Regular,monospace}.font-sans{font-family:Geist,Inter,system-ui,-apple-system,Segoe UI,sans-serif}.text-\[10\.5px\]{font-size:10.5px}.text-\[10px\]{font-size:10px}.text-\[11\.5px\]{font-size:11.5px}.text-\[11px\]{font-size:11px}.text-\[12\.5px\]{font-size:12.5px}.text-\[12px\]{font-size:12px}.text-\[13\.5px\]{font-size:13.5px}.text-\[13px\]{font-size:13px}.text-\[14\.5px\]{font-size:14.5px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.text-\[16px\]{font-size:16px}.text-\[19px\]{font-size:19px}.text-\[20px\]{font-size:20px}.text-\[22px\]{font-size:22px}.text-\[26px\]{font-size:26px}.text-\[28px\]{font-size:28px}.text-\[8\.5px\]{font-size:8.5px}.text-\[8px\]{font-size:8px}.text-\[9\.5px\]{font-size:9.5px}.text-\[9px\]{font-size:9px}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-\[1\.55\]{line-height:1.55}.leading-\[1\.65\]{line-height:1.65}.leading-\[1\.6\]{line-height:1.6}.leading-\[1\.75\]{line-height:1.75}.leading-\[1\.7\]{line-height:1.7}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-\[-0\.01em\]{letter-spacing:-.01em}.tracking-\[-0\.025em\]{letter-spacing:-.025em}.tracking-\[-0\.02em\]{letter-spacing:-.02em}.tracking-\[-0\.035em\]{letter-spacing:-.035em}.tracking-\[-0\.03em\]{letter-spacing:-.03em}.tracking-\[-0\.04em\]{letter-spacing:-.04em}.tracking-\[0\.01em\]{letter-spacing:.01em}.tracking-\[0\.08em\]{letter-spacing:.08em}.tracking-\[0\.12em\]{letter-spacing:.12em}.tracking-\[0\.13em\]{letter-spacing:.13em}.tracking-\[0\.14em\]{letter-spacing:.14em}.tracking-\[0\.1em\]{letter-spacing:.1em}.tracking-\[0\.22em\]{letter-spacing:.22em}.tracking-\[0\.24em\]{letter-spacing:.24em}.tracking-\[0\.2em\]{letter-spacing:.2em}.tracking-normal{letter-spacing:0em}.tracking-tight{letter-spacing:-.025em}.text-accent{--tw-text-opacity: 1;color:rgb(var(--accent) / var(--tw-text-opacity, 1))}.text-accent\/80{color:rgb(var(--accent) / .8)}.text-current{color:currentColor}.text-danger{--tw-text-opacity: 1;color:rgb(var(--danger) / var(--tw-text-opacity, 1))}.text-faint{--tw-text-opacity: 1;color:rgb(var(--faint) / var(--tw-text-opacity, 1))}.text-faint\/50{color:rgb(var(--faint) / .5)}.text-faint\/70{color:rgb(var(--faint) / .7)}.text-faint\/80{color:rgb(var(--faint) / .8)}.text-ink{--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.text-ink\/80{color:rgb(var(--ink) / .8)}.text-ink\/85{color:rgb(var(--ink) / .85)}.text-line{--tw-text-opacity: 1;color:rgb(var(--line) / var(--tw-text-opacity, 1))}.text-muted{--tw-text-opacity: 1;color:rgb(var(--muted) / var(--tw-text-opacity, 1))}.text-on-accent{--tw-text-opacity: 1;color:rgb(var(--on-accent) / var(--tw-text-opacity, 1))}.text-panel{--tw-text-opacity: 1;color:rgb(var(--panel) / var(--tw-text-opacity, 1))}.text-success{--tw-text-opacity: 1;color:rgb(var(--success) / var(--tw-text-opacity, 1))}.text-warning{--tw-text-opacity: 1;color:rgb(var(--warning) / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.decoration-ink\/30{text-decoration-color:rgb(var(--ink) / .3)}.underline-offset-\[3px\]{text-underline-offset:3px}.accent-\[rgb\(var\(--accent\)\)\]{accent-color:rgb(var(--accent))}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow-\[0_0_0_3px_rgb\(var\(--success\)\/0\.18\)\]{--tw-shadow: 0 0 0 3px rgb(var(--success)/.18);--tw-shadow-colored: 0 0 0 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_0_3px_rgb\(var\(--warning\)\/0\.18\)\]{--tw-shadow: 0 0 0 3px rgb(var(--warning)/.18);--tw-shadow-colored: 0 0 0 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_1px_2px_rgba\(20\,22\,30\,0\.08\)\]{--tw-shadow: 0 1px 2px rgba(20,22,30,.08);--tw-shadow-colored: 0 1px 2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[inset_0_1px_3px_rgba\(0\,0\,0\,0\.12\)\]{--tw-shadow: inset 0 1px 3px rgba(0,0,0,.12);--tw-shadow-colored: inset 0 1px 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-composer{--tw-shadow: 0 1px 2px rgba(0,0,0,.04), 0 8px 28px -12px rgba(0,0,0,.12);--tw-shadow-colored: 0 1px 2px var(--tw-shadow-color), 0 8px 28px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-float{--tw-shadow: 0 24px 64px -20px rgba(0,0,0,.28), 0 0 0 1px rgb(var(--line));--tw-shadow-colored: 0 24px 64px -20px var(--tw-shadow-color), 0 0 0 1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-pill{--tw-shadow: 0 1px 2px rgba(0,0,0,.06);--tw-shadow-colored: 0 1px 2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-4{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-accent\/10{--tw-ring-color: rgb(var(--accent) / .1)}.ring-accent\/40{--tw-ring-color: rgb(var(--accent) / .4)}.ring-accent\/\[0\.03\]{--tw-ring-color: rgb(var(--accent) / .03)}.ring-line{--tw-ring-opacity: 1;--tw-ring-color: rgb(var(--line) / var(--tw-ring-opacity, 1))}.blur{--tw-blur: blur(8px);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)}.blur-2xl{--tw-blur: blur(40px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-\[1px\]{--tw-backdrop-blur: blur(1px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-\[2px\]{--tw-backdrop-blur: blur(2px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-\[3px\]{--tw-backdrop-blur: blur(3px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-\[6px\]{--tw-backdrop-blur: blur(6px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur: blur(24px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[bottom\]{transition-property:bottom;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[height\]{transition-property:height;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[width\]{transition-property:width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.\!duration-75{transition-duration:75ms!important}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-out{transition-timing-function:cubic-bezier(.16,1,.3,1)}.ease-spring{transition-timing-function:cubic-bezier(.34,1.56,.64,1)}.\[animation-delay\:0\.8s\]{animation-delay:.8s}.\[animation-delay\:1\.2s\]{animation-delay:1.2s}.\[animation-delay\:120ms\]{animation-delay:.12s}.\[animation-delay\:160ms\]{animation-delay:.16s}.\[animation-delay\:1s\]{animation-delay:1s}.\[animation-delay\:320ms\]{animation-delay:.32s}.\[animation-delay\:420ms\]{animation-delay:.42s}.md{font-family:Geist,Inter,system-ui,-apple-system,Segoe UI,sans-serif;line-height:1.72;--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1));font-size:var(--chat-font-size, 15px);font-feature-settings:"kern" 1,"liga" 1;word-break:break-word}.md p,.md li{text-wrap:pretty}.md h1,.md h2,.md h3,.md h4{text-wrap:balance}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p{margin:.7em 0}.md h1,.md h2,.md h3,.md h4{font-weight:600;letter-spacing:-.02em;--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1));margin:1.4em 0 .55em;line-height:1.3}.md h1,.md h2{font-weight:600}.md h1{font-size:1.45em}.md h2{font-size:1.22em}.md h3{font-weight:600;font-size:1.08em}.md h4{font-weight:600;font-size:1em}.md ul,.md ol{margin:.65em 0;padding-left:1.35em}.md ul{list-style:disc}.md ol{list-style:decimal}.md li{margin:.28em 0}.md li>p{margin:.25em 0}.md li::marker{color:rgb(var(--faint))}.md a{--tw-text-opacity: 1;color:rgb(var(--accent) / var(--tw-text-opacity, 1));text-decoration-line:underline;text-decoration-color:rgb(var(--accent) / .3);text-underline-offset:2px;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.md a:hover{text-decoration-color:rgb(var(--accent) / 1)}.md strong{font-weight:600;--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.md code{border-radius:5px;--tw-bg-opacity: 1;background-color:rgb(var(--raised) / var(--tw-bg-opacity, 1));padding:.12em .36em;font-family:Geist Mono,JetBrains Mono,ui-monospace,SFMono-Regular,monospace;font-size:.845em;--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.md pre code{background:transparent;padding:0;border-radius:0;font-size:inherit;color:inherit}.md blockquote{margin:.9em 0;border-left:2px solid rgb(var(--accent) / .35);padding:.15em 0 .15em .9em;color:rgb(var(--muted))}.md hr{margin:1.5em 0;border:0;border-top:1px solid rgb(var(--line))}.md table{margin:1em 0;width:100%;border-collapse:collapse;font-size:.9em}.md th,.md td{border:1px solid rgb(var(--line));padding:.45em .7em;text-align:left;vertical-align:top}.md th{background-color:rgb(var(--raised) / .7);font-weight:500}.md input[type=checkbox]{margin-right:.4em;accent-color:rgb(var(--accent))}.md img{max-width:100%;border-radius:10px;border:1px solid rgb(var(--line))}.code-shell{margin-top:1rem;margin-bottom:1rem;overflow:hidden;border-radius:.75rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(var(--line) / var(--tw-border-opacity, 1));background:rgb(var(--canvas))}.code-head{display:flex;height:2.25rem;align-items:center;justify-content:space-between;border-bottom-width:1px;border-color:rgb(var(--line) / .7);background-color:rgb(var(--raised) / .4);padding-left:.75rem;padding-right:.75rem}.code-lang{font-family:Geist Mono,JetBrains Mono,ui-monospace,SFMono-Regular,monospace;font-size:10.5px;letter-spacing:.025em;--tw-text-opacity: 1;color:rgb(var(--faint) / var(--tw-text-opacity, 1))}.code-pre{overflow-x:auto;padding:.875rem;font-family:Geist Mono,JetBrains Mono,ui-monospace,SFMono-Regular,monospace;font-size:12.5px;line-height:1.7;-moz-tab-size:2;-o-tab-size:2;tab-size:2}.code-pre code{background:transparent!important;padding:0!important;font-size:inherit!important;white-space:pre}.hljs{color:rgb(var(--ink));background:transparent}.hljs-comment,.hljs-quote{color:rgb(var(--faint));font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-literal,.hljs-type{color:rgb(var(--syn-keyword))}.hljs-string,.hljs-regexp,.hljs-addition{color:rgb(var(--syn-string))}.hljs-number,.hljs-symbol,.hljs-bullet{color:rgb(var(--syn-number))}.hljs-title,.hljs-section,.hljs-name,.hljs-selector-id,.hljs-selector-class{color:rgb(var(--syn-title))}.hljs-attr,.hljs-attribute,.hljs-variable,.hljs-template-variable,.hljs-property{color:rgb(var(--syn-attr))}.hljs-built_in,.hljs-class .hljs-title{color:rgb(var(--syn-title))}.hljs-meta,.hljs-deletion{color:rgb(var(--danger))}.thin-scroll{scrollbar-width:thin;scrollbar-color:rgb(var(--line)) transparent}.thin-scroll::-webkit-scrollbar{width:10px;height:10px}.thin-scroll::-webkit-scrollbar-track{background:transparent}.thin-scroll::-webkit-scrollbar-thumb{border:3px solid transparent;border-radius:999px;background-clip:content-box;background-color:rgb(var(--line))}.thin-scroll::-webkit-scrollbar-thumb:hover{background-color:rgb(var(--faint) / .6)}.hero-wash{background:radial-gradient(60% 50% at 50% 0%,rgb(var(--accent) / .07),transparent 70%)}.bg-dots{background-image:radial-gradient(rgb(var(--faint) / .28) 1px,transparent 1px);background-size:24px 24px;-webkit-mask-image:radial-gradient(70% 60% at 50% 30%,#000 10%,transparent 75%);mask-image:radial-gradient(70% 60% at 50% 30%,#000 10%,transparent 75%)}.ambient-blob{position:absolute;border-radius:9999px;filter:blur(64px);opacity:.35;pointer-events:none}.card-lift{transition:transform .3s cubic-bezier(.16,1,.3,1),box-shadow .3s ease,border-color .2s ease,background-color .2s ease}.card-lift:hover{transform:translateY(-2px);box-shadow:0 14px 32px -20px #0000004d;border-color:rgb(var(--faint) / .4)}@keyframes glow-pulse{0%,to{box-shadow:0 0 rgb(var(--accent) / .2)}50%{box-shadow:0 0 0 7px rgb(var(--accent) / 0)}}.composer-glow{animation:glow-pulse 1.9s ease-out infinite}.composer-shadow{box-shadow:0 1px 2px #0000000a,0 10px 30px -14px #00000024;transition:box-shadow .25s ease,border-color .25s ease}.composer-shadow:focus-within{border-color:rgb(var(--accent) / .45);box-shadow:0 0 0 4px rgb(var(--accent) / .08),0 12px 34px -14px #0000002e}.dark .composer-shadow{box-shadow:0 1px 1px #0006,0 16px 40px -16px #000000b3}.dark .composer-shadow:focus-within{box-shadow:0 0 0 4px rgb(var(--accent) / .1),0 16px 40px -16px #000000b3}.dark .card-lift:hover{box-shadow:0 14px 32px -18px #000c}.modal-panel{animation:modal-in .28s cubic-bezier(.16,1,.3,1) backwards}.modal-scrim{animation:fade .2s ease-out both}@keyframes modal-in{0%{opacity:0;transform:translateY(10px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.stagger>*{animation:rise .6s cubic-bezier(.16,1,.3,1) both}.stagger>*:nth-child(1){animation-delay:40ms}.stagger>*:nth-child(2){animation-delay:.11s}.stagger>*:nth-child(3){animation-delay:.18s}.stagger>*:nth-child(4){animation-delay:.25s}.stagger>*:nth-child(5){animation-delay:.32s}.stagger>*:nth-child(6){animation-delay:.39s}.text-solar{background:linear-gradient(100deg,rgb(var(--ink)) 20%,rgb(var(--accent)) 120%);-webkit-background-clip:text;background-clip:text;color:transparent}.loading-bar{position:relative;overflow:hidden;background:rgb(var(--line))}.loading-bar:after{content:"";position:absolute;inset:0;width:40%;border-radius:inherit;background:linear-gradient(90deg,transparent,rgb(var(--accent)),transparent);animation:sweep 1.4s cubic-bezier(.65,0,.35,1) infinite}.splash{position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center;background:rgb(var(--panel));transition:opacity .5s ease,transform .5s cubic-bezier(.16,1,.3,1),filter .5s ease}.splash.is-leaving{opacity:0;transform:scale(1.03);filter:blur(6px);pointer-events:none}.splash-sun .ray{stroke-dasharray:8;stroke-dashoffset:8;animation:draw .5s cubic-bezier(.16,1,.3,1) forwards}.splash-sun .core{transform-origin:32px 32px;animation:core-in .7s cubic-bezier(.34,1.56,.64,1) both}.splash-sun .rays{transform-origin:32px 32px;animation:spinslow 14s linear infinite}.splash-word span{display:inline-block;opacity:0;animation:blurin .7s cubic-bezier(.16,1,.3,1) forwards}@keyframes draw{to{stroke-dashoffset:0}}@keyframes core-in{0%{transform:scale(0);opacity:0}to{transform:scale(1);opacity:1}}@keyframes spinslow{to{transform:rotate(360deg)}}.md.is-streaming>:last-child:after{content:"";display:inline-block;width:7px;height:1.05em;margin-left:2px;border-radius:2px;vertical-align:-.16em;background:rgb(var(--accent));animation:caret 1.1s ease-in-out infinite}@keyframes caret{0%,to{opacity:1}50%{opacity:.15}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-duration:.01ms!important}}.pxs-a{animation:px-a 1s steps(1) infinite}.pxs-b{animation:px-b 1s steps(1) infinite}@keyframes px-a{0%,to{opacity:1}50%{opacity:.25}}@keyframes px-b{0%,to{opacity:.25}50%{opacity:1}}.text-shimmer{background:linear-gradient(90deg,rgb(var(--muted)),rgb(var(--ink)) 45%,rgb(var(--accent)),rgb(var(--ink)) 55%,rgb(var(--muted)));background-size:250% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:shimmer 2.2s linear infinite}.pxs-blink,.pxs-scan{transform-box:fill-box;transform-origin:center}.pxs-blink{animation:px-blink 4.2s steps(1) infinite}.pxs-scan{animation:px-scan 1.2s steps(1) infinite}@keyframes px-blink{0%,94%,to{transform:scaleY(1)}96%{transform:scaleY(.2)}}@keyframes px-scan{0%,to{transform:translate(-1px)}33%{transform:translate(0)}66%{transform:translate(1px)}}.settings-enter>*{animation:rise .55s cubic-bezier(.16,1,.3,1) both}.settings-enter>*:nth-child(2){animation-delay:60ms}.settings-enter>*:nth-child(3){animation-delay:.12s}.settings-enter>*:nth-child(4){animation-delay:.18s}.settings-enter>*:nth-child(n+5){animation-delay:.24s}.settings-title-in{animation:blurin .55s cubic-bezier(.16,1,.3,1) both}.settings-shell{animation:shell-in .42s cubic-bezier(.16,1,.3,1) backwards}.settings-glow{background:radial-gradient(120% 100% at 0% 0%,rgb(var(--accent) / .16),transparent 65%),radial-gradient(80% 80% at 100% 0%,rgb(var(--accent) / .06),transparent 70%)}@keyframes shell-in{0%{opacity:0;transform:translateY(18px) scale(.975);filter:blur(6px)}to{opacity:1;transform:none;filter:blur(0)}}.deck-scrim{animation:fade .25s ease-out both}.deck-panel{animation:deck-in .5s cubic-bezier(.16,1,.3,1) backwards}.deck-row{animation:deck-row .4s cubic-bezier(.16,1,.3,1) backwards}.deck-pop{animation:scalein .5s cubic-bezier(.34,1.56,.64,1) both}.deck-aurora{background:radial-gradient(70% 120% at 12% 0%,rgb(var(--accent) / .18),transparent 70%),radial-gradient(50% 100% at 95% 0%,rgb(var(--accent) / .07),transparent 70%)}.deck-throttle{background-image:radial-gradient(rgb(var(--line)) 1px,transparent 1px);background-size:14px 14px;background-color:rgb(var(--canvas) / .7)}.throttle-knob{box-shadow:0 0 0 4px rgb(var(--accent) / .16),0 6px 18px -6px rgb(var(--accent) / .55)}.throttle-knob:after{content:"";position:absolute;inset:-6px;border-radius:9999px;border:1px solid rgb(var(--accent) / .5);animation:ping 2.4s cubic-bezier(0,0,.2,1) infinite}.deck-trigger:active{transform:scale(.96)}@keyframes deck-in{0%{opacity:0;transform:translateY(24px) scale(.96);filter:blur(8px)}to{opacity:1;transform:none;filter:blur(0)}}@keyframes deck-row{0%{opacity:0;transform:translate(-10px)}to{opacity:1;transform:none}}.diff-row{animation:deck-row .35s cubic-bezier(.16,1,.3,1) backwards}.diff-badge{animation:scalein .4s cubic-bezier(.34,1.56,.64,1) both}.msg-enter{animation:rise .5s cubic-bezier(.16,1,.3,1) both}button:not(:disabled){-webkit-tap-highlight-color:transparent}.chip,.nav-item,.sidebar-item{will-change:transform}.session-card:hover{transform:translate(2px)}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}}.placeholder\:text-faint::-moz-placeholder{--tw-text-opacity: 1;color:rgb(var(--faint) / var(--tw-text-opacity, 1))}.placeholder\:text-faint::placeholder{--tw-text-opacity: 1;color:rgb(var(--faint) / var(--tw-text-opacity, 1))}.placeholder\:text-muted::-moz-placeholder{--tw-text-opacity: 1;color:rgb(var(--muted) / var(--tw-text-opacity, 1))}.placeholder\:text-muted::placeholder{--tw-text-opacity: 1;color:rgb(var(--muted) / var(--tw-text-opacity, 1))}.focus-within\:opacity-100:focus-within{opacity:1}.hover\:border-accent\/40:hover{border-color:rgb(var(--accent) / .4)}.hover\:border-accent\/50:hover{border-color:rgb(var(--accent) / .5)}.hover\:border-faint\/50:hover{border-color:rgb(var(--faint) / .5)}.hover\:border-line:hover{--tw-border-opacity: 1;border-color:rgb(var(--line) / var(--tw-border-opacity, 1))}.hover\:border-warning\/30:hover{border-color:rgb(var(--warning) / .3)}.hover\:bg-accent\/90:hover{background-color:rgb(var(--accent) / .9)}.hover\:bg-danger\/10:hover{background-color:rgb(var(--danger) / .1)}.hover\:bg-danger\/90:hover{background-color:rgb(var(--danger) / .9)}.hover\:bg-danger\/\[0\.06\]:hover{background-color:rgb(var(--danger) / .06)}.hover\:bg-panel:hover{--tw-bg-opacity: 1;background-color:rgb(var(--panel) / var(--tw-bg-opacity, 1))}.hover\:bg-raised:hover{--tw-bg-opacity: 1;background-color:rgb(var(--raised) / var(--tw-bg-opacity, 1))}.hover\:bg-raised\/40:hover{background-color:rgb(var(--raised) / .4)}.hover\:bg-raised\/60:hover{background-color:rgb(var(--raised) / .6)}.hover\:bg-raised\/70:hover{background-color:rgb(var(--raised) / .7)}.hover\:bg-raised\/80:hover{background-color:rgb(var(--raised) / .8)}.hover\:\!text-danger:hover{--tw-text-opacity: 1 !important;color:rgb(var(--danger) / var(--tw-text-opacity, 1))!important}.hover\:text-danger:hover{--tw-text-opacity: 1;color:rgb(var(--danger) / var(--tw-text-opacity, 1))}.hover\:text-ink:hover{--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.hover\:text-warning:hover{--tw-text-opacity: 1;color:rgb(var(--warning) / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:decoration-ink:hover{text-decoration-color:rgb(var(--ink) / 1)}.hover\:opacity-85:hover{opacity:.85}.hover\:shadow-composer:hover{--tw-shadow: 0 1px 2px rgba(0,0,0,.04), 0 8px 28px -12px rgba(0,0,0,.12);--tw-shadow-colored: 0 1px 2px var(--tw-shadow-color), 0 8px 28px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:brightness-110:hover{--tw-brightness: brightness(1.1);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)}.focus\:border-accent\/50:focus{border-color:rgb(var(--accent) / .5)}.focus\:border-line:focus{--tw-border-opacity: 1;border-color:rgb(var(--line) / var(--tw-border-opacity, 1))}.focus\:bg-panel:focus{--tw-bg-opacity: 1;background-color:rgb(var(--panel) / var(--tw-bg-opacity, 1))}.focus\:opacity-100:focus{opacity:1}.focus\:shadow-\[0_0_0_3px_rgb\(var\(--accent\)\/0\.10\)\]:focus{--tw-shadow: 0 0 0 3px rgb(var(--accent)/.1);--tw-shadow-colored: 0 0 0 3px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:shadow-\[0_0_0_4px_rgb\(var\(--accent\)\/0\.10\)\]:focus{--tw-shadow: 0 0 0 4px rgb(var(--accent)/.1);--tw-shadow-colored: 0 0 0 4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus-visible\:opacity-100:focus-visible{opacity:1}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color: rgb(var(--accent) / .5)}.active\:scale-95:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:scale-\[0\.985\]:active{--tw-scale-x: .985;--tw-scale-y: .985;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:scale-\[0\.98\]:active{--tw-scale-x: .98;--tw-scale-y: .98;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-35:disabled{opacity:.35}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-45:disabled{opacity:.45}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.group\/advanced[open] .group-open\/advanced\:rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:block{display:block}.group:hover .group-hover\:flex{display:flex}.group\/item:hover .group-hover\/item\:hidden,.group:hover .group-hover\:hidden{display:none}.group\/logo:hover .group-hover\/logo\:rotate-90,.group\/new:hover .group-hover\/new\:rotate-90{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/ws:hover .group-hover\/ws\:-rotate-6{--tw-rotate: -6deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/tab:hover .group-hover\/tab\:scale-110,.group\/ws:hover .group-hover\/ws\:scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\/row:hover .group-hover\/row\:bg-panel{--tw-bg-opacity: 1;background-color:rgb(var(--panel) / var(--tw-bg-opacity, 1))}.group\/starter:hover .group-hover\/starter\:bg-accent-soft{--tw-bg-opacity: 1;background-color:rgb(var(--accent-soft) / var(--tw-bg-opacity, 1))}.group\/compact:hover .group-hover\/compact\:text-ink,.group\/row:hover .group-hover\/row\:text-ink{--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.group\/starter:hover .group-hover\/starter\:text-accent{--tw-text-opacity: 1;color:rgb(var(--accent) / var(--tw-text-opacity, 1))}.group\/task:hover .group-hover\/task\:text-ink,.group\/think:hover .group-hover\/think\:text-ink{--tw-text-opacity: 1;color:rgb(var(--ink) / var(--tw-text-opacity, 1))}.group\/item:hover .group-hover\/item\:opacity-100,.group\/session:hover .group-hover\/session\:opacity-100,.group\/user:hover .group-hover\/user\:opacity-100{opacity:1}.group\/ws:hover .group-hover\/ws\:opacity-0{opacity:0}.group\/ws:hover .group-hover\/ws\:opacity-100{opacity:1}.dark\:bg-black\/45:is(.dark *){background-color:#00000073}.dark\:bg-black\/60:is(.dark *){background-color:#0009}.dark\:bg-black\/65:is(.dark *){background-color:#000000a6}@media not all and (min-width:768px){.max-md\:bg-accent-soft{--tw-bg-opacity: 1;background-color:rgb(var(--accent-soft) / var(--tw-bg-opacity, 1))}}@media(min-width:640px){.sm\:bottom-48{bottom:12rem}.sm\:col-span-2{grid-column:span 2 / span 2}.sm\:mx-8{margin-left:2rem;margin-right:2rem}.sm\:ml-auto{margin-left:auto}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:inline-flex{display:inline-flex}.sm\:hidden{display:none}.sm\:h-\[min\(88dvh\,780px\)\]{height:min(88dvh,780px)}.sm\:h-\[min\(90dvh\,780px\)\]{height:min(90dvh,780px)}.sm\:max-h-\[86dvh\]{max-height:86dvh}.sm\:w-\[180px\]{width:180px}.sm\:w-\[210px\]{width:210px}.sm\:w-\[360px\]{width:360px}.sm\:w-\[420px\]{width:420px}.sm\:w-\[min\(1040px\,96vw\)\]{width:min(1040px,96vw)}.sm\:w-\[min\(1080px\,96vw\)\]{width:min(1080px,96vw)}.sm\:w-\[min\(760px\,94vw\)\]{width:min(760px,94vw)}.sm\:w-\[min\(920px\,94vw\)\]{width:min(920px,94vw)}.sm\:w-\[min\(920px\,96vw\)\]{width:min(920px,96vw)}.sm\:w-\[min\(96vw\,620px\)\]{width:min(96vw,620px)}.sm\:max-w-\[260px\]{max-width:260px}.sm\:max-w-\[82\%\]{max-width:82%}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-\[160px_1fr\]{grid-template-columns:160px 1fr}.sm\:grid-cols-\[1fr_auto\]{grid-template-columns:1fr auto}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:self-auto{align-self:auto}.sm\:\!rounded-\[28px\]{border-radius:28px!important}.sm\:rounded-\[28px\]{border-radius:28px}.sm\:rounded-xl{border-radius:.75rem}.sm\:border-l{border-left-width:1px}.sm\:border-line{--tw-border-opacity: 1;border-color:rgb(var(--line) / var(--tw-border-opacity, 1))}.sm\:p-5{padding:1.25rem}.sm\:p-6{padding:1.5rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:px-7{padding-left:1.75rem;padding-right:1.75rem}.sm\:px-8{padding-left:2rem;padding-right:2rem}.sm\:py-6{padding-top:1.5rem;padding-bottom:1.5rem}.sm\:pb-4{padding-bottom:1rem}.sm\:pl-0{padding-left:0}.sm\:pt-10{padding-top:2.5rem}.sm\:pt-7{padding-top:1.75rem}.sm\:pt-\[14vh\]{padding-top:14vh}.sm\:text-\[34px\]{font-size:34px}}@media(min-width:768px){.md\:block{display:block}.md\:inline{display:inline}.md\:flex{display:flex}.md\:inline-flex{display:inline-flex}.md\:hidden{display:none}.md\:h-\[280px\]{height:280px}.md\:w-\[258px\]{width:258px}.md\:w-\[300px\]{width:300px}.md\:w-\[78px\]{width:78px}.md\:min-w-0{min-width:0px}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:overflow-y-auto{overflow-y:auto}.md\:overflow-x-hidden{overflow-x:hidden}.md\:border-b-0{border-bottom-width:0px}.md\:border-l{border-left-width:1px}.md\:border-r{border-right-width:1px}.md\:border-t-0{border-top-width:0px}.md\:border-line{--tw-border-opacity: 1;border-color:rgb(var(--line) / var(--tw-border-opacity, 1))}.md\:p-2\.5{padding:.625rem}}@media(min-width:1024px){.lg\:static{position:static}.lg\:z-auto{z-index:auto}.lg\:my-1\.5{margin-top:.375rem;margin-bottom:.375rem}.lg\:mr-1\.5{margin-right:.375rem}.lg\:inline-flex{display:inline-flex}.lg\:hidden{display:none}.lg\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-\[minmax\(0\,390px\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,390px) minmax(0,1fr)}.lg\:overflow-hidden{overflow:hidden}.lg\:rounded-xl{border-radius:.75rem}.lg\:border{border-width:1px}.lg\:border-b-0{border-bottom-width:0px}.lg\:border-r{border-right-width:1px}.lg\:border-line{--tw-border-opacity: 1;border-color:rgb(var(--line) / var(--tw-border-opacity, 1))}.lg\:shadow-none{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.lg\:shadow-pill{--tw-shadow: 0 1px 2px rgba(0,0,0,.06);--tw-shadow-colored: 0 1px 2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}}@media(min-width:1280px){.xl\:static{position:static}.xl\:z-auto{z-index:auto}.xl\:hidden{display:none}.xl\:max-h-\[38\%\]{max-height:38%}.xl\:w-\[390px\]{width:390px}.xl\:w-\[560px\]{width:560px}.xl\:shadow-none{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}
|
package/dist/index.html
CHANGED
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
if (DARK.includes(theme)) root.classList.add('dark')
|
|
32
32
|
} catch {}
|
|
33
33
|
</script>
|
|
34
|
-
<script type="module" crossorigin src="./assets/index-
|
|
35
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
34
|
+
<script type="module" crossorigin src="./assets/index-CyxlAgKM.js"></script>
|
|
35
|
+
<link rel="stylesheet" crossorigin href="./assets/index-DFeF90S8.css">
|
|
36
36
|
</head>
|
|
37
37
|
<body>
|
|
38
38
|
<div id="root"><div class="splash" id="boot-splash" aria-hidden="true">
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bibobonking/opensol",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "3.1
|
|
4
|
+
"version": "3.2.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"opensol": "./server/opensol.mjs"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"dev": "vite",
|
|
10
|
+
"dev": "node server/opensol.mjs start && vite",
|
|
11
11
|
"build": "tsc -b && vite build",
|
|
12
12
|
"artifacts": "node scripts/pull-artifacts.mjs",
|
|
13
13
|
"preview": "vite preview",
|
|
@@ -42,8 +42,5 @@
|
|
|
42
42
|
"tailwindcss": "^3.4.19",
|
|
43
43
|
"typescript": "^5.9.2",
|
|
44
44
|
"vite": "^7.1.3"
|
|
45
|
-
}
|
|
46
|
-
"workspaces": [
|
|
47
|
-
"packages/*"
|
|
48
|
-
]
|
|
45
|
+
}
|
|
49
46
|
}
|
package/server/README.md
CHANGED
|
@@ -1,75 +1,22 @@
|
|
|
1
|
-
# OpenSol
|
|
1
|
+
# OpenSol daemon
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
The daemon binds to `127.0.0.1` only. It serves the prebuilt app from `dist/` and stores everything under `~/.maxcode`. API keys and hosting tokens are encrypted with AES-256-GCM using a local `master.key`; the browser only ever receives a masked hint.
|
|
6
|
-
|
|
7
|
-
## Run the built app
|
|
8
|
-
|
|
9
|
-
Requires Node.js 20 or newer. No `npm install` is needed to run the built app.
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm start
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
`npm start` launches the daemon in the background and opens `http://127.0.0.1:4174`. Equivalent commands:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
node server/opensol.mjs start --open
|
|
19
|
-
node server/opensol.mjs status
|
|
20
|
-
node server/opensol.mjs logs
|
|
21
|
-
node server/opensol.mjs stop
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
On first launch, choose a real project folder, then add a provider key in **Settings → Models** and pick a live model. Everything else — workspace tree, editor, git, search, build/plan modes, subagents and deploys — runs through the local daemon.
|
|
25
|
-
|
|
26
|
-
## What is in the app
|
|
27
|
-
|
|
28
|
-
- **Build and Plan modes.** Build can edit files and run commands; Plan is read-only investigation.
|
|
29
|
-
- **Live models and reasoning.** Model lists come from each provider; the models.dev catalog is only a fallback. Thinking is the provider's job: an effort control appears only when the selected model advertises effort levels or a token budget, and it is sent automatically.
|
|
30
|
-
- **Themes.** Light, Dark, Night Owl, Claude Code and Flame, plus System, which follows the OS preference. Syntax highlighting follows the theme.
|
|
31
|
-
- **Real tools.** Read/write/edit files, grep, list files, shell, git, web search, URL reading and a read-only SubAgent. Each user turn renders as one answer, with consecutive tool rounds collapsed into a single “Used N tools” group.
|
|
32
|
-
- **Turn queue.** A prompt sent while a turn is streaming waits its turn and runs next, instead of splitting into a second reply.
|
|
33
|
-
- **Local plugins.** Two plugin modes:
|
|
34
|
-
- **API v1 (sandboxed):** Install a package, review its declared workspace capabilities and HTTPS domains, preview in a sandbox, mark it Safe. Plugin code gets a restricted API (`registerCommand`, `registerPanel`, `registerTheme`, workspace read/write, HTTPS-only network to declared hosts).
|
|
35
|
-
- **API v2 (free):** Plugin receives the full Cordis kernel context — register custom AI tools, hook into `agent/request` / `tools/execute` / `system-prompt/assemble` events, patch any app behaviour at runtime. No capability declarations needed. When removed, the Cordis fiber disposes automatically and the app returns to its original state — no source files are ever modified.
|
|
36
|
-
The same panel builds a package from a folder you pick. Packages live under `~/.maxcode/plugins`, outside the selected workspace.
|
|
37
|
-
- **Workspace panel.** Folder tree, file editor, git status and quick file actions.
|
|
38
|
-
- **Deploy.** Cloudflare Pages, Netlify and Vercel, for the open workspace or another existing folder. Tokens stay in the daemon and are passed to CLIs through environment variables.
|
|
39
|
-
- **Local history.** Conversations, settings, workspaces and encrypted keys survive restarts.
|
|
40
|
-
|
|
41
|
-
Slash commands include `/search`, `/plan`, `/build`, `/new`, `/workspace`, `/deploy`, `/settings` and `/clear`.
|
|
42
|
-
|
|
43
|
-
## Development
|
|
3
|
+
`server/` is the local Node daemon (`127.0.0.1:4174`, no runtime dependencies besides `fflate` for plugin packages). It owns everything privileged: provider credentials, workspace files, shell, git, search, MCP connections, plugins, skills and deploys. The React app in `src/` only talks to it over `/api`.
|
|
44
4
|
|
|
45
5
|
```bash
|
|
46
|
-
npm
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
npm run typecheck
|
|
6
|
+
node server/opensol.mjs start --open # background daemon + browser (also: npm start)
|
|
7
|
+
node server/opensol.mjs status | logs | stop
|
|
8
|
+
node server/daemon.mjs # foreground, for debugging
|
|
50
9
|
```
|
|
51
10
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
11
|
+
| File | Role |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `daemon.mjs` | HTTP server, session cookie + CSRF guard, `/api` routes, static `dist/` |
|
|
14
|
+
| `opensol.mjs` | CLI: start, stop, status, logs, open |
|
|
15
|
+
| `lib/store.mjs` | data directory (`~/.opensol`, or `OPENSOL_DATA`), state, AES-256-GCM secrets |
|
|
16
|
+
| `lib/tools.mjs` | file, grep, git and shell tools, workspace path guard, checkpoints |
|
|
17
|
+
| `lib/shell.mjs` | picks bash/zsh/PowerShell/cmd per OS, kills process trees, Plan-mode allowlist |
|
|
18
|
+
| `lib/providers.mjs`, `lib/mcp*.mjs`, `lib/plugins.mjs`, `lib/skills.mjs`, `lib/deploy.mjs` | providers, MCP (+OAuth), plugins, skills, deploys |
|
|
57
19
|
|
|
58
|
-
|
|
20
|
+
Shell selection: Linux → bash; macOS → your `$SHELL` (zsh/bash) as a login shell; Windows → `pwsh`, else Windows PowerShell, else `cmd`. Set `OPENSOL_SHELL` to force one. Commands run in the workspace root with `OPENSOL=1` and `OPENSOL_WORKSPACE` set.
|
|
59
21
|
|
|
60
|
-
|
|
61
|
-
- `secrets.enc` and `master.key` are created with mode `600`.
|
|
62
|
-
- All API routes require the local session cookie and a CSRF header; Host and Origin are checked.
|
|
63
|
-
- The daemon rejects code execution outside the selected workspace except for an explicit, existing deploy folder.
|
|
64
|
-
- No telemetry or OpenSoftX-hosted proxy. Provider requests go directly from the daemon to the provider you configured.
|
|
65
|
-
|
|
66
|
-
## Stack
|
|
67
|
-
|
|
68
|
-
TypeScript, React 19, Vite, Tailwind CSS, Zustand, direct provider adapters (OpenAI, Anthropic, Gemini, OpenCode and OpenAI-compatible endpoints), and a Node HTTP daemon with no runtime dependencies.
|
|
69
|
-
|
|
70
|
-
```
|
|
71
|
-
src/ React app, stores, agent loop, adapters and tools
|
|
72
|
-
server/ local daemon, encrypted store, workspace tools and deploy
|
|
73
|
-
dist/ verified production build
|
|
74
|
-
public/ favicon
|
|
75
|
-
```
|
|
22
|
+
See the root `README.md` for the full feature list and `CLAUDE.md` for architecture notes.
|
package/server/daemon.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describeShell } from './lib/shell.mjs'
|
|
1
2
|
import { randomBytes } from 'node:crypto'
|
|
2
3
|
import { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises'
|
|
3
4
|
import http from 'node:http'
|
|
@@ -92,7 +93,7 @@ import { mcp, sanitizeServerConfig } from './lib/mcp.mjs'
|
|
|
92
93
|
|
|
93
94
|
const HERE = path.dirname(fileURLToPath(import.meta.url))
|
|
94
95
|
const DIST = path.resolve(HERE, '..', 'dist')
|
|
95
|
-
const VERSION = '3.
|
|
96
|
+
const VERSION = '3.2.0'
|
|
96
97
|
const SESSION_COOKIE = 'maxcode_session'
|
|
97
98
|
const CSRF_HEADER = 'x-maxcode-csrf'
|
|
98
99
|
const SESSION_TTL = 14 * 24 * 60 * 60 * 1000
|
|
@@ -236,6 +237,7 @@ function sanitizeSettings(patch, current = {}) {
|
|
|
236
237
|
if (typeof source.theme === 'string' && /^[a-z0-9-]{1,64}$/.test(source.theme)) next.theme = source.theme
|
|
237
238
|
if (source.mode === 'build' || source.mode === 'plan') next.mode = source.mode
|
|
238
239
|
if (typeof source.aiSessionTitles === 'boolean') next.aiSessionTitles = source.aiSessionTitles
|
|
240
|
+
if (typeof source.showMascot === 'boolean') next.showMascot = source.showMascot
|
|
239
241
|
if (source.workspacePrompts && typeof source.workspacePrompts === 'object' && !Array.isArray(source.workspacePrompts)) {
|
|
240
242
|
const prompts = {}
|
|
241
243
|
for (const [root, prompt] of Object.entries(source.workspacePrompts).slice(0, 300)) {
|
|
@@ -331,6 +333,9 @@ async function buildStatePayload() {
|
|
|
331
333
|
dataDir: DATA_DIR,
|
|
332
334
|
home: os.homedir(),
|
|
333
335
|
platform: `${os.platform()} ${os.release()}`,
|
|
336
|
+
os: process.platform,
|
|
337
|
+
arch: os.arch(),
|
|
338
|
+
shell: describeShell(),
|
|
334
339
|
node: process.version,
|
|
335
340
|
settings: state.settings ?? {},
|
|
336
341
|
chats: state.chats ?? { sessions: [], activeId: null },
|
package/server/lib/deploy.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process'
|
|
2
|
+
import { currentShell, killTree } from './shell.mjs'
|
|
2
3
|
import { readFile, readdir, realpath, stat } from 'node:fs/promises'
|
|
3
4
|
import os from 'node:os'
|
|
4
5
|
import path from 'node:path'
|
|
@@ -34,7 +35,8 @@ async function commandExists(bin) {
|
|
|
34
35
|
const direct = await runCapture(bin, ['--version'], { timeoutMs: 15_000, shell: IS_WINDOWS })
|
|
35
36
|
if (direct.code === 0) return { installed: true, version: direct.stdout.split('\n')[0]?.trim() ?? '' }
|
|
36
37
|
if (IS_WINDOWS) return { installed: false, version: '' }
|
|
37
|
-
const
|
|
38
|
+
const shell = currentShell()
|
|
39
|
+
const result = await runCapture(shell.file, shell.args(`command -v ${bin} >/dev/null 2>&1 && ${bin} --version`), {
|
|
38
40
|
timeoutMs: 20_000,
|
|
39
41
|
})
|
|
40
42
|
return { installed: result.code === 0, version: result.stdout.split('\n')[0]?.trim() ?? '' }
|
|
@@ -61,14 +63,7 @@ function runCapture(command, args, { cwd, env, timeoutMs = 120_000, shell = fals
|
|
|
61
63
|
detached: process.platform !== 'win32',
|
|
62
64
|
shell,
|
|
63
65
|
})
|
|
64
|
-
const timer = setTimeout(() =>
|
|
65
|
-
try {
|
|
66
|
-
if (process.platform !== 'win32' && child.pid) process.kill(-child.pid, 'SIGKILL')
|
|
67
|
-
else child.kill('SIGKILL')
|
|
68
|
-
} catch {
|
|
69
|
-
child.kill('SIGKILL')
|
|
70
|
-
}
|
|
71
|
-
}, timeoutMs)
|
|
66
|
+
const timer = setTimeout(() => killTree(child), timeoutMs)
|
|
72
67
|
child.stdout.on('data', (chunk) => {
|
|
73
68
|
if (stdout.length < 300_000) stdout += chunk.toString('utf8')
|
|
74
69
|
})
|
|
@@ -306,7 +301,7 @@ export async function detectProject(raw) {
|
|
|
306
301
|
async function runBuild(cwd, command, onLog, signal) {
|
|
307
302
|
const result = IS_WINDOWS
|
|
308
303
|
? await runStreaming(command, [], { cwd, onLog, signal, env: { MAXCODE_DEPLOY: '1' } })
|
|
309
|
-
: await runStreaming(
|
|
304
|
+
: await runStreaming(currentShell().file, currentShell().args(command), { cwd, onLog, signal, env: { MAXCODE_DEPLOY: '1' } })
|
|
310
305
|
if (result.code !== 0) {
|
|
311
306
|
throw new HttpError(500, `"${command}" failed with exit code ${result.code}. Fix the error in the log, then deploy again.`, 'build_failed')
|
|
312
307
|
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// Cross-platform shell resolution for the `shell` tool and deploy builds.
|
|
2
|
+
// Pure helpers (resolveShell, planShellAllowed, describeShell) take injected platform/env so
|
|
3
|
+
// Windows and macOS behaviour is unit-tested from any OS.
|
|
4
|
+
import { existsSync } from 'node:fs'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import os from 'node:os'
|
|
7
|
+
import { spawn } from 'node:child_process'
|
|
8
|
+
|
|
9
|
+
const PS_PROLOGUE = '[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;$ErrorActionPreference="Continue";'
|
|
10
|
+
// Keeps the exit code of the last native command; PowerShell otherwise reports 0.
|
|
11
|
+
const PS_EPILOGUE = ';if($LASTEXITCODE -is [int] -and $LASTEXITCODE -ne 0){exit $LASTEXITCODE}elseif(-not $?){exit 1}'
|
|
12
|
+
|
|
13
|
+
function platformName(platform) {
|
|
14
|
+
return platform === 'win32' ? 'Windows' : platform === 'darwin' ? 'macOS' : 'Linux'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function posixShell(id, file) {
|
|
18
|
+
return { id, file, family: 'posix', label: id, args: (command) => ['-lc', command] }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function powershell(id, file) {
|
|
22
|
+
return {
|
|
23
|
+
id,
|
|
24
|
+
file,
|
|
25
|
+
family: 'powershell',
|
|
26
|
+
label: id === 'pwsh' ? 'PowerShell 7' : 'PowerShell',
|
|
27
|
+
args: (command) => [
|
|
28
|
+
'-NoLogo',
|
|
29
|
+
'-NoProfile',
|
|
30
|
+
'-NonInteractive',
|
|
31
|
+
'-ExecutionPolicy',
|
|
32
|
+
'Bypass',
|
|
33
|
+
'-Command',
|
|
34
|
+
`${PS_PROLOGUE}${command}${PS_EPILOGUE}`,
|
|
35
|
+
],
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function cmdShell(file) {
|
|
40
|
+
return { id: 'cmd', file, family: 'cmd', label: 'cmd', args: (command) => ['/d', '/s', '/c', command] }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function findOnPath(name, env, platform, exists) {
|
|
44
|
+
const sep = platform === 'win32' ? ';' : ':'
|
|
45
|
+
const exts = platform === 'win32' ? ['.exe', '.cmd', '.bat'] : ['']
|
|
46
|
+
const join = platform === 'win32' ? path.win32.join : path.posix.join
|
|
47
|
+
for (const dir of String(env.PATH ?? env.Path ?? '').split(sep).filter(Boolean)) {
|
|
48
|
+
for (const ext of exts) {
|
|
49
|
+
const candidate = join(dir, `${name}${ext}`)
|
|
50
|
+
if (exists(candidate)) return candidate
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return ''
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Picks the shell used to run agent commands. OPENSOL_SHELL (path or name) overrides detection. */
|
|
57
|
+
export function resolveShell({ platform = process.platform, env = process.env, exists = existsSync } = {}) {
|
|
58
|
+
const override = String(env.OPENSOL_SHELL ?? '').trim()
|
|
59
|
+
if (override) {
|
|
60
|
+
const base = path.basename(override.replace(/\\/g, '/')).toLowerCase().replace(/\.exe$/, '')
|
|
61
|
+
if (base === 'pwsh' || base === 'powershell') return powershell(base, override)
|
|
62
|
+
if (base === 'cmd') return cmdShell(override)
|
|
63
|
+
return posixShell(base || 'sh', override)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (platform === 'win32') {
|
|
67
|
+
const pwsh = findOnPath('pwsh', env, platform, exists)
|
|
68
|
+
if (pwsh) return powershell('pwsh', pwsh)
|
|
69
|
+
const root = env.SystemRoot ?? env.SYSTEMROOT ?? 'C:\\Windows'
|
|
70
|
+
const legacy = path.win32.join(root, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')
|
|
71
|
+
if (exists(legacy)) return powershell('powershell', legacy)
|
|
72
|
+
const onPath = findOnPath('powershell', env, platform, exists)
|
|
73
|
+
if (onPath) return powershell('powershell', onPath)
|
|
74
|
+
return cmdShell(env.ComSpec ?? env.COMSPEC ?? 'cmd.exe')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (platform === 'darwin') {
|
|
78
|
+
const user = String(env.SHELL ?? '')
|
|
79
|
+
const base = path.posix.basename(user)
|
|
80
|
+
if ((base === 'zsh' || base === 'bash') && user && exists(user)) return posixShell(base, user)
|
|
81
|
+
for (const [id, file] of [['zsh', '/bin/zsh'], ['bash', '/bin/bash'], ['sh', '/bin/sh']]) {
|
|
82
|
+
if (exists(file)) return posixShell(id, file)
|
|
83
|
+
}
|
|
84
|
+
return posixShell('sh', '/bin/sh')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const [id, file] of [['bash', '/bin/bash'], ['bash', '/usr/bin/bash'], ['bash', '/usr/local/bin/bash']]) {
|
|
88
|
+
if (exists(file)) return posixShell(id, file)
|
|
89
|
+
}
|
|
90
|
+
const bash = findOnPath('bash', env, platform, exists)
|
|
91
|
+
if (bash) return posixShell('bash', bash)
|
|
92
|
+
return posixShell('sh', exists('/bin/sh') ? '/bin/sh' : 'sh')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let cached
|
|
96
|
+
export function currentShell() {
|
|
97
|
+
cached ??= resolveShell()
|
|
98
|
+
return cached
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function describeShell(shell = currentShell(), { platform = process.platform, arch = os.arch() } = {}) {
|
|
102
|
+
const system = platformName(platform)
|
|
103
|
+
return `${shell.label} on ${system}${platform === 'darwin' ? ` (${arch})` : ''}`
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Kills a spawned command and everything it started. */
|
|
107
|
+
export function killTree(child) {
|
|
108
|
+
if (!child?.pid) return
|
|
109
|
+
try {
|
|
110
|
+
if (process.platform === 'win32') {
|
|
111
|
+
const killer = spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true })
|
|
112
|
+
killer.on('error', () => child.kill('SIGKILL'))
|
|
113
|
+
} else {
|
|
114
|
+
process.kill(-child.pid, 'SIGKILL')
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
try {
|
|
118
|
+
child.kill('SIGKILL')
|
|
119
|
+
} catch {
|
|
120
|
+
// Already gone.
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const GIT_READ = new Set(['status', 'diff', 'log', 'show', 'branch', 'remote', 'rev-parse', 'ls-files'])
|
|
126
|
+
const POSIX_READ = new Set([
|
|
127
|
+
'pwd', 'ls', 'cat', 'head', 'tail', 'wc', 'grep', 'rg', 'find', 'tree', 'file', 'stat', 'du', 'df', 'date', 'whoami', 'git',
|
|
128
|
+
])
|
|
129
|
+
const WINDOWS_READ = new Set([
|
|
130
|
+
'dir', 'type', 'cd', 'where', 'where.exe', 'findstr', 'hostname', 'whoami', 'echo', 'git',
|
|
131
|
+
'get-childitem', 'gci', 'ls', 'get-content', 'gc', 'cat', 'select-string', 'sls', 'get-location', 'pwd',
|
|
132
|
+
'get-item', 'gi', 'test-path', 'get-date', 'get-command', 'measure-object',
|
|
133
|
+
])
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Plan mode runs only read-only commands. Chaining, redirection and substitution are refused
|
|
137
|
+
* outright so an allowlisted head cannot smuggle a second command.
|
|
138
|
+
*/
|
|
139
|
+
export function planShellAllowed(command, shell = currentShell()) {
|
|
140
|
+
if (/[;&|><`$(){}[\]\n\r%^]/.test(command)) return false
|
|
141
|
+
const tokens = command.trim().split(/\s+/).filter(Boolean)
|
|
142
|
+
if (tokens.length === 0) return false
|
|
143
|
+
const windows = shell.family !== 'posix'
|
|
144
|
+
const head = windows ? tokens[0].toLowerCase() : tokens[0]
|
|
145
|
+
if (!(windows ? WINDOWS_READ : POSIX_READ).has(head)) return false
|
|
146
|
+
if (head === 'git' && !GIT_READ.has(tokens[1] ?? '')) return false
|
|
147
|
+
if (head === 'find' && tokens.some((t) => /^-(delete|exec|execdir|ok|okdir|fprint\w*|fls)$/.test(t))) return false
|
|
148
|
+
return true
|
|
149
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import test from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { resolveShell, planShellAllowed, describeShell } from './shell.mjs'
|
|
4
|
+
|
|
5
|
+
const has = (...files) => (file) => files.includes(file)
|
|
6
|
+
|
|
7
|
+
test('linux prefers bash with a login shell', () => {
|
|
8
|
+
const shell = resolveShell({ platform: 'linux', env: {}, exists: has('/bin/bash') })
|
|
9
|
+
assert.equal(shell.file, '/bin/bash')
|
|
10
|
+
assert.deepEqual(shell.args('echo hi'), ['-lc', 'echo hi'])
|
|
11
|
+
assert.equal(describeShell(shell, { platform: 'linux' }), 'bash on Linux')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('linux falls back to sh', () => {
|
|
15
|
+
assert.equal(resolveShell({ platform: 'linux', env: {}, exists: () => false }).id, 'sh')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('macOS uses the user zsh, else /bin/zsh', () => {
|
|
19
|
+
const user = resolveShell({ platform: 'darwin', env: { SHELL: '/opt/homebrew/bin/zsh' }, exists: has('/opt/homebrew/bin/zsh') })
|
|
20
|
+
assert.equal(user.file, '/opt/homebrew/bin/zsh')
|
|
21
|
+
const fallback = resolveShell({ platform: 'darwin', env: { SHELL: '/usr/bin/fish' }, exists: has('/bin/zsh', '/bin/bash') })
|
|
22
|
+
assert.equal(fallback.file, '/bin/zsh')
|
|
23
|
+
assert.equal(describeShell(fallback, { platform: 'darwin', arch: 'arm64' }), 'zsh on macOS (arm64)')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('windows prefers pwsh, then Windows PowerShell, then cmd', () => {
|
|
27
|
+
const env = { PATH: 'C:\\Program Files\\PowerShell\\7', SystemRoot: 'C:\\Windows' }
|
|
28
|
+
const pwsh = resolveShell({ platform: 'win32', env, exists: has('C:\\Program Files\\PowerShell\\7\\pwsh.exe') })
|
|
29
|
+
assert.equal(pwsh.id, 'pwsh')
|
|
30
|
+
const legacy = resolveShell({
|
|
31
|
+
platform: 'win32',
|
|
32
|
+
env: { SystemRoot: 'C:\\Windows' },
|
|
33
|
+
exists: has('C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'),
|
|
34
|
+
})
|
|
35
|
+
assert.equal(legacy.id, 'powershell')
|
|
36
|
+
const args = legacy.args('dir')
|
|
37
|
+
assert.ok(args.includes('-NonInteractive') && args.at(-2) === '-Command')
|
|
38
|
+
assert.match(args.at(-1), /dir.*exit \$LASTEXITCODE/)
|
|
39
|
+
const cmd = resolveShell({ platform: 'win32', env: { ComSpec: 'C:\\Windows\\System32\\cmd.exe' }, exists: () => false })
|
|
40
|
+
assert.equal(cmd.id, 'cmd')
|
|
41
|
+
assert.deepEqual(cmd.args('dir'), ['/d', '/s', '/c', 'dir'])
|
|
42
|
+
assert.equal(describeShell(legacy, { platform: 'win32' }), 'PowerShell on Windows')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('OPENSOL_SHELL overrides detection', () => {
|
|
46
|
+
assert.equal(resolveShell({ platform: 'linux', env: { OPENSOL_SHELL: '/usr/bin/zsh' }, exists: () => false }).id, 'zsh')
|
|
47
|
+
assert.equal(resolveShell({ platform: 'linux', env: { OPENSOL_SHELL: 'pwsh' }, exists: () => false }).family, 'powershell')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('plan mode allows only read-only posix commands', () => {
|
|
51
|
+
const sh = resolveShell({ platform: 'linux', env: {}, exists: has('/bin/bash') })
|
|
52
|
+
assert.equal(planShellAllowed('ls -la src', sh), true)
|
|
53
|
+
assert.equal(planShellAllowed('git log', sh), true)
|
|
54
|
+
assert.equal(planShellAllowed('git push', sh), false)
|
|
55
|
+
assert.equal(planShellAllowed('cat a > b', sh), false)
|
|
56
|
+
assert.equal(planShellAllowed('ls; rm -rf x', sh), false)
|
|
57
|
+
assert.equal(planShellAllowed('find . -delete', sh), false)
|
|
58
|
+
assert.equal(planShellAllowed('rm file', sh), false)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('plan mode allows only read-only PowerShell/cmd commands', () => {
|
|
62
|
+
const ps = resolveShell({ platform: 'win32', env: { SystemRoot: 'C:\\Windows' }, exists: () => false })
|
|
63
|
+
assert.equal(ps.family, 'cmd')
|
|
64
|
+
assert.equal(planShellAllowed('dir src', ps), true)
|
|
65
|
+
assert.equal(planShellAllowed('Get-ChildItem src', ps), true)
|
|
66
|
+
assert.equal(planShellAllowed('Get-Content package.json', ps), true)
|
|
67
|
+
assert.equal(planShellAllowed('Remove-Item x', ps), false)
|
|
68
|
+
assert.equal(planShellAllowed('Set-Content a b', ps), false)
|
|
69
|
+
assert.equal(planShellAllowed('Invoke-Expression foo', ps), false)
|
|
70
|
+
assert.equal(planShellAllowed('type a.txt > b.txt', ps), false)
|
|
71
|
+
assert.equal(planShellAllowed('Get-Content a | Out-File b', ps), false)
|
|
72
|
+
})
|