cicy-desktop 2.1.109 → 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.
- package/.github/workflows/build-wsl-package.yml +91 -0
- package/package.json +1 -1
- package/src/backends/homepage-react/assets/{index-DKTmSMvA.js → index-DRwt5BEG.js} +1 -1
- package/src/backends/homepage-react/index.html +1 -1
- package/src/sidecar/docker.js +53 -7
- package/src/sidecar/wsl-docker.js +141 -49
- package/workers/render/src/App.jsx +2 -2
|
@@ -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
|
@@ -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&>.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&>&&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&>.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&>&&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-
|
|
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>
|
package/src/sidecar/docker.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
300
|
-
//
|
|
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
|
|
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
|
};
|
|
@@ -17,15 +17,33 @@ const os = require("os");
|
|
|
17
17
|
const fs = require("fs");
|
|
18
18
|
const docker = require("./docker"); // shared: downloads, waitUntil, probeHealth, launchElevated, ensureWsl…
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
// Dedicated distro name — NEVER reuse/clobber a user's own "Ubuntu" distro.
|
|
21
|
+
const DISTRO = process.env.CICY_WSL_DISTRO || "cicy-code-wsl";
|
|
21
22
|
const IMAGE = process.env.CICY_DOCKER_IMAGE || "cicybot/cicy-code:latest";
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
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.
|
|
25
29
|
const ROOTFS_URL = process.env.CICY_WSL_ROOTFS_URL ||
|
|
26
|
-
"https://
|
|
30
|
+
"https://cicy-1372193042-cn.oss-cn-shanghai.aliyuncs.com/rootfs/cicy-wsl-latest.tar.gz";
|
|
27
31
|
|
|
28
|
-
function rootfsPath() { return path.join(docker.downloadsDir(), "
|
|
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
|
+
}
|
|
29
47
|
|
|
30
48
|
// Run a bash command as root inside the distro. execFile (no host shell) → the
|
|
31
49
|
// command string is passed verbatim to `bash -lc`, so only bash-level quoting
|
|
@@ -82,27 +100,45 @@ function distroInstalled(distro = DISTRO) {
|
|
|
82
100
|
// Install the Ubuntu distro WITHOUT launching its interactive first-run setup
|
|
83
101
|
// (--no-launch). We always run commands as root afterwards, so no user account
|
|
84
102
|
// is needed. Elevated via the scheduled-task path (reliable on these machines).
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
await docker.ensureDownloaded(ROOTFS_URL, dest, null, { emit, phase: "install-docker", label: "下载 Ubuntu" });
|
|
89
|
-
// 2) Ensure the WSL2 kernel exists (a feature-enable + reboot leaves the kernel
|
|
90
|
-
// component missing → `--import --version 2` errors). Best-effort, non-fatal.
|
|
91
|
-
emit && emit({ phase: "install-docker", status: "running", message: "检查/更新 WSL2 内核…" });
|
|
92
|
-
await new Promise((res) => execFile("wsl", ["--update", "--web-download"], { timeout: 180000, windowsHide: true }, () => res()));
|
|
93
|
-
try { await new Promise((res) => execFile("wsl", ["--set-default-version", "2"], { timeout: 15000, windowsHide: true }, () => res())); } catch {}
|
|
94
|
-
// 3) Import it as a WSL2 distro (creates the VHD under installDir; no MS Store,
|
|
95
|
-
// no interactive first-run — we run everything as root afterwards).
|
|
96
|
-
emit && emit({ phase: "install-docker", status: "running", message: "导入 Ubuntu 到 WSL2…" });
|
|
97
|
-
const installDir = path.join(process.env["LOCALAPPDATA"] || path.join(os.homedir(), "AppData", "Local"), "cicy-ubuntu");
|
|
98
|
-
try { fs.mkdirSync(installDir, { recursive: true }); } catch {}
|
|
99
|
-
await new Promise((resolve, reject) => {
|
|
103
|
+
// Raw `wsl --import` of the rootfs as an ISOLATED v2 distro.
|
|
104
|
+
function importTarball(dest, installDir) {
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
100
106
|
execFile("wsl", ["--import", DISTRO, installDir, dest, "--version", "2"],
|
|
101
107
|
{ timeout: 600000, windowsHide: true },
|
|
102
|
-
(err, _so, se) =>
|
|
108
|
+
(err, _so, se) => { if (err) { err.stderr = String(se || ""); return reject(err); } resolve(); });
|
|
103
109
|
});
|
|
104
110
|
}
|
|
105
111
|
|
|
112
|
+
async function installDistro({ emit } = {}) {
|
|
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…" });
|
|
129
|
+
try {
|
|
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);
|
|
137
|
+
}
|
|
138
|
+
// 3) Free the ~444MB package now that the distro has everything.
|
|
139
|
+
try { fs.unlinkSync(dest); } catch {}
|
|
140
|
+
}
|
|
141
|
+
|
|
106
142
|
// docker CLI present inside the distro?
|
|
107
143
|
async function dockerInstalled() {
|
|
108
144
|
try { await wslRun("command -v docker >/dev/null 2>&1", { timeout: 8000 }); return true; }
|
|
@@ -112,7 +148,18 @@ async function dockerInstalled() {
|
|
|
112
148
|
// Install Docker Engine (docker.io) inside the distro.
|
|
113
149
|
async function installDockerEngine({ emit } = {}) {
|
|
114
150
|
emit && emit({ phase: "install-docker", status: "running", message: "在 Ubuntu 里安装 Docker(apt,几分钟,下面是实时进度)…" });
|
|
115
|
-
|
|
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 });
|
|
116
163
|
}
|
|
117
164
|
|
|
118
165
|
// dockerd reachable inside the distro?
|
|
@@ -121,11 +168,22 @@ async function dockerEngineUp() {
|
|
|
121
168
|
catch { return false; }
|
|
122
169
|
}
|
|
123
170
|
|
|
124
|
-
// Start the Docker daemon
|
|
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.
|
|
125
178
|
async function startEngine() {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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 {}
|
|
129
187
|
}
|
|
130
188
|
|
|
131
189
|
// The cicy-code base image present inside the distro's Docker?
|
|
@@ -153,6 +211,12 @@ async function loadImage(winTarballPath, { emit } = {}) {
|
|
|
153
211
|
const probeHealth = docker.probeHealth;
|
|
154
212
|
|
|
155
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.
|
|
156
220
|
async function runContainer({ port = 8009, container = "cicy-code-docker", volume = "cicy-team", env = {} } = {}) {
|
|
157
221
|
if (await probeHealth(port)) return { adopted: true };
|
|
158
222
|
// Replace any stale same-named container.
|
|
@@ -161,16 +225,17 @@ async function runContainer({ port = 8009, container = "cicy-code-docker", volum
|
|
|
161
225
|
.filter(([, v]) => v != null && v !== "")
|
|
162
226
|
.map(([k, v]) => `-e ${k}='${String(v).replace(/'/g, "'\\''")}'`)
|
|
163
227
|
.join(" ");
|
|
164
|
-
const cmd = `docker run -d --name ${container} --restart unless-stopped -
|
|
228
|
+
const cmd = `docker run -d --name ${container} --restart unless-stopped --network host -e PORT=${port} -v ${volume}:/home/cicy ${envArgs} ${IMAGE}`;
|
|
165
229
|
await wslRun(cmd, { timeout: 60000 });
|
|
166
230
|
return { started: true };
|
|
167
231
|
}
|
|
168
232
|
|
|
169
233
|
// Read the container's own api_token (its volume-persisted global.json) for the
|
|
170
234
|
// team registration — the host token is a different credential.
|
|
171
|
-
async function readContainerToken(port = 8009) {
|
|
235
|
+
async function readContainerToken(port = 8009, container = "cicy-code-docker") {
|
|
172
236
|
try {
|
|
173
|
-
|
|
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 });
|
|
174
239
|
const name = stdout.trim().split("\n")[0];
|
|
175
240
|
if (!name) return "";
|
|
176
241
|
const r = await wslRun(`docker exec ${name} cat /home/cicy/cicy-ai/global.json`, { timeout: 10000 });
|
|
@@ -178,6 +243,20 @@ async function readContainerToken(port = 8009) {
|
|
|
178
243
|
} catch { return ""; }
|
|
179
244
|
}
|
|
180
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
|
+
|
|
181
260
|
// Composite status for the card.
|
|
182
261
|
async function status(port = 8009) {
|
|
183
262
|
const wsl = !docker.wslMissing();
|
|
@@ -187,8 +266,22 @@ async function status(port = 8009) {
|
|
|
187
266
|
return { wsl, distro, engineUp, running };
|
|
188
267
|
}
|
|
189
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
|
+
|
|
190
274
|
// Full bootstrap. Honest progress + honest terminal (ok only when :port healthy).
|
|
191
|
-
async function bootstrap(
|
|
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 = {} } = {}) {
|
|
192
285
|
const emit = (ev) => { try { onProgress && onProgress(ev); } catch {} };
|
|
193
286
|
|
|
194
287
|
// 1) WSL2 platform
|
|
@@ -199,48 +292,47 @@ async function bootstrap({ onProgress, port = 8009, container = "cicy-code-docke
|
|
|
199
292
|
|
|
200
293
|
// 2) Ubuntu distro
|
|
201
294
|
if (!distroInstalled()) {
|
|
202
|
-
try { await installDistro({ emit }); } catch (e) { emit({ phase: "
|
|
295
|
+
try { await installDistro({ emit }); } catch (e) { emit({ phase: "install-docker", status: "error", message: `Ubuntu 安装失败:${e.message}(点重试)` }); return { ok: false, reason: "distro_install_failed" }; }
|
|
203
296
|
const t0 = Date.now();
|
|
204
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,首次较慢请耐心)` }) });
|
|
205
|
-
if (!ok) { emit({ phase: "
|
|
298
|
+
if (!ok) { emit({ phase: "install-docker", status: "error", message: "Ubuntu 还没装好——稍等或点「重试」" }); return { ok: false, reason: "distro_not_ready" }; }
|
|
206
299
|
}
|
|
207
300
|
|
|
208
301
|
// 3) Docker Engine inside Ubuntu
|
|
209
302
|
if (!(await dockerInstalled())) {
|
|
210
|
-
try { await installDockerEngine({ emit }); } catch (e) { emit({ phase: "
|
|
303
|
+
try { await installDockerEngine({ emit }); } catch (e) { emit({ phase: "install-docker", status: "error", message: `Docker 安装失败:${e.message}(点重试)` }); return { ok: false, reason: "docker_install_failed" }; }
|
|
211
304
|
}
|
|
212
305
|
|
|
213
|
-
// 4) dockerd up
|
|
306
|
+
// 4) dockerd up (phase "container" = 启动服务)
|
|
214
307
|
if (!(await dockerEngineUp())) {
|
|
215
|
-
emit({ phase: "
|
|
308
|
+
emit({ phase: "container", status: "running", message: "启动 Docker 引擎…" });
|
|
216
309
|
await startEngine();
|
|
217
310
|
const up = await docker.waitUntil(dockerEngineUp, { totalMs: 60000, everyMs: 3000 });
|
|
218
|
-
if (!up) { emit({ phase: "
|
|
311
|
+
if (!up) { emit({ phase: "container", status: "error", message: "Docker 引擎没起来——点「重试」" }); return { ok: false, reason: "dockerd_not_up" }; }
|
|
219
312
|
}
|
|
220
|
-
emit({ phase: "install-docker", status: "done", message: "Docker 环境就绪" });
|
|
221
313
|
|
|
222
|
-
// 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.
|
|
223
316
|
if (!(await imagePresent())) {
|
|
224
317
|
let tarball;
|
|
225
318
|
try { tarball = await docker.downloadImageTarball({ emit }); }
|
|
226
|
-
catch (e) { emit({ phase: "
|
|
227
|
-
try { await loadImage(tarball, { emit });
|
|
228
|
-
catch (e) { emit({ phase: "
|
|
229
|
-
} else {
|
|
230
|
-
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" }; }
|
|
231
322
|
}
|
|
232
323
|
|
|
233
|
-
// 6) Container
|
|
324
|
+
// 6) Container (phase "container" = 启动服务)
|
|
234
325
|
if (!(await probeHealth(port))) {
|
|
235
|
-
emit({ phase: "container", status: "running", message: "启动 cicy-code
|
|
326
|
+
emit({ phase: "container", status: "running", message: "启动 cicy-code 服务…" });
|
|
236
327
|
try { await runContainer({ port, container, volume, env }); }
|
|
237
|
-
catch (e) { emit({ phase: "
|
|
328
|
+
catch (e) { emit({ phase: "container", status: "error", message: `服务启动失败:${e.message}(点重试)` }); return { ok: false, reason: "container_start_failed" }; }
|
|
238
329
|
}
|
|
239
330
|
|
|
240
331
|
// 7) Health — the ONLY path to ok:true.
|
|
241
|
-
emit({ phase: "
|
|
332
|
+
emit({ phase: "container", status: "running", message: "等待 cicy-code 就绪…" });
|
|
242
333
|
const healthy = await docker.waitUntil(() => probeHealth(port), { totalMs: 120000, everyMs: 3000 });
|
|
243
|
-
|
|
334
|
+
if (healthy) await ensureAutostart(); // survive Windows reboot
|
|
335
|
+
emit({ phase: healthy ? "done" : "container", status: healthy ? "done" : "error", message: healthy ? "Docker cicy-code 已就绪 🎉" : `服务起来了但 :${port} 还没响应——稍等或点「重试」` });
|
|
244
336
|
return { ok: healthy, container };
|
|
245
337
|
}
|
|
246
338
|
|
|
@@ -1500,8 +1500,8 @@ const dockerDrawer = {
|
|
|
1500
1500
|
},
|
|
1501
1501
|
close() { dockerDrawerState = null; emitDockerDrawer(); },
|
|
1502
1502
|
};
|
|
1503
|
-
const DOCKER_PHASES = [["install-docker", "准备环境"], ["image", "
|
|
1504
|
-
const DOCKER_BADGE = { "install-docker": "
|
|
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 "?";
|