@minimalcorp/tsunagi 0.1.23 → 0.1.24
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/.next/standalone/apps/web/.next/BUILD_ID +1 -1
- package/.next/standalone/apps/web/.next/build-manifest.json +3 -3
- package/.next/standalone/apps/web/.next/prerender-manifest.json +3 -3
- package/.next/standalone/apps/web/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/index.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/index.rsc +3 -3
- package/.next/standalone/apps/web/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/apps/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.rsc +3 -3
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_full.segment.rsc +3 -3
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0cwejmp._.js → [root-of-the-server]__053-iaj._.js} +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__07kf5h-._.js +13 -13
- package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0_gamc9._.js +13 -13
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0a7t~wn._.js → [root-of-the-server]__0tq9wv6._.js} +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{_0ie-fls._.js → _03hz06e._.js} +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{_0gpi5iq._.js → _0836rl_._.js} +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{_06gug4~._.js → _0amxree._.js} +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/_0g.cd6a._.js +17 -17
- package/.next/standalone/apps/web/.next/server/chunks/ssr/apps_web_src_app_settings_page_tsx_0i6itlb._.js +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0s64~o2._.js → node_modules_00b-qxe._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0-dhyl6._.js → node_modules_046ca89._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0w5pf1p._.js → node_modules_065xsw3._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0-ecrjy._.js → node_modules_0b547kr._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0.7pxhy._.js → node_modules_0c_-8tt._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0efb87~._.js → node_modules_0dmu62y._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0of7.-5._.js → node_modules_0gs4d2e._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0j3gfu0._.js → node_modules_0jcrj-2._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0uwlz8.._.js → node_modules_0kmpnnk._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0126g83._.js → node_modules_0llz_9l._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0zmomoe._.js → node_modules_0pi.005._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0.6~k__._.js → node_modules_0v86kpj._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0ybihhp._.js → node_modules_0vm.f2d._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_11gk90a._.js → node_modules_0y3g5lf._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0r6ewmn._.js → node_modules_0z0orm9._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_06iewnh._.js → node_modules_0~n91z-._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
- package/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
- package/.next/standalone/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/apps/web/.next/static/chunks/08dtfekvxgpfu.js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/{0e9140l311w8t.js → 0dpeys_k2-mrk.js} +15 -15
- package/.next/standalone/apps/web/.next/static/chunks/0hvtxos.brr01.js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/{0kw1z8z_ld6s0.js → 0nh1-0lee-1pk.js} +15 -15
- package/.next/standalone/apps/web/.next/static/chunks/0wxrfu5la_s94.js +64 -0
- package/.next/standalone/apps/web/.next/static/chunks/{10ildl1-mw9ez.js → 0y9ehkcn04ljq.js} +2 -2
- package/.next/standalone/apps/web/.next/static/chunks/0zu48yvkl8pyj.css +4 -0
- package/.next/standalone/apps/web/.next/static/chunks/0~0s04z6h.rs..js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/12dmkv54xstku.js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/13ohhwsmmfexn.js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/13twvr.qg3khh.js +1 -0
- package/dist/server/index.js +8 -1
- package/dist/server/lib/claude-config-guard.d.ts +9 -0
- package/dist/server/lib/claude-config-guard.js +43 -0
- package/dist/server/lib/llm-process.d.ts +29 -0
- package/dist/server/lib/llm-process.js +235 -0
- package/dist/server/lib/process-port.d.ts +1 -0
- package/dist/server/lib/process-port.js +28 -0
- package/dist/server/lib/whisper-process.d.ts +2 -2
- package/dist/server/lib/whisper-process.js +19 -5
- package/dist/server/routes/llm.d.ts +2 -0
- package/dist/server/routes/llm.js +23 -0
- package/dist/server/routes/terminal.js +5 -0
- package/dist/server/routes/whisper.js +36 -2
- package/llm-server/README.md +27 -0
- package/llm-server/download_model.py +14 -0
- package/llm-server/requirements.txt +1 -0
- package/llm-server/run.sh +26 -0
- package/npm-shrinkwrap.json +24 -40
- package/package.json +2 -1
- package/whisper-server/server.py +25 -4
- package/.next/standalone/apps/web/.next/static/chunks/0-nditm2kmxy3.js +0 -1
- package/.next/standalone/apps/web/.next/static/chunks/00vlzdkbq6_w3.js +0 -64
- package/.next/standalone/apps/web/.next/static/chunks/0krkukgs6hg9c.js +0 -1
- package/.next/standalone/apps/web/.next/static/chunks/0sy8tzk6r-pb~.js +0 -1
- package/.next/standalone/apps/web/.next/static/chunks/0vfrsmi1vl53y.css +0 -4
- /package/.next/standalone/apps/web/.next/static/{S24zAyBAidp_ISALDwbn4 → gWwRKXTSig4Dz-gNeqxJy}/_buildManifest.js +0 -0
- /package/.next/standalone/apps/web/.next/static/{S24zAyBAidp_ISALDwbn4 → gWwRKXTSig4Dz-gNeqxJy}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/apps/web/.next/static/{S24zAyBAidp_ISALDwbn4 → gWwRKXTSig4Dz-gNeqxJy}/_ssgManifest.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,18566,(e,t,r)=>{t.exports=e.r(76562)},43531,78745,e=>{"use strict";let t=(0,e.i(75254).default)("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);e.s(["default",0,t],78745),e.s(["Check",0,t],43531)},37870,31278,e=>{"use strict";var t=e.i(18050),r=e.i(17521);e.s(["Loader2",()=>r.default],31278);var r=r;e.s(["LoadingSpinner",0,function({size:e="md",message:a}){return(0,t.jsxs)("div",{className:"flex flex-col items-center justify-center gap-3",children:[(0,t.jsx)(r.default,{className:`${{sm:"w-4 h-4",md:"w-8 h-8",lg:"w-12 h-12"}[e]} text-primary animate-spin`}),a&&(0,t.jsx)("p",{className:"text-sm text-muted-foreground",children:a})]})}],37870)},10012,24564,94349,e=>{"use strict";var t,r=e.i(18050);e.i(47167);var a=e.i(71645);e.s([],19485),e.i(19485);var i=e.i(76477),n=e.i(67805),l=e.i(17514);let s=((t={}).disabled="data-disabled",t.valid="data-valid",t.invalid="data-invalid",t.touched="data-touched",t.dirty="data-dirty",t.filled="data-filled",t.focused="data-focused",t),o={badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:null,valueMissing:!1},d={valid:null,touched:!1,dirty:!1,filled:!1,focused:!1},u={disabled:!1,...d},c={valid:e=>null===e?null:e?{[s.valid]:""}:{[s.invalid]:""}},f=a.createContext({invalid:void 0,name:void 0,validityData:{state:o,errors:[],error:"",value:"",initialValue:null},setValidityData:l.NOOP,disabled:void 0,touched:d.touched,setTouched:l.NOOP,dirty:d.dirty,setDirty:l.NOOP,filled:d.filled,setFilled:l.NOOP,focused:d.focused,setFocused:l.NOOP,validate:()=>null,validationMode:"onSubmit",validationDebounceTime:0,shouldValidateOnChange:()=>!1,state:u,markedDirtyRef:{current:!1},validation:{getValidationProps:(e=l.EMPTY_OBJECT)=>e,getInputValidationProps:(e=l.EMPTY_OBJECT)=>e,inputRef:{current:null},commit:async()=>{}}});function m(e=!0){let t=a.useContext(f);if(t.setValidityData===l.NOOP&&!e)throw Error((0,n.default)(28));return t}let p=a.createContext({legendId:void 0,setLegendId:()=>{},disabled:void 0}),v=a.createContext({formRef:{current:{fields:new Map}},errors:{},clearErrors:l.NOOP,validationMode:"onSubmit",submitAttemptedRef:{current:!1}});function g(){return a.useContext(v)}var x=e.i(30628),h=e.i(19805),b=e.i(89879);let y=a.createContext({controlId:void 0,registerControlId:l.NOOP,labelId:void 0,setLabelId:l.NOOP,messageIds:[],setMessageIds:l.NOOP,getDescriptionProps:e=>e});function j(){return a.useContext(y)}let w=function(e){let t=(0,b.useBaseUiId)(),n=void 0===e.controlId?t:e.controlId,[l,s]=a.useState(n),[o,d]=a.useState(e.labelId),[u,c]=a.useState([]),f=(0,x.useRefWithInit)(()=>new Map),{messageIds:m}=j(),p=(0,i.useStableCallback)((e,t)=>{let r=f.current;void 0===t?r.delete(e):(r.set(e,t),s(e=>{let t;if(0!==r.size){for(let a of r.values()){if(void 0!==e&&a===e)return e;void 0===t&&(t=a)}return t}}))}),v=a.useCallback(e=>(0,h.mergeProps)({"aria-describedby":m.concat(u).join(" ")||void 0},e),[m,u]),g=a.useMemo(()=>({controlId:l,registerControlId:p,labelId:o,setLabelId:d,messageIds:u,setMessageIds:c,getDescriptionProps:v}),[l,p,o,d,u,c,v]);return(0,r.jsx)(y.Provider,{value:g,children:e.children})};var M=e.i(27601),C=e.i(66745);function k(e,t){return{...e,state:{...e.state,valid:!t&&e.state.valid}}}let N=Object.keys(o),S=a.forwardRef(function(e,t){let{errors:s,validationMode:d,submitAttemptedRef:u}=g(),{render:m,className:v,validate:x,validationDebounceTime:b=0,validationMode:y=d,name:w,disabled:S=!1,invalid:E,dirty:R,touched:L,actionsRef:I,...O}=e,{disabled:P}=function(e=!1){let t=a.useContext(p);if(!t&&!e)throw Error((0,n.default)(86));return t}(),T=(0,i.useStableCallback)(x||(()=>null)),B=P||S,[A,V]=a.useState(!1),[D,_]=a.useState(!1),[z,F]=a.useState(!1),[U,H]=a.useState(!1),$=R??D,q=L??A,G=a.useRef(!1),J=(0,i.useStableCallback)(e=>{void 0===R&&(e&&(G.current=!0),_(e))}),W=(0,i.useStableCallback)(e=>{void 0===L&&V(e)}),K=(0,i.useStableCallback)(()=>"onChange"===y||"onSubmit"===y&&u.current),Y=!!w&&Object.hasOwn(s,w)&&void 0!==s[w],Q=!0===E||Y,[X,Z]=a.useState({state:o,error:"",errors:[],value:null,initialValue:null}),ee=!Q&&X.state.valid,et=a.useMemo(()=>({disabled:B,touched:q,dirty:$,valid:ee,filled:z,focused:U}),[B,q,$,ee,z,U]),er=function(e){let{formRef:t,clearErrors:r}=g(),{setValidityData:n,validate:s,validityData:d,validationDebounceTime:u,invalid:c,markedDirtyRef:f,state:m,name:p,shouldValidateOnChange:v}=e,{controlId:x,getDescriptionProps:b}=j(),y=(0,C.useTimeout)(),w=a.useRef(null),M=(0,i.useStableCallback)(async(e,r=!1)=>{let a,i=w.current;if(!i)return;if(r){if(!1!==m.valid)return;let r=i.validity;if(!r.valueMissing){let r={value:e,state:{...o,valid:!0},error:"",errors:[],initialValue:d.initialValue};if(i.setCustomValidity(""),x){let e=t.current.fields.get(x);e&&t.current.fields.set(x,{...e,...k(r,!1)})}n(r);return}let a=N.reduce((e,t)=>(e[t]=r[t],e),{});if(!a.valid&&!function(e){if(!e||e.valid||!e.valueMissing)return!1;let t=!1;for(let r of N)"valid"!==r&&("valueMissing"===r&&(t=e[r]),e[r]&&(t=!1));return t}(a))return}y.clear();let l=null,u=[],p=function(e){let t=N.reduce((t,r)=>(t[r]=e.validity[r],t),{}),r=!1;for(let e of N)if("valid"!==e){if("valueMissing"===e&&t[e])r=!0;else if(t[e])return t}return r&&!f.current&&(t.valid=!0,t.valueMissing=!1),t}(i),g=v();if(i.validationMessage&&!g)a=i.validationMessage,u=[i.validationMessage];else{let r=s(e,Array.from(t.current.fields.values()).reduce((e,t)=>(t.name&&(e[t.name]=t.getValue()),e),{}));null!==(l="object"==typeof r&&null!==r&&"then"in r?await r:r)?(p.valid=!1,p.customError=!0,Array.isArray(l)?(u=l,i.setCustomValidity(l.join("\n"))):l&&(u=[l],i.setCustomValidity(l))):g&&(i.setCustomValidity(""),p.customError=!1,i.validationMessage?(a=i.validationMessage,u=[i.validationMessage]):i.validity.valid&&!p.valid&&(p.valid=!0))}let h={value:e,state:p,error:a??(Array.isArray(l)?l[0]:l??""),errors:u,initialValue:d.initialValue};if(x){let e=t.current.fields.get(x);e&&t.current.fields.set(x,{...e,...k(h,c)})}n(h)}),S=a.useCallback((e={})=>(0,h.mergeProps)(b,!1===m.valid?{"aria-invalid":!0}:l.EMPTY_OBJECT,e),[b,m.valid]),E=a.useCallback((e={})=>(0,h.mergeProps)({onChange(e){if(e.nativeEvent.defaultPrevented)return;if(r(p),!v())return void M(e.currentTarget.value,!0);let t=e.currentTarget;""===t.value?M(t.value):(y.clear(),u?y.start(u,()=>{M(t.value)}):M(t.value))}},S(e)),[S,r,p,y,M,u,v]);return a.useMemo(()=>({getValidationProps:S,getInputValidationProps:E,inputRef:w,commit:M}),[S,E,M])}({setValidityData:Z,validate:T,validityData:X,validationDebounceTime:b,invalid:Q,markedDirtyRef:G,state:et,name:w,shouldValidateOnChange:K}),ea=a.useCallback(()=>{G.current=!0,er.commit(X.value)},[er,X]);a.useImperativeHandle(I,()=>({validate:ea}),[ea]);let ei=a.useMemo(()=>({invalid:Q,name:w,validityData:X,setValidityData:Z,disabled:B,touched:q,setTouched:W,dirty:$,setDirty:J,filled:z,setFilled:F,focused:U,setFocused:H,validate:T,validationMode:y,validationDebounceTime:b,shouldValidateOnChange:K,state:et,markedDirtyRef:G,validation:er}),[Q,w,X,B,q,W,$,J,z,F,U,H,T,y,b,K,et,er]),en=(0,M.useRenderElement)("div",e,{ref:t,state:et,props:O,stateAttributesMapping:c});return(0,r.jsx)(f.Provider,{value:ei,children:en})}),E=a.forwardRef(function(e,t){return(0,r.jsx)(w,{children:(0,r.jsx)(S,{...e,ref:t})})});var R=e.i(29315),L=e.i(90219),I=e.i(47857),O=e.i(977);function P(e,t){let r=(0,b.useBaseUiId)(e);return(0,O.useIsoLayoutEffect)(()=>(t(r),()=>{t(void 0)}),[r,t]),r}e.s(["useRegisteredLabelId",0,P],24564);let T=a.forwardRef(function(e,t){let{render:r,className:n,id:l,nativeLabel:s=!0,...o}=e,d=m(!1),{labelId:u}=j(),f=a.useRef(null),p=function(e={}){let{id:t,fallbackControlId:r,native:a=!1,setLabelId:n,focusControl:l}=e,{controlId:s,setLabelId:o}=j(),d=P(t,(0,i.useStableCallback)(e=>{o(e),n?.(e)})),u=s??r;function c(e){let t=(0,I.getTarget)(e.nativeEvent);t?.closest("button,input,select,textarea")||(!e.defaultPrevented&&e.detail>1&&e.preventDefault(),a||function(e){if(l)return l(e,u);if(!u)return;let t=(0,L.ownerDocument)(e.currentTarget).getElementById(u);(0,R.isHTMLElement)(t)&&t.focus({focusVisible:!0})}(e))}return a?{id:d,htmlFor:u??void 0,onMouseDown:c}:{id:d,onClick:c,onPointerDown(e){e.preventDefault()}}}({id:u??l,native:s});return(0,M.useRenderElement)("label",e,{ref:[t,f],state:d.state,props:[p,o],stateAttributesMapping:c})});var B=e.i(89269),A=e.i(84759),V=e.i(88841);let D={...c,...A.transitionStatusMapping},_=a.forwardRef(function(e,t){let{render:i,id:n,className:l,match:s,...o}=e,d=(0,b.useBaseUiId)(n),{validityData:u,state:c,name:f}=m(!1),{setMessageIds:p}=j(),{errors:v}=g(),x=f?v[f]:null,h=!1;h=!!x||!0===s||(s?!!u.state[s]:!1===u.state.valid);let{mounted:y,transitionStatus:w,setMounted:C}=(0,V.useTransitionStatus)(h);(0,O.useIsoLayoutEffect)(()=>{if(h&&d)return p(e=>e.concat(d)),()=>{p(e=>e.filter(e=>e!==d))}},[h,d,p]);let k=a.useRef(null),[N,S]=a.useState(null),[E,R]=a.useState(null),L=x||(u.errors.length>1?(0,r.jsx)("ul",{children:u.errors.map(e=>(0,r.jsx)("li",{children:e},e))}):u.error),I=u.error;null!=x?I=Array.isArray(x)?JSON.stringify(x):x:u.errors.length>1&&(I=JSON.stringify(u.errors)),h&&I!==E&&(R(I),S(L)),(0,B.useOpenChangeComplete)({open:h,ref:k,onComplete(){h||C(!1)}});let P={...c,transitionStatus:w},T=(0,M.useRenderElement)("div",e,{ref:[t,k],state:P,props:[{id:d,children:h?L:N},o],stateAttributesMapping:D,enabled:y});return y?T:null}),z=a.forwardRef(function(e,t){let{render:r,id:a,className:i,...n}=e,l=(0,b.useBaseUiId)(a),s=m(!1),{setMessageIds:o}=j();return(0,O.useIsoLayoutEffect)(()=>{if(l)return o(e=>e.concat(l)),()=>{o(e=>e.filter(e=>e!==l))}},[l,o]),(0,M.useRenderElement)("p",e,{ref:t,state:s.state,props:[{id:l},n],stateAttributesMapping:c})});function F({controlled:e,default:t,name:r,state:i="value"}){let{current:n}=a.useRef(void 0!==e),[l,s]=a.useState(t),o=a.useCallback(e=>{n||s(e)},[]);return[n?e:l,o]}e.s(["useControlled",0,F],94349);var U=e.i(74080),H=e.i(5963),$=e.i(3404);let q=a.forwardRef(function(e,t){let{render:r,className:n,id:s,name:o,value:d,disabled:u=!1,onValueChange:f,defaultValue:p,autoFocus:v=!1,...h}=e,{state:y,name:w,disabled:C,setTouched:N,setDirty:S,validityData:E,setFocused:P,setFilled:T,validationMode:B,validation:A}=m(),V=C||u,D=w??o,_={...y,disabled:V},{labelId:z}=j(),q=function(e={}){let{id:t,implicit:r=!1,controlRef:n}=e,{controlId:s,registerControlId:o}=j(),d=(0,b.useBaseUiId)(t),u=r?s:void 0,c=(0,x.useRefWithInit)(()=>Symbol("labelable-control")),f=a.useRef(!1),m=a.useRef(null!=t),p=(0,i.useStableCallback)(()=>{f.current&&o!==l.NOOP&&(f.current=!1,o(c.current,void 0))});return(0,O.useIsoLayoutEffect)(()=>{let e;if(o!==l.NOOP){if(r){let r=n?.current;e=(0,R.isElement)(r)&&null!=r.closest("label")?t??null:u??d}else if(null!=t)m.current=!0,e=t;else{if(!m.current)return void p();e=d}if(void 0===e)return void p();f.current=!0,o(c.current,e)}},[t,n,u,o,r,d,c,p]),a.useEffect(()=>p,[p]),s??d}({id:s});(0,O.useIsoLayoutEffect)(()=>{let e=null!=d;A.inputRef.current?.value||e&&""!==d?T(!0):e&&""===d&&T(!1)},[A.inputRef,T,d]);let G=a.useRef(null);(0,O.useIsoLayoutEffect)(()=>{v&&G.current===(0,I.activeElement)((0,L.ownerDocument)(G.current))&&P(!0)},[v,P]);let[J]=F({controlled:d,default:p,name:"FieldControl",state:"value"}),W=void 0!==d,K=W?J:void 0;return!function(e){let{enabled:t=!0,value:r,id:a,name:n,controlRef:l,commit:s}=e,{formRef:o}=g(),{invalid:d,markedDirtyRef:u,validityData:c,setValidityData:f}=m(),p=(0,i.useStableCallback)(e.getValue);(0,O.useIsoLayoutEffect)(()=>{if(!t)return;let e=r;void 0===e&&(e=p()),null===c.initialValue&&null!==e&&f(t=>({...t,initialValue:e}))},[t,f,r,c.initialValue,p]),(0,O.useIsoLayoutEffect)(()=>{t&&a&&o.current.fields.set(a,{getValue:p,name:n,controlRef:l,validityData:k(c,d),validate(e=!0){let t=r;void 0===t&&(t=p()),u.current=!0,e?U.flushSync(()=>s(t)):s(t)}})},[s,l,t,o,p,a,d,u,n,c,r]),(0,O.useIsoLayoutEffect)(()=>{let e=o.current.fields;return()=>{a&&e.delete(a)}},[o,a])}({id:q,name:D,commit:A.commit,value:K,getValue:()=>A.inputRef.current?.value,controlRef:A.inputRef}),(0,M.useRenderElement)("input",e,{ref:[t,G],state:_,props:[{id:q,disabled:V,name:D,ref:A.inputRef,"aria-labelledby":z,autoFocus:v,...W?{value:K}:{defaultValue:p},onChange(e){let t=e.currentTarget.value;f?.(t,(0,H.createChangeEventDetails)($.REASONS.none,e.nativeEvent)),S(t!==E.initialValue),T(""!==t)},onFocus(){P(!0)},onBlur(e){N(!0),P(!1),"onBlur"===B&&A.commit(e.currentTarget.value)},onKeyDown(e){"INPUT"===e.currentTarget.tagName&&"Enter"===e.key&&(N(!0),A.commit(e.currentTarget.value))}},A.getInputValidationProps(),h],stateAttributesMapping:c})}),G=a.createContext({disabled:!1}),J=a.createContext(void 0),W=a.forwardRef(function(e,t){let{render:i,className:l,disabled:s=!1,...o}=e,{state:d,disabled:u}=m(!1),f=u||s,p=function(e=!0){let t=a.useContext(J);if(void 0===t&&!e)throw Error((0,n.default)(3));return t}(),v=p?.parent.id,g=p?.allValues!==void 0,x=a.useMemo(()=>({disabled:f}),[f]),h=(0,M.useRenderElement)("div",e,{ref:t,state:d,props:o,stateAttributesMapping:c});return(0,r.jsx)(w,{controlId:g?v:void 0,children:(0,r.jsx)(G.Provider,{value:x,children:h})})});e.s(["Control",0,q,"Description",0,z,"Error",0,_,"Item",0,W,"Label",0,T,"Root",0,E,"Validity",0,function(e){let{children:t}=e,{validityData:i,invalid:n}=m(!1),l=a.useMemo(()=>k(i,n),[i,n]),s=!1===l.state.valid,{transitionStatus:o}=(0,V.useTransitionStatus)(s),d=a.useMemo(()=>({...l,validity:l.state,transitionStatus:o}),[l,o]);return(0,r.jsx)(a.Fragment,{children:t(d)})}],71975);var K=e.i(71975),K=K;let Y=a.forwardRef(function(e,t){return(0,r.jsx)(K.Control,{ref:t,...e})});var Q=e.i(52088);e.s(["Input",0,function({className:e,type:t,...a}){return(0,r.jsx)(Y,{type:t,"data-slot":"input",className:(0,Q.cn)("h-9 w-full min-w-0 rounded-md border border-input bg-input/20 px-3 py-1 text-sm transition-colors outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",e),...a})}],10012)},41247,e=>{"use strict";var t=e.i(18050),r=e.i(52088);e.s(["Textarea",0,function({className:e,...a}){return(0,t.jsx)("textarea",{"data-slot":"textarea",className:(0,r.cn)("flex field-sizing-content min-h-16 w-full resize-none rounded-md border border-input bg-input/20 px-2 py-2 text-sm transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-xs/relaxed dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",e),...a})}])},86536,e=>{"use strict";let t=(0,e.i(75254).default)("eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);e.s(["Eye",0,t],86536)},27612,e=>{"use strict";let t=(0,e.i(75254).default)("trash-2",[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]]);e.s(["Trash2",0,t],27612)},95468,e=>{"use strict";var t=e.i(23287);e.s(["CheckCircle2",()=>t.default])},77814,e=>{"use strict";var t,r=e.i(18050);e.s([],33847),e.i(33847),e.i(47167);var a=e.i(71645),i=e.i(231),n=e.i(62372);let l=new Map;function s(e,t,r){return null==e?"":(function(e,t){let r=JSON.stringify({locale:e,options:t}),a=l.get(r);if(a)return a;let i=new Intl.NumberFormat(e,t);return l.set(r,i),i})(t,r).format(e)}var o=e.i(27601),d=e.i(67805);let u=a.createContext(void 0);function c(){let e=a.useContext(u);if(void 0===e)throw Error((0,d.default)(51));return e}let f=((t={}).complete="data-complete",t.indeterminate="data-indeterminate",t.progressing="data-progressing",t),m={status:e=>"progressing"===e?{[f.progressing]:""}:"complete"===e?{[f.complete]:""}:"indeterminate"===e?{[f.indeterminate]:""}:null};function p(e,t){return null==t?"indeterminate progress":e||`${t}%`}let v=a.forwardRef(function(e,t){var l;let{format:d,getAriaValueText:c=p,locale:f,max:v=100,min:g=0,value:x,render:h,className:b,children:y,...j}=e,[w,M]=a.useState(),C=(0,i.useValueAsRef)(d),k="indeterminate";Number.isFinite(x)&&(k=x===v?"complete":"progressing");let N=(l=C.current,null==x?"":l?s(x,f,l):s(x/100,f,{style:"percent"})),S=a.useMemo(()=>({status:k}),[k]),E={"aria-labelledby":w,"aria-valuemax":v,"aria-valuemin":g,"aria-valuenow":x??void 0,"aria-valuetext":c(N,x),role:"progressbar",children:(0,r.jsxs)(a.Fragment,{children:[y,(0,r.jsx)("span",{role:"presentation",style:n.visuallyHidden,children:"x"})]})},R=a.useMemo(()=>({formattedValue:N,max:v,min:g,setLabelId:M,state:S,status:k,value:x}),[N,v,g,M,S,k,x]),L=(0,o.useRenderElement)("div",e,{state:S,ref:t,props:[E,j],stateAttributesMapping:m});return(0,r.jsx)(u.Provider,{value:R,children:L})}),g=a.forwardRef(function(e,t){let{render:r,className:a,...i}=e,{state:n}=c();return(0,o.useRenderElement)("div",e,{state:n,ref:t,props:i,stateAttributesMapping:m})}),x=a.forwardRef(function(e,t){let{render:r,className:i,...n}=e,{max:l,min:s,value:d,state:u}=c(),f=Number.isFinite(d)&&null!==d?(d-s)*100/(l-s):null,p=a.useCallback(()=>null==f?{}:{insetInlineStart:0,height:"inherit",width:`${f}%`},[f]);return(0,o.useRenderElement)("div",e,{state:u,ref:t,props:[{style:p()},n],stateAttributesMapping:m})}),h=a.forwardRef(function(e,t){let{className:r,render:a,children:i,...n}=e,{value:l,formattedValue:s,state:d}=c(),u=null==l?"indeterminate":s,f=null==l?null:s;return(0,o.useRenderElement)("span",e,{state:d,ref:t,props:[{"aria-hidden":!0,children:"function"==typeof i?i(u,l):f},n],stateAttributesMapping:m})});var b=e.i(24564);let y=a.forwardRef(function(e,t){let{render:r,className:a,id:i,...n}=e,{setLabelId:l,state:s}=c(),d=(0,b.useRegisteredLabelId)(i,l);return(0,o.useRenderElement)("span",e,{state:s,ref:t,props:[{id:d,role:"presentation"},n],stateAttributesMapping:m})});e.s(["Indicator",0,x,"Label",0,y,"Root",0,v,"Track",0,g,"Value",0,h],45929);var j=e.i(45929),j=j,w=e.i(52088);function M({className:e,...t}){return(0,r.jsx)(j.Track,{className:(0,w.cn)("relative flex h-1 w-full items-center overflow-x-hidden rounded-md bg-muted",e),"data-slot":"progress-track",...t})}function C({className:e,...t}){return(0,r.jsx)(j.Indicator,{"data-slot":"progress-indicator",className:(0,w.cn)("h-full bg-primary",e),style:{transition:"width 500ms ease-in-out"},...t})}e.s(["Progress",0,function({className:e,children:t,value:a,...i}){return(0,r.jsxs)(j.Root,{value:a,"data-slot":"progress",className:(0,w.cn)("flex flex-wrap gap-3",e),...i,children:[t,(0,r.jsx)(M,{children:(0,r.jsx)(C,{})})]})}],77814)},31245,e=>{"use strict";let t=(0,e.i(75254).default)("bot",[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]]);e.s(["Bot",0,t],31245)},63209,e=>{"use strict";let t=(0,e.i(75254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["AlertCircle",0,t],63209)},87951,52044,e=>{"use strict";var t=e.i(75254);let r=(0,t.default)("mic",[["path",{d:"M12 19v3",key:"npa21l"}],["path",{d:"M19 10v2a7 7 0 0 1-14 0v-2",key:"1vc78b"}],["rect",{x:"9",y:"2",width:"6",height:"13",rx:"3",key:"s6n7sd"}]]);e.s(["Mic",0,r],87951);let a=(0,t.default)("square",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]]);e.s(["Square",0,a],52044)},57024,e=>{"use strict";var t=e.i(85699);function r(e){let{type:r,title:a,description:i,duration:n}=e,l=e.id??crypto.randomUUID(),s={id:l,description:i,duration:n};switch(r){case"loading":t.toast.loading(a,s);break;case"success":t.toast.success(a,s);break;case"error":t.toast.error(a,s);break;case"warning":t.toast.warning(a,s);break;case"info":t.toast.info(a,s);break;default:(0,t.toast)(a,s)}return l}e.s(["toaster",0,{create:e=>r(e),update:(e,t)=>{r({...t,id:e})},dismiss:e=>{t.toast.dismiss(e)}}])},58e3,e=>{"use strict";let t=(0,e.i(75254).default)("circle-question-mark",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);e.s(["CircleHelp",0,t],58e3)},64036,e=>{"use strict";var t=e.i(18050),r=e.i(52088);e.s(["Card",0,function({className:e,size:a="default",...i}){return(0,t.jsx)("div",{"data-slot":"card","data-size":a,className:(0,r.cn)("group/card flex flex-col gap-4 overflow-hidden rounded-lg bg-card py-4 text-xs/relaxed text-card-foreground ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg",e),...i})},"CardContent",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-content",className:(0,r.cn)("px-4 group-data-[size=sm]/card:px-3",e),...a})},"CardDescription",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-description",className:(0,r.cn)("text-xs/relaxed text-muted-foreground",e),...a})},"CardHeader",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-header",className:(0,r.cn)("group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-lg px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",e),...a})},"CardTitle",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-title",className:(0,r.cn)("font-heading text-sm font-medium",e),...a})}])},31449,66961,e=>{"use strict";var t=e.i(18050),r=e.i(71645),a=e.i(31278),i=e.i(87951),n=e.i(52044),l=e.i(85902),s=e.i(70935),o=e.i(57024),d=e.i(31245),u=e.i(95468),c=e.i(58e3),f=e.i(64036),m=e.i(68995),p=e.i(77814);let v="tsunagi:local-llm-enabled",g={not_running:"停止中",installing_deps:"依存関係をインストール中...",downloading_model:"モデルをダウンロード中...",starting_server:"サーバーを起動中... (モデルのロードに数十秒かかる場合があります)",running:"実行中",running_external:"実行中 (tsunagi外で起動)",error:"エラー"},x=["installing_deps","downloading_model","starting_server"],h=["running","running_external"];function b(e){let t=e/0x40000000;return t>=.1?`${t.toFixed(2)} GB`:`${(e/1048576).toFixed(0)} MB`}e.s(["LOCAL_LLM_ENABLED_STORAGE_KEY",0,v,"LocalLlmSection",0,function(){let[e,i]=(0,r.useState)(!1),[y,j]=(0,r.useState)(!1),[w,M]=(0,r.useState)(null),C=(0,r.useRef)(null),k=(0,r.useCallback)(e=>{i(e),localStorage.setItem(v,String(e))},[]),N=(0,r.useCallback)(async()=>{let e=await fetch((0,s.apiUrl)("/api/llm/server/status")),t=await e.json();return M(t),t},[]);(0,r.useEffect)(()=>{i("true"===localStorage.getItem(v)),N()},[N]),(0,r.useEffect)(()=>{w&&h.includes(w.step)&&!e&&(k(!0),o.toaster.create({type:"success",title:"ローカルLLMが有効化されました"}))},[w,e,k]),(0,r.useEffect)(()=>(w&&x.includes(w.step)&&!C.current&&(C.current=setInterval(()=>{N().then(e=>{!x.includes(e.step)&&C.current&&(clearInterval(C.current),C.current=null)})},1e3)),()=>{C.current&&(clearInterval(C.current),C.current=null)}),[w,N]);let S=(0,r.useCallback)(()=>{N(),j(!0)},[N]),E=(0,r.useCallback)(async()=>{try{let e=await fetch((0,s.apiUrl)("/api/llm/server/start"),{method:"POST"});if(!e.ok){let t=await e.json().catch(()=>null);throw Error(t?.error||`HTTPエラー: ${e.status}`)}M(await e.json())}catch(e){o.toaster.create({type:"error",title:"サーバーの起動に失敗しました",description:e instanceof Error?e.message:String(e)})}},[]),R=(0,r.useCallback)(async()=>{try{let e=await fetch((0,s.apiUrl)("/api/llm/server/stop"),{method:"POST"});if(!e.ok){let t=await e.json().catch(()=>null);throw Error(t?.error||`HTTPエラー: ${e.status}`)}M(await e.json())}catch(e){o.toaster.create({type:"error",title:"サーバーの停止に失敗しました",description:e instanceof Error?e.message:String(e)})}},[]),L=(0,r.useCallback)(()=>{k(!1),w&&h.includes(w.step)&&R()},[k,R,w]),I=w?.serverDir,O=w?.downloadProgress,P=O?O.downloadedBytes/O.totalBytes*100:0;return(0,t.jsxs)(f.Card,{children:[(0,t.jsxs)(f.CardHeader,{children:[(0,t.jsxs)("div",{className:"flex items-center gap-1",children:[(0,t.jsx)(f.CardTitle,{children:"ローカルLLM (実験的機能)"}),(0,t.jsx)(l.Button,{variant:"ghost",size:"icon-sm",onClick:S,title:"ローカルLLMについて",children:(0,t.jsx)(c.CircleHelp,{})})]}),(0,t.jsxs)(f.CardDescription,{children:["ローカルで動作するLLM(Qwen3-30B-A3B, MoE)を利用します。",(0,t.jsx)("strong",{className:"font-medium text-foreground",children:"有効にすると、音声入力の文字起こし結果がこのLLMで自動整形されるようになります"}),"(無効時は文字起こし結果をそのまま使用)。"]})]}),(0,t.jsx)(f.CardContent,{className:"flex flex-col gap-3",children:e?(0,t.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,t.jsxs)("span",{className:"flex items-center gap-2 text-success",children:[(0,t.jsx)(u.CheckCircle2,{className:"size-4"}),"ローカルLLM: 有効"]}),w&&(0,t.jsxs)("span",{className:"text-xs/relaxed text-muted-foreground",children:["(",g[w.step],")"]}),(0,t.jsx)(l.Button,{size:"default",variant:"outline",onClick:L,children:"無効にする"})]}):(0,t.jsxs)(l.Button,{size:"default",onClick:S,children:[(0,t.jsx)(d.Bot,{}),"ローカルLLMを有効化する"]})}),(0,t.jsx)(m.Dialog,{open:y,onOpenChange:({open:e})=>j(e),title:"ローカルLLMについて",maxWidth:"2xl",children:(0,t.jsxs)("div",{className:"space-y-4 text-sm",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"font-medium text-foreground",children:"実験的機能です"}),(0,t.jsx)("p",{className:"text-muted-foreground",children:"音声入力の文字起こし結果を、ローカルで動作するLLM(mlx-lm / Qwen3-30B-A3B, MoE構成)で整形します。MoE構成のため実計算に使うアクティブパラメータは約3Bで、denseな同規模モデルより高速に動作します。精度・速度は環境に依存し、今後変更される可能性があります。"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"font-medium text-foreground",children:"対応環境"}),(0,t.jsx)("p",{className:"text-muted-foreground",children:"Apple Silicon Mac (M1/M2/M3/M4) のみ対応。Windows/Linuxでは利用できません。メモリ32GB以上を推奨します。"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"font-medium text-foreground",children:"必要なもの(事前に手動でインストール)"}),(0,t.jsx)("ul",{className:"list-disc pl-5 text-muted-foreground",children:(0,t.jsx)("li",{children:"Python 3.9以降(Xcode Command Line Tools または Homebrew経由で入手可能)"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"mb-1 font-medium text-foreground",children:"セットアップ・起動"}),(0,t.jsx)("p",{className:"text-muted-foreground",children:"下のボタンから、依存関係のインストール・モデルのダウンロード・サーバー起動まで自動で行われます(初回はモデルサイズが大きく数分〜数十分かかります)。"}),w&&!I&&(0,t.jsx)("p",{className:"mt-1 text-destructive",children:"llm-serverが見つかりませんでした。インストールが壊れている可能性があります。"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"mb-1 font-medium text-foreground",children:"アンインストール"}),(0,t.jsxs)("p",{className:"text-muted-foreground",children:["ローカルLLMのためにダウンロードされるもの(Pythonの依存関係・LLMモデル、合計約17GB)は全て",(0,t.jsx)("code",{className:"rounded bg-muted px-1",children:"~/.tsunagi/llm"}),"に保存されます。不要になった場合はこのディレクトリを削除するだけで、関連リソースを完全に削除できます。"]})]}),(0,t.jsxs)("div",{className:"border-t border-border pt-4",children:[(!w||"not_running"===w.step||"error"===w.step)&&(0,t.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,t.jsxs)(l.Button,{size:"default",onClick:()=>void E(),disabled:!w,children:[w?(0,t.jsx)(d.Bot,{}):(0,t.jsx)(a.Loader2,{className:"animate-spin"}),"LLMサーバーを起動"]}),w?.step==="error"&&w.error&&(0,t.jsx)("p",{className:"text-xs/relaxed text-destructive",children:w.error})]}),w?.step==="installing_deps"&&(0,t.jsxs)(l.Button,{size:"default",disabled:!0,children:[(0,t.jsx)(a.Loader2,{className:"animate-spin"}),"依存関係をインストール中... (数分かかる場合があります)"]}),w?.step==="downloading_model"&&(0,t.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 text-xs/relaxed text-muted-foreground",children:[(0,t.jsx)(a.Loader2,{className:"size-4 animate-spin"}),"モデルをダウンロード中... (約17GB)"]}),O&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(p.Progress,{value:P}),(0,t.jsxs)("p",{className:"text-xs/relaxed text-muted-foreground",children:[b(O.downloadedBytes)," / ",b(O.totalBytes)," (",P.toFixed(0),"%) ・ ",function(e){if(null===e)return"残り時間を計算中...";if(e<60)return`残り約${e}秒`;let t=Math.floor(e/60);return`残り約${t}分${e%60}秒`}(O.etaSeconds)]})]})]}),w?.step==="starting_server"&&(0,t.jsxs)(l.Button,{size:"default",disabled:!0,children:[(0,t.jsx)(a.Loader2,{className:"animate-spin"}),"サーバーを起動中... (モデルのロードに数十秒かかる場合があります)"]}),w&&h.includes(w.step)&&(0,t.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,t.jsxs)("span",{className:"flex items-center gap-2 text-success",children:[(0,t.jsx)(u.CheckCircle2,{className:"size-4"}),"サーバーは起動しています"]}),(0,t.jsxs)(l.Button,{size:"default",variant:"outline",onClick:()=>void R(),children:[(0,t.jsx)(n.Square,{}),"サーバーを停止"]})]})]})]})})]})}],66961);let y="tsunagi:whisper-prompt",j=["running","running_external"];e.s(["VoiceInputButton",0,function({onTranscribed:e}){let[d,u]=(0,r.useState)(!1),[c,f]=(0,r.useState)("idle"),[m,p]=(0,r.useState)(0),[g,x]=(0,r.useState)(null),h=(0,r.useRef)(null),b=(0,r.useRef)([]),w=(0,r.useRef)(null),M=(0,r.useRef)(null),C=(0,r.useRef)(null),k=(0,r.useRef)(0),N=(0,r.useRef)(0);(0,r.useEffect)(()=>{u("true"===localStorage.getItem("tsunagi:voice-input-enabled"))},[]),(0,r.useEffect)(()=>{if(!d)return;let e=!1,t=async()=>{try{let t=await fetch((0,s.apiUrl)("/api/whisper/server/status")),r=await t.json();e||x(r.step)}catch{e||x("not_running")}};t();let r=setInterval(t,5e3);return()=>{e=!0,clearInterval(r)}},[d]);let S=(0,r.useCallback)(()=>{C.current&&(clearInterval(C.current),C.current=null),M.current?.close(),M.current=null,p(0)},[]);(0,r.useEffect)(()=>()=>{S()},[S]);let E=(0,r.useCallback)(e=>{let t=new AudioContext,r=t.createMediaStreamSource(e),a=t.createAnalyser();a.fftSize=2048,r.connect(a),M.current=t,k.current=0,N.current=0;let i=new Float32Array(a.fftSize);C.current=setInterval(()=>{a.getFloatTimeDomainData(i);let e=0;for(let t=0;t<i.length;t++)e+=i[t]*i[t];let t=Math.sqrt(e/i.length),r=Math.min(1,Math.max(0,((t>0?20*Math.log10(t):-60)- -60)/50));N.current=Math.max(N.current,r),k.current=.3*k.current+.7*r,p(k.current)},50)},[]),R=(0,r.useCallback)(async t=>{f("transcribing");try{let r=new FormData,a=localStorage.getItem(y);a&&r.append("prompt",a);let i="true"===localStorage.getItem(v);r.append("useLlm",String(i)),r.append("file",t,"recording.webm");let n=await fetch((0,s.apiUrl)("/api/whisper/transcribe"),{method:"POST",body:r});if(!n.ok){let e=await n.json().catch(()=>null);throw Error(e?.error||`HTTPエラー: ${n.status}`)}let l=await n.json();l.warning&&o.toaster.create({type:"error",title:"LLM整形をスキップしました",description:l.warning}),l.text&&e(l.text)}catch(e){o.toaster.create({type:"error",title:"文字起こしに失敗しました",description:e instanceof Error?e.message:String(e)})}finally{f("idle")}},[e]),L=(0,r.useCallback)(async()=>{try{let e=await navigator.mediaDevices.getUserMedia({audio:!0});w.current=e,b.current=[],E(e);let t=new MediaRecorder(e,{audioBitsPerSecond:128e3});t.ondataavailable=e=>{e.data.size>0&&b.current.push(e.data)},t.onstop=()=>{w.current?.getTracks().forEach(e=>e.stop()),w.current=null;let e=N.current;if(S(),e<.15){o.toaster.create({type:"error",title:"音声が検出されませんでした",description:"マイクに向かって話してから、もう一度お試しください。"}),f("idle");return}let t=new Blob(b.current,{type:"audio/webm"});R(t)},h.current=t,t.start(),f("recording")}catch(e){o.toaster.create({type:"error",title:"マイクにアクセスできませんでした",description:e instanceof Error?e.message:String(e)})}},[R,E,S]),I=(0,r.useCallback)(()=>{h.current?.stop()},[]);if(!d)return null;let O=null!==g&&j.includes(g);return(0,t.jsxs)("div",{className:"recording"===c?"inline-flex items-center gap-2 rounded-md bg-destructive/10 p-1":"inline-flex",children:[(0,t.jsx)(l.Button,{size:"icon",variant:"recording"===c?"destructive":"default",onClick:"recording"===c?I:()=>void L(),disabled:"transcribing"===c||"idle"===c&&!O,title:"recording"===c?"停止して入力":"idle"!==c||O?"音声入力":"Whisperサーバーが起動していません(Settingsから起動してください)",children:"transcribing"===c?(0,t.jsx)(a.Loader2,{className:"w-4 h-4 animate-spin"}):"recording"===c?(0,t.jsx)(n.Square,{className:"w-4 h-4"}):(0,t.jsx)(i.Mic,{className:"w-4 h-4"})}),"recording"===c&&(0,t.jsx)("div",{"aria-hidden":!0,className:"h-1.5 w-16 overflow-hidden rounded-full bg-background/60",children:(0,t.jsx)("div",{className:"h-full rounded-full bg-destructive",style:{width:`${Math.round(100*m)}%`}})})]})},"WHISPER_PROMPT_STORAGE_KEY",0,y],31449)},51846,e=>{"use strict";var t=e.i(71645);e.s(["useDocumentTitle",0,function(e){(0,t.useEffect)(()=>{let t=e?`繋 | ${e}`:"繋",r=document.title;return document.title=t,()=>{document.title=r}},[e])}])}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,18566,(e,t,r)=>{t.exports=e.r(76562)},43531,78745,e=>{"use strict";let t=(0,e.i(75254).default)("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);e.s(["default",0,t],78745),e.s(["Check",0,t],43531)},86536,e=>{"use strict";let t=(0,e.i(75254).default)("eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);e.s(["Eye",0,t],86536)},27612,e=>{"use strict";let t=(0,e.i(75254).default)("trash-2",[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]]);e.s(["Trash2",0,t],27612)},10012,24564,94349,e=>{"use strict";var t,r=e.i(18050);e.i(47167);var a=e.i(71645);e.s([],19485),e.i(19485);var i=e.i(76477),n=e.i(67805),l=e.i(17514);let s=((t={}).disabled="data-disabled",t.valid="data-valid",t.invalid="data-invalid",t.touched="data-touched",t.dirty="data-dirty",t.filled="data-filled",t.focused="data-focused",t),o={badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:null,valueMissing:!1},d={valid:null,touched:!1,dirty:!1,filled:!1,focused:!1},u={disabled:!1,...d},c={valid:e=>null===e?null:e?{[s.valid]:""}:{[s.invalid]:""}},f=a.createContext({invalid:void 0,name:void 0,validityData:{state:o,errors:[],error:"",value:"",initialValue:null},setValidityData:l.NOOP,disabled:void 0,touched:d.touched,setTouched:l.NOOP,dirty:d.dirty,setDirty:l.NOOP,filled:d.filled,setFilled:l.NOOP,focused:d.focused,setFocused:l.NOOP,validate:()=>null,validationMode:"onSubmit",validationDebounceTime:0,shouldValidateOnChange:()=>!1,state:u,markedDirtyRef:{current:!1},validation:{getValidationProps:(e=l.EMPTY_OBJECT)=>e,getInputValidationProps:(e=l.EMPTY_OBJECT)=>e,inputRef:{current:null},commit:async()=>{}}});function m(e=!0){let t=a.useContext(f);if(t.setValidityData===l.NOOP&&!e)throw Error((0,n.default)(28));return t}let p=a.createContext({legendId:void 0,setLegendId:()=>{},disabled:void 0}),v=a.createContext({formRef:{current:{fields:new Map}},errors:{},clearErrors:l.NOOP,validationMode:"onSubmit",submitAttemptedRef:{current:!1}});function g(){return a.useContext(v)}var x=e.i(30628),h=e.i(19805),b=e.i(89879);let y=a.createContext({controlId:void 0,registerControlId:l.NOOP,labelId:void 0,setLabelId:l.NOOP,messageIds:[],setMessageIds:l.NOOP,getDescriptionProps:e=>e});function j(){return a.useContext(y)}let w=function(e){let t=(0,b.useBaseUiId)(),n=void 0===e.controlId?t:e.controlId,[l,s]=a.useState(n),[o,d]=a.useState(e.labelId),[u,c]=a.useState([]),f=(0,x.useRefWithInit)(()=>new Map),{messageIds:m}=j(),p=(0,i.useStableCallback)((e,t)=>{let r=f.current;void 0===t?r.delete(e):(r.set(e,t),s(e=>{let t;if(0!==r.size){for(let a of r.values()){if(void 0!==e&&a===e)return e;void 0===t&&(t=a)}return t}}))}),v=a.useCallback(e=>(0,h.mergeProps)({"aria-describedby":m.concat(u).join(" ")||void 0},e),[m,u]),g=a.useMemo(()=>({controlId:l,registerControlId:p,labelId:o,setLabelId:d,messageIds:u,setMessageIds:c,getDescriptionProps:v}),[l,p,o,d,u,c,v]);return(0,r.jsx)(y.Provider,{value:g,children:e.children})};var M=e.i(27601),C=e.i(66745);function k(e,t){return{...e,state:{...e.state,valid:!t&&e.state.valid}}}let N=Object.keys(o),S=a.forwardRef(function(e,t){let{errors:s,validationMode:d,submitAttemptedRef:u}=g(),{render:m,className:v,validate:x,validationDebounceTime:b=0,validationMode:y=d,name:w,disabled:S=!1,invalid:E,dirty:R,touched:L,actionsRef:I,...O}=e,{disabled:P}=function(e=!1){let t=a.useContext(p);if(!t&&!e)throw Error((0,n.default)(86));return t}(),T=(0,i.useStableCallback)(x||(()=>null)),B=P||S,[A,V]=a.useState(!1),[D,_]=a.useState(!1),[z,F]=a.useState(!1),[U,H]=a.useState(!1),$=R??D,q=L??A,G=a.useRef(!1),J=(0,i.useStableCallback)(e=>{void 0===R&&(e&&(G.current=!0),_(e))}),W=(0,i.useStableCallback)(e=>{void 0===L&&V(e)}),K=(0,i.useStableCallback)(()=>"onChange"===y||"onSubmit"===y&&u.current),Y=!!w&&Object.hasOwn(s,w)&&void 0!==s[w],Q=!0===E||Y,[X,Z]=a.useState({state:o,error:"",errors:[],value:null,initialValue:null}),ee=!Q&&X.state.valid,et=a.useMemo(()=>({disabled:B,touched:q,dirty:$,valid:ee,filled:z,focused:U}),[B,q,$,ee,z,U]),er=function(e){let{formRef:t,clearErrors:r}=g(),{setValidityData:n,validate:s,validityData:d,validationDebounceTime:u,invalid:c,markedDirtyRef:f,state:m,name:p,shouldValidateOnChange:v}=e,{controlId:x,getDescriptionProps:b}=j(),y=(0,C.useTimeout)(),w=a.useRef(null),M=(0,i.useStableCallback)(async(e,r=!1)=>{let a,i=w.current;if(!i)return;if(r){if(!1!==m.valid)return;let r=i.validity;if(!r.valueMissing){let r={value:e,state:{...o,valid:!0},error:"",errors:[],initialValue:d.initialValue};if(i.setCustomValidity(""),x){let e=t.current.fields.get(x);e&&t.current.fields.set(x,{...e,...k(r,!1)})}n(r);return}let a=N.reduce((e,t)=>(e[t]=r[t],e),{});if(!a.valid&&!function(e){if(!e||e.valid||!e.valueMissing)return!1;let t=!1;for(let r of N)"valid"!==r&&("valueMissing"===r&&(t=e[r]),e[r]&&(t=!1));return t}(a))return}y.clear();let l=null,u=[],p=function(e){let t=N.reduce((t,r)=>(t[r]=e.validity[r],t),{}),r=!1;for(let e of N)if("valid"!==e){if("valueMissing"===e&&t[e])r=!0;else if(t[e])return t}return r&&!f.current&&(t.valid=!0,t.valueMissing=!1),t}(i),g=v();if(i.validationMessage&&!g)a=i.validationMessage,u=[i.validationMessage];else{let r=s(e,Array.from(t.current.fields.values()).reduce((e,t)=>(t.name&&(e[t.name]=t.getValue()),e),{}));null!==(l="object"==typeof r&&null!==r&&"then"in r?await r:r)?(p.valid=!1,p.customError=!0,Array.isArray(l)?(u=l,i.setCustomValidity(l.join("\n"))):l&&(u=[l],i.setCustomValidity(l))):g&&(i.setCustomValidity(""),p.customError=!1,i.validationMessage?(a=i.validationMessage,u=[i.validationMessage]):i.validity.valid&&!p.valid&&(p.valid=!0))}let h={value:e,state:p,error:a??(Array.isArray(l)?l[0]:l??""),errors:u,initialValue:d.initialValue};if(x){let e=t.current.fields.get(x);e&&t.current.fields.set(x,{...e,...k(h,c)})}n(h)}),S=a.useCallback((e={})=>(0,h.mergeProps)(b,!1===m.valid?{"aria-invalid":!0}:l.EMPTY_OBJECT,e),[b,m.valid]),E=a.useCallback((e={})=>(0,h.mergeProps)({onChange(e){if(e.nativeEvent.defaultPrevented)return;if(r(p),!v())return void M(e.currentTarget.value,!0);let t=e.currentTarget;""===t.value?M(t.value):(y.clear(),u?y.start(u,()=>{M(t.value)}):M(t.value))}},S(e)),[S,r,p,y,M,u,v]);return a.useMemo(()=>({getValidationProps:S,getInputValidationProps:E,inputRef:w,commit:M}),[S,E,M])}({setValidityData:Z,validate:T,validityData:X,validationDebounceTime:b,invalid:Q,markedDirtyRef:G,state:et,name:w,shouldValidateOnChange:K}),ea=a.useCallback(()=>{G.current=!0,er.commit(X.value)},[er,X]);a.useImperativeHandle(I,()=>({validate:ea}),[ea]);let ei=a.useMemo(()=>({invalid:Q,name:w,validityData:X,setValidityData:Z,disabled:B,touched:q,setTouched:W,dirty:$,setDirty:J,filled:z,setFilled:F,focused:U,setFocused:H,validate:T,validationMode:y,validationDebounceTime:b,shouldValidateOnChange:K,state:et,markedDirtyRef:G,validation:er}),[Q,w,X,B,q,W,$,J,z,F,U,H,T,y,b,K,et,er]),en=(0,M.useRenderElement)("div",e,{ref:t,state:et,props:O,stateAttributesMapping:c});return(0,r.jsx)(f.Provider,{value:ei,children:en})}),E=a.forwardRef(function(e,t){return(0,r.jsx)(w,{children:(0,r.jsx)(S,{...e,ref:t})})});var R=e.i(29315),L=e.i(90219),I=e.i(47857),O=e.i(977);function P(e,t){let r=(0,b.useBaseUiId)(e);return(0,O.useIsoLayoutEffect)(()=>(t(r),()=>{t(void 0)}),[r,t]),r}e.s(["useRegisteredLabelId",0,P],24564);let T=a.forwardRef(function(e,t){let{render:r,className:n,id:l,nativeLabel:s=!0,...o}=e,d=m(!1),{labelId:u}=j(),f=a.useRef(null),p=function(e={}){let{id:t,fallbackControlId:r,native:a=!1,setLabelId:n,focusControl:l}=e,{controlId:s,setLabelId:o}=j(),d=P(t,(0,i.useStableCallback)(e=>{o(e),n?.(e)})),u=s??r;function c(e){let t=(0,I.getTarget)(e.nativeEvent);t?.closest("button,input,select,textarea")||(!e.defaultPrevented&&e.detail>1&&e.preventDefault(),a||function(e){if(l)return l(e,u);if(!u)return;let t=(0,L.ownerDocument)(e.currentTarget).getElementById(u);(0,R.isHTMLElement)(t)&&t.focus({focusVisible:!0})}(e))}return a?{id:d,htmlFor:u??void 0,onMouseDown:c}:{id:d,onClick:c,onPointerDown(e){e.preventDefault()}}}({id:u??l,native:s});return(0,M.useRenderElement)("label",e,{ref:[t,f],state:d.state,props:[p,o],stateAttributesMapping:c})});var B=e.i(89269),A=e.i(84759),V=e.i(88841);let D={...c,...A.transitionStatusMapping},_=a.forwardRef(function(e,t){let{render:i,id:n,className:l,match:s,...o}=e,d=(0,b.useBaseUiId)(n),{validityData:u,state:c,name:f}=m(!1),{setMessageIds:p}=j(),{errors:v}=g(),x=f?v[f]:null,h=!1;h=!!x||!0===s||(s?!!u.state[s]:!1===u.state.valid);let{mounted:y,transitionStatus:w,setMounted:C}=(0,V.useTransitionStatus)(h);(0,O.useIsoLayoutEffect)(()=>{if(h&&d)return p(e=>e.concat(d)),()=>{p(e=>e.filter(e=>e!==d))}},[h,d,p]);let k=a.useRef(null),[N,S]=a.useState(null),[E,R]=a.useState(null),L=x||(u.errors.length>1?(0,r.jsx)("ul",{children:u.errors.map(e=>(0,r.jsx)("li",{children:e},e))}):u.error),I=u.error;null!=x?I=Array.isArray(x)?JSON.stringify(x):x:u.errors.length>1&&(I=JSON.stringify(u.errors)),h&&I!==E&&(R(I),S(L)),(0,B.useOpenChangeComplete)({open:h,ref:k,onComplete(){h||C(!1)}});let P={...c,transitionStatus:w},T=(0,M.useRenderElement)("div",e,{ref:[t,k],state:P,props:[{id:d,children:h?L:N},o],stateAttributesMapping:D,enabled:y});return y?T:null}),z=a.forwardRef(function(e,t){let{render:r,id:a,className:i,...n}=e,l=(0,b.useBaseUiId)(a),s=m(!1),{setMessageIds:o}=j();return(0,O.useIsoLayoutEffect)(()=>{if(l)return o(e=>e.concat(l)),()=>{o(e=>e.filter(e=>e!==l))}},[l,o]),(0,M.useRenderElement)("p",e,{ref:t,state:s.state,props:[{id:l},n],stateAttributesMapping:c})});function F({controlled:e,default:t,name:r,state:i="value"}){let{current:n}=a.useRef(void 0!==e),[l,s]=a.useState(t),o=a.useCallback(e=>{n||s(e)},[]);return[n?e:l,o]}e.s(["useControlled",0,F],94349);var U=e.i(74080),H=e.i(5963),$=e.i(3404);let q=a.forwardRef(function(e,t){let{render:r,className:n,id:s,name:o,value:d,disabled:u=!1,onValueChange:f,defaultValue:p,autoFocus:v=!1,...h}=e,{state:y,name:w,disabled:C,setTouched:N,setDirty:S,validityData:E,setFocused:P,setFilled:T,validationMode:B,validation:A}=m(),V=C||u,D=w??o,_={...y,disabled:V},{labelId:z}=j(),q=function(e={}){let{id:t,implicit:r=!1,controlRef:n}=e,{controlId:s,registerControlId:o}=j(),d=(0,b.useBaseUiId)(t),u=r?s:void 0,c=(0,x.useRefWithInit)(()=>Symbol("labelable-control")),f=a.useRef(!1),m=a.useRef(null!=t),p=(0,i.useStableCallback)(()=>{f.current&&o!==l.NOOP&&(f.current=!1,o(c.current,void 0))});return(0,O.useIsoLayoutEffect)(()=>{let e;if(o!==l.NOOP){if(r){let r=n?.current;e=(0,R.isElement)(r)&&null!=r.closest("label")?t??null:u??d}else if(null!=t)m.current=!0,e=t;else{if(!m.current)return void p();e=d}if(void 0===e)return void p();f.current=!0,o(c.current,e)}},[t,n,u,o,r,d,c,p]),a.useEffect(()=>p,[p]),s??d}({id:s});(0,O.useIsoLayoutEffect)(()=>{let e=null!=d;A.inputRef.current?.value||e&&""!==d?T(!0):e&&""===d&&T(!1)},[A.inputRef,T,d]);let G=a.useRef(null);(0,O.useIsoLayoutEffect)(()=>{v&&G.current===(0,I.activeElement)((0,L.ownerDocument)(G.current))&&P(!0)},[v,P]);let[J]=F({controlled:d,default:p,name:"FieldControl",state:"value"}),W=void 0!==d,K=W?J:void 0;return!function(e){let{enabled:t=!0,value:r,id:a,name:n,controlRef:l,commit:s}=e,{formRef:o}=g(),{invalid:d,markedDirtyRef:u,validityData:c,setValidityData:f}=m(),p=(0,i.useStableCallback)(e.getValue);(0,O.useIsoLayoutEffect)(()=>{if(!t)return;let e=r;void 0===e&&(e=p()),null===c.initialValue&&null!==e&&f(t=>({...t,initialValue:e}))},[t,f,r,c.initialValue,p]),(0,O.useIsoLayoutEffect)(()=>{t&&a&&o.current.fields.set(a,{getValue:p,name:n,controlRef:l,validityData:k(c,d),validate(e=!0){let t=r;void 0===t&&(t=p()),u.current=!0,e?U.flushSync(()=>s(t)):s(t)}})},[s,l,t,o,p,a,d,u,n,c,r]),(0,O.useIsoLayoutEffect)(()=>{let e=o.current.fields;return()=>{a&&e.delete(a)}},[o,a])}({id:q,name:D,commit:A.commit,value:K,getValue:()=>A.inputRef.current?.value,controlRef:A.inputRef}),(0,M.useRenderElement)("input",e,{ref:[t,G],state:_,props:[{id:q,disabled:V,name:D,ref:A.inputRef,"aria-labelledby":z,autoFocus:v,...W?{value:K}:{defaultValue:p},onChange(e){let t=e.currentTarget.value;f?.(t,(0,H.createChangeEventDetails)($.REASONS.none,e.nativeEvent)),S(t!==E.initialValue),T(""!==t)},onFocus(){P(!0)},onBlur(e){N(!0),P(!1),"onBlur"===B&&A.commit(e.currentTarget.value)},onKeyDown(e){"INPUT"===e.currentTarget.tagName&&"Enter"===e.key&&(N(!0),A.commit(e.currentTarget.value))}},A.getInputValidationProps(),h],stateAttributesMapping:c})}),G=a.createContext({disabled:!1}),J=a.createContext(void 0),W=a.forwardRef(function(e,t){let{render:i,className:l,disabled:s=!1,...o}=e,{state:d,disabled:u}=m(!1),f=u||s,p=function(e=!0){let t=a.useContext(J);if(void 0===t&&!e)throw Error((0,n.default)(3));return t}(),v=p?.parent.id,g=p?.allValues!==void 0,x=a.useMemo(()=>({disabled:f}),[f]),h=(0,M.useRenderElement)("div",e,{ref:t,state:d,props:o,stateAttributesMapping:c});return(0,r.jsx)(w,{controlId:g?v:void 0,children:(0,r.jsx)(G.Provider,{value:x,children:h})})});e.s(["Control",0,q,"Description",0,z,"Error",0,_,"Item",0,W,"Label",0,T,"Root",0,E,"Validity",0,function(e){let{children:t}=e,{validityData:i,invalid:n}=m(!1),l=a.useMemo(()=>k(i,n),[i,n]),s=!1===l.state.valid,{transitionStatus:o}=(0,V.useTransitionStatus)(s),d=a.useMemo(()=>({...l,validity:l.state,transitionStatus:o}),[l,o]);return(0,r.jsx)(a.Fragment,{children:t(d)})}],71975);var K=e.i(71975),K=K;let Y=a.forwardRef(function(e,t){return(0,r.jsx)(K.Control,{ref:t,...e})});var Q=e.i(52088);e.s(["Input",0,function({className:e,type:t,...a}){return(0,r.jsx)(Y,{type:t,"data-slot":"input",className:(0,Q.cn)("h-9 w-full min-w-0 rounded-md border border-input bg-input/20 px-3 py-1 text-sm transition-colors outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",e),...a})}],10012)},37870,31278,e=>{"use strict";var t=e.i(18050),r=e.i(17521);e.s(["Loader2",()=>r.default],31278);var r=r;e.s(["LoadingSpinner",0,function({size:e="md",message:a}){return(0,t.jsxs)("div",{className:"flex flex-col items-center justify-center gap-3",children:[(0,t.jsx)(r.default,{className:`${{sm:"w-4 h-4",md:"w-8 h-8",lg:"w-12 h-12"}[e]} text-primary animate-spin`}),a&&(0,t.jsx)("p",{className:"text-sm text-muted-foreground",children:a})]})}],37870)},63209,e=>{"use strict";let t=(0,e.i(75254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["AlertCircle",0,t],63209)},95468,e=>{"use strict";var t=e.i(23287);e.s(["CheckCircle2",()=>t.default])},58e3,e=>{"use strict";let t=(0,e.i(75254).default)("circle-question-mark",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);e.s(["CircleHelp",0,t],58e3)},87951,52044,e=>{"use strict";var t=e.i(75254);let r=(0,t.default)("mic",[["path",{d:"M12 19v3",key:"npa21l"}],["path",{d:"M19 10v2a7 7 0 0 1-14 0v-2",key:"1vc78b"}],["rect",{x:"9",y:"2",width:"6",height:"13",rx:"3",key:"s6n7sd"}]]);e.s(["Mic",0,r],87951);let a=(0,t.default)("square",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]]);e.s(["Square",0,a],52044)},64036,e=>{"use strict";var t=e.i(18050),r=e.i(52088);e.s(["Card",0,function({className:e,size:a="default",...i}){return(0,t.jsx)("div",{"data-slot":"card","data-size":a,className:(0,r.cn)("group/card flex flex-col gap-4 overflow-hidden rounded-lg bg-card py-4 text-xs/relaxed text-card-foreground ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg",e),...i})},"CardContent",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-content",className:(0,r.cn)("px-4 group-data-[size=sm]/card:px-3",e),...a})},"CardDescription",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-description",className:(0,r.cn)("text-xs/relaxed text-muted-foreground",e),...a})},"CardHeader",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-header",className:(0,r.cn)("group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-lg px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",e),...a})},"CardTitle",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-title",className:(0,r.cn)("font-heading text-sm font-medium",e),...a})}])},77814,e=>{"use strict";var t,r=e.i(18050);e.s([],33847),e.i(33847),e.i(47167);var a=e.i(71645),i=e.i(231),n=e.i(62372);let l=new Map;function s(e,t,r){return null==e?"":(function(e,t){let r=JSON.stringify({locale:e,options:t}),a=l.get(r);if(a)return a;let i=new Intl.NumberFormat(e,t);return l.set(r,i),i})(t,r).format(e)}var o=e.i(27601),d=e.i(67805);let u=a.createContext(void 0);function c(){let e=a.useContext(u);if(void 0===e)throw Error((0,d.default)(51));return e}let f=((t={}).complete="data-complete",t.indeterminate="data-indeterminate",t.progressing="data-progressing",t),m={status:e=>"progressing"===e?{[f.progressing]:""}:"complete"===e?{[f.complete]:""}:"indeterminate"===e?{[f.indeterminate]:""}:null};function p(e,t){return null==t?"indeterminate progress":e||`${t}%`}let v=a.forwardRef(function(e,t){var l;let{format:d,getAriaValueText:c=p,locale:f,max:v=100,min:g=0,value:x,render:h,className:b,children:y,...j}=e,[w,M]=a.useState(),C=(0,i.useValueAsRef)(d),k="indeterminate";Number.isFinite(x)&&(k=x===v?"complete":"progressing");let N=(l=C.current,null==x?"":l?s(x,f,l):s(x/100,f,{style:"percent"})),S=a.useMemo(()=>({status:k}),[k]),E={"aria-labelledby":w,"aria-valuemax":v,"aria-valuemin":g,"aria-valuenow":x??void 0,"aria-valuetext":c(N,x),role:"progressbar",children:(0,r.jsxs)(a.Fragment,{children:[y,(0,r.jsx)("span",{role:"presentation",style:n.visuallyHidden,children:"x"})]})},R=a.useMemo(()=>({formattedValue:N,max:v,min:g,setLabelId:M,state:S,status:k,value:x}),[N,v,g,M,S,k,x]),L=(0,o.useRenderElement)("div",e,{state:S,ref:t,props:[E,j],stateAttributesMapping:m});return(0,r.jsx)(u.Provider,{value:R,children:L})}),g=a.forwardRef(function(e,t){let{render:r,className:a,...i}=e,{state:n}=c();return(0,o.useRenderElement)("div",e,{state:n,ref:t,props:i,stateAttributesMapping:m})}),x=a.forwardRef(function(e,t){let{render:r,className:i,...n}=e,{max:l,min:s,value:d,state:u}=c(),f=Number.isFinite(d)&&null!==d?(d-s)*100/(l-s):null,p=a.useCallback(()=>null==f?{}:{insetInlineStart:0,height:"inherit",width:`${f}%`},[f]);return(0,o.useRenderElement)("div",e,{state:u,ref:t,props:[{style:p()},n],stateAttributesMapping:m})}),h=a.forwardRef(function(e,t){let{className:r,render:a,children:i,...n}=e,{value:l,formattedValue:s,state:d}=c(),u=null==l?"indeterminate":s,f=null==l?null:s;return(0,o.useRenderElement)("span",e,{state:d,ref:t,props:[{"aria-hidden":!0,children:"function"==typeof i?i(u,l):f},n],stateAttributesMapping:m})});var b=e.i(24564);let y=a.forwardRef(function(e,t){let{render:r,className:a,id:i,...n}=e,{setLabelId:l,state:s}=c(),d=(0,b.useRegisteredLabelId)(i,l);return(0,o.useRenderElement)("span",e,{state:s,ref:t,props:[{id:d,role:"presentation"},n],stateAttributesMapping:m})});e.s(["Indicator",0,x,"Label",0,y,"Root",0,v,"Track",0,g,"Value",0,h],45929);var j=e.i(45929),j=j,w=e.i(52088);function M({className:e,...t}){return(0,r.jsx)(j.Track,{className:(0,w.cn)("relative flex h-1 w-full items-center overflow-x-hidden rounded-md bg-muted",e),"data-slot":"progress-track",...t})}function C({className:e,...t}){return(0,r.jsx)(j.Indicator,{"data-slot":"progress-indicator",className:(0,w.cn)("h-full bg-primary",e),style:{transition:"width 500ms ease-in-out"},...t})}e.s(["Progress",0,function({className:e,children:t,value:a,...i}){return(0,r.jsxs)(j.Root,{value:a,"data-slot":"progress",className:(0,w.cn)("flex flex-wrap gap-3",e),...i,children:[t,(0,r.jsx)(M,{children:(0,r.jsx)(C,{})})]})}],77814)},41247,e=>{"use strict";var t=e.i(18050),r=e.i(52088);e.s(["Textarea",0,function({className:e,...a}){return(0,t.jsx)("textarea",{"data-slot":"textarea",className:(0,r.cn)("flex field-sizing-content min-h-16 w-full resize-none rounded-md border border-input bg-input/20 px-2 py-2 text-sm transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-xs/relaxed dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",e),...a})}])},57024,e=>{"use strict";var t=e.i(85699);function r(e){let{type:r,title:a,description:i,duration:n}=e,l=e.id??crypto.randomUUID(),s={id:l,description:i,duration:n};switch(r){case"loading":t.toast.loading(a,s);break;case"success":t.toast.success(a,s);break;case"error":t.toast.error(a,s);break;case"warning":t.toast.warning(a,s);break;case"info":t.toast.info(a,s);break;default:(0,t.toast)(a,s)}return l}e.s(["toaster",0,{create:e=>r(e),update:(e,t)=>{r({...t,id:e})},dismiss:e=>{t.toast.dismiss(e)}}])},31245,e=>{"use strict";let t=(0,e.i(75254).default)("bot",[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]]);e.s(["Bot",0,t],31245)},31449,66961,e=>{"use strict";var t=e.i(18050),r=e.i(71645),a=e.i(31278),i=e.i(87951),n=e.i(52044),l=e.i(85902),s=e.i(70935),o=e.i(57024),d=e.i(31245),u=e.i(95468),c=e.i(58e3),f=e.i(64036),m=e.i(68995),p=e.i(77814);let v="tsunagi:local-llm-enabled",g={not_running:"停止中",installing_deps:"依存関係をインストール中...",downloading_model:"モデルをダウンロード中...",starting_server:"サーバーを起動中... (モデルのロードに数十秒かかる場合があります)",running:"実行中",running_external:"実行中 (tsunagi外で起動)",error:"エラー"},x=["installing_deps","downloading_model","starting_server"],h=["running","running_external"];function b(e){let t=e/0x40000000;return t>=.1?`${t.toFixed(2)} GB`:`${(e/1048576).toFixed(0)} MB`}e.s(["LOCAL_LLM_ENABLED_STORAGE_KEY",0,v,"LocalLlmSection",0,function(){let[e,i]=(0,r.useState)(!1),[y,j]=(0,r.useState)(!1),[w,M]=(0,r.useState)(null),C=(0,r.useRef)(null),k=(0,r.useCallback)(e=>{i(e),localStorage.setItem(v,String(e))},[]),N=(0,r.useCallback)(async()=>{let e=await fetch((0,s.apiUrl)("/api/llm/server/status")),t=await e.json();return M(t),t},[]);(0,r.useEffect)(()=>{i("true"===localStorage.getItem(v)),N()},[N]),(0,r.useEffect)(()=>{w&&h.includes(w.step)&&!e&&(k(!0),o.toaster.create({type:"success",title:"ローカルLLMが有効化されました"}))},[w,e,k]),(0,r.useEffect)(()=>(w&&x.includes(w.step)&&!C.current&&(C.current=setInterval(()=>{N().then(e=>{!x.includes(e.step)&&C.current&&(clearInterval(C.current),C.current=null)})},1e3)),()=>{C.current&&(clearInterval(C.current),C.current=null)}),[w,N]);let S=(0,r.useCallback)(()=>{N(),j(!0)},[N]),E=(0,r.useCallback)(async()=>{try{let e=await fetch((0,s.apiUrl)("/api/llm/server/start"),{method:"POST"});if(!e.ok){let t=await e.json().catch(()=>null);throw Error(t?.error||`HTTPエラー: ${e.status}`)}M(await e.json())}catch(e){o.toaster.create({type:"error",title:"サーバーの起動に失敗しました",description:e instanceof Error?e.message:String(e)})}},[]),R=(0,r.useCallback)(async()=>{try{let e=await fetch((0,s.apiUrl)("/api/llm/server/stop"),{method:"POST"});if(!e.ok){let t=await e.json().catch(()=>null);throw Error(t?.error||`HTTPエラー: ${e.status}`)}M(await e.json())}catch(e){o.toaster.create({type:"error",title:"サーバーの停止に失敗しました",description:e instanceof Error?e.message:String(e)})}},[]),L=(0,r.useCallback)(()=>{k(!1),w&&h.includes(w.step)&&R()},[k,R,w]),I=w?.serverDir,O=w?.downloadProgress,P=O?O.downloadedBytes/O.totalBytes*100:0;return(0,t.jsxs)(f.Card,{children:[(0,t.jsxs)(f.CardHeader,{children:[(0,t.jsxs)("div",{className:"flex items-center gap-1",children:[(0,t.jsx)(f.CardTitle,{children:"ローカルLLM (実験的機能)"}),(0,t.jsx)(l.Button,{variant:"ghost",size:"icon-sm",onClick:S,title:"ローカルLLMについて",children:(0,t.jsx)(c.CircleHelp,{})})]}),(0,t.jsxs)(f.CardDescription,{children:["ローカルで動作するLLM(Qwen3-30B-A3B, MoE)を利用します。",(0,t.jsx)("strong",{className:"font-medium text-foreground",children:"有効にすると、音声入力の文字起こし結果がこのLLMで自動整形されるようになります"}),"(無効時は文字起こし結果をそのまま使用)。"]})]}),(0,t.jsx)(f.CardContent,{className:"flex flex-col gap-3",children:e?(0,t.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,t.jsxs)("span",{className:"flex items-center gap-2 text-success",children:[(0,t.jsx)(u.CheckCircle2,{className:"size-4"}),"ローカルLLM: 有効"]}),w&&(0,t.jsxs)("span",{className:"text-xs/relaxed text-muted-foreground",children:["(",g[w.step],")"]}),(0,t.jsx)(l.Button,{size:"default",variant:"outline",onClick:L,children:"無効にする"})]}):(0,t.jsxs)(l.Button,{size:"default",onClick:S,children:[(0,t.jsx)(d.Bot,{}),"ローカルLLMを有効化する"]})}),(0,t.jsx)(m.Dialog,{open:y,onOpenChange:({open:e})=>j(e),title:"ローカルLLMについて",maxWidth:"2xl",children:(0,t.jsxs)("div",{className:"space-y-4 text-sm",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"font-medium text-foreground",children:"実験的機能です"}),(0,t.jsx)("p",{className:"text-muted-foreground",children:"音声入力の文字起こし結果を、ローカルで動作するLLM(mlx-lm / Qwen3-30B-A3B, MoE構成)で整形します。MoE構成のため実計算に使うアクティブパラメータは約3Bで、denseな同規模モデルより高速に動作します。精度・速度は環境に依存し、今後変更される可能性があります。"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"font-medium text-foreground",children:"対応環境"}),(0,t.jsx)("p",{className:"text-muted-foreground",children:"Apple Silicon Mac (M1/M2/M3/M4) のみ対応。Windows/Linuxでは利用できません。メモリ32GB以上を推奨します。"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"font-medium text-foreground",children:"必要なもの(事前に手動でインストール)"}),(0,t.jsx)("ul",{className:"list-disc pl-5 text-muted-foreground",children:(0,t.jsx)("li",{children:"Python 3.9以降(Xcode Command Line Tools または Homebrew経由で入手可能)"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"mb-1 font-medium text-foreground",children:"セットアップ・起動"}),(0,t.jsx)("p",{className:"text-muted-foreground",children:"下のボタンから、依存関係のインストール・モデルのダウンロード・サーバー起動まで自動で行われます(初回はモデルサイズが大きく数分〜数十分かかります)。"}),w&&!I&&(0,t.jsx)("p",{className:"mt-1 text-destructive",children:"llm-serverが見つかりませんでした。インストールが壊れている可能性があります。"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"mb-1 font-medium text-foreground",children:"アンインストール"}),(0,t.jsxs)("p",{className:"text-muted-foreground",children:["ローカルLLMのためにダウンロードされるもの(Pythonの依存関係・LLMモデル、合計約17GB)は全て",(0,t.jsx)("code",{className:"rounded bg-muted px-1",children:"~/.tsunagi/llm"}),"に保存されます。不要になった場合はこのディレクトリを削除するだけで、関連リソースを完全に削除できます。"]})]}),(0,t.jsxs)("div",{className:"border-t border-border pt-4",children:[(!w||"not_running"===w.step||"error"===w.step)&&(0,t.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,t.jsxs)(l.Button,{size:"default",onClick:()=>void E(),disabled:!w,children:[w?(0,t.jsx)(d.Bot,{}):(0,t.jsx)(a.Loader2,{className:"animate-spin"}),"LLMサーバーを起動"]}),w?.step==="error"&&w.error&&(0,t.jsx)("p",{className:"text-xs/relaxed text-destructive",children:w.error})]}),w?.step==="installing_deps"&&(0,t.jsxs)(l.Button,{size:"default",disabled:!0,children:[(0,t.jsx)(a.Loader2,{className:"animate-spin"}),"依存関係をインストール中... (数分かかる場合があります)"]}),w?.step==="downloading_model"&&(0,t.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 text-xs/relaxed text-muted-foreground",children:[(0,t.jsx)(a.Loader2,{className:"size-4 animate-spin"}),"モデルをダウンロード中... (約17GB)"]}),O&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(p.Progress,{value:P}),(0,t.jsxs)("p",{className:"text-xs/relaxed text-muted-foreground",children:[b(O.downloadedBytes)," / ",b(O.totalBytes)," (",P.toFixed(0),"%) ・ ",function(e){if(null===e)return"残り時間を計算中...";if(e<60)return`残り約${e}秒`;let t=Math.floor(e/60);return`残り約${t}分${e%60}秒`}(O.etaSeconds)]})]})]}),w?.step==="starting_server"&&(0,t.jsxs)(l.Button,{size:"default",disabled:!0,children:[(0,t.jsx)(a.Loader2,{className:"animate-spin"}),"サーバーを起動中... (モデルのロードに数十秒かかる場合があります)"]}),w&&h.includes(w.step)&&(0,t.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,t.jsxs)("span",{className:"flex items-center gap-2 text-success",children:[(0,t.jsx)(u.CheckCircle2,{className:"size-4"}),"サーバーは起動しています"]}),(0,t.jsxs)(l.Button,{size:"default",variant:"outline",onClick:()=>void R(),children:[(0,t.jsx)(n.Square,{}),"サーバーを停止"]})]})]})]})})]})}],66961);let y="tsunagi:whisper-prompt",j=["running","running_external"];e.s(["VoiceInputButton",0,function({onTranscribed:e}){let[d,u]=(0,r.useState)(!1),[c,f]=(0,r.useState)("idle"),[m,p]=(0,r.useState)(0),[g,x]=(0,r.useState)(null),h=(0,r.useRef)(null),b=(0,r.useRef)([]),w=(0,r.useRef)(null),M=(0,r.useRef)(null),C=(0,r.useRef)(null),k=(0,r.useRef)(0),N=(0,r.useRef)(0);(0,r.useEffect)(()=>{u("true"===localStorage.getItem("tsunagi:voice-input-enabled"))},[]),(0,r.useEffect)(()=>{if(!d)return;let e=!1,t=async()=>{try{let t=await fetch((0,s.apiUrl)("/api/whisper/server/status")),r=await t.json();e||x(r.step)}catch{e||x("not_running")}};t();let r=setInterval(t,5e3);return()=>{e=!0,clearInterval(r)}},[d]);let S=(0,r.useCallback)(()=>{C.current&&(clearInterval(C.current),C.current=null),M.current?.close(),M.current=null,p(0)},[]);(0,r.useEffect)(()=>()=>{S()},[S]);let E=(0,r.useCallback)(e=>{let t=new AudioContext,r=t.createMediaStreamSource(e),a=t.createAnalyser();a.fftSize=2048,r.connect(a),M.current=t,k.current=0,N.current=0;let i=new Float32Array(a.fftSize);C.current=setInterval(()=>{a.getFloatTimeDomainData(i);let e=0;for(let t=0;t<i.length;t++)e+=i[t]*i[t];let t=Math.sqrt(e/i.length),r=Math.min(1,Math.max(0,((t>0?20*Math.log10(t):-60)- -60)/50));N.current=Math.max(N.current,r),k.current=.3*k.current+.7*r,p(k.current)},50)},[]),R=(0,r.useCallback)(async t=>{f("transcribing");try{let r=new FormData,a=localStorage.getItem(y);a&&r.append("prompt",a);let i="true"===localStorage.getItem(v);r.append("useLlm",String(i)),r.append("file",t,"recording.webm");let n=await fetch((0,s.apiUrl)("/api/whisper/transcribe"),{method:"POST",body:r});if(!n.ok){let e=await n.json().catch(()=>null);throw Error(e?.error||`HTTPエラー: ${n.status}`)}let l=await n.json();l.warning&&o.toaster.create({type:"error",title:"LLM整形をスキップしました",description:l.warning}),l.text&&e(l.text)}catch(e){o.toaster.create({type:"error",title:"文字起こしに失敗しました",description:e instanceof Error?e.message:String(e)})}finally{f("idle")}},[e]),L=(0,r.useCallback)(async()=>{try{let e=await navigator.mediaDevices.getUserMedia({audio:!0});w.current=e,b.current=[],E(e);let t=new MediaRecorder(e,{audioBitsPerSecond:128e3});t.ondataavailable=e=>{e.data.size>0&&b.current.push(e.data)},t.onstop=()=>{w.current?.getTracks().forEach(e=>e.stop()),w.current=null;let e=N.current;if(S(),e<.15){o.toaster.create({type:"error",title:"音声が検出されませんでした",description:"マイクに向かって話してから、もう一度お試しください。"}),f("idle");return}let t=new Blob(b.current,{type:"audio/webm"});R(t)},h.current=t,t.start(),f("recording")}catch(e){o.toaster.create({type:"error",title:"マイクにアクセスできませんでした",description:e instanceof Error?e.message:String(e)})}},[R,E,S]),I=(0,r.useCallback)(()=>{h.current?.stop()},[]);if(!d)return null;let O=null!==g&&j.includes(g);return(0,t.jsxs)("div",{className:"recording"===c?"inline-flex items-center gap-2 rounded-md bg-destructive/10 p-1":"inline-flex",children:[(0,t.jsx)(l.Button,{size:"icon",variant:"recording"===c?"destructive":"default",onClick:"recording"===c?I:()=>void L(),disabled:"transcribing"===c||"idle"===c&&!O,title:"recording"===c?"停止して入力":"idle"!==c||O?"音声入力":"Whisperサーバーが起動していません(Settingsから起動してください)",children:"transcribing"===c?(0,t.jsx)(a.Loader2,{className:"w-4 h-4 animate-spin"}):"recording"===c?(0,t.jsx)(n.Square,{className:"w-4 h-4"}):(0,t.jsx)(i.Mic,{className:"w-4 h-4"})}),"recording"===c&&(0,t.jsx)("div",{"aria-hidden":!0,className:"h-1.5 w-16 overflow-hidden rounded-full bg-background/60",children:(0,t.jsx)("div",{className:"h-full rounded-full bg-destructive",style:{width:`${Math.round(100*m)}%`}})})]})},"WHISPER_PROMPT_STORAGE_KEY",0,y],31449)},51846,e=>{"use strict";var t=e.i(71645);e.s(["useDocumentTitle",0,function(e){(0,t.useEffect)(()=>{let t=e?`繋 | ${e}`:"繋",r=document.title;return document.title=t,()=>{document.title=r}},[e])}])}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,471,t=>{"use strict";let e=(0,t.i(75254).default)("arrow-left",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]);t.s(["ArrowLeft",0,e],471)},46349,t=>{"use strict";let e=(0,t.i(75254).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);t.s(["default",0,e])},88699,t=>{"use strict";let e=(0,t.i(75254).default)("pencil",[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]]);t.s(["Pencil",0,e],88699)},55716,t=>{"use strict";let e=(0,t.i(75254).default)("git-branch",[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]]);t.s(["GitBranch",0,e],55716)},7233,5352,t=>{"use strict";let e=(0,t.i(75254).default)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);t.s(["Plus",0,e],7233);let a=[{bg:"bg-blue-100 dark:bg-blue-900/30",text:"text-blue-700 dark:text-blue-300"},{bg:"bg-emerald-100 dark:bg-emerald-900/30",text:"text-emerald-700 dark:text-emerald-300"},{bg:"bg-violet-100 dark:bg-violet-900/30",text:"text-violet-700 dark:text-violet-300"},{bg:"bg-amber-100 dark:bg-amber-900/30",text:"text-amber-700 dark:text-amber-300"},{bg:"bg-rose-100 dark:bg-rose-900/30",text:"text-rose-700 dark:text-rose-300"},{bg:"bg-cyan-100 dark:bg-cyan-900/30",text:"text-cyan-700 dark:text-cyan-300"},{bg:"bg-orange-100 dark:bg-orange-900/30",text:"text-orange-700 dark:text-orange-300"},{bg:"bg-teal-100 dark:bg-teal-900/30",text:"text-teal-700 dark:text-teal-300"},{bg:"bg-indigo-100 dark:bg-indigo-900/30",text:"text-indigo-700 dark:text-indigo-300"},{bg:"bg-pink-100 dark:bg-pink-900/30",text:"text-pink-700 dark:text-pink-300"},{bg:"bg-lime-100 dark:bg-lime-900/30",text:"text-lime-700 dark:text-lime-300"},{bg:"bg-fuchsia-100 dark:bg-fuchsia-900/30",text:"text-fuchsia-700 dark:text-fuchsia-300"}];t.s(["getRepoColor",0,function(t,e){let r=function(t){let e=5381;for(let a=0;a<t.length;a++)e=33*e^t.charCodeAt(a);return e>>>0}(`${t}/${e}`)%a.length;return a[r]}],5352)}]);
|
package/dist/server/index.js
CHANGED
|
@@ -18,6 +18,8 @@ import { terminalRoutes } from './routes/terminal.js';
|
|
|
18
18
|
import { editorRoutes } from './routes/editor.js';
|
|
19
19
|
import { whisperRoutes } from './routes/whisper.js';
|
|
20
20
|
import { stopWhisperServerOnExit } from './lib/whisper-process.js';
|
|
21
|
+
import { llmRoutes } from './routes/llm.js';
|
|
22
|
+
import { stopLlmServerOnExit } from './lib/llm-process.js';
|
|
21
23
|
import { createBasicAuth } from './basic-auth.js';
|
|
22
24
|
// Fastify は単一の公開エンドポイント。Next.js は内部ポートで動かしプロキシする。
|
|
23
25
|
const PORT = Number(process.env.PORT) || 2791;
|
|
@@ -93,6 +95,7 @@ async function start() {
|
|
|
93
95
|
await fastify.register(terminalRoutes, { prefix: '/api' });
|
|
94
96
|
await fastify.register(editorRoutes, { prefix: '/api' });
|
|
95
97
|
await fastify.register(whisperRoutes, { prefix: '/api' });
|
|
98
|
+
await fastify.register(llmRoutes, { prefix: '/api' });
|
|
96
99
|
// catch-all リバースプロキシ: /api・/socket.io・/health 以外を内部 Next.js へ転送。
|
|
97
100
|
// - /api/* と /health は上で定義済みルートが wildcard より優先される。
|
|
98
101
|
// - /socket.io は Socket.IO が HTTP サーバ層で先取りするためここには来ない。
|
|
@@ -144,13 +147,17 @@ async function start() {
|
|
|
144
147
|
const shutdown = async (signal) => {
|
|
145
148
|
console.log(`[server] Received ${signal}, shutting down...`);
|
|
146
149
|
stopWhisperServerOnExit();
|
|
150
|
+
stopLlmServerOnExit();
|
|
147
151
|
await fastify.close();
|
|
148
152
|
process.exit(0);
|
|
149
153
|
};
|
|
150
154
|
process.on('SIGINT', () => void shutdown('SIGINT'));
|
|
151
155
|
process.on('SIGTERM', () => void shutdown('SIGTERM'));
|
|
152
156
|
// SIGINT/SIGTERM を経由しない異常終了時の最後の砦('exit' は同期処理のみ可能)。
|
|
153
|
-
process.on('exit', () =>
|
|
157
|
+
process.on('exit', () => {
|
|
158
|
+
stopWhisperServerOnExit();
|
|
159
|
+
stopLlmServerOnExit();
|
|
160
|
+
});
|
|
154
161
|
}
|
|
155
162
|
start().catch((err) => {
|
|
156
163
|
console.error(err);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `claude logout` は ~/.claude.json の hasCompletedOnboarding を含む
|
|
3
|
+
* first-launch setup state をリセットする。Tsunagi は全タブ/タスクの
|
|
4
|
+
* claude プロセスが同一 HOME(同一 ~/.claude.json)を共有しているため、
|
|
5
|
+
* どこか1セッションで logout すると以降全タブでオンボーディングウィザード
|
|
6
|
+
* (対話UIではなく初回セットアップ画面)が起動してしまい、--resume/--session-id
|
|
7
|
+
* を前提にした自動起動フローが止まる。claude 起動直前にこのフラグだけ補正する。
|
|
8
|
+
*/
|
|
9
|
+
export declare function ensureClaudeOnboardingCompleted(): Promise<void>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* `claude logout` は ~/.claude.json の hasCompletedOnboarding を含む
|
|
6
|
+
* first-launch setup state をリセットする。Tsunagi は全タブ/タスクの
|
|
7
|
+
* claude プロセスが同一 HOME(同一 ~/.claude.json)を共有しているため、
|
|
8
|
+
* どこか1セッションで logout すると以降全タブでオンボーディングウィザード
|
|
9
|
+
* (対話UIではなく初回セットアップ画面)が起動してしまい、--resume/--session-id
|
|
10
|
+
* を前提にした自動起動フローが止まる。claude 起動直前にこのフラグだけ補正する。
|
|
11
|
+
*/
|
|
12
|
+
export async function ensureClaudeOnboardingCompleted() {
|
|
13
|
+
const configPath = path.join(os.homedir(), '.claude.json');
|
|
14
|
+
let raw;
|
|
15
|
+
try {
|
|
16
|
+
raw = await fs.readFile(configPath, 'utf-8');
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// 初回起動などファイルが存在しない場合は何もしない
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let config;
|
|
23
|
+
try {
|
|
24
|
+
config = JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (config.hasCompletedOnboarding === true) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
config.hasCompletedOnboarding = true;
|
|
33
|
+
// 他の claude プロセスによる同時書き込みと衝突しても部分書き込みにならないよう
|
|
34
|
+
// tmpファイルに書いてから rename する。
|
|
35
|
+
const tmpPath = `${configPath}.tsunagi-tmp-${process.pid}`;
|
|
36
|
+
try {
|
|
37
|
+
await fs.writeFile(tmpPath, JSON.stringify(config), 'utf-8');
|
|
38
|
+
await fs.rename(tmpPath, configPath);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
await fs.rm(tmpPath, { force: true }).catch(() => { });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare function getLlmServerUrl(): string;
|
|
2
|
+
export declare function findLlmServerDir(): string | null;
|
|
3
|
+
export type LlmServerStep = 'not_running' | 'installing_deps' | 'downloading_model' | 'starting_server' | 'running' | 'running_external' | 'error';
|
|
4
|
+
export interface DownloadProgress {
|
|
5
|
+
downloadedBytes: number;
|
|
6
|
+
totalBytes: number;
|
|
7
|
+
etaSeconds: number | null;
|
|
8
|
+
}
|
|
9
|
+
export interface LlmServerInfo {
|
|
10
|
+
step: LlmServerStep;
|
|
11
|
+
serverDir: string | null;
|
|
12
|
+
downloadProgress?: DownloadProgress;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function getLlmServerStatus(): Promise<LlmServerInfo>;
|
|
16
|
+
export declare function startLlmServer(): {
|
|
17
|
+
started: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function stopLlmServer(): Promise<{
|
|
21
|
+
stopped: boolean;
|
|
22
|
+
error?: string;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function stopLlmServerOnExit(): void;
|
|
25
|
+
export interface LlmChatMessage {
|
|
26
|
+
role: 'system' | 'user' | 'assistant';
|
|
27
|
+
content: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function generateLlmCompletion(messages: LlmChatMessage[], maxTokens: number): Promise<string>;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { killProcessOnPort } from './process-port.js';
|
|
7
|
+
const MODEL = 'mlx-community/Qwen3-30B-A3B-Instruct-2507-4bit';
|
|
8
|
+
const LLM_SERVER_URL = process.env.TSUNAGI_LLM_SERVER_URL || 'http://127.0.0.1:8766';
|
|
9
|
+
// run.shが待受けるポート固定値(host.docker.internal経由URLと違い、停止処理は
|
|
10
|
+
// 必ずこのNodeプロセスと同じホスト上のポートを対象にする必要があるため分けて持つ)。
|
|
11
|
+
const LLM_PORT = 8766;
|
|
12
|
+
export function getLlmServerUrl() {
|
|
13
|
+
return LLM_SERVER_URL;
|
|
14
|
+
}
|
|
15
|
+
// venv・モデルキャッシュとも ~/.tsunagi/llm 配下にまとめる(run.shと同じ場所)。
|
|
16
|
+
const TSUNAGI_LLM_DIR = path.join(os.homedir(), '.tsunagi', 'llm');
|
|
17
|
+
const VENV_DIR = path.join(TSUNAGI_LLM_DIR, 'venv');
|
|
18
|
+
const HF_CACHE_DIR = path.join(TSUNAGI_LLM_DIR, 'cache');
|
|
19
|
+
const MODEL_CACHE_DIR = path.join(HF_CACHE_DIR, 'hub', 'models--mlx-community--Qwen3-30B-A3B-Instruct-2507-4bit');
|
|
20
|
+
// 実測値(2026-07時点、4bit量子化された重み一式の合計)。多少の変動はあるがETA計算の目安として使う。
|
|
21
|
+
const EXPECTED_MODEL_BYTES = 17_200_000_000;
|
|
22
|
+
// このファイルは apps/server/src/lib (dev) または apps/cli/dist/server/lib
|
|
23
|
+
// (npm配布物) のいずれかにいる。どちらの場合も3階層上に llm-server が
|
|
24
|
+
// 兄弟ディレクトリとして存在するようレイアウトを揃えている
|
|
25
|
+
// (apps/server/dist/lib → apps/server → apps → apps/llm-server,
|
|
26
|
+
// apps/cli/dist/server/lib → apps/cli/dist → apps/cli → apps/cli/llm-server)。
|
|
27
|
+
export function findLlmServerDir() {
|
|
28
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
const candidate = path.join(here, '..', '..', '..', 'llm-server');
|
|
30
|
+
return fs.existsSync(path.join(candidate, 'run.sh')) ? candidate : null;
|
|
31
|
+
}
|
|
32
|
+
function venvPython() {
|
|
33
|
+
return path.join(VENV_DIR, 'bin', 'python3');
|
|
34
|
+
}
|
|
35
|
+
function isVenvReady() {
|
|
36
|
+
return fs.existsSync(venvPython());
|
|
37
|
+
}
|
|
38
|
+
function isModelReady() {
|
|
39
|
+
const snapshotsDir = path.join(MODEL_CACHE_DIR, 'snapshots');
|
|
40
|
+
if (!fs.existsSync(snapshotsDir))
|
|
41
|
+
return false;
|
|
42
|
+
return fs.readdirSync(snapshotsDir).length > 0;
|
|
43
|
+
}
|
|
44
|
+
let currentStep = 'not_running';
|
|
45
|
+
let downloadProgress;
|
|
46
|
+
let lastError;
|
|
47
|
+
let managedProcess = null;
|
|
48
|
+
let setupPromise = null;
|
|
49
|
+
function sleep(ms) {
|
|
50
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
51
|
+
}
|
|
52
|
+
async function checkHealth() {
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch(`${LLM_SERVER_URL}/health`, {
|
|
55
|
+
signal: AbortSignal.timeout(2000),
|
|
56
|
+
});
|
|
57
|
+
return response.ok;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export async function getLlmServerStatus() {
|
|
64
|
+
const serverDir = findLlmServerDir();
|
|
65
|
+
// セットアップ/起動フロー進行中はそのステップをそのまま報告する。
|
|
66
|
+
if (setupPromise) {
|
|
67
|
+
return { step: currentStep, serverDir, downloadProgress, error: lastError };
|
|
68
|
+
}
|
|
69
|
+
const healthy = await checkHealth();
|
|
70
|
+
if (healthy) {
|
|
71
|
+
return { step: managedProcess ? 'running' : 'running_external', serverDir };
|
|
72
|
+
}
|
|
73
|
+
return { step: lastError ? 'error' : 'not_running', serverDir, error: lastError };
|
|
74
|
+
}
|
|
75
|
+
function runStep(cmd, args, cwd, env) {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
const child = spawn(cmd, args, { cwd, stdio: 'ignore', env: env ?? process.env });
|
|
78
|
+
child.on('exit', (code) => {
|
|
79
|
+
if (code === 0)
|
|
80
|
+
resolve();
|
|
81
|
+
else
|
|
82
|
+
reject(new Error(`${path.basename(cmd)} ${args.join(' ')} exited with code ${code}`));
|
|
83
|
+
});
|
|
84
|
+
child.on('error', reject);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// モデルDL中(huggingface_hubがblobs/配下に *.incomplete を書き続ける)のファイルサイズを
|
|
88
|
+
// 定期的にポーリングし、進捗(%)と直近の転送速度からのETAを算出する。
|
|
89
|
+
async function trackModelDownload(child) {
|
|
90
|
+
const blobsDir = path.join(MODEL_CACHE_DIR, 'blobs');
|
|
91
|
+
let lastBytes = 0;
|
|
92
|
+
let lastTime = Date.now();
|
|
93
|
+
let stopped = false;
|
|
94
|
+
child.on('exit', () => {
|
|
95
|
+
stopped = true;
|
|
96
|
+
});
|
|
97
|
+
while (!stopped) {
|
|
98
|
+
await sleep(1000);
|
|
99
|
+
let downloaded = 0;
|
|
100
|
+
try {
|
|
101
|
+
for (const f of fs.readdirSync(blobsDir)) {
|
|
102
|
+
if (!f.endsWith('.incomplete'))
|
|
103
|
+
continue;
|
|
104
|
+
downloaded += fs.statSync(path.join(blobsDir, f)).size;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// blobsDir がまだ作られていない場合は0のまま次のポーリングへ
|
|
109
|
+
}
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
const elapsedSec = (now - lastTime) / 1000;
|
|
112
|
+
const bytesPerSec = elapsedSec > 0 ? (downloaded - lastBytes) / elapsedSec : 0;
|
|
113
|
+
const remaining = EXPECTED_MODEL_BYTES - downloaded;
|
|
114
|
+
const etaSeconds = bytesPerSec > 0 ? Math.max(0, Math.round(remaining / bytesPerSec)) : null;
|
|
115
|
+
downloadProgress = {
|
|
116
|
+
downloadedBytes: downloaded,
|
|
117
|
+
totalBytes: EXPECTED_MODEL_BYTES,
|
|
118
|
+
etaSeconds,
|
|
119
|
+
};
|
|
120
|
+
lastBytes = downloaded;
|
|
121
|
+
lastTime = now;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function runSetupAndStart(dir) {
|
|
125
|
+
const hfEnv = { ...process.env, HF_HOME: HF_CACHE_DIR, HF_HUB_DISABLE_XET: '1' };
|
|
126
|
+
if (!isVenvReady()) {
|
|
127
|
+
currentStep = 'installing_deps';
|
|
128
|
+
fs.mkdirSync(TSUNAGI_LLM_DIR, { recursive: true });
|
|
129
|
+
await runStep('python3', ['-m', 'venv', VENV_DIR], dir);
|
|
130
|
+
await runStep(venvPython(), ['-m', 'pip', 'install', '-r', 'requirements.txt'], dir);
|
|
131
|
+
}
|
|
132
|
+
if (!isModelReady()) {
|
|
133
|
+
currentStep = 'downloading_model';
|
|
134
|
+
downloadProgress = { downloadedBytes: 0, totalBytes: EXPECTED_MODEL_BYTES, etaSeconds: null };
|
|
135
|
+
await new Promise((resolve, reject) => {
|
|
136
|
+
const child = spawn(venvPython(), ['download_model.py'], {
|
|
137
|
+
cwd: dir,
|
|
138
|
+
stdio: 'ignore',
|
|
139
|
+
env: hfEnv,
|
|
140
|
+
});
|
|
141
|
+
void trackModelDownload(child);
|
|
142
|
+
child.on('exit', (code) => {
|
|
143
|
+
if (code === 0)
|
|
144
|
+
resolve();
|
|
145
|
+
else
|
|
146
|
+
reject(new Error(`download_model.py exited with code ${code}`));
|
|
147
|
+
});
|
|
148
|
+
child.on('error', reject);
|
|
149
|
+
});
|
|
150
|
+
downloadProgress = undefined;
|
|
151
|
+
}
|
|
152
|
+
currentStep = 'starting_server';
|
|
153
|
+
const child = spawn(venvPython(), ['-m', 'mlx_lm.server', '--model', MODEL, '--host', '127.0.0.1', '--port', String(LLM_PORT)], { cwd: dir, stdio: 'ignore', env: hfEnv });
|
|
154
|
+
child.on('exit', () => {
|
|
155
|
+
if (managedProcess === child)
|
|
156
|
+
managedProcess = null;
|
|
157
|
+
});
|
|
158
|
+
child.on('error', () => {
|
|
159
|
+
if (managedProcess === child)
|
|
160
|
+
managedProcess = null;
|
|
161
|
+
});
|
|
162
|
+
managedProcess = child;
|
|
163
|
+
// MoEで実計算はアクティブパラメータ(~3B)分のみだが、4bit量子化でも~17GBの
|
|
164
|
+
// 重みファイル自体はメモリへ展開する必要があるため、whisperより長めに待つ。
|
|
165
|
+
const start = Date.now();
|
|
166
|
+
while (Date.now() - start < 90000) {
|
|
167
|
+
if (await checkHealth())
|
|
168
|
+
return;
|
|
169
|
+
await sleep(1000);
|
|
170
|
+
}
|
|
171
|
+
throw new Error('Server did not become healthy within 90s of starting');
|
|
172
|
+
}
|
|
173
|
+
export function startLlmServer() {
|
|
174
|
+
if (setupPromise || managedProcess) {
|
|
175
|
+
return { started: false, error: 'Already starting/running' };
|
|
176
|
+
}
|
|
177
|
+
const dir = findLlmServerDir();
|
|
178
|
+
if (!dir) {
|
|
179
|
+
return { started: false, error: 'llm-server directory not found' };
|
|
180
|
+
}
|
|
181
|
+
lastError = undefined;
|
|
182
|
+
setupPromise = runSetupAndStart(dir)
|
|
183
|
+
.catch((error) => {
|
|
184
|
+
currentStep = 'error';
|
|
185
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
186
|
+
})
|
|
187
|
+
.finally(() => {
|
|
188
|
+
setupPromise = null;
|
|
189
|
+
});
|
|
190
|
+
return { started: true };
|
|
191
|
+
}
|
|
192
|
+
export async function stopLlmServer() {
|
|
193
|
+
if (managedProcess) {
|
|
194
|
+
managedProcess.kill();
|
|
195
|
+
managedProcess = null;
|
|
196
|
+
currentStep = 'not_running';
|
|
197
|
+
return { stopped: true };
|
|
198
|
+
}
|
|
199
|
+
// tsunagi外(make llm等)で起動された場合はchild_processのハンドルを
|
|
200
|
+
// 持たないため、ポート番号を手がかりにOS側から見つけて停止する。
|
|
201
|
+
const killed = await killProcessOnPort(LLM_PORT);
|
|
202
|
+
if (!killed) {
|
|
203
|
+
return {
|
|
204
|
+
stopped: false,
|
|
205
|
+
error: `ポート${LLM_PORT}で待ち受けているプロセスが見つかりませんでした`,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
currentStep = 'not_running';
|
|
209
|
+
return { stopped: true };
|
|
210
|
+
}
|
|
211
|
+
// tsunagi本体プロセスの終了時(Ctrl+C等)に、自分が起動したllm-serverも
|
|
212
|
+
// 道連れで停止する。ユーザーが手動で起動したもの(running_external)には触れない。
|
|
213
|
+
export function stopLlmServerOnExit() {
|
|
214
|
+
managedProcess?.kill();
|
|
215
|
+
managedProcess = null;
|
|
216
|
+
}
|
|
217
|
+
// mlx_lm.serverへストリーミングなしで1回だけ問い合わせ、最終テキストのみを返す。
|
|
218
|
+
// 音声入力の文字起こし結果の整形など、対話UIを介さずLLMの出力だけ欲しい場面で使う。
|
|
219
|
+
export async function generateLlmCompletion(messages, maxTokens) {
|
|
220
|
+
const response = await fetch(`${LLM_SERVER_URL}/v1/chat/completions`, {
|
|
221
|
+
method: 'POST',
|
|
222
|
+
headers: { 'content-type': 'application/json' },
|
|
223
|
+
body: JSON.stringify({ messages, stream: false, max_tokens: maxTokens }),
|
|
224
|
+
signal: AbortSignal.timeout(60_000),
|
|
225
|
+
});
|
|
226
|
+
if (!response.ok) {
|
|
227
|
+
throw new Error(`llm-server responded with ${response.status}`);
|
|
228
|
+
}
|
|
229
|
+
const data = (await response.json());
|
|
230
|
+
const content = data.choices?.[0]?.message?.content;
|
|
231
|
+
if (typeof content !== 'string') {
|
|
232
|
+
throw new Error('Unexpected response shape from llm-server');
|
|
233
|
+
}
|
|
234
|
+
return content;
|
|
235
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function killProcessOnPort(port: number): Promise<boolean>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
// 指定ポートでLISTENしているプロセスを探して停止する。tsunagi自身が起動した
|
|
3
|
+
// 場合はchild_processのハンドルで直接killできるが、`make whisper`/`make llm`
|
|
4
|
+
// のようにtsunagi外で手動起動された場合はハンドルを持たないため、
|
|
5
|
+
// ポート番号を手がかりにOS側から見つけて停止する。
|
|
6
|
+
export function killProcessOnPort(port) {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
execFile('lsof', ['-ti', `:${port}`, '-sTCP:LISTEN'], (_error, stdout) => {
|
|
9
|
+
const pids = (stdout || '')
|
|
10
|
+
.split('\n')
|
|
11
|
+
.map((s) => s.trim())
|
|
12
|
+
.filter(Boolean);
|
|
13
|
+
if (pids.length === 0) {
|
|
14
|
+
resolve(false);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
for (const pid of pids) {
|
|
18
|
+
try {
|
|
19
|
+
process.kill(Number(pid), 'SIGTERM');
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// 既に終了している等は無視
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
resolve(true);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -16,8 +16,8 @@ export declare function startWhisperServer(): {
|
|
|
16
16
|
started: boolean;
|
|
17
17
|
error?: string;
|
|
18
18
|
};
|
|
19
|
-
export declare function stopWhisperServer(): {
|
|
19
|
+
export declare function stopWhisperServer(): Promise<{
|
|
20
20
|
stopped: boolean;
|
|
21
21
|
error?: string;
|
|
22
|
-
}
|
|
22
|
+
}>;
|
|
23
23
|
export declare function stopWhisperServerOnExit(): void;
|
|
@@ -3,7 +3,11 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as os from 'node:os';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { killProcessOnPort } from './process-port.js';
|
|
6
7
|
const WHISPER_SERVER_URL = process.env.TSUNAGI_WHISPER_SERVER_URL || 'http://127.0.0.1:8765';
|
|
8
|
+
// run.shが待受けるポート固定値(host.docker.internal経由URLと違い、停止処理は
|
|
9
|
+
// 必ずこのNodeプロセスと同じホスト上のポートを対象にする必要があるため分けて持つ)。
|
|
10
|
+
const WHISPER_PORT = 8765;
|
|
7
11
|
// venv・モデルキャッシュとも ~/.tsunagi/whisper 配下にまとめる(run.shと同じ場所)。
|
|
8
12
|
const TSUNAGI_WHISPER_DIR = path.join(os.homedir(), '.tsunagi', 'whisper');
|
|
9
13
|
const VENV_DIR = path.join(TSUNAGI_WHISPER_DIR, 'venv');
|
|
@@ -179,12 +183,22 @@ export function startWhisperServer() {
|
|
|
179
183
|
});
|
|
180
184
|
return { started: true };
|
|
181
185
|
}
|
|
182
|
-
export function stopWhisperServer() {
|
|
183
|
-
if (
|
|
184
|
-
|
|
186
|
+
export async function stopWhisperServer() {
|
|
187
|
+
if (managedProcess) {
|
|
188
|
+
managedProcess.kill();
|
|
189
|
+
managedProcess = null;
|
|
190
|
+
currentStep = 'not_running';
|
|
191
|
+
return { stopped: true };
|
|
192
|
+
}
|
|
193
|
+
// tsunagi外(make whisper等)で起動された場合はchild_processのハンドルを
|
|
194
|
+
// 持たないため、ポート番号を手がかりにOS側から見つけて停止する。
|
|
195
|
+
const killed = await killProcessOnPort(WHISPER_PORT);
|
|
196
|
+
if (!killed) {
|
|
197
|
+
return {
|
|
198
|
+
stopped: false,
|
|
199
|
+
error: `ポート${WHISPER_PORT}で待ち受けているプロセスが見つかりませんでした`,
|
|
200
|
+
};
|
|
185
201
|
}
|
|
186
|
-
managedProcess.kill();
|
|
187
|
-
managedProcess = null;
|
|
188
202
|
currentStep = 'not_running';
|
|
189
203
|
return { stopped: true };
|
|
190
204
|
}
|