@sma1lboy/rove 0.9.280 → 0.9.281
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/chunk-01tghgf3.js +8 -0
- package/dist/cli/{chunk-jk4f0xsh.js → chunk-03fv92ya.js} +1 -1
- package/dist/cli/{chunk-7aaw17vr.js → chunk-08mb3qss.js} +1 -1
- package/dist/cli/{chunk-y7mjhjby.js → chunk-0gbg7bg8.js} +1 -1
- package/dist/cli/{chunk-zv0rrhck.js → chunk-0nkk13z9.js} +1 -1
- package/dist/cli/{chunk-zp0q931y.js → chunk-14tevt5v.js} +1 -1
- package/dist/cli/{chunk-1g6vhgfe.js → chunk-16e6jqq6.js} +1 -1
- package/dist/cli/{chunk-3hxm56h6.js → chunk-1a57ejnh.js} +1 -1
- package/dist/cli/{chunk-4z1a65bz.js → chunk-32y7f5dr.js} +1 -1
- package/dist/cli/{chunk-j54c5a0a.js → chunk-3m0ydze9.js} +1 -1
- package/dist/cli/{chunk-tqxbbg3k.js → chunk-3pgp6z3k.js} +1 -1
- package/dist/cli/{chunk-amq030gb.js → chunk-3qj9yw5c.js} +2 -2
- package/dist/cli/chunk-453qpfae.js +2 -0
- package/dist/cli/{chunk-gk8efsx0.js → chunk-46qs744w.js} +1 -1
- package/dist/cli/{chunk-am5cpb9n.js → chunk-4vkrv4eb.js} +1 -1
- package/dist/cli/{chunk-hfvzvj91.js → chunk-4yr1534j.js} +1 -1
- package/dist/cli/{chunk-byh32qnx.js → chunk-5swmvt20.js} +1 -1
- package/dist/cli/{chunk-j8d9sk5j.js → chunk-6wt08a28.js} +1 -1
- package/dist/cli/{chunk-f262ywvg.js → chunk-7911jkq6.js} +1 -1
- package/dist/cli/chunk-8ng1756g.js +3 -0
- package/dist/cli/{chunk-ebdp0z1d.js → chunk-95kk75wh.js} +1 -1
- package/dist/cli/{chunk-6mk6ceb6.js → chunk-9qpea48c.js} +2 -2
- package/dist/cli/chunk-9vwb9f7q.js +7 -0
- package/dist/cli/{chunk-nqwza8b0.js → chunk-a5xbsvx2.js} +1 -1
- package/dist/cli/{chunk-t7dyf50m.js → chunk-ap3fgbbt.js} +1 -1
- package/dist/cli/{chunk-5kgq379z.js → chunk-b5fdqynx.js} +1 -1
- package/dist/cli/{chunk-x85rcep5.js → chunk-bbq6wc47.js} +1 -1
- package/dist/cli/{chunk-np398f2a.js → chunk-bbt217yd.js} +1 -1
- package/dist/cli/{chunk-w2tcb3rn.js → chunk-bm6qhcck.js} +1 -1
- package/dist/cli/{chunk-txvjg1g9.js → chunk-btxfcc77.js} +3 -3
- package/dist/cli/chunk-c9ptxthj.js +2 -0
- package/dist/cli/{chunk-zkby63ej.js → chunk-cs410j8b.js} +1 -1
- package/dist/cli/{chunk-rvjnafyt.js → chunk-cx78tyg1.js} +1 -1
- package/dist/cli/{chunk-4drm07mp.js → chunk-cx8wnz54.js} +1 -1
- package/dist/cli/{chunk-t2p22ww7.js → chunk-ejfs22aq.js} +1 -1
- package/dist/cli/chunk-f23manyf.js +8 -0
- package/dist/cli/{chunk-7nnpp89t.js → chunk-ga82ras9.js} +1 -1
- package/dist/cli/{chunk-f7egph8g.js → chunk-gzkes5bw.js} +1 -1
- package/dist/cli/{chunk-xy5mrrqk.js → chunk-hmy01yg2.js} +16 -14
- package/dist/cli/{chunk-3a7x7jg7.js → chunk-hnxs5shy.js} +16 -17
- package/dist/cli/{chunk-bw2y7670.js → chunk-j2e7jgh8.js} +1 -1
- package/dist/cli/{chunk-qc9sj5wh.js → chunk-j5y7hhc2.js} +1 -1
- package/dist/cli/{chunk-qzhfs668.js → chunk-jar50zcp.js} +1 -1
- package/dist/cli/{chunk-r98h6b42.js → chunk-k2rmj0v3.js} +2 -2
- package/dist/cli/{chunk-bv050ssb.js → chunk-k3h4cpdy.js} +1 -1
- package/dist/cli/{chunk-wz307hf6.js → chunk-k9xabf0t.js} +1 -1
- package/dist/cli/{chunk-02x0ahxk.js → chunk-kp43j420.js} +1 -1
- package/dist/cli/{chunk-6cfs0nqe.js → chunk-kt5ngemc.js} +1 -1
- package/dist/cli/chunk-nkgn0bqq.js +2 -0
- package/dist/cli/{chunk-fkgemeww.js → chunk-q2nhmaq9.js} +2 -2
- package/dist/cli/{chunk-hd1szhba.js → chunk-q6cwy6v5.js} +1 -1
- package/dist/cli/{chunk-jkgp816v.js → chunk-q72w3dsx.js} +1 -1
- package/dist/cli/{chunk-hmsvt5nj.js → chunk-rqm2kn0c.js} +1 -1
- package/dist/cli/{chunk-q6tsx6zc.js → chunk-sm96b61g.js} +1 -1
- package/dist/cli/chunk-sqdmngys.js +2 -0
- package/dist/cli/{chunk-k2j0tg1h.js → chunk-tgv90744.js} +7 -3
- package/dist/cli/{chunk-vbvbwjad.js → chunk-twy9bfps.js} +1 -1
- package/dist/cli/{chunk-5g6v6tq5.js → chunk-v3ecphrz.js} +1 -1
- package/dist/cli/{chunk-mjzjah9d.js → chunk-vtay379p.js} +1 -1
- package/dist/cli/chunk-vx44yxra.js +3 -0
- package/dist/cli/{chunk-w003rpr2.js → chunk-w3kvkax7.js} +1 -1
- package/dist/cli/{chunk-s1xn6wav.js → chunk-w6ft4djp.js} +1 -1
- package/dist/cli/{chunk-8hernv9y.js → chunk-w8dmmnys.js} +1 -1
- package/dist/cli/{chunk-9f34p80j.js → chunk-ww0zctb7.js} +1 -1
- package/dist/cli/{chunk-qwwt4ch2.js → chunk-x119xqa6.js} +1 -1
- package/dist/cli/{chunk-nrx8pf9p.js → chunk-x35dfb1x.js} +1 -1
- package/dist/cli/{chunk-ggjmcqpt.js → chunk-x94e0rw4.js} +3 -3
- package/dist/cli/chunk-xaam0d8r.js +4 -0
- package/dist/cli/{chunk-azt0c2b5.js → chunk-xekk4k54.js} +1 -1
- package/dist/cli/{chunk-mqvkd2g1.js → chunk-xqvyzv62.js} +1 -1
- package/dist/cli/chunk-xxz3np4w.js +2 -0
- package/dist/cli/{chunk-p11emdb2.js → chunk-y07cp9mb.js} +1 -1
- package/dist/cli/{chunk-5dq0n3h7.js → chunk-y2v1x23z.js} +1 -1
- package/dist/cli/chunk-yn01fet6.js +3 -0
- package/dist/cli/{chunk-cakyme4s.js → chunk-z8rvxvx5.js} +1 -1
- package/dist/cli/chunk-zr058kbe.js +3 -0
- package/dist/cli/index.js +3 -3
- package/dist/cli/rove.js +3 -3
- package/dist/skills/rove/SKILL.md +4 -2
- package/dist/skills/rove/references/api-flags.md +7 -4
- package/dist/voice/asr_server.py +318 -0
- package/dist/voice/engine.py +174 -0
- package/dist/voice/fetch_model.py +30 -0
- package/dist/voice/learn.py +184 -0
- package/dist/voice/live.py +93 -0
- package/dist/voice/phonetic.py +87 -0
- package/dist/voice/polish.py +84 -0
- package/dist/voice/polish_prompt.md +50 -0
- package/dist/voice/pyproject.toml +28 -0
- package/dist/voice/uv.lock +815 -0
- package/dist/voice/vocab.py +193 -0
- package/dist/web/assets/index-Bo92_NP5.css +1 -0
- package/dist/web/assets/index-Ct8dDnCc.js +154 -0
- package/dist/web/dictation-worklet.js +31 -0
- package/dist/web/index.html +3 -3
- package/dist/web/relay/index.html +6 -6
- package/package.json +4 -3
- package/dist/cli/chunk-0g1h35mr.js +0 -2
- package/dist/cli/chunk-1wwqf6kx.js +0 -3
- package/dist/cli/chunk-26ydgwym.js +0 -2
- package/dist/cli/chunk-2pfh3zm0.js +0 -33
- package/dist/cli/chunk-2wrt80ft.js +0 -3
- package/dist/cli/chunk-7fya3y0n.js +0 -2
- package/dist/cli/chunk-d2wndk4q.js +0 -2
- package/dist/cli/chunk-mey3vksj.js +0 -2
- package/dist/cli/chunk-nv8eyjep.js +0 -3
- package/dist/cli/chunk-ve76p2mx.js +0 -3
- package/dist/cli/chunk-wfgrn8p2.js +0 -4
- package/dist/cli/chunk-xmjknfy2.js +0 -7
- package/dist/web/assets/index-CuglNDLi.css +0 -1
- package/dist/web/assets/index-DIN0Lz5C.js +0 -149
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<meta name="referrer" content="no-referrer" />
|
|
7
|
-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'sha256-
|
|
7
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'sha256-lLcyTx77kzkYmrZqpg+PCPo7XxG8GkQnrKIRSXlJrc4=' blob:; style-src 'unsafe-inline'; font-src blob: data:; img-src data: blob:; media-src data: blob:; connect-src 'self' ws: wss:; base-uri 'none'; form-action 'none'" />
|
|
8
8
|
<title>rove</title>
|
|
9
9
|
<style>html,body{margin:0;background:#1a1a1a;color:#ccc;font:14px ui-monospace,monospace}#relay-status{padding:24px}</style>
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"><p id="relay-status"></p></div>
|
|
13
|
-
<script type="module">var
|
|
13
|
+
<script type="module">var Ht=Object.create;var{getPrototypeOf:Bt,defineProperty:ae,getOwnPropertyNames:Ct}=Object;var Mt=Object.prototype.hasOwnProperty;function Ot(t){return this[t]}var Nt,yt,Dt=(t,o,s)=>{var i=t!=null&&typeof t==="object";if(i){var h=o?Nt??=new WeakMap:yt??=new WeakMap,g=h.get(t);if(g)return g}s=t!=null?Ht(Bt(t)):{};let p=o||!t||!t.__esModule?ae(s,"default",{value:t,enumerable:!0}):s;for(let v of Ct(t))if(!Mt.call(p,v))ae(p,v,{get:Ot.bind(t,v),enumerable:!0});if(i)h.set(t,p);return p};var zt=(t,o)=>()=>(o||t((o={exports:{}}).exports,o),o.exports);var Ut=(t)=>t;function Et(t,o){this[t]=Ut.bind(null,o)}var f=(t,o)=>{for(var s in o)ae(t,s,{get:o[s],enumerable:!0,configurable:!0,set:Et.bind(o,s)})};var nt=zt((St,K)=>{(function(){function t(e,n){Object.defineProperty(i.prototype,e,{get:function(){console.warn("%s(...) is deprecated in plain JavaScript React classes. %s",n[0],n[1])}})}function o(e){if(e===null||typeof e!=="object")return null;return e=Ge&&e[Ge]||e["@@iterator"],typeof e==="function"?e:null}function s(e,n){e=(e=e.constructor)&&(e.displayName||e.name)||"ReactClass";var r=e+"."+n;Se[r]||(console.error("Can't call %s on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the %s component.",n,e),Se[r]=!0)}function i(e,n,r){this.props=e,this.context=n,this.refs=ie,this.updater=r||Ye}function h(){}function g(e,n,r){this.props=e,this.context=n,this.refs=ie,this.updater=r||Ye}function p(){}function v(e){return""+e}function x(e){try{v(e);var n=!1}catch(d){n=!0}if(n){n=console;var r=n.error,a=typeof Symbol==="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return r.call(n,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",a),v(e)}}function H(e){if(e==null)return null;if(typeof e==="function")return e.$$typeof===Rt?null:e.displayName||e.name||null;if(typeof e==="string")return e;switch(e){case oe:return"Fragment";case Ee:return"Profiler";case Ue:return"StrictMode";case Le:return"Suspense";case ft:return"SuspenseList";case Ie:return"Activity"}if(typeof e==="object")switch(typeof e.tag==="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case ze:return"Portal";case Fe:return e.displayName||"Context";case ne:return(e._context.displayName||"Context")+".Consumer";case Ae:var n=e.render;return e=e.displayName,e||(e=n.displayName||n.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case se:return n=e.displayName||null,n!==null?n:H(e.type)||"Memo";case A:n=e._payload,e=e._init;try{return H(e(n))}catch(r){}}return null}function B(e){if(e===oe)return"<>";if(typeof e==="object"&&e!==null&&e.$$typeof===A)return"<...>";try{var n=H(e);return n?"<"+n+">":"<...>"}catch(r){return"<...>"}}function b(){var e=l.A;return e===null?null:e.getOwner()}function N(){return Error("react-stack-top-frame")}function G(e){if(W.call(e,"key")){var n=Object.getOwnPropertyDescriptor(e,"key").get;if(n&&n.isReactWarning)return!1}return e.key!==void 0}function ct(e,n){function r(){Qe||(Qe=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",n))}r.isReactWarning=!0,Object.defineProperty(e,"key",{get:r,configurable:!0})}function mt(){var e=H(this.type);return Ze[e]||(Ze[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function J(e,n,r,a,d,c){var u=r.ref;return e={$$typeof:te,type:e,key:n,props:r,_owner:a},(u!==void 0?u:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:mt}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:d}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:c}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function pt(e,n){return n=J(e.type,n,e.props,e._owner,e._debugStack,e._debugTask),e._store&&(n._store.validated=e._store.validated),n}function ye(e){y(e)?e._store&&(e._store.validated=1):typeof e==="object"&&e!==null&&e.$$typeof===A&&(e._payload.status==="fulfilled"?y(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function y(e){return typeof e==="object"&&e!==null&&e.$$typeof===te}function wt(e){var n={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return n[r]})}function V(e,n){return typeof e==="object"&&e!==null&&e.key!=null?(x(e.key),wt(""+e.key)):n.toString(36)}function kt(e){switch(e.status){case"fulfilled":return e.value;case"rejected":throw e.reason;default:switch(typeof e.status==="string"?e.then(p,p):(e.status="pending",e.then(function(n){e.status==="pending"&&(e.status="fulfilled",e.value=n)},function(n){e.status==="pending"&&(e.status="rejected",e.reason=n)})),e.status){case"fulfilled":return e.value;case"rejected":throw e.reason}}throw e}function D(e,n,r,a,d){var c=typeof e;if(c==="undefined"||c==="boolean")e=null;var u=!1;if(e===null)u=!0;else switch(c){case"bigint":case"string":case"number":u=!0;break;case"object":switch(e.$$typeof){case te:case ze:u=!0;break;case A:return u=e._init,D(u(e._payload),n,r,a,d)}}if(u){u=e,d=d(u);var w=a===""?"."+V(u,0):a;return We(d)?(r="",w!=null&&(r=w.replace(je,"$&/")+"/"),D(d,n,r,"",function(C){return C})):d!=null&&(y(d)&&(d.key!=null&&(u&&u.key===d.key||x(d.key)),r=pt(d,r+(d.key==null||u&&u.key===d.key?"":(""+d.key).replace(je,"$&/")+"/")+w),a!==""&&u!=null&&y(u)&&u.key==null&&u._store&&!u._store.validated&&(r._store.validated=2),d=r),n.push(d)),1}if(u=0,w=a===""?".":a+":",We(e))for(var m=0;m<e.length;m++)a=e[m],c=w+V(a,m),u+=D(a,n,r,c,d);else if(m=o(e),typeof m==="function")for(m===e.entries&&(Ke||console.warn("Using Maps as children is not supported. Use an array of keyed ReactElements instead."),Ke=!0),e=m.call(e),m=0;!(a=e.next()).done;)a=a.value,c=w+V(a,m++),u+=D(a,n,r,c,d);else if(c==="object"){if(typeof e.then==="function")return D(kt(e),n,r,a,d);throw n=String(e),Error("Objects are not valid as a React child (found: "+(n==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":n)+"). If you meant to render a collection of children, use an array instead.")}return u}function S(e,n,r){if(e==null)return e;var a=[],d=0;return D(e,a,"","",function(c){return n.call(r,c,d++)}),a}function vt(e){if(e._status===-1){var n=e._ioInfo;n!=null&&(n.start=n.end=performance.now()),n=e._result;var r=n();if(r.then(function(d){if(e._status===0||e._status===-1){e._status=1,e._result=d;var c=e._ioInfo;c!=null&&(c.end=performance.now()),r.status===void 0&&(r.status="fulfilled",r.value=d)}},function(d){if(e._status===0||e._status===-1){e._status=2,e._result=d;var c=e._ioInfo;c!=null&&(c.end=performance.now()),r.status===void 0&&(r.status="rejected",r.reason=d)}}),n=e._ioInfo,n!=null){n.value=r;var a=r.displayName;typeof a==="string"&&(n.name=a)}e._status===-1&&(e._status=0,e._result=r)}if(e._status===1)return n=e._result,n===void 0&&console.error(`lazy: Expected the result of a dynamic import() call. Instead received: %s
|
|
14
14
|
|
|
15
15
|
Your code should look like:
|
|
16
16
|
const MyComponent = lazy(() => import('./MyComponent'))
|
|
@@ -18,17 +18,17 @@ Your code should look like:
|
|
|
18
18
|
Did you accidentally put curly braces around the import?`,n),"default"in n||console.error(`lazy: Expected the result of a dynamic import() call. Instead received: %s
|
|
19
19
|
|
|
20
20
|
Your code should look like:
|
|
21
|
-
const MyComponent = lazy(() => import('./MyComponent'))`,n),n.default;throw e._result}function k(){var e=
|
|
21
|
+
const MyComponent = lazy(() => import('./MyComponent'))`,n),n.default;throw e._result}function k(){var e=l.H;return e===null&&console.error(`Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
|
|
22
22
|
1. You might have mismatching versions of React and the renderer (such as React DOM)
|
|
23
23
|
2. You might be breaking the Rules of Hooks
|
|
24
24
|
3. You might have more than one copy of React in the same app
|
|
25
|
-
See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.`),e}function Oe(){m.asyncTransitions--}function Y(e){if(W===null)try{var n=("require"+Math.random()).slice(0,7);W=(j&&j[n]).call(j,"timers").setImmediate}catch(r){W=function(h){Je===!1&&(Je=!0,typeof MessageChannel>"u"&&console.error("This browser does not have a MessageChannel implementation, so enqueuing tasks via await act(async () => ...) will fail. Please file an issue at https://github.com/facebook/react/issues if you encounter this warning."));var d=new MessageChannel;d.port1.onmessage=h,d.port2.postMessage(void 0)}}return W(e)}function A(e){return 1<e.length&&typeof AggregateError==="function"?AggregateError(e):e[0]}function q(e,n){n!==X-1&&console.error("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. "),X=n}function _(e,n,r){var h=m.actQueue;if(h!==null)if(h.length!==0)try{ee(h),Y(function(){return _(e,n,r)});return}catch(d){m.thrownErrors.push(d)}else m.actQueue=null;0<m.thrownErrors.length?(h=A(m.thrownErrors),m.thrownErrors.length=0,r(h)):n(e)}function ee(e){if(!re){re=!0;var n=0;try{for(;n<e.length;n++){var r=e[n];do{m.didUsePromise=!1;var h=r(!1);if(h!==null){if(m.didUsePromise){e[n]=r,e.splice(0,n);return}r=h}else break}while(1)}e.length=0}catch(d){e.splice(0,n+1),m.thrownErrors.push(d)}finally{re=!1}}}typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart==="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var te=Symbol.for("react.transitional.element"),ze=Symbol.for("react.portal"),oe=Symbol.for("react.fragment"),Ue=Symbol.for("react.strict_mode"),Fe=Symbol.for("react.profiler"),ne=Symbol.for("react.consumer"),Ee=Symbol.for("react.context"),Ae=Symbol.for("react.forward_ref"),Le=Symbol.for("react.suspense"),vt=Symbol.for("react.suspense_list"),se=Symbol.for("react.memo"),L=Symbol.for("react.lazy"),ye=Symbol.for("react.activity"),Ge=Symbol.iterator,Ie={},Se={isMounted:function(){return!1},enqueueForceUpdate:function(e){s(e,"forceUpdate")},enqueueReplaceState:function(e){s(e,"replaceState")},enqueueSetState:function(e){s(e,"setState")}},Ye=Object.assign,ie={};Object.freeze(ie),i.prototype.isReactComponent={},i.prototype.setState=function(e,n){if(typeof e!=="object"&&typeof e!=="function"&&e!=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,e,n,"setState")},i.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};var P={isMounted:["isMounted","Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."],replaceState:["replaceState","Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."]};for(y in P)P.hasOwnProperty(y)&&t(y,P[y]);a.prototype=i.prototype,P=g.prototype=new a,P.constructor=g,Ye(P,i.prototype),P.isPureReactComponent=!0;var qe=Array.isArray,ft=Symbol.for("react.client.reference"),m={H:null,A:null,T:null,S:null,actQueue:null,asyncTransitions:0,isBatchingLegacy:!1,didScheduleLegacyUpdate:!1,didUsePromise:!1,thrownErrors:[],getCurrentStack:null,recentlyCreatedOwnerStacks:0},Q=Object.prototype.hasOwnProperty,Qe=console.createTask?console.createTask:function(){return null};P={react_stack_bottom_frame:function(e){return e()}};var We,Xe,Ze={},Rt=P.react_stack_bottom_frame.bind(P,D)(),Tt=Qe(B(D)),$e=!1,je=/\/+/g,Ke=typeof reportError==="function"?reportError:function(e){if(typeof window==="object"&&typeof window.ErrorEvent==="function"){var n=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof e==="object"&&e!==null&&typeof e.message==="string"?String(e.message):String(e),error:e});if(!window.dispatchEvent(n))return}else if(typeof process==="object"&&typeof process.emit==="function"){process.emit("uncaughtException",e);return}console.error(e)},Je=!1,W=null,X=0,Z=!1,re=!1,Ve=typeof queueMicrotask==="function"?function(e){queueMicrotask(function(){return queueMicrotask(e)})}:Y;P=Object.freeze({__proto__:null,c:function(e){return k().useMemoCache(e)}});var y={map:S,forEach:function(e,n,r){S(e,function(){n.apply(this,arguments)},r)},count:function(e){var n=0;return S(e,function(){n++}),n},toArray:function(e){return S(e,function(n){return n})||[]},only:function(e){if(!O(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};It.Activity=ye,It.Children=y,It.Component=i,It.Fragment=oe,It.Profiler=Fe,It.PureComponent=g,It.StrictMode=Ue,It.Suspense=Le,It.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=m,It.__COMPILER_RUNTIME=P,It.act=function(e){var n=m.actQueue,r=X;X++;var h=m.actQueue=n!==null?n:[],d=!1;try{var c=e()}catch(l){m.thrownErrors.push(l)}if(0<m.thrownErrors.length)throw q(n,r),e=A(m.thrownErrors),m.thrownErrors.length=0,e;if(c!==null&&typeof c==="object"&&typeof c.then==="function"){var u=c;return Ve(function(){d||Z||(Z=!0,console.error("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"))}),{then:function(l,H){d=!0,u.then(function(U){if(q(n,r),r===0){try{ee(h),Y(function(){return _(U,l,H)})}catch(bt){m.thrownErrors.push(bt)}if(0<m.thrownErrors.length){var Pt=A(m.thrownErrors);m.thrownErrors.length=0,H(Pt)}}else l(U)},function(U){q(n,r),0<m.thrownErrors.length?(U=A(m.thrownErrors),m.thrownErrors.length=0,H(U)):H(U)})}}}var p=c;if(q(n,r),r===0&&(ee(h),h.length!==0&&Ve(function(){d||Z||(Z=!0,console.error("A component suspended inside an `act` scope, but the `act` call was not awaited. When testing React components that depend on asynchronous data, you must await the result:\n\nawait act(() => ...)"))}),m.actQueue=null),0<m.thrownErrors.length)throw e=A(m.thrownErrors),m.thrownErrors.length=0,e;return{then:function(l,H){d=!0,r===0?(m.actQueue=h,Y(function(){return _(p,l,H)})):l(p)}}},It.cache=function(e){return function(){return e.apply(null,arguments)}},It.cacheSignal=function(){return null},It.captureOwnerStack=function(){var e=m.getCurrentStack;return e===null?null:e()},It.cloneElement=function(e,n,r){if(e===null||e===void 0)throw Error("The argument must be a React element, but you passed "+e+".");var h=Ye({},e.props),d=e.key,c=e._owner;if(n!=null){var u;e:{if(Q.call(n,"ref")&&(u=Object.getOwnPropertyDescriptor(n,"ref").get)&&u.isReactWarning){u=!1;break e}u=n.ref!==void 0}u&&(c=T()),I(n)&&(x(n.key),d=""+n.key);for(p in n)!Q.call(n,p)||p==="key"||p==="__self"||p==="__source"||p==="ref"&&n.ref===void 0||(h[p]=n[p])}var p=arguments.length-2;if(p===1)h.children=r;else if(1<p){u=Array(p);for(var l=0;l<p;l++)u[l]=arguments[l+2];h.children=u}h=J(e.type,d,h,c,e._debugStack,e._debugTask);for(d=2;d<arguments.length;d++)De(arguments[d]);return h},It.createContext=function(e){return e={$$typeof:Ee,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null},e.Provider=e,e.Consumer={$$typeof:ne,_context:e},e._currentRenderer=null,e._currentRenderer2=null,e},It.createElement=function(e,n,r){for(var h=2;h<arguments.length;h++)De(arguments[h]);h={};var d=null;if(n!=null)for(l in Xe||!("__self"in n)||"key"in n||(Xe=!0,console.warn("Your app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance: https://react.dev/link/new-jsx-transform")),I(n)&&(x(n.key),d=""+n.key),n)Q.call(n,l)&&l!=="key"&&l!=="__self"&&l!=="__source"&&(h[l]=n[l]);var c=arguments.length-2;if(c===1)h.children=r;else if(1<c){for(var u=Array(c),p=0;p<c;p++)u[p]=arguments[p+2];Object.freeze&&Object.freeze(u),h.children=u}if(e&&e.defaultProps)for(l in c=e.defaultProps,c)h[l]===void 0&&(h[l]=c[l]);d&&mt(h,typeof e==="function"?e.displayName||e.name||"Unknown":e);var l=1e4>m.recentlyCreatedOwnerStacks++;return J(e,d,h,T(),l?Error("react-stack-top-frame"):Rt,l?Qe(B(e)):Tt)},It.createRef=function(){var e={current:null};return Object.seal(e),e},It.forwardRef=function(e){e!=null&&e.$$typeof===se?console.error("forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."):typeof e!=="function"?console.error("forwardRef requires a render function but was given %s.",e===null?"null":typeof e):e.length!==0&&e.length!==2&&console.error("forwardRef render functions accept exactly two parameters: props and ref. %s",e.length===1?"Did you forget to use the ref parameter?":"Any additional parameter will be undefined."),e!=null&&e.defaultProps!=null&&console.error("forwardRef render functions do not support defaultProps. Did you accidentally pass a React component?");var n={$$typeof:Ae,render:e},r;return Object.defineProperty(n,"displayName",{enumerable:!1,configurable:!0,get:function(){return r},set:function(h){r=h,e.name||e.displayName||(Object.defineProperty(e,"name",{value:h}),e.displayName=h)}}),n},It.isValidElement=O,It.lazy=function(e){e={_status:-1,_result:e};var n={$$typeof:L,_payload:e,_init:kt},r={name:"lazy",start:-1,end:-1,value:null,owner:null,debugStack:Error("react-stack-top-frame"),debugTask:console.createTask?console.createTask("lazy()"):null};return e._ioInfo=r,n._debugInfo=[{awaited:r}],n},It.memo=function(e,n){e==null&&console.error("memo: The first argument must be a component. Instead received: %s",e===null?"null":typeof e),n={$$typeof:se,type:e,compare:n===void 0?null:n};var r;return Object.defineProperty(n,"displayName",{enumerable:!1,configurable:!0,get:function(){return r},set:function(h){r=h,e.name||e.displayName||(Object.defineProperty(e,"name",{value:h}),e.displayName=h)}}),n},It.startTransition=function(e){var n=m.T,r={};r._updatedFibers=new Set,m.T=r;try{var h=e(),d=m.S;d!==null&&d(r,h),typeof h==="object"&&h!==null&&typeof h.then==="function"&&(m.asyncTransitions++,h.then(Oe,Oe),h.then(w,Ke))}catch(c){Ke(c)}finally{n===null&&r._updatedFibers&&(e=r._updatedFibers.size,r._updatedFibers.clear(),10<e&&console.warn("Detected a large number of updates inside startTransition. If this is due to a subscription please re-write it to use React provided hooks. Otherwise concurrent mode guarantees are off the table.")),n!==null&&r.types!==null&&(n.types!==null&&n.types!==r.types&&console.error("We expected inner Transitions to have transferred the outer types set and that you cannot add to the outer Transition while inside the inner.This is a bug in React."),n.types=r.types),m.T=n}},It.unstable_useCacheRefresh=function(){return k().useCacheRefresh()},It.use=function(e){return k().use(e)},It.useActionState=function(e,n,r){return k().useActionState(e,n,r)},It.useCallback=function(e,n){return k().useCallback(e,n)},It.useContext=function(e){var n=k();return e.$$typeof===ne&&console.error("Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you mean to call useContext(Context) instead?"),n.useContext(e)},It.useDebugValue=function(e,n){return k().useDebugValue(e,n)},It.useDeferredValue=function(e,n){return k().useDeferredValue(e,n)},It.useEffect=function(e,n){return e==null&&console.warn("React Hook useEffect requires an effect callback. Did you forget to pass a callback to the hook?"),k().useEffect(e,n)},It.useEffectEvent=function(e){return k().useEffectEvent(e)},It.useId=function(){return k().useId()},It.useImperativeHandle=function(e,n,r){return k().useImperativeHandle(e,n,r)},It.useInsertionEffect=function(e,n){return e==null&&console.warn("React Hook useInsertionEffect requires an effect callback. Did you forget to pass a callback to the hook?"),k().useInsertionEffect(e,n)},It.useLayoutEffect=function(e,n){return e==null&&console.warn("React Hook useLayoutEffect requires an effect callback. Did you forget to pass a callback to the hook?"),k().useLayoutEffect(e,n)},It.useMemo=function(e,n){return k().useMemo(e,n)},It.useOptimistic=function(e,n){return k().useOptimistic(e,n)},It.useReducer=function(e,n,r){return k().useReducer(e,n,r)},It.useRef=function(e){return k().useRef(e)},It.useState=function(e){return k().useState(e)},It.useSyncExternalStore=function(e,n,r){return k().useSyncExternalStore(e,n,r)},It.useTransition=function(){return k().useTransition()},It.version="19.2.8",typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop==="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())})()});var R=(globalThis.crypto??globalThis.crypto).subtle,G=new TextEncoder;function N(t){let o="";for(let s=0;s<t.length;s+=32768)o+=String.fromCharCode(...t.subarray(s,s+32768));return btoa(o).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function M(t){let o=t.replace(/-/g,"+").replace(/_/g,"/"),s=atob(o+"=".repeat((4-o.length%4)%4)),i=new Uint8Array(s.length);for(let a=0;a<s.length;a++)i[a]=s.charCodeAt(a);return i}function ae(...t){let o=t.reduce((a,g)=>a+g.length,0),s=new Uint8Array(o),i=0;for(let a of t)s.set(a,i),i+=a.length;return s}function de(t,o){if(t.length!==o.length)return!1;let s=0;for(let i=0;i<t.length;i++)s|=t[i]^o[i];return s===0}function b(t){return t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)}async function Et(t){return new Uint8Array(await R.digest("SHA-256",b(t)))}async function _e(t){let o=await R.importKey("raw",b(G.encode(t)),"HKDF",!1,["deriveBits"]),s=await R.deriveBits({name:"HKDF",hash:"SHA-256",salt:G.encode("relay-e2e/v1/routing"),info:new Uint8Array(0)},o,128);return N(new Uint8Array(s))}async function ge(){let t=await R.generateKey({name:"ECDH",namedCurve:"P-256"},!1,["deriveBits"]);return{privateKey:t.privateKey,publicKeyRaw:new Uint8Array(await R.exportKey("raw",t.publicKey))}}async function $(t,o,s,i){let a=await R.importKey("raw",b(t),"HKDF",!1,["deriveBits"]);return new Uint8Array(await R.deriveBits({name:"HKDF",hash:"SHA-256",salt:b(o),info:b(ae(G.encode(s),i))},a,256))}async function ue(t){let o=await R.importKey("raw",b(t.peerPublicKeyRaw),{name:"ECDH",namedCurve:"P-256"},!1,[]),s=new Uint8Array(await R.deriveBits({name:"ECDH",public:o},t.privateKey,256)),i=await Et(ae(G.encode("relay-e2e/v1"),t.initiatorPublicKeyRaw,t.responderPublicKeyRaw,t.initiatorNonce,t.responderNonce)),a=G.encode(t.code),[g,w,v,x]=await Promise.all([$(s,a,"c2s|",i),$(s,a,"s2c|",i),$(s,a,"confirm-r|",i),$(s,a,"confirm-i|",i)]);return{c2s:await R.importKey("raw",b(g),"AES-GCM",!1,["encrypt","decrypt"]),s2c:await R.importKey("raw",b(w),"AES-GCM",!1,["encrypt","decrypt"]),confirmResponder:v,confirmInitiator:x}}class F{#e=0n;#t=null;#n;constructor(t){this.#n=t}async seal(t){let o=new Uint8Array(12);new DataView(o.buffer).setBigUint64(4,this.#e++);let s=new Uint8Array(await R.encrypt({name:"AES-GCM",iv:b(o)},this.#n,b(t)));return ae(o,s)}async open(t){if(t.length<12)throw Error("E2E frame too short");let o=t.subarray(0,12),s=t.subarray(12),i=new DataView(o.buffer,o.byteOffset+4,8).getBigUint64(0);if(this.#t!==null&&i<=this.#t)throw Error(`E2E replay detected (counter ${i} <= ${this.#t})`);return this.#t=i,new Uint8Array(await R.decrypt({name:"AES-GCM",iv:b(o)},this.#n,b(s)))}}class me{#e;#t;#n;#o;#a;#h;#s=null;#d=crypto.getRandomValues(new Uint8Array(16));#r=null;#g=null;#u=null;#i="init";#w=[];#p=Promise.resolve();#m=Promise.resolve();#k=new TextEncoder;#f=new TextDecoder;constructor(t){this.#e=t.role,this.#t=t.code,this.#n=t.send,this.#o=t.onReady,this.#a=t.onFrame,this.#h=t.onError}async start(){if(this.#e!=="initiator")return;this.#s=await ge(),this.#i="await-hi",this.#n(JSON.stringify({t:"hi",epk:N(this.#s.publicKeyRaw),n:N(this.#d)}))}isReady(){return this.#i==="ready"}sendFrame(t){if(this.#i==="failed")return Promise.resolve();if(this.#i!=="ready"||!this.#g)return this.#w.push(t),Promise.resolve();let o=this.#p.then(async()=>{if(this.#i==="failed"||!this.#g)throw Error("E2E session failed");let s=await this.#g.seal(this.#k.encode(JSON.stringify(t)));this.#n(s)});return this.#p=o.catch((s)=>this.#c(s instanceof Error?s.message:String(s))),o}onMessage(t){return this.#m=this.#m.then(()=>this.#T(t)).catch(()=>{}),this.#m}async#T(t){if(this.#i==="failed")return;try{if(typeof t==="string")await this.#v(JSON.parse(t));else{if(!this.#u)return;let o=await this.#u.open(t);this.#a(JSON.parse(this.#f.decode(o)))}}catch(o){this.#c(o instanceof Error?o.message:String(o))}}async#v(t){if(this.#e==="responder"&&t.t==="hi"&&this.#i==="init"){let o=M(String(t.epk)),s=M(String(t.n));this.#s=await ge(),this.#r=await ue({privateKey:this.#s.privateKey,peerPublicKeyRaw:o,code:this.#t,initiatorPublicKeyRaw:o,responderPublicKeyRaw:this.#s.publicKeyRaw,initiatorNonce:s,responderNonce:this.#d}),this.#i="await-ok",this.#n(JSON.stringify({t:"hi",epk:N(this.#s.publicKeyRaw),n:N(this.#d),cr:N(this.#r.confirmResponder)}));return}if(this.#e==="initiator"&&t.t==="hi"&&this.#i==="await-hi"){let o=M(String(t.epk)),s=M(String(t.n));if(this.#r=await ue({privateKey:this.#s.privateKey,peerPublicKeyRaw:o,code:this.#t,initiatorPublicKeyRaw:this.#s.publicKeyRaw,responderPublicKeyRaw:o,initiatorNonce:this.#d,responderNonce:s}),!de(M(String(t.cr)),this.#r.confirmResponder)){this.#n(JSON.stringify({t:"err",message:"auth-failed"})),this.#c("Handshake authentication failed (wrong code or man-in-the-middle)");return}this.#n(JSON.stringify({t:"ok",ci:N(this.#r.confirmInitiator)})),this.#g=new F(this.#r.c2s),this.#u=new F(this.#r.s2c),await this.#l();return}if(this.#e==="responder"&&t.t==="ok"&&this.#i==="await-ok"){if(!de(M(String(t.ci)),this.#r.confirmInitiator)){this.#c("Handshake authentication failed (wrong code or man-in-the-middle)");return}this.#g=new F(this.#r.s2c),this.#u=new F(this.#r.c2s),await this.#l();return}if(t.t==="err")this.#c(`Peer rejected handshake: ${String(t.message||"unknown")}`)}async#l(){this.#i="ready",this.#o();let t=this.#w;this.#w=[];for(let o of t)await this.sendFrame(o)}#c(t){if(this.#i!=="failed")this.#i="failed",this.#h(t)}}function At(t,o){switch(t){case 1000:return"closed normally";case 1001:return"relay went away — server shutdown or redeploy";case 1006:return"socket died without a close frame — radio drop, network switch, or NAT idle timeout; reconnect";case 1011:return"agent disconnected — the desktop side went offline; retry once it is back";case 1015:return"TLS handshake failed — TLS-intercepting middlebox or wrong scheme";case 4404:return"no agent registered for this pairing — is the desktop agent running, and is the code right?"}if(t!==null&&t>=4000&&t<5000)return"relay policy rejection — see PROTOCOL.md §9";if(o)return`relay said: ${o}`;return"unexpected close — see PROTOCOL.md §9"}function Lt(t){if(/timed out/i.test(t))return"relay unreachable or slow — check the server URL and network path";if(/authentication failed|auth-failed/i.test(t))return"handshake rejected — wrong pairing code";if(/Failed to open/i.test(t))return"could not open the WebSocket — bad server URL, blocked network, or TLS issue";if(/went silent/i.test(t))return"no frame for idleTimeoutMs — radio sleep or half-dead socket; reconnect and retry";if(/disconnected during pairing/i.test(t))return"close() or a newer connect() interrupted this attempt";if(/not connected/i.test(t))return"tunnel was already down";return"see PROTOCOL.md §9 and the relay's own logs (wrangler tail)"}class et{status;headers;#e;constructor(t,o,s){this.status=t,this.headers=o,this.#e=s}text(){return new TextDecoder().decode(this.concat())}json(){return JSON.parse(this.text())}bytes(){return this.concat()}concat(){let t=0;for(let i of this.#e)t+=i.length;let o=new Uint8Array(t),s=0;for(let i of this.#e)o.set(i,s),s+=i.length;return o}}class tt{#e;#t={open:new Set,message:new Set,close:new Set,error:new Set};onopen=null;onmessage=null;onclose=null;onerror=null;readyState=0;constructor(t){this.#e=t}#n(t,o){this.#t[t].forEach((s)=>s(o)),this["on"+t]?.(o)}addEventListener(t,o){this.#t[t].add(o)}removeEventListener(t,o){this.#t[t].delete(o)}send(t){if(this.readyState!==1)throw Error("tunnel ws not open");if(typeof t==="string")this.#e({t:"wmsg",id:this.#o,s:t});else this.#e({t:"wmsg",id:this.#o,b:ce(t)})}close(){if(this.readyState===2)return;this.readyState=2,this.#e({t:"wclose",id:this.#o,code:1000}),this.#n("close")}#o;_attach(t){this.#o=t}_opened(){if(this.readyState===1)return;this.readyState=1,this.#n("open")}_message(t){for(let o of this.#t.message)o(t);this.onmessage?.(t)}_error(t){this.readyState=2,this.#n("error",t)}_closed(){if(this.readyState===2)return;this.readyState=2,this.#n("close")}}function ce(t){let o="";for(let s=0;s<t.length;s+=32768)o+=String.fromCharCode(...t.subarray(s,s+32768));return btoa(o)}function yt(t){try{return new URL(t).host}catch{return t}}function Gt(t){if(t<1000)return`${t} ms`;if(t<60000)return`${(t/1000).toFixed(1)} s`;return`${(t/60000).toFixed(1)} min`}class le{#e;#t=null;#n=null;#o="idle";#a=new Map;#h=new Map;#s=[];#d=!1;#r=null;#g=null;#u=0;#i=0;#w=null;#p=null;constructor(t){this.#e=t}get state(){return this.#o}#m(t,o){this.#o=t,this.#e.onStateChange?.(t,o)}#k(t){if(!this.#e.quiet){if(t.type==="connect")console.log(`[RelayClient] connecting to ${yt(t.server)}`);else if(t.type==="ready")console.log(`[RelayClient] tunnel ready (${t.ms} ms)`);else if(t.type==="disconnect"){if(!t.byUser){let o=t.lastedMs!=null?` after ${Gt(t.lastedMs)}`:" before ready",s=t.error??`close ${t.code}`;console.warn(`[RelayClient] tunnel closed${o}: ${s} — ${t.hint}`)}}else if(!t.byUser)console.warn(`[RelayClient] tunnel failed: ${t.error} — ${t.hint}`)}try{this.#e.onDiag?.(t)}catch(o){console.warn("[RelayClient] onDiag threw:",o)}}async connect(){if(this.#o==="connecting"||this.#o==="ready")return this.ready();this.#d=!1,this.#m("connecting"),this.#w=Date.now(),this.#p=null,this.#k({type:"connect",server:this.#e.serverUrl});let t=++this.#i;this.#f();let o=this.#e.WebSocketImpl??globalThis.WebSocket,s=await _e(this.#e.code),i=new o(`${this.#e.serverUrl.replace(/\/+$/,"").replace(/^http/,"ws")}/c/${s}/__alma_e2e__`);i.binaryType="arraybuffer",this.#t=i,this.#r=setTimeout(()=>{this.#l("Encrypted relay connection timed out");try{i.close()}catch{}},this.#e.connectTimeoutMs??20000);let a=new me({role:"initiator",code:this.#e.code,send:(g)=>i.send(g),onReady:()=>{if(this.#t!==i)return;clearTimeout(this.#r),this.#u=Date.now(),this.#p=Date.now(),this.#T(),this.#m("ready"),this.#k({type:"ready",ms:this.#w?Date.now()-this.#w:0});for(let g of this.#s)g.resolve();this.#s=[]},onFrame:(g)=>this.#b(g),onError:(g)=>this.#l(g)});if(this.#n=a,i.onopen=()=>void a.start(),i.onmessage=(g)=>{if(this.#t!==i)return;let w=g.data;if(typeof w==="string")a.onMessage(w);else if(w instanceof ArrayBuffer)a.onMessage(new Uint8Array(w));else if(ArrayBuffer.isView(w))a.onMessage(new Uint8Array(w.buffer,w.byteOffset,w.byteLength))},i.onclose=(g)=>{if(this.#t!==i)return;clearTimeout(this.#r);let w=`Relay tunnel closed (${g.code}${g.reason?` ${g.reason}`:""})`;this.#P(w,g.code,g.reason||null)},i.onerror=()=>{if(this.#t===i&&this.#o==="connecting")this.#l("Failed to open relay tunnel")},t!==this.#i)this.#l("Client disconnected during pairing");return this.ready()}ready(){if(this.#o==="ready")return Promise.resolve();if(this.#o==="error"||this.#o==="closed")return Promise.reject(Error("E2E tunnel not connected"));return new Promise((t,o)=>this.#s.push({resolve:t,reject:o}))}close(){this.#i++,this.#d=!0;try{this.#t?.close()}catch{}this.#f(),clearTimeout(this.#r),this.#v(),this.#c(Error("Tunnel closed"));for(let t of this.#s)t.reject(Error("Tunnel closed"));this.#s=[],this.#m("closed")}get lastFrameAt(){return this.#u}#f(){let t=this.#t;if(t)t.onmessage=null,t.onclose=null,t.onerror=null,t.onopen=null;this.#t=null,this.#n=null}#T(){this.#v(),this.#g=setInterval(()=>{if(!this.#a.size||Date.now()-this.#u<=(this.#e.idleTimeoutMs??45000))return;this.#l("Tunnel went silent with a request pending");try{this.#t?.close()}catch{}},2000)}#v(){if(this.#g)clearInterval(this.#g);this.#g=null}#l(t){this.#v();let o=this.#t;this.#f();try{o?.close()}catch{}this.#c(Error(t));for(let s of this.#s)s.reject(Error(t));this.#s=[],this.#m("error",t),this.#k({type:"failed",error:t,hint:Lt(t),byUser:this.#d})}#c(t){for(let[,o]of this.#a)o.reject(t);this.#a.clear();for(let[,o]of this.#h)o._closed();this.#h.clear()}#P(t,o=null,s=null){this.#v(),this.#c(Error(t));for(let i of this.#s)i.reject(Error(t));this.#s=[],this.#m(this.#d?"closed":"error",t),this.#k({type:"disconnect",code:o,reason:s,error:t,hint:At(o,s),lastedMs:this.#p?Date.now()-this.#p:null,byUser:this.#d})}#R(t){this.#n?.sendFrame(t)}#b(t){if(this.#u=Date.now(),t.t==="hres"||t.t==="hchunk"||t.t==="hend"||t.t==="herr"){let o=this.#a.get(String(t.id));if(!o)return;if(t.t==="hres")o.status=t.status,o.headers=t.headers,o.onHeaders?.(t.status,t.headers);else if(t.t==="hchunk"){let s=atob(t.b),i=new Uint8Array(s.length);for(let a=0;a<s.length;a++)i[a]=s.charCodeAt(a);o.chunks.push(i),o.onChunk?.(i)}else if(t.t==="hend")this.#a.delete(String(t.id)),o.resolve(new et(o.status??0,o.headers??{},o.chunks));else this.#a.delete(String(t.id)),o.reject(Error(t.message));return}if(t.t==="wopened"){this.#h.get(String(t.id))?._opened();return}if(t.t==="wmsg"){let o=this.#h.get(String(t.id));if(!o)return;if(typeof t.s==="string")o._message(t.s);else if(typeof t.b==="string"){let s=atob(t.b),i=new Uint8Array(s.length);for(let a=0;a<s.length;a++)i[a]=s.charCodeAt(a);o._message(i)}return}if(t.t==="wclose"){let o=this.#h.get(String(t.id));if(o)this.#h.delete(String(t.id)),o._closed();return}if(t.t==="werr"){let o=this.#h.get(String(t.id));if(o)this.#h.delete(String(t.id)),o._error(t.message)}}async fetch(t,o={}){if(await this.ready(),this.#o==="closed"||this.#o==="error")throw Error("Tunnel closed");let s=o.signal;if(s?.aborted)throw Error("aborted");let i=`h${Math.random().toString(36).slice(2)}`,a=(o.method??"GET").toUpperCase(),g;if(o.body!=null&&a!=="GET"&&a!=="HEAD")g=typeof o.body==="string"?ce(new TextEncoder().encode(o.body)):ce(o.body);let w=new Promise((v,x)=>{let C={resolve:v,reject:x,status:0,headers:{},chunks:[],onChunk:o.onChunk,onHeaders:o.onHeaders};if(s){let B=()=>{if(!this.#a.delete(i))return;this.#R({t:"hcancel",id:i}),C.reject(Error("aborted"))};s.addEventListener("abort",B,{once:!0});let T=(D)=>(I)=>{s.removeEventListener("abort",B),D(I)};C.resolve=T(v),C.reject=T(x)}this.#a.set(i,C)});return this.#R({t:"hreq",id:i,method:a,path:t,headers:o.headers??{},b:g}),w}async openSocket(t){if(await this.ready(),this.#o==="closed"||this.#o==="error")throw Error("Tunnel closed");let o=`w${Math.random().toString(36).slice(2)}`,s=new tt((a)=>this.#R(a));this.#h.set(o,s),s._attach(o);let i=new Promise((a,g)=>{let w=setTimeout(()=>{this.#h.delete(o),s._closed(),g(Error("tunnel ws open timeout"))},15000);s.addEventListener("open",()=>{clearTimeout(w),a()}),s.addEventListener("error",(v)=>{clearTimeout(w),this.#h.delete(o),g(Error(String(v)))})});return this.#R({t:"wopen",id:o,path:t}),await i,s}}var St=Ot(ot(),1);function nt(t){let o=t,s=new Set;return{get:()=>o,set(i){if(Object.is(i,o))return;o=i;for(let a of s)a()},subscribe(i){return s.add(i),()=>s.delete(i)}}}var we={};f(we,{zh:()=>qt,en:()=>Yt});var Yt={artifact:{preview:"preview",source:"source",open:"[ open ]",loading:"Loading…",tooLarge:"Too large to preview ({size}); the source view reads it as text.",notPreviewable:"This file cannot be previewed.",gone:"The file is gone.",failed:"Could not read the file: {error}"}},qt={artifact:{preview:"预览",source:"源码",open:"[ 打开 ]",loading:"加载中…",tooLarge:"文件太大,无法预览({size});可在源码视图中按文本查看。",notPreviewable:"这个文件无法预览。",gone:"文件已不存在。",failed:"无法读取文件:{error}"}};var pe={};f(pe,{zh:()=>Wt,en:()=>Qt});var Qt={compare:{title:"Compare attempts",menu:"Compare attempts",severalEngines:"[ compare engines ]",oneEngine:"[ one engine ]",needsPrompt:"Compare runs the prompt, so it needs one",empty:"No attempts left in this round.",noReply:"no reply yet",noChanges:"no changes",status:{working:"working",waiting:"waiting for you",error:"needs a look",done:"done",idle:"idle"},files:"files: {count}",more:"+{count} more",kept:"kept",strip:"[ compare ]",stackLabel:"Stacked tasks",landBottom:"[ land bottom ]",terminal:"[ terminal ]",diff:"[ diff ]",keep:"[ keep this one ]",unkeep:"[ unkeep ]",keptToast:"kept {title}",unkeptToast:"no longer kept: {title}",keepFailed:"could not mark it kept: {error}",archiveTitle:"Archive the other attempts?",archiveBody:"Hides the other {count} from the rail. Their worktrees and branches stay, and nothing running is stopped.",archiveConfirm:"Archive",archiveBusyTitle:"Some attempts are still working",archiveBusyBody:"Archive them anyway? Their worktrees and branches stay, and nothing is stopped.",archiveBusyConfirm:"Archive anyway",archived:"archived {count} attempts",archiveFailed:"could not archive: {error}"}},Wt={compare:{title:"对比方案",menu:"对比方案",severalEngines:"[ 多引擎对比 ]",oneEngine:"[ 单引擎 ]",needsPrompt:"对比要运行提示词,请先写一个",empty:"这一轮没有剩下的方案。",noReply:"还没有回复",noChanges:"没有改动",status:{working:"运行中",waiting:"等你处理",error:"需要看一眼",done:"已完成",idle:"空闲"},files:"文件:{count}",more:"另有 {count} 个",kept:"已保留",strip:"[ 对比 ]",stackLabel:"堆叠任务",landBottom:"[ 合并底部 ]",terminal:"[ 终端 ]",diff:"[ diff ]",keep:"[ 保留这个 ]",unkeep:"[ 取消保留 ]",keptToast:"已保留 {title}",unkeptToast:"已取消保留:{title}",keepFailed:"无法标记保留:{error}",archiveTitle:"归档其他方案?",archiveBody:"把其余 {count} 个从侧栏收起。它们的 worktree 和分支都保留,运行中的也不会被停掉。",archiveConfirm:"归档",archiveBusyTitle:"有方案还在运行",archiveBusyBody:"仍要归档吗?它们的 worktree 和分支都保留,也不会停掉任何东西。",archiveBusyConfirm:"仍然归档",archived:"已归档 {count} 个方案",archiveFailed:"无法归档:{error}"}};var ke={};f(ke,{zh:()=>Zt,en:()=>Xt});var Xt={context:{title:"Context",used:"{percent} used",usedTokens:"{tokens} used",loading:"Reading context…",none:"No context reading for this conversation: its engine records none, or it has not answered yet.",failed:"Couldn't read context: {error}",approximate:"Approximate.",part:{system:"System prompt & tools",messages:"Messages",used:"Used",free:"Free space"}}},Zt={context:{title:"上下文",used:"已用 {percent}",usedTokens:"已用 {tokens}",loading:"正在读取上下文…",none:"这个会话没有上下文读数:它的引擎不记录,或者还没回复过。",failed:"读取上下文失败:{error}",approximate:"近似值。",part:{system:"系统提示与工具",messages:"消息",used:"已用",free:"剩余空间"}}};var ve={};f(ve,{zh:()=>jt,en:()=>$t});var $t={dialog:{cancel:"cancel",save:"save",close:"close"},whatsNew:{continue:"Continue",openRelease:"Open release notes",keys:"q / esc"},menu:{closeTab:"Close tab",newChat:"New conversation",newChatPick:"New conversation with…",retryEdit:"Retry with edited prompt…",retryNoPrompt:"This task has no stored prompt",startNow:"Start now",cancelStart:"Cancel scheduled start",continueEngine:"Continue in another engine…",newShell:"New shell",newChatNoFolder:"New conversation (no folder)",renameTab:"Rename tab",splitRight:"Split right",splitDown:"Split down",renamePane:"Rename pane",closePane:"Close pane",newTask:"New task",rename:"Rename",pin:"Pin",unpin:"Unpin",setStatus:"Set status",copy:"Copy",copyWhat:{id:"Task ID",branch:"Branch name",path:"Worktree path"},renameBranch:"Rename branch",changeEngine:"Change engine",land:"Land into base branch",delete:"Delete",removeSession:"Remove session",copied:"copied {text}",copyFailed:"the browser refused the clipboard",nothingToCopy:"this task has no {what} yet",what:{id:"id",branch:"branch",path:"worktree path"}},confirm:{closeTabTitle:'Close "{tab}"?',closeTabBody:"Stops the session in this tab. The task and its worktree stay.",closePaneTitle:'Close "{pane}"?',closePaneBody:"The engine session in this pane ends. The tab's other panes stay.",delete:"delete",forceDelete:"force delete"},newTask:{title:"new task",repo:"project",engine:"engine",notReady:"not installed or not signed in",taskTitle:"title",titlePlaceholder:"optional; derived from the prompt otherwise",prompt:"first message",promptPlaceholder:"optional; ⌘↵ creates",create:"create",needRepo:"pick a project first"},newChat:{title:"new conversation",prompt:"first message",start:"start",promptPlaceholder:"optional; empty opens the engine without a message"},newSession:{title:"New session",tagline:"Pick a project and a model, then describe the task. The session starts with your first message.",placeholder:"Describe a task or ask a question",start:"Start session",createOnly:"Create without starting",later:"Later…",laterConfirm:"Start later",laterPlaceholder:"22:30 · tomorrow 9:00 · +30m",laterBad:"That is not a time ahead. Try 22:30, tomorrow 9:00 or +2h.",laterSet:"Starts at {time}",retryArchiveTitle:'Archive "{title}"?',retryArchiveBody:"The retry has started. Archive the original? Nothing is stopped or deleted.",needRepo:"Pick a project first",needPrompt:"Describe the task first",repo:"Project",repoSearch:"Search projects or type a path",repoEmpty:"No matching project",cloneRow:"Clone {source}",cloning:"Cloning {source}…",githubGroup:"Your GitHub repositories",branch:"Branch",branchSearch:"Search branches",branches:"Branches",branchName:"Branch name, shared by every repo",branchNamePlaceholder:"derived from the prompt",primary:"primary",repoCount:"{count} repos: the first is the primary",branchEmpty:"No matching branch",model:"Model",modelHeading:"Model for this session",engine:"Engine",effort:"Effort",modelDefault:"Default",effortDefault:"Default",moreWays:"Clone or adopt a worktree…",attach:"Attach",attachHint:"Add images or PDFs to the first message; you can also paste or drop them on the prompt",removeAttachment:"Remove {name}",worktree:"Worktree",worktreeOn:"Runs in its own worktree and branch. Click to run in the project's checkout instead",worktreeOff:"Runs in the project's checkout as it is, on its current branch. Click for a worktree",worktreeMulti:"A task across several repos always gets a worktree in each",machine:"Machine",thisMachine:"This machine",cloneRowOn:"Clone {source} on {machine}",cloningOn:"Cloning {source} on {machine}…",startingOn:"Starting the task on {machine}…",repoSearchRemote:"Search or type owner/repo",cloneTag:"clone",remoteOffline:"{machine} is offline",remoteOutdated:"{machine} runs a Rove too old to list its projects",remoteFailed:"Could not read {machine}'s projects: {message}",remoteNone:"{machine} has no projects yet. Type owner/repo to clone one there",remoteLoading:"Reading {machine}'s projects…",branchTypeRef:"Type a branch or ref to start from",branchRemoteDefault:"Its current branch"},search:{tasks:"Tasks",commands:"Commands",inConversations:"In conversations",searching:"searching…",none:"Nothing said matches “{query}”.",you:"you",agent:"agent",truncated:"Stopped early to stay fast; add a word to narrow it.",skipped:"{count} task(s) not searched: another machine's, or an engine Rove cannot read."},taskops:{publishTitle:"publish to the remote",publishBody:"{branch} lives on {host}. Push it from there and open a pull request?",publishButton:"push & open PR",publishedPr:"pushed {branch} — PR {url}",publishedNoPr:"pushed {branch}, but no PR: {reason}",set:"set",retire:"delete",close:"close",moveHint:"j/k move · enter or esc done",fixSent:"asked the agent to fix the failing checks",forkNeedsMessage:"a message is required to fork a task",forkLeftBehind:"Started from the last commit — uncommitted work stays in the original task ({reason})",forkBranch:"fork from branch",openedExternal:"opened in the Mac's app"}},jt={dialog:{cancel:"取消",save:"保存",close:"关闭"},whatsNew:{continue:"继续",openRelease:"打开发布说明",keys:"q / esc"},menu:{closeTab:"关闭该标签页",newChat:"新建会话",newChatPick:"选择引擎新建会话…",retryEdit:"改提示词后重试…",retryNoPrompt:"这个任务没有保存的提示词",startNow:"立即启动",cancelStart:"取消定时启动",continueEngine:"换个引擎继续…",newShell:"新建终端",newChatNoFolder:"新建会话(无文件夹)",renameTab:"重命名标签页",splitRight:"向右分屏",splitDown:"向下分屏",renamePane:"重命名分屏",closePane:"关闭分屏",newTask:"新建任务",rename:"重命名",pin:"置顶",unpin:"取消置顶",setStatus:"设置状态",copy:"复制",copyWhat:{id:"任务 ID",branch:"分支名",path:"worktree 路径"},renameBranch:"重命名分支",changeEngine:"切换引擎",land:"合入基础分支",delete:"删除",removeSession:"移除会话",copied:"已复制 {text}",copyFailed:"浏览器拒绝了剪贴板访问",nothingToCopy:"这个任务还没有{what}",what:{id:"ID",branch:"分支",path:"worktree 路径"}},confirm:{closeTabTitle:"关闭「{tab}」?",closeTabBody:"停止这个标签里的会话。任务和它的 worktree 会保留。",closePaneTitle:"关闭「{pane}」?",closePaneBody:"这个分屏里的引擎会话会结束。标签里的其他分屏会保留。",delete:"删除",forceDelete:"强制删除"},newTask:{title:"新建任务",repo:"项目",engine:"引擎",notReady:"未安装或未登录",taskTitle:"标题",titlePlaceholder:"可选;不填就从第一条消息生成",prompt:"第一条消息",promptPlaceholder:"可选;⌘↵ 创建",create:"创建",needRepo:"先选一个项目"},newChat:{title:"新建会话",prompt:"第一条消息",start:"开始",promptPlaceholder:"可选;留空则直接打开引擎,不发消息"},newSession:{title:"新会话",tagline:"选好项目和模型,再描述任务。会话从你的第一条消息开始。",placeholder:"描述一个任务,或提个问题",start:"开始会话",createOnly:"仅创建,不启动",later:"稍后…",laterConfirm:"稍后启动",laterPlaceholder:"22:30 · 明天 9:00 · +30m",laterBad:"这不是一个未来的时间。试试 22:30、明天 9:00 或 +2h。",laterSet:"{time} 启动",retryArchiveTitle:"归档「{title}」?",retryArchiveBody:"重试已经开始。要把原来的会话归档吗?不会停止或删除任何东西。",needRepo:"先选一个项目",needPrompt:"先描述一下任务",repo:"项目",repoSearch:"搜索项目,或输入路径",repoEmpty:"没有匹配的项目",cloneRow:"克隆 {source}",cloning:"正在克隆 {source}…",githubGroup:"你的 GitHub 仓库",branch:"分支",branchSearch:"搜索分支",branches:"分支",branchName:"分支名,所有仓库共用",branchNamePlaceholder:"不填就从提示词生成",primary:"主仓库",repoCount:"{count} 个仓库,第一个是主仓库",branchEmpty:"没有匹配的分支",model:"模型",modelHeading:"本次会话的模型",engine:"引擎",effort:"强度",modelDefault:"默认",effortDefault:"默认",moreWays:"克隆,或接管 worktree…",attach:"附件",attachHint:"给第一条消息加图片或 PDF;也可以直接粘贴或拖到输入框里",removeAttachment:"移除 {name}",worktree:"Worktree",worktreeOn:"在独立的 worktree 和分支里运行。点击改为直接在项目目录里运行",worktreeOff:"直接在项目目录里运行,用它当前的分支。点击改为独立 worktree",worktreeMulti:"跨多个仓库的任务每个仓库都会有 worktree",machine:"机器",thisMachine:"本机",cloneRowOn:"在 {machine} 上克隆 {source}",cloningOn:"正在 {machine} 上克隆 {source}…",startingOn:"正在 {machine} 上启动任务…",repoSearchRemote:"搜索,或输入 owner/repo",cloneTag:"克隆",remoteOffline:"{machine} 已离线",remoteOutdated:"{machine} 上的 Rove 太旧,列不出它的项目",remoteFailed:"读不到 {machine} 上的项目:{message}",remoteNone:"{machine} 上还没有项目。输入 owner/repo 就能在那里克隆",remoteLoading:"正在读取 {machine} 上的项目…",branchTypeRef:"输入要基于的分支或 ref",branchRemoteDefault:"它当前的分支"},search:{tasks:"任务",commands:"命令",inConversations:"在对话里",searching:"搜索中…",none:"没有说过「{query}」。",you:"你",agent:"agent",truncated:"为了快提前停了;多加一个词缩小范围。",skipped:"有 {count} 个任务没搜:在别的机器上,或 Rove 读不了它的引擎记录。"},taskops:{publishTitle:"推送到远端",publishBody:"{branch} 在 {host} 上。要从那台机器推送它并开一个 PR 吗?",publishButton:"推送并开 PR",publishedPr:"已推送 {branch} — PR {url}",publishedNoPr:"已推送 {branch},但没开成 PR:{reason}",set:"设置",retire:"删除",close:"关闭",moveHint:"j/k 移动 · enter 或 esc 完成",fixSent:"已请 agent 修复失败的检查",forkNeedsMessage:"fork 任务需要填写消息",forkLeftBehind:"已从最近一次提交开始——未提交的改动留在原任务里({reason})",forkBranch:"基于分支",openedExternal:"已在 Mac 上用系统应用打开"}};var fe={};f(fe,{zh:()=>Jt,en:()=>Kt});var Kt={machines:{title:"Machines",open:"[ machines ]",none:"No other machines registered. Add one with `rove machine add`.",newHere:"[ new task here ]",tasks:"[ tasks ]",repos:"[ repos ]",hide:"[ hide ]",noTasks:"no tasks",noRepos:"no repos",loading:"…",reposFailed:"couldn't read its repos: {message}",reason:{refused:"ssh was refused (nothing is listening or a firewall is in the way)",timeout:"ssh timed out","host-key-changed":"its ssh host key changed; check it, then update known_hosts","host-key-unverified":"its ssh host key is not known yet; connect once by hand to trust it","auth-denied":"ssh refused the login (no key or password accepted)",unresolved:"its hostname does not resolve","ssh-failed":"ssh failed","no-rove":"rove is not installed there","no-daemon":"its rove daemon is not running","bad-status":"it answered with something rove could not read",handshake:"the link came up but the handshake failed","no-forwarding":"ssh forwarding is disabled there",lost:"the link dropped; reconnecting"}}},Jt={machines:{title:"机器",open:"[ 机器 ]",none:"没有注册其他机器。用 `rove machine add` 添加。",newHere:"[ 在这台新建任务 ]",tasks:"[ 任务 ]",repos:"[ 仓库 ]",hide:"[ 收起 ]",noTasks:"没有任务",noRepos:"没有仓库",loading:"…",reposFailed:"读不到它的仓库:{message}",reason:{refused:"ssh 被拒绝(没有进程在监听,或被防火墙挡住)",timeout:"ssh 超时","host-key-changed":"它的 ssh 主机密钥变了;确认无误后更新 known_hosts","host-key-unverified":"还不认识它的 ssh 主机密钥;先手动连一次以信任它","auth-denied":"ssh 拒绝了登录(没有被接受的密钥或密码)",unresolved:"它的主机名解析不了","ssh-failed":"ssh 失败","no-rove":"那里没有安装 rove","no-daemon":"它的 rove 守护进程没在运行","bad-status":"它返回了 rove 读不懂的内容",handshake:"链路通了但握手失败","no-forwarding":"那里禁用了 ssh 端口转发",lost:"链路断了;正在重连"}}};var Re={};f(Re,{zh:()=>_t,en:()=>Vt});var Vt={files:{autoFix:{on:"[ auto-fix CI ]",off:"[ turn off ]",count:"auto-fix {n}/{max}",stopped:"auto-fix stopped {n}/{max}",hint:"When checks fail on a new push and the agent is idle, send it the failing logs. At most once per push, 3 times in all, then it stops and tells your Inbox."},stripCreate:"[ create PR ]",stripFixCi:"[ fix CI ]",stripConflicts:"[ resolve conflicts ]",stripComments:"[ address comments ]",stripArchive:"[ archive ]",stripFilled:"prompt is in the input; edit it and press Enter",stripNoEngine:"No engine session open in this task — nowhere to put the prompt.",stripUnavailable:"couldn't read the PR's checks: {detail}",stripNothing:"nothing to ask the agent about right now",run:{run:"[ run ]",stop:"[ stop ]",open:"[ open :{port} ]"},checks:{title:"checks",summary:"{pass}/{total} passed",failed:"{count} failed",running:"{count} running",none:"no checks",refresh:"[ refresh ]",loading:"…",unavailable:"couldn't read the PR: {detail}",log:"[ log ]",hideLog:"[ hide ]",noLog:"no log excerpt for this job",logUnavailable:"couldn't read the log: {detail}",more:"{count} more not shown"},merge:{draft:"draft",conflicts:"conflicts",behind:"behind base",unstable:"checks not passing",blocked:"blocked",clean:"mergeable",approved:"approved",changes:"changes requested",reviewRequired:"review required"},stripCommit:"[ commit ]",prRequested:"asked the agent to create the PR",remoteNoExternal:"this file is on {host}; open it there",mentionNoEngine:"No engine session open in this task — nothing to mention into.",landTodos:"{count} todo(s) on this task are still unchecked.",emptyAll:"(empty worktree)"},preview:{loading:"loading…",whole:"whole worktree",unknown:"nothing to show",binary:"binary file — not shown as text",empty:"no changes",noHunks:"no text changes ({kind})",noteTitle:"note on {location}",noteSubmit:"add note",askInChat:"ask in chat",askInChatHint:"Put the selected lines in the engine's prompt",sent:"sent {count} notes to the engine",notSent:"notes not sent: {reason}"},review:{comment:"comment",range:"range",count:"{count} comments",countOne:"1 comment",send:"send {count}",sending:"sending…",edit:"edit",drop:"drop",moved:"moved",movedHint:"The code under this comment changed since it was written.",show:"Show in the diff",editTitle:"edit note on {location}",editSubmit:"save note",fold:"Fold {path}",unfold:"Unfold {path}",foldAll:"fold all",unfoldAll:"unfold all",ignoreWs:"[ ws ]",ignoreWsHint:"Hide whitespace-only changes (git diff -w)",viewed:"[ viewed ]",markViewed:"Mark {path} viewed",viewedOn:"[x] viewed",unmarkViewed:"Unmark {path} viewed",totals:"{files} files · +{added} −{removed}",viewedCount:"{done}/{files} viewed",nextFile:"[ next file ]",nextComment:"[ next comment ]",scopeMenu:"{base} → {scope} ▾",compareAgainst:"Compare against {base}…",baseDefault:"task base",baseBranch:"branch",baseRemote:"remote",baseTag:"tag",basePlaceholder:"branch, tag or commit",shape:{effort:"effort {n}/5",lockfile:"{count} lockfile",lockfiles:"{count} lockfiles",generated:"{count} generated",tests:"{count} tests",config:"{count} config",docs:"{count} docs",source:"{count} source"},scopeWorking:"Uncommitted changes",scopeBranch:"Committed changes",scopeTask:"All changes",lineClipped:"… {count} more characters",notSupported:"This bridge is older than the page and can't do that; restart it."},panel:{tabs:"Side panel views",files:"Files",chat:"Chat",diff:"Diff",browser:"Browser",quota:"Context",resize:"Drag to resize the panel, double-click to reset",empty:"Nothing open. Use + to open Files, a diff or a Browser.",new:"New tab",newChat:"Chat",newFiles:"New Files",newDiff:"Diff of whole worktree",newBrowser:"New Browser",newQuota:"Context window",close:"Close",hide:"Close the side panel",expand:"Expand the side panel",restore:"Restore the side panel size",closeOthers:"Close others",splitDown:"Split down",moveBottom:"Move to bottom",moveTop:"Move to top",resizeSplit:"Drag to resize, double-click to split evenly"},browser:{address:"Address",placeholder:"https://… or localhost:3000",back:"Back",forward:"Forward",reload:"Reload",openTab:"Open in a new tab",invalid:"That is not a web address; only http and https pages open here.",empty:"Enter a web address, or a local dev server.",noFrame:"Only https pages open here; this address uses plain http. Use ↗ to open it in a new tab.",self:"That address is Rove itself, which can't be framed. Use ↗ to open it in a new tab.",refuses:"{host} doesn't allow being shown inside another page.",unreachable:"{host} can't be reached from here.",remote:"{host} runs on the Rove machine, not on this one."}},_t={files:{autoFix:{on:"[ 自动修复 CI ]",off:"[ 关闭 ]",count:"自动修复 {n}/{max}",stopped:"自动修复已停止 {n}/{max}",hint:"检查在新提交上失败且代理空闲时,自动把失败日志发给它。每次提交最多一次、总共 3 次,之后停止并通知收件箱。"},stripCreate:"[ 创建 PR ]",stripFixCi:"[ 修复 CI ]",stripConflicts:"[ 解决冲突 ]",stripComments:"[ 处理评论 ]",stripArchive:"[ 归档 ]",stripFilled:"提示词已放进输入框,修改后按 Enter 发送",stripNoEngine:"这个任务没有打开的引擎会话,提示词无处可放。",stripUnavailable:"读不到 PR 的检查结果:{detail}",stripNothing:"现在没有需要让代理处理的事",run:{run:"[ 运行 ]",stop:"[ 停止 ]",open:"[ 打开 :{port} ]"},checks:{title:"检查",summary:"{pass}/{total} 通过",failed:"{count} 个失败",running:"{count} 个进行中",none:"没有检查",refresh:"[ 刷新 ]",loading:"…",unavailable:"读不到这个 PR:{detail}",log:"[ 日志 ]",hideLog:"[ 收起 ]",noLog:"这个作业没有日志摘录",logUnavailable:"读不到日志:{detail}",more:"还有 {count} 个未显示"},merge:{draft:"草稿",conflicts:"有冲突",behind:"落后于基线",unstable:"检查未通过",blocked:"被阻塞",clean:"可合并",approved:"已批准",changes:"要求修改",reviewRequired:"需要评审"},stripCommit:"[ 提交 ]",prRequested:"已让代理去创建 PR",remoteNoExternal:"这个文件在 {host} 上,请到那台机器上打开",mentionNoEngine:"这个任务没有打开的引擎会话——没有地方可以提及。",landTodos:"此任务还有 {count} 条待办未完成。",emptyAll:"(空工作区)"},preview:{loading:"加载中…",whole:"整个工作区",unknown:"没有可显示的内容",binary:"二进制文件——不以文本显示",empty:"没有变更",noHunks:"没有文本变更({kind})",noteTitle:"批注 {location}",noteSubmit:"添加批注",askInChat:"在对话里问",askInChatHint:"把选中的几行放进引擎的输入框",sent:"已把 {count} 条批注发给引擎",notSent:"批注没有发出:{reason}"},review:{comment:"批注",range:"选范围",count:"{count} 条批注",countOne:"1 条批注",send:"发送 {count} 条",sending:"发送中…",edit:"编辑",drop:"删除",moved:"已移位",movedHint:"这条批注下面的代码在写下之后变了。",show:"在差异里定位",editTitle:"编辑批注 {location}",editSubmit:"保存批注",fold:"折叠 {path}",unfold:"展开 {path}",foldAll:"全部折叠",unfoldAll:"全部展开",ignoreWs:"[ ws ]",ignoreWsHint:"隐藏仅空白的改动 (git diff -w)",viewed:"[ 已看 ]",markViewed:"标记 {path} 为已看",viewedOn:"[x] 已看",unmarkViewed:"取消 {path} 的已看",totals:"{files} 个文件 · +{added} −{removed}",viewedCount:"已看 {done}/{files}",nextFile:"[ 下个文件 ]",nextComment:"[ 下条评论 ]",scopeMenu:"{base} → {scope} ▾",compareAgainst:"对比基准:{base}…",baseDefault:"任务基准",baseBranch:"分支",baseRemote:"远程",baseTag:"标签",basePlaceholder:"分支、标签或提交",shape:{effort:"评审量 {n}/5",lockfile:"{count} 个锁文件",lockfiles:"{count} 个锁文件",generated:"{count} 个生成文件",tests:"{count} 个测试",config:"{count} 个配置",docs:"{count} 个文档",source:"{count} 个源码"},scopeWorking:"未提交的改动",scopeBranch:"已提交的改动",scopeTask:"全部改动",lineClipped:"…还有 {count} 个字符",notSupported:"这个 bridge 比页面旧,做不了这个;请重启它。"},panel:{tabs:"侧边栏视图",files:"文件",chat:"对话",diff:"差异",browser:"浏览器",quota:"上下文",resize:"拖动调整侧边栏宽度,双击复位",empty:"没有打开的内容。点 + 打开文件、差异或浏览器。",new:"新建标签页",newChat:"对话",newFiles:"新建文件",newDiff:"整个工作区的差异",newBrowser:"新建浏览器",newQuota:"上下文窗口",close:"关闭",hide:"关闭侧边栏",expand:"展开侧边栏",restore:"还原侧边栏大小",closeOthers:"关闭其他",splitDown:"向下分屏",moveBottom:"移到下方",moveTop:"移到上方",resizeSplit:"拖动调整高度,双击恢复均分"},browser:{address:"地址",placeholder:"https://… 或 localhost:3000",back:"后退",forward:"前进",reload:"刷新",openTab:"在新标签页打开",invalid:"这不是网页地址;这里只能打开 http 和 https 页面。",empty:"输入网页地址,或本地开发服务器的地址。",noFrame:"这里只能打开 https 页面;这个地址用的是 http。点 ↗ 在新标签页打开。",self:"这个地址就是 Rove 本身,不能嵌入。点 ↗ 在新标签页打开。",refuses:"{host} 不允许嵌入其他页面。",unreachable:"从这里访问不到 {host}。",remote:"{host} 运行在 Rove 所在的机器上,不在这台设备上。"}};var Te={};f(Te,{zh:()=>to,en:()=>eo});var eo={quote:{action:"[ quote ]",title:"Put the selection in the prompt as a quote",atMenu:"The agent is waiting on a choice. Answer it first, then quote.",notReady:"The agent's prompt is not taking pasted text yet. Try again in a moment."},snippets:{empty:"No snippets yet.",name:"Name",namePlaceholder:"review",text:"Text",textPlaceholder:"Review this change for bugs and missing tests.",add:"[ add ]",save:"[ save ]",edit:"[ edit ]",cancel:"[ cancel ]",remove:"[ remove ]",removed:"Removed @{name}",badName:"A name is letters, digits, - or _, up to 32."}},to={quote:{action:"[ 引用 ]",title:"把选中的内容作为引用放进提示词",atMenu:"agent 正在等你做选择。先回答它,再引用。",notReady:"agent 的输入框暂时还不接受粘贴。稍等一下再试。"},snippets:{empty:"还没有片段。",name:"名称",namePlaceholder:"review",text:"内容",textPlaceholder:"检查这次改动有没有 bug,有没有漏掉的测试。",add:"[ 添加 ]",save:"[ 保存 ]",edit:"[ 编辑 ]",cancel:"[ 取消 ]",remove:"[ 删除 ]",removed:"已删除 @{name}",badName:"名称只能是字母、数字、- 或 _,最长 32 位。"}};var Pe={};f(Pe,{zh:()=>no,en:()=>oo});var oo={rail:{inbox:"inbox {count}",empty:"no tasks yet",skillCue:"skill missing",newTask:"New task",settings:"Settings",noProject:"Ungrouped",state:{needs:"needs you",working:"working",ready:"ready",idle:"idle"},extraRepos:"{count} more repos in this task",todos:"{done} of {total} todos done",startsAt:"Starts {time}",pr:{state:{open:"open",merged:"merged",closed:"closed"},many:"{count} PRs in this task and its children",checks:{conflict:"conflicts with its base",failing:"checks failing",passing:"checks passing",pending:"checks running"}},searchPlaceholder:"fuzzy filter",engineSwitched:"Engine → {engine} (applies on reopen)",engineSwitchFailed:"could not switch engine: {error}"},inbox:{title:"inbox",hints:"↑↓ move · enter open · d clear · esc close",attention:"attention",recent:"recent",empty:"no pending attention",reply:"[ reply ]",hide:"[ hide ]",send:"[ send ]",openTab:"[ open ]",noOutput:"no output yet",replyPlaceholder:"reply to this session",gateKept:"Saved; the engine isn't running and will get it with its next message.",state:{done:"done",needsInput:"needs input",error:"error",rateLimited:"rate limited",dead:"engine exited",routineFailed:"routine needs you",decision:"needs a decision"}}},no={rail:{inbox:"收件箱 {count}",empty:"还没有任务",skillCue:"缺少技能",newTask:"新建任务",settings:"设置",noProject:"未分组",state:{needs:"等你处理",working:"进行中",ready:"待收尾",idle:"空闲"},extraRepos:"这个任务还含 {count} 个仓库",todos:"{done}/{total} 条待办已完成",startsAt:"{time} 启动",pr:{state:{open:"进行中",merged:"已合并",closed:"已关闭"},many:"这个任务及子任务共 {count} 个 PR",checks:{conflict:"与基础分支冲突",failing:"检查未通过",passing:"检查已通过",pending:"检查进行中"}},searchPlaceholder:"模糊搜索",engineSwitched:"引擎 → {engine}(重新打开后生效)",engineSwitchFailed:"切换引擎失败:{error}"},inbox:{title:"收件箱",hints:"↑↓ 移动 · enter 打开 · d 清除 · esc 关闭",attention:"待处理",recent:"最近使用",empty:"暂无待处理",reply:"[ 回复 ]",hide:"[ 收起 ]",send:"[ 发送 ]",openTab:"[ 打开 ]",noOutput:"还没有输出",replyPlaceholder:"回复这个会话",gateKept:"已记下;引擎没在运行,下次发消息时一并送达。",state:{done:"完成",needsInput:"需要输入",error:"出错",rateLimited:"限流",dead:"引擎已退出",routineFailed:"例行任务需要处理",decision:"等你决定"}}};var be={};f(be,{zh:()=>io,en:()=>so});var so={reviewQueue:{sourceIssues:"[ issues ]",sourceReviews:"[ PRs waiting on me ]",palette:"Review queue",title:"PRs waiting on me",empty:"No open PR asks for your review in your projects.",other:"{count} more in projects Rove doesn't know.",draft:"draft",fork:"fork",checksPassing:"checks passing",checksFailing:"checks failing",checksPending:"checks running",start:"[ review ]",preparing:"Fetching #{number}…",failed:"Could not start the review of #{number}: {error}",gh:{missing:"The `gh` CLI is not installed or not on PATH.",unauthenticated:"`gh` is not signed in: run `gh auth login` on this Mac.",failed:"GitHub could not be read just now. Press r to retry."},prompt:`Review pull request #{number} in {repo}: {title}
|
|
25
|
+
See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.`),e}function De(){l.asyncTransitions--}function Y(e){if($===null)try{var n=("require"+Math.random()).slice(0,7);$=(K&&K[n]).call(K,"timers").setImmediate}catch(r){$=function(a){Ve===!1&&(Ve=!0,typeof MessageChannel>"u"&&console.error("This browser does not have a MessageChannel implementation, so enqueuing tasks via await act(async () => ...) will fail. Please file an issue at https://github.com/facebook/react/issues if you encounter this warning."));var d=new MessageChannel;d.port1.onmessage=a,d.port2.postMessage(void 0)}}return $(e)}function F(e){return 1<e.length&&typeof AggregateError==="function"?AggregateError(e):e[0]}function q(e,n){n!==Q-1&&console.error("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. "),Q=n}function _(e,n,r){var a=l.actQueue;if(a!==null)if(a.length!==0)try{ee(a),Y(function(){return _(e,n,r)});return}catch(d){l.thrownErrors.push(d)}else l.actQueue=null;0<l.thrownErrors.length?(a=F(l.thrownErrors),l.thrownErrors.length=0,r(a)):n(e)}function ee(e){if(!re){re=!0;var n=0;try{for(;n<e.length;n++){var r=e[n];do{l.didUsePromise=!1;var a=r(!1);if(a!==null){if(l.didUsePromise){e[n]=r,e.splice(0,n);return}r=a}else break}while(1)}e.length=0}catch(d){e.splice(0,n+1),l.thrownErrors.push(d)}finally{re=!1}}}typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart==="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var te=Symbol.for("react.transitional.element"),ze=Symbol.for("react.portal"),oe=Symbol.for("react.fragment"),Ue=Symbol.for("react.strict_mode"),Ee=Symbol.for("react.profiler"),ne=Symbol.for("react.consumer"),Fe=Symbol.for("react.context"),Ae=Symbol.for("react.forward_ref"),Le=Symbol.for("react.suspense"),ft=Symbol.for("react.suspense_list"),se=Symbol.for("react.memo"),A=Symbol.for("react.lazy"),Ie=Symbol.for("react.activity"),Ge=Symbol.iterator,Se={},Ye={isMounted:function(){return!1},enqueueForceUpdate:function(e){s(e,"forceUpdate")},enqueueReplaceState:function(e){s(e,"replaceState")},enqueueSetState:function(e){s(e,"setState")}},qe=Object.assign,ie={};Object.freeze(ie),i.prototype.isReactComponent={},i.prototype.setState=function(e,n){if(typeof e!=="object"&&typeof e!=="function"&&e!=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,e,n,"setState")},i.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};var T={isMounted:["isMounted","Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."],replaceState:["replaceState","Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."]};for(L in T)T.hasOwnProperty(L)&&t(L,T[L]);h.prototype=i.prototype,T=g.prototype=new h,T.constructor=g,qe(T,i.prototype),T.isPureReactComponent=!0;var We=Array.isArray,Rt=Symbol.for("react.client.reference"),l={H:null,A:null,T:null,S:null,actQueue:null,asyncTransitions:0,isBatchingLegacy:!1,didScheduleLegacyUpdate:!1,didUsePromise:!1,thrownErrors:[],getCurrentStack:null,recentlyCreatedOwnerStacks:0},W=Object.prototype.hasOwnProperty,$e=console.createTask?console.createTask:function(){return null};T={react_stack_bottom_frame:function(e){return e()}};var Qe,Xe,Ze={},bt=T.react_stack_bottom_frame.bind(T,N)(),Tt=$e(B(N)),Ke=!1,je=/\/+/g,Je=typeof reportError==="function"?reportError:function(e){if(typeof window==="object"&&typeof window.ErrorEvent==="function"){var n=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof e==="object"&&e!==null&&typeof e.message==="string"?String(e.message):String(e),error:e});if(!window.dispatchEvent(n))return}else if(typeof process==="object"&&typeof process.emit==="function"){process.emit("uncaughtException",e);return}console.error(e)},Ve=!1,$=null,Q=0,X=!1,re=!1,_e=typeof queueMicrotask==="function"?function(e){queueMicrotask(function(){return queueMicrotask(e)})}:Y;T=Object.freeze({__proto__:null,c:function(e){return k().useMemoCache(e)}});var L={map:S,forEach:function(e,n,r){S(e,function(){n.apply(this,arguments)},r)},count:function(e){var n=0;return S(e,function(){n++}),n},toArray:function(e){return S(e,function(n){return n})||[]},only:function(e){if(!y(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};St.Activity=Ie,St.Children=L,St.Component=i,St.Fragment=oe,St.Profiler=Ee,St.PureComponent=g,St.StrictMode=Ue,St.Suspense=Le,St.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=l,St.__COMPILER_RUNTIME=T,St.act=function(e){var n=l.actQueue,r=Q;Q++;var a=l.actQueue=n!==null?n:[],d=!1;try{var c=e()}catch(m){l.thrownErrors.push(m)}if(0<l.thrownErrors.length)throw q(n,r),e=F(l.thrownErrors),l.thrownErrors.length=0,e;if(c!==null&&typeof c==="object"&&typeof c.then==="function"){var u=c;return _e(function(){d||X||(X=!0,console.error("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"))}),{then:function(m,C){d=!0,u.then(function(z){if(q(n,r),r===0){try{ee(a),Y(function(){return _(z,m,C)})}catch(xt){l.thrownErrors.push(xt)}if(0<l.thrownErrors.length){var Pt=F(l.thrownErrors);l.thrownErrors.length=0,C(Pt)}}else m(z)},function(z){q(n,r),0<l.thrownErrors.length?(z=F(l.thrownErrors),l.thrownErrors.length=0,C(z)):C(z)})}}}var w=c;if(q(n,r),r===0&&(ee(a),a.length!==0&&_e(function(){d||X||(X=!0,console.error("A component suspended inside an `act` scope, but the `act` call was not awaited. When testing React components that depend on asynchronous data, you must await the result:\n\nawait act(() => ...)"))}),l.actQueue=null),0<l.thrownErrors.length)throw e=F(l.thrownErrors),l.thrownErrors.length=0,e;return{then:function(m,C){d=!0,r===0?(l.actQueue=a,Y(function(){return _(w,m,C)})):m(w)}}},St.cache=function(e){return function(){return e.apply(null,arguments)}},St.cacheSignal=function(){return null},St.captureOwnerStack=function(){var e=l.getCurrentStack;return e===null?null:e()},St.cloneElement=function(e,n,r){if(e===null||e===void 0)throw Error("The argument must be a React element, but you passed "+e+".");var a=qe({},e.props),d=e.key,c=e._owner;if(n!=null){var u;e:{if(W.call(n,"ref")&&(u=Object.getOwnPropertyDescriptor(n,"ref").get)&&u.isReactWarning){u=!1;break e}u=n.ref!==void 0}u&&(c=b()),G(n)&&(x(n.key),d=""+n.key);for(w in n)!W.call(n,w)||w==="key"||w==="__self"||w==="__source"||w==="ref"&&n.ref===void 0||(a[w]=n[w])}var w=arguments.length-2;if(w===1)a.children=r;else if(1<w){u=Array(w);for(var m=0;m<w;m++)u[m]=arguments[m+2];a.children=u}a=J(e.type,d,a,c,e._debugStack,e._debugTask);for(d=2;d<arguments.length;d++)ye(arguments[d]);return a},St.createContext=function(e){return e={$$typeof:Fe,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null},e.Provider=e,e.Consumer={$$typeof:ne,_context:e},e._currentRenderer=null,e._currentRenderer2=null,e},St.createElement=function(e,n,r){for(var a=2;a<arguments.length;a++)ye(arguments[a]);a={};var d=null;if(n!=null)for(m in Xe||!("__self"in n)||"key"in n||(Xe=!0,console.warn("Your app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance: https://react.dev/link/new-jsx-transform")),G(n)&&(x(n.key),d=""+n.key),n)W.call(n,m)&&m!=="key"&&m!=="__self"&&m!=="__source"&&(a[m]=n[m]);var c=arguments.length-2;if(c===1)a.children=r;else if(1<c){for(var u=Array(c),w=0;w<c;w++)u[w]=arguments[w+2];Object.freeze&&Object.freeze(u),a.children=u}if(e&&e.defaultProps)for(m in c=e.defaultProps,c)a[m]===void 0&&(a[m]=c[m]);d&&ct(a,typeof e==="function"?e.displayName||e.name||"Unknown":e);var m=1e4>l.recentlyCreatedOwnerStacks++;return J(e,d,a,b(),m?Error("react-stack-top-frame"):bt,m?$e(B(e)):Tt)},St.createRef=function(){var e={current:null};return Object.seal(e),e},St.forwardRef=function(e){e!=null&&e.$$typeof===se?console.error("forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."):typeof e!=="function"?console.error("forwardRef requires a render function but was given %s.",e===null?"null":typeof e):e.length!==0&&e.length!==2&&console.error("forwardRef render functions accept exactly two parameters: props and ref. %s",e.length===1?"Did you forget to use the ref parameter?":"Any additional parameter will be undefined."),e!=null&&e.defaultProps!=null&&console.error("forwardRef render functions do not support defaultProps. Did you accidentally pass a React component?");var n={$$typeof:Ae,render:e},r;return Object.defineProperty(n,"displayName",{enumerable:!1,configurable:!0,get:function(){return r},set:function(a){r=a,e.name||e.displayName||(Object.defineProperty(e,"name",{value:a}),e.displayName=a)}}),n},St.isValidElement=y,St.lazy=function(e){e={_status:-1,_result:e};var n={$$typeof:A,_payload:e,_init:vt},r={name:"lazy",start:-1,end:-1,value:null,owner:null,debugStack:Error("react-stack-top-frame"),debugTask:console.createTask?console.createTask("lazy()"):null};return e._ioInfo=r,n._debugInfo=[{awaited:r}],n},St.memo=function(e,n){e==null&&console.error("memo: The first argument must be a component. Instead received: %s",e===null?"null":typeof e),n={$$typeof:se,type:e,compare:n===void 0?null:n};var r;return Object.defineProperty(n,"displayName",{enumerable:!1,configurable:!0,get:function(){return r},set:function(a){r=a,e.name||e.displayName||(Object.defineProperty(e,"name",{value:a}),e.displayName=a)}}),n},St.startTransition=function(e){var n=l.T,r={};r._updatedFibers=new Set,l.T=r;try{var a=e(),d=l.S;d!==null&&d(r,a),typeof a==="object"&&a!==null&&typeof a.then==="function"&&(l.asyncTransitions++,a.then(De,De),a.then(p,Je))}catch(c){Je(c)}finally{n===null&&r._updatedFibers&&(e=r._updatedFibers.size,r._updatedFibers.clear(),10<e&&console.warn("Detected a large number of updates inside startTransition. If this is due to a subscription please re-write it to use React provided hooks. Otherwise concurrent mode guarantees are off the table.")),n!==null&&r.types!==null&&(n.types!==null&&n.types!==r.types&&console.error("We expected inner Transitions to have transferred the outer types set and that you cannot add to the outer Transition while inside the inner.This is a bug in React."),n.types=r.types),l.T=n}},St.unstable_useCacheRefresh=function(){return k().useCacheRefresh()},St.use=function(e){return k().use(e)},St.useActionState=function(e,n,r){return k().useActionState(e,n,r)},St.useCallback=function(e,n){return k().useCallback(e,n)},St.useContext=function(e){var n=k();return e.$$typeof===ne&&console.error("Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you mean to call useContext(Context) instead?"),n.useContext(e)},St.useDebugValue=function(e,n){return k().useDebugValue(e,n)},St.useDeferredValue=function(e,n){return k().useDeferredValue(e,n)},St.useEffect=function(e,n){return e==null&&console.warn("React Hook useEffect requires an effect callback. Did you forget to pass a callback to the hook?"),k().useEffect(e,n)},St.useEffectEvent=function(e){return k().useEffectEvent(e)},St.useId=function(){return k().useId()},St.useImperativeHandle=function(e,n,r){return k().useImperativeHandle(e,n,r)},St.useInsertionEffect=function(e,n){return e==null&&console.warn("React Hook useInsertionEffect requires an effect callback. Did you forget to pass a callback to the hook?"),k().useInsertionEffect(e,n)},St.useLayoutEffect=function(e,n){return e==null&&console.warn("React Hook useLayoutEffect requires an effect callback. Did you forget to pass a callback to the hook?"),k().useLayoutEffect(e,n)},St.useMemo=function(e,n){return k().useMemo(e,n)},St.useOptimistic=function(e,n){return k().useOptimistic(e,n)},St.useReducer=function(e,n,r){return k().useReducer(e,n,r)},St.useRef=function(e){return k().useRef(e)},St.useState=function(e){return k().useState(e)},St.useSyncExternalStore=function(e,n,r){return k().useSyncExternalStore(e,n,r)},St.useTransition=function(){return k().useTransition()},St.version="19.2.8",typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"&&typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop==="function"&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())})()});var R=(globalThis.crypto??globalThis.crypto).subtle,I=new TextEncoder;function M(t){let o="";for(let s=0;s<t.length;s+=32768)o+=String.fromCharCode(...t.subarray(s,s+32768));return btoa(o).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function O(t){let o=t.replace(/-/g,"+").replace(/_/g,"/"),s=atob(o+"=".repeat((4-o.length%4)%4)),i=new Uint8Array(s.length);for(let h=0;h<s.length;h++)i[h]=s.charCodeAt(h);return i}function he(...t){let o=t.reduce((h,g)=>h+g.length,0),s=new Uint8Array(o),i=0;for(let h of t)s.set(h,i),i+=h.length;return s}function de(t,o){if(t.length!==o.length)return!1;let s=0;for(let i=0;i<t.length;i++)s|=t[i]^o[i];return s===0}function P(t){return t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)}async function Ft(t){return new Uint8Array(await R.digest("SHA-256",P(t)))}async function et(t){let o=await R.importKey("raw",P(I.encode(t)),"HKDF",!1,["deriveBits"]),s=await R.deriveBits({name:"HKDF",hash:"SHA-256",salt:I.encode("relay-e2e/v1/routing"),info:new Uint8Array(0)},o,128);return M(new Uint8Array(s))}async function ge(){let t=await R.generateKey({name:"ECDH",namedCurve:"P-256"},!1,["deriveBits"]);return{privateKey:t.privateKey,publicKeyRaw:new Uint8Array(await R.exportKey("raw",t.publicKey))}}async function Z(t,o,s,i){let h=await R.importKey("raw",P(t),"HKDF",!1,["deriveBits"]);return new Uint8Array(await R.deriveBits({name:"HKDF",hash:"SHA-256",salt:P(o),info:P(he(I.encode(s),i))},h,256))}async function ue(t){let o=await R.importKey("raw",P(t.peerPublicKeyRaw),{name:"ECDH",namedCurve:"P-256"},!1,[]),s=new Uint8Array(await R.deriveBits({name:"ECDH",public:o},t.privateKey,256)),i=await Ft(he(I.encode("relay-e2e/v1"),t.initiatorPublicKeyRaw,t.responderPublicKeyRaw,t.initiatorNonce,t.responderNonce)),h=I.encode(t.code),[g,p,v,x]=await Promise.all([Z(s,h,"c2s|",i),Z(s,h,"s2c|",i),Z(s,h,"confirm-r|",i),Z(s,h,"confirm-i|",i)]);return{c2s:await R.importKey("raw",P(g),"AES-GCM",!1,["encrypt","decrypt"]),s2c:await R.importKey("raw",P(p),"AES-GCM",!1,["encrypt","decrypt"]),confirmResponder:v,confirmInitiator:x}}class U{#e=0n;#t=null;#n;constructor(t){this.#n=t}async seal(t){let o=new Uint8Array(12);new DataView(o.buffer).setBigUint64(4,this.#e++);let s=new Uint8Array(await R.encrypt({name:"AES-GCM",iv:P(o)},this.#n,P(t)));return he(o,s)}async open(t){if(t.length<12)throw Error("E2E frame too short");let o=t.subarray(0,12),s=t.subarray(12),i=new DataView(o.buffer,o.byteOffset+4,8).getBigUint64(0);if(this.#t!==null&&i<=this.#t)throw Error(`E2E replay detected (counter ${i} <= ${this.#t})`);return this.#t=i,new Uint8Array(await R.decrypt({name:"AES-GCM",iv:P(o)},this.#n,P(s)))}}class le{#e;#t;#n;#o;#h;#a;#s=null;#d=crypto.getRandomValues(new Uint8Array(16));#r=null;#g=null;#u=null;#i="init";#p=[];#w=Promise.resolve();#l=Promise.resolve();#k=new TextEncoder;#f=new TextDecoder;constructor(t){this.#e=t.role,this.#t=t.code,this.#n=t.send,this.#o=t.onReady,this.#h=t.onFrame,this.#a=t.onError}async start(){if(this.#e!=="initiator")return;this.#s=await ge(),this.#i="await-hi",this.#n(JSON.stringify({t:"hi",epk:M(this.#s.publicKeyRaw),n:M(this.#d)}))}isReady(){return this.#i==="ready"}sendFrame(t){if(this.#i==="failed")return Promise.resolve();if(this.#i!=="ready"||!this.#g)return this.#p.push(t),Promise.resolve();let o=this.#w.then(async()=>{if(this.#i==="failed"||!this.#g)throw Error("E2E session failed");let s=await this.#g.seal(this.#k.encode(JSON.stringify(t)));this.#n(s)});return this.#w=o.catch((s)=>this.#c(s instanceof Error?s.message:String(s))),o}onMessage(t){return this.#l=this.#l.then(()=>this.#b(t)).catch(()=>{}),this.#l}async#b(t){if(this.#i==="failed")return;try{if(typeof t==="string")await this.#v(JSON.parse(t));else{if(!this.#u)return;let o=await this.#u.open(t);this.#h(JSON.parse(this.#f.decode(o)))}}catch(o){this.#c(o instanceof Error?o.message:String(o))}}async#v(t){if(this.#e==="responder"&&t.t==="hi"&&this.#i==="init"){let o=O(String(t.epk)),s=O(String(t.n));this.#s=await ge(),this.#r=await ue({privateKey:this.#s.privateKey,peerPublicKeyRaw:o,code:this.#t,initiatorPublicKeyRaw:o,responderPublicKeyRaw:this.#s.publicKeyRaw,initiatorNonce:s,responderNonce:this.#d}),this.#i="await-ok",this.#n(JSON.stringify({t:"hi",epk:M(this.#s.publicKeyRaw),n:M(this.#d),cr:M(this.#r.confirmResponder)}));return}if(this.#e==="initiator"&&t.t==="hi"&&this.#i==="await-hi"){let o=O(String(t.epk)),s=O(String(t.n));if(this.#r=await ue({privateKey:this.#s.privateKey,peerPublicKeyRaw:o,code:this.#t,initiatorPublicKeyRaw:this.#s.publicKeyRaw,responderPublicKeyRaw:o,initiatorNonce:this.#d,responderNonce:s}),!de(O(String(t.cr)),this.#r.confirmResponder)){this.#n(JSON.stringify({t:"err",message:"auth-failed"})),this.#c("Handshake authentication failed (wrong code or man-in-the-middle)");return}this.#n(JSON.stringify({t:"ok",ci:M(this.#r.confirmInitiator)})),this.#g=new U(this.#r.c2s),this.#u=new U(this.#r.s2c),await this.#m();return}if(this.#e==="responder"&&t.t==="ok"&&this.#i==="await-ok"){if(!de(O(String(t.ci)),this.#r.confirmInitiator)){this.#c("Handshake authentication failed (wrong code or man-in-the-middle)");return}this.#g=new U(this.#r.s2c),this.#u=new U(this.#r.c2s),await this.#m();return}if(t.t==="err")this.#c(`Peer rejected handshake: ${String(t.message||"unknown")}`)}async#m(){this.#i="ready",this.#o();let t=this.#p;this.#p=[];for(let o of t)await this.sendFrame(o)}#c(t){if(this.#i!=="failed")this.#i="failed",this.#a(t)}}function At(t,o){switch(t){case 1000:return"closed normally";case 1001:return"relay went away — server shutdown or redeploy";case 1006:return"socket died without a close frame — radio drop, network switch, or NAT idle timeout; reconnect";case 1011:return"agent disconnected — the desktop side went offline; retry once it is back";case 1015:return"TLS handshake failed — TLS-intercepting middlebox or wrong scheme";case 4404:return"no agent registered for this pairing — is the desktop agent running, and is the code right?"}if(t!==null&&t>=4000&&t<5000)return"relay policy rejection — see PROTOCOL.md §9";if(o)return`relay said: ${o}`;return"unexpected close — see PROTOCOL.md §9"}function Lt(t){if(/timed out/i.test(t))return"relay unreachable or slow — check the server URL and network path";if(/authentication failed|auth-failed/i.test(t))return"handshake rejected — wrong pairing code";if(/Failed to open/i.test(t))return"could not open the WebSocket — bad server URL, blocked network, or TLS issue";if(/went silent/i.test(t))return"no frame for idleTimeoutMs — radio sleep or half-dead socket; reconnect and retry";if(/disconnected during pairing/i.test(t))return"close() or a newer connect() interrupted this attempt";if(/not connected/i.test(t))return"tunnel was already down";return"see PROTOCOL.md §9 and the relay's own logs (wrangler tail)"}class tt{status;headers;#e;constructor(t,o,s){this.status=t,this.headers=o,this.#e=s}text(){return new TextDecoder().decode(this.concat())}json(){return JSON.parse(this.text())}bytes(){return this.concat()}concat(){let t=0;for(let i of this.#e)t+=i.length;let o=new Uint8Array(t),s=0;for(let i of this.#e)o.set(i,s),s+=i.length;return o}}class ot{#e;#t={open:new Set,message:new Set,close:new Set,error:new Set};onopen=null;onmessage=null;onclose=null;onerror=null;readyState=0;constructor(t){this.#e=t}#n(t,o){this.#t[t].forEach((s)=>s(o)),this["on"+t]?.(o)}addEventListener(t,o){this.#t[t].add(o)}removeEventListener(t,o){this.#t[t].delete(o)}send(t){if(this.readyState!==1)throw Error("tunnel ws not open");if(typeof t==="string")this.#e({t:"wmsg",id:this.#o,s:t});else this.#e({t:"wmsg",id:this.#o,b:ce(t)})}close(){if(this.readyState===2)return;this.readyState=2,this.#e({t:"wclose",id:this.#o,code:1000}),this.#n("close")}#o;_attach(t){this.#o=t}_opened(){if(this.readyState===1)return;this.readyState=1,this.#n("open")}_message(t){for(let o of this.#t.message)o(t);this.onmessage?.(t)}_error(t){this.readyState=2,this.#n("error",t)}_closed(){if(this.readyState===2)return;this.readyState=2,this.#n("close")}}function ce(t){let o="";for(let s=0;s<t.length;s+=32768)o+=String.fromCharCode(...t.subarray(s,s+32768));return btoa(o)}function It(t){try{return new URL(t).host}catch{return t}}function Gt(t){if(t<1000)return`${t} ms`;if(t<60000)return`${(t/1000).toFixed(1)} s`;return`${(t/60000).toFixed(1)} min`}class me{#e;#t=null;#n=null;#o="idle";#h=new Map;#a=new Map;#s=[];#d=!1;#r=null;#g=null;#u=0;#i=0;#p=null;#w=null;constructor(t){this.#e=t}get state(){return this.#o}#l(t,o){this.#o=t,this.#e.onStateChange?.(t,o)}#k(t){if(!this.#e.quiet){if(t.type==="connect")console.log(`[RelayClient] connecting to ${It(t.server)}`);else if(t.type==="ready")console.log(`[RelayClient] tunnel ready (${t.ms} ms)`);else if(t.type==="disconnect"){if(!t.byUser){let o=t.lastedMs!=null?` after ${Gt(t.lastedMs)}`:" before ready",s=t.error??`close ${t.code}`;console.warn(`[RelayClient] tunnel closed${o}: ${s} — ${t.hint}`)}}else if(!t.byUser)console.warn(`[RelayClient] tunnel failed: ${t.error} — ${t.hint}`)}try{this.#e.onDiag?.(t)}catch(o){console.warn("[RelayClient] onDiag threw:",o)}}async connect(){if(this.#o==="connecting"||this.#o==="ready")return this.ready();this.#d=!1,this.#l("connecting"),this.#p=Date.now(),this.#w=null,this.#k({type:"connect",server:this.#e.serverUrl});let t=++this.#i;this.#f();let o=this.#e.WebSocketImpl??globalThis.WebSocket,s=await et(this.#e.code),i=new o(`${this.#e.serverUrl.replace(/\/+$/,"").replace(/^http/,"ws")}/c/${s}/__alma_e2e__`);i.binaryType="arraybuffer",this.#t=i,this.#r=setTimeout(()=>{this.#m("Encrypted relay connection timed out");try{i.close()}catch{}},this.#e.connectTimeoutMs??20000);let h=new le({role:"initiator",code:this.#e.code,send:(g)=>i.send(g),onReady:()=>{if(this.#t!==i)return;clearTimeout(this.#r),this.#u=Date.now(),this.#w=Date.now(),this.#b(),this.#l("ready"),this.#k({type:"ready",ms:this.#p?Date.now()-this.#p:0});for(let g of this.#s)g.resolve();this.#s=[]},onFrame:(g)=>this.#P(g),onError:(g)=>this.#m(g)});if(this.#n=h,i.onopen=()=>void h.start(),i.onmessage=(g)=>{if(this.#t!==i)return;let p=g.data;if(typeof p==="string")h.onMessage(p);else if(p instanceof ArrayBuffer)h.onMessage(new Uint8Array(p));else if(ArrayBuffer.isView(p))h.onMessage(new Uint8Array(p.buffer,p.byteOffset,p.byteLength))},i.onclose=(g)=>{if(this.#t!==i)return;clearTimeout(this.#r);let p=`Relay tunnel closed (${g.code}${g.reason?` ${g.reason}`:""})`;this.#T(p,g.code,g.reason||null)},i.onerror=()=>{if(this.#t===i&&this.#o==="connecting")this.#m("Failed to open relay tunnel")},t!==this.#i)this.#m("Client disconnected during pairing");return this.ready()}ready(){if(this.#o==="ready")return Promise.resolve();if(this.#o==="error"||this.#o==="closed")return Promise.reject(Error("E2E tunnel not connected"));return new Promise((t,o)=>this.#s.push({resolve:t,reject:o}))}close(){this.#i++,this.#d=!0;try{this.#t?.close()}catch{}this.#f(),clearTimeout(this.#r),this.#v(),this.#c(Error("Tunnel closed"));for(let t of this.#s)t.reject(Error("Tunnel closed"));this.#s=[],this.#l("closed")}get lastFrameAt(){return this.#u}#f(){let t=this.#t;if(t)t.onmessage=null,t.onclose=null,t.onerror=null,t.onopen=null;this.#t=null,this.#n=null}#b(){this.#v(),this.#g=setInterval(()=>{if(!this.#h.size||Date.now()-this.#u<=(this.#e.idleTimeoutMs??45000))return;this.#m("Tunnel went silent with a request pending");try{this.#t?.close()}catch{}},2000)}#v(){if(this.#g)clearInterval(this.#g);this.#g=null}#m(t){this.#v();let o=this.#t;this.#f();try{o?.close()}catch{}this.#c(Error(t));for(let s of this.#s)s.reject(Error(t));this.#s=[],this.#l("error",t),this.#k({type:"failed",error:t,hint:Lt(t),byUser:this.#d})}#c(t){for(let[,o]of this.#h)o.reject(t);this.#h.clear();for(let[,o]of this.#a)o._closed();this.#a.clear()}#T(t,o=null,s=null){this.#v(),this.#c(Error(t));for(let i of this.#s)i.reject(Error(t));this.#s=[],this.#l(this.#d?"closed":"error",t),this.#k({type:"disconnect",code:o,reason:s,error:t,hint:At(o,s),lastedMs:this.#w?Date.now()-this.#w:null,byUser:this.#d})}#R(t){this.#n?.sendFrame(t)}#P(t){if(this.#u=Date.now(),t.t==="hres"||t.t==="hchunk"||t.t==="hend"||t.t==="herr"){let o=this.#h.get(String(t.id));if(!o)return;if(t.t==="hres")o.status=t.status,o.headers=t.headers,o.onHeaders?.(t.status,t.headers);else if(t.t==="hchunk"){let s=atob(t.b),i=new Uint8Array(s.length);for(let h=0;h<s.length;h++)i[h]=s.charCodeAt(h);o.chunks.push(i),o.onChunk?.(i)}else if(t.t==="hend")this.#h.delete(String(t.id)),o.resolve(new tt(o.status??0,o.headers??{},o.chunks));else this.#h.delete(String(t.id)),o.reject(Error(t.message));return}if(t.t==="wopened"){this.#a.get(String(t.id))?._opened();return}if(t.t==="wmsg"){let o=this.#a.get(String(t.id));if(!o)return;if(typeof t.s==="string")o._message(t.s);else if(typeof t.b==="string"){let s=atob(t.b),i=new Uint8Array(s.length);for(let h=0;h<s.length;h++)i[h]=s.charCodeAt(h);o._message(i)}return}if(t.t==="wclose"){let o=this.#a.get(String(t.id));if(o)this.#a.delete(String(t.id)),o._closed();return}if(t.t==="werr"){let o=this.#a.get(String(t.id));if(o)this.#a.delete(String(t.id)),o._error(t.message)}}async fetch(t,o={}){if(await this.ready(),this.#o==="closed"||this.#o==="error")throw Error("Tunnel closed");let s=o.signal;if(s?.aborted)throw Error("aborted");let i=`h${Math.random().toString(36).slice(2)}`,h=(o.method??"GET").toUpperCase(),g;if(o.body!=null&&h!=="GET"&&h!=="HEAD")g=typeof o.body==="string"?ce(new TextEncoder().encode(o.body)):ce(o.body);let p=new Promise((v,x)=>{let H={resolve:v,reject:x,status:0,headers:{},chunks:[],onChunk:o.onChunk,onHeaders:o.onHeaders};if(s){let B=()=>{if(!this.#h.delete(i))return;this.#R({t:"hcancel",id:i}),H.reject(Error("aborted"))};s.addEventListener("abort",B,{once:!0});let b=(N)=>(G)=>{s.removeEventListener("abort",B),N(G)};H.resolve=b(v),H.reject=b(x)}this.#h.set(i,H)});return this.#R({t:"hreq",id:i,method:h,path:t,headers:o.headers??{},b:g}),p}async openSocket(t){if(await this.ready(),this.#o==="closed"||this.#o==="error")throw Error("Tunnel closed");let o=`w${Math.random().toString(36).slice(2)}`,s=new ot((h)=>this.#R(h));this.#a.set(o,s),s._attach(o);let i=new Promise((h,g)=>{let p=setTimeout(()=>{this.#a.delete(o),s._closed(),g(Error("tunnel ws open timeout"))},15000);s.addEventListener("open",()=>{clearTimeout(p),h()}),s.addEventListener("error",(v)=>{clearTimeout(p),this.#a.delete(o),g(Error(String(v)))})});return this.#R({t:"wopen",id:o,path:t}),await i,s}}var Yt=Dt(nt(),1);function st(t){let o=t,s=new Set;return{get:()=>o,set(i){if(Object.is(i,o))return;o=i;for(let h of s)h()},subscribe(i){return s.add(i),()=>s.delete(i)}}}var pe={};f(pe,{zh:()=>Wt,en:()=>qt});var qt={artifact:{preview:"preview",source:"source",open:"[ open ]",loading:"Loading…",tooLarge:"Too large to preview ({size}); the source view reads it as text.",notPreviewable:"This file cannot be previewed.",gone:"The file is gone.",failed:"Could not read the file: {error}"}},Wt={artifact:{preview:"预览",source:"源码",open:"[ 打开 ]",loading:"加载中…",tooLarge:"文件太大,无法预览({size});可在源码视图中按文本查看。",notPreviewable:"这个文件无法预览。",gone:"文件已不存在。",failed:"无法读取文件:{error}"}};var we={};f(we,{zh:()=>Qt,en:()=>$t});var $t={compare:{title:"Compare attempts",menu:"Compare attempts",severalEngines:"[ compare engines ]",oneEngine:"[ one engine ]",needsPrompt:"Compare runs the prompt, so it needs one",empty:"No attempts left in this round.",noReply:"no reply yet",noChanges:"no changes",status:{working:"working",waiting:"waiting for you",error:"needs a look",done:"done",idle:"idle"},files:"files: {count}",more:"+{count} more",kept:"kept",strip:"[ compare ]",stackLabel:"Stacked tasks",landBottom:"[ land bottom ]",terminal:"[ terminal ]",diff:"[ diff ]",keep:"[ keep this one ]",unkeep:"[ unkeep ]",keptToast:"kept {title}",unkeptToast:"no longer kept: {title}",keepFailed:"could not mark it kept: {error}",archiveTitle:"Archive the other attempts?",archiveBody:"Hides the other {count} from the rail. Their worktrees and branches stay, and nothing running is stopped.",archiveConfirm:"Archive",archiveBusyTitle:"Some attempts are still working",archiveBusyBody:"Archive them anyway? Their worktrees and branches stay, and nothing is stopped.",archiveBusyConfirm:"Archive anyway",archived:"archived {count} attempts",archiveFailed:"could not archive: {error}"}},Qt={compare:{title:"对比方案",menu:"对比方案",severalEngines:"[ 多引擎对比 ]",oneEngine:"[ 单引擎 ]",needsPrompt:"对比要运行提示词,请先写一个",empty:"这一轮没有剩下的方案。",noReply:"还没有回复",noChanges:"没有改动",status:{working:"运行中",waiting:"等你处理",error:"需要看一眼",done:"已完成",idle:"空闲"},files:"文件:{count}",more:"另有 {count} 个",kept:"已保留",strip:"[ 对比 ]",stackLabel:"堆叠任务",landBottom:"[ 合并底部 ]",terminal:"[ 终端 ]",diff:"[ diff ]",keep:"[ 保留这个 ]",unkeep:"[ 取消保留 ]",keptToast:"已保留 {title}",unkeptToast:"已取消保留:{title}",keepFailed:"无法标记保留:{error}",archiveTitle:"归档其他方案?",archiveBody:"把其余 {count} 个从侧栏收起。它们的 worktree 和分支都保留,运行中的也不会被停掉。",archiveConfirm:"归档",archiveBusyTitle:"有方案还在运行",archiveBusyBody:"仍要归档吗?它们的 worktree 和分支都保留,也不会停掉任何东西。",archiveBusyConfirm:"仍然归档",archived:"已归档 {count} 个方案",archiveFailed:"无法归档:{error}"}};var ke={};f(ke,{zh:()=>Zt,en:()=>Xt});var Xt={context:{title:"Context",used:"{percent} used",usedTokens:"{tokens} used",loading:"Reading context…",none:"No context reading for this conversation: its engine records none, or it has not answered yet.",failed:"Couldn't read context: {error}",approximate:"Approximate.",part:{system:"System prompt & tools",messages:"Messages",used:"Used",free:"Free space"}}},Zt={context:{title:"上下文",used:"已用 {percent}",usedTokens:"已用 {tokens}",loading:"正在读取上下文…",none:"这个会话没有上下文读数:它的引擎不记录,或者还没回复过。",failed:"读取上下文失败:{error}",approximate:"近似值。",part:{system:"系统提示与工具",messages:"消息",used:"已用",free:"剩余空间"}}};var ve={};f(ve,{zh:()=>jt,en:()=>Kt});var Kt={dialog:{cancel:"cancel",save:"save",close:"close"},whatsNew:{continue:"Continue",openRelease:"Open release notes",keys:"q / esc"},menu:{closeTab:"Close tab",newChat:"New conversation",newChatPick:"New conversation with…",retryEdit:"Retry with edited prompt…",retryNoPrompt:"This task has no stored prompt",startNow:"Start now",cancelStart:"Cancel scheduled start",continueEngine:"Continue in another engine…",newShell:"New shell",newChatNoFolder:"New conversation (no folder)",renameTab:"Rename tab",splitRight:"Split right",splitDown:"Split down",renamePane:"Rename pane",closePane:"Close pane",newTask:"New task",rename:"Rename",pin:"Pin",unpin:"Unpin",setStatus:"Set status",copy:"Copy",copyWhat:{id:"Task ID",branch:"Branch name",path:"Worktree path"},renameBranch:"Rename branch",changeEngine:"Change engine",land:"Land into base branch",delete:"Delete",removeSession:"Remove session",more:"More",copied:"copied {text}",copyFailed:"the browser refused the clipboard",nothingToCopy:"this task has no {what} yet",what:{id:"id",branch:"branch",path:"worktree path"}},confirm:{closeTabTitle:'Close "{tab}"?',closeTabBody:"Stops the session in this tab. The task and its worktree stay.",closePaneTitle:'Close "{pane}"?',closePaneBody:"The engine session in this pane ends. The tab's other panes stay.",delete:"delete",forceDelete:"force delete"},newTask:{title:"new task",repo:"project",engine:"engine",notReady:"not installed or not signed in",taskTitle:"title",titlePlaceholder:"optional; derived from the prompt otherwise",prompt:"first message",promptPlaceholder:"optional; ⌘↵ creates",create:"create",needRepo:"pick a project first"},newChat:{title:"new conversation",prompt:"first message",start:"start",promptPlaceholder:"optional; empty opens the engine without a message"},newSession:{title:"New session",tagline:"Pick a project and a model, then describe the task. The session starts with your first message.",placeholder:"Describe a task or ask a question",start:"Start session",createOnly:"Create without starting",later:"Later…",laterConfirm:"Start later",laterPlaceholder:"22:30 · tomorrow 9:00 · +30m",laterBad:"That is not a time ahead. Try 22:30, tomorrow 9:00 or +2h.",laterSet:"Starts at {time}",retryArchiveTitle:'Archive "{title}"?',retryArchiveBody:"The retry has started. Archive the original? Nothing is stopped or deleted.",needRepo:"Pick a project first",needPrompt:"Describe the task first",repo:"Project",repoSearch:"Search projects or type a path",repoEmpty:"No matching project",cloneRow:"Clone {source}",cloning:"Cloning {source}…",githubGroup:"Your GitHub repositories",branch:"Branch",branchSearch:"Search branches",branches:"Branches",branchName:"Branch name, shared by every repo",branchNamePlaceholder:"derived from the prompt",primary:"primary",repoCount:"{count} repos: the first is the primary",branchEmpty:"No matching branch",model:"Model",modelHeading:"Model for this session",engine:"Engine",effort:"Effort",modelDefault:"Default",effortDefault:"Default",more:"More",attach:"Attach",attachHint:"Add images or PDFs to the first message; you can also paste or drop them on the prompt",removeAttachment:"Remove {name}",worktree:"Worktree",worktreeOn:"Runs in its own worktree and branch. Click to run in the project's checkout instead",worktreeOff:"Runs in the project's checkout as it is, on its current branch. Click for a worktree",worktreeMulti:"A task across several repos always gets a worktree in each",machine:"Machine",thisMachine:"This machine",cloneRowOn:"Clone {source} on {machine}",cloningOn:"Cloning {source} on {machine}…",startingOn:"Starting the task on {machine}…",repoSearchRemote:"Search or type owner/repo",cloneTag:"clone",remoteOffline:"{machine} is offline",remoteOutdated:"{machine} runs a Rove too old to list its projects",remoteFailed:"Could not read {machine}'s projects: {message}",remoteNone:"{machine} has no projects yet. Type owner/repo to clone one there",remoteLoading:"Reading {machine}'s projects…",branchTypeRef:"Type a branch or ref to start from",branchRemoteDefault:"Its current branch"},search:{tasks:"Tasks",commands:"Commands",inConversations:"In conversations",searching:"searching…",none:"Nothing said matches “{query}”.",you:"you",agent:"agent",truncated:"Stopped early to stay fast; add a word to narrow it.",skipped:"{count} task(s) not searched: another machine's, or an engine Rove cannot read."},taskops:{publishTitle:"publish to the remote",publishBody:"{branch} lives on {host}. Push it from there and open a pull request?",publishButton:"push & open PR",publishedPr:"pushed {branch} — PR {url}",publishedNoPr:"pushed {branch}, but no PR: {reason}",set:"set",retire:"delete",close:"close",moveHint:"j/k move · enter or esc done",fixSent:"asked the agent to fix the failing checks",forkNeedsMessage:"a message is required to fork a task",forkLeftBehind:"Started from the last commit — uncommitted work stays in the original task ({reason})",forkBranch:"fork from branch",openedExternal:"opened in the Mac's app"}},jt={dialog:{cancel:"取消",save:"保存",close:"关闭"},whatsNew:{continue:"继续",openRelease:"打开发布说明",keys:"q / esc"},menu:{closeTab:"关闭该标签页",newChat:"新建会话",newChatPick:"选择引擎新建会话…",retryEdit:"改提示词后重试…",retryNoPrompt:"这个任务没有保存的提示词",startNow:"立即启动",cancelStart:"取消定时启动",continueEngine:"换个引擎继续…",newShell:"新建终端",newChatNoFolder:"新建会话(无文件夹)",renameTab:"重命名标签页",splitRight:"向右分屏",splitDown:"向下分屏",renamePane:"重命名分屏",closePane:"关闭分屏",newTask:"新建任务",rename:"重命名",pin:"置顶",unpin:"取消置顶",setStatus:"设置状态",copy:"复制",copyWhat:{id:"任务 ID",branch:"分支名",path:"worktree 路径"},renameBranch:"重命名分支",changeEngine:"切换引擎",land:"合入基础分支",delete:"删除",removeSession:"移除会话",more:"更多",copied:"已复制 {text}",copyFailed:"浏览器拒绝了剪贴板访问",nothingToCopy:"这个任务还没有{what}",what:{id:"ID",branch:"分支",path:"worktree 路径"}},confirm:{closeTabTitle:"关闭「{tab}」?",closeTabBody:"停止这个标签里的会话。任务和它的 worktree 会保留。",closePaneTitle:"关闭「{pane}」?",closePaneBody:"这个分屏里的引擎会话会结束。标签里的其他分屏会保留。",delete:"删除",forceDelete:"强制删除"},newTask:{title:"新建任务",repo:"项目",engine:"引擎",notReady:"未安装或未登录",taskTitle:"标题",titlePlaceholder:"可选;不填就从第一条消息生成",prompt:"第一条消息",promptPlaceholder:"可选;⌘↵ 创建",create:"创建",needRepo:"先选一个项目"},newChat:{title:"新建会话",prompt:"第一条消息",start:"开始",promptPlaceholder:"可选;留空则直接打开引擎,不发消息"},newSession:{title:"新会话",tagline:"选好项目和模型,再描述任务。会话从你的第一条消息开始。",placeholder:"描述一个任务,或提个问题",start:"开始会话",createOnly:"仅创建,不启动",later:"稍后…",laterConfirm:"稍后启动",laterPlaceholder:"22:30 · 明天 9:00 · +30m",laterBad:"这不是一个未来的时间。试试 22:30、明天 9:00 或 +2h。",laterSet:"{time} 启动",retryArchiveTitle:"归档「{title}」?",retryArchiveBody:"重试已经开始。要把原来的会话归档吗?不会停止或删除任何东西。",needRepo:"先选一个项目",needPrompt:"先描述一下任务",repo:"项目",repoSearch:"搜索项目,或输入路径",repoEmpty:"没有匹配的项目",cloneRow:"克隆 {source}",cloning:"正在克隆 {source}…",githubGroup:"你的 GitHub 仓库",branch:"分支",branchSearch:"搜索分支",branches:"分支",branchName:"分支名,所有仓库共用",branchNamePlaceholder:"不填就从提示词生成",primary:"主仓库",repoCount:"{count} 个仓库,第一个是主仓库",branchEmpty:"没有匹配的分支",model:"模型",modelHeading:"本次会话的模型",engine:"引擎",effort:"强度",modelDefault:"默认",effortDefault:"默认",more:"更多",attach:"附件",attachHint:"给第一条消息加图片或 PDF;也可以直接粘贴或拖到输入框里",removeAttachment:"移除 {name}",worktree:"Worktree",worktreeOn:"在独立的 worktree 和分支里运行。点击改为直接在项目目录里运行",worktreeOff:"直接在项目目录里运行,用它当前的分支。点击改为独立 worktree",worktreeMulti:"跨多个仓库的任务每个仓库都会有 worktree",machine:"机器",thisMachine:"本机",cloneRowOn:"在 {machine} 上克隆 {source}",cloningOn:"正在 {machine} 上克隆 {source}…",startingOn:"正在 {machine} 上启动任务…",repoSearchRemote:"搜索,或输入 owner/repo",cloneTag:"克隆",remoteOffline:"{machine} 已离线",remoteOutdated:"{machine} 上的 Rove 太旧,列不出它的项目",remoteFailed:"读不到 {machine} 上的项目:{message}",remoteNone:"{machine} 上还没有项目。输入 owner/repo 就能在那里克隆",remoteLoading:"正在读取 {machine} 上的项目…",branchTypeRef:"输入要基于的分支或 ref",branchRemoteDefault:"它当前的分支"},search:{tasks:"任务",commands:"命令",inConversations:"在对话里",searching:"搜索中…",none:"没有说过「{query}」。",you:"你",agent:"agent",truncated:"为了快提前停了;多加一个词缩小范围。",skipped:"有 {count} 个任务没搜:在别的机器上,或 Rove 读不了它的引擎记录。"},taskops:{publishTitle:"推送到远端",publishBody:"{branch} 在 {host} 上。要从那台机器推送它并开一个 PR 吗?",publishButton:"推送并开 PR",publishedPr:"已推送 {branch} — PR {url}",publishedNoPr:"已推送 {branch},但没开成 PR:{reason}",set:"设置",retire:"删除",close:"关闭",moveHint:"j/k 移动 · enter 或 esc 完成",fixSent:"已请 agent 修复失败的检查",forkNeedsMessage:"fork 任务需要填写消息",forkLeftBehind:"已从最近一次提交开始——未提交的改动留在原任务里({reason})",forkBranch:"基于分支",openedExternal:"已在 Mac 上用系统应用打开"}};var fe={};f(fe,{zh:()=>Vt,en:()=>Jt});var Jt={machines:{title:"Machines",open:"[ machines ]",none:"No other machines registered. Add one with `rove machine add`.",newHere:"[ new task here ]",tasks:"[ tasks ]",repos:"[ repos ]",hide:"[ hide ]",noTasks:"no tasks",noRepos:"no repos",loading:"…",reposFailed:"couldn't read its repos: {message}",reason:{refused:"ssh was refused (nothing is listening or a firewall is in the way)",timeout:"ssh timed out","host-key-changed":"its ssh host key changed; check it, then update known_hosts","host-key-unverified":"its ssh host key is not known yet; connect once by hand to trust it","auth-denied":"ssh refused the login (no key or password accepted)",unresolved:"its hostname does not resolve","ssh-failed":"ssh failed","no-rove":"rove is not installed there","no-daemon":"its rove daemon is not running","bad-status":"it answered with something rove could not read",handshake:"the link came up but the handshake failed","no-forwarding":"ssh forwarding is disabled there",lost:"the link dropped; reconnecting"}}},Vt={machines:{title:"机器",open:"[ 机器 ]",none:"没有注册其他机器。用 `rove machine add` 添加。",newHere:"[ 在这台新建任务 ]",tasks:"[ 任务 ]",repos:"[ 仓库 ]",hide:"[ 收起 ]",noTasks:"没有任务",noRepos:"没有仓库",loading:"…",reposFailed:"读不到它的仓库:{message}",reason:{refused:"ssh 被拒绝(没有进程在监听,或被防火墙挡住)",timeout:"ssh 超时","host-key-changed":"它的 ssh 主机密钥变了;确认无误后更新 known_hosts","host-key-unverified":"还不认识它的 ssh 主机密钥;先手动连一次以信任它","auth-denied":"ssh 拒绝了登录(没有被接受的密钥或密码)",unresolved:"它的主机名解析不了","ssh-failed":"ssh 失败","no-rove":"那里没有安装 rove","no-daemon":"它的 rove 守护进程没在运行","bad-status":"它返回了 rove 读不懂的内容",handshake:"链路通了但握手失败","no-forwarding":"那里禁用了 ssh 端口转发",lost:"链路断了;正在重连"}}};var Re={};f(Re,{zh:()=>eo,en:()=>_t});var _t={files:{autoFix:{on:"[ auto-fix CI ]",off:"[ turn off ]",count:"auto-fix {n}/{max}",stopped:"auto-fix stopped {n}/{max}",hint:"When checks fail on a new push and the agent is idle, send it the failing logs. At most once per push, 3 times in all, then it stops and tells your Inbox."},stripCreate:"[ create PR ]",stripFixCi:"[ fix CI ]",stripConflicts:"[ resolve conflicts ]",stripComments:"[ address comments ]",stripArchive:"[ archive ]",stripFilled:"prompt is in the input; edit it and press Enter",stripNoEngine:"No engine session open in this task — nowhere to put the prompt.",stripUnavailable:"couldn't read the PR's checks: {detail}",stripNothing:"nothing to ask the agent about right now",run:{run:"[ run ]",stop:"[ stop ]",open:"[ open :{port} ]"},checks:{title:"checks",summary:"{pass}/{total} passed",failed:"{count} failed",running:"{count} running",none:"no checks",refresh:"[ refresh ]",loading:"…",unavailable:"couldn't read the PR: {detail}",log:"[ log ]",hideLog:"[ hide ]",noLog:"no log excerpt for this job",logUnavailable:"couldn't read the log: {detail}",more:"{count} more not shown"},merge:{draft:"draft",conflicts:"conflicts",behind:"behind base",unstable:"checks not passing",blocked:"blocked",clean:"mergeable",approved:"approved",changes:"changes requested",reviewRequired:"review required"},stripCommit:"[ commit ]",prRequested:"asked the agent to create the PR",remoteNoExternal:"this file is on {host}; open it there",mentionNoEngine:"No engine session open in this task — nothing to mention into.",landTodos:"{count} todo(s) on this task are still unchecked.",emptyAll:"(empty worktree)",state:{conflict:"conflict",deleted:"deleted",modified:"modified",untracked:"untracked",added:"added",renamed:"renamed"}},preview:{loading:"loading…",whole:"whole worktree",unknown:"nothing to show",binary:"binary file — not shown as text",empty:"no changes",noHunks:"no text changes ({kind})",modeFile:"[ file ]",modeDiff:"[ diff ]",askInChat:"ask in chat",askInChatHint:"Put the selected lines in the engine's prompt",sent:"sent {count} notes to the engine",notSent:"notes not sent: {reason}"},review:{comment:"comment",range:"range",count:"{count} comments",countOne:"1 comment",send:"send {count}",sending:"sending…",edit:"edit",drop:"drop",moved:"moved",movedHint:"The code under this comment changed since it was written.",show:"Show in the diff",noteOn:"note on {location}",noteEditOn:"edit note on {location}",noteAdd:"[ add note ]",noteSave:"[ save note ]",noteCancel:"[ cancel ]",fold:"Fold {path}",unfold:"Unfold {path}",foldAll:"fold all",unfoldAll:"unfold all",ignoreWs:"[ ws ]",ignoreWsHint:"Hide whitespace-only changes (git diff -w)",viewed:"[ viewed ]",markViewed:"Mark {path} viewed",viewedOn:"[x] viewed",unmarkViewed:"Unmark {path} viewed",totals:"{files} files · +{added} −{removed}",viewedCount:"{done}/{files} viewed",nextFile:"[ next file ]",nextComment:"[ next comment ]",scopeMenu:"{base} → {scope} ▾",compareAgainst:"Compare against {base}…",baseDefault:"task base",baseBranch:"branch",baseRemote:"remote",baseTag:"tag",basePlaceholder:"branch, tag or commit",shape:{effort:"effort {n}/5",lockfile:"{count} lockfile",lockfiles:"{count} lockfiles",generated:"{count} generated",tests:"{count} tests",config:"{count} config",docs:"{count} docs",source:"{count} source"},scopeWorking:"Uncommitted changes",scopeBranch:"Committed changes",scopeTask:"All changes",lineClipped:"… {count} more characters",notSupported:"This bridge is older than the page and can't do that; restart it."},panel:{tabs:"Browser tabs",files:"Files",chat:"Chat",diff:"Diff",browser:"Browser",quota:"Context",resize:"Drag to resize the panel, double-click to reset",new:"New tab",close:"Close",expand:"Expand the side panel",restore:"Restore the side panel size",closeOthers:"Close others",resizeSplit:"Drag to resize, double-click to split evenly"},browser:{address:"Address",placeholder:"https://… or localhost:3000",back:"Back",forward:"Forward",reload:"Reload",openTab:"Open in a new tab",invalid:"That is not a web address; only http and https pages open here.",empty:"Enter a web address, or a local dev server.",noFrame:"Only https pages open here; this address uses plain http. Use ↗ to open it in a new tab.",self:"That address is Rove itself, which can't be framed. Use ↗ to open it in a new tab.",refuses:"{host} doesn't allow being shown inside another page.",unreachable:"{host} can't be reached from here.",remote:"{host} runs on the Rove machine, not on this one."}},eo={files:{autoFix:{on:"[ 自动修复 CI ]",off:"[ 关闭 ]",count:"自动修复 {n}/{max}",stopped:"自动修复已停止 {n}/{max}",hint:"检查在新提交上失败且代理空闲时,自动把失败日志发给它。每次提交最多一次、总共 3 次,之后停止并通知收件箱。"},stripCreate:"[ 创建 PR ]",stripFixCi:"[ 修复 CI ]",stripConflicts:"[ 解决冲突 ]",stripComments:"[ 处理评论 ]",stripArchive:"[ 归档 ]",stripFilled:"提示词已放进输入框,修改后按 Enter 发送",stripNoEngine:"这个任务没有打开的引擎会话,提示词无处可放。",stripUnavailable:"读不到 PR 的检查结果:{detail}",stripNothing:"现在没有需要让代理处理的事",run:{run:"[ 运行 ]",stop:"[ 停止 ]",open:"[ 打开 :{port} ]"},checks:{title:"检查",summary:"{pass}/{total} 通过",failed:"{count} 个失败",running:"{count} 个进行中",none:"没有检查",refresh:"[ 刷新 ]",loading:"…",unavailable:"读不到这个 PR:{detail}",log:"[ 日志 ]",hideLog:"[ 收起 ]",noLog:"这个作业没有日志摘录",logUnavailable:"读不到日志:{detail}",more:"还有 {count} 个未显示"},merge:{draft:"草稿",conflicts:"有冲突",behind:"落后于基线",unstable:"检查未通过",blocked:"被阻塞",clean:"可合并",approved:"已批准",changes:"要求修改",reviewRequired:"需要评审"},stripCommit:"[ 提交 ]",prRequested:"已让代理去创建 PR",remoteNoExternal:"这个文件在 {host} 上,请到那台机器上打开",mentionNoEngine:"这个任务没有打开的引擎会话——没有地方可以提及。",landTodos:"此任务还有 {count} 条待办未完成。",emptyAll:"(空工作区)",state:{conflict:"有冲突",deleted:"已删除",modified:"已修改",untracked:"未跟踪",added:"已新增",renamed:"已重命名"}},preview:{loading:"加载中…",whole:"整个工作区",unknown:"没有可显示的内容",binary:"二进制文件——不以文本显示",empty:"没有变更",noHunks:"没有文本变更({kind})",modeFile:"[ 文件 ]",modeDiff:"[ 差异 ]",askInChat:"在对话里问",askInChatHint:"把选中的几行放进引擎的输入框",sent:"已把 {count} 条批注发给引擎",notSent:"批注没有发出:{reason}"},review:{comment:"批注",range:"选范围",count:"{count} 条批注",countOne:"1 条批注",send:"发送 {count} 条",sending:"发送中…",edit:"编辑",drop:"删除",moved:"已移位",movedHint:"这条批注下面的代码在写下之后变了。",show:"在差异里定位",noteOn:"批注 {location}",noteEditOn:"编辑批注 {location}",noteAdd:"[ 添加批注 ]",noteSave:"[ 保存批注 ]",noteCancel:"[ 取消 ]",fold:"折叠 {path}",unfold:"展开 {path}",foldAll:"全部折叠",unfoldAll:"全部展开",ignoreWs:"[ ws ]",ignoreWsHint:"隐藏仅空白的改动 (git diff -w)",viewed:"[ 已看 ]",markViewed:"标记 {path} 为已看",viewedOn:"[x] 已看",unmarkViewed:"取消 {path} 的已看",totals:"{files} 个文件 · +{added} −{removed}",viewedCount:"已看 {done}/{files}",nextFile:"[ 下个文件 ]",nextComment:"[ 下条评论 ]",scopeMenu:"{base} → {scope} ▾",compareAgainst:"对比基准:{base}…",baseDefault:"任务基准",baseBranch:"分支",baseRemote:"远程",baseTag:"标签",basePlaceholder:"分支、标签或提交",shape:{effort:"评审量 {n}/5",lockfile:"{count} 个锁文件",lockfiles:"{count} 个锁文件",generated:"{count} 个生成文件",tests:"{count} 个测试",config:"{count} 个配置",docs:"{count} 个文档",source:"{count} 个源码"},scopeWorking:"未提交的改动",scopeBranch:"已提交的改动",scopeTask:"全部改动",lineClipped:"…还有 {count} 个字符",notSupported:"这个 bridge 比页面旧,做不了这个;请重启它。"},panel:{tabs:"浏览器标签页",files:"文件",chat:"对话",diff:"差异",browser:"浏览器",quota:"上下文",resize:"拖动调整侧边栏宽度,双击复位",new:"新建标签页",close:"关闭",expand:"展开侧边栏",restore:"还原侧边栏大小",closeOthers:"关闭其他",resizeSplit:"拖动调整宽度,双击恢复均分"},browser:{address:"地址",placeholder:"https://… 或 localhost:3000",back:"后退",forward:"前进",reload:"刷新",openTab:"在新标签页打开",invalid:"这不是网页地址;这里只能打开 http 和 https 页面。",empty:"输入网页地址,或本地开发服务器的地址。",noFrame:"这里只能打开 https 页面;这个地址用的是 http。点 ↗ 在新标签页打开。",self:"这个地址就是 Rove 本身,不能嵌入。点 ↗ 在新标签页打开。",refuses:"{host} 不允许嵌入其他页面。",unreachable:"从这里访问不到 {host}。",remote:"{host} 运行在 Rove 所在的机器上,不在这台设备上。"}};var be={};f(be,{zh:()=>oo,en:()=>to});var to={quote:{action:"[ quote ]",title:"Put the selection in the prompt as a quote",atMenu:"The agent is waiting on a choice. Answer it first, then quote.",notReady:"The agent's prompt is not taking pasted text yet. Try again in a moment."},snippets:{empty:"No snippets yet.",name:"Name",namePlaceholder:"review",text:"Text",textPlaceholder:"Review this change for bugs and missing tests.",add:"[ add ]",save:"[ save ]",edit:"[ edit ]",cancel:"[ cancel ]",remove:"[ remove ]",removed:"Removed @{name}",badName:"A name is letters, digits, - or _, up to 32."}},oo={quote:{action:"[ 引用 ]",title:"把选中的内容作为引用放进提示词",atMenu:"agent 正在等你做选择。先回答它,再引用。",notReady:"agent 的输入框暂时还不接受粘贴。稍等一下再试。"},snippets:{empty:"还没有片段。",name:"名称",namePlaceholder:"review",text:"内容",textPlaceholder:"检查这次改动有没有 bug,有没有漏掉的测试。",add:"[ 添加 ]",save:"[ 保存 ]",edit:"[ 编辑 ]",cancel:"[ 取消 ]",remove:"[ 删除 ]",removed:"已删除 @{name}",badName:"名称只能是字母、数字、- 或 _,最长 32 位。"}};var Te={};f(Te,{zh:()=>so,en:()=>no});var no={rail:{inbox:"inbox {count}",empty:"no tasks yet",skillCue:"skill missing",newTask:"New task",settings:"Settings",noProject:"Ungrouped",state:{needs:"needs you",working:"working",ready:"ready",idle:"idle"},extraRepos:"{count} more repos in this task",todos:"{done} of {total} todos done",startsAt:"Starts {time}",pr:{state:{open:"open",merged:"merged",closed:"closed"},many:"{count} PRs in this task and its children",checks:{conflict:"conflicts with its base",failing:"checks failing",passing:"checks passing",pending:"checks running"}},searchPlaceholder:"fuzzy filter",engineSwitched:"Engine → {engine} (applies on reopen)",engineSwitchFailed:"could not switch engine: {error}"},inbox:{title:"inbox",hints:"↑↓ move · enter open · d ack · esc close",ackAll:"[ ack all ]",attention:"attention",recent:"recent",empty:"no pending attention",reply:"[ reply ]",hide:"[ hide ]",send:"[ send ]",openTab:"[ open ]",noOutput:"no output yet",replyPlaceholder:"reply to this session",gateKept:"Saved; the engine isn't running and will get it with its next message.",state:{done:"done",needsInput:"needs input",error:"error",rateLimited:"rate limited",dead:"engine exited",routineFailed:"routine needs you",decision:"needs a decision",reportMissing:"report missing"}}},so={rail:{inbox:"收件箱 {count}",empty:"还没有任务",skillCue:"缺少技能",newTask:"新建任务",settings:"设置",noProject:"未分组",state:{needs:"等你处理",working:"进行中",ready:"待收尾",idle:"空闲"},extraRepos:"这个任务还含 {count} 个仓库",todos:"{done}/{total} 条待办已完成",startsAt:"{time} 启动",pr:{state:{open:"进行中",merged:"已合并",closed:"已关闭"},many:"这个任务及子任务共 {count} 个 PR",checks:{conflict:"与基础分支冲突",failing:"检查未通过",passing:"检查已通过",pending:"检查进行中"}},searchPlaceholder:"模糊搜索",engineSwitched:"引擎 → {engine}(重新打开后生效)",engineSwitchFailed:"切换引擎失败:{error}"},inbox:{title:"收件箱",hints:"↑↓ 移动 · enter 打开 · d 已阅 · esc 关闭",ackAll:"[ 全部已阅 ]",attention:"待处理",recent:"最近使用",empty:"暂无待处理",reply:"[ 回复 ]",hide:"[ 收起 ]",send:"[ 发送 ]",openTab:"[ 打开 ]",noOutput:"还没有输出",replyPlaceholder:"回复这个会话",gateKept:"已记下;引擎没在运行,下次发消息时一并送达。",state:{done:"完成",needsInput:"需要输入",error:"出错",rateLimited:"限流",dead:"引擎已退出",routineFailed:"例行任务需要处理",decision:"等你决定",reportMissing:"未回报"}}};var Pe={};f(Pe,{zh:()=>ro,en:()=>io});var io={reviewQueue:{sourceIssues:"[ issues ]",sourceReviews:"[ PRs waiting on me ]",palette:"Review queue",title:"PRs waiting on me",empty:"No open PR asks for your review in your projects.",other:"{count} more in projects Rove doesn't know.",draft:"draft",fork:"fork",checksPassing:"checks passing",checksFailing:"checks failing",checksPending:"checks running",start:"[ review ]",preparing:"Fetching #{number}…",failed:"Could not start the review of #{number}: {error}",gh:{missing:"The `gh` CLI is not installed or not on PATH.",unauthenticated:"`gh` is not signed in: run `gh auth login` on this Mac.",failed:"GitHub could not be read just now. Press r to retry."},prompt:`Review pull request #{number} in {repo}: {title}
|
|
26
26
|
{url}
|
|
27
27
|
|
|
28
|
-
This worktree is at the PR's head ({head}, going into {base}). Read the change against {base} (git diff origin/{base}...HEAD), then tell me what you find: bugs, risks, missing tests, anything unclear. Do not push, comment on or approve the PR; report here.`}},
|
|
28
|
+
This worktree is at the PR's head ({head}, going into {base}). Read the change against {base} (git diff origin/{base}...HEAD), then tell me what you find: bugs, risks, missing tests, anything unclear. Do not push, comment on or approve the PR; report here.`}},ro={reviewQueue:{sourceIssues:"[ 议题 ]",sourceReviews:"[ 等我评审的 PR ]",palette:"评审队列",title:"等我评审的 PR",empty:"你的项目里没有等你评审的 PR。",other:"另有 {count} 个在 Rove 不认识的项目里。",draft:"草稿",fork:"fork",checksPassing:"检查通过",checksFailing:"检查未通过",checksPending:"检查进行中",start:"[ 评审 ]",preparing:"正在获取 #{number}…",failed:"无法开始评审 #{number}:{error}",gh:{missing:"没有安装 `gh` 命令行,或不在 PATH 中。",unauthenticated:"`gh` 未登录:请在这台 Mac 上运行 `gh auth login`。",failed:"暂时读不到 GitHub。按 r 重试。"},prompt:`评审 {repo} 的拉取请求 #{number}:{title}
|
|
29
29
|
{url}
|
|
30
30
|
|
|
31
|
-
这个 worktree 在该 PR 的头部({head},合入 {base})。对照 {base} 阅读改动(git diff origin/{base}...HEAD),然后告诉我你的发现:bug、风险、缺失的测试、不清楚的地方。不要推送、评论或批准这个 PR;在这里汇报。`}};var xe={};f(xe,{zh:()=>ho,en:()=>ro});var ro={runLog:{title:"Runs",all:"all",routine:"Routine",allRoutines:"All routines",outcome:{ok:"ok",blocked:"blocked",failed:"failed",skipped:"skipped"},source:{schedule:"schedule",manual:"manual",webhook:"webhook"},blocked:{"task-missing":"task is gone","awaiting-owner":"waiting on a decision","report-missing":"last run never reported",quota:"rate limited","engine-error":"engine error","engine-dead":"engine stopped","awaiting-permission":"waiting on a permission",busy:"still working"},blockedRun:"blocked: {reason}",empty:"No runs match.",more:"+{count} more"}},ho={runLog:{title:"运行记录",all:"全部",routine:"例行任务",allRoutines:"全部例行任务",outcome:{ok:"成功",blocked:"受阻",failed:"失败",skipped:"跳过"},source:{schedule:"定时",manual:"手动",webhook:"Webhook"},blocked:{"task-missing":"任务已不存在","awaiting-owner":"等你做决定","report-missing":"上次运行没有汇报",quota:"触发限流","engine-error":"引擎出错","engine-dead":"引擎已停止","awaiting-permission":"等你授权",busy:"仍在运行"},blockedRun:"受阻:{reason}",empty:"没有符合条件的运行。",more:"另有 {count} 条"}};var Ce={};f(Ce,{zh:()=>go,en:()=>ao});var ao={settings:{title:"Settings",open:"settings",sections:{connectors:"Connectors",appearance:"Appearance",notifications:"Notifications",snippets:"Snippets",about:"About"},groups:{preferences:"Preferences",agents:"Agents and tools",more:"More"},pages:{general:"Language, layout, and where Rove opens files and creates worktrees.",appearance:"Theme, colours, and how tasks and panes look.",notifications:"When Rove tells you a task finished or needs you.",keys:"Rebind chords in your own file, and how the prefix guide behaves.",engines:"The coding agents Rove can start, and how it plugs into them.",autoRouting:"What each depth tier starts, and who picks the tier.",connectors:"Accounts Rove uses, here and on your other machines.",plugins:"Built-in pages and registered plugins.",about:"Versions, updates, and this browser.",feedback:"Send a note to the Rove team.",dev:"Experimental switches, restarts and resets.",snippets:"Prompt text you reuse. Type @name in a prompt to insert it."},connectors:{githubDescription:"Clone, push and open PRs on every machine with one GitHub account",connect:"Connect",disconnect:"Disconnect",disconnectBody:"Signs this machine's gh out of GitHub, for every tool that uses gh and not only Rove. Other machines keep their own sign-ins.",machines:"Machines",thisMachine:"This machine",refresh:"Refresh",useRove:"Use Rove's account",loginThere:"Log in there",codeHint:"Enter this code at GitHub (click the code to copy it)",openGithub:"Open github.com/login/device",codeCopied:"Code copied",syncing:"Signed in. Bringing the other machines along…",loginFailed:"Sign-in failed: {error}",dismiss:"Dismiss",loading:"checking…",markSame:"✓ same account",markDifferent:"⚠ @{login}",markSignedOut:"✗ signed out",markNoGh:"gh is not installed",markOutdated:"Rove there is outdated",markOffline:"offline",markDenied:"refuses credential changes",markFailed:"could not check",detail:"{protocol}, ssh {ssh}",synced:"{machine} now uses @{login}"},general:{groupInterface:"Interface",groupFiles:"Files and worktrees",groupTerminal:"Terminal",editorNote:"Opens files from the file tree. auto follows $VISUAL / $EDITOR, else nvim / vim / emacs / nano.",tabStrip:"Tab strip",scrollback:"Scrollback"},appearance:{groupColours:"Colours",groupTasks:"Tasks and panes",groupTerminal:"Terminal app only",taskColorsNote:"Gives each task its own hue on the working border and in the list.",colorblindNote:"Shows added and removed lines in blue and red instead of green and red.",workingBorderNote:"The pane's border while the selected task is working.",railGroup:"Task list",railGroupProject:"By project",railGroupState:"By state"},notifications:{toastNote:"A popup in the corner of this page.",volume:"Chime volume"},about:{groupRove:"Rove",daemon:"Daemon",daemonCurrent:"rove {version}, same build as this page",daemonStale:"rove {version} is out of date; run `rove daemon restart`",updates:"Updates",browser:"This browser",browserHint:"Forget this browser's token. A login token is revoked on the host too.",signOut:"Sign out"},engines:{enabled:"Offer {name} when picking an engine",default:"Make {name} the default engine",namePlaceholder:"Display name (blank = default)",rename:"Rename",reset:"Reset to defaults",remove:"Remove engine",save:"Save",cancel:"Cancel"},plugins:{builtIn:"Built in",builtInHint:"First-party pages. Turning one off removes its entry from the rail.",registered:"Registered plugins",empty:"No other plugins registered.",linked:"linked",unsupportedPlatform:"unsupported platform",turnOn:"Install {name}"},feedback:{hint:"Posts a public GitHub Discussion in the Rove repo, through `gh` on the machine running rove.",titleLabel:"Title",body:"What happened",send:"Send",sent:"Feedback posted {url}"}},prefs:{terminalOnly:"Terminal only: the TUI changes, this page has nothing to draw.",languageHint:"Display language, shared with the TUI.",soundHint:"chime, plus a desktop notification while this tab is in the background",soundVolumeHint:"Plays the new level when you pick it.",push:"Notify when this page is closed",pushHint:"This browser gets a notification when a task finishes, needs you or fails, even with no Rove page open. Follows the cross-task switch.",pushUnsupported:"This browser can't receive push here: it needs https (or localhost), and on iPhone/iPad the page added to the home screen.",pushBlocked:"Notifications are blocked for this site; allow them in the browser's site settings.",pushFailed:"Couldn't turn it on: {error}",zenHint:"Hide the Files pane. The zen chip and ctrl+a z toggle it and write this same setting.",editInTui:"Edit it in the TUI.",tierModel:"model",tierEdit:"Edit",tierEffort:"Reasoning effort",classifierConnection:"Classifier connection",unset:"(none)",modeAuto:"Auto (follow the browser)",themePreviewHint:"Point at a theme to see this page in it; click to keep it. The TUI follows.",themeUser:"yours",themeInstalled:"Installed",themeMoreHint:"Not installed yet: point to preview, click to install and switch.",themeBrowse:"Browse {count} more from rove.run",themeHide:"Hide themes from rove.run",themeGet:"get",themeInstalling:"installing…",themeInstallFailed:"failed, retry",themeAgent:"Make one with an agent",themeAgentHint:"opens a Scratch session; reopen Settings to see what it wrote.",themeAgentFailed:"couldn't open the session; try again.",themeAgentPrompt:"Make me a new Rove color theme. Ask me first what look I want, unless I already said. Save it as one JSON file in {dir} (for example {dir}/my-theme.json); the file name is the theme's name. The format is in https://github.com/sma1lboy/rove/blob/main/docs/themes.md and the JSON schema is https://raw.githubusercontent.com/sma1lboy/rove/main/packages/rove/src/tui/context/theme/theme.schema.json. Start from https://raw.githubusercontent.com/sma1lboy/rove/main/packages/rove/src/tui/context/theme/claude.json, give each color slot a { dark, light } pair, and keep text readable on every background. When it's saved, run `rove theme list` to check it loads, then tell me the name: it appears in Settings → Appearance.",resetButton:"Reset",resetHint:"Clears this Mac's saved settings and task list, restarts the Rove daemon, then reloads this page. Worktrees on disk and engine session history are not touched.",resetBody:"Wipes ~/.config/rove/state.json and ~/.rove/tasks.json and restarts the daemon, then reloads this page with an empty task list. Worktrees on disk and engine session history are NOT touched.",restartButton:"Restart",restartHint:"Stops the Rove daemon and starts a fresh one; this page reconnects on its own. Running engine sessions live in the PTY host and survive it.",restartBody:"Stops the Rove daemon and starts a fresh one, picking up daemon edits. This page reconnects in a few seconds. Running engine sessions survive.",keysYaml:"The file is read on the Mac; changes apply when you come back to this page."},keys:{createFile:"Create keybindings.yaml",exists:"keybindings.yaml already exists; nothing was changed.",pluginNoTask:"Select a task before using a plugin chord.",pluginChords:"Plugin chords",agent:"Change them with an agent",agentHint:"opens a Scratch session; reopen Settings to see what it changed.",agentFailed:"couldn't open the session; try again.",agentPrompt:"Change my Rove keybindings. Ask me first what I want changed, unless I already said. Edit {path} (create it if it is missing; keep existing entries and comments you were not asked to change). The format, every binding id and its default chord are in https://github.com/sma1lboy/rove/blob/main/docs/KEYBINDINGS.md (see Customizing). Only use binding ids listed there; a direct override replaces that binding's whole chord list, and null unbinds. The file must stay valid YAML: check it parses (for example with `ruby -ryaml -e 'YAML.load_file(ARGV[0])' {path}` or any YAML parser you have). Rove reloads it live, and unknown ids or invalid chords are ignored with a warning, so when you are done tell me what you changed and ask me to reopen Settings → Keybindings: it lists every applied override and every warning."},pages:{attachTooLarge:"{name} is larger than 5 MB",new:"[ new ]",routinePause:"[ pause ]",routineEnable:"[ enable ]",delete:"[ delete ]",routinesNeedYou:"NEEDS YOU",routinesActive:"ACTIVE",routinesPaused:"PAUSED",routineSchedule:"Schedule",routineNext:"Next run",routineProject:"Project",routineDelivers:"Delivers to",routinePrompt:"PROMPT",routineScript:"SCRIPT",cronDown:"Decrease {field}",cronUp:"Increase {field}",storySave:"save",storySaveStart:"save & start",storyStart:"start",attachHintTouch:"paste a path / a screenshot → inserts an image placeholder"}},go={settings:{title:"设置",open:"设置",sections:{connectors:"连接器",appearance:"外观",notifications:"通知",snippets:"片段",about:"关于"},groups:{preferences:"偏好",agents:"智能体与工具",more:"更多"},pages:{general:"语言、布局,以及 Rove 在哪里打开文件、创建 worktree。",appearance:"主题、颜色,以及任务和面板的外观。",notifications:"任务完成或需要你时,Rove 怎么提醒。",keys:"在你自己的文件里重绑快捷键,以及前缀引导的行为。",engines:"Rove 能启动的编码智能体,以及它如何接入它们。",autoRouting:"每个深度档位启动什么,以及由谁选择档位。",connectors:"Rove 使用的账号,本机和你的其他机器。",plugins:"内置页面和已注册的插件。",about:"版本、更新,以及此浏览器。",feedback:"给 Rove 团队留一条意见。",dev:"实验开关、重启和重置。",snippets:"常用的提示词文本。在提示词里输入 @名称 即可插入。"},connectors:{githubDescription:"用同一个 GitHub 账号在每台机器上克隆、推送和开 PR",connect:"连接",disconnect:"断开",disconnectBody:"把这台机器的 gh 从 GitHub 退出登录,所有用 gh 的工具都会受影响,不只是 Rove。其他机器保持各自的登录。",machines:"机器",thisMachine:"本机",refresh:"刷新",useRove:"使用 Rove 的账号",loginThere:"在那里登录",codeHint:"到 GitHub 输入这个验证码(点击验证码可复制)",openGithub:"打开 github.com/login/device",codeCopied:"验证码已复制",syncing:"已登录,正在同步其他机器…",loginFailed:"登录失败:{error}",dismiss:"关闭",loading:"检查中…",markSame:"✓ 同一账号",markDifferent:"⚠ @{login}",markSignedOut:"✗ 未登录",markNoGh:"未安装 gh",markOutdated:"那边的 Rove 版本过旧",markOffline:"离线",markDenied:"拒绝修改凭据",markFailed:"无法检查",detail:"{protocol}, ssh {ssh}",synced:"{machine} 现在使用 @{login}"},general:{groupInterface:"界面",groupFiles:"文件与 worktree",groupTerminal:"终端",editorNote:"从文件树打开文件时用它。auto 跟随 $VISUAL / $EDITOR,否则依次尝试 nvim / vim / emacs / nano。",tabStrip:"标签栏",scrollback:"回滚行数"},appearance:{groupColours:"颜色",groupTasks:"任务与面板",groupTerminal:"仅终端应用",taskColorsNote:"让每个任务在工作边框和列表里有自己的色调。",colorblindNote:"新增和删除的行用蓝色和红色,而不是绿色和红色。",workingBorderNote:"所选任务工作时,面板边框的样子。",railGroup:"任务列表",railGroupProject:"按项目",railGroupState:"按状态"},notifications:{toastNote:"本页角落里的弹出提示。",volume:"提示音音量"},about:{groupRove:"Rove",daemon:"守护进程",daemonCurrent:"rove {version},与本页同一版本",daemonStale:"rove {version} 版本过旧;运行 `rove daemon restart`",updates:"更新",browser:"此浏览器",browserHint:"忘掉此浏览器的令牌。登录得到的令牌也会在主机上吊销。",signOut:"退出登录"},engines:{enabled:"选引擎时列出 {name}",default:"把 {name} 设为默认引擎",namePlaceholder:"显示名称(留空 = 默认)",rename:"重命名",reset:"恢复默认",remove:"移除引擎",save:"保存",cancel:"取消"},plugins:{builtIn:"内置",builtInHint:"官方页面。关掉后,它在侧栏的入口会消失。",registered:"已注册的插件",empty:"没有其他已注册的插件。",linked:"已链接",unsupportedPlatform:"平台不支持",turnOn:"安装 {name}"},feedback:{hint:"通过运行 rove 那台机器上的 `gh`,在 Rove 仓库发一条公开的 GitHub Discussion。",titleLabel:"标题",body:"发生了什么",send:"发送",sent:"反馈已发布 {url}"}},prefs:{terminalOnly:"仅终端:TUI 会变化,网页没有可画的东西。",languageHint:"显示语言,与 TUI 共用同一设置。",soundHint:"提示音;本页在后台时另发桌面通知",soundVolumeHint:"选中后会播放新音量。",push:"页面关闭时也通知",pushHint:"任务完成、需要你或出错时,即使没有打开任何 Rove 页面,这个浏览器也会收到通知。跟随「跨任务通知」开关。",pushUnsupported:"这个浏览器在这里收不到推送:需要 https(或 localhost),iPhone/iPad 上还要先把页面添加到主屏幕。",pushBlocked:"这个网站的通知被浏览器屏蔽了,去浏览器的网站设置里允许。",pushFailed:"没能开启:{error}",zenHint:"收起文件栏。zen 按钮和 ctrl+a z 切换时会写入同一个设置。",editInTui:"请在 TUI 中编辑",tierModel:"模型",tierEdit:"编辑",tierEffort:"推理强度",classifierConnection:"分类器连接",unset:"(无)",modeAuto:"自动(跟随浏览器)",themePreviewHint:"鼠标指向一个主题,整页就换成它看看;点一下就定下来。TUI 也会跟着换。",themeUser:"自装",themeInstalled:"已安装",themeMoreHint:"尚未安装:指向即预览,点一下安装并切换。",themeBrowse:"浏览 rove.run 上的另外 {count} 个主题",themeHide:"收起 rove.run 的主题",themeGet:"安装",themeInstalling:"安装中…",themeInstallFailed:"失败,重试",themeAgent:"让 agent 做一个",themeAgentHint:"会开一个 Scratch 会话;做好后重新打开设置就能看到。",themeAgentFailed:"会话没打开,再试一次。",themeAgentPrompt:"帮我做一个新的 Rove 配色主题。先问我想要什么风格,除非我已经说过。把它存成 {dir} 下的一个 JSON 文件(比如 {dir}/my-theme.json),文件名就是主题名。格式说明见 https://github.com/sma1lboy/rove/blob/main/docs/themes.md,JSON schema 是 https://raw.githubusercontent.com/sma1lboy/rove/main/packages/rove/src/tui/context/theme/theme.schema.json。以 https://raw.githubusercontent.com/sma1lboy/rove/main/packages/rove/src/tui/context/theme/claude.json 为起点,每个颜色槽都给 { dark, light } 两个值,并保证文字在每种背景上都看得清。存好后运行 `rove theme list` 确认能加载,然后告诉我主题名:它会出现在 设置 → 外观 里。",resetButton:"重置",resetHint:"清空这台 Mac 上保存的设置和任务列表,重启 Rove 守护进程,然后刷新本页。磁盘上的工作树和引擎会话历史不受影响。",resetBody:"清空 ~/.config/rove/state.json 和 ~/.rove/tasks.json 并重启守护进程,然后刷新本页,任务列表为空。磁盘上的工作树和引擎会话历史不会被动。",restartButton:"重启",restartHint:"停止 Rove 守护进程并启动新的进程,本页会自动重连。运行中的引擎会话在 PTY 宿主里,不受影响。",restartBody:"停止 Rove 守护进程并启动新的进程,载入守护进程的改动。本页几秒后自动重连,运行中的引擎会话不受影响。",keysYaml:"该文件在 Mac 上读取,改动会在你回到本页时生效。"},keys:{createFile:"创建 keybindings.yaml",exists:"keybindings.yaml 已存在,未做任何改动。",pluginNoTask:"使用插件快捷键前请先选中一个任务。",pluginChords:"插件快捷键",agent:"让 agent 来改",agentHint:"会开一个 Scratch 会话;改好后重新打开设置就能看到。",agentFailed:"会话没打开,再试一次。",agentPrompt:"帮我改 Rove 的快捷键。先问我想改什么,除非我已经说过。编辑 {path}(不存在就新建;没让你改的条目和注释都保留)。格式、每个 binding id 及其默认按键见 https://github.com/sma1lboy/rove/blob/main/docs/KEYBINDINGS.md(Customizing 一节)。只用那里列出的 binding id;直接覆盖会替换该 binding 的整个按键列表,null 表示解绑。文件必须保持合法 YAML:确认它能解析(比如 `ruby -ryaml -e 'YAML.load_file(ARGV[0])' {path}`,或任何你有的 YAML 解析器)。Rove 会实时重新加载它,未知 id 或无效按键会被忽略并给出警告,所以改完后告诉我改了什么,并请我重新打开 设置 → 快捷键:那里会列出所有生效的覆盖和所有警告。"},pages:{attachTooLarge:"{name} 超过 5 MB",new:"[ 新建 ]",routinePause:"[ 暂停 ]",routineEnable:"[ 启用 ]",delete:"[ 删除 ]",routinesNeedYou:"需要处理",routinesActive:"运行中",routinesPaused:"已暂停",routineSchedule:"调度",routineNext:"下次执行",routineProject:"项目",routineDelivers:"投递到",routinePrompt:"提示词",routineScript:"脚本",cronDown:"减小{field}",cronUp:"增大{field}",storySave:"保存",storySaveStart:"保存并启动",storyStart:"启动",attachHintTouch:"粘贴路径 / 截图 → 插入图片占位行"}};var Be={};f(Be,{zh:()=>mo,en:()=>uo});var uo={transcript:{you:"You",agent:"Agent",system:"System",copy:"[ copy ]",copied:"Copied",export:"[ export .md ]",findPlaceholder:"Find in this conversation",findCount:"{at} / {total}",findNone:"no match",prev:"[ prev ]",next:"[ next ]",older:"[ load earlier ]",loading:"Reading the conversation…",empty:"Nothing said yet.",failed:"failed",error:"Could not read the conversation: {error}",retry:"[ retry ]",noHistory:"This engine keeps no history Rove can read. Showing its terminal tail.",tail:"Terminal tail"}},mo={transcript:{you:"你",agent:"智能体",system:"系统",copy:"[ 复制 ]",copied:"已复制",export:"[ 导出 .md ]",findPlaceholder:"在这段对话中查找",findCount:"{at} / {total}",findNone:"没有匹配",prev:"[ 上一个 ]",next:"[ 下一个 ]",older:"[ 加载更早 ]",loading:"正在读取对话…",empty:"还没有对话内容。",failed:"失败",error:"读不到这段对话:{error}",retry:"[ 重试 ]",noHistory:"这个引擎没有 Rove 能读取的历史记录,显示的是它的终端末尾。",tail:"终端末尾"}};var He={};f(He,{zh:()=>lo,en:()=>co});var co={usage:{title:"Usage",open:"Open usage",range:"last {days} days · tokens are input + output",perDay:"Per day",byModel:"By engine and model",topTasks:"Top tasks",quota:"Quota",turns:"{count} turns",loading:"Loading…",empty:"No turns recorded in this window.",failed:"Could not read usage: {error}",cutoff:"The daemon returns at most {count} turns: {day} and earlier are partial.",noReader:"{engines}: no turn reader, so their work is not counted here.",removedTask:"(removed task)",full:"{engine} {label} {percent}% · at this pace full around {time}, before it resets",lasts:"{engine} {label} {percent}% · at this pace it lasts until the reset",window:"{engine} {label} {percent}%"}},lo={usage:{title:"用量",open:"打开用量",range:"最近 {days} 天 · token 为输入加输出",perDay:"按天",byModel:"按引擎和模型",topTasks:"用量最高的任务",quota:"额度",turns:"{count} 轮",loading:"加载中…",empty:"这段时间没有记录到任何一轮。",failed:"无法读取用量:{error}",cutoff:"守护进程最多返回 {count} 轮:{day} 及更早的数据不完整。",noReader:"{engines}:没有轮次读取器,这里不统计它们的工作。",removedTask:"(已删除的任务)",full:"{engine} {label} {percent}% · 按当前速度约 {time} 用完,早于重置时间",lasts:"{engine} {label} {percent}% · 按当前速度可撑到重置",window:"{engine} {label} {percent}%"}};var Ne={};f(Ne,{zh:()=>po,en:()=>wo});var wo={workspace:{pickTask:"pick a task on the left",noTabs:"no open sessions in this task",startEngine:"Start {engine}",startSession:"Start session",tabNotLive:"this tab has no running session",restored:"restored session, read-only until reopened",exited:"session ended (exit {code})",attachFailed:"could not open this session: {message}",reopen:"Reopen session",reopening:"Reopening…",reopenFailed:"could not reopen this session: {message}",engineExited:"engine exited (code {code})",retry:"Retry",engineSettings:"Settings → Engines",usageLimit:"usage limit reached",dismiss:"Dismiss"},tab:{shell:"shell",engine:"engine"},firstRun:{title:"Run several agents side by side",body:"Every task gets its own worktree and branch, so sessions never step on each other.",start:"Start your first task"},status:{connecting:"connecting",retrying:"reconnecting, attempt {count}",retry:"retry now",backlog:"Backlog",inProgress:"In progress",inReview:"In review",done:"Done",canceled:"Canceled",error:"Error"},connect:{title:"open the link rove printed",body:"Run `rove --web` and open the link it prints, or paste it here.",placeholder:"http://127.0.0.1:7878/#token=…",submit:"connect",invalid:"that is not a `rove --web` link",failing:"can't reach rove. Is `rove --web` still running? After `--rotate-token`, open the new link.",forget:"forget this link"},relay:{unpaired:"open the relay link that `rove --web --preset relay` printed on your computer",connecting:"connecting to your computer through the relay…",loading:"loading rove…",offline:"your computer is not answering on this relay. Is `rove --web --preset relay` still running? After `--rotate-relay`, open the new link. Reload to retry.",failed:"can't reach your computer through the relay ({error}). Reload to retry."},login:{title:"sign in",body:"Enter the password set with `rove --web --set-password`.",field:"password",submit:"sign in",checking:"checking…",wrong:"that did not work",locked:"too many tries from here; try again in {minutes} min",lockedHost:"password login is locked; run `rove --web --unlock` on the host",unreachable:"can't reach rove. Is `rove --web` still running?"},layout:{splitTooSmall:"Not enough room to split here",railFold:"Fold the task list",navBack:"Back",navForward:"Forward",railUnfold:"Unfold the task list",dropUnsupported:"Only images and PDFs can be dropped into a terminal",pasteNoImage:"No image on this browser's clipboard",pasteNoAccess:"Can't read this browser's clipboard: {error}",pasteNeedsHttps:"the page needs https or localhost",refreshTimeout:"The daemon did not come back on the new build in time; check `rove daemon status`",railResize:"Drag to resize the task list, double-click to reset",composerPlaceholder:"Message the agent",composerSend:"Send",composerAttach:"Attach an image or PDF",composerNotSent:"Not sent: {reason}",composerOldBridge:"This Mac's rove --web is older than this page; restart it to send from here.",composerAtMenu:"The agent is waiting on a choice. Answer it with the 1–4 and ⏎ keys below; your message is kept.",composerQueued:"The agent is working: sent when this turn ends (#{position} in line).",queuedChip:"queued",queueCancel:"Take this message back",termKeys:"Terminal keys",termKeyUp:"Up arrow",termKeyDown:"Down arrow",termKeyLeft:"Left arrow",termKeyRight:"Right arrow",termKeyInterrupt:"Ctrl+C, interrupt",termKeyEnter:"Enter",narrowTerminal:"Terminal",narrowChat:"Chat",narrowFiles:"Files",narrowDiff:"Diff",narrowBrowser:"Browser",narrowQuota:"Context"}},po={workspace:{pickTask:"从左边选一个任务",noTabs:"这个任务没有打开的会话",startEngine:"启动 {engine}",startSession:"启动会话",tabNotLive:"这个标签没有在运行的会话",restored:"已恢复的会话,重新打开前只读",exited:"会话已结束(退出码 {code})",attachFailed:"打不开这个会话:{message}",reopen:"重新打开会话",reopening:"正在重新打开…",reopenFailed:"重新打开失败:{message}",engineExited:"引擎已退出(退出码 {code})",retry:"重试",engineSettings:"设置 → 引擎",usageLimit:"已达到用量上限",dismiss:"忽略"},tab:{shell:"终端",engine:"引擎"},firstRun:{title:"让多个智能体并排工作",body:"每个任务有独立的 worktree 和分支,会话之间互不干扰。",start:"开始第一个任务"},status:{connecting:"连接中",retrying:"重连中,第 {count} 次",retry:"立即重试",backlog:"待办",inProgress:"进行中",inReview:"待评审",done:"已完成",canceled:"已取消",error:"出错"},connect:{title:"打开 rove 打印的链接",body:"运行 `rove --web`,打开它打印的链接,或者把链接粘贴到这里。",placeholder:"http://127.0.0.1:7878/#token=…",submit:"连接",invalid:"这不是 `rove --web` 打印的链接",failing:"连不上 rove。`rove --web` 还在运行吗?用过 `--rotate-token` 的话,请打开新的链接。",forget:"忘掉这个链接"},relay:{unpaired:"打开电脑上 `rove --web --preset relay` 打印的 relay 链接",connecting:"正在通过 relay 连接你的电脑…",loading:"正在加载 rove…",offline:"你的电脑没有在这个 relay 上应答。`rove --web --preset relay` 还在运行吗?用过 `--rotate-relay` 的话,请打开新的链接。刷新重试。",failed:"无法通过 relay 连到你的电脑({error})。刷新重试。"},login:{title:"登录",body:"输入用 `rove --web --set-password` 设置的密码。",field:"密码",submit:"登录",checking:"验证中…",wrong:"登录失败",locked:"这里尝试次数过多;{minutes} 分钟后再试",lockedHost:"密码登录已锁定;在主机上运行 `rove --web --unlock`",unreachable:"连不上 rove。`rove --web` 还在运行吗?"},layout:{splitTooSmall:"这里放不下分屏了",railFold:"折叠任务列表",navBack:"后退",navForward:"前进",railUnfold:"展开任务列表",dropUnsupported:"终端里只能拖入图片和 PDF",pasteNoImage:"浏览器剪贴板里没有图片",pasteNoAccess:"读不到浏览器剪贴板:{error}",pasteNeedsHttps:"页面要走 https 或 localhost",refreshTimeout:"守护进程没能及时以新版本重新上线,请检查 `rove daemon status`",railResize:"拖动调整任务列表宽度,双击还原",composerPlaceholder:"给 agent 发消息",composerSend:"发送",composerAttach:"附加图片或 PDF",composerNotSent:"没发出去:{reason}",composerOldBridge:"这台 Mac 上的 rove --web 比页面旧,重启它之后才能从这里发送。",composerAtMenu:"agent 正在等你选择。用下方的 1–4 和 ⏎ 键回答;你的消息已保留。",composerQueued:"agent 正在干活:这一轮结束后发出(排第 {position})。",queuedChip:"排队中",queueCancel:"撤回这条消息",termKeys:"终端按键",termKeyUp:"上方向键",termKeyDown:"下方向键",termKeyLeft:"左方向键",termKeyRight:"右方向键",termKeyInterrupt:"Ctrl+C 中断",termKeyEnter:"回车",narrowTerminal:"终端",narrowChat:"对话",narrowFiles:"文件",narrowDiff:"差异",narrowBrowser:"浏览器",narrowQuota:"上下文"}};var it=[Pe,Ne,ve,Re,Ce,pe,Te,xe,He,Be,fe,we,be,ke],rt=Object.assign({},...it.map((t)=>t.en)),ko=Object.assign({},...it.map((t)=>t.zh)),vo={en:rt,zh:ko};function fo(){try{let t=JSON.parse(localStorage.getItem("rove.prefs")??"null");if(t&&typeof t==="object"&&"prefs"in t){let o=t.prefs;if(o&&typeof o==="object"&&"locale"in o&&(o.locale==="en"||o.locale==="zh"))return o.locale}}catch{}return globalThis.navigator?.language?.toLowerCase().startsWith("zh")?"zh":"en"}var Me=nt(fo());function ht(){let t=typeof document>"u"?void 0:document.documentElement;if(t)t.lang=Me.get()==="zh"?"zh-CN":"en"}ht();Me.subscribe(ht);function st(t,o){let s=t;for(let i of o.split(".")){if(!s||typeof s!=="object"||!(i in s))return;s=Reflect.get(s,i)}return typeof s==="string"?s:void 0}function Ro(t,o){return o?t.replace(/\{(\w+)\}/g,(s,i)=>String(o[i]??s)):t}function E(t,o){return Ro(st(vo[Me.get()],t)??st(rt,t)??t,o)}var To=new Set([101,204,205,304]);function at(t){return t.state==="ready"?Promise.resolve():t.connect()}function dt(t){class o{static OPEN=1;readyState=0;onopen=null;onmessage=null;onclose=null;onerror=null;sock=null;constructor(s,i){let a=i.find((w)=>w.startsWith("rove.bearer."))?.slice(12),g=new URL(s,"http://relay").pathname+(a?`?token=${encodeURIComponent(a)}`:"");this.open(g)}async open(s){try{await at(t);let i=await t.openSocket(s);if(this.readyState===3){i.close();return}this.sock=i,i.onmessage=(a)=>this.onmessage?.({data:typeof a==="string"?a:new TextDecoder().decode(a)}),i.onclose=()=>this.closed(),this.readyState=1,this.onopen?.(new Event("open"))}catch{this.closed()}}send(s){this.sock?.send(s)}close(){this.sock?.close(),this.closed()}closed(){if(this.readyState===3)return;this.readyState=3,this.onclose?.(new Event("close"))}}return{async fetch(s,i={}){await at(t);let a=typeof i.body==="string"?i.body:void 0,g=await t.fetch(s,{method:i.method,headers:Object.fromEntries(new Headers(i.headers).entries()),body:a});return new Response(To.has(g.status)?null:g.bytes(),{status:g.status,headers:g.headers})},WebSocket:o}}var gt="rove.relay",Po=/url\((\/assets\/[^)]+)\)/g;function K(t){let o=document.getElementById("relay-status");if(o)o.textContent=t}function bo(){let t=new URLSearchParams(location.hash.slice(1)),o=t.get("code");if(o){let s={server:t.get("relay")??location.origin,code:o};localStorage.setItem(gt,JSON.stringify(s));let i=t.get("token");return history.replaceState(null,"",location.pathname+location.search+(i?`#token=${i}`:"")),s}try{let s=JSON.parse(localStorage.getItem(gt)??"null");if(s&&typeof s==="object"&&"server"in s&&"code"in s){if(typeof s.server==="string"&&typeof s.code==="string")return{server:s.server,code:s.code}}}catch{}return null}function xo(t){let o=t instanceof Error?t.message:String(t);if(/\b(4404|1011)\b|timed out|auth/i.test(o))return E("relay.offline");return E("relay.failed",{error:o})}async function ut(t,o,s){let i=await t.fetch(o);if(!i.ok)throw Error(`${o}: HTTP ${i.status}`);return URL.createObjectURL(new Blob([await i.arrayBuffer()],{type:s}))}async function Co(t){let o=await t.fetch("/"),s=new DOMParser().parseFromString(await o.text(),"text/html"),i=s.querySelector('script[type="module"][src]')?.getAttribute("src");if(!i)throw Error(`no client script in the bridge's page (HTTP ${o.status})`);for(let g of s.querySelectorAll('link[rel="stylesheet"][href]')){let v=await(await t.fetch(g.getAttribute("href")??"")).text(),x=[...new Set([...v.matchAll(Po)].map((T)=>T[1]??""))],C=await Promise.all(x.map(async(T)=>[T,await ut(t,T,"")]));for(let[T,D]of C)v=v.replaceAll(`url(${T})`,`url(${D})`);let B=document.createElement("style");B.textContent=v,document.head.append(B)}Reflect.set(globalThis,"__roveRelay",t);let a=document.createElement("script");a.type="module",a.src=await ut(t,i,"text/javascript"),document.head.append(a)}async function Bo(){let t=bo();if(!t){K(E("relay.unpaired"));return}K(E("relay.connecting"));let o=new le({serverUrl:t.server,code:t.code,quiet:!0});try{await o.connect(),K(E("relay.loading")),await Co(dt(o))}catch(s){K(xo(s))}}Bo();
|
|
31
|
+
这个 worktree 在该 PR 的头部({head},合入 {base})。对照 {base} 阅读改动(git diff origin/{base}...HEAD),然后告诉我你的发现:bug、风险、缺失的测试、不清楚的地方。不要推送、评论或批准这个 PR;在这里汇报。`}};var xe={};f(xe,{zh:()=>ho,en:()=>ao});var ao={runLog:{title:"Runs",all:"all",routine:"Routine",allRoutines:"All routines",outcome:{ok:"ok",blocked:"blocked",failed:"failed",skipped:"skipped"},source:{schedule:"schedule",manual:"manual",webhook:"webhook"},blocked:{"task-missing":"task is gone","awaiting-owner":"waiting on a decision","report-missing":"last run never reported",quota:"rate limited","engine-error":"engine error","engine-dead":"engine stopped","awaiting-permission":"waiting on a permission",busy:"still working"},blockedRun:"blocked: {reason}",empty:"No runs match.",more:"+{count} more"}},ho={runLog:{title:"运行记录",all:"全部",routine:"例行任务",allRoutines:"全部例行任务",outcome:{ok:"成功",blocked:"受阻",failed:"失败",skipped:"跳过"},source:{schedule:"定时",manual:"手动",webhook:"Webhook"},blocked:{"task-missing":"任务已不存在","awaiting-owner":"等你做决定","report-missing":"上次运行没有汇报",quota:"触发限流","engine-error":"引擎出错","engine-dead":"引擎已停止","awaiting-permission":"等你授权",busy:"仍在运行"},blockedRun:"受阻:{reason}",empty:"没有符合条件的运行。",more:"另有 {count} 条"}};var He={};f(He,{zh:()=>uo,en:()=>go});var go={settings:{title:"Settings",open:"settings",sections:{connectors:"Connectors",appearance:"Appearance",notifications:"Notifications",snippets:"Snippets",about:"About",voice:"Voice"},groups:{preferences:"Preferences",agents:"Agents and tools",more:"More"},pages:{general:"Language, layout, and where Rove opens files and creates worktrees.",appearance:"Theme, colours, and how tasks and panes look.",notifications:"When Rove tells you a task finished or needs you.",keys:"Rebind chords in your own file, and how the prefix guide behaves.",engines:"The coding agents Rove can start, and how it plugs into them.",autoRouting:"What each depth tier starts, and who picks the tier.",voice:"Dictation: the recognizer that turns what you say into prompts.",connectors:"Accounts Rove uses, here and on your other machines.",plugins:"Built-in pages and registered plugins.",about:"Versions, updates, and this browser.",feedback:"Send a note to the Rove team.",dev:"Experimental switches, restarts and resets.",snippets:"Prompt text you reuse. Type @name in a prompt to insert it."},connectors:{githubDescription:"Clone, push and open PRs on every machine with one GitHub account",connect:"Connect",disconnect:"Disconnect",disconnectBody:"Signs this machine's gh out of GitHub, for every tool that uses gh and not only Rove. Other machines keep their own sign-ins.",machines:"Machines",thisMachine:"This machine",refresh:"Refresh",useRove:"Use Rove's account",loginThere:"Log in there",codeHint:"Enter this code at GitHub (click the code to copy it)",openGithub:"Open github.com/login/device",codeCopied:"Code copied",syncing:"Signed in. Bringing the other machines along…",loginFailed:"Sign-in failed: {error}",dismiss:"Dismiss",loading:"checking…",markSame:"✓ same account",markDifferent:"⚠ @{login}",markSignedOut:"✗ signed out",markNoGh:"gh is not installed",markOutdated:"Rove there is outdated",markOffline:"offline",markDenied:"refuses credential changes",markFailed:"could not check",detail:"{protocol}, ssh {ssh}",synced:"{machine} now uses @{login}"},general:{groupInterface:"Interface",groupFiles:"Files and worktrees",groupTerminal:"Terminal",editorNote:"Opens files from the file tree. auto follows $VISUAL / $EDITOR, else nvim / vim / emacs / nano.",tabStrip:"Tab strip",scrollback:"Scrollback"},appearance:{groupColours:"Colours",groupTasks:"Tasks and panes",groupTerminal:"Terminal app only",taskColorsNote:"Gives each task its own hue on the working border and in the list.",colorblindNote:"Shows added and removed lines in blue and red instead of green and red.",workingBorderNote:"The pane's border while the selected task is working.",railGroup:"Task list",railGroupProject:"By project",railGroupState:"By state"},notifications:{toastNote:"A popup in the corner of this page.",volume:"Chime volume"},about:{groupRove:"Rove",daemon:"Daemon",daemonCurrent:"rove {version}, same build as this page",daemonStale:"rove {version} is out of date; run `rove daemon restart`",updates:"Updates",browser:"This browser",browserHint:"Forget this browser's token. A login token is revoked on the host too.",signOut:"Sign out"},engines:{enabled:"Offer {name} when picking an engine",default:"Make {name} the default engine",namePlaceholder:"Display name (blank = default)",rename:"Rename",reset:"Reset to defaults",remove:"Remove engine",save:"Save",cancel:"Cancel"},plugins:{builtIn:"Built in",builtInHint:"First-party pages. Turning one off removes its entry from the rail.",registered:"Registered plugins",empty:"No other plugins registered.",linked:"linked",unsupportedPlatform:"unsupported platform",turnOn:"Install {name}"},feedback:{hint:"Files a public GitHub issue on the Rove repo, through `gh` on the machine running rove. Without a signed-in `gh` you get a prefilled link to open instead.",kindLabel:"Kind",kindBug:"Bug",kindIdea:"Idea",kindQuestion:"Question",titleLabel:"Title",body:"What happened",send:"File issue",sent:"Filed issue #{number}",notFiled:"Not filed yet. {hint}",openLink:"Open the prefilled issue",report:"Report a problem with an agent",reportNote:"An agent asks what went wrong, drafts the issue with you and files it.",reportFailed:"Could not start the conversation.",reportPrompt:"You are helping the user report a problem with Rove itself (not their own project) as a GitHub issue. First ask what they were doing and what felt wrong: one to three short questions, one at a time. Then collect context: run `rove --version` and `rove daemon status`, and read the last ~50 lines of `${ROVE_HOME_DIR:-$HOME}/.rove/daemon.log` only if the problem could involve the daemon. Draft an issue title and body (what happened, what they expected, steps, relevant output) and show it to the user. Only after they say OK, file it with a heredoc: `rove feedback --kind bug|idea|question --title \"...\" --body-file - <<'EOF'` followed by the body and `EOF`. Then share the issue URL. If it prints a prefilled link instead (gh missing or signed out), give the user that link and tell them to run `gh auth login`."}},prefs:{terminalOnly:"Terminal only: the TUI changes, this page has nothing to draw.",languageHint:"Display language, shared with the TUI.",soundHint:"chime, plus a desktop notification while this tab is in the background",soundVolumeHint:"Plays the new level when you pick it.",push:"Notify when this page is closed",pushHint:"This browser gets a notification when a task finishes, needs you or fails, even with no Rove page open. Follows the cross-task switch.",pushUnsupported:"This browser can't receive push here: it needs https (or localhost), and on iPhone/iPad the page added to the home screen.",pushBlocked:"Notifications are blocked for this site; allow them in the browser's site settings.",pushFailed:"Couldn't turn it on: {error}",zenHint:"Hide the Files pane. The zen chip and ctrl+a z toggle it and write this same setting.",editInTui:"Edit it in the TUI.",tierModel:"model",tierEdit:"Edit",tierEffort:"Reasoning effort",classifierConnection:"Classifier connection",unset:"(none)",modeAuto:"Auto (follow the browser)",themePreviewHint:"Point at a theme to see this page in it; click to keep it. The TUI follows.",themeUser:"yours",themeInstalled:"Installed",themeMoreHint:"Not installed yet: point to preview, click to install and switch.",themeBrowse:"Browse {count} more from rove.run",themeHide:"Hide themes from rove.run",themeGet:"get",themeInstalling:"installing…",themeInstallFailed:"failed, retry",themeAgent:"Make one with an agent",themeAgentHint:"opens a Scratch session; reopen Settings to see what it wrote.",themeAgentFailed:"couldn't open the session; try again.",themeAgentPrompt:"Make me a new Rove color theme. Ask me first what look I want, unless I already said. Save it as one JSON file in {dir} (for example {dir}/my-theme.json); the file name is the theme's name. The format is in https://github.com/sma1lboy/rove/blob/main/docs/themes.md and the JSON schema is https://raw.githubusercontent.com/sma1lboy/rove/main/packages/rove/src/tui/context/theme/theme.schema.json. Start from https://raw.githubusercontent.com/sma1lboy/rove/main/packages/rove/src/tui/context/theme/claude.json, give each color slot a { dark, light } pair, and keep text readable on every background. When it's saved, run `rove theme list` to check it loads, then tell me the name: it appears in Settings → Appearance.",resetButton:"Reset",resetHint:"Clears this Mac's saved settings and task list, restarts the Rove daemon, then reloads this page. Worktrees on disk and engine session history are not touched.",resetBody:"Wipes ~/.config/rove/state.json and ~/.rove/tasks.json and restarts the daemon, then reloads this page with an empty task list. Worktrees on disk and engine session history are NOT touched.",restartButton:"Restart",restartHint:"Stops the Rove daemon and starts a fresh one; this page reconnects on its own. Running engine sessions live in the PTY host and survive it.",restartBody:"Stops the Rove daemon and starts a fresh one, picking up daemon edits. This page reconnects in a few seconds. Running engine sessions survive.",keysYaml:"The file is read on the Mac; changes apply when you come back to this page."},keys:{createFile:"Create keybindings.yaml",exists:"keybindings.yaml already exists; nothing was changed.",pluginNoTask:"Select a task before using a plugin chord.",pluginChords:"Plugin chords",agent:"Change them with an agent",agentHint:"opens a Scratch session; reopen Settings to see what it changed.",agentFailed:"couldn't open the session; try again.",agentPrompt:"Change my Rove keybindings. Ask me first what I want changed, unless I already said. Edit {path} (create it if it is missing; keep existing entries and comments you were not asked to change). The format, every binding id and its default chord are in https://github.com/sma1lboy/rove/blob/main/docs/KEYBINDINGS.md (see Customizing). Only use binding ids listed there; a direct override replaces that binding's whole chord list, and null unbinds. The file must stay valid YAML: check it parses (for example with `ruby -ryaml -e 'YAML.load_file(ARGV[0])' {path}` or any YAML parser you have). Rove reloads it live, and unknown ids or invalid chords are ignored with a warning, so when you are done tell me what you changed and ask me to reopen Settings → Keybindings: it lists every applied override and every warning."},pages:{attachTooLarge:"{name} is larger than 5 MB",new:"[ new ]",routinePause:"[ pause ]",routineEnable:"[ enable ]",delete:"[ delete ]",routinesNeedYou:"NEEDS YOU",routinesActive:"ACTIVE",routinesPaused:"PAUSED",routineSchedule:"Schedule",routineNext:"Next run",routineProject:"Project",routineDelivers:"Delivers to",routinePrompt:"PROMPT",routineScript:"SCRIPT",cronDown:"Decrease {field}",cronUp:"Increase {field}",storySave:"save",storySaveStart:"save & start",storyStart:"start",attachHintTouch:"paste a path / a screenshot → inserts an image placeholder"}},uo={settings:{title:"设置",open:"设置",sections:{connectors:"连接器",appearance:"外观",notifications:"通知",snippets:"片段",about:"关于",voice:"语音"},groups:{preferences:"偏好",agents:"智能体与工具",more:"更多"},pages:{general:"语言、布局,以及 Rove 在哪里打开文件、创建 worktree。",appearance:"主题、颜色,以及任务和面板的外观。",notifications:"任务完成或需要你时,Rove 怎么提醒。",keys:"在你自己的文件里重绑快捷键,以及前缀引导的行为。",engines:"Rove 能启动的编码智能体,以及它如何接入它们。",autoRouting:"每个深度档位启动什么,以及由谁选择档位。",voice:"口述:把你说的话变成提示词的识别器。",connectors:"Rove 使用的账号,本机和你的其他机器。",plugins:"内置页面和已注册的插件。",about:"版本、更新,以及此浏览器。",feedback:"给 Rove 团队留一条意见。",dev:"实验开关、重启和重置。",snippets:"常用的提示词文本。在提示词里输入 @名称 即可插入。"},connectors:{githubDescription:"用同一个 GitHub 账号在每台机器上克隆、推送和开 PR",connect:"连接",disconnect:"断开",disconnectBody:"把这台机器的 gh 从 GitHub 退出登录,所有用 gh 的工具都会受影响,不只是 Rove。其他机器保持各自的登录。",machines:"机器",thisMachine:"本机",refresh:"刷新",useRove:"使用 Rove 的账号",loginThere:"在那里登录",codeHint:"到 GitHub 输入这个验证码(点击验证码可复制)",openGithub:"打开 github.com/login/device",codeCopied:"验证码已复制",syncing:"已登录,正在同步其他机器…",loginFailed:"登录失败:{error}",dismiss:"关闭",loading:"检查中…",markSame:"✓ 同一账号",markDifferent:"⚠ @{login}",markSignedOut:"✗ 未登录",markNoGh:"未安装 gh",markOutdated:"那边的 Rove 版本过旧",markOffline:"离线",markDenied:"拒绝修改凭据",markFailed:"无法检查",detail:"{protocol}, ssh {ssh}",synced:"{machine} 现在使用 @{login}"},general:{groupInterface:"界面",groupFiles:"文件与 worktree",groupTerminal:"终端",editorNote:"从文件树打开文件时用它。auto 跟随 $VISUAL / $EDITOR,否则依次尝试 nvim / vim / emacs / nano。",tabStrip:"标签栏",scrollback:"回滚行数"},appearance:{groupColours:"颜色",groupTasks:"任务与面板",groupTerminal:"仅终端应用",taskColorsNote:"让每个任务在工作边框和列表里有自己的色调。",colorblindNote:"新增和删除的行用蓝色和红色,而不是绿色和红色。",workingBorderNote:"所选任务工作时,面板边框的样子。",railGroup:"任务列表",railGroupProject:"按项目",railGroupState:"按状态"},notifications:{toastNote:"本页角落里的弹出提示。",volume:"提示音音量"},about:{groupRove:"Rove",daemon:"守护进程",daemonCurrent:"rove {version},与本页同一版本",daemonStale:"rove {version} 版本过旧;运行 `rove daemon restart`",updates:"更新",browser:"此浏览器",browserHint:"忘掉此浏览器的令牌。登录得到的令牌也会在主机上吊销。",signOut:"退出登录"},engines:{enabled:"选引擎时列出 {name}",default:"把 {name} 设为默认引擎",namePlaceholder:"显示名称(留空 = 默认)",rename:"重命名",reset:"恢复默认",remove:"移除引擎",save:"保存",cancel:"取消"},plugins:{builtIn:"内置",builtInHint:"官方页面。关掉后,它在侧栏的入口会消失。",registered:"已注册的插件",empty:"没有其他已注册的插件。",linked:"已链接",unsupportedPlatform:"平台不支持",turnOn:"安装 {name}"},feedback:{hint:"通过运行 rove 那台机器上的 `gh`,在 Rove 仓库提一个公开的 GitHub issue。没有登录 `gh` 时会给你一条预填好的链接。",kindLabel:"类型",kindBug:"缺陷",kindIdea:"想法",kindQuestion:"问题",titleLabel:"标题",body:"发生了什么",send:"提交 issue",sent:"已提交 issue #{number}",notFiled:"还没提交。{hint}",openLink:"打开预填好的 issue",report:"让 agent 帮我报告问题",reportNote:"由 agent 问清哪里不对,和你一起起草 issue 并提交。",reportFailed:"没能开始对话。",reportPrompt:"你在帮用户把 Rove 本身的问题(不是他们自己项目的问题)提成一个 GitHub issue。先问清楚他们当时在做什么、哪里不对劲:一到三个简短的问题,一次问一个。然后收集上下文:运行 `rove --version` 和 `rove daemon status`,只有问题可能和 daemon 有关时才读 `${ROVE_HOME_DIR:-$HOME}/.rove/daemon.log` 的最后约 50 行。起草 issue 的标题和正文(发生了什么、期望什么、复现步骤、相关输出)并展示给用户。用户确认之后才提交,用 heredoc:`rove feedback --kind bug|idea|question --title \"...\" --body-file - <<'EOF'`,接正文,最后一行 `EOF`。然后把 issue 链接发给用户。如果输出的是预填链接(没装 gh 或没登录),把链接给用户,并告诉他运行 `gh auth login`。"}},prefs:{terminalOnly:"仅终端:TUI 会变化,网页没有可画的东西。",languageHint:"显示语言,与 TUI 共用同一设置。",soundHint:"提示音;本页在后台时另发桌面通知",soundVolumeHint:"选中后会播放新音量。",push:"页面关闭时也通知",pushHint:"任务完成、需要你或出错时,即使没有打开任何 Rove 页面,这个浏览器也会收到通知。跟随「跨任务通知」开关。",pushUnsupported:"这个浏览器在这里收不到推送:需要 https(或 localhost),iPhone/iPad 上还要先把页面添加到主屏幕。",pushBlocked:"这个网站的通知被浏览器屏蔽了,去浏览器的网站设置里允许。",pushFailed:"没能开启:{error}",zenHint:"收起文件栏。zen 按钮和 ctrl+a z 切换时会写入同一个设置。",editInTui:"请在 TUI 中编辑",tierModel:"模型",tierEdit:"编辑",tierEffort:"推理强度",classifierConnection:"分类器连接",unset:"(无)",modeAuto:"自动(跟随浏览器)",themePreviewHint:"鼠标指向一个主题,整页就换成它看看;点一下就定下来。TUI 也会跟着换。",themeUser:"自装",themeInstalled:"已安装",themeMoreHint:"尚未安装:指向即预览,点一下安装并切换。",themeBrowse:"浏览 rove.run 上的另外 {count} 个主题",themeHide:"收起 rove.run 的主题",themeGet:"安装",themeInstalling:"安装中…",themeInstallFailed:"失败,重试",themeAgent:"让 agent 做一个",themeAgentHint:"会开一个 Scratch 会话;做好后重新打开设置就能看到。",themeAgentFailed:"会话没打开,再试一次。",themeAgentPrompt:"帮我做一个新的 Rove 配色主题。先问我想要什么风格,除非我已经说过。把它存成 {dir} 下的一个 JSON 文件(比如 {dir}/my-theme.json),文件名就是主题名。格式说明见 https://github.com/sma1lboy/rove/blob/main/docs/themes.md,JSON schema 是 https://raw.githubusercontent.com/sma1lboy/rove/main/packages/rove/src/tui/context/theme/theme.schema.json。以 https://raw.githubusercontent.com/sma1lboy/rove/main/packages/rove/src/tui/context/theme/claude.json 为起点,每个颜色槽都给 { dark, light } 两个值,并保证文字在每种背景上都看得清。存好后运行 `rove theme list` 确认能加载,然后告诉我主题名:它会出现在 设置 → 外观 里。",resetButton:"重置",resetHint:"清空这台 Mac 上保存的设置和任务列表,重启 Rove 守护进程,然后刷新本页。磁盘上的工作树和引擎会话历史不受影响。",resetBody:"清空 ~/.config/rove/state.json 和 ~/.rove/tasks.json 并重启守护进程,然后刷新本页,任务列表为空。磁盘上的工作树和引擎会话历史不会被动。",restartButton:"重启",restartHint:"停止 Rove 守护进程并启动新的进程,本页会自动重连。运行中的引擎会话在 PTY 宿主里,不受影响。",restartBody:"停止 Rove 守护进程并启动新的进程,载入守护进程的改动。本页几秒后自动重连,运行中的引擎会话不受影响。",keysYaml:"该文件在 Mac 上读取,改动会在你回到本页时生效。"},keys:{createFile:"创建 keybindings.yaml",exists:"keybindings.yaml 已存在,未做任何改动。",pluginNoTask:"使用插件快捷键前请先选中一个任务。",pluginChords:"插件快捷键",agent:"让 agent 来改",agentHint:"会开一个 Scratch 会话;改好后重新打开设置就能看到。",agentFailed:"会话没打开,再试一次。",agentPrompt:"帮我改 Rove 的快捷键。先问我想改什么,除非我已经说过。编辑 {path}(不存在就新建;没让你改的条目和注释都保留)。格式、每个 binding id 及其默认按键见 https://github.com/sma1lboy/rove/blob/main/docs/KEYBINDINGS.md(Customizing 一节)。只用那里列出的 binding id;直接覆盖会替换该 binding 的整个按键列表,null 表示解绑。文件必须保持合法 YAML:确认它能解析(比如 `ruby -ryaml -e 'YAML.load_file(ARGV[0])' {path}`,或任何你有的 YAML 解析器)。Rove 会实时重新加载它,未知 id 或无效按键会被忽略并给出警告,所以改完后告诉我改了什么,并请我重新打开 设置 → 快捷键:那里会列出所有生效的覆盖和所有警告。"},pages:{attachTooLarge:"{name} 超过 5 MB",new:"[ 新建 ]",routinePause:"[ 暂停 ]",routineEnable:"[ 启用 ]",delete:"[ 删除 ]",routinesNeedYou:"需要处理",routinesActive:"运行中",routinesPaused:"已暂停",routineSchedule:"调度",routineNext:"下次执行",routineProject:"项目",routineDelivers:"投递到",routinePrompt:"提示词",routineScript:"脚本",cronDown:"减小{field}",cronUp:"增大{field}",storySave:"保存",storySaveStart:"保存并启动",storyStart:"启动",attachHintTouch:"粘贴路径 / 截图 → 插入图片占位行"}};var Be={};f(Be,{zh:()=>co,en:()=>lo});var lo={transcript:{you:"You",agent:"Agent",system:"System",copy:"[ copy ]",copied:"Copied",export:"[ export .md ]",findPlaceholder:"Find in this conversation",findCount:"{at} / {total}",findNone:"no match",prev:"[ prev ]",next:"[ next ]",older:"[ load earlier ]",loading:"Reading the conversation…",empty:"Nothing said yet.",failed:"failed",error:"Could not read the conversation: {error}",retry:"[ retry ]",noHistory:"This engine keeps no history Rove can read. Showing its terminal tail.",tail:"Terminal tail"}},co={transcript:{you:"你",agent:"智能体",system:"系统",copy:"[ 复制 ]",copied:"已复制",export:"[ 导出 .md ]",findPlaceholder:"在这段对话中查找",findCount:"{at} / {total}",findNone:"没有匹配",prev:"[ 上一个 ]",next:"[ 下一个 ]",older:"[ 加载更早 ]",loading:"正在读取对话…",empty:"还没有对话内容。",failed:"失败",error:"读不到这段对话:{error}",retry:"[ 重试 ]",noHistory:"这个引擎没有 Rove 能读取的历史记录,显示的是它的终端末尾。",tail:"终端末尾"}};var Ce={};f(Ce,{zh:()=>po,en:()=>mo});var mo={usage:{title:"Usage",open:"Open usage",range:"last {days} days · tokens are input + output",perDay:"Per day",byModel:"By engine and model",topTasks:"Top tasks",quota:"Quota",turns:"{count} turns",loading:"Loading…",empty:"No turns recorded in this window.",failed:"Could not read usage: {error}",cutoff:"The daemon returns at most {count} turns: {day} and earlier are partial.",noReader:"{engines}: no turn reader, so their work is not counted here.",removedTask:"(removed task)",full:"{engine} {label} {percent}% · at this pace full around {time}, before it resets",lasts:"{engine} {label} {percent}% · at this pace it lasts until the reset",window:"{engine} {label} {percent}%"}},po={usage:{title:"用量",open:"打开用量",range:"最近 {days} 天 · token 为输入加输出",perDay:"按天",byModel:"按引擎和模型",topTasks:"用量最高的任务",quota:"额度",turns:"{count} 轮",loading:"加载中…",empty:"这段时间没有记录到任何一轮。",failed:"无法读取用量:{error}",cutoff:"守护进程最多返回 {count} 轮:{day} 及更早的数据不完整。",noReader:"{engines}:没有轮次读取器,这里不统计它们的工作。",removedTask:"(已删除的任务)",full:"{engine} {label} {percent}% · 按当前速度约 {time} 用完,早于重置时间",lasts:"{engine} {label} {percent}% · 按当前速度可撑到重置",window:"{engine} {label} {percent}%"}};var Me={};f(Me,{zh:()=>ko,en:()=>wo});var wo={voice:{say:"Say",stop:"Stop {time}",release:"Release {time}",working:"Hearing…",hint:"Dictate: tap to start and stop, or hold to talk and let go",slideToSend:"Slide up to send",tipSend:"Let go to send",hold:"Hold to talk",type:"Type instead",learned:"Learned {pairs}",learnedPair:"{spoken} → {term}",undo:"undo",unlearned:"Forgotten",unlearnFailed:"Couldn't undo: the voice sidecar isn't answering",setup:{recognizer:"Recognizer",ready:"{model} · ready",starting:"{model} · starting",off:"Not set up: the mic stays hidden until one below is",localTitle:"Local model",localHint:"Whisper large-v3 on this Mac's GPU, about 3.5 GB to download. Audio never leaves the Mac.",install:"Install",installing:"Installing…",installed:"Installed",failed:"Install failed: {line}",use:"Use",inUse:"In use",cloudTitle:"Cloud endpoint",cloudHint:"Any OpenAI-compatible /audio/transcriptions service. The audio and your terms go there; the key stays on the Mac.",baseURL:"Base URL",model:"Model",apiKey:"API key",keySaved:"Saved; leave empty to keep it",save:"Save and use"}}},ko={voice:{say:"说",stop:"停 {time}",release:"松手 {time}",working:"在听…",hint:"口述:点一下开始、再点一下结束,或按住说话、松手结束",slideToSend:"上滑发送",tipSend:"松手发送",hold:"按住说话",type:"打字",learned:"记住了 {pairs}",learnedPair:"{spoken} → {term}",undo:"撤销",unlearned:"已撤销",unlearnFailed:"没能撤销:语音服务没有响应",setup:{recognizer:"识别器",ready:"{model} · 就绪",starting:"{model} · 启动中",off:"未设置:设置好下面任意一种之前,不显示麦克风",localTitle:"本地模型",localHint:"在这台 Mac 的 GPU 上运行 Whisper large-v3,约需下载 3.5 GB。音频不离开这台 Mac。",install:"安装",installing:"安装中…",installed:"已安装",failed:"安装失败:{line}",use:"使用",inUse:"使用中",cloudTitle:"云端服务",cloudHint:"任何兼容 OpenAI /audio/transcriptions 的服务。音频和你的术语会发到那里;密钥只存在这台 Mac 上。",baseURL:"Base URL",model:"模型",apiKey:"API 密钥",keySaved:"已保存;留空则保留原密钥",save:"保存并使用"}}};var Oe={};f(Oe,{zh:()=>fo,en:()=>vo});var vo={workspace:{pickTask:"pick a task on the left",noTabs:"no open sessions in this task",startEngine:"Start {engine}",startSession:"Start session",tabNotLive:"this tab has no running session",restored:"restored session, read-only until reopened",exited:"session ended (exit {code})",attachFailed:"could not open this session: {message}",reopen:"Reopen session",reopening:"Reopening…",reopenFailed:"could not reopen this session: {message}",engineExited:"engine exited (code {code})",retry:"Retry",engineSettings:"Settings → Engines",usageLimit:"usage limit reached",dismiss:"Dismiss"},tab:{shell:"shell",engine:"engine"},firstRun:{title:"Run several agents side by side",body:"Every task gets its own worktree and branch, so sessions never step on each other.",start:"Start your first task"},status:{connecting:"connecting",retrying:"reconnecting, attempt {count}",retry:"retry now",backlog:"Backlog",inProgress:"In progress",inReview:"In review",done:"Done",canceled:"Canceled",error:"Error"},connect:{title:"open the link rove printed",body:"Run `rove --web` and open the link it prints, or paste it here.",placeholder:"http://127.0.0.1:7878/#token=…",submit:"connect",invalid:"that is not a `rove --web` link",failing:"can't reach rove. Is `rove --web` still running? After `--rotate-token`, open the new link.",forget:"forget this link"},relay:{unpaired:"open the relay link that `rove --web --preset relay` printed on your computer",connecting:"connecting to your computer through the relay…",loading:"loading rove…",offline:"your computer is not answering on this relay. Is `rove --web --preset relay` still running? After `--rotate-relay`, open the new link. Reload to retry.",failed:"can't reach your computer through the relay ({error}). Reload to retry."},login:{title:"sign in",body:"Enter the password set with `rove --web --set-password`.",field:"password",submit:"sign in",checking:"checking…",wrong:"that did not work",locked:"too many tries from here; try again in {minutes} min",lockedHost:"password login is locked; run `rove --web --unlock` on the host",unreachable:"can't reach rove. Is `rove --web` still running?"},layout:{splitTooSmall:"Not enough room to split here",railFold:"Fold the task list",navBack:"Back",navForward:"Forward",railUnfold:"Unfold the task list",dropUnsupported:"Only images and PDFs can be dropped into a terminal",pasteNoImage:"No image on this browser's clipboard",pasteNoAccess:"Can't read this browser's clipboard: {error}",pasteNeedsHttps:"the page needs https or localhost",refreshTimeout:"The daemon did not come back on the new build in time; check `rove daemon status`",railResize:"Drag to resize the task list, double-click to reset",composerPlaceholder:"Message the agent",composerSend:"Send",composerAttach:"Attach an image or PDF",composerNotSent:"Not sent: {reason}",composerOldBridge:"This Mac's rove --web is older than this page; restart it to send from here.",composerAtMenu:"The agent is waiting on a choice. Answer it with the 1–4 and ⏎ keys below; your message is kept.",termKeys:"Terminal keys",termKeyUp:"Up arrow",termKeyDown:"Down arrow",termKeyLeft:"Left arrow",termKeyRight:"Right arrow",termKeyInterrupt:"Ctrl+C, interrupt",termKeyEnter:"Enter",narrowTerminal:"Terminal"}},fo={workspace:{pickTask:"从左边选一个任务",noTabs:"这个任务没有打开的会话",startEngine:"启动 {engine}",startSession:"启动会话",tabNotLive:"这个标签没有在运行的会话",restored:"已恢复的会话,重新打开前只读",exited:"会话已结束(退出码 {code})",attachFailed:"打不开这个会话:{message}",reopen:"重新打开会话",reopening:"正在重新打开…",reopenFailed:"重新打开失败:{message}",engineExited:"引擎已退出(退出码 {code})",retry:"重试",engineSettings:"设置 → 引擎",usageLimit:"已达到用量上限",dismiss:"忽略"},tab:{shell:"终端",engine:"引擎"},firstRun:{title:"让多个智能体并排工作",body:"每个任务有独立的 worktree 和分支,会话之间互不干扰。",start:"开始第一个任务"},status:{connecting:"连接中",retrying:"重连中,第 {count} 次",retry:"立即重试",backlog:"待办",inProgress:"进行中",inReview:"待评审",done:"已完成",canceled:"已取消",error:"出错"},connect:{title:"打开 rove 打印的链接",body:"运行 `rove --web`,打开它打印的链接,或者把链接粘贴到这里。",placeholder:"http://127.0.0.1:7878/#token=…",submit:"连接",invalid:"这不是 `rove --web` 打印的链接",failing:"连不上 rove。`rove --web` 还在运行吗?用过 `--rotate-token` 的话,请打开新的链接。",forget:"忘掉这个链接"},relay:{unpaired:"打开电脑上 `rove --web --preset relay` 打印的 relay 链接",connecting:"正在通过 relay 连接你的电脑…",loading:"正在加载 rove…",offline:"你的电脑没有在这个 relay 上应答。`rove --web --preset relay` 还在运行吗?用过 `--rotate-relay` 的话,请打开新的链接。刷新重试。",failed:"无法通过 relay 连到你的电脑({error})。刷新重试。"},login:{title:"登录",body:"输入用 `rove --web --set-password` 设置的密码。",field:"密码",submit:"登录",checking:"验证中…",wrong:"登录失败",locked:"这里尝试次数过多;{minutes} 分钟后再试",lockedHost:"密码登录已锁定;在主机上运行 `rove --web --unlock`",unreachable:"连不上 rove。`rove --web` 还在运行吗?"},layout:{splitTooSmall:"这里放不下分屏了",railFold:"折叠任务列表",navBack:"后退",navForward:"前进",railUnfold:"展开任务列表",dropUnsupported:"终端里只能拖入图片和 PDF",pasteNoImage:"浏览器剪贴板里没有图片",pasteNoAccess:"读不到浏览器剪贴板:{error}",pasteNeedsHttps:"页面要走 https 或 localhost",refreshTimeout:"守护进程没能及时以新版本重新上线,请检查 `rove daemon status`",railResize:"拖动调整任务列表宽度,双击还原",composerPlaceholder:"给 agent 发消息",composerSend:"发送",composerAttach:"附加图片或 PDF",composerNotSent:"没发出去:{reason}",composerOldBridge:"这台 Mac 上的 rove --web 比页面旧,重启它之后才能从这里发送。",composerAtMenu:"agent 正在等你选择。用下方的 1–4 和 ⏎ 键回答;你的消息已保留。",termKeys:"终端按键",termKeyUp:"上方向键",termKeyDown:"下方向键",termKeyLeft:"左方向键",termKeyRight:"右方向键",termKeyInterrupt:"Ctrl+C 中断",termKeyEnter:"回车",narrowTerminal:"终端"}};var rt=[Te,Oe,ve,Re,He,we,be,xe,Ce,Be,fe,pe,Pe,ke,Me],at=Object.assign({},...rt.map((t)=>t.en)),Ro=Object.assign({},...rt.map((t)=>t.zh)),bo={en:at,zh:Ro};function To(){try{let t=JSON.parse(localStorage.getItem("rove.prefs")??"null");if(t&&typeof t==="object"&&"prefs"in t){let o=t.prefs;if(o&&typeof o==="object"&&"locale"in o&&(o.locale==="en"||o.locale==="zh"))return o.locale}}catch{}return globalThis.navigator?.language?.toLowerCase().startsWith("zh")?"zh":"en"}var Ne=st(To());function ht(){let t=typeof document>"u"?void 0:document.documentElement;if(t)t.lang=Ne.get()==="zh"?"zh-CN":"en"}ht();Ne.subscribe(ht);function it(t,o){let s=t;for(let i of o.split(".")){if(!s||typeof s!=="object"||!(i in s))return;s=Reflect.get(s,i)}return typeof s==="string"?s:void 0}function Po(t,o){return o?t.replace(/\{(\w+)\}/g,(s,i)=>String(o[i]??s)):t}function E(t,o){return Po(it(bo[Ne.get()],t)??it(at,t)??t,o)}var xo=new Set([101,204,205,304]);function dt(t){return t.state==="ready"?Promise.resolve():t.connect()}function gt(t){class o{static OPEN=1;readyState=0;onopen=null;onmessage=null;onclose=null;onerror=null;sock=null;constructor(s,i){let h=i.find((p)=>p.startsWith("rove.bearer."))?.slice(12),g=new URL(s,"http://relay").pathname+(h?`?token=${encodeURIComponent(h)}`:"");this.open(g)}async open(s){try{await dt(t);let i=await t.openSocket(s);if(this.readyState===3){i.close();return}this.sock=i,i.onmessage=(h)=>this.onmessage?.({data:typeof h==="string"?h:new TextDecoder().decode(h)}),i.onclose=()=>this.closed(),this.readyState=1,this.onopen?.(new Event("open"))}catch{this.closed()}}send(s){this.sock?.send(s)}close(){this.sock?.close(),this.closed()}closed(){if(this.readyState===3)return;this.readyState=3,this.onclose?.(new Event("close"))}}return{async fetch(s,i={}){await dt(t);let h=typeof i.body==="string"?i.body:void 0,g=await t.fetch(s,{method:i.method,headers:Object.fromEntries(new Headers(i.headers).entries()),body:h});return new Response(xo.has(g.status)?null:g.bytes(),{status:g.status,headers:g.headers})},WebSocket:o}}var ut="rove.relay",Ho=/url\((\/assets\/[^)]+)\)/g;function j(t){let o=document.getElementById("relay-status");if(o)o.textContent=t}function Bo(){let t=new URLSearchParams(location.hash.slice(1)),o=t.get("code");if(o){let s={server:t.get("relay")??location.origin,code:o};localStorage.setItem(ut,JSON.stringify(s));let i=t.get("token");return history.replaceState(null,"",location.pathname+location.search+(i?`#token=${i}`:"")),s}try{let s=JSON.parse(localStorage.getItem(ut)??"null");if(s&&typeof s==="object"&&"server"in s&&"code"in s){if(typeof s.server==="string"&&typeof s.code==="string")return{server:s.server,code:s.code}}}catch{}return null}function Co(t){let o=t instanceof Error?t.message:String(t);if(/\b(4404|1011)\b|timed out|auth/i.test(o))return E("relay.offline");return E("relay.failed",{error:o})}async function lt(t,o,s){let i=await t.fetch(o);if(!i.ok)throw Error(`${o}: HTTP ${i.status}`);return URL.createObjectURL(new Blob([await i.arrayBuffer()],{type:s}))}async function Mo(t){let o=await t.fetch("/"),s=new DOMParser().parseFromString(await o.text(),"text/html"),i=s.querySelector('script[type="module"][src]')?.getAttribute("src");if(!i)throw Error(`no client script in the bridge's page (HTTP ${o.status})`);for(let g of s.querySelectorAll('link[rel="stylesheet"][href]')){let v=await(await t.fetch(g.getAttribute("href")??"")).text(),x=[...new Set([...v.matchAll(Ho)].map((b)=>b[1]??""))],H=await Promise.all(x.map(async(b)=>[b,await lt(t,b,"")]));for(let[b,N]of H)v=v.replaceAll(`url(${b})`,`url(${N})`);let B=document.createElement("style");B.textContent=v,document.head.append(B)}Reflect.set(globalThis,"__roveRelay",t);let h=document.createElement("script");h.type="module",h.src=await lt(t,i,"text/javascript"),document.head.append(h)}async function Oo(){let t=Bo();if(!t){j(E("relay.unpaired"));return}j(E("relay.connecting"));let o=new me({serverUrl:t.server,code:t.code,quiet:!0});try{await o.connect(),j(E("relay.loading")),await Mo(gt(o))}catch(s){j(Co(s))}}Oo();
|
|
32
32
|
</script>
|
|
33
33
|
</body>
|
|
34
34
|
</html>
|