feat: browser stream waits through round gaps instead of disconnecting

Stale bridge state used to end the stream on the first stale read,
so every round gap and every worker restart killed the watch (the
2026-10-30 demo watch died this way when a 15:03 deploy restarted
the worker). Now a stale state switches to a waiting heartbeat
every 5s while the task is still alive (running/queued/paused/
interrupted), and frames resume when the next round's MCP starts
polling. The stream ends only on a terminal task state, a missing
task row, or 10 minutes of continuous staleness. The panel shows
a waiting status and keeps the last frame. Also force-close the
SSE connection on stream end: HTTP/1.1 keep-alive otherwise left
terminal streams hanging with no EOF.
This commit is contained in:
host committed 2026-10-30 15:14:37 +08:00
1 parent 671b4bedef
commit 962bb50a1c
4 files changed
+137 -22

No files matched your search

@@ -105,9 +105,23 @@ export default function BrowserPanel() {
holder?: string;
url?: string;
title?: string;
waiting?: boolean;
task_status?: string;
};
if (payload.image) {
if (payload.waiting) {
// Round gap / worker restart: the server keeps
// the stream open instead of dropping the watch.
// Keep the last frame on screen, just say so.
setStatus(
payload.task_status === "interrupted"
? "任务已中断,等待续跑…"
: payload.task_status === "paused"
? "任务已暂停…"
: "等待会话…",
);
} else if (payload.image) {
setImg("data:image/jpeg;base64," + payload.image);
setStatus("直播中");
}
const patch: BrowserState = {};
if (payload.holder) patch.holder = payload.holder;
+15 -15
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Codex 命令台</title>
<script type="module" crossorigin>(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const S of document.querySelectorAll('link[rel="modulepreload"]'))f(S);new MutationObserver(S=>{for(const N of S)if(N.type==="childList")for(const A of N.addedNodes)A.tagName==="LINK"&&A.rel==="modulepreload"&&f(A)}).observe(document,{childList:!0,subtree:!0});function h(S){const N={};return S.integrity&&(N.integrity=S.integrity),S.referrerPolicy&&(N.referrerPolicy=S.referrerPolicy),S.crossOrigin==="use-credentials"?N.credentials="include":S.crossOrigin==="anonymous"?N.credentials="omit":N.credentials="same-origin",N}function f(S){if(S.ep)return;S.ep=!0;const N=h(S);fetch(S.href,N)}})();function Zh(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var no={exports:{}},ui={};/**
<script type="module" crossorigin>(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const S of document.querySelectorAll('link[rel="modulepreload"]'))f(S);new MutationObserver(S=>{for(const C of S)if(C.type==="childList")for(const A of C.addedNodes)A.tagName==="LINK"&&A.rel==="modulepreload"&&f(A)}).observe(document,{childList:!0,subtree:!0});function h(S){const C={};return S.integrity&&(C.integrity=S.integrity),S.referrerPolicy&&(C.referrerPolicy=S.referrerPolicy),S.crossOrigin==="use-credentials"?C.credentials="include":S.crossOrigin==="anonymous"?C.credentials="omit":C.credentials="same-origin",C}function f(S){if(S.ep)return;S.ep=!0;const C=h(S);fetch(S.href,C)}})();function Zh(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var no={exports:{}},ui={};/**
* @license React
* react-jsx-runtime.production.js
*
@@ -12,7 +12,7 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var Rh;function wy(){if(Rh)return ui;Rh=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function h(f,S,N){var A=null;if(N!==void 0&&(A=""+N),S.key!==void 0&&(A=""+S.key),"key"in S){N={};for(var p in S)p!=="key"&&(N[p]=S[p])}else N=S;return S=N.ref,{$$typeof:i,type:f,key:A,ref:S!==void 0?S:null,props:N}}return ui.Fragment=r,ui.jsx=h,ui.jsxs=h,ui}var Dh;function ky(){return Dh||(Dh=1,no.exports=wy()),no.exports}var c=ky(),uo={exports:{}},ht={};/**
*/var Rh;function wy(){if(Rh)return ui;Rh=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function h(f,S,C){var A=null;if(C!==void 0&&(A=""+C),S.key!==void 0&&(A=""+S.key),"key"in S){C={};for(var p in S)p!=="key"&&(C[p]=S[p])}else C=S;return S=C.ref,{$$typeof:i,type:f,key:A,ref:S!==void 0?S:null,props:C}}return ui.Fragment=r,ui.jsx=h,ui.jsxs=h,ui}var Dh;function ky(){return Dh||(Dh=1,no.exports=wy()),no.exports}var c=ky(),uo={exports:{}},ht={};/**
* @license React
* react.production.js
*
@@ -20,7 +20,7 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var Uh;function By(){if(Uh)return ht;Uh=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),h=Symbol.for("react.fragment"),f=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),N=Symbol.for("react.consumer"),A=Symbol.for("react.context"),p=Symbol.for("react.forward_ref"),H=Symbol.for("react.suspense"),O=Symbol.for("react.memo"),Y=Symbol.for("react.lazy"),j=Symbol.for("react.activity"),w=Symbol.for("react.view_transition"),X=Symbol.iterator;function V(y){return y===null||typeof y!="object"?null:(y=X&&y[X]||y["@@iterator"],typeof y=="function"?y:null)}var at={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},L=Object.assign,vt={};function bt(y,D,ut){this.props=y,this.context=D,this.refs=vt,this.updater=ut||at}bt.prototype.isReactComponent={},bt.prototype.setState=function(y,D){if(typeof y!="object"&&typeof y!="function"&&y!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,y,D,"setState")},bt.prototype.forceUpdate=function(y){this.updater.enqueueForceUpdate(this,y,"forceUpdate")};function zt(){}zt.prototype=bt.prototype;function J(y,D,ut){this.props=y,this.context=D,this.refs=vt,this.updater=ut||at}var Rt=J.prototype=new zt;Rt.constructor=J,L(Rt,bt.prototype),Rt.isPureReactComponent=!0;var it=Array.isArray;function Q(){}var rt={H:null,A:null,T:null,S:null},Ut=Object.prototype.hasOwnProperty;function Qt(y,D,ut){var nt=ut.ref;return{$$typeof:i,type:y,key:D,ref:nt!==void 0?nt:null,props:ut}}function Nt(y,D){return Qt(y.type,D,y.props)}function kt(y){return typeof y=="object"&&y!==null&&y.$$typeof===i}function B(y){var D={"=":"=0",":":"=2"};return"$"+y.replace(/[=:]/g,function(ut){return D[ut]})}var ot=/\/+/g;function st(y,D){return typeof y=="object"&&y!==null&&y.key!=null?B(""+y.key):D.toString(36)}function q(y){switch(y.status){case"fulfilled":return y.value;case"rejected":throw y.reason;default:switch(typeof y.status=="string"?y.then(Q,Q):(y.status="pending",y.then(function(D){y.status==="pending"&&(y.status="fulfilled",y.value=D)},function(D){y.status==="pending"&&(y.status="rejected",y.reason=D)})),y.status){case"fulfilled":return y.value;case"rejected":throw y.reason}}throw y}function C(y,D,ut,nt,Tt){var xt=typeof y;(xt==="undefined"||xt==="boolean")&&(y=null);var Ot=!1;if(y===null)Ot=!0;else switch(xt){case"bigint":case"string":case"number":Ot=!0;break;case"object":switch(y.$$typeof){case i:case r:Ot=!0;break;case Y:return Ot=y._init,C(Ot(y._payload),D,ut,nt,Tt)}}if(Ot)return Tt=Tt(y),Ot=nt===""?"."+st(y,0):nt,it(Tt)?(ut="",Ot!=null&&(ut=Ot.replace(ot,"$&/")+"/"),C(Tt,D,ut,"",function(ce){return ce})):Tt!=null&&(kt(Tt)&&(Tt=Nt(Tt,ut+(Tt.key==null||y&&y.key===Tt.key?"":(""+Tt.key).replace(ot,"$&/")+"/")+Ot)),D.push(Tt)),1;Ot=0;var $=nt===""?".":nt+":";if(it(y))for(var dt=0;dt<y.length;dt++)nt=y[dt],xt=$+st(nt,dt),Ot+=C(nt,D,ut,xt,Tt);else if(dt=V(y),typeof dt=="function")for(y=dt.call(y),dt=0;!(nt=y.next()).done;)nt=nt.value,xt=$+st(nt,dt++),Ot+=C(nt,D,ut,xt,Tt);else if(xt==="object"){if(typeof y.then=="function")return C(q(y),D,ut,nt,Tt);throw D=String(y),Error("Objects are not valid as a React child (found: "+(D==="[object Object]"?"object with keys {"+Object.keys(y).join(", ")+"}":D)+"). If you meant to render a collection of children, use an array instead.")}return Ot}function lt(y,D,ut){if(y==null)return y;var nt=[],Tt=0;return C(y,nt,"","",function(xt){return D.call(ut,xt,Tt++)}),nt}function gt(y){if(y._status===-1){var D=y._result,ut=D();ut.then(function(nt){(y._status===0||y._status===-1)&&(y._status=1,y._result=nt,ut.status===void 0&&(ut.status="fulfilled",ut.value=nt))},function(nt){(y._status===0||y._status===-1)&&(y._status=2,y._result=nt,ut.status===void 0&&(ut.status="rejected",ut.reason=nt))}),y._status===-1&&(y._status=0,y._result=ut)}if(y._status===1)return y._result.default;throw y._result}var yt=typeof reportError=="function"?reportError:function(y){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var D=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof y=="object"&&y!==null&&typeof y.message=="string"?String(y.message):String(y),error:y});if(!window.dispatchEvent(D))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",y);return}console.error(y)};function ge(y){var D=rt.T,ut={};ut.types=D!==null?D.types:null,rt.T=ut;try{var nt=y(),Tt=rt.S;Tt!==null&&Tt(ut,nt),typeof nt=="object"&&nt!==null&&typeof nt.then=="function"&&nt.then(Q,yt)}catch(xt){yt(xt)}finally{D!==null&&ut.types!==null&&(D.types=ut.types),rt.T=D}}function we(y){var D=rt.T;if(D!==null){var ut=D.types;ut===null?D.types=[y]:ut.indexOf(y)===-1&&ut.push(y)}else ge(we.bind(null,y))}var Ce={map:lt,forEach:function(y,D,ut){lt(y,function(){D.apply(this,arguments)},ut)},count:function(y){var D=0;return lt(y,function(){D++}),DLine truncated
*/var Uh;function By(){if(Uh)return ht;Uh=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),h=Symbol.for("react.fragment"),f=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),C=Symbol.for("react.consumer"),A=Symbol.for("react.context"),p=Symbol.for("react.forward_ref"),H=Symbol.for("react.suspense"),O=Symbol.for("react.memo"),Y=Symbol.for("react.lazy"),j=Symbol.for("react.activity"),w=Symbol.for("react.view_transition"),X=Symbol.iterator;function V(y){return y===null||typeof y!="object"?null:(y=X&&y[X]||y["@@iterator"],typeof y=="function"?y:null)}var at={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},L=Object.assign,vt={};function bt(y,D,ut){this.props=y,this.context=D,this.refs=vt,this.updater=ut||at}bt.prototype.isReactComponent={},bt.prototype.setState=function(y,D){if(typeof y!="object"&&typeof y!="function"&&y!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,y,D,"setState")},bt.prototype.forceUpdate=function(y){this.updater.enqueueForceUpdate(this,y,"forceUpdate")};function zt(){}zt.prototype=bt.prototype;function J(y,D,ut){this.props=y,this.context=D,this.refs=vt,this.updater=ut||at}var Rt=J.prototype=new zt;Rt.constructor=J,L(Rt,bt.prototype),Rt.isPureReactComponent=!0;var it=Array.isArray;function Q(){}var rt={H:null,A:null,T:null,S:null},Ut=Object.prototype.hasOwnProperty;function Qt(y,D,ut){var nt=ut.ref;return{$$typeof:i,type:y,key:D,ref:nt!==void 0?nt:null,props:ut}}function Nt(y,D){return Qt(y.type,D,y.props)}function kt(y){return typeof y=="object"&&y!==null&&y.$$typeof===i}function B(y){var D={"=":"=0",":":"=2"};return"$"+y.replace(/[=:]/g,function(ut){return D[ut]})}var ot=/\/+/g;function st(y,D){return typeof y=="object"&&y!==null&&y.key!=null?B(""+y.key):D.toString(36)}function q(y){switch(y.status){case"fulfilled":return y.value;case"rejected":throw y.reason;default:switch(typeof y.status=="string"?y.then(Q,Q):(y.status="pending",y.then(function(D){y.status==="pending"&&(y.status="fulfilled",y.value=D)},function(D){y.status==="pending"&&(y.status="rejected",y.reason=D)})),y.status){case"fulfilled":return y.value;case"rejected":throw y.reason}}throw y}function N(y,D,ut,nt,Tt){var xt=typeof y;(xt==="undefined"||xt==="boolean")&&(y=null);var Ot=!1;if(y===null)Ot=!0;else switch(xt){case"bigint":case"string":case"number":Ot=!0;break;case"object":switch(y.$$typeof){case i:case r:Ot=!0;break;case Y:return Ot=y._init,N(Ot(y._payload),D,ut,nt,Tt)}}if(Ot)return Tt=Tt(y),Ot=nt===""?"."+st(y,0):nt,it(Tt)?(ut="",Ot!=null&&(ut=Ot.replace(ot,"$&/")+"/"),N(Tt,D,ut,"",function(ce){return ce})):Tt!=null&&(kt(Tt)&&(Tt=Nt(Tt,ut+(Tt.key==null||y&&y.key===Tt.key?"":(""+Tt.key).replace(ot,"$&/")+"/")+Ot)),D.push(Tt)),1;Ot=0;var $=nt===""?".":nt+":";if(it(y))for(var dt=0;dt<y.length;dt++)nt=y[dt],xt=$+st(nt,dt),Ot+=N(nt,D,ut,xt,Tt);else if(dt=V(y),typeof dt=="function")for(y=dt.call(y),dt=0;!(nt=y.next()).done;)nt=nt.value,xt=$+st(nt,dt++),Ot+=N(nt,D,ut,xt,Tt);else if(xt==="object"){if(typeof y.then=="function")return N(q(y),D,ut,nt,Tt);throw D=String(y),Error("Objects are not valid as a React child (found: "+(D==="[object Object]"?"object with keys {"+Object.keys(y).join(", ")+"}":D)+"). If you meant to render a collection of children, use an array instead.")}return Ot}function lt(y,D,ut){if(y==null)return y;var nt=[],Tt=0;return N(y,nt,"","",function(xt){return D.call(ut,xt,Tt++)}),nt}function gt(y){if(y._status===-1){var D=y._result,ut=D();ut.then(function(nt){(y._status===0||y._status===-1)&&(y._status=1,y._result=nt,ut.status===void 0&&(ut.status="fulfilled",ut.value=nt))},function(nt){(y._status===0||y._status===-1)&&(y._status=2,y._result=nt,ut.status===void 0&&(ut.status="rejected",ut.reason=nt))}),y._status===-1&&(y._status=0,y._result=ut)}if(y._status===1)return y._result.default;throw y._result}var yt=typeof reportError=="function"?reportError:function(y){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var D=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof y=="object"&&y!==null&&typeof y.message=="string"?String(y.message):String(y),error:y});if(!window.dispatchEvent(D))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",y);return}console.error(y)};function ge(y){var D=rt.T,ut={};ut.types=D!==null?D.types:null,rt.T=ut;try{var nt=y(),Tt=rt.S;Tt!==null&&Tt(ut,nt),typeof nt=="object"&&nt!==null&&typeof nt.then=="function"&&nt.then(Q,yt)}catch(xt){yt(xt)}finally{D!==null&&ut.types!==null&&(D.types=ut.types),rt.T=D}}function we(y){var D=rt.T;if(D!==null){var ut=D.types;ut===null?D.types=[y]:ut.indexOf(y)===-1&&ut.push(y)}else ge(we.bind(null,y))}var Ce={map:lt,forEach:function(y,D,ut){lt(y,function(){D.apply(this,arguments)},ut)},count:function(y){var D=0;return lt(y,function(){D++}),DLine truncated
* @license React
* scheduler.production.js
*
@@ -28,7 +28,7 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var wh;function Yy(){return wh||(wh=1,(function(i){function r(q,C){var lt=q.length;q.push(C);t:for(;0<lt;){var gt=lt-1>>>1,yt=q[gt];if(0<S(yt,C))q[gt]=C,q[lt]=yt,lt=gt;else break t}}function h(q){return q.length===0?null:q[0]}function f(q){if(q.length===0)return null;var C=q[0],lt=q.pop();if(lt!==C){q[0]=lt;t:for(var gt=0,yt=q.length,ge=yt>>>1;gt<ge;){var we=2*(gt+1)-1,Ce=q[we],y=we+1,D=q[y];if(0>S(Ce,lt))y<yt&&0>S(D,Ce)?(q[gt]=D,q[y]=lt,gt=y):(q[gt]=Ce,q[we]=lt,gt=we);else if(y<yt&&0>S(D,lt))q[gt]=D,q[y]=lt,gt=y;else break t}}return C}function S(q,C){var lt=q.sortIndex-C.sortIndex;return lt!==0?lt:q.id-C.id}if(i.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var N=performance;i.unstable_now=function(){return N.now()}}else{var A=Date,p=A.now();i.unstable_now=function(){return A.now()-p}}var H=[],O=[],Y=1,j=null,w=3,X=!1,V=!1,at=!1,L=!1,vt=typeof setTimeout=="function"?setTimeout:null,bt=typeof clearTimeout=="function"?clearTimeout:null,zt=typeof setImmediate<"u"?setImmediate:null;function J(q){for(var C=h(O);C!==null;){if(C.callback===null)f(O);else if(C.startTime<=q)f(O),C.sortIndex=C.expirationTime,r(H,C);else break;C=h(O)}}function Rt(q){if(at=!1,J(q),!V)if(h(H)!==null)V=!0,it||(it=!0,kt());else{var C=h(O);C!==null&&st(Rt,C.startTime-q)}}var it=!1,Q=-1,rt=5,Ut=-1;function Qt(){return L?!0:!(i.unstable_now()-Ut<rt)}function Nt(){if(L=!1,it){var q=i.unstable_now();Ut=q;var C=!0;try{t:{V=!1,at&&(at=!1,bt(Q),Q=-1),X=!0;var lt=w;try{e:{for(J(q),j=h(H);j!==null&&!(j.expirationTime>q&&Qt());){var gt=j.callback;if(typeof gt=="function"){j.callback=null,w=j.priorityLevel;var yt=gt(j.expirationTime<=q);if(q=i.unstable_now(),typeof yt=="function"){j.callback=yt,J(q),C=!0;break e}j===h(H)&&f(H),J(q)}else f(H);j=h(H)}if(j!==null)C=!0;else{var ge=h(O);ge!==null&&st(Rt,ge.startTime-q),C=!1}}break t}finally{j=null,w=lt,X=!1}C=void 0}}finally{C?kt():it=!1}}}var kt;if(typeof zt=="function")kt=function(){zt(Nt)};else if(typeof MessageChannel<"u"){var B=new MessageChannel,ot=B.port2;B.port1.onmessage=Nt,kt=function(){ot.postMessage(null)}}else kt=function(){vt(Nt,0)};function st(q,C){Q=vt(function(){q(i.unstable_now())},C)}i.unstable_IdlePriority=5,i.unstable_ImmediatePriority=1,i.unstable_LowPriority=4,i.unstable_NormalPriority=3,i.unstable_Profiling=null,i.unstable_UserBlockingPriority=2,i.unstable_cancelCallback=function(q){q.callback=null},i.unstable_forceFrameRate=function(q){0>q||125<q?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):rt=0<q?Math.floor(1e3/q):5},i.unstable_getCurrentPriorityLevel=function(){return w},i.unstable_next=function(q){switch(w){case 1:case 2:case 3:var C=3;break;default:C=w}var lt=w;w=C;try{return q()}finally{w=lt}},i.unstable_requestPaint=function(){L=!0},i.unstable_runWithPriority=function(q,C){switch(q){case 1:case 2:case 3:case 4:case 5:break;default:q=3}var lt=w;w=q;try{return C()}finally{w=lt}},i.unstable_scheduleCallback=function(q,C,lt){var gt=i.unstable_now();switch(typeof lt=="object"&&lt!==null?(lt=lt.delay,lt=typeof lt=="number"&&0<lt?gt+lt:gt):lt=gt,q){case 1:var yt=-1;break;case 2:yt=250;break;case 5:yt=1073741823;break;case 4:yt=1e4;break;default:yt=5e3}return yt=lt+yt,q={id:Y++,callback:C,priorityLevel:q,startTime:lt,expirationTime:yt,sortIndex:-1},lt>gt?(q.sortIndex=lt,r(O,q),h(H)===null&&q===h(O)&&(at?(bt(Q),Q=-1):at=!0,st(Rt,lt-gt))):(q.sortIndex=yt,r(H,q),V||X||(V=!0,it||(it=!0,kt()))),q},i.unstable_shouldYield=Qt,i.unstable_wrapCallback=function(q){var C=w;return function(){var lt=w;w=C;try{return q.apply(this,arguments)}finally{w=lt}}}})(so)),so}var kh;function Gy(){return kh||(kh=1,co.exports=Yy()),co.exports}var ro={exports:{}},He={};/**
*/var wh;function Yy(){return wh||(wh=1,(function(i){function r(q,N){var lt=q.length;q.push(N);t:for(;0<lt;){var gt=lt-1>>>1,yt=q[gt];if(0<S(yt,N))q[gt]=N,q[lt]=yt,lt=gt;else break t}}function h(q){return q.length===0?null:q[0]}function f(q){if(q.length===0)return null;var N=q[0],lt=q.pop();if(lt!==N){q[0]=lt;t:for(var gt=0,yt=q.length,ge=yt>>>1;gt<ge;){var we=2*(gt+1)-1,Ce=q[we],y=we+1,D=q[y];if(0>S(Ce,lt))y<yt&&0>S(D,Ce)?(q[gt]=D,q[y]=lt,gt=y):(q[gt]=Ce,q[we]=lt,gt=we);else if(y<yt&&0>S(D,lt))q[gt]=D,q[y]=lt,gt=y;else break t}}return N}function S(q,N){var lt=q.sortIndex-N.sortIndex;return lt!==0?lt:q.id-N.id}if(i.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var C=performance;i.unstable_now=function(){return C.now()}}else{var A=Date,p=A.now();i.unstable_now=function(){return A.now()-p}}var H=[],O=[],Y=1,j=null,w=3,X=!1,V=!1,at=!1,L=!1,vt=typeof setTimeout=="function"?setTimeout:null,bt=typeof clearTimeout=="function"?clearTimeout:null,zt=typeof setImmediate<"u"?setImmediate:null;function J(q){for(var N=h(O);N!==null;){if(N.callback===null)f(O);else if(N.startTime<=q)f(O),N.sortIndex=N.expirationTime,r(H,N);else break;N=h(O)}}function Rt(q){if(at=!1,J(q),!V)if(h(H)!==null)V=!0,it||(it=!0,kt());else{var N=h(O);N!==null&&st(Rt,N.startTime-q)}}var it=!1,Q=-1,rt=5,Ut=-1;function Qt(){return L?!0:!(i.unstable_now()-Ut<rt)}function Nt(){if(L=!1,it){var q=i.unstable_now();Ut=q;var N=!0;try{t:{V=!1,at&&(at=!1,bt(Q),Q=-1),X=!0;var lt=w;try{e:{for(J(q),j=h(H);j!==null&&!(j.expirationTime>q&&Qt());){var gt=j.callback;if(typeof gt=="function"){j.callback=null,w=j.priorityLevel;var yt=gt(j.expirationTime<=q);if(q=i.unstable_now(),typeof yt=="function"){j.callback=yt,J(q),N=!0;break e}j===h(H)&&f(H),J(q)}else f(H);j=h(H)}if(j!==null)N=!0;else{var ge=h(O);ge!==null&&st(Rt,ge.startTime-q),N=!1}}break t}finally{j=null,w=lt,X=!1}N=void 0}}finally{N?kt():it=!1}}}var kt;if(typeof zt=="function")kt=function(){zt(Nt)};else if(typeof MessageChannel<"u"){var B=new MessageChannel,ot=B.port2;B.port1.onmessage=Nt,kt=function(){ot.postMessage(null)}}else kt=function(){vt(Nt,0)};function st(q,N){Q=vt(function(){q(i.unstable_now())},N)}i.unstable_IdlePriority=5,i.unstable_ImmediatePriority=1,i.unstable_LowPriority=4,i.unstable_NormalPriority=3,i.unstable_Profiling=null,i.unstable_UserBlockingPriority=2,i.unstable_cancelCallback=function(q){q.callback=null},i.unstable_forceFrameRate=function(q){0>q||125<q?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):rt=0<q?Math.floor(1e3/q):5},i.unstable_getCurrentPriorityLevel=function(){return w},i.unstable_next=function(q){switch(w){case 1:case 2:case 3:var N=3;break;default:N=w}var lt=w;w=N;try{return q()}finally{w=lt}},i.unstable_requestPaint=function(){L=!0},i.unstable_runWithPriority=function(q,N){switch(q){case 1:case 2:case 3:case 4:case 5:break;default:q=3}var lt=w;w=q;try{return N()}finally{w=lt}},i.unstable_scheduleCallback=function(q,N,lt){var gt=i.unstable_now();switch(typeof lt=="object"&&lt!==null?(lt=lt.delay,lt=typeof lt=="number"&&0<lt?gt+lt:gt):lt=gt,q){case 1:var yt=-1;break;case 2:yt=250;break;case 5:yt=1073741823;break;case 4:yt=1e4;break;default:yt=5e3}return yt=lt+yt,q={id:Y++,callback:N,priorityLevel:q,startTime:lt,expirationTime:yt,sortIndex:-1},lt>gt?(q.sortIndex=lt,r(O,q),h(H)===null&&q===h(O)&&(at?(bt(Q),Q=-1):at=!0,st(Rt,lt-gt))):(q.sortIndex=yt,r(H,q),V||X||(V=!0,it||(it=!0,kt()))),q},i.unstable_shouldYield=Qt,i.unstable_wrapCallback=function(q){var N=w;return function(){var lt=w;w=N;try{return q.apply(this,arguments)}finally{w=lt}}}})(so)),so}var kh;function Gy(){return kh||(kh=1,co.exports=Yy()),co.exports}var ro={exports:{}},He={};/**
* @license React
* react-dom.production.js
*
@@ -36,7 +36,7 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var Bh;function Qy(){if(Bh)return He;Bh=1;var i=yo();function r(Y){var j="https://react.dev/errors/"+Y;if(1<arguments.length){j+="?args[]="+encodeURIComponent(arguments[1]);for(var w=2;w<arguments.length;w++)j+="&args[]="+encodeURIComponent(arguments[w])}return"Minified React error #"+Y+"; visit "+j+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function h(){}var f={d:{f:h,r:function(){throw Error(r(522))},D:h,C:h,L:h,m:h,X:h,S:h,M:h},p:0,findDOMNode:null},S=Symbol.for("react.portal"),N=Symbol.for("react.recoverable"),A=Symbol.for("react.optimistic_key");function p(Y,j,w){var X=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:S,key:X==null?null:X===A?A:""+X,children:Y,containerInfo:j,implementation:w}}var H=i.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function O(Y,j){if(Y==="font")return"";if(typeof j=="string")return j==="use-credentials"?j:""}return He.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=f,He.browser=function(Y){return{$$typeof:N,_reason:Y}},He.createPortal=function(Y,j){var w=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!j||j.nodeType!==1&&j.nodeType!==9&&j.nodeType!==11)throw Error(r(299));return p(Y,j,null,w)},He.flushSync=function(Y){var j=H.T,w=f.p;try{if(H.T=null,f.p=2,Y)return Y()}finally{H.T=j,f.p=w,f.d.f()}},He.preconnect=function(Y,j){typeof Y=="string"&&(j?(j=j.crossOrigin,j=typeof j=="string"?j==="use-credentials"?j:"":void 0):j=null,f.d.C(Y,j))},He.prefetchDNS=function(Y){typeof Y=="string"&&f.d.D(Y)},He.preinit=function(Y,j){if(typeof Y=="string"&&j&&typeof j.as=="string"){var w=j.as,X=O(w,j.crossOrigin),V=typeof j.integrity=="string"?j.integrity:void 0,at=typeof j.fetchPriority=="string"?j.fetchPriority:void 0;w==="style"?f.d.S(Y,typeof j.precedence=="string"?j.precedence:void 0,{crossOrigin:X,integrity:V,fetchPriority:at}):w==="script"&&f.d.X(Y,{crossOrigin:X,integrity:V,fetchPriority:at,nonce:typeof j.nonce=="string"?j.nonce:void 0})}},He.preinitModule=function(Y,j){if(typeof Y=="string")if(typeof j=="object"&&j!==null){if(j.as==null||j.as==="script"){var w=O(j.as,j.crossOrigin);f.d.M(Y,{crossOrigin:w,integrity:typeof j.integrity=="string"?j.integrity:void 0,nonce:typeof j.nonce=="string"?j.nonce:void 0,fetchPriority:typeof j.fetchPriority=="string"?j.fetchPriority:void 0})}}else j==null&&f.d.M(Y)},He.preload=function(Y,j){if(typeof Y=="string"&&typeof j=="object"&&j!==null&&typeof j.as=="string"){var w=j.as,X=O(w,j.crossOrigin);f.d.L(Y,w,{crossOrigin:X,integrity:typeof j.integrity=="string"?j.integrity:void 0,nonce:typeof j.nonce=="string"?j.nonce:void 0,type:typeof j.type=="string"?j.type:void 0,fetchPriority:typeof j.fetchPriority=="string"?j.fetchPriority:void 0,referrerPolicy:typeof j.referrerPolicy=="string"?j.referrerPolicy:void 0,imageSrcSet:typeof j.imageSrcSet=="string"?j.imageSrcSet:void 0,imageSizes:typeof j.imageSizes=="string"?j.imageSizes:void 0,media:typeof j.media=="string"?j.media:void 0})}},He.preloadModule=function(Y,j){if(typeof Y=="string")if(j){var w=O(j.as,j.crossOrigin);f.d.m(Y,{as:typeof j.as=="string"&&j.as!=="script"?j.as:void 0,crossOrigin:w,integrity:typeof j.integrity=="string"?j.integrity:void 0,nonce:typeof j.nonce=="string"?j.nonce:void 0,fetchPriority:typeof j.fetchPriority=="string"?j.fetchPriority:void 0})}else f.d.m(Y)},He.requestFormReset=function(Y){f.d.r(Y)},He.unstable_batchedUpdates=function(Y,j){return Y(j)},He.useFormState=function(Y,j,w){return H.H.useFormState(Y,j,w)},He.useFormStatus=function(){return H.H.useHostTransitionStatus()},He.version="19.3.1",He}var qh;function Xy(){if(qh)return ro.exports;qh=1;function i(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(r){console.error(r)}}return i(),ro.exports=Qy(),ro.exports}/**
*/var Bh;function Qy(){if(Bh)return He;Bh=1;var i=yo();function r(Y){var j="https://react.dev/errors/"+Y;if(1<arguments.length){j+="?args[]="+encodeURIComponent(arguments[1]);for(var w=2;w<arguments.length;w++)j+="&args[]="+encodeURIComponent(arguments[w])}return"Minified React error #"+Y+"; visit "+j+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function h(){}var f={d:{f:h,r:function(){throw Error(r(522))},D:h,C:h,L:h,m:h,X:h,S:h,M:h},p:0,findDOMNode:null},S=Symbol.for("react.portal"),C=Symbol.for("react.recoverable"),A=Symbol.for("react.optimistic_key");function p(Y,j,w){var X=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:S,key:X==null?null:X===A?A:""+X,children:Y,containerInfo:j,implementation:w}}var H=i.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function O(Y,j){if(Y==="font")return"";if(typeof j=="string")return j==="use-credentials"?j:""}return He.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=f,He.browser=function(Y){return{$$typeof:C,_reason:Y}},He.createPortal=function(Y,j){var w=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!j||j.nodeType!==1&&j.nodeType!==9&&j.nodeType!==11)throw Error(r(299));return p(Y,j,null,w)},He.flushSync=function(Y){var j=H.T,w=f.p;try{if(H.T=null,f.p=2,Y)return Y()}finally{H.T=j,f.p=w,f.d.f()}},He.preconnect=function(Y,j){typeof Y=="string"&&(j?(j=j.crossOrigin,j=typeof j=="string"?j==="use-credentials"?j:"":void 0):j=null,f.d.C(Y,j))},He.prefetchDNS=function(Y){typeof Y=="string"&&f.d.D(Y)},He.preinit=function(Y,j){if(typeof Y=="string"&&j&&typeof j.as=="string"){var w=j.as,X=O(w,j.crossOrigin),V=typeof j.integrity=="string"?j.integrity:void 0,at=typeof j.fetchPriority=="string"?j.fetchPriority:void 0;w==="style"?f.d.S(Y,typeof j.precedence=="string"?j.precedence:void 0,{crossOrigin:X,integrity:V,fetchPriority:at}):w==="script"&&f.d.X(Y,{crossOrigin:X,integrity:V,fetchPriority:at,nonce:typeof j.nonce=="string"?j.nonce:void 0})}},He.preinitModule=function(Y,j){if(typeof Y=="string")if(typeof j=="object"&&j!==null){if(j.as==null||j.as==="script"){var w=O(j.as,j.crossOrigin);f.d.M(Y,{crossOrigin:w,integrity:typeof j.integrity=="string"?j.integrity:void 0,nonce:typeof j.nonce=="string"?j.nonce:void 0,fetchPriority:typeof j.fetchPriority=="string"?j.fetchPriority:void 0})}}else j==null&&f.d.M(Y)},He.preload=function(Y,j){if(typeof Y=="string"&&typeof j=="object"&&j!==null&&typeof j.as=="string"){var w=j.as,X=O(w,j.crossOrigin);f.d.L(Y,w,{crossOrigin:X,integrity:typeof j.integrity=="string"?j.integrity:void 0,nonce:typeof j.nonce=="string"?j.nonce:void 0,type:typeof j.type=="string"?j.type:void 0,fetchPriority:typeof j.fetchPriority=="string"?j.fetchPriority:void 0,referrerPolicy:typeof j.referrerPolicy=="string"?j.referrerPolicy:void 0,imageSrcSet:typeof j.imageSrcSet=="string"?j.imageSrcSet:void 0,imageSizes:typeof j.imageSizes=="string"?j.imageSizes:void 0,media:typeof j.media=="string"?j.media:void 0})}},He.preloadModule=function(Y,j){if(typeof Y=="string")if(j){var w=O(j.as,j.crossOrigin);f.d.m(Y,{as:typeof j.as=="string"&&j.as!=="script"?j.as:void 0,crossOrigin:w,integrity:typeof j.integrity=="string"?j.integrity:void 0,nonce:typeof j.nonce=="string"?j.nonce:void 0,fetchPriority:typeof j.fetchPriority=="string"?j.fetchPriority:void 0})}else f.d.m(Y)},He.requestFormReset=function(Y){f.d.r(Y)},He.unstable_batchedUpdates=function(Y,j){return Y(j)},He.useFormState=function(Y,j,w){return H.H.useFormState(Y,j,w)},He.useFormStatus=function(){return H.H.useHostTransitionStatus()},He.version="19.3.1",He}var qh;function Xy(){if(qh)return ro.exports;qh=1;function i(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(r){console.error(r)}}return i(),ro.exports=Qy(),ro.exports}/**
* @license React
* react-dom-client.production.js
*
@@ -44,37 +44,37 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var Yh;function Ly(){if(Yh)return ii;Yh=1;var i=Gy(),r=yo(),h=Xy();function f(t){var e="https://react.dev/errors/"+t;if(1<arguments.length){e+="?args[]="+encodeURIComponent(arguments[1]);for(var l=2;l<arguments.length;l++)e+="&args[]="+encodeURIComponent(arguments[l])}return"Minified React error #"+t+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function S(t){return!(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)}function N(t){for(var e=t,l=e;l&&!l.alternate;)e=l,(e.flags&4098)!==0&&(t=e.return),l=e.return;for(;e.return;)e=e.return;return e.tag===3?t:null}function A(t){if(t.tag===13){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function p(t){if(t.tag===31){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function H(t){if(N(t)!==t)throw Error(f(188))}function O(t){var e=t.alternate;if(!e){if(e=N(t),e===null)throw Error(f(188));return e!==t?null:t}for(var l=t,a=e;;){var n=l.return;if(n===null)break;var u=n.alternate;if(u===null){if(a=n.return,a!==null){l=a;continue}break}if(n.child===u.child){for(u=n.child;u;){if(u===l)return H(n),t;if(u===a)return H(n),e;u=u.sibling}throw Error(f(188))}if(l.return!==a.return)l=n,a=u;else{for(var s=!1,o=n.child;o;){if(o===l){s=!0,l=n,a=u;break}if(o===a){s=!0,a=n,l=u;break}o=o.sibling}if(!s){for(o=u.child;o;){if(o===l){s=!0,l=u,a=n;break}if(o===a){s=!0,a=u,l=n;break}o=o.sibling}if(!s)throw Error(f(189))}}if(l.alternate!==a)throw Error(f(190))}if(l.tag!==3)throw Error(f(188));return l.stateNode.current===l?t:e}function Y(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t;for(t=t.child;t!==null;){if(e=Y(t),e!==null)return e;t=t.sibling}return null}function j(t,e,l,a,n,u){for(;t!==null;){if((t.tag===5||t.tag===27||t.tag===6)&&l(t,a,n,u)||(t.tag!==22||t.memoizedState===null)&&(e||t.tag!==5&&t.tag!==27)&&j(t.child,e,l,a,n,u))return!0;t=t.sibling}return!1}function w(t){for(t=t.return;t!==null;){if(t.tag===3||t.tag===5||t.tag===27)return t;t=t.return}return null}function X(t){var e=!1;for(t=t.return;t!==null&&(t.tag===4&&(e=!0),!(t.tag===3||t.tag===5||t.tag===27));)t=t.return;return e}function V(t){var e=[null,null],l=w(t);return l===null||at(e,t,l.child,{foundSelf:!1}),e}function at(t,e,l,a){for(;l!==null;){if(l===e)a.foundSelf=!0;else if(l.tag===5||l.tag===27||l.tag===6){if(a.foundSelf)return t[1]=l,!0;t[0]=l}else if((l.tag!==22||l.memoizedState===null)&&at(t,e,l.child,a))return!0;l=l.sibling}return!1}function L(t){switch(t.tag){case 5:case 27:case 6:return t.stateNode;case 3:return t.stateNode.containerInfo;default:throw Error(f(559))}}var vt=null,bt=null;function zt(t,e,l){return t===l?!0:t===e?(vt=t,!0):!1}function J(t,e,l){return t===l?(bt=t,!1):t===e?(bt!==null&&(vt=t),!0):!1}function Rt(t){if(t===null)return null;do t=t===null?null:t.return;while(t&&t.tag!==5&&t.tag!==27&&t.tag!==3);return t||null}function it(t,e,l){for(var a=0,n=t;n;n=l(n))a++;n=0;for(var u=e;u;u=l(u))n++;for(;0<a-n;)t=l(t),a--;for(;0<n-a;)e=l(e),n--;for(;a--;){if(t===e||e!==null&&t===e.alternate)return t;t=l(t),e=l(e)}return null}var Q=Object.assign,rt=Symbol.for("react.element"),Ut=Symbol.for("react.transitional.element"),Qt=Symbol.for("react.portal"),Nt=Symbol.for("react.fragment"),kt=Symbol.for("react.strict_mode"),B=Symbol.for("react.profiler"),ot=Symbol.for("react.consumer"),st=Symbol.for("react.context"),q=Symbol.for("react.forward_ref"),C=Symbol.for("react.suspense"),lt=Symbol.for("react.suspense_list"),gt=Symbol.for("react.memo"),yt=Symbol.for("react.lazy"),ge=Symbol.for("react.activity"),we=Symbol.for("react.legacy_hidden"),Ce=Symbol.for("react.memo_cache_sentinel"),y=Symbol.for("react.view_transition"),D=Symbol.for("react.recoverable"),ut=Symbol.iterator;function nt(t){return t===null||typeof t!="object"?null:(t=ut&&t[ut]||t["@@iterator"],typeof t=="function"?t:null)}var Tt=Symbol.for("react.client.reference");function xt(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===Tt?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case Nt:return"Fragment";case B:return"Profiler";case kt:return"StrictMode";case C:return"Suspense";case lt:return"SuspenseList";case ge:return"Activity";case y:return"ViewTransition"}if(typeof t=="object")switch(t.$$typeof){case Qt:return"Portal";case st:return t.displayName||"Context";case ot:return(t._context.displayName||"Context")+".Consumer";case q:var e=t.render;return t=t.displayName,t||(t=e.displayName||e.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case gt:return e=t.displayName||null,e!==null?e:xt(t.type)||"Memo";case yt:e=t._payload,t=t._init;try{return xt(t(e))}catch{}}return null}var Ot=Array.isArray,$=r.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,dt=h.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ce={pending:!1,data:null,method:null,actiLine truncated
*/var Yh;function Ly(){if(Yh)return ii;Yh=1;var i=Gy(),r=yo(),h=Xy();function f(t){var e="https://react.dev/errors/"+t;if(1<arguments.length){e+="?args[]="+encodeURIComponent(arguments[1]);for(var l=2;l<arguments.length;l++)e+="&args[]="+encodeURIComponent(arguments[l])}return"Minified React error #"+t+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function S(t){return!(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)}function C(t){for(var e=t,l=e;l&&!l.alternate;)e=l,(e.flags&4098)!==0&&(t=e.return),l=e.return;for(;e.return;)e=e.return;return e.tag===3?t:null}function A(t){if(t.tag===13){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function p(t){if(t.tag===31){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function H(t){if(C(t)!==t)throw Error(f(188))}function O(t){var e=t.alternate;if(!e){if(e=C(t),e===null)throw Error(f(188));return e!==t?null:t}for(var l=t,a=e;;){var n=l.return;if(n===null)break;var u=n.alternate;if(u===null){if(a=n.return,a!==null){l=a;continue}break}if(n.child===u.child){for(u=n.child;u;){if(u===l)return H(n),t;if(u===a)return H(n),e;u=u.sibling}throw Error(f(188))}if(l.return!==a.return)l=n,a=u;else{for(var s=!1,o=n.child;o;){if(o===l){s=!0,l=n,a=u;break}if(o===a){s=!0,a=n,l=u;break}o=o.sibling}if(!s){for(o=u.child;o;){if(o===l){s=!0,l=u,a=n;break}if(o===a){s=!0,a=u,l=n;break}o=o.sibling}if(!s)throw Error(f(189))}}if(l.alternate!==a)throw Error(f(190))}if(l.tag!==3)throw Error(f(188));return l.stateNode.current===l?t:e}function Y(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t;for(t=t.child;t!==null;){if(e=Y(t),e!==null)return e;t=t.sibling}return null}function j(t,e,l,a,n,u){for(;t!==null;){if((t.tag===5||t.tag===27||t.tag===6)&&l(t,a,n,u)||(t.tag!==22||t.memoizedState===null)&&(e||t.tag!==5&&t.tag!==27)&&j(t.child,e,l,a,n,u))return!0;t=t.sibling}return!1}function w(t){for(t=t.return;t!==null;){if(t.tag===3||t.tag===5||t.tag===27)return t;t=t.return}return null}function X(t){var e=!1;for(t=t.return;t!==null&&(t.tag===4&&(e=!0),!(t.tag===3||t.tag===5||t.tag===27));)t=t.return;return e}function V(t){var e=[null,null],l=w(t);return l===null||at(e,t,l.child,{foundSelf:!1}),e}function at(t,e,l,a){for(;l!==null;){if(l===e)a.foundSelf=!0;else if(l.tag===5||l.tag===27||l.tag===6){if(a.foundSelf)return t[1]=l,!0;t[0]=l}else if((l.tag!==22||l.memoizedState===null)&&at(t,e,l.child,a))return!0;l=l.sibling}return!1}function L(t){switch(t.tag){case 5:case 27:case 6:return t.stateNode;case 3:return t.stateNode.containerInfo;default:throw Error(f(559))}}var vt=null,bt=null;function zt(t,e,l){return t===l?!0:t===e?(vt=t,!0):!1}function J(t,e,l){return t===l?(bt=t,!1):t===e?(bt!==null&&(vt=t),!0):!1}function Rt(t){if(t===null)return null;do t=t===null?null:t.return;while(t&&t.tag!==5&&t.tag!==27&&t.tag!==3);return t||null}function it(t,e,l){for(var a=0,n=t;n;n=l(n))a++;n=0;for(var u=e;u;u=l(u))n++;for(;0<a-n;)t=l(t),a--;for(;0<n-a;)e=l(e),n--;for(;a--;){if(t===e||e!==null&&t===e.alternate)return t;t=l(t),e=l(e)}return null}var Q=Object.assign,rt=Symbol.for("react.element"),Ut=Symbol.for("react.transitional.element"),Qt=Symbol.for("react.portal"),Nt=Symbol.for("react.fragment"),kt=Symbol.for("react.strict_mode"),B=Symbol.for("react.profiler"),ot=Symbol.for("react.consumer"),st=Symbol.for("react.context"),q=Symbol.for("react.forward_ref"),N=Symbol.for("react.suspense"),lt=Symbol.for("react.suspense_list"),gt=Symbol.for("react.memo"),yt=Symbol.for("react.lazy"),ge=Symbol.for("react.activity"),we=Symbol.for("react.legacy_hidden"),Ce=Symbol.for("react.memo_cache_sentinel"),y=Symbol.for("react.view_transition"),D=Symbol.for("react.recoverable"),ut=Symbol.iterator;function nt(t){return t===null||typeof t!="object"?null:(t=ut&&t[ut]||t["@@iterator"],typeof t=="function"?t:null)}var Tt=Symbol.for("react.client.reference");function xt(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===Tt?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case Nt:return"Fragment";case B:return"Profiler";case kt:return"StrictMode";case N:return"Suspense";case lt:return"SuspenseList";case ge:return"Activity";case y:return"ViewTransition"}if(typeof t=="object")switch(t.$$typeof){case Qt:return"Portal";case st:return t.displayName||"Context";case ot:return(t._context.displayName||"Context")+".Consumer";case q:var e=t.render;return t=t.displayName,t||(t=e.displayName||e.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case gt:return e=t.displayName||null,e!==null?e:xt(t.type)||"Memo";case yt:e=t._payload,t=t._init;try{return xt(t(e))}catch{}}return null}var Ot=Array.isArray,$=r.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,dt=h.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ce={pending:!1,data:null,method:null,actiLine truncated
at`)?" (<anonymous>)":-1<l.stack.indexOf("@")?"@unknown:0:0":""}return`
`+Be+t+se}var rl=!1;function Jl(t,e){if(!t||rl)return"";rl=!0;var l=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var a={DetermineComponentFrameRoot:function(){try{if(e){var R=function(){throw Error()};if(Object.defineProperty(R.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(R,[])}catch(G){var g=G}Reflect.construct(t,[],R)}else{try{R.call()}catch(G){g=G}R=!1;try{var _=Object.getOwnPropertyDescriptor(t.prototype,"props");Object.defineProperty(t.prototype,"props",{configurable:!0,set:function(){throw Error()}}),R=!0,new t}finally{R&&(_!==void 0?Object.defineProperty(t.prototype,"props",_):delete t.prototype.props)}}}else{try{throw Error()}catch(G){g=G}(R=t())&&typeof R.catch=="function"&&R.catch(function(){})}}catch(G){if(G&&g&&typeof G.stack=="string")return[G.stack,g.stack]}return[null,null]}};a.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var n=Object.getOwnPropertyDescriptor(a.DetermineComponentFrameRoot,"name");n&&n.configurable&&Object.defineProperty(a.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var u=a.DetermineComponentFrameRoot(),s=u[0],o=u[1];if(s&&o){var m=s.split(`
`),x=o.split(`
`);for(n=a=0;a<m.length&&!m[a].includes("DetermineComponentFrameRoot");)a++;for(;n<x.length&&!x[n].includes("DetermineComponentFrameRoot");)n++;if(a===m.length||n===x.length)for(a=m.length-1,n=x.length-1;1<=a&&0<=n&&m[a]!==x[n];)n--;for(;1<=a&&0<=n;a--,n--)if(m[a]!==x[n]){if(a!==1||n!==1)do if(a--,n--,0>n||m[a]!==x[n]){var E=`
`+m[a].replace(" at new "," at ");return t.displayName&&E.includes("<anonymous>")&&(E=E.replace("<anonymous>",t.displayName)),E}while(1<=a&&0<=n);break}}}finally{rl=!1,Error.prepareStackTrace=l}return(l=t?t.displayName||t.name:"")?re(l):""}function da(t,e){switch(t.tag){case 26:case 27:case 5:return re(t.type);case 16:return re("Lazy");case 13:return t.child!==e&&e!==null?re("Suspense Fallback"):re("Suspense");case 19:return re("SuspenseList");case 0:case 15:return Jl(t.type,!1);case 11:return Jl(t.type.render,!1);case 1:return Jl(t.type,!0);case 31:return re("Activity");case 30:return re("ViewTransition");default:return""}}function Dl(t){try{var e="",l=null;do e+=da(t,l),l=t,t=t.return;while(t);return e}catch(a){return`
Error generating stack: `+a.message+`
`+a.stack}}var te=Object.prototype.hasOwnProperty,Pe=i.unstable_scheduleCallback,qe=i.unstable_cancelCallback,_l=i.unstable_shouldYield,ol=i.unstable_requestPaint,Wt=i.unstable_now,Ul=i.unstable_getCurrentPriorityLevel,mn=i.unstable_ImmediatePriority,Qa=i.unstable_UserBlockingPriority,Se=i.unstable_NormalPriority,Xa=i.unstable_LowPriority,ma=i.unstable_IdlePriority,su=i.log,ru=i.unstable_setDisableYieldValue,Fl=null,Ae=null;function fl(t){if(typeof su=="function"&&ru(t),Ae&&typeof Ae.setStrictMode=="function")try{Ae.setStrictMode(Fl,t)}catch{}}var Oe=Math.clz32?Math.clz32:du,ou=Math.log,fu=Math.LN2;function du(t){return t>>>=0,t===0?32:31-(ou(t)/fu|0)|0}var k=256,U=262144,W=4194304;function tt(t){var e=t&42;if(e!==0)return e;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&-t;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function M(t,e,l){var a=t.pendingLanes;if(a===0)return 0;var n=0,u=t.suspendedLanes,s=t.pingedLanes;t=t.warmLanes;var o=a&134217727;return o!==0?(a=o&~u,a!==0?n=tt(a):(s&=o,s!==0?n=tt(s):l||(l=o&~t,l!==0&&(n=tt(l))))):(o=a&~u,o!==0?n=tt(o):s!==0?n=tt(s):l||(l=a&~t,l!==0&&(n=tt(l)))),n===0?0:e!==0&&e!==n&&(e&u)===0&&(u=n&-n,l=e&-e,u>=l||u===32&&(l&4194048)!==0)?e:n}function Z(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function et(t,e){(e&8)!==0&&(e|=e&32);var l=t.entangledLanes;if(l!==0)for(t=t.entanglements,l&=e;0<l;){var a=31-Oe(l),n=1<<a;e|=t[a],l&=~n}return e}function Bt(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Mt(){var t=W;return W<<=1,(W&62914560)===0&&(W=4194304),t}function be(t){for(var e=[],l=0;31>l;l++)e.push(t);return e}function xe(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function Dc(t,e,l,a,n,u){var s=t.pendingLanes;t.pendingLanes=l,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=l,t.entangledLanes&=l,t.errorRecoveryDisabledLanes&=l,t.shellSuspendCounter=0;var o=t.entanglements,m=t.expirationTimes,x=t.hiddenUpdates;for(l=s&~l;0<l;){var E=31-Oe(l),R=1<<E;o[E]=0,m[E]=-1;var g=x[E];if(g!==null)for(x[E]=null,E=0;E<g.length;E++){var _=g[E];_!==null&&(_.lane&=-536870913)}l&=~R}a!==0&&ci(t,a,0),u!==0&&n===0&&t.tag!==0&&(t.suspendedLanes|=u&~(s&~e))}function ci(t,e,l){t.pendingLanes|=e,t.suspendedLanes&=~e;var a=31-Oe(e);t.entangledLanes|=e,t.entanglements[a]=t.entanglements[a]|1073741824|l&261930}function hn(t,e){var l=t.entangledLanes|=e;for(t=t.entanglements;l;){var a=31-Oe(l),n=1<<a;n&e|t[a]&e&&(t[a]|=e),l&=~n}}function Wl(t,e){var l=e&-e;return l=(l&42)!==0?1:Hl(l),(l&(t.suspendedLanes|e))!==0?0:l}function Hl(t){switch(t){case 2:t=1;break;case 8:t=4;break;case 32:t=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:t=128;break;case 268435456:t=134217728;break;default:t=0}return t}function mu(t){return t&=-t,2<t?8<t?(t&134217727)!==0?32:268435456:8:2}function hu(){var t=dt.p;return t!==0?t:(t=window.event,t===void 0?32:Eh(t.type))}function Ze(t,e){var l=dt.p;try{return dt.p=t,e()}finally{dt.p=l}}var ae=Math.random().toString(36).slice(2),ve="__reactFiber$"+ae,Ke="__reactProps$"+ae,vn="__reactContainer$"+ae,So="__reactEvents$"+ae,Ih="__reactListeners$"+ae,Ph="__reactHandles$"+ae,bo="__reactResources$"+ae,vu="__reactMarker$"+ae,si="__reactLoad$"+ae;function ri(t){delete t[ve],delete t[Ke],delete t[Ih],delete t[Ph]}function La(t){var e;if(e=t[ve])return e;for(var l=t.parentNode;l;){if(e=l[vn]||l[ve]){if(l=e.alternate,e.child!==null||l!==null&&l.child!==null)for(t=uh(t);t!==null;){if(l=t[ve])return l;t=uh(t)}return e}t=l,l=t.parentNode}return null}function yn(t){if(t=t[ve]||t[vn]){var e=t.tag;if(e===5||e===6||e===13||e===31||e===26||e===27||e===3)return t}return null}function yu(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t.stateNode;throw Error(f(33))}function gn(t){var e=t[bo];return e||(e=t[bo]={hoistableStyles:new Map,hoistableScripts:new Map}),e}function je(t){t[vu]=!0}function xo(t){t[si]=void 0}var jo=new Set,To={};function Va(t,e){pn(t,e),pn(t+"Capture",e)}function pn(t,Line truncated
`).replace(Y0,"")}function qm(t,e){return e=Bm(e),Bm(t)===e}function Vt(t,e,l,a,n,u){switch(l){case"children":if(typeof a=="string")e==="body"||e==="textarea"&&a===""||bn(t,a);else if(typeof a=="number"||typeof a=="bigint")e!=="body"&&bn(t,""+a);else return;break;case"className":fi(t,"class",a);break;case"tabIndex":fi(t,"tabindex",a);break;case"dir":case"role":case"viewBox":case"width":case"height":fi(t,l,a);break;case"style":Do(t,a,u);return;case"data":if(e!=="object"){fi(t,"data",a);break}case"src":case"href":if(a===""&&(e!=="a"||l!=="href")){t.removeAttribute(l);break}if(a==null||typeof a=="function"||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=di(a),t.setAttribute(l,a);break;case"action":case"formAction":if(typeof a=="function"){t.setAttribute(l,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof u=="function"&&(l==="formAction"?(e!=="input"&&Vt(t,e,"name",n.name,n,null),Vt(t,e,"formEncType",n.formEncType,n,null),Vt(t,e,"formMethod",n.formMethod,n,null),Vt(t,e,"formTarget",n.formTarget,n,null)):(Vt(t,e,"encType",n.encType,n,null),Vt(t,e,"method",n.method,n,null),Vt(t,e,"target",n.target,n,null)));if(a==null||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=di(a),t.setAttribute(l,a);break;case"onClick":a!=null&&(t.onclick=wl);return;case"onScroll":a!=null&&Et("scroll",t);return;case"onScrollEnd":a!=null&&Et("scrollend",t);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(f(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(f(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"multiple":t.multiple=a&&typeof a!="function"&&typeof a!="symbol";break;case"muted":t.muted=a&&typeof a!="function"&&typeof a!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(a==null||typeof a=="function"||typeof a=="boolean"||typeof a=="symbol"){t.removeAttribute("xlink:href");break}l=di(a),t.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",l);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"credentialless":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":a&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,""):t.removeAttribute(l);break;case"capture":case"download":a===!0?t.setAttribute(l,""):a!==!1&&a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"cols":case"rows":case"size":case"span":a!=null&&typeof a!="function"&&typeof a!="symbol"&&!isNaN(a)&&1<=a?t.setAttribute(l,a):t.removeAttribute(l);break;case"rowSpan":case"start":a==null||typeof a=="function"||typeof a=="symbol"||isNaN(a)?t.removeAttribute(l):t.setAttribute(l,a);break;case"popover":Et("beforetoggle",t),Et("toggle",t),oi(t,"popover",a);break;case"xlinkActuate":$l(t,"http://www.w3.org/1999/xlink","xlink:actuate",a);break;case"xlinkArcrole":$l(t,"http://www.w3.org/1999/xlink","xlink:arcrole",a);break;case"xlinkRole":$l(t,"http://www.w3.org/1999/xlink","xlink:role",a);break;case"xlinkShow":$l(t,"http://www.w3.org/1999/xlink","xlink:show",a);break;case"xlinkTitle":$l(t,"http://www.w3.org/1999/xlink","xlink:title",a);break;case"xlinkType":$l(t,"http://www.w3.org/1999/xlink","xlink:type",a);break;case"xmlBase":$l(t,"http://www.w3.org/XML/1998/namespace","xml:base",a);break;case"xmlLang":$l(t,"http://www.w3.org/XML/1998/namespace","xml:lang",a);break;case"xmlSpace":$l(t,"http://www.w3.org/XML/1998/namespace","xml:space",a);break;case"is":oi(t,"is",a);break;case"innerText":case"textContent":return;default:if(!(2<l.length)||l[0]!=="o"&&l[0]!=="O"||l[1]!=="n"&&l[1]!=="N")l=uv.get(l)||l,oi(t,l,a);else return}Ht=!0}function Hr(t,e,l,a,n,u){switch(l){case"style":Do(t,a,u);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(f(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(f(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"children":if(typeof a=="string")bn(t,a);else if(typeof a=="number"||typeof a=="bigint")bn(t,""+a);else return;break;case"onScroll":a!=null&&Et("scroll",t);return;case"onScrollEnd":a!=null&&Et("scroLine truncated
`+a.stack}}var te=Object.prototype.hasOwnProperty,Pe=i.unstable_scheduleCallback,qe=i.unstable_cancelCallback,_l=i.unstable_shouldYield,ol=i.unstable_requestPaint,Wt=i.unstable_now,Ul=i.unstable_getCurrentPriorityLevel,mn=i.unstable_ImmediatePriority,Qa=i.unstable_UserBlockingPriority,Se=i.unstable_NormalPriority,Xa=i.unstable_LowPriority,ma=i.unstable_IdlePriority,su=i.log,ru=i.unstable_setDisableYieldValue,Fl=null,Ae=null;function fl(t){if(typeof su=="function"&&ru(t),Ae&&typeof Ae.setStrictMode=="function")try{Ae.setStrictMode(Fl,t)}catch{}}var Oe=Math.clz32?Math.clz32:du,ou=Math.log,fu=Math.LN2;function du(t){return t>>>=0,t===0?32:31-(ou(t)/fu|0)|0}var k=256,U=262144,W=4194304;function tt(t){var e=t&42;if(e!==0)return e;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&-t;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function M(t,e,l){var a=t.pendingLanes;if(a===0)return 0;var n=0,u=t.suspendedLanes,s=t.pingedLanes;t=t.warmLanes;var o=a&134217727;return o!==0?(a=o&~u,a!==0?n=tt(a):(s&=o,s!==0?n=tt(s):l||(l=o&~t,l!==0&&(n=tt(l))))):(o=a&~u,o!==0?n=tt(o):s!==0?n=tt(s):l||(l=a&~t,l!==0&&(n=tt(l)))),n===0?0:e!==0&&e!==n&&(e&u)===0&&(u=n&-n,l=e&-e,u>=l||u===32&&(l&4194048)!==0)?e:n}function Z(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function et(t,e){(e&8)!==0&&(e|=e&32);var l=t.entangledLanes;if(l!==0)for(t=t.entanglements,l&=e;0<l;){var a=31-Oe(l),n=1<<a;e|=t[a],l&=~n}return e}function Bt(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Mt(){var t=W;return W<<=1,(W&62914560)===0&&(W=4194304),t}function be(t){for(var e=[],l=0;31>l;l++)e.push(t);return e}function xe(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function Dc(t,e,l,a,n,u){var s=t.pendingLanes;t.pendingLanes=l,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=l,t.entangledLanes&=l,t.errorRecoveryDisabledLanes&=l,t.shellSuspendCounter=0;var o=t.entanglements,m=t.expirationTimes,x=t.hiddenUpdates;for(l=s&~l;0<l;){var E=31-Oe(l),R=1<<E;o[E]=0,m[E]=-1;var g=x[E];if(g!==null)for(x[E]=null,E=0;E<g.length;E++){var _=g[E];_!==null&&(_.lane&=-536870913)}l&=~R}a!==0&&ci(t,a,0),u!==0&&n===0&&t.tag!==0&&(t.suspendedLanes|=u&~(s&~e))}function ci(t,e,l){t.pendingLanes|=e,t.suspendedLanes&=~e;var a=31-Oe(e);t.entangledLanes|=e,t.entanglements[a]=t.entanglements[a]|1073741824|l&261930}function hn(t,e){var l=t.entangledLanes|=e;for(t=t.entanglements;l;){var a=31-Oe(l),n=1<<a;n&e|t[a]&e&&(t[a]|=e),l&=~n}}function Wl(t,e){var l=e&-e;return l=(l&42)!==0?1:Hl(l),(l&(t.suspendedLanes|e))!==0?0:l}function Hl(t){switch(t){case 2:t=1;break;case 8:t=4;break;case 32:t=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:t=128;break;case 268435456:t=134217728;break;default:t=0}return t}function mu(t){return t&=-t,2<t?8<t?(t&134217727)!==0?32:268435456:8:2}function hu(){var t=dt.p;return t!==0?t:(t=window.event,t===void 0?32:Eh(t.type))}function Ze(t,e){var l=dt.p;try{return dt.p=t,e()}finally{dt.p=l}}var ae=Math.random().toString(36).slice(2),ve="__reactFiber$"+ae,Ke="__reactProps$"+ae,vn="__reactContainer$"+ae,So="__reactEvents$"+ae,Ih="__reactListeners$"+ae,Ph="__reactHandles$"+ae,bo="__reactResources$"+ae,vu="__reactMarker$"+ae,si="__reactLoad$"+ae;function ri(t){delete t[ve],delete t[Ke],delete t[Ih],delete t[Ph]}function La(t){var e;if(e=t[ve])return e;for(var l=t.parentNode;l;){if(e=l[vn]||l[ve]){if(l=e.alternate,e.child!==null||l!==null&&l.child!==null)for(t=uh(t);t!==null;){if(l=t[ve])return l;t=uh(t)}return e}t=l,l=t.parentNode}return null}function yn(t){if(t=t[ve]||t[vn]){var e=t.tag;if(e===5||e===6||e===13||e===31||e===26||e===27||e===3)return t}return null}function yu(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t.stateNode;throw Error(f(33))}function gn(t){var e=t[bo];return e||(e=t[bo]={hoistableStyles:new Map,hoistableScripts:new Map}),e}function je(t){t[vu]=!0}function xo(t){t[si]=void 0}var jo=new Set,To={};function Va(t,e){pn(t,e),pn(t+"Capture",e)}function pn(t,Line truncated
`).replace(Y0,"")}function qm(t,e){return e=Bm(e),Bm(t)===e}function Vt(t,e,l,a,n,u){switch(l){case"children":if(typeof a=="string")e==="body"||e==="textarea"&&a===""||bn(t,a);else if(typeof a=="number"||typeof a=="bigint")e!=="body"&&bn(t,""+a);else return;break;case"className":fi(t,"class",a);break;case"tabIndex":fi(t,"tabindex",a);break;case"dir":case"role":case"viewBox":case"width":case"height":fi(t,l,a);break;case"style":Do(t,a,u);return;case"data":if(e!=="object"){fi(t,"data",a);break}case"src":case"href":if(a===""&&(e!=="a"||l!=="href")){t.removeAttribute(l);break}if(a==null||typeof a=="function"||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=di(a),t.setAttribute(l,a);break;case"action":case"formAction":if(typeof a=="function"){t.setAttribute(l,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof u=="function"&&(l==="formAction"?(e!=="input"&&Vt(t,e,"name",n.name,n,null),Vt(t,e,"formEncType",n.formEncType,n,null),Vt(t,e,"formMethod",n.formMethod,n,null),Vt(t,e,"formTarget",n.formTarget,n,null)):(Vt(t,e,"encType",n.encType,n,null),Vt(t,e,"method",n.method,n,null),Vt(t,e,"target",n.target,n,null)));if(a==null||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=di(a),t.setAttribute(l,a);break;case"onClick":a!=null&&(t.onclick=wl);return;case"onScroll":a!=null&&Et("scroll",t);return;case"onScrollEnd":a!=null&&Et("scrollend",t);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(f(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(f(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"multiple":t.multiple=a&&typeof a!="function"&&typeof a!="symbol";break;case"muted":t.muted=a&&typeof a!="function"&&typeof a!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(a==null||typeof a=="function"||typeof a=="boolean"||typeof a=="symbol"){t.removeAttribute("xlink:href");break}l=di(a),t.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",l);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"credentialless":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":a&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,""):t.removeAttribute(l);break;case"capture":case"download":a===!0?t.setAttribute(l,""):a!==!1&&a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"cols":case"rows":case"size":case"span":a!=null&&typeof a!="function"&&typeof a!="symbol"&&!isNaN(a)&&1<=a?t.setAttribute(l,a):t.removeAttribute(l);break;case"rowSpan":case"start":a==null||typeof a=="function"||typeof a=="symbol"||isNaN(a)?t.removeAttribute(l):t.setAttribute(l,a);break;case"popover":Et("beforetoggle",t),Et("toggle",t),oi(t,"popover",a);break;case"xlinkActuate":$l(t,"http://www.w3.org/1999/xlink","xlink:actuate",a);break;case"xlinkArcrole":$l(t,"http://www.w3.org/1999/xlink","xlink:arcrole",a);break;case"xlinkRole":$l(t,"http://www.w3.org/1999/xlink","xlink:role",a);break;case"xlinkShow":$l(t,"http://www.w3.org/1999/xlink","xlink:show",a);break;case"xlinkTitle":$l(t,"http://www.w3.org/1999/xlink","xlink:title",a);break;case"xlinkType":$l(t,"http://www.w3.org/1999/xlink","xlink:type",a);break;case"xmlBase":$l(t,"http://www.w3.org/XML/1998/namespace","xml:base",a);break;case"xmlLang":$l(t,"http://www.w3.org/XML/1998/namespace","xml:lang",a);break;case"xmlSpace":$l(t,"http://www.w3.org/XML/1998/namespace","xml:space",a);break;case"is":oi(t,"is",a);break;case"innerText":case"textContent":return;default:if(!(2<l.length)||l[0]!=="o"&&l[0]!=="O"||l[1]!=="n"&&l[1]!=="N")l=uv.get(l)||l,oi(t,l,a);else return}Ht=!0}function Hr(t,e,l,a,n,u){switch(l){case"style":Do(t,a,u);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(f(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(f(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"children":if(typeof a=="string")bn(t,a);else if(typeof a=="number"||typeof a=="bigint")bn(t,""+a);else return;break;case"onScroll":a!=null&&Et("scroll",t);return;case"onScrollEnd":a!=null&&Et("scroLine truncated
`;Bt&&(be+=Bt+`
`),Mt&&(be+=(Array.isArray(Mt)?Mt.join(`
`):String(Mt))+`
`),be+=JSON.stringify(et,null,2).slice(0,4e3),jl(be),ce("已返回登录信息,见下方")}catch(et){ce("发起失败:"+et.message)}},[]),U=d.useCallback(async()=>{const M=V.trim();if(!M){X("请填要查配额的用户 id");return}try{const Z=await I(K.adminQuotaUser(M));X("用户 "+M.slice(0,8)+":本月用量 "+fo(Z.used_usd_micros)+" / 配额 "+fo(Z.quota_usd_micros)+(Z.remaining_usd_micros!==null&&Z.remaining_usd_micros!==void 0?"(剩余 "+fo(Z.remaining_usd_micros)+")":"")+" | 月份 "+(Z.month||""))}catch(Z){X(oo(Z))}},[V]),W=d.useCallback(async()=>{if(window.confirm("确定手动触发 sweep 吗?超阈值的陈旧 running 任务会被标为 interrupted。"))try{const Z=(await I(K.adminSweep,"POST",{})).swept||[];X("sweep 完成,清理 "+Z.length+" 个任务"+(Z.length?":"+Z.join("、"):""))}catch(M){X(oo(M))}},[]),tt=d.useCallback(async()=>{try{const M=await I(K.adminBackup,"POST",{});X("备份完成:backend="+M.backend+" | 路径 "+M.path+" | 大小 "+M.size_bytes+" 字节 | 任务 "+M.task_count+" 行")}catch(M){X(oo(M))}},[]);return{keySaved:i,keyState:h,quotaText:S,healthText:A,adminGate:H,adminBody:Y,adminResult:w,adminUserId:V,setAdminUserId:at,projects:L,projectId:bt,setProjectId:zt,projectMsg:J,githubRepos:it,workers:rt,workersSummary:Qt,workersError:kt,members:ot,memberMsg:q,memberError:lt,skills:yt,apps:we,mcpServers:y,skillMsg:ut,skillError:Tt,mcpMsg:Ot,mcpOauthMsg:dt,mcpOauthBox:Ga,envs:Le,envCount:Yt,envMsg:Kl,envRepoLine:fa,envRepoBar:zl,envRepoMsg:Tl,envSelect:se,setEnvSelect:re,models:rl,modelCustomVisible:da,refreshHealth:qe,loadQuota:_l,probeAdmin:ol,refreshProjects:Wt,loadGithubRepos:Ul,loadRelayModels:mn,refreshWorkers:Qa,refreshEnvs:Se,refreshMembers:Xa,refreshSkills:ma,saveKeyInput:su,clearKeyInput:ru,createProject:Fl,createEnv:Ae,archiveEnv:fl,saveEnvRepo:Oe,inviteMember:ou,changeMemberRole:fu,reloadMcp:du,mcpOauth:k,adminQuota:U,adminSweep:W,adminBackup:tt}}const mo=20;function lg(){const[i,r]=d.useState([]),[h,f]=d.useState(""),[S,N]=d.useState(""),[A,p]=d.useState(""),[H,O]=d.useState(""),[Y,j]=d.useState(0),[w,X]=d.useState(0),[V,at]=d.useState({}),L=d.useRef(0),vt=d.useRef({status:"",executor:"",q:""});vt.current={status:S,executor:A,q:H};const bt=d.useRef(""),zt=d.useCallback(async(ot,st,q)=>{const C=ot!==void 0?ot:st;if(!C){r([]),X(0),at({});return}bt.current=C;const lt=q!==void 0?q:L.current,gt=vt.current;try{const yt=await I(K.tasksPage(C,mo,lt*mo,gt.status,gt.executor,gt.q));r(yt.tasks||[]),X(yt.total??(yt.tasks||[]).length),at(yt.status_counts||{}),f("")}catch(yt){f("加载失败:"+yt.message)}},[]),J=d.useCallback(ot=>{const st=Math.max(0,ot);L.current=st,j(st)},[]),Rt=d.useCallback(ot=>{J(ot),zt(bt.current||void 0,void 0,ot)},[zt,J]);d.useEffect(()=>{if(!bt.current)return;const ot=setTimeout(()=>{zt(bt.current,void 0,0)},300);return()=>clearTimeout(ot)},[S,A,H]);const it=d.useCallback(ot=>{N(ot),J(0)},[J]),Q=d.useCallback(ot=>{p(ot),J(0)},[J]),rt=d.useCallback(ot=>{O(ot),J(0)},[J]),Ut=d.useCallback(async ot=>{J(0),await zt(ot,void 0,0)},[zt,J]),Qt=Math.max(1,Math.ceil(w/mo)),Nt=w===0?"共 0 个任务":"共 "+w+" 个任务,第 "+(Y+1)+" / "+Qt+" 页",kt=i,B=d.useCallback(async(ot,st)=>{const q={...ot};st.length&&(q.upload_id=await Py(st));const C=await I(K.tasksCreate,"POST",q);return"任务已建:"+Ne(C.id)},[]);return{tasks:i,taskError:h,statusFilter:S,setStatusFilter:it,executorFilter:A,setExecutorFilter:Q,searchText:H,setSearchText:rt,filteredTasks:kt,taskCountText:Nt,refreshTasks:zt,refreshTasksFirstPage:Ut,page:Y,totalPages:Qt,taskTotal:w,taskStatusCounts:V,goToPage:Rt,dispatch:B}}function ag(i,r){const[h,f]=d.useState(null),[S,N]=d.useState(""),[A,p]=d.useState(""),[H,O]=d.useState(""),[Y,j]=d.useState(""),[w,X]=d.useState(void 0),[V,at]=d.useState(!1),[L,vt]=d.useState(""),[bt,zt]=d.useState([]),[J,Rt]=d.useState([]),[it,Q]=d.useState(""),[rt,Ut]=d.useState([]),[Qt,Nt]=d.useState(""),[kt,B]=d.useState(""),[ot,st]=d.useState([]),[q,C]=d.useState(""),[lt,gt]=d.useState(""),[yt,ge]=d.useState(""),[we,Ce]=d.useState(""),[y,D]=d.useState([]),[ut,nt]=d.useState([]),[Tt,xt]=d.useState(""),[Ot,$]=d.useState([]),[dt,ce]=d.useState(""),[Ga,jl]=d.useState(""),[Le,le]=d.useState(""),[Yt,he]=d.useState(""),[Kl,pe]=d.useState([]),[fa,Ve]=d.useState(""),[zl,Rl]=d.useState(null),Tl=d.useRef(null),Be=d.useRef(null),se=d.useRef(null);se.current=h;const re=d.useCallback(()=>{if(Tl.current){try{Tl.current.abort()}catch{}Tl.current=null}Be.current&&(clearTimeout(Be.current),Be.current=null)},[]),rl=d.useCallback(async k=>{try{const U=await I(K.taskAgents(k));Rt(U.agents||[]),Q(U.note||"")}catch(U){Rt([]),Q("子代理链路加载失败:"+U.message)}},[]),Jl=d.useCallback(k=>{Be.current&&clearTimeout(Be.current),Be.current=setTimeout(()=>void rl(k),1e3)},[rl]),da=d.useCallback(async k=>{if(!k.coordinator_parts){zt([]);return}try{const U=await I(K.tasksByParent(k.project_id||"",k.id));zt(U.tasks||[])}catch{zt([])}},[]),Dl=d.useCallback(async k=>{trLine truncated
`),be+=JSON.stringify(et,null,2).slice(0,4e3),jl(be),ce("已返回登录信息,见下方")}catch(et){ce("发起失败:"+et.message)}},[]),U=d.useCallback(async()=>{const M=V.trim();if(!M){X("请填要查配额的用户 id");return}try{const Z=await I(K.adminQuotaUser(M));X("用户 "+M.slice(0,8)+":本月用量 "+fo(Z.used_usd_micros)+" / 配额 "+fo(Z.quota_usd_micros)+(Z.remaining_usd_micros!==null&&Z.remaining_usd_micros!==void 0?"(剩余 "+fo(Z.remaining_usd_micros)+")":"")+" | 月份 "+(Z.month||""))}catch(Z){X(oo(Z))}},[V]),W=d.useCallback(async()=>{if(window.confirm("确定手动触发 sweep 吗?超阈值的陈旧 running 任务会被标为 interrupted。"))try{const Z=(await I(K.adminSweep,"POST",{})).swept||[];X("sweep 完成,清理 "+Z.length+" 个任务"+(Z.length?":"+Z.join("、"):""))}catch(M){X(oo(M))}},[]),tt=d.useCallback(async()=>{try{const M=await I(K.adminBackup,"POST",{});X("备份完成:backend="+M.backend+" | 路径 "+M.path+" | 大小 "+M.size_bytes+" 字节 | 任务 "+M.task_count+" 行")}catch(M){X(oo(M))}},[]);return{keySaved:i,keyState:h,quotaText:S,healthText:A,adminGate:H,adminBody:Y,adminResult:w,adminUserId:V,setAdminUserId:at,projects:L,projectId:bt,setProjectId:zt,projectMsg:J,githubRepos:it,workers:rt,workersSummary:Qt,workersError:kt,members:ot,memberMsg:q,memberError:lt,skills:yt,apps:we,mcpServers:y,skillMsg:ut,skillError:Tt,mcpMsg:Ot,mcpOauthMsg:dt,mcpOauthBox:Ga,envs:Le,envCount:Yt,envMsg:Kl,envRepoLine:fa,envRepoBar:zl,envRepoMsg:Tl,envSelect:se,setEnvSelect:re,models:rl,modelCustomVisible:da,refreshHealth:qe,loadQuota:_l,probeAdmin:ol,refreshProjects:Wt,loadGithubRepos:Ul,loadRelayModels:mn,refreshWorkers:Qa,refreshEnvs:Se,refreshMembers:Xa,refreshSkills:ma,saveKeyInput:su,clearKeyInput:ru,createProject:Fl,createEnv:Ae,archiveEnv:fl,saveEnvRepo:Oe,inviteMember:ou,changeMemberRole:fu,reloadMcp:du,mcpOauth:k,adminQuota:U,adminSweep:W,adminBackup:tt}}const mo=20;function lg(){const[i,r]=d.useState([]),[h,f]=d.useState(""),[S,C]=d.useState(""),[A,p]=d.useState(""),[H,O]=d.useState(""),[Y,j]=d.useState(0),[w,X]=d.useState(0),[V,at]=d.useState({}),L=d.useRef(0),vt=d.useRef({status:"",executor:"",q:""});vt.current={status:S,executor:A,q:H};const bt=d.useRef(""),zt=d.useCallback(async(ot,st,q)=>{const N=ot!==void 0?ot:st;if(!N){r([]),X(0),at({});return}bt.current=N;const lt=q!==void 0?q:L.current,gt=vt.current;try{const yt=await I(K.tasksPage(N,mo,lt*mo,gt.status,gt.executor,gt.q));r(yt.tasks||[]),X(yt.total??(yt.tasks||[]).length),at(yt.status_counts||{}),f("")}catch(yt){f("加载失败:"+yt.message)}},[]),J=d.useCallback(ot=>{const st=Math.max(0,ot);L.current=st,j(st)},[]),Rt=d.useCallback(ot=>{J(ot),zt(bt.current||void 0,void 0,ot)},[zt,J]);d.useEffect(()=>{if(!bt.current)return;const ot=setTimeout(()=>{zt(bt.current,void 0,0)},300);return()=>clearTimeout(ot)},[S,A,H]);const it=d.useCallback(ot=>{C(ot),J(0)},[J]),Q=d.useCallback(ot=>{p(ot),J(0)},[J]),rt=d.useCallback(ot=>{O(ot),J(0)},[J]),Ut=d.useCallback(async ot=>{J(0),await zt(ot,void 0,0)},[zt,J]),Qt=Math.max(1,Math.ceil(w/mo)),Nt=w===0?"共 0 个任务":"共 "+w+" 个任务,第 "+(Y+1)+" / "+Qt+" 页",kt=i,B=d.useCallback(async(ot,st)=>{const q={...ot};st.length&&(q.upload_id=await Py(st));const N=await I(K.tasksCreate,"POST",q);return"任务已建:"+Ne(N.id)},[]);return{tasks:i,taskError:h,statusFilter:S,setStatusFilter:it,executorFilter:A,setExecutorFilter:Q,searchText:H,setSearchText:rt,filteredTasks:kt,taskCountText:Nt,refreshTasks:zt,refreshTasksFirstPage:Ut,page:Y,totalPages:Qt,taskTotal:w,taskStatusCounts:V,goToPage:Rt,dispatch:B}}function ag(i,r){const[h,f]=d.useState(null),[S,C]=d.useState(""),[A,p]=d.useState(""),[H,O]=d.useState(""),[Y,j]=d.useState(""),[w,X]=d.useState(void 0),[V,at]=d.useState(!1),[L,vt]=d.useState(""),[bt,zt]=d.useState([]),[J,Rt]=d.useState([]),[it,Q]=d.useState(""),[rt,Ut]=d.useState([]),[Qt,Nt]=d.useState(""),[kt,B]=d.useState(""),[ot,st]=d.useState([]),[q,N]=d.useState(""),[lt,gt]=d.useState(""),[yt,ge]=d.useState(""),[we,Ce]=d.useState(""),[y,D]=d.useState([]),[ut,nt]=d.useState([]),[Tt,xt]=d.useState(""),[Ot,$]=d.useState([]),[dt,ce]=d.useState(""),[Ga,jl]=d.useState(""),[Le,le]=d.useState(""),[Yt,he]=d.useState(""),[Kl,pe]=d.useState([]),[fa,Ve]=d.useState(""),[zl,Rl]=d.useState(null),Tl=d.useRef(null),Be=d.useRef(null),se=d.useRef(null);se.current=h;const re=d.useCallback(()=>{if(Tl.current){try{Tl.current.abort()}catch{}Tl.current=null}Be.current&&(clearTimeout(Be.current),Be.current=null)},[]),rl=d.useCallback(async k=>{try{const U=await I(K.taskAgents(k));Rt(U.agents||[]),Q(U.note||"")}catch(U){Rt([]),Q("子代理链路加载失败:"+U.message)}},[]),Jl=d.useCallback(k=>{Be.current&&clearTimeout(Be.current),Be.current=setTimeout(()=>void rl(k),1e3)},[rl]),da=d.useCallback(async k=>{if(!k.coordinator_parts){zt([]);return}try{const U=await I(K.tasksByParent(k.project_id||"",k.id));zt(U.tasks||[])}catch{zt([])}},[]),Dl=d.useCallback(async k=>{trLine truncated
`);hn=Hl.pop()||"";for(const mu of Hl){const hu=mu.split(`
`).filter(Ze=>Ze.indexOf("data: ")===0).map(Ze=>Ze.slice(6)).join(`
`);if(hu)try{const Ze=JSON.parse(hu);if(Ze.type==="snapshot")tt=Ze.entries||[],Object.keys(M).forEach(ae=>delete M[ae]),tt.forEach((ae,ve)=>{ae.id&&(M[ae.id]=ve)}),Z=Ze.note||"",st([...tt]),C(Z),Jl(k);else if(Ze.type==="entry"){const ae=Ze.entry;ae.id&&ae.id in M?tt[M[ae.id]]=ae:(ae.id&&(M[ae.id]=tt.length),tt.push(ae)),st([...tt]),Jl(k)}else Ze.type==="done"&&(et=!0,Bt=Ze.status,gt(Bt))}catch{}}if(et)break}}catch(Mt){if(Mt&&Mt.name==="AbortError")return;C("链路流中断:"+Mt.message);return}et&&r()},[re,Jl,r]),Se=d.useCallback(async k=>{re();try{const U=await I(K.task(k));f(U);let W="";U.result_text&&(W+=`=== result ===
`);if(hu)try{const Ze=JSON.parse(hu);if(Ze.type==="snapshot")tt=Ze.entries||[],Object.keys(M).forEach(ae=>delete M[ae]),tt.forEach((ae,ve)=>{ae.id&&(M[ae.id]=ve)}),Z=Ze.note||"",st([...tt]),N(Z),Jl(k);else if(Ze.type==="entry"){const ae=Ze.entry;ae.id&&ae.id in M?tt[M[ae.id]]=ae:(ae.id&&(M[ae.id]=tt.length),tt.push(ae)),st([...tt]),Jl(k)}else Ze.type==="done"&&(et=!0,Bt=Ze.status,gt(Bt))}catch{}}if(et)break}}catch(Mt){if(Mt&&Mt.name==="AbortError")return;N("链路流中断:"+Mt.message);return}et&&r()},[re,Jl,r]),Se=d.useCallback(async k=>{re();try{const U=await I(K.task(k));f(U);let W="";U.result_text&&(W+=`=== result ===
`+U.result_text.slice(0,8e3)+`
`),U.error_text&&(W+=`=== error ===
`+U.error_text.slice(0,8e3)+`
`),W||(W="(暂无 result/error 输出)"),N(W),j(""),X(void 0),Dl(k),at(!1),vt(""),ce(""),he(""),mn(U.status);const tt=i(U.project_id),M=tt==="maintainer"||tt==="owner",Z=tt==="contributor"||M,et=uu.includes(U.status),Bt=[];["failed","interrupted","merge-failed"].includes(U.status)&&M||Bt.push("重试:不可用——仅 failed/interrupted/merge-failed 可重试"+(M?"":",且仅 maintainer 可重试")),!et&&Z||Bt.push("取消:不可用——"+(et?"终态任务不可取消":"仅项目成员(contributor 及以上)可取消")),U.status==="completed"&&M||Bt.push("合并:不可用——仅 completed 可合并"+(M?"":",且仅 maintainer 可合并")),U.status==="merged"&&M||Bt.push("提 PR:不可用——仅 merged 可提 PR"+(M?"":",且仅 maintainer 可提 PR")),O(Bt.join(";")),p(""),Qa(k,U.status),rl(k),da(U),Pe(k),te(k),ol(k),Wt(k),Ul(k),qe(k),_l(k)}catch(U){f(null),N("加载失败:"+U.message)}},[re,Qa,rl,da,Pe,te,ol,Wt,Ul,qe,_l,Dl,i]),Xa=d.useCallback(async()=>{const k=se.current;if(k){try{const U=await I(K.task(k.id));f(U);let W="";U.result_text&&(W+=`=== result ===
`),W||(W="(暂无 result/error 输出)"),C(W),j(""),X(void 0),Dl(k),at(!1),vt(""),ce(""),he(""),mn(U.status);const tt=i(U.project_id),M=tt==="maintainer"||tt==="owner",Z=tt==="contributor"||M,et=uu.includes(U.status),Bt=[];["failed","interrupted","merge-failed"].includes(U.status)&&M||Bt.push("重试:不可用——仅 failed/interrupted/merge-failed 可重试"+(M?"":",且仅 maintainer 可重试")),!et&&Z||Bt.push("取消:不可用——"+(et?"终态任务不可取消":"仅项目成员(contributor 及以上)可取消")),U.status==="completed"&&M||Bt.push("合并:不可用——仅 completed 可合并"+(M?"":",且仅 maintainer 可合并")),U.status==="merged"&&M||Bt.push("提 PR:不可用——仅 merged 可提 PR"+(M?"":",且仅 maintainer 可提 PR")),O(Bt.join(";")),p(""),Qa(k,U.status),rl(k),da(U),Pe(k),te(k),ol(k),Wt(k),Ul(k),qe(k),_l(k)}catch(U){f(null),C("加载失败:"+U.message)}},[re,Qa,rl,da,Pe,te,ol,Wt,Ul,qe,_l,Dl,i]),Xa=d.useCallback(async()=>{const k=se.current;if(k){try{const U=await I(K.task(k.id));f(U);let W="";U.result_text&&(W+=`=== result ===
`+U.result_text.slice(0,8e3)+`
`),U.error_text&&(W+=`=== error ===
`+U.error_text.slice(0,8e3)+`
`),N(W||"(暂无 result/error 输出)")}catch{}te(k.id),Pe(k.id),qe(k.id),_l(k.id),da(k),rl(k.id),ol(k.id),Wt(k.id),Ul(k.id),Dl(k.id)}},[te,Pe,qe,_l,da,rl,ol,Wt,Ul,Dl]),ma=d.useCallback(async k=>{const U=se.current;if(!U)return;const W={cancel:"确定取消该任务吗?",merge:"确定把 completed 任务标为 merged 吗?",pr:"确定为该任务提 PR 吗?(会在仓库真实建 PR)"};if(!(W[k]&&!window.confirm(W[k])))try{const tt=await I(K.taskAction(U.id,k),"POST",{});p(k+" 已提交:"+JSON.stringify(tt).slice(0,300)),r(),await Se(U.id)}catch(tt){p(k+" 失败:"+tt.message)}},[Se,r]),su=d.useCallback(async()=>{const k=se.current;if(k)try{const U=await I(K.taskCost(k.id));j(JSON.stringify(U,null,2).slice(0,4e3))}catch(U){j("查成本失败:"+U.message)}},[]),ru=d.useCallback(async k=>{const U=se.current;if(U){if(!k.trim()){vt("分支消息必填");return}if(!uu.includes(U.status)){vt("仅终态任务可分支");return}try{const W=await I(K.taskFork(U.id),"POST",{message:k.trim()});vt("分支任务已建:"+W.id.slice(0,8)),r()}catch(W){vt("分支失败:"+W.message)}}},[r]),Fl=d.useCallback(async k=>{const U=se.current;if(U)try{await I(K.taskReviews(U.id),"POST",k),Nt("评审已登记(requested)"),await qe(U.id)}catch(W){Nt("登记评审需求失败:"+W.message)}},[qe]),Ae=d.useCallback(k=>{const U=se.current;if(!U)return;const W=window.prompt("输入该条评审的 findings(将写回记录并标为 completed):");W!==null&&I(K.taskReviewFindings(U.id,k),"POST",{findings:W}).then(()=>void qe(U.id)).catch(tt=>Nt("写 findings 失败:"+tt.message))},[qe]),fl=d.useCallback((k,U)=>{const W=se.current;if(!W)return;const tt=U==="approved"?"approve":"deny";I(K.taskApprovalDecision(W.id,k,tt),"POST",{}).then(()=>{xt("已记录"+(U==="approved"?"允许":"拒绝")+"(当前版本只留回执,不拦截实时行为)"),_l(W.id)}).catch(M=>xt("决定失败:"+M.message))},[_l]),Oe=d.useCallback(async(k,U,W)=>{const tt=se.current;if(tt){if(!U.trim()){ce("目标任务 id 不能为空");return}if(!W.trim()){ce("消息正文不能为空");return}try{const M=await I(K.taskMessagesTo(U.trim()),"POST",{from_task_id:tt.id,body:W.trim(),mode:k});ce("已发送("+M.delivery+")"),await ol(tt.id)}catch(M){ce("发送失败:"+M.message)}}},[ol]),ou=d.useCallback((k,U)=>{const W=se.current;if(!W)return;if(!["completed","failed","interrupted"].includes(W.status)){he("仅 completed/failed/interrupted 任务可操作线程(merged/merge-failed 不可改,服务端同样 409)");return}const tt={};if(k==="rename"){if(!U.trim()){he("新线程名不能为空");return}tt.name=U.trim()}I(K.taskThreadAction(W.id,k),"POST",tt).then(M=>{he(k+" 已提交:"+JSON.stringify(M).slice(0,300)),Wt(W.id),Ul(W.id)}).catch(M=>he(k+" 失败:"+M.message))},[Wt,Ul]),fu=d.useCallback(async k=>{const U=se.current;if(!U)return;const W=k.trim();if(!W){Ve("消息不能为空");return}try{uu.includes(U.status)&&U.status!=="merged"?(await I(K.taskContinue(U.id),"POST",{message:W}),Ve("已重新排队,同一会话继续"),r(),await Se(U.id)):["queued","running"].includes(U.status)?(await I(K.taskFollowups(U.id),"POST",{body:W,mode:"direct"}),Ve("已直接发送"),await te(U.id),await Pe(U.id)):Ve("merged/merge-failed 任务不可继续,也不可发后续消息")}catch(tt){Ve("发送失败:"+tt.message)}},[Se,r,te,Pe]),du=d.useCallback(async()=>{const k=se.current;if(!k){Ce("先选一个任务");return}try{const U=await I(K.taskTrace(k.id)),W=new Blob([JSON.stringify({task_id:k.id,status:U.status,note:U.note||"",count:(U.entries||[]).length,entries:U.entries||[]},null,2)],{type:"application/json"}),tt=document.createElement("a");tt.href=URL.createObjectURL(W),tt.download="trace-"+k.id.slice(0,8)+".json",document.body.appendChild(tt),tt.click(),tt.remove(),setTimeout(()=>URL.revokeObjectURL(tt.href),1e3),Ce("已下载完整链路 "+(U.entries||[]).length+" 条")}catch(U){Ce("下载失败:"+U.message)}},[]);return d.useEffect(()=>()=>re(),[re]),{currentTask:h,detailBody:S,actionMsg:A,actionReasons:H,costBox:Y,costSummary:w,forkVisible:V,setForkVisible:at,forkMsg:L,coordChildren:bt,agents:J,agentsNote:it,reviews:rt,reviewMsg:Qt,reviewError:kt,traceEntries:ot,traceNote:q,traceDone:lt,traceKind:yt,setTraceKind:ge,traceMsg:we,followups:y,approvals:ut,approvalMsg:Tt,peerMessages:Ot,peerMsg:dt,threadText:Ga,threadGateNote:Le,threadMsg:Yt,threadList:Kl,chatMsg:fa,chatHistory:zl,showDetail:Se,refreshDetailLists:Xa,doAction:ma,taskCost:su,forkTask:ru,postReview:Fl,submitFindings:Ae,decideApproval:fl,postPeer:Oe,threadAction:ou,sendChatMessage:fu,downloadTrace:du,refreshReviews:()=>void qe(),refreshApprovals:()=>void _l()}}function ng(i){const[r,h]=d.useState([]),[f,S]=d.useState(""),[N,A]=d.useState(null),[p,H]=d.useState(""),O=d.useCallback(async X=>{const V=X!==void 0?X:i();if(!V){h([]);return}try{const at=await I(K.projectAutomations(V));h(at.automations||[])}catch(at){S("自动化加载失败:"+at.message)}},[i]),Line truncated
(链路摘要已截断至 300 字,完整回复见右侧任务结果)`:f,label:"助手"}}if(i.kind==="exec"||i.kind==="file"||i.kind==="tool"||i.kind==="call"){const f=i.kind==="exec"?"命令":i.kind==="file"?"文件":i.kind==="tool"?"工具":"调用";return{id:h,role:"tool",kind:i.kind,text:vo(i),label:f}}return i.kind==="plan"?{id:h,role:"status",kind:i.kind,text:i.text||""}:i.kind==="reasoning"?{id:h,role:"status",kind:i.kind,text:"思考:"+(i.summary||"")}:i.kind==="agent-start"?{id:h,role:"status",kind:i.kind,text:"子代理开始:"+(i.name||"")+(i.prompt?"("+i.prompt+")":"")}:i.kind==="agent-done"?{id:h,role:"status",kind:i.kind,text:"子代理结束:"+(i.name||"")+" "+(i.status||"")}:{id:h,role:"status",kind:i.kind,text:vo(i)}}function gg(){const i=Zt(),r=i.currentTask,h=d.useMemo(()=>{if(!r)return[];let H;if(i.chatHistory){H=i.chatHistory.messages.map(V=>({id:V.id,role:V.role,text:V.text,label:V.label,kind:V.kind}));const w=new Set(H.map(V=>V.id)),X=i.traceEntries.filter(V=>V.id&&!w.has(V.id)&&!String(V.id).startsWith("fu-")).map((V,at)=>Xh(V,at));H=[...H,...X]}else{const w=r.prompt?[{id:"task-prompt",role:"user",text:r.prompt}]:[],X=i.traceEntries.filter(at=>!String(at.id||"").startsWith("fu-")).map((at,L)=>Xh(at,L)),V=i.followups.map((at,L)=>({id:"followup-"+L,role:"user",text:String(at.body||""),label:"后续消息"}));H=[...w,...X,...V]}const O=i.traceKind,Y=O?H.filter(w=>w.role==="user"||!w.kind||w.kind===O):H,j=i.traceDone?[{id:"trace-done",role:"status",text:"链路已结束("+i.traceDone+")"}]:[];return[...Y,...j]},[r,i.chatHistory,i.traceEntries,i.followups,i.traceKind,i.traceDone]);if(!r)return c.jsx("div",{className:"muted",children:"点任务行查看详情后,这里显示继续会话"});const f=uu.includes(r.status),S=["queued","running"].includes(r.status),N=f||S,A=f?"终态任务:发送走继续接口(同一会话再跑一轮)":S?"运行中/排队中:发送走后续消息直接发送(跑完当前轮后优先执行)":"merged/merge-failed 任务不可继续,也不可发后续消息",p=i.chatHistory?i.chatHistory.legacy_continue_count??null:null;return c.jsxs("div",{className:"chat-column",children:[c.jsx("div",{className:"muted",children:"实时对话"}),c.jsxs(wt,{children:[c.jsxs("select",{"aria-label":"链路类型",value:i.traceKind,onChange:H=>i.setTraceKind(H.target.value),children:[c.jsx("option",{value:"",children:"链路类型:全部"}),["exec","file","call","tool","message","reasoning","plan","agent-start","agent-done"].map(H=>c.jsx("option",{value:H,children:H==="exec"?"命令":H==="file"?"文件":H==="call"?"调用":H==="tool"?"工具":H==="message"?"消息":H==="reasoning"?"思考":H==="plan"?"计划":"子代理"},H))]}),c.jsx("button",{onClick:i.downloadTrace,children:"下载完整链路"}),c.jsx("span",{className:"muted",children:i.traceMsg})]}),i.traceNote?c.jsx("div",{children:c.jsx("span",{className:"err",children:i.traceNote})}):null,p!==null&&p>0?c.jsxs("div",{className:"muted",children:["此前有 ",p," 条继续消息发生在持久化之前,未入库,上面历史不完整;部署后新发的继续消息会正常显示。"]}):null,i.chatHistory&&i.chatHistory.trace_note?c.jsx("div",{className:"muted",children:i.chatHistory.trace_note}):null,c.jsx(vg,{messages:h,empty:"(暂无会话消息)"}),c.jsx(yg,{disabled:!N,placeholder:"消息",onSend:H=>void i.sendChatMessage(H)}),c.jsxs("div",{className:"muted",children:[A,"。",i.chatMsg]}),c.jsx("div",{className:"muted",children:"对话时间线:首条是本任务 prompt(用户);助手回复、命令/文件/工具/调用、计划/思考/子代理开始结束都在同一列里,不再另起一份链路名单。流式进行中时新条目实时追加;你在底部时自动跟随,往上翻时暂停跟随,点“回到最新”恢复。"})]})}function pg(){return Zt().currentTask?c.jsx("div",{className:"conversation",children:c.jsx(gg,{})}):c.jsx("div",{className:"conversation",children:c.jsx("div",{className:"muted",children:"在任务列表里点一条任务,对话和详情就切到该任务;也可以在上面派发一个新任务。"})})}function Sg(){const i=Zt(),r=i.currentTask,[h,f]=d.useState("");if(!r)return null;const S=uu.includes(r.status),N=["failed","interrupted","merge-failed"].includes(r.status);return c.jsxs(c.Fragment,{children:[c.jsxs(wt,{children:[c.jsx("button",{disabled:!N,title:N?"":"仅 failed/interrupted/merge-failed 可重试",onClick:()=>i.doAction("retry"),children:"重试"}),c.jsx("button",{className:"danger",disabled:S,title:S?"终态任务不可取消":"",onClick:()=>i.doAction("cancel"),children:"取消"}),c.jsx("button",{disabled:r.status!=="completed",title:r.status!=="completed"?"仅 completed 可合并":"",onClick:()=>i.doAction("merge"),children:"合并(completed → merged)"}),c.jsx("button",{disabled:r.status!=="merged",title:r.status!=="merged"?"仅 merged 可提 PR":"",onClick:()=>i.doAction("pr"),children:"提 PR(须Line truncated
`),C(W||"(暂无 result/error 输出)")}catch{}te(k.id),Pe(k.id),qe(k.id),_l(k.id),da(k),rl(k.id),ol(k.id),Wt(k.id),Ul(k.id),Dl(k.id)}},[te,Pe,qe,_l,da,rl,ol,Wt,Ul,Dl]),ma=d.useCallback(async k=>{const U=se.current;if(!U)return;const W={cancel:"确定取消该任务吗?",merge:"确定把 completed 任务标为 merged 吗?",pr:"确定为该任务提 PR 吗?(会在仓库真实建 PR)"};if(!(W[k]&&!window.confirm(W[k])))try{const tt=await I(K.taskAction(U.id,k),"POST",{});p(k+" 已提交:"+JSON.stringify(tt).slice(0,300)),r(),await Se(U.id)}catch(tt){p(k+" 失败:"+tt.message)}},[Se,r]),su=d.useCallback(async()=>{const k=se.current;if(k)try{const U=await I(K.taskCost(k.id));j(JSON.stringify(U,null,2).slice(0,4e3))}catch(U){j("查成本失败:"+U.message)}},[]),ru=d.useCallback(async k=>{const U=se.current;if(U){if(!k.trim()){vt("分支消息必填");return}if(!uu.includes(U.status)){vt("仅终态任务可分支");return}try{const W=await I(K.taskFork(U.id),"POST",{message:k.trim()});vt("分支任务已建:"+W.id.slice(0,8)),r()}catch(W){vt("分支失败:"+W.message)}}},[r]),Fl=d.useCallback(async k=>{const U=se.current;if(U)try{await I(K.taskReviews(U.id),"POST",k),Nt("评审已登记(requested)"),await qe(U.id)}catch(W){Nt("登记评审需求失败:"+W.message)}},[qe]),Ae=d.useCallback(k=>{const U=se.current;if(!U)return;const W=window.prompt("输入该条评审的 findings(将写回记录并标为 completed):");W!==null&&I(K.taskReviewFindings(U.id,k),"POST",{findings:W}).then(()=>void qe(U.id)).catch(tt=>Nt("写 findings 失败:"+tt.message))},[qe]),fl=d.useCallback((k,U)=>{const W=se.current;if(!W)return;const tt=U==="approved"?"approve":"deny";I(K.taskApprovalDecision(W.id,k,tt),"POST",{}).then(()=>{xt("已记录"+(U==="approved"?"允许":"拒绝")+"(当前版本只留回执,不拦截实时行为)"),_l(W.id)}).catch(M=>xt("决定失败:"+M.message))},[_l]),Oe=d.useCallback(async(k,U,W)=>{const tt=se.current;if(tt){if(!U.trim()){ce("目标任务 id 不能为空");return}if(!W.trim()){ce("消息正文不能为空");return}try{const M=await I(K.taskMessagesTo(U.trim()),"POST",{from_task_id:tt.id,body:W.trim(),mode:k});ce("已发送("+M.delivery+")"),await ol(tt.id)}catch(M){ce("发送失败:"+M.message)}}},[ol]),ou=d.useCallback((k,U)=>{const W=se.current;if(!W)return;if(!["completed","failed","interrupted"].includes(W.status)){he("仅 completed/failed/interrupted 任务可操作线程(merged/merge-failed 不可改,服务端同样 409)");return}const tt={};if(k==="rename"){if(!U.trim()){he("新线程名不能为空");return}tt.name=U.trim()}I(K.taskThreadAction(W.id,k),"POST",tt).then(M=>{he(k+" 已提交:"+JSON.stringify(M).slice(0,300)),Wt(W.id),Ul(W.id)}).catch(M=>he(k+" 失败:"+M.message))},[Wt,Ul]),fu=d.useCallback(async k=>{const U=se.current;if(!U)return;const W=k.trim();if(!W){Ve("消息不能为空");return}try{uu.includes(U.status)&&U.status!=="merged"?(await I(K.taskContinue(U.id),"POST",{message:W}),Ve("已重新排队,同一会话继续"),r(),await Se(U.id)):["queued","running"].includes(U.status)?(await I(K.taskFollowups(U.id),"POST",{body:W,mode:"direct"}),Ve("已直接发送"),await te(U.id),await Pe(U.id)):Ve("merged/merge-failed 任务不可继续,也不可发后续消息")}catch(tt){Ve("发送失败:"+tt.message)}},[Se,r,te,Pe]),du=d.useCallback(async()=>{const k=se.current;if(!k){Ce("先选一个任务");return}try{const U=await I(K.taskTrace(k.id)),W=new Blob([JSON.stringify({task_id:k.id,status:U.status,note:U.note||"",count:(U.entries||[]).length,entries:U.entries||[]},null,2)],{type:"application/json"}),tt=document.createElement("a");tt.href=URL.createObjectURL(W),tt.download="trace-"+k.id.slice(0,8)+".json",document.body.appendChild(tt),tt.click(),tt.remove(),setTimeout(()=>URL.revokeObjectURL(tt.href),1e3),Ce("已下载完整链路 "+(U.entries||[]).length+" 条")}catch(U){Ce("下载失败:"+U.message)}},[]);return d.useEffect(()=>()=>re(),[re]),{currentTask:h,detailBody:S,actionMsg:A,actionReasons:H,costBox:Y,costSummary:w,forkVisible:V,setForkVisible:at,forkMsg:L,coordChildren:bt,agents:J,agentsNote:it,reviews:rt,reviewMsg:Qt,reviewError:kt,traceEntries:ot,traceNote:q,traceDone:lt,traceKind:yt,setTraceKind:ge,traceMsg:we,followups:y,approvals:ut,approvalMsg:Tt,peerMessages:Ot,peerMsg:dt,threadText:Ga,threadGateNote:Le,threadMsg:Yt,threadList:Kl,chatMsg:fa,chatHistory:zl,showDetail:Se,refreshDetailLists:Xa,doAction:ma,taskCost:su,forkTask:ru,postReview:Fl,submitFindings:Ae,decideApproval:fl,postPeer:Oe,threadAction:ou,sendChatMessage:fu,downloadTrace:du,refreshReviews:()=>void qe(),refreshApprovals:()=>void _l()}}function ng(i){const[r,h]=d.useState([]),[f,S]=d.useState(""),[C,A]=d.useState(null),[p,H]=d.useState(""),O=d.useCallback(async X=>{const V=X!==void 0?X:i();if(!V){h([]);return}try{const at=await I(K.projectAutomations(V));h(at.automations||[])}catch(at){S("自动化加载失败:"+at.message)}},[i]),Line truncated
(链路摘要已截断至 300 字,完整回复见右侧任务结果)`:f,label:"助手"}}if(i.kind==="exec"||i.kind==="file"||i.kind==="tool"||i.kind==="call"){const f=i.kind==="exec"?"命令":i.kind==="file"?"文件":i.kind==="tool"?"工具":"调用";return{id:h,role:"tool",kind:i.kind,text:vo(i),label:f}}return i.kind==="plan"?{id:h,role:"status",kind:i.kind,text:i.text||""}:i.kind==="reasoning"?{id:h,role:"status",kind:i.kind,text:"思考:"+(i.summary||"")}:i.kind==="agent-start"?{id:h,role:"status",kind:i.kind,text:"子代理开始:"+(i.name||"")+(i.prompt?"("+i.prompt+")":"")}:i.kind==="agent-done"?{id:h,role:"status",kind:i.kind,text:"子代理结束:"+(i.name||"")+" "+(i.status||"")}:{id:h,role:"status",kind:i.kind,text:vo(i)}}function gg(){const i=Zt(),r=i.currentTask,h=d.useMemo(()=>{if(!r)return[];let H;if(i.chatHistory){H=i.chatHistory.messages.map(V=>({id:V.id,role:V.role,text:V.text,label:V.label,kind:V.kind}));const w=new Set(H.map(V=>V.id)),X=i.traceEntries.filter(V=>V.id&&!w.has(V.id)&&!String(V.id).startsWith("fu-")).map((V,at)=>Xh(V,at));H=[...H,...X]}else{const w=r.prompt?[{id:"task-prompt",role:"user",text:r.prompt}]:[],X=i.traceEntries.filter(at=>!String(at.id||"").startsWith("fu-")).map((at,L)=>Xh(at,L)),V=i.followups.map((at,L)=>({id:"followup-"+L,role:"user",text:String(at.body||""),label:"后续消息"}));H=[...w,...X,...V]}const O=i.traceKind,Y=O?H.filter(w=>w.role==="user"||!w.kind||w.kind===O):H,j=i.traceDone?[{id:"trace-done",role:"status",text:"链路已结束("+i.traceDone+")"}]:[];return[...Y,...j]},[r,i.chatHistory,i.traceEntries,i.followups,i.traceKind,i.traceDone]);if(!r)return c.jsx("div",{className:"muted",children:"点任务行查看详情后,这里显示继续会话"});const f=uu.includes(r.status),S=["queued","running"].includes(r.status),C=f||S,A=f?"终态任务:发送走继续接口(同一会话再跑一轮)":S?"运行中/排队中:发送走后续消息直接发送(跑完当前轮后优先执行)":"merged/merge-failed 任务不可继续,也不可发后续消息",p=i.chatHistory?i.chatHistory.legacy_continue_count??null:null;return c.jsxs("div",{className:"chat-column",children:[c.jsx("div",{className:"muted",children:"实时对话"}),c.jsxs(wt,{children:[c.jsxs("select",{"aria-label":"链路类型",value:i.traceKind,onChange:H=>i.setTraceKind(H.target.value),children:[c.jsx("option",{value:"",children:"链路类型:全部"}),["exec","file","call","tool","message","reasoning","plan","agent-start","agent-done"].map(H=>c.jsx("option",{value:H,children:H==="exec"?"命令":H==="file"?"文件":H==="call"?"调用":H==="tool"?"工具":H==="message"?"消息":H==="reasoning"?"思考":H==="plan"?"计划":"子代理"},H))]}),c.jsx("button",{onClick:i.downloadTrace,children:"下载完整链路"}),c.jsx("span",{className:"muted",children:i.traceMsg})]}),i.traceNote?c.jsx("div",{children:c.jsx("span",{className:"err",children:i.traceNote})}):null,p!==null&&p>0?c.jsxs("div",{className:"muted",children:["此前有 ",p," 条继续消息发生在持久化之前,未入库,上面历史不完整;部署后新发的继续消息会正常显示。"]}):null,i.chatHistory&&i.chatHistory.trace_note?c.jsx("div",{className:"muted",children:i.chatHistory.trace_note}):null,c.jsx(vg,{messages:h,empty:"(暂无会话消息)"}),c.jsx(yg,{disabled:!C,placeholder:"消息",onSend:H=>void i.sendChatMessage(H)}),c.jsxs("div",{className:"muted",children:[A,"。",i.chatMsg]}),c.jsx("div",{className:"muted",children:"对话时间线:首条是本任务 prompt(用户);助手回复、命令/文件/工具/调用、计划/思考/子代理开始结束都在同一列里,不再另起一份链路名单。流式进行中时新条目实时追加;你在底部时自动跟随,往上翻时暂停跟随,点“回到最新”恢复。"})]})}function pg(){return Zt().currentTask?c.jsx("div",{className:"conversation",children:c.jsx(gg,{})}):c.jsx("div",{className:"conversation",children:c.jsx("div",{className:"muted",children:"在任务列表里点一条任务,对话和详情就切到该任务;也可以在上面派发一个新任务。"})})}function Sg(){const i=Zt(),r=i.currentTask,[h,f]=d.useState("");if(!r)return null;const S=uu.includes(r.status),C=["failed","interrupted","merge-failed"].includes(r.status);return c.jsxs(c.Fragment,{children:[c.jsxs(wt,{children:[c.jsx("button",{disabled:!C,title:C?"":"仅 failed/interrupted/merge-failed 可重试",onClick:()=>i.doAction("retry"),children:"重试"}),c.jsx("button",{className:"danger",disabled:S,title:S?"终态任务不可取消":"",onClick:()=>i.doAction("cancel"),children:"取消"}),c.jsx("button",{disabled:r.status!=="completed",title:r.status!=="completed"?"仅 completed 可合并":"",onClick:()=>i.doAction("merge"),children:"合并(completed → merged)"}),c.jsx("button",{disabled:r.status!=="merged",title:r.status!=="merged"?"仅 merged 可提 PR":"",onClick:()=>i.doAction("pr"),children:"提 PR(须Line truncated
`);for(;ot>=0;){const st=Nt.slice(0,ot);Nt=Nt.slice(ot+2);for(const q of st.split(`
`))if(q.startsWith("data: "))try{const C=JSON.parse(q.slice(6));C.image&&f("data:image/jpeg;base64,"+C.image);const lt={};C.holder&&(lt.holder=C.holder),C.url&&(lt.url=C.url),C.title!==void 0&&(lt.title=C.title),Object.keys(lt).length>0&&N(gt=>({...gt||{},...lt}))}catch{}ot=Nt.indexOf(`
`))if(q.startsWith("data: "))try{const N=JSON.parse(q.slice(6));N.waiting?p(N.task_status==="interrupted"?"任务已中断,等待续跑…":N.task_status==="paused"?"任务已暂停…":"等待会话…"):N.image&&(f("data:image/jpeg;base64,"+N.image),p("直播中"));const lt={};N.holder&&(lt.holder=N.holder),N.url&&(lt.url=N.url),N.title!==void 0&&(lt.title=N.title),Object.keys(lt).length>0&&C(gt=>({...gt||{},...lt}))}catch{}ot=Nt.indexOf(`
`)}}p("流已断开")}catch(rt){if(rt.name==="AbortError")return;O(rt instanceof Error?rt.message:String(rt)),p("未开始")}finally{j(!1)}},[r]),L=d.useCallback(()=>{var it;(it=w.current)==null||it.abort(),j(!1),p("已停止")},[]),vt=d.useCallback(async it=>{if(r)try{const Q=await I(K.taskBrowserControl(r.id),"POST",{holder:it});N(Q),O("")}catch(Q){O(Q instanceof Error?Q.message:String(Q))}},[r]),bt=d.useCallback(async it=>{if(!r||(S==null?void 0:S.holder)!=="user"||!X.current)return;const Q=X.current.getBoundingClientRect(),rt=Math.round((it.clientX-Q.left)/Q.width*1280),Ut=Math.round((it.clientY-Q.top)/Q.height*800);try{await I(K.taskBrowserInput(r.id),"POST",{type:"mouse_click",x:rt,y:Ut})}catch(Qt){O(Qt instanceof Error?Qt.message:String(Qt))}},[r,S]),zt=d.useCallback(async it=>{if(!(!r||(S==null?void 0:S.holder)!=="user")){it.preventDefault();try{it.key.length===1?await I(K.taskBrowserInput(r.id),"POST",{type:"type",text:it.key}):await I(K.taskBrowserInput(r.id),"POST",{type:"key",key:it.key})}catch(Q){O(Q instanceof Error?Q.message:String(Q))}}},[r,S]);if(!r)return c.jsx("div",{className:"muted",children:"点任务行查看详情"});const J=(S==null?void 0:S.holder)||"—",Rt=S!==null;return c.jsxs("div",{children:[c.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",alignItems:"center"},children:[c.jsxs("span",{children:["状态 ",c.jsx("b",{children:A})]}),c.jsxs("span",{className:"muted",children:["会话 ",Rt?"活跃":"未激活(轮次结束即消失)"]}),c.jsxs("span",{className:"muted",children:["控制权 ",J]}),S!=null&&S.url?c.jsx("span",{className:"muted",title:S.url,children:S.url.slice(0,80)}):null]}),c.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginTop:8},children:[Y?c.jsx("button",{type:"button",onClick:L,children:"停止观看"}):c.jsx("button",{type:"button",onClick:()=>void at(),children:"开始观看"}),c.jsx("button",{type:"button",onClick:()=>void V(),children:"刷新状态"}),J!=="user"?c.jsx("button",{type:"button",onClick:()=>void vt("user"),disabled:!Rt,children:"接管"}):c.jsx("button",{type:"button",onClick:()=>void vt("agent"),children:"交还给 Agent"})]}),H?c.jsx("div",{className:"muted",style:{marginTop:8},children:H}):null,J==="user"?c.jsx("div",{className:"muted",style:{marginTop:8},children:"你已接管:点画面即点击,键盘输入直达页面。交还后 Agent 续跑前会先快照确认你的改动。"}):null,c.jsx("div",{style:{marginTop:8},children:h?c.jsx("img",{ref:X,src:h,alt:"浏览器实时画面",tabIndex:0,onClick:it=>void bt(it),onKeyDown:it=>void zt(it),style:{width:"100%",maxWidth:1280,cursor:J==="user"?"crosshair":"default",border:"1px solid var(--border, #ccc)"}}):c.jsx("div",{className:"muted",children:Rt?"点“开始观看”接入画面流(接管后点画面即点击)":"浏览器会话未激活:任务本轮未开浏览器,或轮次已结束。"})})]})}function xg(){const i=Zt(),[r,h]=d.useState("uncommittedChanges"),[f,S]=d.useState(""),[N,A]=d.useState(""),[p,H]=d.useState("");return c.jsxs(c.Fragment,{children:[c.jsx("h3",{children:"评审队列"}),c.jsxs("select",{"aria-label":"评审目标",value:r,onChange:O=>h(O.target.value),children:[c.jsx("option",{value:"uncommittedChanges",children:"未提交改动(工作区)"}),c.jsx("option",{value:"baseBranch",children:"与分支的差异"}),c.jsx("option",{value:"commit",children:"指定提交"}),c.jsx("option",{value:"custom",children:"自定义评审指令"})]}),c.jsx("input",{className:"stacked",placeholder:"与分支的差异用:分支名(如 main)",style:{width:"100%"},value:f,onChange:O=>S(O.target.value)}),c.jsx("input",{className:"stacked",placeholder:"指定提交用:提交 sha",style:{width:"100%"},value:N,onChange:O=>A(O.target.value)}),c.jsx("textarea",{placeholder:"自定义评审指令用:评审指令正文",value:p,onChange:O=>H(O.target.value)}),c.jsxs(wt,{children:[c.jsx("button",{className:"primary",onClick:()=>void i.postReview({target_type:r,branch:f.trim()||void 0,sha:N.trim()||void 0,instructions:p.trim()||void 0}),children:"登记评审需求"}),c.jsx("button",{onClick:i.refreshReviews,children:"刷新队列"}),c.jsx("span",{className:"muted",children:i.reviewMsg})]}),c.jsx("div",{className:"muted",children:"本基线只登记评审需求(requested),不自动跑评审;评审是否执行、结果如何,以评审队列里的记录为准。"}),i.reviewError?c.jsx("div",{className:"muted",children:i.reviewError}):i.reviews.length===0?c.jsx("div",{className:"muted",children:"点任务行查看详情后,点“刷新队列”查看评审记录"}):c.jsx(cu,{children:i.reviews.map(O=>{let Y=O.target_type||"";return O.branch&&(Y+=":"+O.branch),O.sha&&(Y+=":"+String(O.sha).slice(0,12)),O.instructions&&(Y+=":"+String(O.instructions).slice(0,80)),c.jsxs("li",{children:[c.jsxs("span",{className:"row-main",children:[c.jsx("b",{children:"评审"})," ",Ne(O.id)," | ",Y," | ",c.jsx("span",{className:"st-"+(O.status||Line truncated
`)}}p("流已断开")}catch(rt){if(rt.name==="AbortError")return;O(rt instanceof Error?rt.message:String(rt)),p("未开始")}finally{j(!1)}},[r]),L=d.useCallback(()=>{var it;(it=w.current)==null||it.abort(),j(!1),p("已停止")},[]),vt=d.useCallback(async it=>{if(r)try{const Q=await I(K.taskBrowserControl(r.id),"POST",{holder:it});C(Q),O("")}catch(Q){O(Q instanceof Error?Q.message:String(Q))}},[r]),bt=d.useCallback(async it=>{if(!r||(S==null?void 0:S.holder)!=="user"||!X.current)return;const Q=X.current.getBoundingClientRect(),rt=Math.round((it.clientX-Q.left)/Q.width*1280),Ut=Math.round((it.clientY-Q.top)/Q.height*800);try{await I(K.taskBrowserInput(r.id),"POST",{type:"mouse_click",x:rt,y:Ut})}catch(Qt){O(Qt instanceof Error?Qt.message:String(Qt))}},[r,S]),zt=d.useCallback(async it=>{if(!(!r||(S==null?void 0:S.holder)!=="user")){it.preventDefault();try{it.key.length===1?await I(K.taskBrowserInput(r.id),"POST",{type:"type",text:it.key}):await I(K.taskBrowserInput(r.id),"POST",{type:"key",key:it.key})}catch(Q){O(Q instanceof Error?Q.message:String(Q))}}},[r,S]);if(!r)return c.jsx("div",{className:"muted",children:"点任务行查看详情"});const J=(S==null?void 0:S.holder)||"—",Rt=S!==null;return c.jsxs("div",{children:[c.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",alignItems:"center"},children:[c.jsxs("span",{children:["状态 ",c.jsx("b",{children:A})]}),c.jsxs("span",{className:"muted",children:["会话 ",Rt?"活跃":"未激活(轮次结束即消失)"]}),c.jsxs("span",{className:"muted",children:["控制权 ",J]}),S!=null&&S.url?c.jsx("span",{className:"muted",title:S.url,children:S.url.slice(0,80)}):null]}),c.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginTop:8},children:[Y?c.jsx("button",{type:"button",onClick:L,children:"停止观看"}):c.jsx("button",{type:"button",onClick:()=>void at(),children:"开始观看"}),c.jsx("button",{type:"button",onClick:()=>void V(),children:"刷新状态"}),J!=="user"?c.jsx("button",{type:"button",onClick:()=>void vt("user"),disabled:!Rt,children:"接管"}):c.jsx("button",{type:"button",onClick:()=>void vt("agent"),children:"交还给 Agent"})]}),H?c.jsx("div",{className:"muted",style:{marginTop:8},children:H}):null,J==="user"?c.jsx("div",{className:"muted",style:{marginTop:8},children:"你已接管:点画面即点击,键盘输入直达页面。交还后 Agent 续跑前会先快照确认你的改动。"}):null,c.jsx("div",{style:{marginTop:8},children:h?c.jsx("img",{ref:X,src:h,alt:"浏览器实时画面",tabIndex:0,onClick:it=>void bt(it),onKeyDown:it=>void zt(it),style:{width:"100%",maxWidth:1280,cursor:J==="user"?"crosshair":"default",border:"1px solid var(--border, #ccc)"}}):c.jsx("div",{className:"muted",children:Rt?"点“开始观看”接入画面流(接管后点画面即点击)":"浏览器会话未激活:任务本轮未开浏览器,或轮次已结束。"})})]})}function xg(){const i=Zt(),[r,h]=d.useState("uncommittedChanges"),[f,S]=d.useState(""),[C,A]=d.useState(""),[p,H]=d.useState("");return c.jsxs(c.Fragment,{children:[c.jsx("h3",{children:"评审队列"}),c.jsxs("select",{"aria-label":"评审目标",value:r,onChange:O=>h(O.target.value),children:[c.jsx("option",{value:"uncommittedChanges",children:"未提交改动(工作区)"}),c.jsx("option",{value:"baseBranch",children:"与分支的差异"}),c.jsx("option",{value:"commit",children:"指定提交"}),c.jsx("option",{value:"custom",children:"自定义评审指令"})]}),c.jsx("input",{className:"stacked",placeholder:"与分支的差异用:分支名(如 main)",style:{width:"100%"},value:f,onChange:O=>S(O.target.value)}),c.jsx("input",{className:"stacked",placeholder:"指定提交用:提交 sha",style:{width:"100%"},value:C,onChange:O=>A(O.target.value)}),c.jsx("textarea",{placeholder:"自定义评审指令用:评审指令正文",value:p,onChange:O=>H(O.target.value)}),c.jsxs(wt,{children:[c.jsx("button",{className:"primary",onClick:()=>void i.postReview({target_type:r,branch:f.trim()||void 0,sha:C.trim()||void 0,instructions:p.trim()||void 0}),children:"登记评审需求"}),c.jsx("button",{onClick:i.refreshReviews,children:"刷新队列"}),c.jsx("span",{className:"muted",children:i.reviewMsg})]}),c.jsx("div",{className:"muted",children:"本基线只登记评审需求(requested),不自动跑评审;评审是否执行、结果如何,以评审队列里的记录为准。"}),i.reviewError?c.jsx("div",{className:"muted",children:i.reviewError}):i.reviews.length===0?c.jsx("div",{className:"muted",children:"点任务行查看详情后,点“刷新队列”查看评审记录"}):c.jsx(cu,{children:i.reviews.map(O=>{let Y=O.target_type||"";return O.branch&&(Y+=":"+O.branch),O.sha&&(Y+=":"+String(O.sha).slice(0,12)),O.instructions&&(Y+=":"+String(O.instructions).slice(0,80)),c.jsxs("li",{children:[c.jsxs("span",{className:"row-main",children:[c.jsx("b",{children:"评审"})," ",Ne(O.id)," | ",Y," | ",c.jsx("span",{className:"st-"+(O.status||Line truncated
<style rel="stylesheet" crossorigin>:root{--text:#1f2328;--muted:#656d76;--subtle:#6e7781;--line:#d0d7de;--line-muted:#d8dee4;--paper:#ffffff;--card:#ffffff;--subtle-bg:#f6f8fa;--inset-bg:#f6f8fa;--accent:#0969da;--accent-emphasis:#0969da;--green:#2da44e;--green-hover:#2c974b;--success:#1a7f37;--danger:#d1242f;--danger-bg:#ffebe9;--attention:#9a6700;--done:#8250df;--selected-bg:#ddf4ff;--radius:6px;--shadow:0 1px 0 rgba(27,31,36,.04);--sans:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif;--mono:ui-monospace,SFMono-Regular,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;--ctl:32px}*{box-sizing:border-box}html,body,#root{height:100%}body{font-family:var(--sans);margin:0;color:var(--text);background:#fff;line-height:1.5;font-size:14px}a{color:var(--accent);text-underline-offset:3px}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}.muted{color:var(--muted);font-size:12px;line-height:1.5}.err{color:var(--danger)}.ok{color:var(--success)}button{cursor:pointer;font-weight:500;border-radius:var(--radius);border:1px solid var(--line);background:#fff;color:var(--text);box-shadow:var(--shadow)}button:hover{background:var(--subtle-bg)}button:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}button.primary,button.seal{background:var(--green);border-color:#1b1f2426;color:#fff}button.primary:hover,button.seal:hover{background:var(--green-hover)}button.danger{color:var(--danger);border-color:var(--line);background:#fff}button.danger:hover{background:var(--danger-bg);color:var(--danger)}.icon-btn{width:32px;height:32px;padding:0;display:grid;place-items:center;border:1px solid var(--line);border-radius:var(--radius);background:#fff;color:var(--text);font-size:16px;line-height:1}.icon-btn:hover{background:var(--subtle-bg)}.dash-topbar{position:sticky;top:0;z-index:10;background:#24292f;color:#fff;border-bottom:1px solid rgba(255,255,255,.15);padding:10px 20px 8px}.dash-topbar .muted{color:#ffffffbf}.dash-topbar-row{display:flex;gap:14px;align-items:center;flex-wrap:wrap}.dash-brand{font-weight:600;font-size:16px;color:#fff}.dash-status{font-size:13px;font-weight:600;color:#ffffffbf}.dash-status.ok{color:#2da44e}.dash-stamp{margin-left:auto}.dash-nav{display:flex;gap:4px;flex-wrap:wrap;margin-top:8px}.dash-nav a{color:#fff;text-decoration:none;font-size:13px;font-weight:600;padding:6px 12px;border-radius:var(--radius)}.dash-nav a:hover{background:#ffffff1f;text-decoration:none}.tab-nav button{background:transparent;border:0;box-shadow:none;color:#fff;font-size:13px;font-weight:600;padding:6px 12px;border-radius:var(--radius)}.tab-nav button:hover{background:#ffffff1f}.tab-nav button.active{background:#ffffff2e}.dash-page-tabs{display:flex}.dash-page-tabs>.tab-content{grid-column:1 / -1;min-width:0;display:flex;flex-direction:column;gap:20px}.tab-workbench{display:grid;grid-template-columns:320px minmax(0,1fr);gap:20px;align-items:start}.tab-workbench>*{min-width:0}@media(max-width:899px){.tab-workbench{grid-template-columns:1fr}.tab-workbench #sec-dispatch{order:1}.tab-workbench #sec-tasks{order:2}.tab-workbench #sec-conv{order:3}}.dash-page{max-width:1180px;margin:0 auto;padding:20px;display:flex;flex-direction:column;gap:20px}.dash-page>*{min-width:0}.dash-section>*{min-width:0}.dash-section{scroll-margin-top:130px;display:flex;flex-direction:column;gap:16px}.dash-section.card{padding:16px}.dash-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.dash-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:16px;min-width:0}.dash-card-label{color:var(--muted);font-size:12px;font-weight:600}.dash-card-value{font-size:24px;font-weight:600;line-height:1.3;overflow-wrap:anywhere}.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:none;padding:16px}.card h2{font-size:14px;margin:0 0 12px;font-weight:600}.card h3{font-size:14px;margin:24px 0 12px;padding-top:16px;border-top:1px solid var(--line);font-weight:600}.card h3:first-of-type{border-top:0;padding-top:0}.composer h2{font-size:16px}.composer textarea.prompt{min-height:148px;font-size:14px;padding:12px;border-radius:var(--radius)}.composer textarea.coord{font-family:var(--mono);font-size:13px}.bar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:12px 0}.bar>button,.bar>label{display:inline-flex;align-items:center;height:var(--ctl);min-height:var(--ctl);padding:0 16px;box-sizing:border-box;white-space:nowrap}.bar>input,.bar>select{height:var(--ctl);min-height:var(--ctl)}input,select,textarea,button{font:inherit;font-size:14px;padding:5px 12px;border-radius:var(--radius);border:1px solid var(--line);background:#fff;color:var(--text)}input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px #0969da4d}select{-moz-appearance:none;appearance:none;-webkit-appearance:none;cursLine truncated
</head>
<body>
+62 -6
View File
@@ -5383,9 +5383,20 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
current holder at least every 5s). The payload also
carries the session url/title, so the console panel can
track the header from the stream alone instead of
polling the state endpoint while watching. Ends the
stream when the state file goes stale, so a dead
session reads as a disconnect, not a frozen picture.
polling the state endpoint while watching.
Stale state does NOT end the stream while the task is
still alive: between rounds (and across a worker
restart) the MCP is gone for a while and the state file
ages past the 30s staleness mark, which used to kill
every watch at the first round gap. Now a stale state
only switches the stream to waiting mode -- a heartbeat
with waiting:true every 5s, task status checked at the
same cadence -- and frames resume when the next round's
MCP starts polling again. The stream ends only when the
task reaches a terminal state (completed, merged,
failed), the task row disappears, or the state stays
stale for 10 minutes straight (abandoned watch).
"""
import time
from cloud.playwright_remote import (
@@ -5406,23 +5417,62 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
paths = bridge_paths(worktree)
last_frame = None
last_heartbeat = 0.0
last_holder = ""
stale_since = None
last_status_check = 0.0
task_status = None
try:
while True:
state, serr = read_state(worktree)
now = time.time()
if serr is not None:
break
# No live session right now (round gap,
# worker restart, or a blocked drain loop).
# Wait it out while the task is still alive
# instead of dropping the watch.
if stale_since is None:
stale_since = now
if now - stale_since > 600:
break # abandoned watch
if now - last_status_check >= 5:
last_status_check = now
try:
row = self._fetch_task(task_id)
except Exception: # noqa: BLE001 - keep waiting
row = False # transient error, not deletion
if row is None:
break # task row gone
if row is not False:
task_status = row["status"]
if task_status in (
"completed", "merged", "failed"):
break
if now - last_heartbeat >= 5:
last_heartbeat = now
payload = json.dumps(
{"image": "",
"holder": last_holder,
"waiting": True,
"task_status": task_status or "",
"ts": int(now)}, ensure_ascii=False)
self.wfile.write(
("data: %s\n\n" % payload).encode("utf-8"))
self.wfile.flush()
time.sleep(0.02)
continue
stale_since = None
try:
with open(paths["frame"], "rb") as fh:
frame = fh.read().decode("utf-8")
except (OSError, UnicodeDecodeError):
frame = ""
now = time.time()
if frame != last_frame or now - last_heartbeat >= 5:
last_frame = frame
last_heartbeat = now
last_holder = effective_holder(worktree, state)
payload = json.dumps(
{"image": frame,
"holder": effective_holder(worktree, state),
"holder": last_holder,
"url": state.get("url", ""),
"title": state.get("title", ""),
"ts": int(now)}, ensure_ascii=False)
@@ -5432,6 +5482,12 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
time.sleep(0.02)
except (BrokenPipeError, ConnectionResetError, OSError):
pass
# An SSE stream has no Content-Length, so the client can
# only tell "stream ended" from the connection closing.
# HTTP/1.1 keep-alive would otherwise leave it hanging
# after a terminal break (completed/failed/cap) with no
# more bytes ever coming. Force the close on stream end.
self.close_connection = True
def _task_chat_history(self, user_id, task_id):
"""GET /tasks/{id}/chat-history:真正的交替历史,一次性
+45
View File
@@ -174,6 +174,51 @@ class BrowserProxyTestCase(unittest.TestCase):
finally:
conn.close()
def test_stream_waits_through_stale_state(self):
"""A stale state must not drop a live task's watch.
Regression for the 2026-10-30 demo watch: the old loop
broke on the first stale read, so every round gap (and
every worker restart) killed the stream. Now a stale
state switches to a waiting heartbeat while the task is
still alive, and only a terminal task status ends it.
"""
Path(self.wt, ".browser-remote-frame.jpg").write_bytes(b"JPEG1")
self._write_state("agent")
conn = http.client.HTTPConnection("127.0.0.1", self.port,
timeout=30)
try:
conn.request("GET", "/tasks/task-b1/browser/stream",
headers={"X-API-Key": self.owner_key})
resp = conn.getresponse()
self.assertEqual(resp.status, 200)
# Each SSE event is two lines: data + blank separator.
line = resp.readline().decode("utf-8")
payload = json.loads(line[6:])
self.assertEqual(payload["image"], "JPEG1")
self.assertEqual(resp.readline().decode("utf-8"), "\n")
# Round gap: state goes stale. The stream must stay
# open and emit a waiting heartbeat within ~5s.
self._write_state("agent", fresh=False)
line = resp.readline().decode("utf-8")
self.assertTrue(line.startswith("data: "), line)
payload = json.loads(line[6:])
self.assertTrue(payload.get("waiting"))
self.assertEqual(payload.get("task_status"), "running")
self.assertEqual(resp.readline().decode("utf-8"), "\n")
# Terminal task status ends the stream.
db = sqlite3.connect(self.db_path)
try:
db.execute("UPDATE tasks SET status = 'completed'"
" WHERE id = 'task-b1'")
db.commit()
finally:
db.close()
line = resp.readline().decode("utf-8")
self.assertEqual(line, "")
finally:
conn.close()
if __name__ == "__main__":
unittest.main()