cicy-desktop 2.1.108 → 2.1.110

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.
@@ -0,0 +1,91 @@
1
+ name: Build WSL Package
2
+
3
+ # Bakes a single WSL2 rootfs that already contains Ubuntu 22.04 + Docker Engine
4
+ # + the cicy-code image (pre-loaded into /var/lib/docker), so a Windows install
5
+ # is just: wsl --import <this> → dockerd auto-starts → docker run. No apt, no
6
+ # image pull/load on the customer machine. Uploaded to Aliyun OSS (CN-fast).
7
+
8
+ on:
9
+ workflow_dispatch:
10
+ inputs:
11
+ image:
12
+ description: cicy-code image to embed
13
+ default: cicybot/cicy-code:latest
14
+ oss_key:
15
+ description: OSS object key
16
+ default: rootfs/cicy-wsl-latest.tar.gz
17
+
18
+ jobs:
19
+ build:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Build base rootfs image (Ubuntu + Docker + autostart)
23
+ run: |
24
+ mkdir -p ctx
25
+ # dockerd auto-start on WSL boot: legacy iptables (WSL2 needs it) + detached dockerd.
26
+ cat > ctx/start-dockerd.sh <<'EOS'
27
+ #!/bin/sh
28
+ update-alternatives --set iptables /usr/sbin/iptables-legacy 2>/dev/null || true
29
+ update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy 2>/dev/null || true
30
+ pgrep dockerd >/dev/null 2>&1 || nohup dockerd >/var/log/dockerd.log 2>&1 &
31
+ EOS
32
+ cat > ctx/wsl.conf <<'EOS'
33
+ [boot]
34
+ command = /usr/local/sbin/start-dockerd.sh
35
+ [user]
36
+ default = root
37
+ [automount]
38
+ enabled = true
39
+ EOS
40
+ cat > ctx/Dockerfile <<'EOF'
41
+ FROM ubuntu:22.04
42
+ ENV DEBIAN_FRONTEND=noninteractive
43
+ # The GHA runner is overseas — use the default Ubuntu archive (fast here).
44
+ # The end-user's WSL apt mirror is a separate concern (handled at runtime).
45
+ RUN apt-get update \
46
+ && apt-get install -y --no-install-recommends docker.io ca-certificates iptables iproute2 \
47
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
48
+ RUN update-alternatives --set iptables /usr/sbin/iptables-legacy || true; \
49
+ update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy || true
50
+ COPY start-dockerd.sh /usr/local/sbin/start-dockerd.sh
51
+ COPY wsl.conf /etc/wsl.conf
52
+ RUN chmod +x /usr/local/sbin/start-dockerd.sh
53
+ EOF
54
+ docker build -t cicy-wsl-base ctx
55
+
56
+ - name: Pre-load cicy-code image into the rootfs
57
+ run: |
58
+ docker pull "${{ inputs.image }}"
59
+ docker save "${{ inputs.image }}" -o /tmp/cicy-image.tar
60
+ # Run the base privileged, start an inner dockerd, load the image into
61
+ # its /var/lib/docker (kept in the container fs, NOT a volume), then stop.
62
+ cid=$(docker run -d --privileged -v /tmp/cicy-image.tar:/img.tar:ro cicy-wsl-base \
63
+ bash -c '
64
+ nohup dockerd >/var/log/dind.log 2>&1 &
65
+ for i in $(seq 1 60); do docker info >/dev/null 2>&1 && break; sleep 1; done
66
+ docker load -i /img.tar
67
+ docker images
68
+ pkill dockerd || true
69
+ sleep 4
70
+ ')
71
+ docker logs -f "$cid" || true
72
+ rc=$(docker wait "$cid"); echo "inner exit=$rc"
73
+ # Export the whole container filesystem (incl. /var/lib/docker) = the WSL rootfs.
74
+ docker export "$cid" | gzip > cicy-wsl.tar.gz
75
+ ls -lh cicy-wsl.tar.gz
76
+
77
+ - name: Upload to Aliyun OSS (public-read)
78
+ run: |
79
+ curl -sL https://gosspublic.alicdn.com/ossutil/1.7.18/ossutil64 -o ossutil && chmod +x ossutil
80
+ test -s ossutil && head -c4 ossutil | grep -q ELF || { echo "ossutil download bad"; exit 1; }
81
+ ./ossutil config -e oss-cn-shanghai.aliyuncs.com \
82
+ -i "${{ secrets.OSS_ACCESS_KEY_ID }}" -k "${{ secrets.OSS_ACCESS_KEY_SECRET }}"
83
+ ./ossutil cp cicy-wsl.tar.gz "oss://cicy-1372193042-cn/${{ inputs.oss_key }}" -f --acl public-read
84
+ echo "Uploaded: https://cicy-1372193042-cn.oss-cn-shanghai.aliyuncs.com/${{ inputs.oss_key }}"
85
+
86
+ - name: Upload artifact (backup)
87
+ uses: actions/upload-artifact@v4
88
+ with:
89
+ name: cicy-wsl-package
90
+ path: cicy-wsl.tar.gz
91
+ retention-days: 7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cicy-desktop",
3
- "version": "2.1.108",
3
+ "version": "2.1.110",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -362,4 +362,4 @@ Error generating stack: `+l.message+`
362
362
  > - Backend gating: Section 4 corresponds to the \`~/cicy-ai/mitm/ca-trust-consent\` flag + \`exec cicy-code mitm install-ca/uninstall-ca\`.
363
363
  > - Container/headless deployment: if using the \`CICY_CA_TRUST_CONSENT=1\` escape hatch, "setting this environment variable means the deployer has, on behalf of its environment, given the notice and consent under Section 4.4"; responsibility rests with the deployer and this must be documented.
364
364
  > - Production: zh + en (EU) bilingual; wording of Sections 3C/6/8 may be adjusted per jurisdiction; high-risk authorizations should retain a user-revocable audit log as evidence of consent.
365
- `},w=(o,A)=>{var T,r;try{const D=(r=(T=window.cicyI18n)==null?void 0:T.t)==null?void 0:r.call(T,o);return D&&D!==o?D:A}catch{return A}},Wl="cicy_token",gu="cicy_access_token",vu="cicy_user_id",pu="https://cicy-ai.com";async function Su(o){var A,T,r;try{(r=(T=(A=window.cicy)==null?void 0:A.shell)==null?void 0:T.openExternal)==null||r.call(T,`${pu}/dash${o}`)}catch{}}const Qs=new Set;let Jy=0,Va=[];const Fn=new Map;function nm(){Qs.forEach(o=>o(Va))}const de={show(o={}){const A=o.id||`t${++Jy}`,T=Va.find(q=>q.id===A),r={id:A,status:"running",...T,...o};Va=T?Va.map(q=>q.id===A?r:q):[...Va,r],nm();const D=Fn.get(A);return D&&(clearTimeout(D),Fn.delete(A)),o.ttl&&Fn.set(A,setTimeout(()=>de.dismiss(A),o.ttl)),A},dismiss(o){Va=Va.filter(T=>T.id!==o);const A=Fn.get(o);A&&(clearTimeout(A),Fn.delete(o)),nm()}};function $y(){const[o,A]=N.useState(Va);return N.useEffect(()=>(Qs.add(A),()=>{Qs.delete(A)}),[]),o.length?c.jsx("div",{className:"toast-host","data-id":"ToastHost",children:o.map(T=>c.jsxs("div",{className:"toast","data-id":`Toast-${T.id}`,"data-status":T.status||"running",children:[c.jsx("button",{type:"button",className:"toast__x","data-id":"Toast-dismiss",onClick:()=>de.dismiss(T.id),"aria-label":"dismiss",children:"×"}),c.jsxs("span",{className:"toast__msg",children:[T.message,Number.isFinite(T.progress)?` ${T.progress}%`:""]}),Number.isFinite(T.progress)&&c.jsx("span",{className:"toast__bar",children:c.jsx("span",{style:{width:`${Math.min(100,T.progress)}%`}})})]},T.id))}):null}const Xs=new Set;let im=0,Wt=null;function Fl(){Xs.forEach(o=>o(Wt))}function bu(){return new Date().toTimeString().slice(0,8)}const ea={open({teamId:o,fromVer:A,toVer:T,onRetry:r}={}){Wt={teamId:o,fromVer:A||null,toVer:T||null,status:"running",phase:"download",logs:[],onRetry:r||null,lastAt:Date.now()},Fl()},push(o={}){if(!Wt)return;const A={id:++im,t:bu(),phase:o.phase||Wt.phase,status:o.status||"running",message:o.message||""};Wt={...Wt,phase:o.phase||Wt.phase,toVer:o.toVer||Wt.toVer,logs:[...Wt.logs,A],lastAt:Date.now()},Fl()},minimize(){Wt&&(Wt={...Wt,minimized:!0},Fl())},restore(){Wt&&(Wt={...Wt,minimized:!1},Fl())},finish({ok:o,message:A}={}){if(!Wt)return;const T=o?"done":"error",r={id:++im,t:bu(),phase:"done",status:T,message:A||(o?"更新完成":"更新失败")};Wt={...Wt,status:T,phase:"done",minimized:!1,logs:[...Wt.logs,r],lastAt:Date.now()},Fl()},close(){Wt=null,Fl()}},Ys=[["download","下载"],["swap","切换"],["done","完成"]];function Wy(){var R;const[o,A]=N.useState(Wt);N.useEffect(()=>(Xs.add(A),()=>{Xs.delete(A)}),[]);const T=N.useRef(null);N.useEffect(()=>{const C=T.current;C&&(C.scrollTop=C.scrollHeight)},[(R=o==null?void 0:o.logs)==null?void 0:R.length]);const[r,D]=N.useState(!1);if(N.useEffect(()=>{if(!o||o.status!=="running"){D(!1);return}const C=setInterval(()=>D(Date.now()-(o.lastAt||0)>25e3),3e3);return()=>clearInterval(C)},[o==null?void 0:o.lastAt,o==null?void 0:o.status]),!o)return null;const q=o.status==="running",V=Ys.findIndex(([C])=>C===o.phase);return o.minimized?c.jsxs("button",{type:"button",className:`drawer-min drawer-min--${o.status}`,"data-id":"UpdateDrawer-restore",onClick:()=>ea.restore(),children:[c.jsx("span",{className:"drawer-min__spark",children:q?c.jsx(ge,{}):o.status==="done"?"✓":o.status==="reboot"?"⟳":"!"}),c.jsxs("span",{className:"drawer-min__label",children:["更新 cicy-code",o.toVer?` · v${o.toVer}`:""]})]}):c.jsx("div",{className:"drawer-scrim","data-id":"UpdateDrawer-scrim",onClick:()=>q?ea.minimize():ea.close(),children:c.jsxs("div",{className:"drawer","data-id":"UpdateDrawer","data-status":o.status,onClick:C=>C.stopPropagation(),children:[c.jsxs("div",{className:"drawer__head",children:[c.jsxs("div",{className:"drawer__title",children:[c.jsx("span",{className:`drawer__spark drawer__spark--${o.status}`,children:q?c.jsx(ge,{}):o.status==="done"?"✓":o.status==="reboot"?"⟳":"!"}),c.jsxs("div",{children:[c.jsx("div",{className:"drawer__h",children:"更新 cicy-code"}),c.jsxs("div",{className:"drawer__sub",children:[o.fromVer?`v${o.fromVer}`:"当前"," → ",o.toVer?`v${o.toVer}`:"最新版"]})]})]}),c.jsx("div",{className:"drawer__headbtns",children:c.jsx("button",{type:"button",className:"drawer__x","data-id":"UpdateDrawer-min",title:"最小化",onClick:()=>ea.minimize(),"aria-label":"minimize",children:"‒"})})]}),c.jsx("div",{className:"drawer__steps","data-id":"UpdateDrawer-steps",children:Ys.map(([C,g],Y)=>{const H=o.status==="done"||Y<V,lt=Y===V&&q,J=o.status==="error"&&Y===V;return c.jsxs("div",{className:`drawer__step${lt?" is-active":""}${H?" is-done":""}${J?" is-error":""}`,children:[c.jsx("span",{className:"drawer__step-dot",children:H?"✓":J?"!":Y+1}),c.jsx("span",{className:"drawer__step-label",children:g}),Y<Ys.length-1&&c.jsx("span",{className:"drawer__step-bar"})]},C)})}),c.jsx("div",{className:"drawer__log","data-id":"UpdateDrawer-log",ref:T,children:o.logs.length===0?c.jsx("div",{className:"drawer__log-empty",children:"准备中…"}):o.logs.map(C=>c.jsxs("div",{className:"drawer__line","data-status":C.status,children:[c.jsx("span",{className:"drawer__t",children:C.t}),c.jsx("span",{className:`drawer__badge drawer__badge--${C.phase}`,children:{download:"下载",swap:"切换",done:"完成"}[C.phase]||C.phase}),c.jsx("span",{className:"drawer__linemsg",children:C.message})]},C.id))}),r&&q&&c.jsx("div",{className:"drawer__hint","data-id":"UpdateDrawer-stuck",children:"正在等待新版本就绪,耗时比平常久。可以放到后台继续,完成或失败都会提示。"}),c.jsx("div",{className:"drawer__foot",children:q?c.jsx(c.Fragment,{children:c.jsx("span",{className:"drawer__foot-status",children:"更新进行中…"})}):o.status==="error"?c.jsxs(c.Fragment,{children:[c.jsx("span",{className:"drawer__foot-status is-error",children:"更新失败"}),o.onRetry&&c.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"UpdateDrawer-retry",onClick:()=>o.onRetry(),children:"重试"}),c.jsx("button",{type:"button",className:"drawer__btn","data-id":"UpdateDrawer-dismiss",onClick:()=>ea.close(),children:"关闭"})]}):c.jsxs(c.Fragment,{children:[c.jsx("span",{className:"drawer__foot-status is-done",children:"已更新到最新"}),c.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"UpdateDrawer-finish",onClick:()=>ea.close(),children:"完成"})]})})]})})}function Fy(){const[o,A]=N.useState(void 0);N.useEffect(()=>{var B,at;if(!((at=(B=window.cicy)==null?void 0:B.terms)!=null&&at.status)){A(!0);return}window.cicy.terms.status(vm).then(x=>A(!!(x!=null&&x.accepted))).catch(()=>A(!0))},[]),N.useEffect(()=>{var x,K,L,et;const B=document.documentElement;try{B.setAttribute("data-platform",((x=window.cicy)==null?void 0:x.platform)||"linux")}catch{}B.setAttribute("data-fullscreen","0");let at;try{at=(et=(L=(K=window.cicy)==null?void 0:K.window)==null?void 0:L.onFullscreen)==null?void 0:et.call(L,Zt=>B.setAttribute("data-fullscreen",Zt?"1":"0"))}catch{}return()=>{try{at&&at()}catch{}}},[]);const[T,r]=N.useState(()=>In(Wl)),[D,q]=N.useState(()=>In(gu)),[V,R]=N.useState(()=>In(vu)),[C,g]=N.useState(()=>!In(Wl)),[Y,H]=N.useState(!1),[lt,J]=N.useState(""),[I,W]=N.useState(""),[it,Z]=N.useState(null),[k,P]=N.useState(null),[dt,ot]=N.useState(!1),[X,M]=N.useState(""),pt=N.useRef(!1),[U,gt]=N.useState(null),[qt,Lt]=N.useState(!1),[Yt,me]=N.useState(!1),[ut,S]=N.useState("all"),O=N.useCallback(async(B,at)=>{var K,L,et,Zt;if(!B)return;if(!((L=(K=window.cicy)==null?void 0:K.cloud)!=null&&L.fetch)){M("cloud fetch bridge missing");return}ot(!0),M("");const x={Authorization:`Bearer ${B}`};try{const[ue,Xt]=await Promise.all([window.cicy.cloud.fetch(`${pu}/api/user/self`,{headers:x}),window.cicy.cloud.fetch(`${pu}/api/teams`,{headers:x})]);if((Xt==null?void 0:Xt.status)===401){if(!pt.current&&((Zt=(et=window.cicy)==null?void 0:et.auth)!=null&&Zt.loginStart)){pt.current=!0,M("会话已过期,正在重新登录…");try{await window.cicy.auth.loginStart()}catch{}}return}if(!(Xt!=null&&Xt.ok))throw new Error(`/api/teams ${(Xt==null?void 0:Xt.status)||"?"} ${(Xt==null?void 0:Xt.error)||""}`);const kt=JSON.parse(Xt.body||"{}");if(P(Array.isArray(kt==null?void 0:kt.teams)?kt.teams:[]),ue!=null&&ue.ok)try{const Ae=JSON.parse(ue.body||"{}");Z((Ae==null?void 0:Ae.success)===!1?null:(Ae==null?void 0:Ae.data)||null)}catch{Z(null)}else Z(null)}catch(ue){M(ue.message||String(ue))}finally{ot(!1)}},[]),Q=N.useRef("");N.useEffect(()=>{Q.current=T||D||""},[T,D]);const mt=N.useCallback(async()=>{var at,x;const B=Q.current;if(!(!B||!((x=(at=window.cicy)==null?void 0:at.cloud)!=null&&x.fetch)))try{const K=await window.cicy.cloud.fetch(`${pu}/api/teams`,{headers:{Authorization:`Bearer ${B}`}});if(K!=null&&K.ok){const L=JSON.parse(K.body||"{}");Array.isArray(L==null?void 0:L.teams)&&P(L.teams)}}catch{}},[]);N.useEffect(()=>{const B=T||D;B&&O(B,V)},[T,D,V,O]);const st=N.useCallback(async()=>{var B,at;if((at=(B=window.cicy)==null?void 0:B.localTeams)!=null&&at.list){Lt(!0);try{const x=await window.cicy.localTeams.list({refresh:!0});gt(Array.isArray(x)?x:[])}catch{gt([])}finally{Lt(!1),me(!0)}}},[]),d=N.useCallback(async(B,at)=>{var K,L;if(!((L=(K=window.cicy)==null?void 0:K.localTeams)!=null&&L.update))return{ok:!1,error:"no_bridge"};let x;try{x=await window.cicy.localTeams.update(B,{name:String(at||"").trim()||w("localTeams.unnamed","未命名")})}catch(et){x={ok:!1,error:(et==null?void 0:et.message)||String(et)}}return await st(),x||{ok:!1,error:"no_result"}},[st]);N.useEffect(()=>{let B,at=!1;const x=3e3,K=3e4,L=async()=>{var ue,Xt,kt;try{await((kt=(Xt=(ue=window.cicy)==null?void 0:ue.localTeams)==null?void 0:Xt.syncCloud)==null?void 0:kt.call(Xt))}catch{}await Promise.all([st(),mt()])},et=()=>{if(at)return;const ue=document.visibilityState==="visible";B=setTimeout(async()=>{document.visibilityState==="visible"?await L():await st(),et()},ue?x:K)};L(),et();const Zt=()=>{document.visibilityState==="visible"&&L()};return document.addEventListener("visibilitychange",Zt),window.addEventListener("focus",Zt),()=>{at=!0,clearTimeout(B),document.removeEventListener("visibilitychange",Zt),window.removeEventListener("focus",Zt)}},[st,mt]),N.useEffect(()=>{var at,x;if(!((x=(at=window.cicy)==null?void 0:at.localTeams)!=null&&x.onWebviewRelay))return;const B=window.cicy.localTeams.onWebviewRelay(async({reqId:K,msg:L})=>{let et={ok:!1,error:"unknown relay type"};try{(L==null?void 0:L.type)==="localTeams:add"?et=await window.cicy.localTeams.add(L.spec||{}):(L==null?void 0:L.type)==="localTeams:remove"?et=await window.cicy.localTeams.remove(L.id):(L==null?void 0:L.type)==="localTeams:update"?et=await window.cicy.localTeams.update(L.id,L.patch||{}):(L==null?void 0:L.type)==="localTeams:upgrade"?et=await window.cicy.localTeams.upgrade(L.id):(L==null?void 0:L.type)==="localTeams:list"&&(et={ok:!0,teams:await window.cicy.localTeams.list({refresh:!0})}),st()}catch(Zt){et={ok:!1,error:(Zt==null?void 0:Zt.message)||String(Zt)}}try{window.cicy.localTeams.replyWebviewRelay(K,et)}catch{}});return()=>{try{B==null||B()}catch{}}},[st]);const y=N.useCallback(async B=>{var at,x;if((x=(at=window.cicy)==null?void 0:at.localTeams)!=null&&x.open)try{await window.cicy.localTeams.open(B)}catch{}},[]);N.useEffect(()=>{var at,x;if(In(Wl)){g(!1);return}if(!((x=(at=window.cicy)==null?void 0:at.auth)!=null&&x.getSaved)){g(!1);return}let B=!1;return(async()=>{try{const K=await window.cicy.auth.getSaved();if(B)return;if(K!=null&&K.token){try{localStorage.setItem(Wl,K.token)}catch{}if(r(K.token),K.accessToken){try{localStorage.setItem(gu,K.accessToken)}catch{}q(K.accessToken)}if(K.userId){try{localStorage.setItem(vu,String(K.userId))}catch{}R(String(K.userId))}}}catch{}finally{B||g(!1)}})(),()=>{B=!0}},[]),N.useEffect(()=>{var B,at;if((at=(B=window.cicy)==null?void 0:B.auth)!=null&&at.onComplete)return window.cicy.auth.onComplete(x=>{if(H(!1),x!=null&&x.error){J(dm(x.error));return}if(x!=null&&x.token){pt.current=!1,M("");try{localStorage.setItem(Wl,x.token)}catch{}if(r(x.token),x.accessToken){try{localStorage.setItem(gu,x.accessToken)}catch{}q(x.accessToken)}if(x.userId){try{localStorage.setItem(vu,String(x.userId))}catch{}R(String(x.userId))}J(""),W(x.reused?"已恢复你之前的登录":"登录成功"),setTimeout(()=>W(""),3e3)}})},[]);async function G(){var at,x;if(!((x=(at=window.cicy)==null?void 0:at.auth)!=null&&x.loginStart)){J("auth bridge missing");return}J(""),H(!0);const B=await window.cicy.auth.loginStart();B!=null&&B.ok||(H(!1),J(dm((B==null?void 0:B.error)||"login start failed")))}function $(){var B,at,x;try{localStorage.removeItem(Wl),localStorage.removeItem(gu),localStorage.removeItem(vu)}catch{}try{(x=(at=(B=window.cicy)==null?void 0:B.auth)==null?void 0:at.logout)==null||x.call(at)}catch{}r(null),q(null),R(null),Z(null),P(null),J(""),M("")}if(o===void 0)return c.jsxs("div",{className:"shell","data-id":"TermsCheckingSplash",children:[c.jsx("div",{className:"glow","aria-hidden":!0}),c.jsxs("div",{className:"card",children:[c.jsx(Gs,{}),c.jsx("div",{className:"spinner-row",children:c.jsx(ge,{})})]})]});if(!o)return c.jsx(pm,{onAgree:()=>A(!0)});if(!T&&C)return c.jsxs("div",{className:"shell","data-id":"AuthRestoringSplash",children:[c.jsx("div",{className:"glow","aria-hidden":!0}),c.jsxs("div",{className:"card",children:[c.jsx(Gs,{}),c.jsxs("div",{className:"spinner-row",children:[c.jsx(ge,{}),c.jsx("span",{children:"正在恢复登录…"})]})]})]});if(!T)return c.jsxs("div",{className:"shell",children:[c.jsx("div",{className:"glow","aria-hidden":!0}),c.jsxs("div",{className:"card",children:[c.jsx(Gs,{}),!Y&&c.jsxs(c.Fragment,{children:[c.jsx("p",{className:"tagline",children:"登录以同步你的团队、配置与 AI 助手"}),c.jsxs("button",{className:"btn-primary",onClick:G,children:[c.jsx("span",{children:"使用浏览器登录"}),c.jsx(_u,{})]}),c.jsx("p",{className:"hint",children:"点击后会自动打开浏览器"})]}),Y&&c.jsxs(c.Fragment,{children:[c.jsx("p",{className:"tagline",children:"已在浏览器打开登录页,等待你完成…"}),c.jsxs("div",{className:"spinner-row",children:[c.jsx(ge,{}),c.jsx("span",{children:"等待回调"})]}),c.jsx("button",{className:"btn-ghost",onClick:()=>{var B,at,x;(x=(at=(B=window.cicy)==null?void 0:B.auth)==null?void 0:at.loginCancel)==null||x.call(at),H(!1)},children:"取消"})]}),lt&&c.jsx("div",{className:"error",children:lt})]})]});const ft=(U||[]).find(B=>om(B.base_url))||null,rt=(U||[]).filter(B=>Vs(B.base_url)),Tt=(U||[]).filter(B=>!Vs(B.base_url)&&!om(B.base_url)),Rt=rt.length,Et=Tt.length,Re=(k||[]).filter(B=>!B.is_local&&B.kind!=="local"),aa=Re.length,We=ut==="all"||ut==="local",Ka=ut==="all"||ut==="custom",He=ut==="all"||ut==="cloud";return c.jsxs("div",{className:"shell shell--app",children:[c.jsx("div",{className:"glow glow--app","aria-hidden":!0}),c.jsxs("div",{className:"shell__left",children:[c.jsx(t0,{me:it,welcome:I,onLogout:$,mitmTeam:rt.length>0?rt[0]:null}),c.jsxs("main",{className:"main",children:[c.jsxs("div",{className:"app__tabsrow",children:[c.jsx("div",{className:"app__tabs",children:[{k:"all",label:"全部",n:Rt+Et+aa},{k:"local",label:"本地",n:Rt},{k:"cloud",label:"私有云",n:aa},{k:"custom",label:"自定义",n:Et}].map(({k:B,label:at,n:x})=>c.jsxs("button",{type:"button",className:`app__tab ${ut===B?"is-active":""}`,onClick:()=>S(B),children:[at,c.jsx("span",{className:"app__tab-count",children:x})]},B))}),c.jsxs("button",{type:"button","data-id":"AddTeamButton",className:"app__add-team",title:w("teams.addHint","在云端新建私有云团队"),onClick:()=>Su("?tab=private"),children:["+ ",w("teams.add","新加团队")]})]}),X&&c.jsxs("div",{className:"error",style:{marginBottom:12},children:["云端: ",X,c.jsx("button",{className:"btn-ghost",style:{marginLeft:8},onClick:()=>O(T||D,V),children:"重试"})]}),c.jsxs("div",{className:"app__grid",children:[We&&rt.map(B=>c.jsx(sm,{team:B,onOpen:()=>y(B.id),onRename:d,onRefresh:st},"local:"+B.id)),We&&rt.length===0&&c.jsxs("div",{"data-id":"LocalTeamPlaceholder",className:"bcard bcard--local",children:[c.jsx("div",{className:"bcard__accent"}),c.jsx("div",{className:"bcard__top",children:c.jsxs("div",{className:"bcard__pill",children:[c.jsx("span",{className:"bcard__dot","data-tone":"warn"}),c.jsx(Sm,{})]})}),c.jsxs("div",{className:"bcard__body",children:[c.jsx("h3",{className:"bcard__name",children:"本地团队"}),c.jsx("div",{className:"bcard__host",children:"http://127.0.0.1:8008"}),c.jsx("div",{className:"bcard__meta"})]}),c.jsxs("button",{type:"button",className:"bcard__cta",disabled:!0,children:[c.jsx(ge,{}),c.jsx("span",{children:Yt?"正在启动,就绪后自动加入…":"检测中…"})]})]}),We&&c.jsx(u0,{dockerTeam:ft,onOpen:B=>{var at,x,K;B?y(B):(K=(x=(at=window.cicy)==null?void 0:at.tabs)==null?void 0:x.open)==null||K.call(x,"http://127.0.0.1:8009","Docker cicy-code")},onRefresh:st}),Ka&&Tt.map(B=>c.jsx(sm,{team:B,onOpen:()=>y(B.id),onRename:d,onRefresh:st},"custom:"+B.id)),He&&Re.map(B=>c.jsx(c0,{team:B,onOpen:()=>{var x,K,L;const at=B.kind==="private"?B.host_url:B.workspace_url||B.workspace_direct_url;at&&((L=(K=(x=window.cicy)==null?void 0:x.tabs)==null?void 0:K.open)==null||L.call(K,at,B.name||B.title||""))}},"cloud:"+B.id))]}),!dt&&!X&&k&&k.length===0&&!(U!=null&&U.length)&&c.jsx("div",{className:"empty",style:{marginTop:14},children:"还没有团队 — 安装本地 cicy-code 起一个本地 team,或在云端创建。"})]})]}),c.jsx($y,{}),c.jsx(Wy,{}),c.jsx(i0,{})]})}function Iy({onClose:o}){const[A,T]=N.useState(null),[r,D]=N.useState(""),[q,V]=N.useState(!1),[R,C]=N.useState(""),g=typeof window<"u"&&window.cicy&&window.cicy.trustedOrigins||null,Y=N.useCallback(async()=>{try{T(g&&await g.list()||[])}catch{T([])}},[g]);N.useEffect(()=>{Y()},[Y]);const H=async()=>{const I=r.trim();if(!(!I||q||!g)){V(!0),C("");try{const W=await g.add(I);W&&W.ok===!1?C(W.error||w("trustedSites.addFailed","添加失败")):(D(""),T(W&&W.origins||await g.list()))}catch(W){C(String(W&&W.message||W))}finally{V(!1)}}},lt=async I=>{if(!(q||!g)){V(!0),C("");try{const W=await g.remove(I);W&&W.ok===!1?C(W.error||w("trustedSites.removeFailed","删除失败")):T(W&&W.origins||await g.list())}catch(W){C(String(W&&W.message||W))}finally{V(!1)}}},J={overlay:{position:"fixed",inset:0,zIndex:2e3,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(0,0,0,.62)",backdropFilter:"blur(3px)"},card:{width:560,maxWidth:"94vw",maxHeight:"82vh",display:"flex",flexDirection:"column",background:"#101012",border:"1px solid rgba(255,255,255,.09)",borderRadius:16,boxShadow:"0 24px 64px rgba(0,0,0,.55)",overflow:"hidden",color:"#e4e4e7"},head:{display:"flex",alignItems:"center",gap:8,padding:"14px 16px",borderBottom:"1px solid rgba(255,255,255,.06)"},title:{margin:0,fontSize:15,fontWeight:600,flex:1},x:{background:"transparent",border:"none",color:"#a1a1aa",fontSize:16,cursor:"pointer",lineHeight:1,padding:4},warn:{margin:"14px 16px 0",padding:"10px 12px",fontSize:12.5,lineHeight:1.55,color:"#fca5a5",background:"rgba(239,68,68,.08)",border:"1px solid rgba(239,68,68,.25)",borderRadius:10},addRow:{display:"flex",gap:8,padding:"12px 16px 4px"},input:{flex:1,minWidth:0,background:"#161618",border:"1px solid rgba(255,255,255,.1)",borderRadius:9,padding:"9px 11px",color:"#e4e4e7",fontSize:13,outline:"none"},addBtn:{background:"rgba(255,255,255,.1)",border:"none",borderRadius:9,padding:"0 16px",color:"#fff",fontSize:13,fontWeight:500,cursor:"pointer"},err:{margin:"6px 16px 0",fontSize:12,color:"#fca5a5"},listWrap:{margin:"10px 16px 16px",border:"1px solid rgba(255,255,255,.07)",borderRadius:10,overflow:"auto",flex:1,minHeight:80},row:{display:"flex",alignItems:"center",gap:10,padding:"9px 12px",borderTop:"1px solid rgba(255,255,255,.05)"},host:I=>({flex:1,fontFamily:"ui-monospace,SFMono-Regular,Menlo,monospace",fontSize:13,color:I?"#71717a":"#e4e4e7",wordBreak:"break-all"}),tag:{fontSize:11,color:"#71717a",background:"rgba(255,255,255,.05)",borderRadius:6,padding:"2px 7px"},rm:{background:"transparent",border:"none",color:"#a1a1aa",fontSize:12,cursor:"pointer",padding:"3px 6px",borderRadius:6},muted:{padding:"16px",textAlign:"center",color:"#71717a",fontSize:12.5}};return Pl.createPortal(c.jsx("div",{style:J.overlay,onClick:o,"data-id":"TrustedSitesModal",children:c.jsxs("div",{style:J.card,onClick:I=>I.stopPropagation(),children:[c.jsxs("div",{style:J.head,children:[c.jsx("h2",{style:J.title,children:w("trustedSites.title","受信任站点")}),c.jsx("button",{type:"button",style:J.x,onClick:o,"aria-label":"close",children:"✕"})]}),c.jsx("div",{style:J.warn,children:w("trustedSites.warn","⚠ 列表中的站点可以在你的电脑上执行命令(exec)。只添加你完全信任的地址。")}),c.jsxs("div",{style:J.addRow,children:[c.jsx("input",{"data-id":"trusted-sites-input",style:J.input,value:r,onChange:I=>D(I.target.value),onKeyDown:I=>{I.key==="Enter"&&H()},placeholder:w("trustedSites.placeholder","添加站点,如 app.cicy-ai.com 或 my-cloud.example.org")}),c.jsx("button",{type:"button","data-id":"trusted-sites-add",style:{...J.addBtn,opacity:q||!r.trim()?.5:1},onClick:H,disabled:q||!r.trim(),children:w("trustedSites.add","添加")})]}),R&&c.jsx("div",{style:J.err,children:R}),c.jsx("div",{style:J.listWrap,children:A===null?c.jsx("div",{style:J.muted,children:w("trustedSites.loading","加载中…")}):A.length===0?c.jsx("div",{style:J.muted,children:w("trustedSites.empty","暂无")}):A.map(I=>c.jsxs("div",{style:J.row,"data-id":"trusted-sites-row",children:[c.jsx("span",{style:J.host(I.builtin),children:I.host}),I.builtin?c.jsx("span",{style:J.tag,children:w("trustedSites.builtin","系统")}):c.jsx("button",{type:"button",style:J.rm,onClick:()=>lt(I.host),disabled:q,children:w("trustedSites.remove","删除")})]},I.host))})]})}),document.body)}function Py({onClose:o}){const[A,T]=N.useState(null),[r,D]=N.useState(""),[q,V]=N.useState(""),[R,C]=N.useState(!1),g=typeof window<"u"&&window.cicy&&window.cicy.rpcAudit||null,Y=N.useCallback(async()=>{C(!0),D("");try{const U=g&&await g.tail(400);!U||U.ok===!1?(D(U&&U.error||w("audit.loadFailed","读取失败")),T([])):(T(U.entries||[]),V(U.path||""))}catch(U){D(String(U&&U.message||U)),T([])}finally{C(!1)}},[g]);N.useEffect(()=>{Y()},[Y]);const[H,lt]=N.useState("all"),[J,I]=N.useState(""),W=U=>{try{return new Date(U).toLocaleString()}catch{return U||""}},it=U=>{if(U.kind==="auth"){const gt=/deny/.test(U.decision||"");return{text:U.decision||"auth",color:gt?"#fca5a5":"#86efac",bg:gt?"rgba(239,68,68,.14)":"rgba(34,197,94,.14)"}}if(U.kind==="rpc"){const gt=U.ok!==!1&&!U.error;return{text:gt?"ok":"err",color:gt?"#86efac":"#fca5a5",bg:gt?"rgba(34,197,94,.14)":"rgba(239,68,68,.14)"}}return{text:U.kind||"log",color:"#a1a1aa",bg:"rgba(255,255,255,.06)"}},Z=U=>U.kind==="auth"?`${U.gate||""}${U.decision?" · "+U.decision:""}`:U.kind==="rpc"?`${U.tool||""}${U.dangerous?" ⚠":""}`:U.kind||"",k=U=>U.error||U.args||(U.kind==="rpc"?U.channel:"")||"",P=A||[],dt=J.trim().toLowerCase(),ot=P.filter(U=>H!=="all"&&U.kind!==H?!1:dt?[U.origin,U.host,U.tool,U.gate,U.decision,U.channel,U.args,U.error,U.kind].filter(Boolean).join(" ").toLowerCase().includes(dt):!0),X="186px 104px minmax(160px,1.1fr) minmax(150px,1.1fr) minmax(220px,1.8fr)",M={overlay:{position:"fixed",inset:0,zIndex:2e3,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(0,0,0,.66)",backdropFilter:"blur(4px)"},card:{width:"96vw",height:"92vh",maxWidth:1480,display:"flex",flexDirection:"column",background:"#0d0d0f",border:"1px solid rgba(255,255,255,.09)",borderRadius:18,boxShadow:"0 32px 80px rgba(0,0,0,.6)",overflow:"hidden",color:"#e4e4e7"},head:{display:"flex",alignItems:"center",gap:14,padding:"20px 24px",borderBottom:"1px solid rgba(255,255,255,.07)"},titleWrap:{flex:1,minWidth:0},title:{margin:0,fontSize:21,fontWeight:650,letterSpacing:.2},subtitle:{margin:"3px 0 0",fontSize:12.5,color:"#71717a",fontFamily:"ui-monospace,SFMono-Regular,Menlo,monospace",wordBreak:"break-all"},count:{fontSize:12.5,color:"#a1a1aa",whiteSpace:"nowrap"},refresh:{background:"rgba(255,255,255,.1)",border:"none",borderRadius:9,padding:"9px 16px",color:"#fff",fontSize:13,fontWeight:500,cursor:"pointer"},x:{background:"transparent",border:"none",color:"#a1a1aa",fontSize:20,cursor:"pointer",lineHeight:1,padding:6},toolbar:{display:"flex",alignItems:"center",gap:10,padding:"14px 24px",borderBottom:"1px solid rgba(255,255,255,.05)"},chips:{display:"flex",gap:6},chip:U=>({background:U?"rgba(255,255,255,.14)":"transparent",border:"1px solid rgba(255,255,255,.12)",borderRadius:999,padding:"6px 16px",color:U?"#fff":"#a1a1aa",fontSize:13,cursor:"pointer"}),search:{flex:1,minWidth:0,background:"#161618",border:"1px solid rgba(255,255,255,.1)",borderRadius:10,padding:"10px 14px",color:"#e4e4e7",fontSize:13.5,outline:"none"},err:{margin:"10px 24px 0",fontSize:12.5,color:"#fca5a5"},tableWrap:{flex:1,overflow:"auto",margin:"0"},theadRow:{position:"sticky",top:0,zIndex:1,display:"grid",gridTemplateColumns:X,gap:16,padding:"12px 24px",background:"#141417",borderBottom:"1px solid rgba(255,255,255,.08)",fontSize:11.5,letterSpacing:.6,textTransform:"uppercase",color:"#71717a",fontWeight:600},row:{display:"grid",gridTemplateColumns:X,gap:16,padding:"13px 24px",borderBottom:"1px solid rgba(255,255,255,.045)",alignItems:"center"},time:{fontSize:12.5,color:"#a1a1aa",fontFamily:"ui-monospace,SFMono-Regular,Menlo,monospace",whiteSpace:"nowrap"},badge:U=>({justifySelf:"start",fontSize:11.5,color:U.color,background:U.bg,borderRadius:7,padding:"3px 10px",whiteSpace:"nowrap",fontWeight:500}),cell:{fontSize:13,color:"#d4d4d8",fontFamily:"ui-monospace,SFMono-Regular,Menlo,monospace",wordBreak:"break-all"},detail:{fontSize:12.5,color:"#8b8b93",wordBreak:"break-all"},muted:{padding:"60px 24px",textAlign:"center",color:"#71717a",fontSize:14}},pt=U=>c.jsx("div",{children:U});return Pl.createPortal(c.jsx("div",{style:M.overlay,onClick:o,"data-id":"AuditLogModal",children:c.jsxs("div",{style:M.card,onClick:U=>U.stopPropagation(),children:[c.jsxs("div",{style:M.head,children:[c.jsxs("div",{style:M.titleWrap,children:[c.jsx("h2",{style:M.title,children:w("audit.title","审计日志")}),q&&c.jsx("p",{style:M.subtitle,children:q})]}),c.jsxs("span",{style:M.count,children:[w("audit.count","共")," ",ot.length,H!=="all"||dt?` / ${P.length}`:""]}),c.jsx("button",{type:"button","data-id":"audit-refresh",style:{...M.refresh,opacity:R?.5:1},onClick:Y,disabled:R,children:w("audit.refresh","刷新")}),c.jsx("button",{type:"button",style:M.x,onClick:o,"aria-label":"close",children:"✕"})]}),c.jsxs("div",{style:M.toolbar,children:[c.jsxs("div",{style:M.chips,children:[c.jsx("button",{type:"button",style:M.chip(H==="all"),onClick:()=>lt("all"),children:w("audit.all","全部")}),c.jsx("button",{type:"button",style:M.chip(H==="rpc"),onClick:()=>lt("rpc"),children:w("audit.rpc","RPC 调用")}),c.jsx("button",{type:"button",style:M.chip(H==="auth"),onClick:()=>lt("auth"),children:w("audit.auth","授权决定")})]}),c.jsx("input",{"data-id":"audit-search",style:M.search,value:J,onChange:U=>I(U.target.value),placeholder:w("audit.search","搜索来源 / 工具 / 命令…")})]}),r&&c.jsx("div",{style:M.err,children:r}),c.jsxs("div",{style:M.tableWrap,children:[c.jsxs("div",{style:M.theadRow,children:[pt(w("audit.colTime","时间")),pt(w("audit.colType","类型")),pt(w("audit.colSource","来源")),pt(w("audit.colOp","操作")),pt(w("audit.colDetail","详情"))]}),A===null?c.jsx("div",{style:M.muted,children:w("audit.loading","加载中…")}):ot.length===0?c.jsx("div",{style:M.muted,children:P.length===0?w("audit.empty","暂无审计记录"):w("audit.noMatch","无匹配记录")}):ot.map((U,gt)=>{const qt=it(U);return c.jsxs("div",{style:M.row,"data-id":"audit-row",children:[c.jsx("span",{style:M.time,children:W(U.ts)}),c.jsx("span",{style:M.badge(qt),children:qt.text}),c.jsx("span",{style:M.cell,children:U.origin||U.host||"—"}),c.jsx("span",{style:M.cell,children:Z(U)||"—"}),c.jsx("span",{style:M.detail,children:k(U)||"—"})]},gt)})]})]})}),document.body)}function t0({me:o,welcome:A,onLogout:T,mitmTeam:r}){const D=(o==null?void 0:o.display_name)||(o==null?void 0:o.username)||"…",q=D.slice(0,1).toUpperCase(),[V,R]=N.useState(!1),[C,g]=N.useState(!1),[Y,H]=N.useState(!1),[lt,J]=N.useState(!1),[I,W]=N.useState(""),it=N.useRef(null);N.useEffect(()=>{var P,dt,ot;let k=!0;return(ot=(dt=(P=window.cicy)==null?void 0:P.app)==null?void 0:dt.getVersion)==null||ot.call(dt).then(X=>{k&&W(typeof X=="string"?X:String((X==null?void 0:X.desktop)||""))}).catch(()=>{}),()=>{k=!1}},[]),N.useEffect(()=>{if(!V)return;const k=P=>{it.current&&!it.current.contains(P.target)&&R(!1)};return document.addEventListener("mousedown",k),()=>document.removeEventListener("mousedown",k)},[V]);const Z=k=>{Su(k),R(!1)};return c.jsxs("header",{className:"topbar",children:[c.jsxs("div",{className:"brand-mini",children:[c.jsx("div",{className:"brand-mark sm",children:c.jsx(bm,{})}),c.jsx("span",{className:"brand-name",children:"CiCy Desktop"})]}),c.jsxs("div",{className:"user-chip","data-id":"UserChip",ref:it,children:[A&&c.jsx("span",{className:"welcome",children:A}),c.jsxs("button",{type:"button","data-id":"UserChip-trigger",className:`user-chip__trigger${V?" is-open":""}`,onClick:()=>R(k=>!k),children:[c.jsx("div",{className:"avatar",children:q}),c.jsx("span",{className:"user-name",children:D}),c.jsx("span",{className:"user-chip__caret","aria-hidden":!0,children:"▾"})]}),V&&c.jsxs("div",{className:"user-chip__menu","data-id":"UserChip-menu",role:"menu",children:[c.jsx("button",{type:"button","data-id":"UserChip-wallet",className:"user-chip__menu-item",onClick:()=>Z("?view=wallet"),children:"我的钱包"}),c.jsx("button",{type:"button","data-id":"UserChip-billing",className:"user-chip__menu-item",onClick:()=>Z("?view=usage"),children:"我的账单"}),c.jsx("button",{type:"button","data-id":"UserChip-trusted-sites",className:"user-chip__menu-item",onClick:()=>{R(!1),g(!0)},children:w("trustedSites.menu","受信任站点")}),c.jsx("button",{type:"button","data-id":"UserChip-audit-log",className:"user-chip__menu-item",onClick:()=>{R(!1),H(!0)},children:w("audit.menu","审计日志")}),c.jsx("button",{type:"button","data-id":"UserChip-terms",className:"user-chip__menu-item",onClick:()=>{R(!1),J(!0)},children:w("firstRunTerms.menu","用户协议")}),r&&c.jsx("div",{className:"user-chip__menu-mitm","data-id":"UserChip-mitm",onClick:k=>k.stopPropagation(),children:c.jsx(e0,{team:r,variant:"menu"})}),c.jsx("div",{className:"user-chip__menu-sep","aria-hidden":!0}),c.jsx("button",{type:"button","data-id":"UserChip-logout",className:"user-chip__menu-item is-danger",onClick:()=>{R(!1),T()},children:"退出"}),c.jsxs("div",{className:"user-chip__menu-version","data-id":"UserChip-version",children:["CiCy Desktop ",I?`v${I}`:"…"]})]})]}),C&&c.jsx(Iy,{onClose:()=>g(!1)}),Y&&c.jsx(Py,{onClose:()=>H(!1)}),lt&&c.jsx(pm,{onClose:()=>J(!1)})]})}function pm({onAgree:o,onClose:A}){var it;const[T,r]=N.useState(!1),[D,q]=N.useState(!1),[V,R]=N.useState(!1),C=(((it=window.cicyI18n)==null?void 0:it.locale)||"en").startsWith("zh")?"zh-CN":"en",g=(Z,k)=>w(`firstRunTerms.${Z}`,k),Y=!!A,H=[1,2,3,4,5,6].map(Z=>g(`summary${Z}`,"")),lt=Z=>{const k=Z.currentTarget;k.scrollHeight-k.scrollTop-k.clientHeight<24&&r(!0)},J=N.useRef(null);N.useEffect(()=>{const Z=J.current;Z&&Z.scrollHeight<=Z.clientHeight+24&&r(!0)},[D]);const I=async()=>{var Z,k,P;if(!(V||!T)){R(!0);try{await((P=(k=(Z=window.cicy)==null?void 0:Z.terms)==null?void 0:k.agree)==null?void 0:P.call(k,vm)),o==null||o()}catch{o==null||o()}}},W=()=>{var Z,k,P;try{(P=(k=(Z=window.cicy)==null?void 0:Z.terms)==null?void 0:k.decline)==null||P.call(k)}catch{}};return c.jsxs("div",{className:Y?"terms-gate terms-gate--review":"shell terms-gate","data-id":"FirstRunTermsGate",style:Y?{position:"fixed",inset:0,zIndex:1e3,background:"rgba(8,9,14,.72)",backdropFilter:"blur(4px)"}:void 0,onClick:Y?Z=>{Z.target===Z.currentTarget&&A()}:void 0,children:[c.jsx("div",{className:"glow","aria-hidden":!0}),c.jsxs("div",{className:"terms-gate__panel",children:[c.jsx("h1",{className:"terms-gate__title","data-id":"FirstRunTermsGate-title",children:g("title","用户协议与授权说明")}),c.jsx("p",{className:"terms-gate__subtitle",children:g("subtitle","使用 CiCy Desktop 前,请阅读并同意以下条款")}),c.jsxs("div",{className:"terms-gate__body",ref:J,onScroll:lt,"data-id":"FirstRunTermsGate-body",children:[c.jsx("h2",{className:"terms-gate__h2",children:g("summaryTitle","一眼看懂")}),c.jsx("ol",{className:"terms-gate__summary",children:H.filter(Boolean).map((Z,k)=>c.jsx("li",{children:Z},k))}),D?c.jsx("pre",{className:"terms-gate__fulltext","data-id":"FirstRunTermsGate-fulltext",children:lm[C]||lm.en}):c.jsx("button",{className:"terms-gate__viewfull","data-id":"FirstRunTermsGate-viewfull",onClick:()=>q(!0),children:g("viewFull","查看完整条款")})]}),Y?c.jsx("div",{className:"terms-gate__actions",children:c.jsx("button",{"data-id":"FirstRunTermsGate-close",className:"terms-gate__btn",onClick:A,children:g("close","关闭")})}):c.jsxs(c.Fragment,{children:[!T&&c.jsx("div",{className:"terms-gate__scrollhint","data-id":"FirstRunTermsGate-scrollhint",children:g("scrollHint","请阅读至底部以继续")}),c.jsxs("div",{className:"terms-gate__actions",children:[c.jsx("button",{"data-id":"FirstRunTermsGate-decline",className:"terms-gate__btn terms-gate__btn--ghost",onClick:W,children:g("decline","不同意并退出")}),c.jsx("button",{"data-id":"FirstRunTermsGate-agree",className:"terms-gate__btn",disabled:!T||V,title:T?"":g("mustAgree","未同意则无法使用本软件。"),onClick:I,children:g("agree","同意并继续")})]})]})]})]})}function e0({team:o,variant:A}){const[T,r]=N.useState(void 0),[D,q]=N.useState(""),[V,R]=N.useState(""),C=((o==null?void 0:o.base_url)||"").replace(/\/$/,""),g=(o==null?void 0:o.api_token)||"",Y=N.useCallback(async(Z,k={})=>{var ot,X;if(!((X=(ot=window.cicy)==null?void 0:ot.cloud)!=null&&X.fetch))throw new Error("bridge missing");const P=await window.cicy.cloud.fetch(`${C}${Z}`,{...k,headers:{Authorization:`Bearer ${g}`,"Content-Type":"application/json",...k.headers||{}}});let dt=null;try{dt=JSON.parse(P.body)}catch{}return{ok:P.ok,status:P.status,json:dt}},[C,g]),H=N.useCallback(async()=>{try{const Z=await Y("/api/mitm/ca-status");r(Z.ok&&Z.json?Z.json:null)}catch{r(null)}},[Y]);if(N.useEffect(()=>{C&&g&&H()},[C,g,H]),T===void 0||!T||!T.generated)return null;const lt=async()=>{var Z,k,P,dt,ot,X,M,pt,U;if(!D){q("enable"),R("");try{const gt=await Y("/api/mitm/consent",{method:"POST",body:JSON.stringify({enable:!0})}),qt=`${((Z=gt.json)==null?void 0:Z.error)||""} ${((k=gt.json)==null?void 0:k.detail)||""}`,Lt=((P=gt.json)==null?void 0:P.error)==="need_elevation"||!gt.ok&&gt.status===403||/need_elevation|add-trusted-cert|write permission|SecCertificate|not permitted|requires admin|administrator/i.test(qt);if(gt.ok&&((dt=gt.json)!=null&&dt.ok)&&((ot=gt.json)!=null&&ot.trusted))await H();else if(Lt){const Yt=await((pt=(M=(X=window.cicy)==null?void 0:X.mitm)==null?void 0:M.caExec)==null?void 0:pt.call(M,"install"));Yt!=null&&Yt.ok?await H():R(/cancel/i.test((Yt==null?void 0:Yt.stderr)||"")?w("mitmConsent.errorAdminDenied","未获得管理员授权,已取消。"):(Yt==null?void 0:Yt.stderr)||w("mitmConsent.errorTitle","提权失败,请从管理员控制台运行"))}else R(((U=gt.json)==null?void 0:U.error)||`失败 (HTTP ${gt.status})`)}catch(gt){R(String((gt==null?void 0:gt.message)||gt))}finally{q("")}}},J=async()=>{var Z,k,P,dt,ot;if(!D){q("disable"),R("");try{const X=await Y("/api/mitm/consent",{method:"POST",body:JSON.stringify({enable:!1})});if(X.ok&&((Z=X.json)!=null&&Z.ok))await H();else{const M=await((dt=(P=(k=window.cicy)==null?void 0:k.mitm)==null?void 0:P.caExec)==null?void 0:dt.call(P,"uninstall"));M!=null&&M.ok?await H():R((M==null?void 0:M.stderr)||((ot=X.json)==null?void 0:ot.error)||"撤销失败")}}catch(X){R(String((X==null?void 0:X.message)||X))}finally{q("")}}},I=T.consent&&T.trusted,W=T.consent&&!T.trusted,it=(Z,k)=>w(`mitmConsent.${Z}`,k);if(A==="menu"){const Z=k=>{var P;(P=k==null?void 0:k.stopPropagation)==null||P.call(k),!D&&(I?window.confirm(it("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&J():lt())};return c.jsxs("div",{className:"user-chip__mitm","data-id":"MitmConsentCard",children:[c.jsxs("div",{className:"user-chip__menu-item user-chip__mitm-row",title:it("scopeNote","仅解密 AI 厂商域名,数据留本地,随时可关闭。"),children:[c.jsx("span",{className:"user-chip__mitm-label",children:it("rowLabel","HTTPS 审计")}),c.jsx("button",{type:"button",role:"switch","aria-checked":I?"true":"false","data-id":"MitmConsentCard-toggle",className:`mini-switch${I?" is-on":""}${D?" is-busy":""}`,disabled:!!D,onClick:Z,children:c.jsx("span",{className:"mini-switch__knob"})})]}),W&&!D&&c.jsx("div",{className:"user-chip__mitm-note","data-id":"MitmConsentCard-note",children:it("partialNote","已同意但未安装,点开关重试")}),V&&c.jsx("div",{className:"user-chip__mitm-err","data-id":"MitmConsentCard-error",children:V})]})}return I||D==="disable"?Pl.createPortal(c.jsxs("div",{"data-id":"MitmConsentCard",className:"mitm-pill",title:it("grantedDesc","HTTPS 审计已开启,仅对 CiCy 启动的 AI 工具生效;随时可关闭。"),children:[c.jsx("span",{className:"mitm-pill__dot","data-busy":D?"1":"0"}),c.jsx("span",{className:"mitm-pill__text","data-id":"MitmConsentCard-title",children:D==="disable"?it("processingRevoke","正在关闭…"):it("statePillOn","HTTPS 审计已开启")}),!D&&c.jsx("button",{type:"button","data-id":"MitmConsentCard-revoke",className:"mitm-pill__off",onClick:()=>{window.confirm(it("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&J()},children:it("turnOff","关闭")})]}),document.body):c.jsxs("div",{"data-id":"MitmConsentCard",className:`mitm-card${I?" mitm-card--on":""}`,children:[c.jsxs("div",{className:"mitm-card__head",children:[c.jsx("span",{className:"mitm-card__dot","data-state":I?"on":W?"warn":"off"}),c.jsx("span",{className:"mitm-card__title","data-id":"MitmConsentCard-title",children:D?it("stateProcessingTitle","处理中…"):I?it("stateGrantedTitle","已启用"):`${it("cardTitle","HTTPS 流量本地审计")}${W?" — "+it("retry","重试"):""}`})]}),c.jsxs("p",{className:"mitm-card__desc","data-id":"MitmConsentCard-desc",children:[I?it("grantedDesc","HTTPS 审计已开启,仅对 CiCy 启动的 AI 工具(claude / codex 等)生效;随时可关闭。"):it("body","启用后,CiCy 启动的 AI 工具(claude / codex 等)访问 AI 厂商(Claude / OpenAI / DeepSeek / Gemini)的 HTTPS 流量将被本地审计解密,数据留本地,随时可关闭。"),!I&&c.jsxs(c.Fragment,{children:[c.jsx("br",{}),c.jsx("span",{className:"mitm-card__note",children:it("adminNote","通过环境变量对 CiCy 启动的 AI 工具生效,不修改系统、无需管理员授权。")}),c.jsx("br",{}),c.jsx("span",{className:"mitm-card__sub",children:it("scopeNote","仅解密上述 AI 厂商域名,其余一切流量不被解密、不被读取。")})]})]}),V&&c.jsxs("div",{className:"mitm-card__error","data-id":"MitmConsentCard-error",children:[it("errorTitle","操作失败"),": ",V]}),c.jsx("div",{className:"mitm-card__actions",children:I?c.jsx("button",{"data-id":"MitmConsentCard-revoke",className:"mitm-card__btn mitm-card__btn--ghost",disabled:!!D,onClick:()=>{window.confirm(it("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&J()},children:D==="disable"?it("processingRevoke","正在关闭…"):it("revoke","撤销")}):c.jsx("button",{"data-id":"MitmConsentCard-enable",className:"mitm-card__btn",disabled:!!D,onClick:lt,children:D==="enable"?it("processingEnable","正在启用…"):W?it("retry","重试"):it("enable","同意并启用")})})]})}const Zs=new Set;let um=0,Ft=null;function Il(){Zs.forEach(o=>o(Ft))}const ie={open({onRetry:o}={}){Ft={status:"running",phase:"install-docker",logs:[],bars:{},minimized:!1,onRetry:o||null,lastAt:Date.now()},Il()},minimize(){Ft&&(Ft={...Ft,minimized:!0},Il())},restore(){Ft&&(Ft={...Ft,minimized:!1},Il())},push(o={}){var V;if(!Ft)return;const A=o.phase==="health"?"container":o.phase||Ft.phase,T={...Ft,phase:A,lastAt:Date.now()},r=Number.isFinite(o.progress),D=A==="install-docker"||A==="image";if(D&&(r||o.dest||o.url)){const R=((V=Ft.bars)==null?void 0:V[A])||{},C=r?o.progress:o.status==="skip"||o.status==="done"?100:R.progress;T.bars={...Ft.bars,[A]:{progress:C,received:o.received??R.received,total:o.total??R.total,url:o.url||R.url,dest:o.dest||R.dest}}}if(!(o.status==="running"&&r&&D)){const R={id:++um,t:bu(),phase:A,status:o.status||"running",message:o.message||""};T.logs=[...Ft.logs,R]}Ft=T,Il()},finish({ok:o,message:A,status:T}={}){if(!Ft)return;const r=T||(o?"done":"error"),D={id:++um,t:bu(),phase:"done",status:r,message:A||(o?"完成":"失败")};Ft={...Ft,status:r,phase:"done",minimized:!1,logs:[...Ft.logs,D],lastAt:Date.now()},Il()},close(){Ft=null,Il()}},ks=[["install-docker","准备环境"],["image","加载镜像"],["container","启动容器"],["done","完成"]],a0={"install-docker":"Docker",image:"镜像",container:"容器",health:"容器",done:"完成"},l0={"install-docker":"Docker Desktop",image:"基础镜像"};function cm(o){return Number.isFinite(o)?o<1024?o+" B":o<1048576?(o/1024).toFixed(0)+" KB":o<1073741824?(o/1048576).toFixed(1)+" MB":(o/1073741824).toFixed(2)+" GB":"?"}function n0({phaseKey:o,bar:A}){const T=Number.isFinite(A==null?void 0:A.progress)?Math.max(0,Math.min(100,A.progress)):0,r=T>=100;return c.jsxs("div",{className:"dlbar","data-id":`DockerDrawer-dlbar-${o}`,children:[c.jsxs("div",{className:"dlbar__head",children:[c.jsx("span",{className:"dlbar__name",children:l0[o]||o}),c.jsxs("span",{className:"dlbar__pct",children:[T,"%",A!=null&&A.total?` · ${cm(A.received)} / ${cm(A.total)}`:""]})]}),c.jsx("div",{className:"dlbar__track",children:c.jsx("div",{className:`dlbar__fill${r?" is-done":""}`,style:{width:`${T}%`}})}),(A==null?void 0:A.url)&&c.jsxs("div",{className:"dlbar__url",title:A.url,children:[c.jsx("span",{className:"dlbar__urlk",children:"源"})," ",A.url]}),(A==null?void 0:A.dest)&&c.jsxs("div",{className:"dlbar__url",title:A.dest,children:[c.jsx("span",{className:"dlbar__urlk",children:"存"})," ",A.dest]})]})}function i0(){var V;const[o,A]=N.useState(Ft);N.useEffect(()=>(Zs.add(A),()=>{Zs.delete(A)}),[]);const T=N.useRef(null);if(N.useEffect(()=>{const R=T.current;R&&(R.scrollTop=R.scrollHeight)},[(V=o==null?void 0:o.logs)==null?void 0:V.length]),!o)return null;const r=o.status==="running",D=ks.findIndex(([R])=>R===o.phase),q=["install-docker","image"].filter(R=>{var C;return(C=o.bars)==null?void 0:C[R]});if(o.minimized){const R=q.map(g=>{var Y;return(Y=o.bars[g])==null?void 0:Y.progress}).filter(Number.isFinite),C=R.length?Math.round(R.reduce((g,Y)=>g+Y,0)/R.length):null;return c.jsxs("button",{type:"button",className:`drawer-min drawer-min--${o.status}`,"data-id":"DockerDrawer-restore",onClick:()=>ie.restore(),children:[c.jsx("span",{className:"drawer-min__spark",children:r?c.jsx(ge,{}):o.status==="done"?"✓":o.status==="reboot"?"⟳":"!"}),c.jsxs("span",{className:"drawer-min__label",children:[w("docker.setupTitle","安装 Docker cicy-code"),C!=null?` · ${C}%`:""]})]})}return c.jsx("div",{className:"drawer-scrim","data-id":"DockerDrawer-scrim",onClick:()=>r?ie.minimize():ie.close(),children:c.jsxs("div",{className:"drawer","data-id":"DockerDrawer","data-status":o.status,onClick:R=>R.stopPropagation(),children:[c.jsxs("div",{className:"drawer__head",children:[c.jsxs("div",{className:"drawer__title",children:[c.jsx("span",{className:`drawer__spark drawer__spark--${o.status}`,children:r?c.jsx(ge,{}):o.status==="done"?"✓":o.status==="reboot"?"⟳":"!"}),c.jsxs("div",{children:[c.jsx("div",{className:"drawer__h",children:w("docker.setupTitle","安装 Docker cicy-code")}),c.jsx("div",{className:"drawer__sub",children:"127.0.0.1:8009"})]})]}),c.jsx("div",{className:"drawer__headbtns",children:c.jsx("button",{type:"button",className:"drawer__x","data-id":"DockerDrawer-min",title:w("common.minimize","最小化"),onClick:()=>ie.minimize(),"aria-label":"minimize",children:"‒"})})]}),c.jsx("div",{className:"drawer__steps","data-id":"DockerDrawer-steps",children:ks.map(([R,C],g)=>{const Y=o.status==="done"||D>=0&&g<D,H=g===D&&r,lt=o.status==="error"&&g===D;return c.jsxs("div",{className:`drawer__step${H?" is-active":""}${Y?" is-done":""}${lt?" is-error":""}`,children:[c.jsx("span",{className:"drawer__step-dot",children:Y?"✓":lt?"!":g+1}),c.jsx("span",{className:"drawer__step-label",children:C}),g<ks.length-1&&c.jsx("span",{className:"drawer__step-bar"})]},R)})}),q.length>0&&c.jsx("div",{className:"drawer__dlbars","data-id":"DockerDrawer-dlbars",children:q.map(R=>c.jsx(n0,{phaseKey:R,bar:o.bars[R]},R))}),r&&o.logs.length>0&&c.jsxs("div",{className:"drawer__now","data-id":"DockerDrawer-now",children:[c.jsx(ge,{}),c.jsx("span",{children:o.logs[o.logs.length-1].message})]}),c.jsx("div",{className:"drawer__log drawer__log--scroll","data-id":"DockerDrawer-log",ref:T,children:o.logs.length===0?c.jsx("div",{className:"drawer__log-empty",children:w("docker.preparing","准备中…")}):o.logs.map(R=>c.jsxs("div",{className:"drawer__line","data-status":R.status,children:[c.jsx("span",{className:"drawer__t",children:R.t}),c.jsx("span",{className:`drawer__badge drawer__badge--${R.phase}`,children:a0[R.phase]||R.phase}),c.jsx("span",{className:"drawer__linemsg",children:R.message})]},R.id))}),c.jsx("div",{className:"drawer__foot",children:r?c.jsx(c.Fragment,{children:c.jsx("span",{className:"drawer__foot-status",children:w("docker.installing2","安装进行中…")})}):o.status==="reboot"?c.jsxs(c.Fragment,{children:[c.jsx("span",{className:"drawer__foot-status is-reboot",children:w("docker.rebootShort","需重启 Windows")}),o.onRetry&&c.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"DockerDrawer-retry",onClick:()=>o.onRetry(),children:w("common.retry","重试")}),c.jsx("button",{type:"button",className:"drawer__btn","data-id":"DockerDrawer-dismiss",onClick:()=>ie.close(),children:w("common.close","关闭")})]}):o.status==="error"?c.jsxs(c.Fragment,{children:[c.jsx("span",{className:"drawer__foot-status is-error",children:w("docker.failed","安装失败")}),o.onRetry&&c.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"DockerDrawer-retry",onClick:()=>o.onRetry(),children:w("common.retry","重试")}),c.jsx("button",{type:"button",className:"drawer__btn","data-id":"DockerDrawer-dismiss",onClick:()=>ie.close(),children:w("common.close","关闭")})]}):c.jsxs(c.Fragment,{children:[c.jsx("span",{className:"drawer__foot-status is-done",children:w("docker.ready","已就绪")}),c.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"DockerDrawer-finish",onClick:()=>ie.close(),children:w("common.done","完成")})]})})]})})}function u0({dockerTeam:o,onOpen:A,onRefresh:T}){var me;const[r,D]=N.useState(null),[q,V]=N.useState(""),[R,C]=N.useState(!1),[g,Y]=N.useState({top:0,left:0}),H=N.useRef(null),lt=N.useRef(null),J=184,I="#2496ed",W=N.useCallback(async()=>{var ut,S,O;try{D(await((O=(S=(ut=window.cicy)==null?void 0:ut.docker)==null?void 0:S.appStatus)==null?void 0:O.call(S)))}catch(Q){console.warn("[DockerCard]",Q)}},[]);N.useEffect(()=>{W();const ut=setInterval(()=>{q||W()},12e3);return()=>clearInterval(ut)},[W,q]),N.useEffect(()=>{if(!R)return;const ut=O=>{var Q,mt;(Q=H.current)!=null&&Q.contains(O.target)||(mt=lt.current)!=null&&mt.contains(O.target)||C(!1)},S=O=>{O.key==="Escape"&&C(!1)};return document.addEventListener("mousedown",ut),document.addEventListener("keydown",S),()=>{document.removeEventListener("mousedown",ut),document.removeEventListener("keydown",S)}},[R]);const it=()=>{if(!R&&H.current){const ut=H.current.getBoundingClientRect(),S=Math.max(8,Math.min(ut.right-J,window.innerWidth-J-8));Y({top:Math.round(ut.bottom+4),left:Math.round(S)})}C(ut=>!ut)},Z=N.useCallback(async()=>{var S,O,Q,mt,st,d;V("bootstrap"),ie.open({onRetry:Z});const ut=(Q=(O=(S=window.cicy)==null?void 0:S.docker)==null?void 0:O.onAppProgress)==null?void 0:Q.call(O,y=>ie.push(y));try{const y=await((d=(st=(mt=window.cicy)==null?void 0:mt.docker)==null?void 0:st.appBootstrap)==null?void 0:d.call(st));(y==null?void 0:y.reason)==="installer_launched"?ie.finish({status:"reboot",message:w("docker.installerLaunched","已打开 Docker 安装程序——请完成安装(会装 WSL2、可能需重启),装好后点「重试」")}):(y==null?void 0:y.reason)==="wsl_reboot_required"?ie.finish({status:"reboot",message:w("docker.rebootNeeded","WSL2 已安装,请【重启 Windows】后回来点「重试」继续")}):ie.finish({ok:!!(y!=null&&y.ok),message:y!=null&&y.ok?w("docker.ready","Docker cicy-code 已就绪"):(y==null?void 0:y.error)||w("docker.failed","安装失败")}),y!=null&&y.ok&&(T==null||T())}catch(y){ie.finish({ok:!1,message:y.message})}finally{try{ut&&ut()}catch{}V(""),W()}},[W,T]),k=N.useCallback(async()=>{var S,O,Q,mt,st,d;C(!1),V("upgrade"),ie.open({onRetry:k});const ut=(Q=(O=(S=window.cicy)==null?void 0:S.docker)==null?void 0:O.onAppProgress)==null?void 0:Q.call(O,y=>ie.push(y));try{const y=await((d=(st=(mt=window.cicy)==null?void 0:mt.docker)==null?void 0:st.appUpgrade)==null?void 0:d.call(st));(y==null?void 0:y.reason)==="wsl_reboot_required"?ie.finish({status:"reboot",message:w("docker.rebootNeeded","WSL2 已安装,请【重启 Windows】后回来点「重试」继续")}):ie.finish({ok:!!(y!=null&&y.ok),message:y!=null&&y.ok?w("docker.upgraded","已升级到最新"):(y==null?void 0:y.error)||w("docker.upgradeFailed","升级失败")}),y!=null&&y.ok&&(T==null||T())}catch(y){ie.finish({ok:!1,message:y.message})}finally{try{ut&&ut()}catch{}V(""),W()}},[W,T]),P=N.useCallback(async(ut,S,O)=>{C(!1),V(ut),de.show({id:"docker-op",message:w(`docker.${ut}ing`,ut==="restart"?"重启中…":"停止中…"),status:"running"});try{const Q=await S();Q!=null&&Q.ok?de.show({id:"docker-op",message:O,status:"done",ttl:2500}):de.show({id:"docker-op",message:(Q==null?void 0:Q.error)||w("docker.opFailed","操作失败"),status:"error",ttl:6e3})}catch(Q){de.show({id:"docker-op",message:Q.message,status:"error",ttl:6e3})}finally{V(""),W()}},[W]);if((((me=window.cicy)==null?void 0:me.platform)||(r==null?void 0:r.platform))!=="win32")return null;const ot=!!(r!=null&&r.running)||(o==null?void 0:o.status)==="running",X=!!(r!=null&&r.dockerRunning),M=!!(r!=null&&r.installed),pt=ot?"ok":X||M?"warn":"off",U=!!q,gt=ot?w("docker.running","运行中 · :8009"):X?w("docker.notRunning","未启动 · 点「启动」"):M?w("docker.engineDown","Docker 未运行 · 点启动"):w("docker.notInstalled","Docker Desktop 未安装"),qt=U?w("docker.working","处理中…"):ot?w("localTeams.open","打开"):X?w("docker.start","启动"):M?w("docker.startDocker","启动 Docker"):w("docker.install","下载安装"),Lt=()=>{if(!U){if(ot){A==null||A(o==null?void 0:o.id);return}Z()}},Yt=ot;return c.jsxs("div",{"data-id":"DockerCard",className:`bcard bcard--docker${ot?" bcard--online":""}`,children:[c.jsx("div",{className:"bcard__accent",style:{background:I}}),c.jsxs("div",{className:"bcard__top",children:[c.jsxs("div",{className:"bcard__pill",style:{color:I},children:[c.jsx("span",{className:"bcard__dot","data-tone":pt}),c.jsx("svg",{style:{width:18,height:18},viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:c.jsx("path",{d:"M21.81 10.25c-.06-.05-.67-.51-1.95-.51-.34 0-.68.03-1.01.09-.25-1.69-1.64-2.51-1.7-2.55l-.34-.2-.22.32a4.5 4.5 0 0 0-.59 1.4c-.23.94-.09 1.83.39 2.59-.58.32-1.51.4-1.7.41H2.62a.61.61 0 0 0-.61.61 9.32 9.32 0 0 0 .57 3.35 4.9 4.9 0 0 0 1.95 2.53c.92.52 2.42.82 4.12.82.77 0 1.54-.07 2.3-.21a9.6 9.6 0 0 0 3-1.09 8.3 8.3 0 0 0 2.05-1.68c.98-1.11 1.56-2.35 1.99-3.45h.17c1.36 0 2.2-.55 2.66-1l.13-.16zM4.7 11.33h1.78a.16.16 0 0 0 .16-.16V9.58a.16.16 0 0 0-.16-.16H4.7a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16m2.46 0h1.78a.16.16 0 0 0 .16-.16V9.58a.16.16 0 0 0-.16-.16H7.16a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16m2.5 0h1.78a.16.16 0 0 0 .16-.16V9.58a.16.16 0 0 0-.16-.16H9.66a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16m2.47 0h1.78a.16.16 0 0 0 .16-.16V9.58a.16.16 0 0 0-.16-.16h-1.78a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16M7.16 9.06h1.78a.16.16 0 0 0 .16-.16V7.31a.16.16 0 0 0-.16-.16H7.16a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16m2.5 0h1.78a.16.16 0 0 0 .16-.16V7.31a.16.16 0 0 0-.16-.16H9.66a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16m2.47 0h1.78a.16.16 0 0 0 .16-.16V7.31a.16.16 0 0 0-.16-.16h-1.78a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16"})})]}),Yt&&c.jsxs("div",{className:"bcard__menuwrap",onClick:ut=>ut.stopPropagation(),children:[c.jsx("button",{type:"button",ref:H,"data-id":"DockerCard-menu-btn",className:"bcard__kebab",title:w("docker.manage","管理 Docker cicy-code"),disabled:U,onClick:it,children:U?c.jsx(ge,{}):c.jsx(Js,{})}),R&&Pl.createPortal(c.jsxs("div",{className:"bcard__menu bcard__menu--portal","data-id":"DockerCard-menu",role:"menu",ref:lt,style:{position:"fixed",top:g.top,left:g.left,width:J},onClick:ut=>ut.stopPropagation(),children:[ot&&c.jsx("button",{type:"button","data-id":"DockerCard-restart",className:"bcard__menu-item",onClick:()=>P("restart",()=>window.cicy.docker.appRestart(),w("docker.restarted","已重启")),children:w("docker.restart","重启")}),c.jsx("button",{type:"button","data-id":"DockerCard-upgrade",className:"bcard__menu-item is-accent",onClick:k,children:w("docker.upgrade","升级(拉取最新镜像)")}),ot&&c.jsx("button",{type:"button","data-id":"DockerCard-stop",className:"bcard__menu-item is-danger",onClick:()=>P("stop",()=>window.cicy.docker.appStop(),w("docker.stopped","已停止")),children:w("docker.stop","停止")})]}),document.body)]})]}),c.jsxs("div",{className:"bcard__body",children:[c.jsx("h3",{className:"bcard__name",children:w("docker.title","Docker cicy-code")}),c.jsx("div",{className:"bcard__host",children:"http://127.0.0.1:8009"}),c.jsx("div",{className:"bcard__meta",style:{fontSize:12,color:"#8b949e"},children:gt})]}),c.jsxs("button",{type:"button",className:"bcard__cta","data-id":"DockerCard-cta",disabled:U,onClick:Lt,style:ot?void 0:{background:I,color:"white"},children:[U?c.jsx(ge,{}):c.jsx(_u,{}),c.jsx("span",{children:qt})]})]})}function sm({team:o,onOpen:A,onRename:T,onRefresh:r}){var Ka,He,B,at;const q=(rm[o.status]||rm.error).tone,[V,R]=N.useState(!1),[C,g]=N.useState(o.name||""),[Y,H]=N.useState(null),[lt,J]=N.useState(""),I=Y??o.name;N.useEffect(()=>{Y!=null&&o.name===Y&&H(null)},[o.name,Y]);const W=x=>{var K;(K=x==null?void 0:x.stopPropagation)==null||K.call(x),g(I||""),R(!0)},it=async()=>{R(!1);const x=String(C||"").trim();if(!T||!x||x===I)return;H(x),J("saving");let K;try{K=await T(o.id,x)}catch(L){K={ok:!1,error:L==null?void 0:L.message}}H(null),K&&K.ok?(J("saved"),setTimeout(()=>J(L=>L==="saved"?"":L),1500)):(J(""),de.show({message:w("localTeams.renameFailed","改名没保存,已恢复"),status:"error",ttl:4e3}))},k=!!((He=(Ka=window.cicy)==null?void 0:Ka.sidecar)!=null&&He.restart)&&Vs(o.base_url),P=o.status==="running",[dt,ot]=N.useState(""),[X,M]=N.useState(!1),[pt,U]=N.useState({running:void 0,latest:null,installed:null}),gt=pt.latest,qt=pt.running,[Lt,Yt]=N.useState(!1),me=N.useRef(null),ut=N.useRef(null),S=N.useRef(null),[O,Q]=N.useState({top:0,left:0}),mt=184,st=()=>{if(!X&&ut.current){const x=ut.current.getBoundingClientRect(),K=Math.max(8,Math.min(x.right-mt,window.innerWidth-mt-8));Q({top:Math.round(x.bottom+4),left:Math.round(K)})}M(x=>!x)},d=N.useCallback(async(x=!1)=>{var K,L;if(!(!k||!((L=(K=window.cicy)==null?void 0:K.sidecar)!=null&&L.versions))){x&&Yt(!0);try{const et=await window.cicy.sidecar.versions();U({running:(et==null?void 0:et.running)??null,latest:(et==null?void 0:et.latest)??null,installed:(et==null?void 0:et.installed)??null}),x&&(et!=null&&et.running&&(et!=null&&et.latest)&&fm(et.latest,et.running)>0?de.show({message:`${w("sidecar.found","发现新版本")} v${et.latest}`,status:"done",ttl:2500}):et!=null&&et.running?de.show({message:`${w("sidecar.upToDate","已是最新")} v${et.running}`,status:"done",ttl:2500}):de.show({message:w("sidecar.notRunning","cicy-code 未运行"),status:"error",ttl:4e3}))}catch{x&&de.show({message:w("sidecar.checkFailed","检查更新失败"),status:"error",ttl:5e3})}finally{x&&Yt(!1)}}},[k]);N.useEffect(()=>{d(!1)},[d]),N.useEffect(()=>{P&&d(!1)},[P,d]);const y=!!(k&&gt&&qt&&fm(gt,qt)>0),G=!k&&!!((at=(B=window.cicy)==null?void 0:B.localTeams)!=null&&at.remove),$=k||G,[ft,rt]=N.useState(!1);N.useEffect(()=>{if(!X)return;const x=K=>{var L,et;(L=ut.current)!=null&&L.contains(K.target)||(et=S.current)!=null&&et.contains(K.target)||M(!1)};return document.addEventListener("mousedown",x),()=>document.removeEventListener("mousedown",x)},[X]),N.useEffect(()=>{X||rt(!1)},[X]);const Tt=async()=>{var x,K,L;if(!ft){rt(!0);return}if(M(!1),rt(!1),!dt){ot("remove");try{await((L=(K=(x=window.cicy)==null?void 0:x.localTeams)==null?void 0:K.remove)==null?void 0:L.call(K,o.id))}catch{}ot(""),r==null||r()}},Rt=`sidecar-op:${o.id}`,Et=async(x,K,L)=>{var ue,Xt,kt,Ae,tn;if(M(!1),dt)return;ot(x);const et=x==="update";let Zt=null;et?(ea.open({teamId:o.id,fromVer:qt,toVer:gt,onRetry:()=>Et("update",K,L)}),(Xt=(ue=window.cicy)==null?void 0:ue.sidecar)!=null&&Xt.onOpProgress&&(Zt=window.cicy.sidecar.onOpProgress(Gt=>{(Gt==null?void 0:Gt.op)==="update"&&ea.push(Gt)}))):de.show({id:Rt,message:Re[x]||`${x}…`,status:"running",progress:void 0});try{const Gt=await K(),ve=!!(Gt!=null&&Gt.ok),Pn=Gt!=null&&Gt.warning?`${L}(${Gt.warning})`:L,en=w("sidecar.failed","操作失败")+(Gt!=null&&Gt.error?`: ${Gt.error}`:"");if(et){if(ea.finish({ok:ve,message:ve?Pn:en}),ve)try{await((tn=(Ae=(kt=window.cicy)==null?void 0:kt.localTeams)==null?void 0:Ae.reload)==null?void 0:tn.call(Ae,o.id,{ignoreCache:!0}))}catch{}}else de.show({id:Rt,message:ve?Pn:en,progress:void 0,status:ve?"done":"error",ttl:ve?4e3:8e3})}catch(Gt){const ve=w("sidecar.failed","操作失败")+`: ${(Gt==null?void 0:Gt.message)||Gt}`;et?ea.finish({ok:!1,message:ve}):de.show({id:Rt,message:ve,progress:void 0,status:"error",ttl:8e3})}finally{try{Zt&&Zt()}catch{}ot(""),r==null||r(),(x==="update"||x==="restart"||x==="start")&&d(!1)}},Re={start:"启动中…",restart:"重启中…",update:"更新中…",stop:"停止中…"},aa=async()=>{var x,K;if(!dt){if(!P&&k&&((K=(x=window.cicy)==null?void 0:x.sidecar)!=null&&K.start)){ot("start"),de.show({id:Rt,message:Re.start,status:"running",progress:void 0});const L=await window.cicy.sidecar.start().catch(et=>({ok:!1,error:(et==null?void 0:et.message)||String(et)}));if(ot(""),r==null||r(),!(L!=null&&L.ok)||L!=null&&L.warning){de.show({id:Rt,message:w("sidecar.startFailed","启动失败")+(L!=null&&L.error?`: ${L.error}`:L!=null&&L.warning?`: ${L.warning}`:""),status:"error",ttl:8e3});return}de.dismiss(Rt)}A()}},We=P?w("localTeams.open","打开"):k?w("localTeams.startOpen","启动并打开"):w("localTeams.open","打开");return c.jsxs("div",{"data-id":"LocalTeamCard",className:`bcard ${k?"bcard--local":"bcard--custom"}${q==="ok"?" bcard--online":""}`,children:[c.jsx("div",{className:"bcard__accent"}),c.jsxs("div",{className:"bcard__top",children:[c.jsxs("div",{className:"bcard__pill",children:[c.jsx("span",{className:"bcard__dot","data-tone":q}),c.jsx(Sm,{})]}),$&&c.jsxs("div",{className:"bcard__menuwrap",ref:me,onClick:x=>x.stopPropagation(),children:[c.jsx("button",{type:"button",ref:ut,"data-id":"LocalTeamCard-menu-btn",className:`bcard__kebab${y?" has-dot":""}`,title:k?w("localTeams.manage","管理本地 cicy-code"):w("localTeams.more","更多"),disabled:!!dt,onClick:st,children:dt?c.jsx(ge,{}):c.jsx(Js,{})}),X&&Pl.createPortal(c.jsxs("div",{className:"bcard__menu bcard__menu--portal","data-id":"LocalTeamCard-menu",role:"menu",ref:S,style:{position:"fixed",top:O.top,left:O.left,width:mt},onClick:x=>x.stopPropagation(),children:[k&&c.jsx("button",{type:"button","data-id":"LocalTeamCard-check-update",className:"bcard__menu-item",disabled:Lt,onClick:x=>{x.stopPropagation(),d(!0)},children:Lt?w("sidecar.checking2","检查中…"):w("sidecar.checkUpdate","检查更新")}),y&&c.jsxs("button",{type:"button","data-id":"LocalTeamCard-update",className:"bcard__menu-item is-accent",onClick:()=>Et("update",()=>window.cicy.sidecar.update(),w("sidecar.updated","已更新到最新")),children:[w("sidecar.updateTo","更新到")," v",gt]}),k&&P&&c.jsxs(c.Fragment,{children:[c.jsx("button",{type:"button","data-id":"LocalTeamCard-reload",className:"bcard__menu-item",onClick:()=>Et("reload",async()=>{const x=await window.cicy.localTeams.reload(o.id);return!(x!=null&&x.ok)&&(x==null?void 0:x.error)==="no_open_window"?{ok:!1,error:w("localTeams.windowNotOpen","窗口未打开,请先点「打开」")}:x},w("localTeams.reloaded","已刷新窗口")),children:w("localTeams.reloadWindow","刷新窗口")}),c.jsx("button",{type:"button","data-id":"LocalTeamCard-restart",className:"bcard__menu-item",onClick:()=>Et("restart",()=>window.cicy.sidecar.restart(),w("sidecar.restarted","已重启")),children:w("sidecar.restart","重启")}),c.jsx("button",{type:"button","data-id":"LocalTeamCard-stop",className:"bcard__menu-item is-danger",onClick:()=>Et("stop",()=>window.cicy.sidecar.stop(),w("sidecar.stoppedDone","已停止")),children:w("sidecar.stop","停止")})]}),o.cloud_team_id&&c.jsx("button",{type:"button","data-id":"LocalTeamCard-billing",className:"bcard__menu-item",onClick:x=>{x.stopPropagation(),M(!1),Su(`?team=${encodeURIComponent(o.cloud_team_id)}`)},children:w("localTeams.billing","账单")}),G&&c.jsx("button",{type:"button","data-id":"LocalTeamCard-remove",className:"bcard__menu-item is-danger",onClick:Tt,children:ft?w("localTeams.removeConfirm","确认删除?"):w("localTeams.remove","删除")})]}),document.body)]})]}),c.jsxs("div",{className:"bcard__body",children:[V?c.jsx("input",{"data-id":"LocalTeamCard-rename-input",autoFocus:!0,value:C,onChange:x=>g(x.target.value),onFocus:x=>x.target.select(),onBlur:it,onClick:x=>x.stopPropagation(),onKeyDown:x=>{x.nativeEvent.isComposing||x.keyCode===229||(x.key==="Enter"?it():x.key==="Escape"&&R(!1))},style:{width:"100%",font:"inherit",fontWeight:600,padding:"2px 6px",border:"1px solid #3b82f6",borderRadius:6,background:"#0d1117",color:"#e6edf3",boxSizing:"border-box"}}):c.jsxs("h3",{className:"bcard__name",title:w("localTeams.renameHint","点名字或 ✎ 改名"),style:{display:"flex",alignItems:"center",gap:6},onDoubleClick:W,children:[c.jsx("span",{"data-id":"LocalTeamCard-name-text",onClick:W,style:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",cursor:"text"},children:I}),lt==="saving"&&c.jsx("span",{"data-id":"LocalTeamCard-save-state",title:w("localTeams.saving","保存中…"),style:{flex:"none",display:"inline-flex"},children:c.jsx(ge,{})}),lt==="saved"&&c.jsx("span",{"data-id":"LocalTeamCard-save-state",title:w("localTeams.saved","已保存"),style:{flex:"none",color:"#3fb950",fontSize:13,lineHeight:1},children:"✓"}),!lt&&c.jsx("button",{type:"button","data-id":"LocalTeamCard-rename-btn",title:w("localTeams.rename","重命名"),onClick:W,style:{flex:"none",cursor:"pointer",border:"none",background:"transparent",color:"#8b949e",fontSize:13,padding:0,lineHeight:1},children:"✎"})]}),c.jsx("div",{className:"bcard__host",children:o.base_url||"—"}),c.jsx("div",{className:"bcard__meta",children:(qt||o.version)&&c.jsxs("span",{className:"bcard__ver","data-id":"LocalTeamCard-version",children:["v",qt||o.version]})})]}),c.jsxs("button",{type:"button",className:"bcard__cta","data-id":"LocalTeamCard-open",disabled:!!dt||!o.base_url,onClick:aa,children:[dt&&dt!=="stop"?c.jsx(ge,{}):c.jsx(_u,{}),c.jsx("span",{children:dt&&Re[dt]||We})]})]})}function Vs(o){try{const A=new URL(o);return(A.hostname==="127.0.0.1"||A.hostname==="localhost"||A.hostname==="::1")&&(A.port==="8008"||A.port==="")}catch{return!1}}function om(o){try{const A=new URL(o);return(A.hostname==="127.0.0.1"||A.hostname==="localhost"||A.hostname==="::1")&&A.port==="8009"}catch{return!1}}function fm(o,A){const T=String(o).split("."),r=String(A).split(".");for(let D=0;D<Math.max(T.length,r.length);D++){const q=(parseInt(T[D],10)||0)-(parseInt(r[D],10)||0);if(q)return q>0?1:-1}return 0}const rm={running:{tone:"ok",label:"running",cta:"打开"},stopped:{tone:"off",label:"stopped",cta:"未运行"},auth_error:{tone:"warn",label:"auth error",cta:"Token 失效"},misconfigured:{tone:"err",label:"bad config",cta:"URL 错误"},error:{tone:"err",label:"error",cta:"异常"}};function c0({team:o,onOpen:A}){const T=o.kind==="private",r=o.status==="active",D=o.name||o.title||"—",q=o.host_url||"",V=o.teamId||o.id,R=T?"私有云":o.team_kind==="personal"?"个人":"共享",C=T?q:o.workspace_url||o.workspace_direct_url,g=!!C,[Y,H]=N.useState(!1),[lt,J]=N.useState(!1),[I,W]=N.useState({top:0,left:0}),it=N.useRef(null),Z=N.useRef(null),k=N.useRef(null),P=184,dt=()=>{if(!Y&&Z.current){const X=Z.current.getBoundingClientRect(),M=Math.max(8,Math.min(X.right-P,window.innerWidth-P-8));W({top:Math.round(X.bottom+4),left:Math.round(M)})}H(X=>!X)};N.useEffect(()=>{if(!Y)return;const X=M=>{var pt,U;(pt=Z.current)!=null&&pt.contains(M.target)||(U=k.current)!=null&&U.contains(M.target)||H(!1)};return document.addEventListener("mousedown",X),()=>document.removeEventListener("mousedown",X)},[Y]);const ot=async()=>{var X,M,pt;if(!(!g||lt)){J(!0),H(!1);try{await((pt=(M=(X=window.cicy)==null?void 0:X.tabs)==null?void 0:M.reload)==null?void 0:pt.call(M,C,D))}catch{}finally{J(!1)}}};return c.jsxs("div",{"data-id":"TeamCard",className:`bcard bcard--cloud${r?" bcard--online":""}`,children:[c.jsx("div",{className:"bcard__accent"}),c.jsxs("div",{className:"bcard__top",children:[c.jsxs("div",{className:"bcard__pill",children:[c.jsx("span",{className:"bcard__dot","data-tone":r?"ok":"off"}),c.jsx(s0,{})]}),c.jsxs("div",{className:"bcard__top-right",children:[o.is_trial&&c.jsx("span",{className:"bcard__badge",children:"trial"}),V!=null&&c.jsx("button",{type:"button","data-id":"TeamCard-billing",className:"bcard__billing-btn",title:w("localTeams.billing","账单"),onClick:X=>{X.stopPropagation(),Su(`?team=${encodeURIComponent(V)}`)},children:w("localTeams.billing","账单")}),g&&c.jsxs("div",{className:"bcard__menuwrap",ref:it,onClick:X=>X.stopPropagation(),children:[c.jsx("button",{type:"button",ref:Z,"data-id":"TeamCard-menu-btn",className:"bcard__kebab",title:w("localTeams.more","更多"),disabled:lt,onClick:dt,children:lt?c.jsx(ge,{}):c.jsx(Js,{})}),Y&&Pl.createPortal(c.jsx("div",{className:"bcard__menu bcard__menu--portal","data-id":"TeamCard-menu",role:"menu",ref:k,style:{position:"fixed",top:I.top,left:I.left,width:P},onClick:X=>X.stopPropagation(),children:c.jsx("button",{type:"button","data-id":"TeamCard-reload",className:"bcard__menu-item",onClick:ot,children:w("localTeams.reloadWindow","刷新窗口")})}),document.body)]})]})]}),c.jsxs("div",{className:"bcard__body",children:[c.jsx("h3",{className:"bcard__name",title:D,children:D}),c.jsx("div",{className:"bcard__host",title:T&&q||"",children:T?q||w("teamCard.noHost","未填访问地址"):o.runtime_region||o.region||"—"}),c.jsxs("div",{className:"bcard__meta",children:[c.jsx("span",{className:"bcard__chip",children:R}),!T&&o.membership_status&&o.membership_status!=="active"&&c.jsx("span",{className:"bcard__chip",children:o.membership_status})]})]}),c.jsxs("button",{type:"button",className:"bcard__cta",onClick:A,disabled:!g,children:[c.jsx(_u,{}),c.jsx("span",{children:g?w("localTeams.open","打开"):T?w("teamCard.noHost","未填访问地址"):w("teamCard.noUrl","无 URL")})]})]})}function Gs(){return c.jsxs("div",{className:"brand",children:[c.jsx("div",{className:"brand-mark",children:c.jsx(bm,{})}),c.jsxs("div",{className:"brand-text",children:[c.jsx("div",{className:"brand-name",children:"CiCy Desktop"}),c.jsx("div",{className:"brand-sub",children:"团队 AI 协作工作台"})]})]})}function bm(){return c.jsx("svg",{width:"22",height:"22",viewBox:"0 0 96 96",fill:"none",children:c.jsx("path",{d:"M48 11L39.5 33.3L16 29.5L31 48L16 66.5L39.5 62.7L48 85L56.5 62.7L80 66.5L65 48L80 29.5L56.5 33.3Z",fill:"white",stroke:"white",strokeWidth:"8",strokeLinejoin:"round",strokeLinecap:"round"})})}function _u(){return c.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2",strokeLinecap:"round",strokeLinejoin:"round",children:[c.jsx("line",{x1:"5",y1:"12",x2:"19",y2:"12"}),c.jsx("polyline",{points:"12 5 19 12 12 19"})]})}function ge(){return c.jsx("svg",{className:"spin",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.4",strokeLinecap:"round",children:c.jsx("path",{d:"M21 12a9 9 0 1 1-6.2-8.55"})})}function Sm(){return c.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[c.jsx("rect",{x:"3",y:"4",width:"18",height:"12",rx:"2"}),c.jsx("line",{x1:"2",y1:"20",x2:"22",y2:"20"})]})}function Js(){return c.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:[c.jsx("circle",{cx:"12",cy:"5",r:"1.7"}),c.jsx("circle",{cx:"12",cy:"12",r:"1.7"}),c.jsx("circle",{cx:"12",cy:"19",r:"1.7"})]})}function s0(){return c.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[c.jsx("circle",{cx:"12",cy:"12",r:"10"}),c.jsx("line",{x1:"2",y1:"12",x2:"22",y2:"12"}),c.jsx("path",{d:"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"})]})}function In(o){try{return localStorage.getItem(o)||null}catch{return null}}function dm(o){return o?/timeout/i.test(o)?"登录超时,请重新点击 Login。":/state/i.test(o)?"校验失败,请重新登录。":/no token/i.test(o)?"登录未完成,请重试。":/bridge missing/i.test(o)?"无法连接到登录服务(preload 未就绪)。":o:""}var mm;const o0=((mm=window.cicy)==null?void 0:mm.platform)||(()=>{const o=navigator.userAgent||"";return/Mac/i.test(o)?"darwin":/Windows/i.test(o)?"win32":"linux"})();document.documentElement.dataset.platform=o0;document.documentElement.dataset.fullscreen="0";var hm,ym;(ym=(hm=window.cicy)==null?void 0:hm.window)!=null&&ym.onFullscreen&&window.cicy.window.onFullscreen(o=>{document.documentElement.dataset.fullscreen=o?"1":"0"});Ky.createRoot(document.getElementById("root")).render(c.jsx(Fy,{}));
365
+ `},w=(o,A)=>{var T,r;try{const D=(r=(T=window.cicyI18n)==null?void 0:T.t)==null?void 0:r.call(T,o);return D&&D!==o?D:A}catch{return A}},Wl="cicy_token",gu="cicy_access_token",vu="cicy_user_id",pu="https://cicy-ai.com";async function Su(o){var A,T,r;try{(r=(T=(A=window.cicy)==null?void 0:A.shell)==null?void 0:T.openExternal)==null||r.call(T,`${pu}/dash${o}`)}catch{}}const Qs=new Set;let Jy=0,Va=[];const Fn=new Map;function nm(){Qs.forEach(o=>o(Va))}const de={show(o={}){const A=o.id||`t${++Jy}`,T=Va.find(q=>q.id===A),r={id:A,status:"running",...T,...o};Va=T?Va.map(q=>q.id===A?r:q):[...Va,r],nm();const D=Fn.get(A);return D&&(clearTimeout(D),Fn.delete(A)),o.ttl&&Fn.set(A,setTimeout(()=>de.dismiss(A),o.ttl)),A},dismiss(o){Va=Va.filter(T=>T.id!==o);const A=Fn.get(o);A&&(clearTimeout(A),Fn.delete(o)),nm()}};function $y(){const[o,A]=N.useState(Va);return N.useEffect(()=>(Qs.add(A),()=>{Qs.delete(A)}),[]),o.length?c.jsx("div",{className:"toast-host","data-id":"ToastHost",children:o.map(T=>c.jsxs("div",{className:"toast","data-id":`Toast-${T.id}`,"data-status":T.status||"running",children:[c.jsx("button",{type:"button",className:"toast__x","data-id":"Toast-dismiss",onClick:()=>de.dismiss(T.id),"aria-label":"dismiss",children:"×"}),c.jsxs("span",{className:"toast__msg",children:[T.message,Number.isFinite(T.progress)?` ${T.progress}%`:""]}),Number.isFinite(T.progress)&&c.jsx("span",{className:"toast__bar",children:c.jsx("span",{style:{width:`${Math.min(100,T.progress)}%`}})})]},T.id))}):null}const Xs=new Set;let im=0,Wt=null;function Fl(){Xs.forEach(o=>o(Wt))}function bu(){return new Date().toTimeString().slice(0,8)}const ea={open({teamId:o,fromVer:A,toVer:T,onRetry:r}={}){Wt={teamId:o,fromVer:A||null,toVer:T||null,status:"running",phase:"download",logs:[],onRetry:r||null,lastAt:Date.now()},Fl()},push(o={}){if(!Wt)return;const A={id:++im,t:bu(),phase:o.phase||Wt.phase,status:o.status||"running",message:o.message||""};Wt={...Wt,phase:o.phase||Wt.phase,toVer:o.toVer||Wt.toVer,logs:[...Wt.logs,A],lastAt:Date.now()},Fl()},minimize(){Wt&&(Wt={...Wt,minimized:!0},Fl())},restore(){Wt&&(Wt={...Wt,minimized:!1},Fl())},finish({ok:o,message:A}={}){if(!Wt)return;const T=o?"done":"error",r={id:++im,t:bu(),phase:"done",status:T,message:A||(o?"更新完成":"更新失败")};Wt={...Wt,status:T,phase:"done",minimized:!1,logs:[...Wt.logs,r],lastAt:Date.now()},Fl()},close(){Wt=null,Fl()}},Ys=[["download","下载"],["swap","切换"],["done","完成"]];function Wy(){var R;const[o,A]=N.useState(Wt);N.useEffect(()=>(Xs.add(A),()=>{Xs.delete(A)}),[]);const T=N.useRef(null);N.useEffect(()=>{const C=T.current;C&&(C.scrollTop=C.scrollHeight)},[(R=o==null?void 0:o.logs)==null?void 0:R.length]);const[r,D]=N.useState(!1);if(N.useEffect(()=>{if(!o||o.status!=="running"){D(!1);return}const C=setInterval(()=>D(Date.now()-(o.lastAt||0)>25e3),3e3);return()=>clearInterval(C)},[o==null?void 0:o.lastAt,o==null?void 0:o.status]),!o)return null;const q=o.status==="running",V=Ys.findIndex(([C])=>C===o.phase);return o.minimized?c.jsxs("button",{type:"button",className:`drawer-min drawer-min--${o.status}`,"data-id":"UpdateDrawer-restore",onClick:()=>ea.restore(),children:[c.jsx("span",{className:"drawer-min__spark",children:q?c.jsx(ge,{}):o.status==="done"?"✓":o.status==="reboot"?"⟳":"!"}),c.jsxs("span",{className:"drawer-min__label",children:["更新 cicy-code",o.toVer?` · v${o.toVer}`:""]})]}):c.jsx("div",{className:"drawer-scrim","data-id":"UpdateDrawer-scrim",onClick:()=>q?ea.minimize():ea.close(),children:c.jsxs("div",{className:"drawer","data-id":"UpdateDrawer","data-status":o.status,onClick:C=>C.stopPropagation(),children:[c.jsxs("div",{className:"drawer__head",children:[c.jsxs("div",{className:"drawer__title",children:[c.jsx("span",{className:`drawer__spark drawer__spark--${o.status}`,children:q?c.jsx(ge,{}):o.status==="done"?"✓":o.status==="reboot"?"⟳":"!"}),c.jsxs("div",{children:[c.jsx("div",{className:"drawer__h",children:"更新 cicy-code"}),c.jsxs("div",{className:"drawer__sub",children:[o.fromVer?`v${o.fromVer}`:"当前"," → ",o.toVer?`v${o.toVer}`:"最新版"]})]})]}),c.jsx("div",{className:"drawer__headbtns",children:c.jsx("button",{type:"button",className:"drawer__x","data-id":"UpdateDrawer-min",title:"最小化",onClick:()=>ea.minimize(),"aria-label":"minimize",children:"‒"})})]}),c.jsx("div",{className:"drawer__steps","data-id":"UpdateDrawer-steps",children:Ys.map(([C,g],Y)=>{const H=o.status==="done"||Y<V,lt=Y===V&&q,J=o.status==="error"&&Y===V;return c.jsxs("div",{className:`drawer__step${lt?" is-active":""}${H?" is-done":""}${J?" is-error":""}`,children:[c.jsx("span",{className:"drawer__step-dot",children:H?"✓":J?"!":Y+1}),c.jsx("span",{className:"drawer__step-label",children:g}),Y<Ys.length-1&&c.jsx("span",{className:"drawer__step-bar"})]},C)})}),c.jsx("div",{className:"drawer__log","data-id":"UpdateDrawer-log",ref:T,children:o.logs.length===0?c.jsx("div",{className:"drawer__log-empty",children:"准备中…"}):o.logs.map(C=>c.jsxs("div",{className:"drawer__line","data-status":C.status,children:[c.jsx("span",{className:"drawer__t",children:C.t}),c.jsx("span",{className:`drawer__badge drawer__badge--${C.phase}`,children:{download:"下载",swap:"切换",done:"完成"}[C.phase]||C.phase}),c.jsx("span",{className:"drawer__linemsg",children:C.message})]},C.id))}),r&&q&&c.jsx("div",{className:"drawer__hint","data-id":"UpdateDrawer-stuck",children:"正在等待新版本就绪,耗时比平常久。可以放到后台继续,完成或失败都会提示。"}),c.jsx("div",{className:"drawer__foot",children:q?c.jsx(c.Fragment,{children:c.jsx("span",{className:"drawer__foot-status",children:"更新进行中…"})}):o.status==="error"?c.jsxs(c.Fragment,{children:[c.jsx("span",{className:"drawer__foot-status is-error",children:"更新失败"}),o.onRetry&&c.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"UpdateDrawer-retry",onClick:()=>o.onRetry(),children:"重试"}),c.jsx("button",{type:"button",className:"drawer__btn","data-id":"UpdateDrawer-dismiss",onClick:()=>ea.close(),children:"关闭"})]}):c.jsxs(c.Fragment,{children:[c.jsx("span",{className:"drawer__foot-status is-done",children:"已更新到最新"}),c.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"UpdateDrawer-finish",onClick:()=>ea.close(),children:"完成"})]})})]})})}function Fy(){const[o,A]=N.useState(void 0);N.useEffect(()=>{var B,at;if(!((at=(B=window.cicy)==null?void 0:B.terms)!=null&&at.status)){A(!0);return}window.cicy.terms.status(vm).then(x=>A(!!(x!=null&&x.accepted))).catch(()=>A(!0))},[]),N.useEffect(()=>{var x,K,L,et;const B=document.documentElement;try{B.setAttribute("data-platform",((x=window.cicy)==null?void 0:x.platform)||"linux")}catch{}B.setAttribute("data-fullscreen","0");let at;try{at=(et=(L=(K=window.cicy)==null?void 0:K.window)==null?void 0:L.onFullscreen)==null?void 0:et.call(L,Zt=>B.setAttribute("data-fullscreen",Zt?"1":"0"))}catch{}return()=>{try{at&&at()}catch{}}},[]);const[T,r]=N.useState(()=>In(Wl)),[D,q]=N.useState(()=>In(gu)),[V,R]=N.useState(()=>In(vu)),[C,g]=N.useState(()=>!In(Wl)),[Y,H]=N.useState(!1),[lt,J]=N.useState(""),[I,W]=N.useState(""),[it,Z]=N.useState(null),[k,P]=N.useState(null),[dt,ot]=N.useState(!1),[X,M]=N.useState(""),pt=N.useRef(!1),[U,gt]=N.useState(null),[qt,Lt]=N.useState(!1),[Yt,me]=N.useState(!1),[ut,S]=N.useState("all"),O=N.useCallback(async(B,at)=>{var K,L,et,Zt;if(!B)return;if(!((L=(K=window.cicy)==null?void 0:K.cloud)!=null&&L.fetch)){M("cloud fetch bridge missing");return}ot(!0),M("");const x={Authorization:`Bearer ${B}`};try{const[ue,Xt]=await Promise.all([window.cicy.cloud.fetch(`${pu}/api/user/self`,{headers:x}),window.cicy.cloud.fetch(`${pu}/api/teams`,{headers:x})]);if((Xt==null?void 0:Xt.status)===401){if(!pt.current&&((Zt=(et=window.cicy)==null?void 0:et.auth)!=null&&Zt.loginStart)){pt.current=!0,M("会话已过期,正在重新登录…");try{await window.cicy.auth.loginStart()}catch{}}return}if(!(Xt!=null&&Xt.ok))throw new Error(`/api/teams ${(Xt==null?void 0:Xt.status)||"?"} ${(Xt==null?void 0:Xt.error)||""}`);const kt=JSON.parse(Xt.body||"{}");if(P(Array.isArray(kt==null?void 0:kt.teams)?kt.teams:[]),ue!=null&&ue.ok)try{const Ae=JSON.parse(ue.body||"{}");Z((Ae==null?void 0:Ae.success)===!1?null:(Ae==null?void 0:Ae.data)||null)}catch{Z(null)}else Z(null)}catch(ue){M(ue.message||String(ue))}finally{ot(!1)}},[]),Q=N.useRef("");N.useEffect(()=>{Q.current=T||D||""},[T,D]);const mt=N.useCallback(async()=>{var at,x;const B=Q.current;if(!(!B||!((x=(at=window.cicy)==null?void 0:at.cloud)!=null&&x.fetch)))try{const K=await window.cicy.cloud.fetch(`${pu}/api/teams`,{headers:{Authorization:`Bearer ${B}`}});if(K!=null&&K.ok){const L=JSON.parse(K.body||"{}");Array.isArray(L==null?void 0:L.teams)&&P(L.teams)}}catch{}},[]);N.useEffect(()=>{const B=T||D;B&&O(B,V)},[T,D,V,O]);const st=N.useCallback(async()=>{var B,at;if((at=(B=window.cicy)==null?void 0:B.localTeams)!=null&&at.list){Lt(!0);try{const x=await window.cicy.localTeams.list({refresh:!0});gt(Array.isArray(x)?x:[])}catch{gt([])}finally{Lt(!1),me(!0)}}},[]),d=N.useCallback(async(B,at)=>{var K,L;if(!((L=(K=window.cicy)==null?void 0:K.localTeams)!=null&&L.update))return{ok:!1,error:"no_bridge"};let x;try{x=await window.cicy.localTeams.update(B,{name:String(at||"").trim()||w("localTeams.unnamed","未命名")})}catch(et){x={ok:!1,error:(et==null?void 0:et.message)||String(et)}}return await st(),x||{ok:!1,error:"no_result"}},[st]);N.useEffect(()=>{let B,at=!1;const x=3e3,K=3e4,L=async()=>{var ue,Xt,kt;try{await((kt=(Xt=(ue=window.cicy)==null?void 0:ue.localTeams)==null?void 0:Xt.syncCloud)==null?void 0:kt.call(Xt))}catch{}await Promise.all([st(),mt()])},et=()=>{if(at)return;const ue=document.visibilityState==="visible";B=setTimeout(async()=>{document.visibilityState==="visible"?await L():await st(),et()},ue?x:K)};L(),et();const Zt=()=>{document.visibilityState==="visible"&&L()};return document.addEventListener("visibilitychange",Zt),window.addEventListener("focus",Zt),()=>{at=!0,clearTimeout(B),document.removeEventListener("visibilitychange",Zt),window.removeEventListener("focus",Zt)}},[st,mt]),N.useEffect(()=>{var at,x;if(!((x=(at=window.cicy)==null?void 0:at.localTeams)!=null&&x.onWebviewRelay))return;const B=window.cicy.localTeams.onWebviewRelay(async({reqId:K,msg:L})=>{let et={ok:!1,error:"unknown relay type"};try{(L==null?void 0:L.type)==="localTeams:add"?et=await window.cicy.localTeams.add(L.spec||{}):(L==null?void 0:L.type)==="localTeams:remove"?et=await window.cicy.localTeams.remove(L.id):(L==null?void 0:L.type)==="localTeams:update"?et=await window.cicy.localTeams.update(L.id,L.patch||{}):(L==null?void 0:L.type)==="localTeams:upgrade"?et=await window.cicy.localTeams.upgrade(L.id):(L==null?void 0:L.type)==="localTeams:list"&&(et={ok:!0,teams:await window.cicy.localTeams.list({refresh:!0})}),st()}catch(Zt){et={ok:!1,error:(Zt==null?void 0:Zt.message)||String(Zt)}}try{window.cicy.localTeams.replyWebviewRelay(K,et)}catch{}});return()=>{try{B==null||B()}catch{}}},[st]);const y=N.useCallback(async B=>{var at,x;if((x=(at=window.cicy)==null?void 0:at.localTeams)!=null&&x.open)try{await window.cicy.localTeams.open(B)}catch{}},[]);N.useEffect(()=>{var at,x;if(In(Wl)){g(!1);return}if(!((x=(at=window.cicy)==null?void 0:at.auth)!=null&&x.getSaved)){g(!1);return}let B=!1;return(async()=>{try{const K=await window.cicy.auth.getSaved();if(B)return;if(K!=null&&K.token){try{localStorage.setItem(Wl,K.token)}catch{}if(r(K.token),K.accessToken){try{localStorage.setItem(gu,K.accessToken)}catch{}q(K.accessToken)}if(K.userId){try{localStorage.setItem(vu,String(K.userId))}catch{}R(String(K.userId))}}}catch{}finally{B||g(!1)}})(),()=>{B=!0}},[]),N.useEffect(()=>{var B,at;if((at=(B=window.cicy)==null?void 0:B.auth)!=null&&at.onComplete)return window.cicy.auth.onComplete(x=>{if(H(!1),x!=null&&x.error){J(dm(x.error));return}if(x!=null&&x.token){pt.current=!1,M("");try{localStorage.setItem(Wl,x.token)}catch{}if(r(x.token),x.accessToken){try{localStorage.setItem(gu,x.accessToken)}catch{}q(x.accessToken)}if(x.userId){try{localStorage.setItem(vu,String(x.userId))}catch{}R(String(x.userId))}J(""),W(x.reused?"已恢复你之前的登录":"登录成功"),setTimeout(()=>W(""),3e3)}})},[]);async function G(){var at,x;if(!((x=(at=window.cicy)==null?void 0:at.auth)!=null&&x.loginStart)){J("auth bridge missing");return}J(""),H(!0);const B=await window.cicy.auth.loginStart();B!=null&&B.ok||(H(!1),J(dm((B==null?void 0:B.error)||"login start failed")))}function $(){var B,at,x;try{localStorage.removeItem(Wl),localStorage.removeItem(gu),localStorage.removeItem(vu)}catch{}try{(x=(at=(B=window.cicy)==null?void 0:B.auth)==null?void 0:at.logout)==null||x.call(at)}catch{}r(null),q(null),R(null),Z(null),P(null),J(""),M("")}if(o===void 0)return c.jsxs("div",{className:"shell","data-id":"TermsCheckingSplash",children:[c.jsx("div",{className:"glow","aria-hidden":!0}),c.jsxs("div",{className:"card",children:[c.jsx(Gs,{}),c.jsx("div",{className:"spinner-row",children:c.jsx(ge,{})})]})]});if(!o)return c.jsx(pm,{onAgree:()=>A(!0)});if(!T&&C)return c.jsxs("div",{className:"shell","data-id":"AuthRestoringSplash",children:[c.jsx("div",{className:"glow","aria-hidden":!0}),c.jsxs("div",{className:"card",children:[c.jsx(Gs,{}),c.jsxs("div",{className:"spinner-row",children:[c.jsx(ge,{}),c.jsx("span",{children:"正在恢复登录…"})]})]})]});if(!T)return c.jsxs("div",{className:"shell",children:[c.jsx("div",{className:"glow","aria-hidden":!0}),c.jsxs("div",{className:"card",children:[c.jsx(Gs,{}),!Y&&c.jsxs(c.Fragment,{children:[c.jsx("p",{className:"tagline",children:"登录以同步你的团队、配置与 AI 助手"}),c.jsxs("button",{className:"btn-primary",onClick:G,children:[c.jsx("span",{children:"使用浏览器登录"}),c.jsx(_u,{})]}),c.jsx("p",{className:"hint",children:"点击后会自动打开浏览器"})]}),Y&&c.jsxs(c.Fragment,{children:[c.jsx("p",{className:"tagline",children:"已在浏览器打开登录页,等待你完成…"}),c.jsxs("div",{className:"spinner-row",children:[c.jsx(ge,{}),c.jsx("span",{children:"等待回调"})]}),c.jsx("button",{className:"btn-ghost",onClick:()=>{var B,at,x;(x=(at=(B=window.cicy)==null?void 0:B.auth)==null?void 0:at.loginCancel)==null||x.call(at),H(!1)},children:"取消"})]}),lt&&c.jsx("div",{className:"error",children:lt})]})]});const ft=(U||[]).find(B=>om(B.base_url))||null,rt=(U||[]).filter(B=>Vs(B.base_url)),Tt=(U||[]).filter(B=>!Vs(B.base_url)&&!om(B.base_url)),Rt=rt.length,Et=Tt.length,Re=(k||[]).filter(B=>!B.is_local&&B.kind!=="local"),aa=Re.length,We=ut==="all"||ut==="local",Ka=ut==="all"||ut==="custom",He=ut==="all"||ut==="cloud";return c.jsxs("div",{className:"shell shell--app",children:[c.jsx("div",{className:"glow glow--app","aria-hidden":!0}),c.jsxs("div",{className:"shell__left",children:[c.jsx(t0,{me:it,welcome:I,onLogout:$,mitmTeam:rt.length>0?rt[0]:null}),c.jsxs("main",{className:"main",children:[c.jsxs("div",{className:"app__tabsrow",children:[c.jsx("div",{className:"app__tabs",children:[{k:"all",label:"全部",n:Rt+Et+aa},{k:"local",label:"本地",n:Rt},{k:"cloud",label:"私有云",n:aa},{k:"custom",label:"自定义",n:Et}].map(({k:B,label:at,n:x})=>c.jsxs("button",{type:"button",className:`app__tab ${ut===B?"is-active":""}`,onClick:()=>S(B),children:[at,c.jsx("span",{className:"app__tab-count",children:x})]},B))}),c.jsxs("button",{type:"button","data-id":"AddTeamButton",className:"app__add-team",title:w("teams.addHint","在云端新建私有云团队"),onClick:()=>Su("?tab=private"),children:["+ ",w("teams.add","新加团队")]})]}),X&&c.jsxs("div",{className:"error",style:{marginBottom:12},children:["云端: ",X,c.jsx("button",{className:"btn-ghost",style:{marginLeft:8},onClick:()=>O(T||D,V),children:"重试"})]}),c.jsxs("div",{className:"app__grid",children:[We&&rt.map(B=>c.jsx(sm,{team:B,onOpen:()=>y(B.id),onRename:d,onRefresh:st},"local:"+B.id)),We&&rt.length===0&&c.jsxs("div",{"data-id":"LocalTeamPlaceholder",className:"bcard bcard--local",children:[c.jsx("div",{className:"bcard__accent"}),c.jsx("div",{className:"bcard__top",children:c.jsxs("div",{className:"bcard__pill",children:[c.jsx("span",{className:"bcard__dot","data-tone":"warn"}),c.jsx(Sm,{})]})}),c.jsxs("div",{className:"bcard__body",children:[c.jsx("h3",{className:"bcard__name",children:"本地团队"}),c.jsx("div",{className:"bcard__host",children:"http://127.0.0.1:8008"}),c.jsx("div",{className:"bcard__meta"})]}),c.jsxs("button",{type:"button",className:"bcard__cta",disabled:!0,children:[c.jsx(ge,{}),c.jsx("span",{children:Yt?"正在启动,就绪后自动加入…":"检测中…"})]})]}),We&&c.jsx(u0,{dockerTeam:ft,onOpen:B=>{var at,x,K;B?y(B):(K=(x=(at=window.cicy)==null?void 0:at.tabs)==null?void 0:x.open)==null||K.call(x,"http://127.0.0.1:8009","Docker cicy-code")},onRefresh:st}),Ka&&Tt.map(B=>c.jsx(sm,{team:B,onOpen:()=>y(B.id),onRename:d,onRefresh:st},"custom:"+B.id)),He&&Re.map(B=>c.jsx(c0,{team:B,onOpen:()=>{var x,K,L;const at=B.kind==="private"?B.host_url:B.workspace_url||B.workspace_direct_url;at&&((L=(K=(x=window.cicy)==null?void 0:x.tabs)==null?void 0:K.open)==null||L.call(K,at,B.name||B.title||""))}},"cloud:"+B.id))]}),!dt&&!X&&k&&k.length===0&&!(U!=null&&U.length)&&c.jsx("div",{className:"empty",style:{marginTop:14},children:"还没有团队 — 安装本地 cicy-code 起一个本地 team,或在云端创建。"})]})]}),c.jsx($y,{}),c.jsx(Wy,{}),c.jsx(i0,{})]})}function Iy({onClose:o}){const[A,T]=N.useState(null),[r,D]=N.useState(""),[q,V]=N.useState(!1),[R,C]=N.useState(""),g=typeof window<"u"&&window.cicy&&window.cicy.trustedOrigins||null,Y=N.useCallback(async()=>{try{T(g&&await g.list()||[])}catch{T([])}},[g]);N.useEffect(()=>{Y()},[Y]);const H=async()=>{const I=r.trim();if(!(!I||q||!g)){V(!0),C("");try{const W=await g.add(I);W&&W.ok===!1?C(W.error||w("trustedSites.addFailed","添加失败")):(D(""),T(W&&W.origins||await g.list()))}catch(W){C(String(W&&W.message||W))}finally{V(!1)}}},lt=async I=>{if(!(q||!g)){V(!0),C("");try{const W=await g.remove(I);W&&W.ok===!1?C(W.error||w("trustedSites.removeFailed","删除失败")):T(W&&W.origins||await g.list())}catch(W){C(String(W&&W.message||W))}finally{V(!1)}}},J={overlay:{position:"fixed",inset:0,zIndex:2e3,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(0,0,0,.62)",backdropFilter:"blur(3px)"},card:{width:560,maxWidth:"94vw",maxHeight:"82vh",display:"flex",flexDirection:"column",background:"#101012",border:"1px solid rgba(255,255,255,.09)",borderRadius:16,boxShadow:"0 24px 64px rgba(0,0,0,.55)",overflow:"hidden",color:"#e4e4e7"},head:{display:"flex",alignItems:"center",gap:8,padding:"14px 16px",borderBottom:"1px solid rgba(255,255,255,.06)"},title:{margin:0,fontSize:15,fontWeight:600,flex:1},x:{background:"transparent",border:"none",color:"#a1a1aa",fontSize:16,cursor:"pointer",lineHeight:1,padding:4},warn:{margin:"14px 16px 0",padding:"10px 12px",fontSize:12.5,lineHeight:1.55,color:"#fca5a5",background:"rgba(239,68,68,.08)",border:"1px solid rgba(239,68,68,.25)",borderRadius:10},addRow:{display:"flex",gap:8,padding:"12px 16px 4px"},input:{flex:1,minWidth:0,background:"#161618",border:"1px solid rgba(255,255,255,.1)",borderRadius:9,padding:"9px 11px",color:"#e4e4e7",fontSize:13,outline:"none"},addBtn:{background:"rgba(255,255,255,.1)",border:"none",borderRadius:9,padding:"0 16px",color:"#fff",fontSize:13,fontWeight:500,cursor:"pointer"},err:{margin:"6px 16px 0",fontSize:12,color:"#fca5a5"},listWrap:{margin:"10px 16px 16px",border:"1px solid rgba(255,255,255,.07)",borderRadius:10,overflow:"auto",flex:1,minHeight:80},row:{display:"flex",alignItems:"center",gap:10,padding:"9px 12px",borderTop:"1px solid rgba(255,255,255,.05)"},host:I=>({flex:1,fontFamily:"ui-monospace,SFMono-Regular,Menlo,monospace",fontSize:13,color:I?"#71717a":"#e4e4e7",wordBreak:"break-all"}),tag:{fontSize:11,color:"#71717a",background:"rgba(255,255,255,.05)",borderRadius:6,padding:"2px 7px"},rm:{background:"transparent",border:"none",color:"#a1a1aa",fontSize:12,cursor:"pointer",padding:"3px 6px",borderRadius:6},muted:{padding:"16px",textAlign:"center",color:"#71717a",fontSize:12.5}};return Pl.createPortal(c.jsx("div",{style:J.overlay,onClick:o,"data-id":"TrustedSitesModal",children:c.jsxs("div",{style:J.card,onClick:I=>I.stopPropagation(),children:[c.jsxs("div",{style:J.head,children:[c.jsx("h2",{style:J.title,children:w("trustedSites.title","受信任站点")}),c.jsx("button",{type:"button",style:J.x,onClick:o,"aria-label":"close",children:"✕"})]}),c.jsx("div",{style:J.warn,children:w("trustedSites.warn","⚠ 列表中的站点可以在你的电脑上执行命令(exec)。只添加你完全信任的地址。")}),c.jsxs("div",{style:J.addRow,children:[c.jsx("input",{"data-id":"trusted-sites-input",style:J.input,value:r,onChange:I=>D(I.target.value),onKeyDown:I=>{I.key==="Enter"&&H()},placeholder:w("trustedSites.placeholder","添加站点,如 app.cicy-ai.com 或 my-cloud.example.org")}),c.jsx("button",{type:"button","data-id":"trusted-sites-add",style:{...J.addBtn,opacity:q||!r.trim()?.5:1},onClick:H,disabled:q||!r.trim(),children:w("trustedSites.add","添加")})]}),R&&c.jsx("div",{style:J.err,children:R}),c.jsx("div",{style:J.listWrap,children:A===null?c.jsx("div",{style:J.muted,children:w("trustedSites.loading","加载中…")}):A.length===0?c.jsx("div",{style:J.muted,children:w("trustedSites.empty","暂无")}):A.map(I=>c.jsxs("div",{style:J.row,"data-id":"trusted-sites-row",children:[c.jsx("span",{style:J.host(I.builtin),children:I.host}),I.builtin?c.jsx("span",{style:J.tag,children:w("trustedSites.builtin","系统")}):c.jsx("button",{type:"button",style:J.rm,onClick:()=>lt(I.host),disabled:q,children:w("trustedSites.remove","删除")})]},I.host))})]})}),document.body)}function Py({onClose:o}){const[A,T]=N.useState(null),[r,D]=N.useState(""),[q,V]=N.useState(""),[R,C]=N.useState(!1),g=typeof window<"u"&&window.cicy&&window.cicy.rpcAudit||null,Y=N.useCallback(async()=>{C(!0),D("");try{const U=g&&await g.tail(400);!U||U.ok===!1?(D(U&&U.error||w("audit.loadFailed","读取失败")),T([])):(T(U.entries||[]),V(U.path||""))}catch(U){D(String(U&&U.message||U)),T([])}finally{C(!1)}},[g]);N.useEffect(()=>{Y()},[Y]);const[H,lt]=N.useState("all"),[J,I]=N.useState(""),W=U=>{try{return new Date(U).toLocaleString()}catch{return U||""}},it=U=>{if(U.kind==="auth"){const gt=/deny/.test(U.decision||"");return{text:U.decision||"auth",color:gt?"#fca5a5":"#86efac",bg:gt?"rgba(239,68,68,.14)":"rgba(34,197,94,.14)"}}if(U.kind==="rpc"){const gt=U.ok!==!1&&!U.error;return{text:gt?"ok":"err",color:gt?"#86efac":"#fca5a5",bg:gt?"rgba(34,197,94,.14)":"rgba(239,68,68,.14)"}}return{text:U.kind||"log",color:"#a1a1aa",bg:"rgba(255,255,255,.06)"}},Z=U=>U.kind==="auth"?`${U.gate||""}${U.decision?" · "+U.decision:""}`:U.kind==="rpc"?`${U.tool||""}${U.dangerous?" ⚠":""}`:U.kind||"",k=U=>U.error||U.args||(U.kind==="rpc"?U.channel:"")||"",P=A||[],dt=J.trim().toLowerCase(),ot=P.filter(U=>H!=="all"&&U.kind!==H?!1:dt?[U.origin,U.host,U.tool,U.gate,U.decision,U.channel,U.args,U.error,U.kind].filter(Boolean).join(" ").toLowerCase().includes(dt):!0),X="186px 104px minmax(160px,1.1fr) minmax(150px,1.1fr) minmax(220px,1.8fr)",M={overlay:{position:"fixed",inset:0,zIndex:2e3,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(0,0,0,.66)",backdropFilter:"blur(4px)"},card:{width:"96vw",height:"92vh",maxWidth:1480,display:"flex",flexDirection:"column",background:"#0d0d0f",border:"1px solid rgba(255,255,255,.09)",borderRadius:18,boxShadow:"0 32px 80px rgba(0,0,0,.6)",overflow:"hidden",color:"#e4e4e7"},head:{display:"flex",alignItems:"center",gap:14,padding:"20px 24px",borderBottom:"1px solid rgba(255,255,255,.07)"},titleWrap:{flex:1,minWidth:0},title:{margin:0,fontSize:21,fontWeight:650,letterSpacing:.2},subtitle:{margin:"3px 0 0",fontSize:12.5,color:"#71717a",fontFamily:"ui-monospace,SFMono-Regular,Menlo,monospace",wordBreak:"break-all"},count:{fontSize:12.5,color:"#a1a1aa",whiteSpace:"nowrap"},refresh:{background:"rgba(255,255,255,.1)",border:"none",borderRadius:9,padding:"9px 16px",color:"#fff",fontSize:13,fontWeight:500,cursor:"pointer"},x:{background:"transparent",border:"none",color:"#a1a1aa",fontSize:20,cursor:"pointer",lineHeight:1,padding:6},toolbar:{display:"flex",alignItems:"center",gap:10,padding:"14px 24px",borderBottom:"1px solid rgba(255,255,255,.05)"},chips:{display:"flex",gap:6},chip:U=>({background:U?"rgba(255,255,255,.14)":"transparent",border:"1px solid rgba(255,255,255,.12)",borderRadius:999,padding:"6px 16px",color:U?"#fff":"#a1a1aa",fontSize:13,cursor:"pointer"}),search:{flex:1,minWidth:0,background:"#161618",border:"1px solid rgba(255,255,255,.1)",borderRadius:10,padding:"10px 14px",color:"#e4e4e7",fontSize:13.5,outline:"none"},err:{margin:"10px 24px 0",fontSize:12.5,color:"#fca5a5"},tableWrap:{flex:1,overflow:"auto",margin:"0"},theadRow:{position:"sticky",top:0,zIndex:1,display:"grid",gridTemplateColumns:X,gap:16,padding:"12px 24px",background:"#141417",borderBottom:"1px solid rgba(255,255,255,.08)",fontSize:11.5,letterSpacing:.6,textTransform:"uppercase",color:"#71717a",fontWeight:600},row:{display:"grid",gridTemplateColumns:X,gap:16,padding:"13px 24px",borderBottom:"1px solid rgba(255,255,255,.045)",alignItems:"center"},time:{fontSize:12.5,color:"#a1a1aa",fontFamily:"ui-monospace,SFMono-Regular,Menlo,monospace",whiteSpace:"nowrap"},badge:U=>({justifySelf:"start",fontSize:11.5,color:U.color,background:U.bg,borderRadius:7,padding:"3px 10px",whiteSpace:"nowrap",fontWeight:500}),cell:{fontSize:13,color:"#d4d4d8",fontFamily:"ui-monospace,SFMono-Regular,Menlo,monospace",wordBreak:"break-all"},detail:{fontSize:12.5,color:"#8b8b93",wordBreak:"break-all"},muted:{padding:"60px 24px",textAlign:"center",color:"#71717a",fontSize:14}},pt=U=>c.jsx("div",{children:U});return Pl.createPortal(c.jsx("div",{style:M.overlay,onClick:o,"data-id":"AuditLogModal",children:c.jsxs("div",{style:M.card,onClick:U=>U.stopPropagation(),children:[c.jsxs("div",{style:M.head,children:[c.jsxs("div",{style:M.titleWrap,children:[c.jsx("h2",{style:M.title,children:w("audit.title","审计日志")}),q&&c.jsx("p",{style:M.subtitle,children:q})]}),c.jsxs("span",{style:M.count,children:[w("audit.count","共")," ",ot.length,H!=="all"||dt?` / ${P.length}`:""]}),c.jsx("button",{type:"button","data-id":"audit-refresh",style:{...M.refresh,opacity:R?.5:1},onClick:Y,disabled:R,children:w("audit.refresh","刷新")}),c.jsx("button",{type:"button",style:M.x,onClick:o,"aria-label":"close",children:"✕"})]}),c.jsxs("div",{style:M.toolbar,children:[c.jsxs("div",{style:M.chips,children:[c.jsx("button",{type:"button",style:M.chip(H==="all"),onClick:()=>lt("all"),children:w("audit.all","全部")}),c.jsx("button",{type:"button",style:M.chip(H==="rpc"),onClick:()=>lt("rpc"),children:w("audit.rpc","RPC 调用")}),c.jsx("button",{type:"button",style:M.chip(H==="auth"),onClick:()=>lt("auth"),children:w("audit.auth","授权决定")})]}),c.jsx("input",{"data-id":"audit-search",style:M.search,value:J,onChange:U=>I(U.target.value),placeholder:w("audit.search","搜索来源 / 工具 / 命令…")})]}),r&&c.jsx("div",{style:M.err,children:r}),c.jsxs("div",{style:M.tableWrap,children:[c.jsxs("div",{style:M.theadRow,children:[pt(w("audit.colTime","时间")),pt(w("audit.colType","类型")),pt(w("audit.colSource","来源")),pt(w("audit.colOp","操作")),pt(w("audit.colDetail","详情"))]}),A===null?c.jsx("div",{style:M.muted,children:w("audit.loading","加载中…")}):ot.length===0?c.jsx("div",{style:M.muted,children:P.length===0?w("audit.empty","暂无审计记录"):w("audit.noMatch","无匹配记录")}):ot.map((U,gt)=>{const qt=it(U);return c.jsxs("div",{style:M.row,"data-id":"audit-row",children:[c.jsx("span",{style:M.time,children:W(U.ts)}),c.jsx("span",{style:M.badge(qt),children:qt.text}),c.jsx("span",{style:M.cell,children:U.origin||U.host||"—"}),c.jsx("span",{style:M.cell,children:Z(U)||"—"}),c.jsx("span",{style:M.detail,children:k(U)||"—"})]},gt)})]})]})}),document.body)}function t0({me:o,welcome:A,onLogout:T,mitmTeam:r}){const D=(o==null?void 0:o.display_name)||(o==null?void 0:o.username)||"…",q=D.slice(0,1).toUpperCase(),[V,R]=N.useState(!1),[C,g]=N.useState(!1),[Y,H]=N.useState(!1),[lt,J]=N.useState(!1),[I,W]=N.useState(""),it=N.useRef(null);N.useEffect(()=>{var P,dt,ot;let k=!0;return(ot=(dt=(P=window.cicy)==null?void 0:P.app)==null?void 0:dt.getVersion)==null||ot.call(dt).then(X=>{k&&W(typeof X=="string"?X:String((X==null?void 0:X.desktop)||""))}).catch(()=>{}),()=>{k=!1}},[]),N.useEffect(()=>{if(!V)return;const k=P=>{it.current&&!it.current.contains(P.target)&&R(!1)};return document.addEventListener("mousedown",k),()=>document.removeEventListener("mousedown",k)},[V]);const Z=k=>{Su(k),R(!1)};return c.jsxs("header",{className:"topbar",children:[c.jsxs("div",{className:"brand-mini",children:[c.jsx("div",{className:"brand-mark sm",children:c.jsx(bm,{})}),c.jsx("span",{className:"brand-name",children:"CiCy Desktop"})]}),c.jsxs("div",{className:"user-chip","data-id":"UserChip",ref:it,children:[A&&c.jsx("span",{className:"welcome",children:A}),c.jsxs("button",{type:"button","data-id":"UserChip-trigger",className:`user-chip__trigger${V?" is-open":""}`,onClick:()=>R(k=>!k),children:[c.jsx("div",{className:"avatar",children:q}),c.jsx("span",{className:"user-name",children:D}),c.jsx("span",{className:"user-chip__caret","aria-hidden":!0,children:"▾"})]}),V&&c.jsxs("div",{className:"user-chip__menu","data-id":"UserChip-menu",role:"menu",children:[c.jsx("button",{type:"button","data-id":"UserChip-wallet",className:"user-chip__menu-item",onClick:()=>Z("?view=wallet"),children:"我的钱包"}),c.jsx("button",{type:"button","data-id":"UserChip-billing",className:"user-chip__menu-item",onClick:()=>Z("?view=usage"),children:"我的账单"}),c.jsx("button",{type:"button","data-id":"UserChip-trusted-sites",className:"user-chip__menu-item",onClick:()=>{R(!1),g(!0)},children:w("trustedSites.menu","受信任站点")}),c.jsx("button",{type:"button","data-id":"UserChip-audit-log",className:"user-chip__menu-item",onClick:()=>{R(!1),H(!0)},children:w("audit.menu","审计日志")}),c.jsx("button",{type:"button","data-id":"UserChip-terms",className:"user-chip__menu-item",onClick:()=>{R(!1),J(!0)},children:w("firstRunTerms.menu","用户协议")}),r&&c.jsx("div",{className:"user-chip__menu-mitm","data-id":"UserChip-mitm",onClick:k=>k.stopPropagation(),children:c.jsx(e0,{team:r,variant:"menu"})}),c.jsx("div",{className:"user-chip__menu-sep","aria-hidden":!0}),c.jsx("button",{type:"button","data-id":"UserChip-logout",className:"user-chip__menu-item is-danger",onClick:()=>{R(!1),T()},children:"退出"}),c.jsxs("div",{className:"user-chip__menu-version","data-id":"UserChip-version",children:["CiCy Desktop ",I?`v${I}`:"…"]})]})]}),C&&c.jsx(Iy,{onClose:()=>g(!1)}),Y&&c.jsx(Py,{onClose:()=>H(!1)}),lt&&c.jsx(pm,{onClose:()=>J(!1)})]})}function pm({onAgree:o,onClose:A}){var it;const[T,r]=N.useState(!1),[D,q]=N.useState(!1),[V,R]=N.useState(!1),C=(((it=window.cicyI18n)==null?void 0:it.locale)||"en").startsWith("zh")?"zh-CN":"en",g=(Z,k)=>w(`firstRunTerms.${Z}`,k),Y=!!A,H=[1,2,3,4,5,6].map(Z=>g(`summary${Z}`,"")),lt=Z=>{const k=Z.currentTarget;k.scrollHeight-k.scrollTop-k.clientHeight<24&&r(!0)},J=N.useRef(null);N.useEffect(()=>{const Z=J.current;Z&&Z.scrollHeight<=Z.clientHeight+24&&r(!0)},[D]);const I=async()=>{var Z,k,P;if(!(V||!T)){R(!0);try{await((P=(k=(Z=window.cicy)==null?void 0:Z.terms)==null?void 0:k.agree)==null?void 0:P.call(k,vm)),o==null||o()}catch{o==null||o()}}},W=()=>{var Z,k,P;try{(P=(k=(Z=window.cicy)==null?void 0:Z.terms)==null?void 0:k.decline)==null||P.call(k)}catch{}};return c.jsxs("div",{className:Y?"terms-gate terms-gate--review":"shell terms-gate","data-id":"FirstRunTermsGate",style:Y?{position:"fixed",inset:0,zIndex:1e3,background:"rgba(8,9,14,.72)",backdropFilter:"blur(4px)"}:void 0,onClick:Y?Z=>{Z.target===Z.currentTarget&&A()}:void 0,children:[c.jsx("div",{className:"glow","aria-hidden":!0}),c.jsxs("div",{className:"terms-gate__panel",children:[c.jsx("h1",{className:"terms-gate__title","data-id":"FirstRunTermsGate-title",children:g("title","用户协议与授权说明")}),c.jsx("p",{className:"terms-gate__subtitle",children:g("subtitle","使用 CiCy Desktop 前,请阅读并同意以下条款")}),c.jsxs("div",{className:"terms-gate__body",ref:J,onScroll:lt,"data-id":"FirstRunTermsGate-body",children:[c.jsx("h2",{className:"terms-gate__h2",children:g("summaryTitle","一眼看懂")}),c.jsx("ol",{className:"terms-gate__summary",children:H.filter(Boolean).map((Z,k)=>c.jsx("li",{children:Z},k))}),D?c.jsx("pre",{className:"terms-gate__fulltext","data-id":"FirstRunTermsGate-fulltext",children:lm[C]||lm.en}):c.jsx("button",{className:"terms-gate__viewfull","data-id":"FirstRunTermsGate-viewfull",onClick:()=>q(!0),children:g("viewFull","查看完整条款")})]}),Y?c.jsx("div",{className:"terms-gate__actions",children:c.jsx("button",{"data-id":"FirstRunTermsGate-close",className:"terms-gate__btn",onClick:A,children:g("close","关闭")})}):c.jsxs(c.Fragment,{children:[!T&&c.jsx("div",{className:"terms-gate__scrollhint","data-id":"FirstRunTermsGate-scrollhint",children:g("scrollHint","请阅读至底部以继续")}),c.jsxs("div",{className:"terms-gate__actions",children:[c.jsx("button",{"data-id":"FirstRunTermsGate-decline",className:"terms-gate__btn terms-gate__btn--ghost",onClick:W,children:g("decline","不同意并退出")}),c.jsx("button",{"data-id":"FirstRunTermsGate-agree",className:"terms-gate__btn",disabled:!T||V,title:T?"":g("mustAgree","未同意则无法使用本软件。"),onClick:I,children:g("agree","同意并继续")})]})]})]})]})}function e0({team:o,variant:A}){const[T,r]=N.useState(void 0),[D,q]=N.useState(""),[V,R]=N.useState(""),C=((o==null?void 0:o.base_url)||"").replace(/\/$/,""),g=(o==null?void 0:o.api_token)||"",Y=N.useCallback(async(Z,k={})=>{var ot,X;if(!((X=(ot=window.cicy)==null?void 0:ot.cloud)!=null&&X.fetch))throw new Error("bridge missing");const P=await window.cicy.cloud.fetch(`${C}${Z}`,{...k,headers:{Authorization:`Bearer ${g}`,"Content-Type":"application/json",...k.headers||{}}});let dt=null;try{dt=JSON.parse(P.body)}catch{}return{ok:P.ok,status:P.status,json:dt}},[C,g]),H=N.useCallback(async()=>{try{const Z=await Y("/api/mitm/ca-status");r(Z.ok&&Z.json?Z.json:null)}catch{r(null)}},[Y]);if(N.useEffect(()=>{C&&g&&H()},[C,g,H]),T===void 0||!T||!T.generated)return null;const lt=async()=>{var Z,k,P,dt,ot,X,M,pt,U;if(!D){q("enable"),R("");try{const gt=await Y("/api/mitm/consent",{method:"POST",body:JSON.stringify({enable:!0})}),qt=`${((Z=gt.json)==null?void 0:Z.error)||""} ${((k=gt.json)==null?void 0:k.detail)||""}`,Lt=((P=gt.json)==null?void 0:P.error)==="need_elevation"||!gt.ok&&gt.status===403||/need_elevation|add-trusted-cert|write permission|SecCertificate|not permitted|requires admin|administrator/i.test(qt);if(gt.ok&&((dt=gt.json)!=null&&dt.ok)&&((ot=gt.json)!=null&&ot.trusted))await H();else if(Lt){const Yt=await((pt=(M=(X=window.cicy)==null?void 0:X.mitm)==null?void 0:M.caExec)==null?void 0:pt.call(M,"install"));Yt!=null&&Yt.ok?await H():R(/cancel/i.test((Yt==null?void 0:Yt.stderr)||"")?w("mitmConsent.errorAdminDenied","未获得管理员授权,已取消。"):(Yt==null?void 0:Yt.stderr)||w("mitmConsent.errorTitle","提权失败,请从管理员控制台运行"))}else R(((U=gt.json)==null?void 0:U.error)||`失败 (HTTP ${gt.status})`)}catch(gt){R(String((gt==null?void 0:gt.message)||gt))}finally{q("")}}},J=async()=>{var Z,k,P,dt,ot;if(!D){q("disable"),R("");try{const X=await Y("/api/mitm/consent",{method:"POST",body:JSON.stringify({enable:!1})});if(X.ok&&((Z=X.json)!=null&&Z.ok))await H();else{const M=await((dt=(P=(k=window.cicy)==null?void 0:k.mitm)==null?void 0:P.caExec)==null?void 0:dt.call(P,"uninstall"));M!=null&&M.ok?await H():R((M==null?void 0:M.stderr)||((ot=X.json)==null?void 0:ot.error)||"撤销失败")}}catch(X){R(String((X==null?void 0:X.message)||X))}finally{q("")}}},I=T.consent&&T.trusted,W=T.consent&&!T.trusted,it=(Z,k)=>w(`mitmConsent.${Z}`,k);if(A==="menu"){const Z=k=>{var P;(P=k==null?void 0:k.stopPropagation)==null||P.call(k),!D&&(I?window.confirm(it("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&J():lt())};return c.jsxs("div",{className:"user-chip__mitm","data-id":"MitmConsentCard",children:[c.jsxs("div",{className:"user-chip__menu-item user-chip__mitm-row",title:it("scopeNote","仅解密 AI 厂商域名,数据留本地,随时可关闭。"),children:[c.jsx("span",{className:"user-chip__mitm-label",children:it("rowLabel","HTTPS 审计")}),c.jsx("button",{type:"button",role:"switch","aria-checked":I?"true":"false","data-id":"MitmConsentCard-toggle",className:`mini-switch${I?" is-on":""}${D?" is-busy":""}`,disabled:!!D,onClick:Z,children:c.jsx("span",{className:"mini-switch__knob"})})]}),W&&!D&&c.jsx("div",{className:"user-chip__mitm-note","data-id":"MitmConsentCard-note",children:it("partialNote","已同意但未安装,点开关重试")}),V&&c.jsx("div",{className:"user-chip__mitm-err","data-id":"MitmConsentCard-error",children:V})]})}return I||D==="disable"?Pl.createPortal(c.jsxs("div",{"data-id":"MitmConsentCard",className:"mitm-pill",title:it("grantedDesc","HTTPS 审计已开启,仅对 CiCy 启动的 AI 工具生效;随时可关闭。"),children:[c.jsx("span",{className:"mitm-pill__dot","data-busy":D?"1":"0"}),c.jsx("span",{className:"mitm-pill__text","data-id":"MitmConsentCard-title",children:D==="disable"?it("processingRevoke","正在关闭…"):it("statePillOn","HTTPS 审计已开启")}),!D&&c.jsx("button",{type:"button","data-id":"MitmConsentCard-revoke",className:"mitm-pill__off",onClick:()=>{window.confirm(it("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&J()},children:it("turnOff","关闭")})]}),document.body):c.jsxs("div",{"data-id":"MitmConsentCard",className:`mitm-card${I?" mitm-card--on":""}`,children:[c.jsxs("div",{className:"mitm-card__head",children:[c.jsx("span",{className:"mitm-card__dot","data-state":I?"on":W?"warn":"off"}),c.jsx("span",{className:"mitm-card__title","data-id":"MitmConsentCard-title",children:D?it("stateProcessingTitle","处理中…"):I?it("stateGrantedTitle","已启用"):`${it("cardTitle","HTTPS 流量本地审计")}${W?" — "+it("retry","重试"):""}`})]}),c.jsxs("p",{className:"mitm-card__desc","data-id":"MitmConsentCard-desc",children:[I?it("grantedDesc","HTTPS 审计已开启,仅对 CiCy 启动的 AI 工具(claude / codex 等)生效;随时可关闭。"):it("body","启用后,CiCy 启动的 AI 工具(claude / codex 等)访问 AI 厂商(Claude / OpenAI / DeepSeek / Gemini)的 HTTPS 流量将被本地审计解密,数据留本地,随时可关闭。"),!I&&c.jsxs(c.Fragment,{children:[c.jsx("br",{}),c.jsx("span",{className:"mitm-card__note",children:it("adminNote","通过环境变量对 CiCy 启动的 AI 工具生效,不修改系统、无需管理员授权。")}),c.jsx("br",{}),c.jsx("span",{className:"mitm-card__sub",children:it("scopeNote","仅解密上述 AI 厂商域名,其余一切流量不被解密、不被读取。")})]})]}),V&&c.jsxs("div",{className:"mitm-card__error","data-id":"MitmConsentCard-error",children:[it("errorTitle","操作失败"),": ",V]}),c.jsx("div",{className:"mitm-card__actions",children:I?c.jsx("button",{"data-id":"MitmConsentCard-revoke",className:"mitm-card__btn mitm-card__btn--ghost",disabled:!!D,onClick:()=>{window.confirm(it("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&J()},children:D==="disable"?it("processingRevoke","正在关闭…"):it("revoke","撤销")}):c.jsx("button",{"data-id":"MitmConsentCard-enable",className:"mitm-card__btn",disabled:!!D,onClick:lt,children:D==="enable"?it("processingEnable","正在启用…"):W?it("retry","重试"):it("enable","同意并启用")})})]})}const Zs=new Set;let um=0,Ft=null;function Il(){Zs.forEach(o=>o(Ft))}const ie={open({onRetry:o}={}){Ft={status:"running",phase:"install-docker",logs:[],bars:{},minimized:!1,onRetry:o||null,lastAt:Date.now()},Il()},minimize(){Ft&&(Ft={...Ft,minimized:!0},Il())},restore(){Ft&&(Ft={...Ft,minimized:!1},Il())},push(o={}){var V;if(!Ft)return;const A=o.phase==="health"?"container":o.phase||Ft.phase,T={...Ft,phase:A,lastAt:Date.now()},r=Number.isFinite(o.progress),D=A==="install-docker"||A==="image";if(D&&(r||o.dest||o.url)){const R=((V=Ft.bars)==null?void 0:V[A])||{},C=r?o.progress:o.status==="skip"||o.status==="done"?100:R.progress;T.bars={...Ft.bars,[A]:{progress:C,received:o.received??R.received,total:o.total??R.total,url:o.url||R.url,dest:o.dest||R.dest}}}if(!(o.status==="running"&&r&&D)){const R={id:++um,t:bu(),phase:A,status:o.status||"running",message:o.message||""};T.logs=[...Ft.logs,R]}Ft=T,Il()},finish({ok:o,message:A,status:T}={}){if(!Ft)return;const r=T||(o?"done":"error"),D={id:++um,t:bu(),phase:"done",status:r,message:A||(o?"完成":"失败")};Ft={...Ft,status:r,phase:"done",minimized:!1,logs:[...Ft.logs,D],lastAt:Date.now()},Il()},close(){Ft=null,Il()}},ks=[["install-docker","准备环境"],["image","下载运行环境"],["container","启动服务"],["done","完成"]],a0={"install-docker":"准备",image:"下载",container:"启动",health:"启动",done:"完成"},l0={"install-docker":"Docker Desktop",image:"基础镜像"};function cm(o){return Number.isFinite(o)?o<1024?o+" B":o<1048576?(o/1024).toFixed(0)+" KB":o<1073741824?(o/1048576).toFixed(1)+" MB":(o/1073741824).toFixed(2)+" GB":"?"}function n0({phaseKey:o,bar:A}){const T=Number.isFinite(A==null?void 0:A.progress)?Math.max(0,Math.min(100,A.progress)):0,r=T>=100;return c.jsxs("div",{className:"dlbar","data-id":`DockerDrawer-dlbar-${o}`,children:[c.jsxs("div",{className:"dlbar__head",children:[c.jsx("span",{className:"dlbar__name",children:l0[o]||o}),c.jsxs("span",{className:"dlbar__pct",children:[T,"%",A!=null&&A.total?` · ${cm(A.received)} / ${cm(A.total)}`:""]})]}),c.jsx("div",{className:"dlbar__track",children:c.jsx("div",{className:`dlbar__fill${r?" is-done":""}`,style:{width:`${T}%`}})}),(A==null?void 0:A.url)&&c.jsxs("div",{className:"dlbar__url",title:A.url,children:[c.jsx("span",{className:"dlbar__urlk",children:"源"})," ",A.url]}),(A==null?void 0:A.dest)&&c.jsxs("div",{className:"dlbar__url",title:A.dest,children:[c.jsx("span",{className:"dlbar__urlk",children:"存"})," ",A.dest]})]})}function i0(){var V;const[o,A]=N.useState(Ft);N.useEffect(()=>(Zs.add(A),()=>{Zs.delete(A)}),[]);const T=N.useRef(null);if(N.useEffect(()=>{const R=T.current;R&&(R.scrollTop=R.scrollHeight)},[(V=o==null?void 0:o.logs)==null?void 0:V.length]),!o)return null;const r=o.status==="running",D=ks.findIndex(([R])=>R===o.phase),q=["install-docker","image"].filter(R=>{var C;return(C=o.bars)==null?void 0:C[R]});if(o.minimized){const R=q.map(g=>{var Y;return(Y=o.bars[g])==null?void 0:Y.progress}).filter(Number.isFinite),C=R.length?Math.round(R.reduce((g,Y)=>g+Y,0)/R.length):null;return c.jsxs("button",{type:"button",className:`drawer-min drawer-min--${o.status}`,"data-id":"DockerDrawer-restore",onClick:()=>ie.restore(),children:[c.jsx("span",{className:"drawer-min__spark",children:r?c.jsx(ge,{}):o.status==="done"?"✓":o.status==="reboot"?"⟳":"!"}),c.jsxs("span",{className:"drawer-min__label",children:[w("docker.setupTitle","安装 Docker cicy-code"),C!=null?` · ${C}%`:""]})]})}return c.jsx("div",{className:"drawer-scrim","data-id":"DockerDrawer-scrim",onClick:()=>r?ie.minimize():ie.close(),children:c.jsxs("div",{className:"drawer","data-id":"DockerDrawer","data-status":o.status,onClick:R=>R.stopPropagation(),children:[c.jsxs("div",{className:"drawer__head",children:[c.jsxs("div",{className:"drawer__title",children:[c.jsx("span",{className:`drawer__spark drawer__spark--${o.status}`,children:r?c.jsx(ge,{}):o.status==="done"?"✓":o.status==="reboot"?"⟳":"!"}),c.jsxs("div",{children:[c.jsx("div",{className:"drawer__h",children:w("docker.setupTitle","安装 Docker cicy-code")}),c.jsx("div",{className:"drawer__sub",children:"127.0.0.1:8009"})]})]}),c.jsx("div",{className:"drawer__headbtns",children:c.jsx("button",{type:"button",className:"drawer__x","data-id":"DockerDrawer-min",title:w("common.minimize","最小化"),onClick:()=>ie.minimize(),"aria-label":"minimize",children:"‒"})})]}),c.jsx("div",{className:"drawer__steps","data-id":"DockerDrawer-steps",children:ks.map(([R,C],g)=>{const Y=o.status==="done"||D>=0&&g<D,H=g===D&&r,lt=o.status==="error"&&g===D;return c.jsxs("div",{className:`drawer__step${H?" is-active":""}${Y?" is-done":""}${lt?" is-error":""}`,children:[c.jsx("span",{className:"drawer__step-dot",children:Y?"✓":lt?"!":g+1}),c.jsx("span",{className:"drawer__step-label",children:C}),g<ks.length-1&&c.jsx("span",{className:"drawer__step-bar"})]},R)})}),q.length>0&&c.jsx("div",{className:"drawer__dlbars","data-id":"DockerDrawer-dlbars",children:q.map(R=>c.jsx(n0,{phaseKey:R,bar:o.bars[R]},R))}),r&&o.logs.length>0&&c.jsxs("div",{className:"drawer__now","data-id":"DockerDrawer-now",children:[c.jsx(ge,{}),c.jsx("span",{children:o.logs[o.logs.length-1].message})]}),c.jsx("div",{className:"drawer__log drawer__log--scroll","data-id":"DockerDrawer-log",ref:T,children:o.logs.length===0?c.jsx("div",{className:"drawer__log-empty",children:w("docker.preparing","准备中…")}):o.logs.map(R=>c.jsxs("div",{className:"drawer__line","data-status":R.status,children:[c.jsx("span",{className:"drawer__t",children:R.t}),c.jsx("span",{className:`drawer__badge drawer__badge--${R.phase}`,children:a0[R.phase]||R.phase}),c.jsx("span",{className:"drawer__linemsg",children:R.message})]},R.id))}),c.jsx("div",{className:"drawer__foot",children:r?c.jsx(c.Fragment,{children:c.jsx("span",{className:"drawer__foot-status",children:w("docker.installing2","安装进行中…")})}):o.status==="reboot"?c.jsxs(c.Fragment,{children:[c.jsx("span",{className:"drawer__foot-status is-reboot",children:w("docker.rebootShort","需重启 Windows")}),o.onRetry&&c.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"DockerDrawer-retry",onClick:()=>o.onRetry(),children:w("common.retry","重试")}),c.jsx("button",{type:"button",className:"drawer__btn","data-id":"DockerDrawer-dismiss",onClick:()=>ie.close(),children:w("common.close","关闭")})]}):o.status==="error"?c.jsxs(c.Fragment,{children:[c.jsx("span",{className:"drawer__foot-status is-error",children:w("docker.failed","安装失败")}),o.onRetry&&c.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"DockerDrawer-retry",onClick:()=>o.onRetry(),children:w("common.retry","重试")}),c.jsx("button",{type:"button",className:"drawer__btn","data-id":"DockerDrawer-dismiss",onClick:()=>ie.close(),children:w("common.close","关闭")})]}):c.jsxs(c.Fragment,{children:[c.jsx("span",{className:"drawer__foot-status is-done",children:w("docker.ready","已就绪")}),c.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"DockerDrawer-finish",onClick:()=>ie.close(),children:w("common.done","完成")})]})})]})})}function u0({dockerTeam:o,onOpen:A,onRefresh:T}){var me;const[r,D]=N.useState(null),[q,V]=N.useState(""),[R,C]=N.useState(!1),[g,Y]=N.useState({top:0,left:0}),H=N.useRef(null),lt=N.useRef(null),J=184,I="#2496ed",W=N.useCallback(async()=>{var ut,S,O;try{D(await((O=(S=(ut=window.cicy)==null?void 0:ut.docker)==null?void 0:S.appStatus)==null?void 0:O.call(S)))}catch(Q){console.warn("[DockerCard]",Q)}},[]);N.useEffect(()=>{W();const ut=setInterval(()=>{q||W()},12e3);return()=>clearInterval(ut)},[W,q]),N.useEffect(()=>{if(!R)return;const ut=O=>{var Q,mt;(Q=H.current)!=null&&Q.contains(O.target)||(mt=lt.current)!=null&&mt.contains(O.target)||C(!1)},S=O=>{O.key==="Escape"&&C(!1)};return document.addEventListener("mousedown",ut),document.addEventListener("keydown",S),()=>{document.removeEventListener("mousedown",ut),document.removeEventListener("keydown",S)}},[R]);const it=()=>{if(!R&&H.current){const ut=H.current.getBoundingClientRect(),S=Math.max(8,Math.min(ut.right-J,window.innerWidth-J-8));Y({top:Math.round(ut.bottom+4),left:Math.round(S)})}C(ut=>!ut)},Z=N.useCallback(async()=>{var S,O,Q,mt,st,d;V("bootstrap"),ie.open({onRetry:Z});const ut=(Q=(O=(S=window.cicy)==null?void 0:S.docker)==null?void 0:O.onAppProgress)==null?void 0:Q.call(O,y=>ie.push(y));try{const y=await((d=(st=(mt=window.cicy)==null?void 0:mt.docker)==null?void 0:st.appBootstrap)==null?void 0:d.call(st));(y==null?void 0:y.reason)==="installer_launched"?ie.finish({status:"reboot",message:w("docker.installerLaunched","已打开 Docker 安装程序——请完成安装(会装 WSL2、可能需重启),装好后点「重试」")}):(y==null?void 0:y.reason)==="wsl_reboot_required"?ie.finish({status:"reboot",message:w("docker.rebootNeeded","WSL2 已安装,请【重启 Windows】后回来点「重试」继续")}):ie.finish({ok:!!(y!=null&&y.ok),message:y!=null&&y.ok?w("docker.ready","Docker cicy-code 已就绪"):(y==null?void 0:y.error)||w("docker.failed","安装失败")}),y!=null&&y.ok&&(T==null||T())}catch(y){ie.finish({ok:!1,message:y.message})}finally{try{ut&&ut()}catch{}V(""),W()}},[W,T]),k=N.useCallback(async()=>{var S,O,Q,mt,st,d;C(!1),V("upgrade"),ie.open({onRetry:k});const ut=(Q=(O=(S=window.cicy)==null?void 0:S.docker)==null?void 0:O.onAppProgress)==null?void 0:Q.call(O,y=>ie.push(y));try{const y=await((d=(st=(mt=window.cicy)==null?void 0:mt.docker)==null?void 0:st.appUpgrade)==null?void 0:d.call(st));(y==null?void 0:y.reason)==="wsl_reboot_required"?ie.finish({status:"reboot",message:w("docker.rebootNeeded","WSL2 已安装,请【重启 Windows】后回来点「重试」继续")}):ie.finish({ok:!!(y!=null&&y.ok),message:y!=null&&y.ok?w("docker.upgraded","已升级到最新"):(y==null?void 0:y.error)||w("docker.upgradeFailed","升级失败")}),y!=null&&y.ok&&(T==null||T())}catch(y){ie.finish({ok:!1,message:y.message})}finally{try{ut&&ut()}catch{}V(""),W()}},[W,T]),P=N.useCallback(async(ut,S,O)=>{C(!1),V(ut),de.show({id:"docker-op",message:w(`docker.${ut}ing`,ut==="restart"?"重启中…":"停止中…"),status:"running"});try{const Q=await S();Q!=null&&Q.ok?de.show({id:"docker-op",message:O,status:"done",ttl:2500}):de.show({id:"docker-op",message:(Q==null?void 0:Q.error)||w("docker.opFailed","操作失败"),status:"error",ttl:6e3})}catch(Q){de.show({id:"docker-op",message:Q.message,status:"error",ttl:6e3})}finally{V(""),W()}},[W]);if((((me=window.cicy)==null?void 0:me.platform)||(r==null?void 0:r.platform))!=="win32")return null;const ot=!!(r!=null&&r.running)||(o==null?void 0:o.status)==="running",X=!!(r!=null&&r.dockerRunning),M=!!(r!=null&&r.installed),pt=ot?"ok":X||M?"warn":"off",U=!!q,gt=ot?w("docker.running","运行中 · :8009"):X?w("docker.notRunning","未启动 · 点「启动」"):M?w("docker.engineDown","Docker 未运行 · 点启动"):w("docker.notInstalled","Docker Desktop 未安装"),qt=U?w("docker.working","处理中…"):ot?w("localTeams.open","打开"):X?w("docker.start","启动"):M?w("docker.startDocker","启动 Docker"):w("docker.install","下载安装"),Lt=()=>{if(!U){if(ot){A==null||A(o==null?void 0:o.id);return}Z()}},Yt=ot;return c.jsxs("div",{"data-id":"DockerCard",className:`bcard bcard--docker${ot?" bcard--online":""}`,children:[c.jsx("div",{className:"bcard__accent",style:{background:I}}),c.jsxs("div",{className:"bcard__top",children:[c.jsxs("div",{className:"bcard__pill",style:{color:I},children:[c.jsx("span",{className:"bcard__dot","data-tone":pt}),c.jsx("svg",{style:{width:18,height:18},viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:c.jsx("path",{d:"M21.81 10.25c-.06-.05-.67-.51-1.95-.51-.34 0-.68.03-1.01.09-.25-1.69-1.64-2.51-1.7-2.55l-.34-.2-.22.32a4.5 4.5 0 0 0-.59 1.4c-.23.94-.09 1.83.39 2.59-.58.32-1.51.4-1.7.41H2.62a.61.61 0 0 0-.61.61 9.32 9.32 0 0 0 .57 3.35 4.9 4.9 0 0 0 1.95 2.53c.92.52 2.42.82 4.12.82.77 0 1.54-.07 2.3-.21a9.6 9.6 0 0 0 3-1.09 8.3 8.3 0 0 0 2.05-1.68c.98-1.11 1.56-2.35 1.99-3.45h.17c1.36 0 2.2-.55 2.66-1l.13-.16zM4.7 11.33h1.78a.16.16 0 0 0 .16-.16V9.58a.16.16 0 0 0-.16-.16H4.7a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16m2.46 0h1.78a.16.16 0 0 0 .16-.16V9.58a.16.16 0 0 0-.16-.16H7.16a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16m2.5 0h1.78a.16.16 0 0 0 .16-.16V9.58a.16.16 0 0 0-.16-.16H9.66a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16m2.47 0h1.78a.16.16 0 0 0 .16-.16V9.58a.16.16 0 0 0-.16-.16h-1.78a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16M7.16 9.06h1.78a.16.16 0 0 0 .16-.16V7.31a.16.16 0 0 0-.16-.16H7.16a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16m2.5 0h1.78a.16.16 0 0 0 .16-.16V7.31a.16.16 0 0 0-.16-.16H9.66a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16m2.47 0h1.78a.16.16 0 0 0 .16-.16V7.31a.16.16 0 0 0-.16-.16h-1.78a.16.16 0 0 0-.16.16v1.59c0 .09.07.16.16.16"})})]}),Yt&&c.jsxs("div",{className:"bcard__menuwrap",onClick:ut=>ut.stopPropagation(),children:[c.jsx("button",{type:"button",ref:H,"data-id":"DockerCard-menu-btn",className:"bcard__kebab",title:w("docker.manage","管理 Docker cicy-code"),disabled:U,onClick:it,children:U?c.jsx(ge,{}):c.jsx(Js,{})}),R&&Pl.createPortal(c.jsxs("div",{className:"bcard__menu bcard__menu--portal","data-id":"DockerCard-menu",role:"menu",ref:lt,style:{position:"fixed",top:g.top,left:g.left,width:J},onClick:ut=>ut.stopPropagation(),children:[ot&&c.jsx("button",{type:"button","data-id":"DockerCard-restart",className:"bcard__menu-item",onClick:()=>P("restart",()=>window.cicy.docker.appRestart(),w("docker.restarted","已重启")),children:w("docker.restart","重启")}),c.jsx("button",{type:"button","data-id":"DockerCard-upgrade",className:"bcard__menu-item is-accent",onClick:k,children:w("docker.upgrade","升级(拉取最新镜像)")}),ot&&c.jsx("button",{type:"button","data-id":"DockerCard-stop",className:"bcard__menu-item is-danger",onClick:()=>P("stop",()=>window.cicy.docker.appStop(),w("docker.stopped","已停止")),children:w("docker.stop","停止")})]}),document.body)]})]}),c.jsxs("div",{className:"bcard__body",children:[c.jsx("h3",{className:"bcard__name",children:w("docker.title","Docker cicy-code")}),c.jsx("div",{className:"bcard__host",children:"http://127.0.0.1:8009"}),c.jsx("div",{className:"bcard__meta",style:{fontSize:12,color:"#8b949e"},children:gt})]}),c.jsxs("button",{type:"button",className:"bcard__cta","data-id":"DockerCard-cta",disabled:U,onClick:Lt,style:ot?void 0:{background:I,color:"white"},children:[U?c.jsx(ge,{}):c.jsx(_u,{}),c.jsx("span",{children:qt})]})]})}function sm({team:o,onOpen:A,onRename:T,onRefresh:r}){var Ka,He,B,at;const q=(rm[o.status]||rm.error).tone,[V,R]=N.useState(!1),[C,g]=N.useState(o.name||""),[Y,H]=N.useState(null),[lt,J]=N.useState(""),I=Y??o.name;N.useEffect(()=>{Y!=null&&o.name===Y&&H(null)},[o.name,Y]);const W=x=>{var K;(K=x==null?void 0:x.stopPropagation)==null||K.call(x),g(I||""),R(!0)},it=async()=>{R(!1);const x=String(C||"").trim();if(!T||!x||x===I)return;H(x),J("saving");let K;try{K=await T(o.id,x)}catch(L){K={ok:!1,error:L==null?void 0:L.message}}H(null),K&&K.ok?(J("saved"),setTimeout(()=>J(L=>L==="saved"?"":L),1500)):(J(""),de.show({message:w("localTeams.renameFailed","改名没保存,已恢复"),status:"error",ttl:4e3}))},k=!!((He=(Ka=window.cicy)==null?void 0:Ka.sidecar)!=null&&He.restart)&&Vs(o.base_url),P=o.status==="running",[dt,ot]=N.useState(""),[X,M]=N.useState(!1),[pt,U]=N.useState({running:void 0,latest:null,installed:null}),gt=pt.latest,qt=pt.running,[Lt,Yt]=N.useState(!1),me=N.useRef(null),ut=N.useRef(null),S=N.useRef(null),[O,Q]=N.useState({top:0,left:0}),mt=184,st=()=>{if(!X&&ut.current){const x=ut.current.getBoundingClientRect(),K=Math.max(8,Math.min(x.right-mt,window.innerWidth-mt-8));Q({top:Math.round(x.bottom+4),left:Math.round(K)})}M(x=>!x)},d=N.useCallback(async(x=!1)=>{var K,L;if(!(!k||!((L=(K=window.cicy)==null?void 0:K.sidecar)!=null&&L.versions))){x&&Yt(!0);try{const et=await window.cicy.sidecar.versions();U({running:(et==null?void 0:et.running)??null,latest:(et==null?void 0:et.latest)??null,installed:(et==null?void 0:et.installed)??null}),x&&(et!=null&&et.running&&(et!=null&&et.latest)&&fm(et.latest,et.running)>0?de.show({message:`${w("sidecar.found","发现新版本")} v${et.latest}`,status:"done",ttl:2500}):et!=null&&et.running?de.show({message:`${w("sidecar.upToDate","已是最新")} v${et.running}`,status:"done",ttl:2500}):de.show({message:w("sidecar.notRunning","cicy-code 未运行"),status:"error",ttl:4e3}))}catch{x&&de.show({message:w("sidecar.checkFailed","检查更新失败"),status:"error",ttl:5e3})}finally{x&&Yt(!1)}}},[k]);N.useEffect(()=>{d(!1)},[d]),N.useEffect(()=>{P&&d(!1)},[P,d]);const y=!!(k&&gt&&qt&&fm(gt,qt)>0),G=!k&&!!((at=(B=window.cicy)==null?void 0:B.localTeams)!=null&&at.remove),$=k||G,[ft,rt]=N.useState(!1);N.useEffect(()=>{if(!X)return;const x=K=>{var L,et;(L=ut.current)!=null&&L.contains(K.target)||(et=S.current)!=null&&et.contains(K.target)||M(!1)};return document.addEventListener("mousedown",x),()=>document.removeEventListener("mousedown",x)},[X]),N.useEffect(()=>{X||rt(!1)},[X]);const Tt=async()=>{var x,K,L;if(!ft){rt(!0);return}if(M(!1),rt(!1),!dt){ot("remove");try{await((L=(K=(x=window.cicy)==null?void 0:x.localTeams)==null?void 0:K.remove)==null?void 0:L.call(K,o.id))}catch{}ot(""),r==null||r()}},Rt=`sidecar-op:${o.id}`,Et=async(x,K,L)=>{var ue,Xt,kt,Ae,tn;if(M(!1),dt)return;ot(x);const et=x==="update";let Zt=null;et?(ea.open({teamId:o.id,fromVer:qt,toVer:gt,onRetry:()=>Et("update",K,L)}),(Xt=(ue=window.cicy)==null?void 0:ue.sidecar)!=null&&Xt.onOpProgress&&(Zt=window.cicy.sidecar.onOpProgress(Gt=>{(Gt==null?void 0:Gt.op)==="update"&&ea.push(Gt)}))):de.show({id:Rt,message:Re[x]||`${x}…`,status:"running",progress:void 0});try{const Gt=await K(),ve=!!(Gt!=null&&Gt.ok),Pn=Gt!=null&&Gt.warning?`${L}(${Gt.warning})`:L,en=w("sidecar.failed","操作失败")+(Gt!=null&&Gt.error?`: ${Gt.error}`:"");if(et){if(ea.finish({ok:ve,message:ve?Pn:en}),ve)try{await((tn=(Ae=(kt=window.cicy)==null?void 0:kt.localTeams)==null?void 0:Ae.reload)==null?void 0:tn.call(Ae,o.id,{ignoreCache:!0}))}catch{}}else de.show({id:Rt,message:ve?Pn:en,progress:void 0,status:ve?"done":"error",ttl:ve?4e3:8e3})}catch(Gt){const ve=w("sidecar.failed","操作失败")+`: ${(Gt==null?void 0:Gt.message)||Gt}`;et?ea.finish({ok:!1,message:ve}):de.show({id:Rt,message:ve,progress:void 0,status:"error",ttl:8e3})}finally{try{Zt&&Zt()}catch{}ot(""),r==null||r(),(x==="update"||x==="restart"||x==="start")&&d(!1)}},Re={start:"启动中…",restart:"重启中…",update:"更新中…",stop:"停止中…"},aa=async()=>{var x,K;if(!dt){if(!P&&k&&((K=(x=window.cicy)==null?void 0:x.sidecar)!=null&&K.start)){ot("start"),de.show({id:Rt,message:Re.start,status:"running",progress:void 0});const L=await window.cicy.sidecar.start().catch(et=>({ok:!1,error:(et==null?void 0:et.message)||String(et)}));if(ot(""),r==null||r(),!(L!=null&&L.ok)||L!=null&&L.warning){de.show({id:Rt,message:w("sidecar.startFailed","启动失败")+(L!=null&&L.error?`: ${L.error}`:L!=null&&L.warning?`: ${L.warning}`:""),status:"error",ttl:8e3});return}de.dismiss(Rt)}A()}},We=P?w("localTeams.open","打开"):k?w("localTeams.startOpen","启动并打开"):w("localTeams.open","打开");return c.jsxs("div",{"data-id":"LocalTeamCard",className:`bcard ${k?"bcard--local":"bcard--custom"}${q==="ok"?" bcard--online":""}`,children:[c.jsx("div",{className:"bcard__accent"}),c.jsxs("div",{className:"bcard__top",children:[c.jsxs("div",{className:"bcard__pill",children:[c.jsx("span",{className:"bcard__dot","data-tone":q}),c.jsx(Sm,{})]}),$&&c.jsxs("div",{className:"bcard__menuwrap",ref:me,onClick:x=>x.stopPropagation(),children:[c.jsx("button",{type:"button",ref:ut,"data-id":"LocalTeamCard-menu-btn",className:`bcard__kebab${y?" has-dot":""}`,title:k?w("localTeams.manage","管理本地 cicy-code"):w("localTeams.more","更多"),disabled:!!dt,onClick:st,children:dt?c.jsx(ge,{}):c.jsx(Js,{})}),X&&Pl.createPortal(c.jsxs("div",{className:"bcard__menu bcard__menu--portal","data-id":"LocalTeamCard-menu",role:"menu",ref:S,style:{position:"fixed",top:O.top,left:O.left,width:mt},onClick:x=>x.stopPropagation(),children:[k&&c.jsx("button",{type:"button","data-id":"LocalTeamCard-check-update",className:"bcard__menu-item",disabled:Lt,onClick:x=>{x.stopPropagation(),d(!0)},children:Lt?w("sidecar.checking2","检查中…"):w("sidecar.checkUpdate","检查更新")}),y&&c.jsxs("button",{type:"button","data-id":"LocalTeamCard-update",className:"bcard__menu-item is-accent",onClick:()=>Et("update",()=>window.cicy.sidecar.update(),w("sidecar.updated","已更新到最新")),children:[w("sidecar.updateTo","更新到")," v",gt]}),k&&P&&c.jsxs(c.Fragment,{children:[c.jsx("button",{type:"button","data-id":"LocalTeamCard-reload",className:"bcard__menu-item",onClick:()=>Et("reload",async()=>{const x=await window.cicy.localTeams.reload(o.id);return!(x!=null&&x.ok)&&(x==null?void 0:x.error)==="no_open_window"?{ok:!1,error:w("localTeams.windowNotOpen","窗口未打开,请先点「打开」")}:x},w("localTeams.reloaded","已刷新窗口")),children:w("localTeams.reloadWindow","刷新窗口")}),c.jsx("button",{type:"button","data-id":"LocalTeamCard-restart",className:"bcard__menu-item",onClick:()=>Et("restart",()=>window.cicy.sidecar.restart(),w("sidecar.restarted","已重启")),children:w("sidecar.restart","重启")}),c.jsx("button",{type:"button","data-id":"LocalTeamCard-stop",className:"bcard__menu-item is-danger",onClick:()=>Et("stop",()=>window.cicy.sidecar.stop(),w("sidecar.stoppedDone","已停止")),children:w("sidecar.stop","停止")})]}),o.cloud_team_id&&c.jsx("button",{type:"button","data-id":"LocalTeamCard-billing",className:"bcard__menu-item",onClick:x=>{x.stopPropagation(),M(!1),Su(`?team=${encodeURIComponent(o.cloud_team_id)}`)},children:w("localTeams.billing","账单")}),G&&c.jsx("button",{type:"button","data-id":"LocalTeamCard-remove",className:"bcard__menu-item is-danger",onClick:Tt,children:ft?w("localTeams.removeConfirm","确认删除?"):w("localTeams.remove","删除")})]}),document.body)]})]}),c.jsxs("div",{className:"bcard__body",children:[V?c.jsx("input",{"data-id":"LocalTeamCard-rename-input",autoFocus:!0,value:C,onChange:x=>g(x.target.value),onFocus:x=>x.target.select(),onBlur:it,onClick:x=>x.stopPropagation(),onKeyDown:x=>{x.nativeEvent.isComposing||x.keyCode===229||(x.key==="Enter"?it():x.key==="Escape"&&R(!1))},style:{width:"100%",font:"inherit",fontWeight:600,padding:"2px 6px",border:"1px solid #3b82f6",borderRadius:6,background:"#0d1117",color:"#e6edf3",boxSizing:"border-box"}}):c.jsxs("h3",{className:"bcard__name",title:w("localTeams.renameHint","点名字或 ✎ 改名"),style:{display:"flex",alignItems:"center",gap:6},onDoubleClick:W,children:[c.jsx("span",{"data-id":"LocalTeamCard-name-text",onClick:W,style:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",cursor:"text"},children:I}),lt==="saving"&&c.jsx("span",{"data-id":"LocalTeamCard-save-state",title:w("localTeams.saving","保存中…"),style:{flex:"none",display:"inline-flex"},children:c.jsx(ge,{})}),lt==="saved"&&c.jsx("span",{"data-id":"LocalTeamCard-save-state",title:w("localTeams.saved","已保存"),style:{flex:"none",color:"#3fb950",fontSize:13,lineHeight:1},children:"✓"}),!lt&&c.jsx("button",{type:"button","data-id":"LocalTeamCard-rename-btn",title:w("localTeams.rename","重命名"),onClick:W,style:{flex:"none",cursor:"pointer",border:"none",background:"transparent",color:"#8b949e",fontSize:13,padding:0,lineHeight:1},children:"✎"})]}),c.jsx("div",{className:"bcard__host",children:o.base_url||"—"}),c.jsx("div",{className:"bcard__meta",children:(qt||o.version)&&c.jsxs("span",{className:"bcard__ver","data-id":"LocalTeamCard-version",children:["v",qt||o.version]})})]}),c.jsxs("button",{type:"button",className:"bcard__cta","data-id":"LocalTeamCard-open",disabled:!!dt||!o.base_url,onClick:aa,children:[dt&&dt!=="stop"?c.jsx(ge,{}):c.jsx(_u,{}),c.jsx("span",{children:dt&&Re[dt]||We})]})]})}function Vs(o){try{const A=new URL(o);return(A.hostname==="127.0.0.1"||A.hostname==="localhost"||A.hostname==="::1")&&(A.port==="8008"||A.port==="")}catch{return!1}}function om(o){try{const A=new URL(o);return(A.hostname==="127.0.0.1"||A.hostname==="localhost"||A.hostname==="::1")&&A.port==="8009"}catch{return!1}}function fm(o,A){const T=String(o).split("."),r=String(A).split(".");for(let D=0;D<Math.max(T.length,r.length);D++){const q=(parseInt(T[D],10)||0)-(parseInt(r[D],10)||0);if(q)return q>0?1:-1}return 0}const rm={running:{tone:"ok",label:"running",cta:"打开"},stopped:{tone:"off",label:"stopped",cta:"未运行"},auth_error:{tone:"warn",label:"auth error",cta:"Token 失效"},misconfigured:{tone:"err",label:"bad config",cta:"URL 错误"},error:{tone:"err",label:"error",cta:"异常"}};function c0({team:o,onOpen:A}){const T=o.kind==="private",r=o.status==="active",D=o.name||o.title||"—",q=o.host_url||"",V=o.teamId||o.id,R=T?"私有云":o.team_kind==="personal"?"个人":"共享",C=T?q:o.workspace_url||o.workspace_direct_url,g=!!C,[Y,H]=N.useState(!1),[lt,J]=N.useState(!1),[I,W]=N.useState({top:0,left:0}),it=N.useRef(null),Z=N.useRef(null),k=N.useRef(null),P=184,dt=()=>{if(!Y&&Z.current){const X=Z.current.getBoundingClientRect(),M=Math.max(8,Math.min(X.right-P,window.innerWidth-P-8));W({top:Math.round(X.bottom+4),left:Math.round(M)})}H(X=>!X)};N.useEffect(()=>{if(!Y)return;const X=M=>{var pt,U;(pt=Z.current)!=null&&pt.contains(M.target)||(U=k.current)!=null&&U.contains(M.target)||H(!1)};return document.addEventListener("mousedown",X),()=>document.removeEventListener("mousedown",X)},[Y]);const ot=async()=>{var X,M,pt;if(!(!g||lt)){J(!0),H(!1);try{await((pt=(M=(X=window.cicy)==null?void 0:X.tabs)==null?void 0:M.reload)==null?void 0:pt.call(M,C,D))}catch{}finally{J(!1)}}};return c.jsxs("div",{"data-id":"TeamCard",className:`bcard bcard--cloud${r?" bcard--online":""}`,children:[c.jsx("div",{className:"bcard__accent"}),c.jsxs("div",{className:"bcard__top",children:[c.jsxs("div",{className:"bcard__pill",children:[c.jsx("span",{className:"bcard__dot","data-tone":r?"ok":"off"}),c.jsx(s0,{})]}),c.jsxs("div",{className:"bcard__top-right",children:[o.is_trial&&c.jsx("span",{className:"bcard__badge",children:"trial"}),V!=null&&c.jsx("button",{type:"button","data-id":"TeamCard-billing",className:"bcard__billing-btn",title:w("localTeams.billing","账单"),onClick:X=>{X.stopPropagation(),Su(`?team=${encodeURIComponent(V)}`)},children:w("localTeams.billing","账单")}),g&&c.jsxs("div",{className:"bcard__menuwrap",ref:it,onClick:X=>X.stopPropagation(),children:[c.jsx("button",{type:"button",ref:Z,"data-id":"TeamCard-menu-btn",className:"bcard__kebab",title:w("localTeams.more","更多"),disabled:lt,onClick:dt,children:lt?c.jsx(ge,{}):c.jsx(Js,{})}),Y&&Pl.createPortal(c.jsx("div",{className:"bcard__menu bcard__menu--portal","data-id":"TeamCard-menu",role:"menu",ref:k,style:{position:"fixed",top:I.top,left:I.left,width:P},onClick:X=>X.stopPropagation(),children:c.jsx("button",{type:"button","data-id":"TeamCard-reload",className:"bcard__menu-item",onClick:ot,children:w("localTeams.reloadWindow","刷新窗口")})}),document.body)]})]})]}),c.jsxs("div",{className:"bcard__body",children:[c.jsx("h3",{className:"bcard__name",title:D,children:D}),c.jsx("div",{className:"bcard__host",title:T&&q||"",children:T?q||w("teamCard.noHost","未填访问地址"):o.runtime_region||o.region||"—"}),c.jsxs("div",{className:"bcard__meta",children:[c.jsx("span",{className:"bcard__chip",children:R}),!T&&o.membership_status&&o.membership_status!=="active"&&c.jsx("span",{className:"bcard__chip",children:o.membership_status})]})]}),c.jsxs("button",{type:"button",className:"bcard__cta",onClick:A,disabled:!g,children:[c.jsx(_u,{}),c.jsx("span",{children:g?w("localTeams.open","打开"):T?w("teamCard.noHost","未填访问地址"):w("teamCard.noUrl","无 URL")})]})]})}function Gs(){return c.jsxs("div",{className:"brand",children:[c.jsx("div",{className:"brand-mark",children:c.jsx(bm,{})}),c.jsxs("div",{className:"brand-text",children:[c.jsx("div",{className:"brand-name",children:"CiCy Desktop"}),c.jsx("div",{className:"brand-sub",children:"团队 AI 协作工作台"})]})]})}function bm(){return c.jsx("svg",{width:"22",height:"22",viewBox:"0 0 96 96",fill:"none",children:c.jsx("path",{d:"M48 11L39.5 33.3L16 29.5L31 48L16 66.5L39.5 62.7L48 85L56.5 62.7L80 66.5L65 48L80 29.5L56.5 33.3Z",fill:"white",stroke:"white",strokeWidth:"8",strokeLinejoin:"round",strokeLinecap:"round"})})}function _u(){return c.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2",strokeLinecap:"round",strokeLinejoin:"round",children:[c.jsx("line",{x1:"5",y1:"12",x2:"19",y2:"12"}),c.jsx("polyline",{points:"12 5 19 12 12 19"})]})}function ge(){return c.jsx("svg",{className:"spin",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.4",strokeLinecap:"round",children:c.jsx("path",{d:"M21 12a9 9 0 1 1-6.2-8.55"})})}function Sm(){return c.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[c.jsx("rect",{x:"3",y:"4",width:"18",height:"12",rx:"2"}),c.jsx("line",{x1:"2",y1:"20",x2:"22",y2:"20"})]})}function Js(){return c.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:[c.jsx("circle",{cx:"12",cy:"5",r:"1.7"}),c.jsx("circle",{cx:"12",cy:"12",r:"1.7"}),c.jsx("circle",{cx:"12",cy:"19",r:"1.7"})]})}function s0(){return c.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[c.jsx("circle",{cx:"12",cy:"12",r:"10"}),c.jsx("line",{x1:"2",y1:"12",x2:"22",y2:"12"}),c.jsx("path",{d:"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"})]})}function In(o){try{return localStorage.getItem(o)||null}catch{return null}}function dm(o){return o?/timeout/i.test(o)?"登录超时,请重新点击 Login。":/state/i.test(o)?"校验失败,请重新登录。":/no token/i.test(o)?"登录未完成,请重试。":/bridge missing/i.test(o)?"无法连接到登录服务(preload 未就绪)。":o:""}var mm;const o0=((mm=window.cicy)==null?void 0:mm.platform)||(()=>{const o=navigator.userAgent||"";return/Mac/i.test(o)?"darwin":/Windows/i.test(o)?"win32":"linux"})();document.documentElement.dataset.platform=o0;document.documentElement.dataset.fullscreen="0";var hm,ym;(ym=(hm=window.cicy)==null?void 0:hm.window)!=null&&ym.onFullscreen&&window.cicy.window.onFullscreen(o=>{document.documentElement.dataset.fullscreen=o?"1":"0"});Ky.createRoot(document.getElementById("root")).render(c.jsx(Fy,{}));
@@ -6,7 +6,7 @@
6
6
  <link rel="icon" type="image/svg+xml" href="./favicon.svg" />
7
7
  <link rel="icon" type="image/png" sizes="256x256" href="./favicon-256.png" />
8
8
  <title>CiCy Desktop</title>
9
- <script type="module" crossorigin src="./assets/index-DKTmSMvA.js"></script>
9
+ <script type="module" crossorigin src="./assets/index-DRwt5BEG.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="./assets/index-BcVFakIC.css">
11
11
  </head>
12
12
  <body>
@@ -16,7 +16,10 @@ const os = require("os");
16
16
  const path = require("path");
17
17
 
18
18
  const IMAGE = process.env.CICY_DOCKER_IMAGE || "cicybot/cicy-code:latest";
19
- const R2_TARBALL = process.env.CICY_DOCKER_URL || "https://r2.deepfetch.de5.net/docker/cicy-code-latest.tar.gz";
19
+ // Image tarball on Aliyun OSS (oss-cn-shanghai, public-read) — CN-domestic and
20
+ // fast (~13MB/s upload); R2 was throttled to ~150KB/s from CN. Override via env.
21
+ const R2_TARBALL = process.env.CICY_DOCKER_URL || "https://cicy-1372193042-cn.oss-cn-shanghai.aliyuncs.com/images/cicy-code-latest.tar.gz";
22
+ const DL_UA = process.env.CICY_DL_UA || "cicy-desktop"; // download UA (CN mirrors 403 empty/Mozilla UAs)
20
23
  const CONTAINER = process.env.CICY_DOCKER_CONTAINER || "cicy-code";
21
24
  const VOLUME = process.env.CICY_DOCKER_VOLUME || "cicy-ai-data";
22
25
  // Docker Desktop installer (Windows). Direct from docker.com, with a COS mirror
@@ -99,7 +102,10 @@ function download(url, dest, { hops = 5, onProgress = null, resume = false } = {
99
102
  let offset = 0;
100
103
  if (resume) { try { offset = fs.statSync(dest).size; } catch {} }
101
104
  const lib = url.startsWith("https:") ? https : http;
102
- const headers = offset > 0 ? { Range: `bytes=${offset}-` } : {};
105
+ // A non-empty, non-browser UA is required by some CN mirrors (Tsinghua TUNA
106
+ // 403s an empty UA AND a Mozilla/browser UA — anti-hotlink — but allows a
107
+ // plain client UA like this).
108
+ const headers = { "User-Agent": DL_UA, ...(offset > 0 ? { Range: `bytes=${offset}-` } : {}) };
103
109
  const req = lib.get(url, { timeout: 60000, headers }, (res) => {
104
110
  if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
105
111
  res.resume();
@@ -162,7 +168,7 @@ function headSize(url, hops = 5) {
162
168
  return new Promise((resolve) => {
163
169
  if (hops <= 0) return resolve(0);
164
170
  const lib = url.startsWith("https:") ? https : http;
165
- const req = lib.request(url, { method: "HEAD", timeout: 15000 }, (res) => {
171
+ const req = lib.request(url, { method: "HEAD", timeout: 15000, headers: { "User-Agent": DL_UA } }, (res) => {
166
172
  if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
167
173
  res.resume();
168
174
  return headSize(res.headers.location, hops - 1).then(resolve);
@@ -295,12 +301,52 @@ function findExistingInstaller() {
295
301
  } catch { return null; }
296
302
  }
297
303
 
304
+ // Download via the OS `curl` binary. node's http.get stalls at ~150KB/s on this
305
+ // R2 endpoint while curl.exe sustains ~1.4MB/s (10×) — so for the big image
306
+ // tarball we shell out to curl. `-C -` resumes a partial; progress comes from
307
+ // polling the file size against the HEAD content-length.
308
+ function curlDownload(url, dest, { emit, phase = "image", label = "下载镜像" } = {}) {
309
+ return new Promise(async (resolve, reject) => {
310
+ let total = 0; try { total = await headSize(url); } catch {}
311
+ const bin = process.platform === "win32" ? "curl.exe" : "curl";
312
+ // --retry-all-errors so a transient DNS blip (curl exit 6, common right after
313
+ // the app starts) retries on the fast curl path instead of falling back to
314
+ // the slow node downloader. --retry-connrefused covers a not-yet-ready net.
315
+ const args = ["-sL", "-A", DL_UA, "-C", "-", "--retry", "8", "--retry-delay", "3", "--retry-all-errors", "--retry-connrefused", "-o", dest, url];
316
+ let child;
317
+ try { child = spawn(bin, args, { windowsHide: true }); }
318
+ catch (e) { return reject(e); }
319
+ let lastPct = -1;
320
+ const timer = setInterval(() => {
321
+ let have = 0; try { have = fs.statSync(dest).size; } catch {}
322
+ const pct = total ? Math.round((have / total) * 100) : 0;
323
+ if (pct === lastPct) return;
324
+ lastPct = pct;
325
+ emit && emit({ phase, status: "running", message: label, progress: pct, received: have, total, url, dest });
326
+ }, 1000);
327
+ child.on("error", (e) => { clearInterval(timer); reject(e); });
328
+ child.on("close", (code) => {
329
+ clearInterval(timer);
330
+ if (code !== 0) return reject(new Error(`curl exit ${code}`));
331
+ let have = 0; try { have = fs.statSync(dest).size; } catch {}
332
+ if (total && have < total) return reject(new Error(`incomplete ${have}/${total}`));
333
+ emit && emit({ phase, status: "running", message: label, progress: 100, received: have, total, url, dest });
334
+ resolve(dest);
335
+ });
336
+ });
337
+ }
338
+
298
339
  // Download the R2 base-env image tarball (no docker needed yet). Split out of
299
- // loadImage so bootstrap can run this IN PARALLEL with the Docker Desktop
300
- // install (主人: 装 Docker 的同时下载 R2 镜像). Returns the tarball path.
340
+ // loadImage so bootstrap can run this IN PARALLEL with the Docker install
341
+ // (主人: 装 Docker 的同时下载 R2 镜像). Returns the tarball path. Prefers curl
342
+ // (much faster here); falls back to the node downloader if curl is unavailable.
301
343
  async function downloadImageTarball({ emit } = {}) {
302
344
  const dest = imageTarballPath();
303
- await ensureDownloaded(R2_TARBALL, dest, null, { emit, phase: "image", label: "下载镜像" });
345
+ try { await curlDownload(R2_TARBALL, dest, { emit, phase: "image", label: "下载镜像" }); }
346
+ catch (e) {
347
+ emit && emit({ phase: "image", status: "running", message: `curl 下载失败(${e.message}),改用内置下载…` });
348
+ await ensureDownloaded(R2_TARBALL, dest, null, { emit, phase: "image", label: "下载镜像" });
349
+ }
304
350
  return dest;
305
351
  }
306
352
 
@@ -650,5 +696,5 @@ module.exports = {
650
696
  bootstrap, probeHealth, readContainerToken, dockerDesktopExe, desktopDir, downloadsDir, imageTarballPath,
651
697
  launchElevated, wslMissing, ensureWsl,
652
698
  // platform-agnostic download/retry primitives, reused by native.js
653
- ensureDownloaded, withRetry, waitUntil, run,
699
+ ensureDownloaded, curlDownload, withRetry, waitUntil, run,
654
700
  };
@@ -13,10 +13,37 @@
13
13
 
14
14
  const { execFile, execFileSync, spawn } = require("child_process");
15
15
  const path = require("path");
16
+ const os = require("os");
17
+ const fs = require("fs");
16
18
  const docker = require("./docker"); // shared: downloads, waitUntil, probeHealth, launchElevated, ensureWsl…
17
19
 
18
- const DISTRO = process.env.CICY_WSL_DISTRO || "Ubuntu";
20
+ // Dedicated distro name — NEVER reuse/clobber a user's own "Ubuntu" distro.
21
+ const DISTRO = process.env.CICY_WSL_DISTRO || "cicy-code-wsl";
19
22
  const IMAGE = process.env.CICY_DOCKER_IMAGE || "cicybot/cicy-code:latest";
23
+ // PRE-BAKED WSL rootfs (built in CI, .github/workflows/build-wsl-package.yml):
24
+ // Ubuntu 22.04 + Docker Engine + the cicy-code image already loaded into
25
+ // /var/lib/docker, with dockerd auto-start via /etc/wsl.conf. We just download
26
+ // it (Aliyun OSS, CN-fast ~2.7MB/s) and `wsl --import` it — so the bootstrap's
27
+ // apt-install + image download/load steps are already done inside the tarball
28
+ // (their checks see docker present + image present and SKIP). ~444MB.
29
+ const ROOTFS_URL = process.env.CICY_WSL_ROOTFS_URL ||
30
+ "https://cicy-1372193042-cn.oss-cn-shanghai.aliyuncs.com/rootfs/cicy-wsl-latest.tar.gz";
31
+
32
+ function rootfsPath() { return path.join(docker.downloadsDir(), "cicy-wsl-rootfs.tar.gz"); }
33
+ // WSL2 kernel update package (the small ~17MB MSI behind aka.ms/wsl2kernel).
34
+ const KERNEL_MSI_URL = process.env.CICY_WSL_KERNEL_URL || "https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi";
35
+
36
+ // Install the WSL2 kernel component (idempotent — msiexec on an already-present
37
+ // kernel is a fast no-op). Streams a progress bar; non-fatal on download failure
38
+ // so we still attempt the import (the kernel may already be there).
39
+ async function ensureWslKernel({ emit } = {}) {
40
+ const msi = path.join(docker.downloadsDir(), "wsl_update_x64.msi");
41
+ try { await docker.ensureDownloaded(KERNEL_MSI_URL, msi, null, { emit, phase: "install-docker", label: "下载 WSL2 内核" }); }
42
+ catch (e) { emit && emit({ phase: "install-docker", status: "running", message: `WSL2 内核下载失败:${e.message}(尝试继续)` }); return; }
43
+ emit && emit({ phase: "install-docker", status: "running", message: "安装 WSL2 内核组件…" });
44
+ await docker.launchElevated("msiexec", ["/i", msi, "/qn", "/norestart"], { emit });
45
+ await new Promise((r) => setTimeout(r, 8000)); // let msiexec register the kernel
46
+ }
20
47
 
21
48
  // Run a bash command as root inside the distro. execFile (no host shell) → the
22
49
  // command string is passed verbatim to `bash -lc`, so only bash-level quoting
@@ -73,17 +100,43 @@ function distroInstalled(distro = DISTRO) {
73
100
  // Install the Ubuntu distro WITHOUT launching its interactive first-run setup
74
101
  // (--no-launch). We always run commands as root afterwards, so no user account
75
102
  // is needed. Elevated via the scheduled-task path (reliable on these machines).
103
+ // Raw `wsl --import` of the rootfs as an ISOLATED v2 distro.
104
+ function importTarball(dest, installDir) {
105
+ return new Promise((resolve, reject) => {
106
+ execFile("wsl", ["--import", DISTRO, installDir, dest, "--version", "2"],
107
+ { timeout: 600000, windowsHide: true },
108
+ (err, _so, se) => { if (err) { err.stderr = String(se || ""); return reject(err); } resolve(); });
109
+ });
110
+ }
111
+
76
112
  async function installDistro({ emit } = {}) {
77
- emit && emit({ phase: "install-docker", status: "running", message: `安装 ${DISTRO} 子系统(首次下载较大,请耐心等待)…` });
78
- // Try non-elevated first (adding a distro to an existing WSL is per-user);
79
- // fall back to elevated if it errors.
113
+ // 1) Download the PRE-BAKED rootfs (Ubuntu+Docker+image baked in, ~444MB) with
114
+ // a real progress bar. curl is ~10× faster than node's downloader on OSS.
115
+ const dest = rootfsPath();
116
+ try { await docker.curlDownload(ROOTFS_URL, dest, { emit, phase: "image", label: "下载运行环境" }); }
117
+ catch (e) {
118
+ emit && emit({ phase: "image", status: "running", message: `下载器异常(${e.message}),改用备用下载…` });
119
+ await docker.ensureDownloaded(ROOTFS_URL, dest, null, { emit, phase: "image", label: "下载运行环境" });
120
+ }
121
+ // 2) Import as an ISOLATED WSL2 distro: its OWN VHDX under a dedicated dir, so
122
+ // it never touches the user's existing distros. `--version 2` sets just THIS
123
+ // distro to v2 — we do NOT run `--set-default-version` (that would change the
124
+ // user's global default). The WSL2 kernel is shared; we install it ONLY when
125
+ // import actually fails for lack of it (never downgrade an existing kernel).
126
+ const installDir = path.join(process.env["LOCALAPPDATA"] || path.join(os.homedir(), "AppData", "Local"), "cicy-code-wsl");
127
+ try { fs.mkdirSync(installDir, { recursive: true }); } catch {}
128
+ emit && emit({ phase: "container", status: "running", message: "导入运行环境到 WSL2…" });
80
129
  try {
81
- await new Promise((resolve, reject) => {
82
- execFile("wsl", ["--install", "-d", DISTRO, "--no-launch"], { timeout: 600000, windowsHide: true }, (err) => err ? reject(err) : resolve());
83
- });
84
- } catch {
85
- await docker.launchElevated("wsl", ["--install", "-d", DISTRO, "--no-launch"], { emit });
130
+ await importTarball(dest, installDir);
131
+ } catch (e) {
132
+ // Most likely the shared WSL2 kernel component is missing — install it
133
+ // (idempotent) and retry once.
134
+ emit && emit({ phase: "container", status: "running", message: "需要 WSL2 内核,正在安装后重试…" });
135
+ await ensureWslKernel({ emit });
136
+ await importTarball(dest, installDir);
86
137
  }
138
+ // 3) Free the ~444MB package now that the distro has everything.
139
+ try { fs.unlinkSync(dest); } catch {}
87
140
  }
88
141
 
89
142
  // docker CLI present inside the distro?
@@ -95,7 +148,18 @@ async function dockerInstalled() {
95
148
  // Install Docker Engine (docker.io) inside the distro.
96
149
  async function installDockerEngine({ emit } = {}) {
97
150
  emit && emit({ phase: "install-docker", status: "running", message: "在 Ubuntu 里安装 Docker(apt,几分钟,下面是实时进度)…" });
98
- await wslRunStream("apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io 2>&1", { emit, phase: "install-docker", timeout: 900000 });
151
+ // Point apt at the Tsinghua TUNA mirror (CN-fast; archive.ubuntu.com is slow
152
+ // from CN) WITH the universe component (docker.io lives there). DPkg::Lock::
153
+ // Timeout waits out the first-boot apt locks instead of failing with exit 100.
154
+ const M = process.env.CICY_APT_MIRROR || "https://mirrors.tuna.tsinghua.edu.cn/ubuntu/";
155
+ const setSources =
156
+ `{ echo 'deb ${M} jammy main restricted universe multiverse'; ` +
157
+ `echo 'deb ${M} jammy-updates main restricted universe multiverse'; ` +
158
+ `echo 'deb ${M} jammy-security main restricted universe multiverse'; } > /etc/apt/sources.list`;
159
+ await wslRunStream(
160
+ `${setSources} && apt-get -o DPkg::Lock::Timeout=300 update && ` +
161
+ `DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 install -y docker.io 2>&1`,
162
+ { emit, phase: "install-docker", timeout: 900000 });
99
163
  }
100
164
 
101
165
  // dockerd reachable inside the distro?
@@ -104,11 +168,22 @@ async function dockerEngineUp() {
104
168
  catch { return false; }
105
169
  }
106
170
 
107
- // Start the Docker daemon (SysV service; docker.io ships an init script).
171
+ // Start the Docker daemon inside WSL2. Modern docker.io (29.x) ships ONLY a
172
+ // systemd unit (no SysV init → `service docker start` says "unrecognized
173
+ // service"), and WSL distros have no systemd by default. So we launch dockerd
174
+ // directly. Two WSL2-specific prerequisites, both verified on a clean machine:
175
+ // • iptables must use the LEGACY backend (Ubuntu defaults to nft, which
176
+ // dockerd can't drive in WSL2 → daemon fails to set up networking).
177
+ // • run dockerd detached and wait for /var/run/docker.sock.
108
178
  async function startEngine() {
109
- // `service docker start` works without systemd; the `|| dockerd &` keeps it
110
- // up on distros where the service script is absent.
111
- try { await wslRun("service docker start 2>/dev/null || (nohup dockerd >/var/log/cicy-dockerd.log 2>&1 &)", { timeout: 30000 }); } catch {}
179
+ try {
180
+ await wslRun(
181
+ "update-alternatives --set iptables /usr/sbin/iptables-legacy >/dev/null 2>&1; " +
182
+ "update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy >/dev/null 2>&1; " +
183
+ "pgrep dockerd >/dev/null 2>&1 || (nohup dockerd >/var/log/cicy-dockerd.log 2>&1 &); " +
184
+ "for i in $(seq 1 20); do [ -S /var/run/docker.sock ] && docker version >/dev/null 2>&1 && break; sleep 1; done",
185
+ { timeout: 40000 });
186
+ } catch {}
112
187
  }
113
188
 
114
189
  // The cicy-code base image present inside the distro's Docker?
@@ -136,6 +211,12 @@ async function loadImage(winTarballPath, { emit } = {}) {
136
211
  const probeHealth = docker.probeHealth;
137
212
 
138
213
  // Start (or adopt) the container on :port inside the distro.
214
+ //
215
+ // cicy-code binds 127.0.0.1 inside the container (localhost-only by design), so
216
+ // `-p 8009:8008` doesn't work — docker-proxy can't reach a loopback-bound app.
217
+ // Instead use host networking + PORT=<port>: the app listens on <port> in the
218
+ // distro's network namespace, and WSL2's localhost relay forwards it to Windows
219
+ // 127.0.0.1:<port>. Verified: HEALTH 200 from Windows.
139
220
  async function runContainer({ port = 8009, container = "cicy-code-docker", volume = "cicy-team", env = {} } = {}) {
140
221
  if (await probeHealth(port)) return { adopted: true };
141
222
  // Replace any stale same-named container.
@@ -144,16 +225,17 @@ async function runContainer({ port = 8009, container = "cicy-code-docker", volum
144
225
  .filter(([, v]) => v != null && v !== "")
145
226
  .map(([k, v]) => `-e ${k}='${String(v).replace(/'/g, "'\\''")}'`)
146
227
  .join(" ");
147
- const cmd = `docker run -d --name ${container} --restart unless-stopped -p ${port}:8008 -v ${volume}:/home/cicy ${envArgs} ${IMAGE}`;
228
+ const cmd = `docker run -d --name ${container} --restart unless-stopped --network host -e PORT=${port} -v ${volume}:/home/cicy ${envArgs} ${IMAGE}`;
148
229
  await wslRun(cmd, { timeout: 60000 });
149
230
  return { started: true };
150
231
  }
