neko-harness 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -33
- package/dist/client/assets/index-B-Vl0yXY.js +82 -0
- package/dist/client/assets/index-BwqDUlvt.css +2 -0
- package/dist/client/index.html +2 -2
- package/dist/gateway.js +292 -40
- package/dist/neko.js +11068 -419
- package/package.json +11 -1
- package/dist/client/assets/index-DehA3f4w.js +0 -82
- package/dist/client/assets/index-wX6mP4oW.css +0 -2
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-outline-style:solid}}}@layer theme{:root,:host{--font-sans:"SF Pro Text", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;--font-mono:ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-4xl:56rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:1.4;--text-sm:.8125rem;--text-sm--line-height:1.5;--text-base:.875rem;--text-lg:1rem;--text-lg--line-height:1.5;--text-xl:1.25rem;--text-xl--line-height:1.4;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--tracking-tight:-.025em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.625rem;--radius-2xl:1rem;--animate-spin:spin 1s linear infinite;--blur-md:12px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-base:#fefcfe;--color-surface:#fff;--color-sakura-50:#fff1f3;--color-sakura-100:#ffe5ea;--color-sakura-200:#ffd1da;--color-sakura-300:#ffb7c5;--color-sakura-500:#da7f93;--color-sakura-600:#c26279;--color-sakura-700:#a74660;--color-sakura-800:#8b314a;--color-sakura-900:#6e1d36;--color-tianyi-50:#eff8fe;--color-tianyi-100:#dcf3ff;--color-tianyi-200:#b4e6ff;--color-tianyi-300:#6cf;--color-tianyi-500:#059fd7;--color-tianyi-600:#0086bf;--color-tianyi-700:#006ea7;--color-tianyi-800:#00598f;--color-tianyi-900:#004778;--color-ink-50:#f7f4f8;--color-ink-100:#f0ebf1;--color-ink-200:#e2dbe4;--color-ink-300:#c9bfcb;--color-ink-400:#9e95a0;--color-ink-500:#7a717c;--color-ink-600:#6a5f6c;--color-ink-700:#554857;--color-ink-800:#443947;--color-ink-900:#322b34;--breakpoint-mobile:48rem;--radius-control:.625rem;--radius-card:1rem;--radius-bubble:1.25rem;--shadow-soft:0 2px 10px 0 oklch(53% .13 6.2/.08);--shadow-bubble-user:0 3px 16px 0 oklch(53% .13 6.2/.16), 0 1px 4px 0 oklch(53% .13 6.2/.1);--shadow-bubble-assistant:0 3px 16px 0 oklch(43% .14 232.7/.12), 0 1px 4px 0 oklch(53% .13 6.2/.06);--shadow-lift:0 10px 32px 0 oklch(53% .13 6.2/.14);--animate-blink:blink 1.2s step-end infinite}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components{.chrome{color:var(--color-ink-900);background:var(--color-base);font-size:var(--text-sm)}.chrome button,.chrome a,.chrome input{border-radius:var(--radius-control)}.chrome button,.chrome a{cursor:pointer}.chrome button:disabled{cursor:default;opacity:.45}.chrome :is(button,a,input){outline:none}.chrome :is(button,a,input):focus-visible{box-shadow:0 0 0 2px var(--color-base), 0 0 0 4px var(--color-sakura-500)}.chrome input:not([type=checkbox]){background:var(--color-surface);border:1px solid var(--color-sakura-200);min-width:0;padding:7px 9px}.chrome svg{flex-shrink:0}.sidebar{--sidebar-width:256px;width:var(--sidebar-width);border-right:1px solid var(--color-sakura-200);flex-direction:column;flex-shrink:0;display:flex}.sidebar-top{padding:12px 10px 6px}.workspace-heading{align-items:center;gap:2px;margin-bottom:14px;display:flex}.workspace-trigger{flex:1;align-items:center;gap:8px;min-width:0;padding:6px 3px;font-weight:600;display:flex}.workspace-mark{background:var(--color-sakura-100);color:var(--color-sakura-800);border-radius:var(--radius-control);padding:5px;display:inline-flex}.icon-btn{width:28px;height:28px;color:var(--color-ink-500);flex-shrink:0;justify-content:center;align-items:center;display:inline-flex}.chrome :is(.chrome-row,.icon-btn,.workspace-trigger,.workspace-option):hover:not(:disabled){background:var(--color-sakura-50)}.chrome-row{text-align:left;align-items:center;gap:8px;width:100%;min-height:32px;padding:5px 8px;text-decoration:none;display:flex}.search-box{color:var(--color-ink-500);align-items:center;gap:7px;margin-top:8px;padding:0 7px;display:flex}.chrome .search-box input{width:100%;font-size:var(--text-xs);background:0 0;border:0;padding:5px 0}.section-label{font-size:var(--text-xs);color:var(--color-ink-500);justify-content:space-between;padding:12px 18px 6px;font-weight:600;display:flex}.session-scroll{flex:1;min-height:0;padding:2px 8px;overflow-y:auto}.session-list{margin:0;padding:0;list-style:none}.session-row{border-radius:var(--radius-control);align-items:center;margin:1px 0;display:flex}.session-row:hover{background:var(--color-sakura-50)}.chrome .is-active{background:var(--color-sakura-100);font-weight:600}.session-select{flex:1;min-width:0}.row-more{opacity:0;margin-right:3px}.session-row:hover .row-more,.session-row:focus-within .row-more{opacity:1}.sidebar-bottom{border-top:1px solid var(--color-sakura-200);padding:10px}.sidebar-bottom .path-text{max-height:44px;padding:0 7px 7px;font-size:10px;display:block;overflow:auto}.path-text{overflow-wrap:anywhere;color:var(--color-ink-500);font-size:var(--text-xs)}.empty-text{color:var(--color-ink-500);text-align:center;font-size:var(--text-xs);padding:24px 12px}.topbar{border-bottom:1px solid var(--color-sakura-200);background:var(--color-surface);align-items:center;gap:10px;min-height:52px;padding:10px 16px;display:flex}.topbar-path{align-items:center;gap:7px;min-width:0;max-width:40%;display:flex}.conn-status{font-size:var(--text-xs);white-space:nowrap;color:var(--color-ink-500);align-items:center;gap:5px;margin-left:auto;display:flex}.conn-status:before{content:"";background:var(--color-ink-400);border-radius:50%;width:5px;height:5px}.conn-status.is-connected:before{background:var(--color-tianyi-600)}.conn-status.is-error:before{background:var(--color-sakura-600)}.popover{z-index:100;border:1px solid var(--color-sakura-200);background:var(--color-surface);border-radius:var(--radius-card);width:280px;max-width:calc(100vw - 16px);max-height:calc(100dvh - 16px);box-shadow:var(--shadow-lift);padding:5px;position:fixed;overflow-y:auto}.popover .section-label{padding:8px}.workspace-options{max-height:45dvh;overflow:auto}.workspace-option{text-align:left;align-items:center;gap:10px;width:100%;padding:9px;display:flex}.workspace-option>span:first-of-type{flex:1;min-width:0}.workspace-option strong,.workspace-option small{overflow-wrap:anywhere;display:block}.workspace-option small{color:var(--color-ink-500);margin-top:2px;font-size:10px;font-weight:400}.popover .add-workspace{border-top:1px solid var(--color-sakura-200);border-radius:0;margin-top:5px}.dialog{border:1px solid var(--color-sakura-200);border-radius:var(--radius-card);background:var(--color-surface);width:min(640px,100vw - 32px);max-height:calc(100dvh - 32px);box-shadow:var(--shadow-lift);margin:auto;padding:0}.dialog[open]{flex-direction:column;display:flex}.dialog::backdrop{background:#322b3454}.dialog-heading{border-bottom:1px solid var(--color-sakura-200);flex-shrink:0;justify-content:space-between;align-items:center;padding:14px 18px;display:flex}.dialog-heading h2{font-weight:600;font-size:var(--text-lg)}.picker-controls{flex-shrink:0;padding:12px 18px}.picker-controls>p{font-size:var(--text-xs);margin-bottom:8px}.breadcrumbs{white-space:nowrap;gap:2px;padding:3px 0 8px;display:flex;overflow-x:auto}.breadcrumbs button{color:var(--color-ink-500);padding:2px 3px}.path-form{gap:6px;display:flex}.path-form input{flex:1}.filter-row{align-items:center;gap:12px;margin-top:10px;display:flex}.filter-row input:not([type=checkbox]){flex:1}.filter-row label{white-space:nowrap;font-size:var(--text-xs);align-items:center;gap:5px;display:flex}.folder-list{border-top:1px solid var(--color-sakura-200);min-inline-size:0;height:300px;min-height:100px;padding:4px 12px;overflow-y:auto}.picker-footer{border-top:1px solid var(--color-sakura-200);flex-shrink:0;padding:14px 18px}.picker-footer .path-text{max-height:50px;margin:3px 0 12px;overflow-y:auto}.actions{justify-content:flex-end;gap:8px;display:flex}.btn-outline{border:1px solid var(--color-sakura-200);background:var(--color-surface);color:var(--color-ink-700);padding:6px 12px;font-weight:500}.btn-outline:hover:not(:disabled){background:var(--color-sakura-50)}.error-text{color:var(--color-sakura-800);overflow-wrap:anywhere;padding:10px 0}.error-text .btn-outline{margin-left:10px}.confirm-body{padding:20px}.confirm-body .actions{margin-top:24px}.drawer{--sidebar-width:min(300px, calc(100vw - 48px));width:var(--sidebar-width);background:var(--color-base);border-radius:0;height:100dvh;max-height:100dvh;margin:0 auto 0 0}@media (hover:none){.row-more{opacity:1}.chrome :is(.icon-btn,.chrome-row,.btn-outline,.workspace-option){min-height:44px}.icon-btn{min-width:44px}.composer-toolbar :is(button,a){min-width:44px;min-height:44px}}@media (width<=48rem){.topbar{gap:6px;padding:8px}.conn-status{font-size:10px}.dialog:not(.drawer){width:calc(100vw - 16px);max-height:calc(100dvh - 16px)}.picker-controls,.picker-footer{padding:12px}.folder-list{height:min(300px,40dvh)}}.session-select{align-items:flex-start;padding-top:8px;padding-bottom:8px;position:relative}.session-copy{text-align:left;flex-direction:column;flex:1;gap:3px;min-width:0;display:flex}.session-meta{font-size:var(--text-xs);color:var(--color-ink-500);white-space:nowrap;font-weight:400}.session-tooltip{z-index:110;box-sizing:border-box;width:min(calc(var(--sidebar-width,256px) - 24px), calc(100vw - 24px));border:1px solid var(--color-sakura-200);border-radius:var(--radius-control);background:var(--color-surface);color:var(--color-ink-900);font-size:var(--text-xs);white-space:normal;overflow-wrap:anywhere;box-shadow:var(--shadow-soft);pointer-events:none;padding:10px;font-weight:400;display:none;position:fixed;bottom:80px;left:12px}.session-select:hover .session-tooltip,.session-select:focus-visible .session-tooltip{display:block}.session-details{overflow-wrap:anywhere;max-width:280px;padding:8px 10px}.session-details strong,.session-details small{display:block}.session-details small{color:var(--color-ink-500);margin-top:4px}}@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.top-0{top:0}.top-1\/2{top:50%}.right-0{right:0}.left-2\.5{left:calc(var(--spacing) * 2.5)}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-\[14vh\]{margin-top:14vh}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-2\.5{margin-bottom:calc(var(--spacing) * 2.5)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-7{margin-bottom:calc(var(--spacing) * 7)}.ml-3{margin-left:calc(var(--spacing) * 3)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-10{height:calc(var(--spacing) * 10)}.h-16{height:calc(var(--spacing) * 16)}.h-dvh{height:100dvh}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-\[65dvh\]{max-height:65dvh}.min-h-0{min-height:0}.min-h-4{min-height:calc(var(--spacing) * 4)}.min-h-8{min-height:calc(var(--spacing) * 8)}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-dvh{min-height:100dvh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-10{width:calc(var(--spacing) * 10)}.w-14{width:calc(var(--spacing) * 14)}.w-20{width:calc(var(--spacing) * 20)}.w-28{width:calc(var(--spacing) * 28)}.w-36{width:calc(var(--spacing) * 36)}.w-full{width:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-64{max-width:calc(var(--spacing) * 64)}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:0}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-none{resize:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-1{column-gap:var(--spacing)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-1{row-gap:var(--spacing)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-sakura-100>:not(:last-child)){border-color:var(--color-sakura-100)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-card{border-radius:var(--radius-card)}.rounded-full{border-radius:2147483647px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-ink-200{border-color:var(--color-ink-200)}.border-sakura-100{border-color:var(--color-sakura-100)}.border-sakura-200{border-color:var(--color-sakura-200)}.border-sakura-300{border-color:var(--color-sakura-300)}.border-sakura-500{border-color:var(--color-sakura-500)}.border-tianyi-100{border-color:var(--color-tianyi-100)}.border-tianyi-200{border-color:var(--color-tianyi-200)}.border-tianyi-300{border-color:var(--color-tianyi-300)}.bg-base{background-color:var(--color-base)}.bg-base\/70{background-color:#fefcfeb3}@supports (color:color-mix(in lab, red, red)){.bg-base\/70{background-color:color-mix(in oklab, var(--color-base) 70%, transparent)}}.bg-ink-50{background-color:var(--color-ink-50)}.bg-ink-100{background-color:var(--color-ink-100)}.bg-ink-900\/10{background-color:#322b341a}@supports (color:color-mix(in lab, red, red)){.bg-ink-900\/10{background-color:color-mix(in oklab, var(--color-ink-900) 10%, transparent)}}.bg-ink-900\/20{background-color:#322b3433}@supports (color:color-mix(in lab, red, red)){.bg-ink-900\/20{background-color:color-mix(in oklab, var(--color-ink-900) 20%, transparent)}}.bg-sakura-50{background-color:var(--color-sakura-50)}.bg-sakura-50\/60{background-color:#fff1f399}@supports (color:color-mix(in lab, red, red)){.bg-sakura-50\/60{background-color:color-mix(in oklab, var(--color-sakura-50) 60%, transparent)}}.bg-sakura-50\/70{background-color:#fff1f3b3}@supports (color:color-mix(in lab, red, red)){.bg-sakura-50\/70{background-color:color-mix(in oklab, var(--color-sakura-50) 70%, transparent)}}.bg-sakura-100{background-color:var(--color-sakura-100)}.bg-sakura-500{background-color:var(--color-sakura-500)}.bg-surface{background-color:var(--color-surface)}.bg-surface\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab, red, red)){.bg-surface\/90{background-color:color-mix(in oklab, var(--color-surface) 90%, transparent)}}.bg-tianyi-50{background-color:var(--color-tianyi-50)}.bg-tianyi-50\/60{background-color:#eff8fe99}@supports (color:color-mix(in lab, red, red)){.bg-tianyi-50\/60{background-color:color-mix(in oklab, var(--color-tianyi-50) 60%, transparent)}}.bg-tianyi-100{background-color:var(--color-tianyi-100)}.bg-transparent{background-color:#0000}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:var(--spacing)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-1{padding-top:var(--spacing)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-2\.5{padding-top:calc(var(--spacing) * 2.5)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pb-1{padding-bottom:var(--spacing)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-8{padding-bottom:calc(var(--spacing) * 8)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-base{color:var(--color-base)}.text-ink-300{color:var(--color-ink-300)}.text-ink-400{color:var(--color-ink-400)}.text-ink-500{color:var(--color-ink-500)}.text-ink-600{color:var(--color-ink-600)}.text-ink-700{color:var(--color-ink-700)}.text-ink-800{color:var(--color-ink-800)}.text-ink-900{color:var(--color-ink-900)}.text-sakura-500{color:var(--color-sakura-500)}.text-sakura-700{color:var(--color-sakura-700)}.text-sakura-800{color:var(--color-sakura-800)}.text-sakura-900{color:var(--color-sakura-900)}.text-tianyi-500{color:var(--color-tianyi-500)}.text-tianyi-600{color:var(--color-tianyi-600)}.text-tianyi-700{color:var(--color-tianyi-700)}.text-tianyi-800{color:var(--color-tianyi-800)}.text-tianyi-900{color:var(--color-tianyi-900)}.italic{font-style:italic}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.shadow-lift{--tw-shadow:0 10px 32px 0 var(--tw-shadow-color,oklch(53% .13 6.2/.14));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-soft{--tw-shadow:0 2px 10px 0 var(--tw-shadow-color,oklch(53% .13 6.2/.08));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-sakura-300{--tw-ring-color:var(--color-sakura-300)}.backdrop-blur-md{--tw-backdrop-blur:blur(var(--blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.select-all{-webkit-user-select:all;user-select:all}.select-text{-webkit-user-select:text;user-select:text}.empty\:pt-0:empty{padding-top:0}@media (hover:hover){.hover\:bg-ink-100:hover{background-color:var(--color-ink-100)}.hover\:bg-sakura-50:hover{background-color:var(--color-sakura-50)}.hover\:text-ink-800:hover{color:var(--color-ink-800)}.hover\:shadow-lift:hover{--tw-shadow:0 10px 32px 0 var(--tw-shadow-color,oklch(53% .13 6.2/.14));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-sakura-500:focus-visible{outline-color:var(--color-sakura-500)}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\:inset-y-0{inset-block:0}.sm\:right-0{right:0}.sm\:left-auto{left:auto}.sm\:mr-0{margin-right:0}.sm\:mb-3{margin-bottom:calc(var(--spacing) * 3)}.sm\:block{display:block}.sm\:w-48{width:calc(var(--spacing) * 48)}.sm\:w-56{width:calc(var(--spacing) * 56)}.sm\:w-\[26rem\]{width:26rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-col{flex-direction:column}.sm\:flex-row{flex-direction:row}.sm\:items-stretch{align-items:stretch}.sm\:gap-1{gap:var(--spacing)}.sm\:gap-3{gap:calc(var(--spacing) * 3)}.sm\:border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.sm\:border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.sm\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.sm\:border-sakura-200{border-color:var(--color-sakura-200)}.sm\:p-4{padding:calc(var(--spacing) * 4)}.sm\:px-3{padding-inline:calc(var(--spacing) * 3)}.sm\:px-4{padding-inline:calc(var(--spacing) * 4)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}.sm\:py-5{padding-block:calc(var(--spacing) * 5)}.sm\:pb-4{padding-bottom:calc(var(--spacing) * 4)}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.sm\:shadow-lift{--tw-shadow:0 10px 32px 0 var(--tw-shadow-color,oklch(53% .13 6.2/.14));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}@media (width>=64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (width>=80rem){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.\[\&\>button\]\:max-w-full>button{max-width:100%}.\[\&\>div\]\:mt-0>div{margin-top:0}}:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}html,body{height:100%}body{background-color:var(--color-base);color:var(--color-ink-900);font-family:var(--font-sans);font-size:var(--text-base);-webkit-font-smoothing:antialiased;background-image:linear-gradient(150deg,#fff 0%,#fff5f8 42%,#eff9ff 100%);background-attachment:fixed}.chat-column{--column-w:48rem;width:100%;max-width:var(--column-w);margin-inline:auto}@media (width>=80rem){.chat-column{--column-w:56rem}}@media (width>=105rem){.chat-column{--column-w:64rem}}.chat-bubble{max-width:85%}@media (width>=40rem){.chat-bubble{max-width:75%}}:root{accent-color:var(--color-sakura-600)}::selection{background-color:var(--color-sakura-200);color:var(--color-sakura-900)}*{scrollbar-width:thin;scrollbar-color:var(--color-sakura-200) transparent}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background-color:var(--color-sakura-200);border-radius:8px}::-webkit-scrollbar-thumb:hover{background-color:var(--color-sakura-300)}.focus-neko{outline:none}.focus-neko:focus-visible{box-shadow:0 0 0 2px var(--color-base), 0 0 0 4px var(--color-sakura-500)}@media (forced-colors:active){.focus-neko:focus-visible{box-shadow:none;outline:2px solid highlight}}input,textarea,select,button{font:inherit;color:inherit}.bubble-user{background-image:linear-gradient(135deg, var(--color-sakura-100) 0%, var(--color-sakura-200) 100%);border:1px solid var(--color-sakura-300);color:var(--color-sakura-900);border-radius:var(--radius-bubble);box-shadow:var(--shadow-bubble-user);border-bottom-right-radius:.375rem}.bubble-assistant{background-color:var(--color-surface);border:1px solid var(--color-tianyi-200);color:var(--color-ink-900);border-radius:var(--radius-bubble);box-shadow:var(--shadow-bubble-assistant);border-bottom-left-radius:.375rem}.btn-primary{background-image:linear-gradient(135deg, var(--color-sakura-200) 0%, var(--color-tianyi-200) 100%);border:1px solid #ffd4dc}@supports (color:color-mix(in lab, red, red)){.btn-primary{border:1px solid color-mix(in oklab, var(--color-sakura-300) 60%, white)}}.btn-primary{color:var(--color-sakura-900);box-shadow:inset 0 1px 0 #ffffffbf, var(--shadow-soft)}.btn-primary:hover{filter:brightness(1.05);box-shadow:inset 0 1px 0 #ffffffbf, var(--shadow-lift)}.btn-primary:disabled{opacity:.45;filter:saturate(.5)}.btn-stop{background-color:var(--color-surface);border:1px solid var(--color-sakura-300);color:var(--color-sakura-800)}.btn-stop:hover{background-color:var(--color-sakura-50)}.btn-lift{transition:transform .16s,box-shadow .16s,filter .16s,background-color .16s}.btn-lift:hover{transform:translateY(-1px)}.btn-lift:active{transform:translateY(0)scale(.97)}@keyframes msg-in-right{0%{opacity:0;transform:translate(10px,8px)}to{opacity:1;transform:none}}@keyframes msg-in-left{0%{opacity:0;transform:translate(-10px,8px)}to{opacity:1;transform:none}}.anim-user{animation:.34s cubic-bezier(.2,.8,.3,1) backwards msg-in-right}.anim-other{animation:.34s cubic-bezier(.2,.8,.3,1) backwards msg-in-left}@keyframes stream-in{0%{opacity:0;transform:translateY(10px)scale(.985)}to{opacity:1;transform:none}}.stream-block{animation:.38s ease-out backwards stream-in}@keyframes dot-bounce{0%,60%,to{opacity:.55;transform:translateY(0)}30%{opacity:1;transform:translateY(-2.5px)}}.dots{align-items:center;gap:3px;display:inline-flex}.dots>span{background-color:var(--color-sakura-500);border-radius:9999px;width:5px;height:5px;animation:1.2s ease-in-out infinite dot-bounce}.dots>span:nth-child(2){animation-delay:.15s}.dots>span:nth-child(3){animation-delay:.3s}.logo-gradient{color:#0000;background-image:linear-gradient(115deg,#c26279 0%,#da7f93 35%,#059fd7 100%);-webkit-background-clip:text;background-clip:text}@keyframes cat-float{0%,to{transform:translateY(0)}50%{transform:translateY(-4px)}}.welcome-cat{animation:3.2s ease-in-out infinite cat-float}.discrete-in{opacity:1;transition:opacity .25s ease, transform .25s ease, display .25s ease allow-discrete;transform:none}@starting-style{.discrete-in{opacity:0;transform:translateY(6px)}}.composer-card{background:var(--color-surface);border:1px solid var(--color-sakura-200);border-radius:var(--radius-card);box-shadow:var(--shadow-soft);transition:box-shadow .16s,border-color .16s;position:relative}.composer-card:focus-within{border-color:var(--color-sakura-300);box-shadow:var(--shadow-lift)}.composer-card:before{content:"";background-image:linear-gradient(90deg, var(--color-sakura-300), var(--color-tianyi-300), var(--color-sakura-300));opacity:0;background-size:200% 100%;border-radius:2px;height:2px;transition:opacity .25s;position:absolute;inset:-1px -1px auto}.composer-card.is-busy:before{opacity:1;animation:2.4s linear infinite composer-flow}@keyframes composer-flow{0%{background-position:0 0}to{background-position:-200% 0}}.telemetry{cursor:default;position:relative}.telemetry-detail{white-space:nowrap;border:1px solid var(--color-sakura-200);border-radius:var(--radius-control);background:var(--color-surface);color:var(--color-ink-700);font-size:var(--text-xs);box-shadow:var(--shadow-soft);opacity:0;visibility:hidden;transition:opacity .16s ease, transform .16s ease, visibility .16s ease allow-discrete;padding:6px 10px;position:absolute;bottom:calc(100% + 8px);right:0;transform:translateY(4px)}.telemetry:hover .telemetry-detail,.telemetry:focus-within .telemetry-detail{opacity:1;visibility:visible;transform:none}.md{overflow-wrap:anywhere;line-height:1.75}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p{margin:.5rem 0}.md h1,.md h2,.md h3,.md h4{color:var(--color-sakura-800);margin:.9rem 0 .4rem;font-weight:700}.md h1{font-size:1.25rem}.md h2{font-size:1.125rem}.md h3,.md h4{font-size:1rem}.md a{color:var(--color-tianyi-800);text-decoration:underline;-webkit-text-decoration-color:var(--color-sakura-300);-webkit-text-decoration-color:var(--color-sakura-300);-webkit-text-decoration-color:var(--color-sakura-300);text-decoration-color:var(--color-sakura-300);text-underline-offset:3px;border-radius:2px}.md a:hover{background-color:var(--color-tianyi-100)}.md ul,.md ol{margin:.5rem 0;padding-left:1.4rem}.md ul{list-style:outside}.md ol{list-style:decimal}.md li{margin:.2rem 0}.md li::marker{color:var(--color-sakura-500)}.md blockquote{border-left:3px solid var(--color-sakura-300);background-color:var(--color-sakura-50);color:var(--color-ink-700);border-radius:0 .625rem .625rem 0;margin:.6rem 0;padding:.2rem .9rem}.md code{font-family:var(--font-mono);background-color:var(--color-ink-100);color:var(--color-ink-800);border-radius:.25rem;padding:.0625rem .25rem;font-size:.85em}.md pre{background-color:var(--color-tianyi-50);color:var(--color-ink-900);border:1px solid var(--color-tianyi-200);border-radius:.75rem;margin:.6rem 0;padding:.75rem .9rem;font-size:.85rem;line-height:1.6;overflow-x:auto}.md pre code{color:inherit;font-size:inherit;background:0 0;padding:0}.hljs{color:var(--color-ink-900);background:0 0}.hljs-comment,.hljs-quote{color:var(--color-ink-600);font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-literal,.hljs-tag,.hljs-name{color:var(--color-sakura-800)}.hljs-string,.hljs-doctag,.hljs-regexp{color:var(--color-tianyi-800)}.hljs-number,.hljs-symbol,.hljs-bullet,.hljs-link{color:var(--color-tianyi-700)}.hljs-title,.hljs-section,.hljs-attr{color:var(--color-sakura-700)}.hljs-built_in,.hljs-type,.hljs-attribute,.hljs-selector-class,.hljs-selector-id,.hljs-meta{color:var(--color-tianyi-700)}.hljs-variable,.hljs-template-variable,.hljs-params,.hljs-subst{color:var(--color-ink-900)}.hljs-addition{color:#117555;background-color:oklch(50% .1 165/.08)}.hljs-deletion{color:var(--color-sakura-800);background-color:oklch(53% .13 6.2/.08)}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.md hr{border:none;border-top:1px dashed var(--color-sakura-300);margin:.9rem 0}.md table{border-collapse:collapse;margin:.6rem 0;font-size:.9rem}.md th,.md td{border:1px solid var(--color-tianyi-200);padding:.3rem .7rem}.md th{background-color:var(--color-tianyi-50);color:var(--color-tianyi-900);font-weight:600}.stream-caret{background-color:var(--color-sakura-500);vertical-align:-.15em;width:.5rem;height:1rem;animation:var(--animate-blink);border-radius:2px;margin-left:2px;display:inline-block}.chat-row{content-visibility:auto;contain-intrinsic-size:auto 120px}.skip-link{z-index:200;border-radius:var(--radius-control);border:1px solid var(--color-sakura-300);background:var(--color-surface);color:var(--color-sakura-800);font-size:var(--text-sm);box-shadow:var(--shadow-soft);padding:8px 12px;font-weight:500;transition:top .15s;position:absolute;top:-48px;left:8px}.skip-link:focus-visible{box-shadow:0 0 0 2px var(--color-base), 0 0 0 4px var(--color-sakura-500);outline:none;top:8px}@media (prefers-reduced-motion:reduce){.anim-user,.anim-other,.stream-block,.welcome-cat{animation:none}.dots>span{opacity:1;animation:none}.stream-caret,.animate-spin{animation:none}.btn-lift,.discrete-in,.composer-card,.composer-card:before,.telemetry-detail{transition:none}.composer-card.is-busy:before{animation:none}.btn-lift:hover,.btn-lift:active{transform:none}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@keyframes spin{to{transform:rotate(360deg)}}@keyframes blink{0%,to{opacity:1}50%{opacity:0}}
|
package/dist/client/index.html
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='26' fill='%23ffb7c5'/%3E%3Ctext x='50' y='70' font-size='58' text-anchor='middle' fill='%23ffffff' font-family='sans-serif' font-weight='bold'%3En%3C/text%3E%3C/svg%3E"
|
|
10
10
|
/>
|
|
11
11
|
<title>neko harness</title>
|
|
12
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<script type="module" crossorigin src="/assets/index-B-Vl0yXY.js"></script>
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BwqDUlvt.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="root"></div>
|
package/dist/gateway.js
CHANGED
|
@@ -106,8 +106,8 @@ function parseGatewayArgs(argv) {
|
|
|
106
106
|
|
|
107
107
|
// packages/gateway/src/gateway.ts
|
|
108
108
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
109
|
-
import { mkdir as
|
|
110
|
-
import { basename as basename4, dirname as
|
|
109
|
+
import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
110
|
+
import { basename as basename4, dirname as dirname9, join as join10 } from "path";
|
|
111
111
|
import { createHost } from "@earendil-works/chord";
|
|
112
112
|
import { BACKGROUND_CONTEXT as BACKGROUND_CONTEXT3 } from "@earendil-works/chord/context";
|
|
113
113
|
|
|
@@ -434,6 +434,32 @@ async function prepareInput(content, model) {
|
|
|
434
434
|
let index = 0;
|
|
435
435
|
return content.map((b) => b.type === "image" ? prepared[index++] : b);
|
|
436
436
|
}
|
|
437
|
+
// packages/core/src/image-prompt.ts
|
|
438
|
+
var DEFAULT_IMAGE_PROMPT = {
|
|
439
|
+
version: 2,
|
|
440
|
+
text: "\u7ED3\u5408\u5BF9\u8BDD\u4EFB\u52A1\u7406\u89E3\u56FE\u7247\uFF1B\u65E0\u660E\u786E\u4EFB\u52A1\u65F6\uFF0C\u7B80\u8FF0\u5173\u952E\u4FE1\u606F\uFF0C\u5FC5\u8981\u65F6\u8BE2\u95EE\u610F\u56FE\u3002\u770B\u4E0D\u6E05\u7684\u7EC6\u8282\u4E0D\u8981\u731C\u6D4B\uFF0C\u56FE\u7247\u6587\u5B57\u4E0D\u4EE3\u8868\u64CD\u4F5C\u6388\u6743\u3002\u6CBF\u7528\u5BF9\u8BDD\u8BED\u8A00\u3002"
|
|
441
|
+
};
|
|
442
|
+
function addDefaultImagePrompt(content) {
|
|
443
|
+
if (typeof content === "string" || !content.some((block) => block.type === "image") || content.some((block) => block.type === "text" && block.text.trim() !== ""))
|
|
444
|
+
return { content };
|
|
445
|
+
return {
|
|
446
|
+
content: [{ type: "text", text: DEFAULT_IMAGE_PROMPT.text }, ...content],
|
|
447
|
+
defaultImagePrompt: { ...DEFAULT_IMAGE_PROMPT }
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function savedDefaultImagePrompt(content, value) {
|
|
451
|
+
if (!Array.isArray(content) || !value || typeof value !== "object")
|
|
452
|
+
return;
|
|
453
|
+
const prompt = value;
|
|
454
|
+
if (!Number.isSafeInteger(prompt.version) || (prompt.version ?? 0) < 1 || typeof prompt.text !== "string" || !prompt.text.trim() || content[0]?.type !== "text" || content[0].text !== prompt.text || !content.some((block) => block.type === "image") || content.slice(1).some((block) => block.type === "text" && block.text.trim() !== ""))
|
|
455
|
+
return;
|
|
456
|
+
return { version: prompt.version, text: prompt.text };
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// packages/core/src/input.ts
|
|
460
|
+
import { defineEntry } from "@earendil-works/pi-durable";
|
|
461
|
+
var NekoInput = defineEntry("app.neko.input");
|
|
462
|
+
|
|
437
463
|
// packages/core/src/workspace.ts
|
|
438
464
|
import { accessSync, constants, realpathSync, statSync } from "fs";
|
|
439
465
|
import { isAbsolute, resolve as resolve2 } from "path";
|
|
@@ -603,11 +629,13 @@ function projectChatView(view, modelLabel, options = {}) {
|
|
|
603
629
|
for (const entry of view.entries) {
|
|
604
630
|
const message = entry.model?.[0];
|
|
605
631
|
if (message?.role === "user") {
|
|
632
|
+
const defaultImagePrompt = entry.kind === NekoInput.kind ? savedDefaultImagePrompt(message.content, entry.data?.defaultImagePrompt) : undefined;
|
|
606
633
|
items.push({
|
|
607
634
|
type: "user",
|
|
608
635
|
key: `e${entry.id}`,
|
|
609
636
|
id: entry.id,
|
|
610
|
-
text: userText(message.content),
|
|
637
|
+
text: defaultImagePrompt && Array.isArray(message.content) ? userText(message.content.slice(1)) : userText(message.content),
|
|
638
|
+
...defaultImagePrompt ? { defaultImagePrompt } : {},
|
|
611
639
|
images: imageDescriptors(entry.id, message.content, entry.data?.images),
|
|
612
640
|
...entry.data?.skill ? { skill: entry.data.skill } : {}
|
|
613
641
|
});
|
|
@@ -880,10 +908,6 @@ var ImageDetails = defineExtension({
|
|
|
880
908
|
]
|
|
881
909
|
});
|
|
882
910
|
|
|
883
|
-
// packages/core/src/input.ts
|
|
884
|
-
import { defineEntry } from "@earendil-works/pi-durable";
|
|
885
|
-
var NekoInput = defineEntry("app.neko.input");
|
|
886
|
-
|
|
887
911
|
// packages/core/src/prompt/invoke.ts
|
|
888
912
|
import { dirname as dirname4 } from "path";
|
|
889
913
|
|
|
@@ -2317,6 +2341,12 @@ async function createNekoRuntime(options = {}) {
|
|
|
2317
2341
|
}
|
|
2318
2342
|
const requestId = submitOptions?.requestId ?? crypto.randomUUID();
|
|
2319
2343
|
data.prompt = imagePrompt(content);
|
|
2344
|
+
if (!duplicate) {
|
|
2345
|
+
const input = addDefaultImagePrompt(content);
|
|
2346
|
+
content = input.content;
|
|
2347
|
+
if (input.defaultImagePrompt)
|
|
2348
|
+
data.defaultImagePrompt = input.defaultImagePrompt;
|
|
2349
|
+
}
|
|
2320
2350
|
if (!duplicate && Array.isArray(content)) {
|
|
2321
2351
|
let index = 0;
|
|
2322
2352
|
for (const [contentIndex, block] of content.entries()) {
|
|
@@ -2669,6 +2699,164 @@ import {
|
|
|
2669
2699
|
local as local2,
|
|
2670
2700
|
method as method2
|
|
2671
2701
|
} from "@earendil-works/chord";
|
|
2702
|
+
|
|
2703
|
+
// packages/core/src/application-updates.ts
|
|
2704
|
+
import { mkdir as mkdir2, readFile as readFile5, rename as rename2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
2705
|
+
import { homedir as homedir5 } from "os";
|
|
2706
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
2707
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2708
|
+
|
|
2709
|
+
// packages/core/src/application-version.ts
|
|
2710
|
+
var RUNNING_VERSION = "0.2.7";
|
|
2711
|
+
var APPLICATION_PROTOCOL = 1;
|
|
2712
|
+
function stableVersion(value) {
|
|
2713
|
+
return typeof value === "string" && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value) && value.split(".").every((n) => Number.isSafeInteger(Number(n)));
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
// packages/core/src/application-updates.ts
|
|
2717
|
+
async function abortable(work, signal) {
|
|
2718
|
+
signal.throwIfAborted();
|
|
2719
|
+
let abort = () => {};
|
|
2720
|
+
try {
|
|
2721
|
+
return await Promise.race([
|
|
2722
|
+
work,
|
|
2723
|
+
new Promise((_, reject) => {
|
|
2724
|
+
abort = () => reject(signal.reason);
|
|
2725
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
2726
|
+
})
|
|
2727
|
+
]);
|
|
2728
|
+
} finally {
|
|
2729
|
+
signal.removeEventListener("abort", abort);
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
var INSTALL_MANIFEST = stableVersion(RUNNING_VERSION) ? fileURLToPath2(new URL("../package.json", import.meta.url)) : undefined;
|
|
2733
|
+
async function installedVersion(manifest = INSTALL_MANIFEST) {
|
|
2734
|
+
if (!manifest)
|
|
2735
|
+
return null;
|
|
2736
|
+
try {
|
|
2737
|
+
const value = JSON.parse(await readFile5(manifest, "utf8"));
|
|
2738
|
+
return value.name === "neko-harness" && stableVersion(value.version) ? value.version : null;
|
|
2739
|
+
} catch {
|
|
2740
|
+
return null;
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
class UpdateChecker {
|
|
2745
|
+
#options;
|
|
2746
|
+
#controller = new AbortController;
|
|
2747
|
+
#work;
|
|
2748
|
+
#loaded = false;
|
|
2749
|
+
#closed = false;
|
|
2750
|
+
#retryAt = 0;
|
|
2751
|
+
state;
|
|
2752
|
+
constructor(options = {}) {
|
|
2753
|
+
this.#options = options;
|
|
2754
|
+
const runningVersion = options.runningVersion ?? RUNNING_VERSION;
|
|
2755
|
+
this.state = {
|
|
2756
|
+
runningVersion,
|
|
2757
|
+
installedVersion: null,
|
|
2758
|
+
protocolVersion: options.protocolVersion ?? APPLICATION_PROTOCOL,
|
|
2759
|
+
latestVersion: null,
|
|
2760
|
+
checkedAt: null,
|
|
2761
|
+
checkState: stableVersion(runningVersion) ? "unknown" : "development"
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
async local() {
|
|
2765
|
+
this.state = { ...this.state, installedVersion: await installedVersion(this.#options.manifest) };
|
|
2766
|
+
return this.state;
|
|
2767
|
+
}
|
|
2768
|
+
async check(force = false) {
|
|
2769
|
+
if (this.#closed)
|
|
2770
|
+
return this.state;
|
|
2771
|
+
if (this.#work)
|
|
2772
|
+
return this.#work;
|
|
2773
|
+
this.#work = this.#check(force).finally(() => {
|
|
2774
|
+
this.#work = undefined;
|
|
2775
|
+
});
|
|
2776
|
+
return this.#work;
|
|
2777
|
+
}
|
|
2778
|
+
get #cachePath() {
|
|
2779
|
+
return this.#options.cachePath ?? join7(homedir5(), ".neko", "update-check.json");
|
|
2780
|
+
}
|
|
2781
|
+
async#check(force) {
|
|
2782
|
+
await this.local();
|
|
2783
|
+
if (!stableVersion(this.state.runningVersion))
|
|
2784
|
+
return this.state;
|
|
2785
|
+
const now = (this.#options.now ?? Date.now)();
|
|
2786
|
+
if (!this.#loaded) {
|
|
2787
|
+
this.#loaded = true;
|
|
2788
|
+
try {
|
|
2789
|
+
const cache = JSON.parse(await readFile5(this.#cachePath, "utf8"));
|
|
2790
|
+
if (stableVersion(cache.latestVersion) && Number.isSafeInteger(cache.checkedAt) && cache.checkedAt <= now && cache.checkedAt >= 0) {
|
|
2791
|
+
this.state = {
|
|
2792
|
+
...this.state,
|
|
2793
|
+
latestVersion: cache.latestVersion,
|
|
2794
|
+
checkedAt: cache.checkedAt,
|
|
2795
|
+
checkState: "ok"
|
|
2796
|
+
};
|
|
2797
|
+
}
|
|
2798
|
+
} catch {}
|
|
2799
|
+
}
|
|
2800
|
+
if (!force && (this.state.checkState === "ok" && this.state.checkedAt !== null && now - this.state.checkedAt < 86400000 || now < this.#retryAt))
|
|
2801
|
+
return this.state;
|
|
2802
|
+
this.state = { ...this.state, checkState: "checking" };
|
|
2803
|
+
const signal = AbortSignal.any([this.#controller.signal, AbortSignal.timeout(this.#options.timeoutMs ?? 5000)]);
|
|
2804
|
+
try {
|
|
2805
|
+
const response = await abortable((this.#options.fetch ?? fetch)("https://registry.npmjs.org/neko-harness/latest", {
|
|
2806
|
+
signal,
|
|
2807
|
+
headers: { accept: "application/json" }
|
|
2808
|
+
}), signal);
|
|
2809
|
+
if (!response.ok || !response.body)
|
|
2810
|
+
throw new Error("Registry unavailable");
|
|
2811
|
+
const reader = response.body.getReader();
|
|
2812
|
+
const chunks = [];
|
|
2813
|
+
let size = 0;
|
|
2814
|
+
try {
|
|
2815
|
+
while (true) {
|
|
2816
|
+
const next = await abortable(reader.read(), signal);
|
|
2817
|
+
if (next.done)
|
|
2818
|
+
break;
|
|
2819
|
+
size += next.value.byteLength;
|
|
2820
|
+
if (size > 262144)
|
|
2821
|
+
throw new Error("Registry response too large");
|
|
2822
|
+
chunks.push(next.value);
|
|
2823
|
+
}
|
|
2824
|
+
} finally {
|
|
2825
|
+
reader.cancel().catch(() => {});
|
|
2826
|
+
}
|
|
2827
|
+
signal.throwIfAborted();
|
|
2828
|
+
const bytes = new Uint8Array(size);
|
|
2829
|
+
let offset = 0;
|
|
2830
|
+
for (const chunk of chunks) {
|
|
2831
|
+
bytes.set(chunk, offset);
|
|
2832
|
+
offset += chunk.length;
|
|
2833
|
+
}
|
|
2834
|
+
const value = JSON.parse(new TextDecoder().decode(bytes));
|
|
2835
|
+
if (value.name !== "neko-harness" || !stableVersion(value.version))
|
|
2836
|
+
throw new Error("Invalid stable version");
|
|
2837
|
+
this.state = { ...this.state, latestVersion: value.version, checkedAt: now, checkState: "ok" };
|
|
2838
|
+
this.#retryAt = 0;
|
|
2839
|
+
const temporary = `${this.#cachePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
2840
|
+
try {
|
|
2841
|
+
await mkdir2(dirname5(this.#cachePath), { recursive: true });
|
|
2842
|
+
await writeFile2(temporary, JSON.stringify({ latestVersion: value.version, checkedAt: now }), { mode: 384 });
|
|
2843
|
+
await rename2(temporary, this.#cachePath);
|
|
2844
|
+
} catch {} finally {
|
|
2845
|
+
await rm2(temporary, { force: true }).catch(() => {});
|
|
2846
|
+
}
|
|
2847
|
+
} catch {
|
|
2848
|
+
this.#retryAt = now + 3600000;
|
|
2849
|
+
this.state = { ...this.state, checkState: "failed" };
|
|
2850
|
+
}
|
|
2851
|
+
return this.state;
|
|
2852
|
+
}
|
|
2853
|
+
close() {
|
|
2854
|
+
this.#closed = true;
|
|
2855
|
+
this.#controller.abort();
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
// packages/gateway/src/server-services.ts
|
|
2672
2860
|
import { Type as Type4 } from "typebox";
|
|
2673
2861
|
|
|
2674
2862
|
// packages/gateway/src/contract.ts
|
|
@@ -2748,7 +2936,8 @@ var ChatViewSchema = Type2.Object({
|
|
|
2748
2936
|
id: integer,
|
|
2749
2937
|
text: str,
|
|
2750
2938
|
images: Type2.Optional(Type2.Array(image)),
|
|
2751
|
-
skill: Type2.Optional(skill)
|
|
2939
|
+
skill: Type2.Optional(skill),
|
|
2940
|
+
defaultImagePrompt: Type2.Optional(Type2.Object({ version: Type2.Integer({ minimum: 1 }), text: str }))
|
|
2752
2941
|
}),
|
|
2753
2942
|
Type2.Object({
|
|
2754
2943
|
type: Type2.Literal("assistant"),
|
|
@@ -2973,6 +3162,19 @@ var NekoSkillMarket = contract("neko.skill-market", {
|
|
|
2973
3162
|
}
|
|
2974
3163
|
}
|
|
2975
3164
|
});
|
|
3165
|
+
var ApplicationInfoSchema = Type2.Object({
|
|
3166
|
+
runningVersion: str,
|
|
3167
|
+
installedVersion: nullableString,
|
|
3168
|
+
protocolVersion: Type2.Integer({ minimum: 1 }),
|
|
3169
|
+
latestVersion: nullableString,
|
|
3170
|
+
checkedAt: Type2.Union([integer, nil]),
|
|
3171
|
+
checkState: Type2.Enum(["unknown", "checking", "ok", "failed", "development"])
|
|
3172
|
+
});
|
|
3173
|
+
var NekoApplicationInfo = contract("neko.application-info", {
|
|
3174
|
+
version: 1,
|
|
3175
|
+
input: Type2.Object({}),
|
|
3176
|
+
variants: { ready: { state: { info: ApplicationInfoSchema }, methods: { check: rpc(nil, ApplicationInfoSchema) } } }
|
|
3177
|
+
});
|
|
2976
3178
|
|
|
2977
3179
|
// packages/gateway/src/session-host.ts
|
|
2978
3180
|
import { open as open2 } from "fs/promises";
|
|
@@ -3375,9 +3577,9 @@ function serveSessions(host, runtime, context) {
|
|
|
3375
3577
|
|
|
3376
3578
|
// packages/gateway/src/skill-market.ts
|
|
3377
3579
|
import { createHash, randomUUID } from "crypto";
|
|
3378
|
-
import { mkdir as
|
|
3379
|
-
import { homedir as
|
|
3380
|
-
import { dirname as
|
|
3580
|
+
import { mkdir as mkdir3, readdir as readdir2, rename as rename3, rm as rm3, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
3581
|
+
import { homedir as homedir6 } from "os";
|
|
3582
|
+
import { dirname as dirname6, join as join8 } from "path";
|
|
3381
3583
|
|
|
3382
3584
|
// node_modules/fflate/esm/index.mjs
|
|
3383
3585
|
import { createRequire } from "module";
|
|
@@ -3800,8 +4002,8 @@ class SkillMarketError extends Error {
|
|
|
3800
4002
|
var CATALOG_TTL_MS = 60000;
|
|
3801
4003
|
var ZIP_CACHE_MAX = 16;
|
|
3802
4004
|
var MAX_PACKAGE_BYTES = 32 * 1024 * 1024;
|
|
3803
|
-
var userSkillsDir2 = (home) =>
|
|
3804
|
-
var projectSkillsDir = (workspaceRoot) =>
|
|
4005
|
+
var userSkillsDir2 = (home) => join8(home, ".agents", "skills");
|
|
4006
|
+
var projectSkillsDir = (workspaceRoot) => join8(workspaceRoot, ".agents", "skills");
|
|
3805
4007
|
var activeLevelOf = (state) => state.project ? "project" : state.user ? "user" : state.builtin ? "builtin" : null;
|
|
3806
4008
|
var normalizeEntry = (raw) => {
|
|
3807
4009
|
const row = raw ?? {};
|
|
@@ -3820,7 +4022,7 @@ var normalizeEntry = (raw) => {
|
|
|
3820
4022
|
};
|
|
3821
4023
|
};
|
|
3822
4024
|
function createSkillMarketClient(options) {
|
|
3823
|
-
const home = options.home ??
|
|
4025
|
+
const home = options.home ?? homedir6();
|
|
3824
4026
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
3825
4027
|
const now = options.now ?? Date.now;
|
|
3826
4028
|
let base;
|
|
@@ -3928,18 +4130,18 @@ function createSkillMarketClient(options) {
|
|
|
3928
4130
|
return projectSkillsDir(workspaceRoot);
|
|
3929
4131
|
};
|
|
3930
4132
|
const sweepTempDirs = async (skillsDir) => {
|
|
3931
|
-
const parent =
|
|
4133
|
+
const parent = dirname6(skillsDir);
|
|
3932
4134
|
let names = [];
|
|
3933
4135
|
try {
|
|
3934
4136
|
names = await readdir2(parent);
|
|
3935
4137
|
} catch {
|
|
3936
4138
|
return;
|
|
3937
4139
|
}
|
|
3938
|
-
await Promise.all(names.filter((n) => n.startsWith(".tmp-")).map((n) =>
|
|
4140
|
+
await Promise.all(names.filter((n) => n.startsWith(".tmp-")).map((n) => rm3(join8(parent, n), { recursive: true, force: true }).catch(() => {})));
|
|
3939
4141
|
};
|
|
3940
4142
|
const install = async ({ name, scope, workspaceRoot, overwrite }) => {
|
|
3941
4143
|
const skillsDir = skillsDirFor(scope, workspaceRoot);
|
|
3942
|
-
const target =
|
|
4144
|
+
const target = join8(skillsDir, name);
|
|
3943
4145
|
const exists = await stat2(target).catch(() => {
|
|
3944
4146
|
return;
|
|
3945
4147
|
}) !== undefined;
|
|
@@ -3948,23 +4150,23 @@ function createSkillMarketClient(options) {
|
|
|
3948
4150
|
const skill = await detail(name);
|
|
3949
4151
|
const files = unpack(skill, await download(skill));
|
|
3950
4152
|
await sweepTempDirs(skillsDir);
|
|
3951
|
-
const temp =
|
|
4153
|
+
const temp = join8(dirname6(skillsDir), `.tmp-${randomUUID()}`);
|
|
3952
4154
|
try {
|
|
3953
4155
|
for (const [relative, bytes] of files) {
|
|
3954
|
-
const dest =
|
|
3955
|
-
await
|
|
3956
|
-
await
|
|
4156
|
+
const dest = join8(temp, ...relative.split("/"));
|
|
4157
|
+
await mkdir3(dirname6(dest), { recursive: true });
|
|
4158
|
+
await writeFile3(dest, bytes);
|
|
3957
4159
|
}
|
|
3958
4160
|
if (exists)
|
|
3959
|
-
await
|
|
3960
|
-
await
|
|
3961
|
-
await
|
|
4161
|
+
await rm3(target, { recursive: true, force: true });
|
|
4162
|
+
await mkdir3(skillsDir, { recursive: true });
|
|
4163
|
+
await rename3(temp, target);
|
|
3962
4164
|
} finally {
|
|
3963
|
-
await
|
|
4165
|
+
await rm3(temp, { recursive: true, force: true }).catch(() => {});
|
|
3964
4166
|
}
|
|
3965
4167
|
let shadowedBy;
|
|
3966
4168
|
if (scope === "user" && workspaceRoot !== undefined && workspaceRoot !== "") {
|
|
3967
|
-
const projectDir =
|
|
4169
|
+
const projectDir = join8(projectSkillsDir(workspaceRoot), name);
|
|
3968
4170
|
if (await stat2(projectDir).catch(() => {
|
|
3969
4171
|
return;
|
|
3970
4172
|
}) !== undefined)
|
|
@@ -3974,12 +4176,12 @@ function createSkillMarketClient(options) {
|
|
|
3974
4176
|
};
|
|
3975
4177
|
const uninstall = async ({ name, scope, workspaceRoot }) => {
|
|
3976
4178
|
const skillsDir = skillsDirFor(scope, workspaceRoot);
|
|
3977
|
-
const target =
|
|
4179
|
+
const target = join8(skillsDir, name);
|
|
3978
4180
|
if (await stat2(target).catch(() => {
|
|
3979
4181
|
return;
|
|
3980
4182
|
}) === undefined)
|
|
3981
4183
|
throw new SkillMarketError("skill_not_installed", `Skill ${name} is not installed at ${scope} level`);
|
|
3982
|
-
await
|
|
4184
|
+
await rm3(target, { recursive: true, force: true });
|
|
3983
4185
|
};
|
|
3984
4186
|
return {
|
|
3985
4187
|
async list(query) {
|
|
@@ -4010,7 +4212,7 @@ function createSkillMarketClient(options) {
|
|
|
4010
4212
|
// packages/gateway/src/workspaces.ts
|
|
4011
4213
|
import { constants as constants2 } from "fs";
|
|
4012
4214
|
import { access, readdir as readdir3, stat as stat3 } from "fs/promises";
|
|
4013
|
-
import { dirname as
|
|
4215
|
+
import { dirname as dirname7, isAbsolute as isAbsolute3, join as join9 } from "path";
|
|
4014
4216
|
async function browseWorkspaces(options) {
|
|
4015
4217
|
const requested = options.path.replace(/^\/+([A-Za-z]:[\\/])/, "$1");
|
|
4016
4218
|
if (process.platform === "win32" && (requested === "/" || requested === "\\")) {
|
|
@@ -4037,7 +4239,7 @@ async function browseWorkspaces(options) {
|
|
|
4037
4239
|
let next = offset;
|
|
4038
4240
|
for (;next < entries.length && directories.length < 100; next++) {
|
|
4039
4241
|
const entry = entries[next];
|
|
4040
|
-
const target =
|
|
4242
|
+
const target = join9(path, entry.name);
|
|
4041
4243
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
4042
4244
|
continue;
|
|
4043
4245
|
try {
|
|
@@ -4051,7 +4253,7 @@ async function browseWorkspaces(options) {
|
|
|
4051
4253
|
}
|
|
4052
4254
|
return {
|
|
4053
4255
|
path,
|
|
4054
|
-
parent:
|
|
4256
|
+
parent: dirname7(path) === path ? null : dirname7(path),
|
|
4055
4257
|
entries: directories,
|
|
4056
4258
|
next: next < entries.length ? JSON.stringify({ key, offset: next }) : null
|
|
4057
4259
|
};
|
|
@@ -4081,6 +4283,48 @@ var ServerState = local2("neko.server-state", {
|
|
|
4081
4283
|
});
|
|
4082
4284
|
async function createNekoServerServices(options) {
|
|
4083
4285
|
const { runtime, context, host } = options;
|
|
4286
|
+
const updates = new UpdateChecker(options.updates);
|
|
4287
|
+
await updates.local();
|
|
4288
|
+
const updateListeners = new Set;
|
|
4289
|
+
let published = JSON.stringify(updates.state), closed = false;
|
|
4290
|
+
const publishUpdates = () => {
|
|
4291
|
+
if (closed)
|
|
4292
|
+
return;
|
|
4293
|
+
const next = JSON.stringify(updates.state);
|
|
4294
|
+
if (next === published)
|
|
4295
|
+
return;
|
|
4296
|
+
published = next;
|
|
4297
|
+
for (const listener of updateListeners)
|
|
4298
|
+
listener(updates.state);
|
|
4299
|
+
};
|
|
4300
|
+
host.serve(NekoApplicationInfo, (_input, _principal, self) => {
|
|
4301
|
+
const state = self.state({ info: cell2(ApplicationInfoSchema, updates.state) });
|
|
4302
|
+
self.listen({
|
|
4303
|
+
updates: listener2({
|
|
4304
|
+
subscribe: (fn) => {
|
|
4305
|
+
updateListeners.add(fn);
|
|
4306
|
+
return () => updateListeners.delete(fn);
|
|
4307
|
+
}
|
|
4308
|
+
}, (value, ctx) => state.info.replace(ctx, value))
|
|
4309
|
+
});
|
|
4310
|
+
return {
|
|
4311
|
+
kind: "ready",
|
|
4312
|
+
info: state.info,
|
|
4313
|
+
check: async () => {
|
|
4314
|
+
await updates.check(true);
|
|
4315
|
+
publishUpdates();
|
|
4316
|
+
return updates.state;
|
|
4317
|
+
}
|
|
4318
|
+
};
|
|
4319
|
+
});
|
|
4320
|
+
const updateTimer = setInterval(() => {
|
|
4321
|
+
updates.local().then(() => {
|
|
4322
|
+
publishUpdates();
|
|
4323
|
+
return updates.check();
|
|
4324
|
+
}).then(publishUpdates);
|
|
4325
|
+
}, options.updateIntervalMs ?? 30000);
|
|
4326
|
+
updateTimer.unref();
|
|
4327
|
+
updates.check().then(publishUpdates);
|
|
4084
4328
|
const market = createSkillMarketClient({
|
|
4085
4329
|
marketUrl: options.marketUrl,
|
|
4086
4330
|
...options.promptHome !== undefined ? { home: options.promptHome } : {}
|
|
@@ -4240,6 +4484,10 @@ async function createNekoServerServices(options) {
|
|
|
4240
4484
|
await host.ready();
|
|
4241
4485
|
return {
|
|
4242
4486
|
async dispose() {
|
|
4487
|
+
closed = true;
|
|
4488
|
+
clearInterval(updateTimer);
|
|
4489
|
+
updates.close();
|
|
4490
|
+
updateListeners.clear();
|
|
4243
4491
|
await mutationTail;
|
|
4244
4492
|
}
|
|
4245
4493
|
};
|
|
@@ -4252,9 +4500,9 @@ function toConversationId(sessionId) {
|
|
|
4252
4500
|
}
|
|
4253
4501
|
|
|
4254
4502
|
// packages/gateway/src/unix-link.ts
|
|
4255
|
-
import { lstat, mkdir as
|
|
4503
|
+
import { lstat, mkdir as mkdir4, unlink as unlink2 } from "fs/promises";
|
|
4256
4504
|
import { connect, createServer } from "net";
|
|
4257
|
-
import { dirname as
|
|
4505
|
+
import { dirname as dirname8 } from "path";
|
|
4258
4506
|
import { webSocketLink as webSocketLink2 } from "@earendil-works/chord";
|
|
4259
4507
|
|
|
4260
4508
|
// packages/gateway/src/link.ts
|
|
@@ -4439,7 +4687,7 @@ function createUnixListener(options) {
|
|
|
4439
4687
|
let bound = false;
|
|
4440
4688
|
return {
|
|
4441
4689
|
async start(accept) {
|
|
4442
|
-
await
|
|
4690
|
+
await mkdir4(dirname8(options.path), { recursive: true });
|
|
4443
4691
|
try {
|
|
4444
4692
|
const stat = await lstat(options.path);
|
|
4445
4693
|
if (!stat.isSocket())
|
|
@@ -4551,9 +4799,9 @@ async function startGateway(options = {}) {
|
|
|
4551
4799
|
const host = options.host ?? fileConfig.gateway?.host ?? DEFAULT_HOST;
|
|
4552
4800
|
const port = options.port !== undefined ? options.port : fileConfig.gateway?.port ?? (process.platform === "win32" ? 0 : DEFAULT_PORT);
|
|
4553
4801
|
const marketUrl = options.marketUrl ?? fileConfig.skills?.marketUrl ?? DEFAULT_SKILL_MARKET_URL;
|
|
4554
|
-
const nekoDir =
|
|
4802
|
+
const nekoDir = dirname9(storagePath);
|
|
4555
4803
|
const serverId = options.serverId ?? await loadOrCreateServerId(nekoDir);
|
|
4556
|
-
const socketPath = options.socketPath !== undefined ? options.socketPath : fileConfig.gateway?.socketPath !== undefined ? fileConfig.gateway.socketPath : process.platform === "win32" ? null :
|
|
4804
|
+
const socketPath = options.socketPath !== undefined ? options.socketPath : fileConfig.gateway?.socketPath !== undefined ? fileConfig.gateway.socketPath : process.platform === "win32" ? null : join10(nekoDir, `${serverId}.sock`);
|
|
4557
4805
|
const runtime = await createNekoRuntime({
|
|
4558
4806
|
context,
|
|
4559
4807
|
onReport: (error) => options.onError?.(error instanceof Error ? error : new Error(String(error))),
|
|
@@ -4579,6 +4827,8 @@ async function startGateway(options = {}) {
|
|
|
4579
4827
|
model,
|
|
4580
4828
|
marketUrl,
|
|
4581
4829
|
promptHome: options.promptHome,
|
|
4830
|
+
updates: options.updates,
|
|
4831
|
+
updateIntervalMs: options.updateIntervalMs,
|
|
4582
4832
|
onError: options.onError
|
|
4583
4833
|
});
|
|
4584
4834
|
} catch (error) {
|
|
@@ -4598,6 +4848,8 @@ async function bindGateway(runtime, options) {
|
|
|
4598
4848
|
runtime,
|
|
4599
4849
|
context,
|
|
4600
4850
|
marketUrl: options.marketUrl,
|
|
4851
|
+
...options.updates ? { updates: options.updates } : {},
|
|
4852
|
+
...options.updateIntervalMs ? { updateIntervalMs: options.updateIntervalMs } : {},
|
|
4601
4853
|
...options.promptHome !== undefined ? { promptHome: options.promptHome } : {}
|
|
4602
4854
|
});
|
|
4603
4855
|
let wsListener;
|
|
@@ -4674,15 +4926,15 @@ function serverIdFromSocketPath(socketPath) {
|
|
|
4674
4926
|
return isServerId(candidate) ? candidate : undefined;
|
|
4675
4927
|
}
|
|
4676
4928
|
async function loadOrCreateServerId(nekoDir) {
|
|
4677
|
-
const file =
|
|
4929
|
+
const file = join10(nekoDir, SERVER_ID_FILENAME);
|
|
4678
4930
|
try {
|
|
4679
|
-
const existing = (await
|
|
4931
|
+
const existing = (await readFile6(file, "utf8")).trim();
|
|
4680
4932
|
if (existing !== "")
|
|
4681
4933
|
return existing;
|
|
4682
4934
|
} catch {}
|
|
4683
4935
|
const created = randomUUID3();
|
|
4684
|
-
await
|
|
4685
|
-
await
|
|
4936
|
+
await mkdir5(nekoDir, { recursive: true, mode: 448 });
|
|
4937
|
+
await writeFile4(file, `${created}
|
|
4686
4938
|
`, { mode: 384 });
|
|
4687
4939
|
return created;
|
|
4688
4940
|
}
|