151
232
 
152
233
  // Read the container's own api_token (its volume-persisted global.json) for the
153
234
  // team registration — the host token is a different credential.
154
- async function readContainerToken(port = 8009) {
235
+ async function readContainerToken(port = 8009, container = "cicy-code-docker") {
155
236
  try {
156
- const { stdout } = await wslRun(`docker ps --filter "publish=${port}" --format '{{.Names}}'`, { timeout: 10000 });
237
+ // Host networking has no "publish" mapping, so look the container up by name.
238
+ const { stdout } = await wslRun(`docker ps --filter "name=${container}" --format '{{.Names}}'`, { timeout: 10000 });
157
239
  const name = stdout.trim().split("\n")[0];
158
240
  if (!name) return "";
159
241
  const r = await wslRun(`docker exec ${name} cat /home/cicy/cicy-ai/global.json`, { timeout: 10000 });
@@ -161,6 +243,20 @@ async function readContainerToken(port = 8009) {
161
243
  } catch { return ""; }
162
244
  }
163
245
 
246
+ // Register a Windows logon task that starts dockerd in our distro on every
247
+ // logon — old inbox WSL ignores wsl.conf [boot], so without this :8009 is dead
248
+ // after a Windows reboot until the user clicks 启动. The container's
249
+ // --restart unless-stopped then brings cicy-code back automatically. Idempotent
250
+ // (start-dockerd.sh is `pgrep dockerd || dockerd`); /f overwrites a stale task.
251
+ function ensureAutostart() {
252
+ if (process.platform !== "win32") return Promise.resolve();
253
+ return new Promise((res) => {
254
+ const tr = `wsl.exe -d ${DISTRO} -u root -e /usr/local/sbin/start-dockerd.sh`;
255
+ execFile("schtasks", ["/create", "/tn", "cicy-docker-autostart", "/tr", tr, "/sc", "onlogon", "/rl", "HIGHEST", "/f"],
256
+ { windowsHide: true }, () => res());
257
+ });
258
+ }
259
+
164
260
  // Composite status for the card.
165
261
  async function status(port = 8009) {
166
262
  const wsl = !docker.wslMissing();
@@ -170,8 +266,22 @@ async function status(port = 8009) {
170
266
  return { wsl, distro, engineUp, running };
171
267
  }
172
268
 
269
+ // Guard against overlapping bootstrap runs (double-click 重试 / re-entrancy):
270
+ // two concurrent runs would race on the same download file + apt lock → corrupt
271
+ // downloads and exit-100. A second caller just attaches to the in-flight run.
272
+ let _bootstrapInFlight = null;
273
+
173
274
  // Full bootstrap. Honest progress + honest terminal (ok only when :port healthy).
174
- async function bootstrap({ onProgress, port = 8009, container = "cicy-code-docker", volume = "cicy-team", env = {} } = {}) {
275
+ async function bootstrap(opts = {}) {
276
+ if (_bootstrapInFlight) {
277
+ try { opts.onProgress && opts.onProgress({ phase: "install-docker", status: "running", message: "安装已在进行中,正在跟随同一进度…" }); } catch {}
278
+ return _bootstrapInFlight;
279
+ }
280
+ _bootstrapInFlight = _bootstrap(opts).finally(() => { _bootstrapInFlight = null; });
281
+ return _bootstrapInFlight;
282
+ }
283
+
284
+ async function _bootstrap({ onProgress, port = 8009, container = "cicy-code-docker", volume = "cicy-team", env = {} } = {}) {
175
285
  const emit = (ev) => { try { onProgress && onProgress(ev); } catch {} };
176
286
 
177
287
  // 1) WSL2 platform
@@ -182,48 +292,47 @@ async function bootstrap({ onProgress, port = 8009, container = "cicy-code-docke
182
292
 
183
293
  // 2) Ubuntu distro
184
294
  if (!distroInstalled()) {
185
- try { await installDistro({ emit }); } catch (e) { emit({ phase: "done", status: "error", message: `Ubuntu 安装失败:${e.message}(点重试)` }); return { ok: false, reason: "distro_install_failed" }; }
295
+ try { await installDistro({ emit }); } catch (e) { emit({ phase: "install-docker", status: "error", message: `Ubuntu 安装失败:${e.message}(点重试)` }); return { ok: false, reason: "distro_install_failed" }; }
186
296
  const t0 = Date.now();
187
297
  const ok = await docker.waitUntil(() => distroInstalled(), { totalMs: 600000, everyMs: 5000, onTick: () => emit({ phase: "install-docker", status: "running", message: `正在下载/注册 Ubuntu…(已 ${Math.round((Date.now() - t0) / 1000)}s,首次较慢请耐心)` }) });
188
- if (!ok) { emit({ phase: "done", status: "error", message: "Ubuntu 还没装好——稍等或点「重试」" }); return { ok: false, reason: "distro_not_ready" }; }
298
+ if (!ok) { emit({ phase: "install-docker", status: "error", message: "Ubuntu 还没装好——稍等或点「重试」" }); return { ok: false, reason: "distro_not_ready" }; }
189
299
  }
190
300
 
191
301
  // 3) Docker Engine inside Ubuntu
192
302
  if (!(await dockerInstalled())) {
193
- try { await installDockerEngine({ emit }); } catch (e) { emit({ phase: "done", status: "error", message: `Docker 安装失败:${e.message}(点重试)` }); return { ok: false, reason: "docker_install_failed" }; }
303
+ try { await installDockerEngine({ emit }); } catch (e) { emit({ phase: "install-docker", status: "error", message: `Docker 安装失败:${e.message}(点重试)` }); return { ok: false, reason: "docker_install_failed" }; }
194
304
  }
195
305
 
196
- // 4) dockerd up
306
+ // 4) dockerd up (phase "container" = 启动服务)
197
307
  if (!(await dockerEngineUp())) {
198
- emit({ phase: "install-docker", status: "running", message: "启动 Docker 引擎…" });
308
+ emit({ phase: "container", status: "running", message: "启动 Docker 引擎…" });
199
309
  await startEngine();
200
310
  const up = await docker.waitUntil(dockerEngineUp, { totalMs: 60000, everyMs: 3000 });
201
- if (!up) { emit({ phase: "done", status: "error", message: "Docker 引擎没起来——点「重试」" }); return { ok: false, reason: "dockerd_not_up" }; }
311
+ if (!up) { emit({ phase: "container", status: "error", message: "Docker 引擎没起来——点「重试」" }); return { ok: false, reason: "dockerd_not_up" }; }
202
312
  }
203
- emit({ phase: "install-docker", status: "done", message: "Docker 环境就绪" });
204
313
 
205
- // 5) Base image
314
+ // 5) Base image — pre-baked into the package, so this normally just confirms.
315
+ // The download-tarball path is a fallback for a non-pre-baked rootfs.
206
316
  if (!(await imagePresent())) {
207
317
  let tarball;
208
318
  try { tarball = await docker.downloadImageTarball({ emit }); }
209
- catch (e) { emit({ phase: "done", status: "error", message: `镜像下载失败:${e.message}(点重试续传)` }); return { ok: false, reason: "image_download_failed" }; }
210
- try { await loadImage(tarball, { emit }); emit({ phase: "image", status: "done", message: "镜像就绪" }); }
211
- catch (e) { emit({ phase: "done", status: "error", message: `镜像导入失败:${e.message}(点重试)` }); return { ok: false, reason: "image_load_failed" }; }
212
- } else {
213
- emit({ phase: "image", status: "skip", message: "镜像已就绪,跳过" });
319
+ catch (e) { emit({ phase: "image", status: "error", message: `镜像下载失败:${e.message}(点重试续传)` }); return { ok: false, reason: "image_download_failed" }; }
320
+ try { await loadImage(tarball, { emit }); }
321
+ catch (e) { emit({ phase: "image", status: "error", message: `镜像导入失败:${e.message}(点重试)` }); return { ok: false, reason: "image_load_failed" }; }
214
322
  }
215
323
 
216
- // 6) Container
324
+ // 6) Container (phase "container" = 启动服务)
217
325
  if (!(await probeHealth(port))) {
218
- emit({ phase: "container", status: "running", message: "启动 cicy-code 容器…" });
326
+ emit({ phase: "container", status: "running", message: "启动 cicy-code 服务…" });
219
327
  try { await runContainer({ port, container, volume, env }); }
220
- catch (e) { emit({ phase: "done", status: "error", message: `容器启动失败:${e.message}(点重试)` }); return { ok: false, reason: "container_start_failed" }; }
328
+ catch (e) { emit({ phase: "container", status: "error", message: `服务启动失败:${e.message}(点重试)` }); return { ok: false, reason: "container_start_failed" }; }
221
329
  }
222
330
 
223
331
  // 7) Health — the ONLY path to ok:true.
224
- emit({ phase: "health", status: "running", message: "等待 cicy-code 就绪…" });
332
+ emit({ phase: "container", status: "running", message: "等待 cicy-code 就绪…" });
225
333
  const healthy = await docker.waitUntil(() => probeHealth(port), { totalMs: 120000, everyMs: 3000 });
226
- emit({ phase: "done", status: healthy ? "done" : "error", message: healthy ? "Docker cicy-code 已就绪 🎉" : `容器起来了但 :${port} 还没响应——稍等或点「重试」` });
334
+ if (healthy) await ensureAutostart(); // survive Windows reboot
335
+ emit({ phase: healthy ? "done" : "container", status: healthy ? "done" : "error", message: healthy ? "Docker cicy-code 已就绪 🎉" : `服务起来了但 :${port} 还没响应——稍等或点「重试」` });
227
336
  return { ok: healthy, container };
228
337
  }
229
338
 
@@ -1500,8 +1500,8 @@ const dockerDrawer = {
1500
1500
  },
1501
1501
  close() { dockerDrawerState = null; emitDockerDrawer(); },
1502
1502
  };
1503
- const DOCKER_PHASES = [["install-docker", "准备环境"], ["image", "加载镜像"], ["container", "启动容器"], ["done", "完成"]];
1504
- const DOCKER_BADGE = { "install-docker": "Docker", image: "镜像", container: "容器", health: "容器", done: "完成" };
1503
+ const DOCKER_PHASES = [["install-docker", "准备环境"], ["image", "下载运行环境"], ["container", "启动服务"], ["done", "完成"]];
1504
+ const DOCKER_BADGE = { "install-docker": "准备", image: "下载", container: "启动", health: "启动", done: "完成" };
1505
1505
  const DOCKER_DL_LABEL = { "install-docker": "Docker Desktop", image: "基础镜像" };
1506
1506
  function fmtBytes(n) {
1507
1507
  if (!Number.isFinite(n)) return "?";