cicy-desktop 2.1.82 → 2.1.84
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/package.json +6 -6
- package/src/backends/homepage-react/assets/{index-CSsNZgC5.js → index-xHkT3-tl.js} +1 -1
- package/src/backends/homepage-react/index.html +1 -1
- package/src/main.js +15 -0
- package/src/tools/desktop-snapshot-tools.js +72 -0
- package/src/tools/index.js +1 -0
- package/src/tools/tab-browser-tools.js +26 -10
- package/src/utils/desktop-snapshot.js +190 -0
- package/workers/render/src/App.jsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cicy-desktop",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.84",
|
|
4
4
|
"description": "CiCy - AI-powered operating system browser",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"bin": {
|
|
@@ -133,11 +133,11 @@
|
|
|
133
133
|
},
|
|
134
134
|
"//optionalDependencies": "Runtime Bundle v1 (主人指令): platform binaries delivered by `npm i -g cicy-desktop` itself — npm installs only the current-platform subpackage (os/cpu pinned in each), so first start seeds the runtime store with ZERO network, ZERO npx. Windows packages are named *-windows-* (npm spam filter 403s new names containing win32). cicy-msys2 added once published.",
|
|
135
135
|
"optionalDependencies": {
|
|
136
|
-
"cicy-code-darwin-x64": "2.3.
|
|
137
|
-
"cicy-code-darwin-arm64": "2.3.
|
|
138
|
-
"cicy-code-linux-x64": "2.3.
|
|
139
|
-
"cicy-code-linux-arm64": "2.3.
|
|
140
|
-
"cicy-code-windows-x64": "2.3.
|
|
136
|
+
"cicy-code-darwin-x64": "2.3.4",
|
|
137
|
+
"cicy-code-darwin-arm64": "2.3.4",
|
|
138
|
+
"cicy-code-linux-x64": "2.3.4",
|
|
139
|
+
"cicy-code-linux-arm64": "2.3.4",
|
|
140
|
+
"cicy-code-windows-x64": "2.3.4",
|
|
141
141
|
"cicy-mihomo-darwin-x64": "1.10.4",
|
|
142
142
|
"cicy-mihomo-darwin-arm64": "1.10.4",
|
|
143
143
|
"cicy-mihomo-linux-x64": "1.10.4",
|
|
@@ -362,4 +362,4 @@ Error generating stack: `+a.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
|
-
`},Y=(o,U)=>{var E,m;try{const O=(m=(E=window.cicyI18n)==null?void 0:E.t)==null?void 0:m.call(E,o);return O&&O!==o?O:U}catch{return U}},Ka="cicy_token",oi="cicy_access_token",ri="cicy_user_id",mi="https://cicy-ai.com";async function hi(o){var U,E,m;try{(m=(E=(U=window.cicy)==null?void 0:U.shell)==null?void 0:E.openExternal)==null||m.call(E,`${mi}/dash${o}`)}catch{}}const Rs=new Set;let Yy=0,Ql=[];const Zn=new Map;function $d(){Rs.forEach(o=>o(Ql))}const je={show(o={}){const U=o.id||`t${++Yy}`,E=Ql.find(B=>B.id===U),m={id:U,status:"running",...E,...o};Ql=E?Ql.map(B=>B.id===U?m:B):[...Ql,m],$d();const O=Zn.get(U);return O&&(clearTimeout(O),Zn.delete(U)),o.ttl&&Zn.set(U,setTimeout(()=>je.dismiss(U),o.ttl)),U},dismiss(o){Ql=Ql.filter(E=>E.id!==o);const U=Zn.get(o);U&&(clearTimeout(U),Zn.delete(o)),$d()}};function Ly(){const[o,U]=N.useState(Ql);return N.useEffect(()=>(Rs.add(U),()=>{Rs.delete(U)}),[]),o.length?s.jsx("div",{className:"toast-host","data-id":"ToastHost",children:o.map(E=>s.jsxs("div",{className:"toast","data-id":`Toast-${E.id}`,"data-status":E.status||"running",children:[s.jsx("button",{type:"button",className:"toast__x","data-id":"Toast-dismiss",onClick:()=>je.dismiss(E.id),"aria-label":"dismiss",children:"×"}),s.jsxs("span",{className:"toast__msg",children:[E.message,Number.isFinite(E.progress)?` ${E.progress}%`:""]}),Number.isFinite(E.progress)&&s.jsx("span",{className:"toast__bar",children:s.jsx("span",{style:{width:`${Math.min(100,E.progress)}%`}})})]},E.id))}):null}const Hs=new Set;let Fd=0,oe=null;function di(){Hs.forEach(o=>o(oe))}function Id(){return new Date().toTimeString().slice(0,8)}const hl={open({teamId:o,fromVer:U,toVer:E,onRetry:m}={}){oe={teamId:o,fromVer:U||null,toVer:E||null,status:"running",phase:"download",logs:[],onRetry:m||null,lastAt:Date.now()},di()},push(o={}){if(!oe)return;const U={id:++Fd,t:Id(),phase:o.phase||oe.phase,status:o.status||"running",message:o.message||""};oe={...oe,phase:o.phase||oe.phase,toVer:o.toVer||oe.toVer,logs:[...oe.logs,U],lastAt:Date.now()},di()},finish({ok:o,message:U}={}){if(!oe)return;const E=o?"done":"error",m={id:++Fd,t:Id(),phase:"done",status:E,message:U||(o?"更新完成":"更新失败")};oe={...oe,status:E,phase:"done",logs:[...oe.logs,m],lastAt:Date.now()},di()},close(){oe=null,di()}},ws=[["download","下载"],["swap","切换"],["done","完成"]];function Gy(){var at;const[o,U]=N.useState(oe);N.useEffect(()=>(Hs.add(U),()=>{Hs.delete(U)}),[]);const E=N.useRef(null);N.useEffect(()=>{const z=E.current;z&&(z.scrollTop=z.scrollHeight)},[(at=o==null?void 0:o.logs)==null?void 0:at.length]);const[m,O]=N.useState(!1);if(N.useEffect(()=>{if(!o||o.status!=="running"){O(!1);return}const z=setInterval(()=>O(Date.now()-(o.lastAt||0)>25e3),3e3);return()=>clearInterval(z)},[o==null?void 0:o.lastAt,o==null?void 0:o.status]),!o)return null;const B=o.status==="running",P=ws.findIndex(([z])=>z===o.phase);return s.jsx("div",{className:"drawer-scrim","data-id":"UpdateDrawer-scrim",onClick:()=>{B||hl.close()},children:s.jsxs("div",{className:"drawer","data-id":"UpdateDrawer","data-status":o.status,onClick:z=>z.stopPropagation(),children:[s.jsxs("div",{className:"drawer__head",children:[s.jsxs("div",{className:"drawer__title",children:[s.jsx("span",{className:`drawer__spark drawer__spark--${o.status}`,children:B?s.jsx(yl,{}):o.status==="done"?"✓":"!"}),s.jsxs("div",{children:[s.jsx("div",{className:"drawer__h",children:"更新 cicy-code"}),s.jsxs("div",{className:"drawer__sub",children:[o.fromVer?`v${o.fromVer}`:"当前"," → ",o.toVer?`v${o.toVer}`:"最新版"]})]})]}),s.jsx("button",{type:"button",className:"drawer__x","data-id":"UpdateDrawer-close",disabled:B,title:B?"更新进行中":"关闭",onClick:()=>hl.close(),"aria-label":"close",children:"×"})]}),s.jsx("div",{className:"drawer__steps","data-id":"UpdateDrawer-steps",children:ws.map(([z,b],X)=>{const w=o.status==="done"||X<P,lt=X===P&&B,K=o.status==="error"&&X===P;return s.jsxs("div",{className:`drawer__step${lt?" is-active":""}${w?" is-done":""}${K?" is-error":""}`,children:[s.jsx("span",{className:"drawer__step-dot",children:w?"✓":K?"!":X+1}),s.jsx("span",{className:"drawer__step-label",children:b}),X<ws.length-1&&s.jsx("span",{className:"drawer__step-bar"})]},z)})}),s.jsx("div",{className:"drawer__log","data-id":"UpdateDrawer-log",ref:E,children:o.logs.length===0?s.jsx("div",{className:"drawer__log-empty",children:"准备中…"}):o.logs.map(z=>s.jsxs("div",{className:"drawer__line","data-status":z.status,children:[s.jsx("span",{className:"drawer__t",children:z.t}),s.jsx("span",{className:`drawer__badge drawer__badge--${z.phase}`,children:{download:"下载",swap:"切换",done:"完成"}[z.phase]||z.phase}),s.jsx("span",{className:"drawer__linemsg",children:z.message})]},z.id))}),m&&B&&s.jsx("div",{className:"drawer__hint","data-id":"UpdateDrawer-stuck",children:"正在等待新版本就绪,耗时比平常久。可以放到后台继续,完成或失败都会提示。"}),s.jsx("div",{className:"drawer__foot",children:B?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"drawer__foot-status",children:"更新进行中…"}),s.jsx("button",{type:"button",className:"drawer__btn","data-id":"UpdateDrawer-background",onClick:()=>hl.close(),children:"在后台继续"})]}):o.status==="error"?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"drawer__foot-status is-error",children:"更新失败"}),o.onRetry&&s.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"UpdateDrawer-retry",onClick:()=>o.onRetry(),children:"重试"}),s.jsx("button",{type:"button",className:"drawer__btn","data-id":"UpdateDrawer-dismiss",onClick:()=>hl.close(),children:"关闭"})]}):s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"drawer__foot-status is-done",children:"已更新到最新"}),s.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"UpdateDrawer-finish",onClick:()=>hl.close(),children:"完成"})]})})]})})}function Qy(){const[o,U]=N.useState(void 0);N.useEffect(()=>{var D,F;if(!((F=(D=window.cicy)==null?void 0:D.terms)!=null&&F.status)){U(!0);return}window.cicy.terms.status(cm).then(G=>U(!!(G!=null&&G.accepted))).catch(()=>U(!0))},[]),N.useEffect(()=>{var G,C,q,$;const D=document.documentElement;try{D.setAttribute("data-platform",((G=window.cicy)==null?void 0:G.platform)||"linux")}catch{}D.setAttribute("data-fullscreen","0");let F;try{F=($=(q=(C=window.cicy)==null?void 0:C.window)==null?void 0:q.onFullscreen)==null?void 0:$.call(q,et=>D.setAttribute("data-fullscreen",et?"1":"0"))}catch{}return()=>{try{F&&F()}catch{}}},[]);const[E,m]=N.useState(()=>Vn(Ka)),[O,B]=N.useState(()=>Vn(oi)),[P,at]=N.useState(()=>Vn(ri)),[z,b]=N.useState(()=>!Vn(Ka)),[X,w]=N.useState(!1),[lt,K]=N.useState(""),[Q,L]=N.useState(""),[k,ct]=N.useState(null),[st,it]=N.useState(null),[ht,Tt]=N.useState(!1),[W,j]=N.useState(""),bt=N.useRef(!1),[M,yt]=N.useState(null),[Qt,Xt]=N.useState(!1),[Zt,pe]=N.useState(!1),[Yt,S]=N.useState("all"),R=N.useCallback(async(D,F)=>{var C,q,$,et;if(!D)return;if(!((q=(C=window.cicy)==null?void 0:C.cloud)!=null&&q.fetch)){j("cloud fetch bridge missing");return}Tt(!0),j("");const G={Authorization:`Bearer ${D}`};try{const[Wt,Gt]=await Promise.all([window.cicy.cloud.fetch(`${mi}/api/user/self`,{headers:G}),window.cicy.cloud.fetch(`${mi}/api/teams`,{headers:G})]);if((Gt==null?void 0:Gt.status)===401){if(!bt.current&&((et=($=window.cicy)==null?void 0:$.auth)!=null&&et.loginStart)){bt.current=!0,j("会话已过期,正在重新登录…");try{await window.cicy.auth.loginStart()}catch{}}return}if(!(Gt!=null&&Gt.ok))throw new Error(`/api/teams ${(Gt==null?void 0:Gt.status)||"?"} ${(Gt==null?void 0:Gt.error)||""}`);const Oe=JSON.parse(Gt.body||"{}");if(it(Array.isArray(Oe==null?void 0:Oe.teams)?Oe.teams:[]),Wt!=null&&Wt.ok)try{const rt=JSON.parse(Wt.body||"{}");ct((rt==null?void 0:rt.success)===!1?null:(rt==null?void 0:rt.data)||null)}catch{ct(null)}else ct(null)}catch(Wt){j(Wt.message||String(Wt))}finally{Tt(!1)}},[]),tt=N.useRef("");N.useEffect(()=>{tt.current=E||O||""},[E,O]);const xt=N.useCallback(async()=>{var F,G;const D=tt.current;if(!(!D||!((G=(F=window.cicy)==null?void 0:F.cloud)!=null&&G.fetch)))try{const C=await window.cicy.cloud.fetch(`${mi}/api/teams`,{headers:{Authorization:`Bearer ${D}`}});if(C!=null&&C.ok){const q=JSON.parse(C.body||"{}");Array.isArray(q==null?void 0:q.teams)&&it(q.teams)}}catch{}},[]);N.useEffect(()=>{const D=E||O;D&&R(D,P)},[E,O,P,R]);const gt=N.useCallback(async()=>{var D,F;if((F=(D=window.cicy)==null?void 0:D.localTeams)!=null&&F.list){Xt(!0);try{const G=await window.cicy.localTeams.list({refresh:!0});yt(Array.isArray(G)?G:[])}catch{yt([])}finally{Xt(!1),pe(!0)}}},[]),r=N.useCallback(async(D,F)=>{var C,q;if(!((q=(C=window.cicy)==null?void 0:C.localTeams)!=null&&q.update))return{ok:!1,error:"no_bridge"};let G;try{G=await window.cicy.localTeams.update(D,{name:String(F||"").trim()||Y("localTeams.unnamed","未命名")})}catch($){G={ok:!1,error:($==null?void 0:$.message)||String($)}}return await gt(),G||{ok:!1,error:"no_result"}},[gt]);N.useEffect(()=>{let D,F=!1;const G=3e3,C=3e4,q=async()=>{var Wt,Gt,Oe;try{await((Oe=(Gt=(Wt=window.cicy)==null?void 0:Wt.localTeams)==null?void 0:Gt.syncCloud)==null?void 0:Oe.call(Gt))}catch{}await Promise.all([gt(),xt()])},$=()=>{if(F)return;const Wt=document.visibilityState==="visible";D=setTimeout(async()=>{document.visibilityState==="visible"?await q():await gt(),$()},Wt?G:C)};q(),$();const et=()=>{document.visibilityState==="visible"&&q()};return document.addEventListener("visibilitychange",et),window.addEventListener("focus",et),()=>{F=!0,clearTimeout(D),document.removeEventListener("visibilitychange",et),window.removeEventListener("focus",et)}},[gt,xt]),N.useEffect(()=>{var F,G;if(!((G=(F=window.cicy)==null?void 0:F.localTeams)!=null&&G.onWebviewRelay))return;const D=window.cicy.localTeams.onWebviewRelay(async({reqId:C,msg:q})=>{let $={ok:!1,error:"unknown relay type"};try{(q==null?void 0:q.type)==="localTeams:add"?$=await window.cicy.localTeams.add(q.spec||{}):(q==null?void 0:q.type)==="localTeams:remove"?$=await window.cicy.localTeams.remove(q.id):(q==null?void 0:q.type)==="localTeams:update"?$=await window.cicy.localTeams.update(q.id,q.patch||{}):(q==null?void 0:q.type)==="localTeams:upgrade"?$=await window.cicy.localTeams.upgrade(q.id):(q==null?void 0:q.type)==="localTeams:list"&&($={ok:!0,teams:await window.cicy.localTeams.list({refresh:!0})}),gt()}catch(et){$={ok:!1,error:(et==null?void 0:et.message)||String(et)}}try{window.cicy.localTeams.replyWebviewRelay(C,$)}catch{}});return()=>{try{D==null||D()}catch{}}},[gt]);const x=N.useCallback(async D=>{var F,G;if((G=(F=window.cicy)==null?void 0:F.localTeams)!=null&&G.open)try{await window.cicy.localTeams.open(D)}catch{}},[]);N.useEffect(()=>{var F,G;if(Vn(Ka)){b(!1);return}if(!((G=(F=window.cicy)==null?void 0:F.auth)!=null&&G.getSaved)){b(!1);return}let D=!1;return(async()=>{try{const C=await window.cicy.auth.getSaved();if(D)return;if(C!=null&&C.token){try{localStorage.setItem(Ka,C.token)}catch{}if(m(C.token),C.accessToken){try{localStorage.setItem(oi,C.accessToken)}catch{}B(C.accessToken)}if(C.userId){try{localStorage.setItem(ri,String(C.userId))}catch{}at(String(C.userId))}}}catch{}finally{D||b(!1)}})(),()=>{D=!0}},[]),N.useEffect(()=>{var D,F;if((F=(D=window.cicy)==null?void 0:D.auth)!=null&&F.onComplete)return window.cicy.auth.onComplete(G=>{if(w(!1),G!=null&&G.error){K(lm(G.error));return}if(G!=null&&G.token){bt.current=!1,j("");try{localStorage.setItem(Ka,G.token)}catch{}if(m(G.token),G.accessToken){try{localStorage.setItem(oi,G.accessToken)}catch{}B(G.accessToken)}if(G.userId){try{localStorage.setItem(ri,String(G.userId))}catch{}at(String(G.userId))}K(""),L(G.reused?"已恢复你之前的登录":"登录成功"),setTimeout(()=>L(""),3e3)}})},[]);async function H(){var F,G;if(!((G=(F=window.cicy)==null?void 0:F.auth)!=null&&G.loginStart)){K("auth bridge missing");return}K(""),w(!0);const D=await window.cicy.auth.loginStart();D!=null&&D.ok||(w(!1),K(lm((D==null?void 0:D.error)||"login start failed")))}function Z(){var D,F,G;try{localStorage.removeItem(Ka),localStorage.removeItem(oi),localStorage.removeItem(ri)}catch{}try{(G=(F=(D=window.cicy)==null?void 0:D.auth)==null?void 0:F.logout)==null||G.call(F)}catch{}m(null),B(null),at(null),ct(null),it(null),K(""),j("")}if(o===void 0)return s.jsxs("div",{className:"shell","data-id":"TermsCheckingSplash",children:[s.jsx("div",{className:"glow","aria-hidden":!0}),s.jsxs("div",{className:"card",children:[s.jsx(Us,{}),s.jsx("div",{className:"spinner-row",children:s.jsx(yl,{})})]})]});if(!o)return s.jsx(Ky,{onAgree:()=>U(!0)});if(!E&&z)return s.jsxs("div",{className:"shell","data-id":"AuthRestoringSplash",children:[s.jsx("div",{className:"glow","aria-hidden":!0}),s.jsxs("div",{className:"card",children:[s.jsx(Us,{}),s.jsxs("div",{className:"spinner-row",children:[s.jsx(yl,{}),s.jsx("span",{children:"正在恢复登录…"})]})]})]});if(!E)return s.jsxs("div",{className:"shell",children:[s.jsx("div",{className:"glow","aria-hidden":!0}),s.jsxs("div",{className:"card",children:[s.jsx(Us,{}),!X&&s.jsxs(s.Fragment,{children:[s.jsx("p",{className:"tagline",children:"登录以同步你的团队、配置与 AI 助手"}),s.jsxs("button",{className:"btn-primary",onClick:H,children:[s.jsx("span",{children:"使用浏览器登录"}),s.jsx(Ys,{})]}),s.jsx("p",{className:"hint",children:"点击后会自动打开浏览器"})]}),X&&s.jsxs(s.Fragment,{children:[s.jsx("p",{className:"tagline",children:"已在浏览器打开登录页,等待你完成…"}),s.jsxs("div",{className:"spinner-row",children:[s.jsx(yl,{}),s.jsx("span",{children:"等待回调"})]}),s.jsx("button",{className:"btn-ghost",onClick:()=>{var D,F,G;(G=(F=(D=window.cicy)==null?void 0:D.auth)==null?void 0:F.loginCancel)==null||G.call(F),w(!1)},children:"取消"})]}),lt&&s.jsx("div",{className:"error",children:lt})]})]});const nt=(M||[]).filter(D=>Bs(D.base_url)),ft=(M||[]).filter(D=>!Bs(D.base_url)),At=nt.length,Ht=ft.length,Nt=(st||[]).filter(D=>!D.is_local&&D.kind!=="local"),Ne=Nt.length,Ze=Yt==="all"||Yt==="local",gl=Yt==="all"||Yt==="custom",Xl=Yt==="all"||Yt==="cloud";return s.jsxs("div",{className:"shell shell--app",children:[s.jsx("div",{className:"glow glow--app","aria-hidden":!0}),s.jsxs("div",{className:"shell__left",children:[s.jsx(Vy,{me:k,welcome:Q,onLogout:Z,mitmTeam:nt.length>0?nt[0]:null}),s.jsxs("main",{className:"main",children:[s.jsxs("div",{className:"app__tabsrow",children:[s.jsx("div",{className:"app__tabs",children:[{k:"all",label:"全部",n:At+Ht+Ne},{k:"local",label:"本地",n:At},{k:"cloud",label:"私有云",n:Ne},{k:"custom",label:"自定义",n:Ht}].map(({k:D,label:F,n:G})=>s.jsxs("button",{type:"button",className:`app__tab ${Yt===D?"is-active":""}`,onClick:()=>S(D),children:[F,s.jsx("span",{className:"app__tab-count",children:G})]},D))}),s.jsxs("button",{type:"button","data-id":"AddTeamButton",className:"app__add-team",title:Y("teams.addHint","在云端新建私有云团队"),onClick:()=>hi("?tab=private"),children:["+ ",Y("teams.add","新加团队")]})]}),W&&s.jsxs("div",{className:"error",style:{marginBottom:12},children:["云端: ",W,s.jsx("button",{className:"btn-ghost",style:{marginLeft:8},onClick:()=>R(E||O,P),children:"重试"})]}),s.jsxs("div",{className:"app__grid",children:[Ze&&nt.map(D=>s.jsx(Pd,{team:D,onOpen:()=>x(D.id),onRename:r,onRefresh:gt},"local:"+D.id)),Ze&&nt.length===0&&s.jsxs("div",{"data-id":"LocalTeamPlaceholder",className:"bcard bcard--local",children:[s.jsx("div",{className:"bcard__accent"}),s.jsx("div",{className:"bcard__top",children:s.jsxs("div",{className:"bcard__pill",children:[s.jsx("span",{className:"bcard__dot","data-tone":"warn"}),s.jsx(fm,{})]})}),s.jsxs("div",{className:"bcard__body",children:[s.jsx("h3",{className:"bcard__name",children:"本地团队"}),s.jsx("div",{className:"bcard__host",children:"http://127.0.0.1:8008"}),s.jsx("div",{className:"bcard__meta"})]}),s.jsxs("button",{type:"button",className:"bcard__cta",disabled:!0,children:[s.jsx(yl,{}),s.jsx("span",{children:Zt?"正在启动,就绪后自动加入…":"检测中…"})]})]}),gl&&ft.map(D=>s.jsx(Pd,{team:D,onOpen:()=>x(D.id),onRename:r,onRefresh:gt},"custom:"+D.id)),Xl&&Nt.map(D=>s.jsx(Jy,{team:D,onOpen:()=>{var G,C,q;const F=D.kind==="private"?D.host_url:D.workspace_url||D.workspace_direct_url;F&&((q=(C=(G=window.cicy)==null?void 0:G.tabs)==null?void 0:C.open)==null||q.call(C,F,D.name||D.title||""))}},"cloud:"+D.id)),Ze&&s.jsxs("button",{type:"button",className:"add-card",onClick:()=>{alert("装本地 cicy-code(npx cicy-code / docker run)后会自动出现,或在云端创建团队。")},children:[s.jsx("span",{className:"add-card__plus",children:"+"}),s.jsx("span",{className:"add-card__label",children:"新建本地团队"})]})]}),!ht&&!W&&st&&st.length===0&&!(M!=null&&M.length)&&s.jsx("div",{className:"empty",style:{marginTop:14},children:"还没有团队 — 安装本地 cicy-code 起一个本地 team,或在云端创建。"})]})]}),s.jsx(Ly,{}),s.jsx(Gy,{})]})}function Xy({onClose:o}){const[U,E]=N.useState(null),[m,O]=N.useState(""),[B,P]=N.useState(!1),[at,z]=N.useState(""),b=typeof window<"u"&&window.cicy&&window.cicy.trustedOrigins||null,X=N.useCallback(async()=>{try{E(b&&await b.list()||[])}catch{E([])}},[b]);N.useEffect(()=>{X()},[X]);const w=async()=>{const Q=m.trim();if(!(!Q||B||!b)){P(!0),z("");try{const L=await b.add(Q);L&&L.ok===!1?z(L.error||Y("trustedSites.addFailed","添加失败")):(O(""),E(L&&L.origins||await b.list()))}catch(L){z(String(L&&L.message||L))}finally{P(!1)}}},lt=async Q=>{if(!(B||!b)){P(!0),z("");try{const L=await b.remove(Q);L&&L.ok===!1?z(L.error||Y("trustedSites.removeFailed","删除失败")):E(L&&L.origins||await b.list())}catch(L){z(String(L&&L.message||L))}finally{P(!1)}}},K={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:Q=>({flex:1,fontFamily:"ui-monospace,SFMono-Regular,Menlo,monospace",fontSize:13,color:Q?"#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 Kn.createPortal(s.jsx("div",{style:K.overlay,onClick:o,"data-id":"TrustedSitesModal",children:s.jsxs("div",{style:K.card,onClick:Q=>Q.stopPropagation(),children:[s.jsxs("div",{style:K.head,children:[s.jsx("h2",{style:K.title,children:Y("trustedSites.title","受信任站点")}),s.jsx("button",{type:"button",style:K.x,onClick:o,"aria-label":"close",children:"✕"})]}),s.jsx("div",{style:K.warn,children:Y("trustedSites.warn","⚠ 列表中的站点可以在你的电脑上执行命令(exec)。只添加你完全信任的地址。")}),s.jsxs("div",{style:K.addRow,children:[s.jsx("input",{"data-id":"trusted-sites-input",style:K.input,value:m,onChange:Q=>O(Q.target.value),onKeyDown:Q=>{Q.key==="Enter"&&w()},placeholder:Y("trustedSites.placeholder","添加站点,如 app.cicy-ai.com 或 my-cloud.example.org")}),s.jsx("button",{type:"button","data-id":"trusted-sites-add",style:{...K.addBtn,opacity:B||!m.trim()?.5:1},onClick:w,disabled:B||!m.trim(),children:Y("trustedSites.add","添加")})]}),at&&s.jsx("div",{style:K.err,children:at}),s.jsx("div",{style:K.listWrap,children:U===null?s.jsx("div",{style:K.muted,children:Y("trustedSites.loading","加载中…")}):U.length===0?s.jsx("div",{style:K.muted,children:Y("trustedSites.empty","暂无")}):U.map(Q=>s.jsxs("div",{style:K.row,"data-id":"trusted-sites-row",children:[s.jsx("span",{style:K.host(Q.builtin),children:Q.host}),Q.builtin?s.jsx("span",{style:K.tag,children:Y("trustedSites.builtin","系统")}):s.jsx("button",{type:"button",style:K.rm,onClick:()=>lt(Q.host),disabled:B,children:Y("trustedSites.remove","删除")})]},Q.host))})]})}),document.body)}function Zy({onClose:o}){const[U,E]=N.useState(null),[m,O]=N.useState(""),[B,P]=N.useState(""),[at,z]=N.useState(!1),b=typeof window<"u"&&window.cicy&&window.cicy.rpcAudit||null,X=N.useCallback(async()=>{z(!0),O("");try{const M=b&&await b.tail(400);!M||M.ok===!1?(O(M&&M.error||Y("audit.loadFailed","读取失败")),E([])):(E(M.entries||[]),P(M.path||""))}catch(M){O(String(M&&M.message||M)),E([])}finally{z(!1)}},[b]);N.useEffect(()=>{X()},[X]);const[w,lt]=N.useState("all"),[K,Q]=N.useState(""),L=M=>{try{return new Date(M).toLocaleString()}catch{return M||""}},k=M=>{if(M.kind==="auth"){const yt=/deny/.test(M.decision||"");return{text:M.decision||"auth",color:yt?"#fca5a5":"#86efac",bg:yt?"rgba(239,68,68,.14)":"rgba(34,197,94,.14)"}}if(M.kind==="rpc"){const yt=M.ok!==!1&&!M.error;return{text:yt?"ok":"err",color:yt?"#86efac":"#fca5a5",bg:yt?"rgba(34,197,94,.14)":"rgba(239,68,68,.14)"}}return{text:M.kind||"log",color:"#a1a1aa",bg:"rgba(255,255,255,.06)"}},ct=M=>M.kind==="auth"?`${M.gate||""}${M.decision?" · "+M.decision:""}`:M.kind==="rpc"?`${M.tool||""}${M.dangerous?" ⚠":""}`:M.kind||"",st=M=>M.error||M.args||(M.kind==="rpc"?M.channel:"")||"",it=U||[],ht=K.trim().toLowerCase(),Tt=it.filter(M=>w!=="all"&&M.kind!==w?!1:ht?[M.origin,M.host,M.tool,M.gate,M.decision,M.channel,M.args,M.error,M.kind].filter(Boolean).join(" ").toLowerCase().includes(ht):!0),W="186px 104px minmax(160px,1.1fr) minmax(150px,1.1fr) minmax(220px,1.8fr)",j={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:M=>({background:M?"rgba(255,255,255,.14)":"transparent",border:"1px solid rgba(255,255,255,.12)",borderRadius:999,padding:"6px 16px",color:M?"#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:W,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:W,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:M=>({justifySelf:"start",fontSize:11.5,color:M.color,background:M.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}},bt=M=>s.jsx("div",{children:M});return Kn.createPortal(s.jsx("div",{style:j.overlay,onClick:o,"data-id":"AuditLogModal",children:s.jsxs("div",{style:j.card,onClick:M=>M.stopPropagation(),children:[s.jsxs("div",{style:j.head,children:[s.jsxs("div",{style:j.titleWrap,children:[s.jsx("h2",{style:j.title,children:Y("audit.title","审计日志")}),B&&s.jsx("p",{style:j.subtitle,children:B})]}),s.jsxs("span",{style:j.count,children:[Y("audit.count","共")," ",Tt.length,w!=="all"||ht?` / ${it.length}`:""]}),s.jsx("button",{type:"button","data-id":"audit-refresh",style:{...j.refresh,opacity:at?.5:1},onClick:X,disabled:at,children:Y("audit.refresh","刷新")}),s.jsx("button",{type:"button",style:j.x,onClick:o,"aria-label":"close",children:"✕"})]}),s.jsxs("div",{style:j.toolbar,children:[s.jsxs("div",{style:j.chips,children:[s.jsx("button",{type:"button",style:j.chip(w==="all"),onClick:()=>lt("all"),children:Y("audit.all","全部")}),s.jsx("button",{type:"button",style:j.chip(w==="rpc"),onClick:()=>lt("rpc"),children:Y("audit.rpc","RPC 调用")}),s.jsx("button",{type:"button",style:j.chip(w==="auth"),onClick:()=>lt("auth"),children:Y("audit.auth","授权决定")})]}),s.jsx("input",{"data-id":"audit-search",style:j.search,value:K,onChange:M=>Q(M.target.value),placeholder:Y("audit.search","搜索来源 / 工具 / 命令…")})]}),m&&s.jsx("div",{style:j.err,children:m}),s.jsxs("div",{style:j.tableWrap,children:[s.jsxs("div",{style:j.theadRow,children:[bt(Y("audit.colTime","时间")),bt(Y("audit.colType","类型")),bt(Y("audit.colSource","来源")),bt(Y("audit.colOp","操作")),bt(Y("audit.colDetail","详情"))]}),U===null?s.jsx("div",{style:j.muted,children:Y("audit.loading","加载中…")}):Tt.length===0?s.jsx("div",{style:j.muted,children:it.length===0?Y("audit.empty","暂无审计记录"):Y("audit.noMatch","无匹配记录")}):Tt.map((M,yt)=>{const Qt=k(M);return s.jsxs("div",{style:j.row,"data-id":"audit-row",children:[s.jsx("span",{style:j.time,children:L(M.ts)}),s.jsx("span",{style:j.badge(Qt),children:Qt.text}),s.jsx("span",{style:j.cell,children:M.origin||M.host||"—"}),s.jsx("span",{style:j.cell,children:ct(M)||"—"}),s.jsx("span",{style:j.detail,children:st(M)||"—"})]},yt)})]})]})}),document.body)}function Vy({me:o,welcome:U,onLogout:E,mitmTeam:m}){const O=(o==null?void 0:o.display_name)||(o==null?void 0:o.username)||"…",B=O.slice(0,1).toUpperCase(),[P,at]=N.useState(!1),[z,b]=N.useState(!1),[X,w]=N.useState(!1),lt=N.useRef(null);N.useEffect(()=>{if(!P)return;const Q=L=>{lt.current&&!lt.current.contains(L.target)&&at(!1)};return document.addEventListener("mousedown",Q),()=>document.removeEventListener("mousedown",Q)},[P]);const K=Q=>{hi(Q),at(!1)};return s.jsxs("header",{className:"topbar",children:[s.jsxs("div",{className:"brand-mini",children:[s.jsx("div",{className:"brand-mark sm",children:s.jsx(sm,{})}),s.jsx("span",{className:"brand-name",children:"CiCy Desktop"})]}),s.jsxs("div",{className:"user-chip","data-id":"UserChip",ref:lt,children:[U&&s.jsx("span",{className:"welcome",children:U}),s.jsxs("button",{type:"button","data-id":"UserChip-trigger",className:`user-chip__trigger${P?" is-open":""}`,onClick:()=>at(Q=>!Q),children:[s.jsx("div",{className:"avatar",children:B}),s.jsx("span",{className:"user-name",children:O}),s.jsx("span",{className:"user-chip__caret","aria-hidden":!0,children:"▾"})]}),P&&s.jsxs("div",{className:"user-chip__menu","data-id":"UserChip-menu",role:"menu",children:[s.jsx("button",{type:"button","data-id":"UserChip-wallet",className:"user-chip__menu-item",onClick:()=>K("?view=wallet"),children:"我的钱包"}),s.jsx("button",{type:"button","data-id":"UserChip-billing",className:"user-chip__menu-item",onClick:()=>K("?view=usage"),children:"我的账单"}),s.jsx("button",{type:"button","data-id":"UserChip-trusted-sites",className:"user-chip__menu-item",onClick:()=>{at(!1),b(!0)},children:Y("trustedSites.menu","受信任站点")}),s.jsx("button",{type:"button","data-id":"UserChip-audit-log",className:"user-chip__menu-item",onClick:()=>{at(!1),w(!0)},children:Y("audit.menu","审计日志")}),m&&s.jsx("div",{className:"user-chip__menu-mitm","data-id":"UserChip-mitm",onClick:Q=>Q.stopPropagation(),children:s.jsx(ky,{team:m,variant:"menu"})}),s.jsx("div",{className:"user-chip__menu-sep","aria-hidden":!0}),s.jsx("button",{type:"button","data-id":"UserChip-logout",className:"user-chip__menu-item is-danger",onClick:()=>{at(!1),E()},children:"退出"})]})]}),z&&s.jsx(Xy,{onClose:()=>b(!1)}),X&&s.jsx(Zy,{onClose:()=>w(!1)})]})}function Ky({onAgree:o}){var Q;const[U,E]=N.useState(!1),[m,O]=N.useState(!1),[B,P]=N.useState(!1),at=(((Q=window.cicyI18n)==null?void 0:Q.locale)||"en").startsWith("zh")?"zh-CN":"en",z=(L,k)=>Y(`firstRunTerms.${L}`,k),b=[1,2,3,4,5,6].map(L=>z(`summary${L}`,"")),X=L=>{const k=L.currentTarget;k.scrollHeight-k.scrollTop-k.clientHeight<24&&E(!0)},w=N.useRef(null);N.useEffect(()=>{const L=w.current;L&&L.scrollHeight<=L.clientHeight+24&&E(!0)},[m]);const lt=async()=>{var L,k,ct;if(!(B||!U)){P(!0);try{await((ct=(k=(L=window.cicy)==null?void 0:L.terms)==null?void 0:k.agree)==null?void 0:ct.call(k,cm)),o==null||o()}catch{o==null||o()}}},K=()=>{var L,k,ct;try{(ct=(k=(L=window.cicy)==null?void 0:L.terms)==null?void 0:k.decline)==null||ct.call(k)}catch{}};return s.jsxs("div",{className:"shell terms-gate","data-id":"FirstRunTermsGate",children:[s.jsx("div",{className:"glow","aria-hidden":!0}),s.jsxs("div",{className:"terms-gate__panel",children:[s.jsx("h1",{className:"terms-gate__title","data-id":"FirstRunTermsGate-title",children:z("title","用户协议与授权说明")}),s.jsx("p",{className:"terms-gate__subtitle",children:z("subtitle","使用 CiCy Desktop 前,请阅读并同意以下条款")}),s.jsxs("div",{className:"terms-gate__body",ref:w,onScroll:X,"data-id":"FirstRunTermsGate-body",children:[s.jsx("h2",{className:"terms-gate__h2",children:z("summaryTitle","一眼看懂")}),s.jsx("ol",{className:"terms-gate__summary",children:b.filter(Boolean).map((L,k)=>s.jsx("li",{children:L},k))}),m?s.jsx("pre",{className:"terms-gate__fulltext","data-id":"FirstRunTermsGate-fulltext",children:Wd[at]||Wd.en}):s.jsx("button",{className:"terms-gate__viewfull","data-id":"FirstRunTermsGate-viewfull",onClick:()=>O(!0),children:z("viewFull","查看完整条款")})]}),!U&&s.jsx("div",{className:"terms-gate__scrollhint","data-id":"FirstRunTermsGate-scrollhint",children:z("scrollHint","请阅读至底部以继续")}),s.jsxs("div",{className:"terms-gate__actions",children:[s.jsx("button",{"data-id":"FirstRunTermsGate-decline",className:"terms-gate__btn terms-gate__btn--ghost",onClick:K,children:z("decline","不同意并退出")}),s.jsx("button",{"data-id":"FirstRunTermsGate-agree",className:"terms-gate__btn",disabled:!U||B,title:U?"":z("mustAgree","未同意则无法使用本软件。"),onClick:lt,children:z("agree","同意并继续")})]})]})]})}function ky({team:o,variant:U}){const[E,m]=N.useState(void 0),[O,B]=N.useState(""),[P,at]=N.useState(""),z=((o==null?void 0:o.base_url)||"").replace(/\/$/,""),b=(o==null?void 0:o.api_token)||"",X=N.useCallback(async(ct,st={})=>{var Tt,W;if(!((W=(Tt=window.cicy)==null?void 0:Tt.cloud)!=null&&W.fetch))throw new Error("bridge missing");const it=await window.cicy.cloud.fetch(`${z}${ct}`,{...st,headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json",...st.headers||{}}});let ht=null;try{ht=JSON.parse(it.body)}catch{}return{ok:it.ok,status:it.status,json:ht}},[z,b]),w=N.useCallback(async()=>{try{const ct=await X("/api/mitm/ca-status");m(ct.ok&&ct.json?ct.json:null)}catch{m(null)}},[X]);if(N.useEffect(()=>{z&&b&&w()},[z,b,w]),E===void 0||!E||!E.generated)return null;const lt=async()=>{var ct,st,it,ht,Tt,W,j,bt,M;if(!O){B("enable"),at("");try{const yt=await X("/api/mitm/consent",{method:"POST",body:JSON.stringify({enable:!0})}),Qt=`${((ct=yt.json)==null?void 0:ct.error)||""} ${((st=yt.json)==null?void 0:st.detail)||""}`,Xt=((it=yt.json)==null?void 0:it.error)==="need_elevation"||!yt.ok&&yt.status===403||/need_elevation|add-trusted-cert|write permission|SecCertificate|not permitted|requires admin|administrator/i.test(Qt);if(yt.ok&&((ht=yt.json)!=null&&ht.ok)&&((Tt=yt.json)!=null&&Tt.trusted))await w();else if(Xt){const Zt=await((bt=(j=(W=window.cicy)==null?void 0:W.mitm)==null?void 0:j.caExec)==null?void 0:bt.call(j,"install"));Zt!=null&&Zt.ok?await w():at(/cancel/i.test((Zt==null?void 0:Zt.stderr)||"")?Y("mitmConsent.errorAdminDenied","未获得管理员授权,已取消。"):(Zt==null?void 0:Zt.stderr)||Y("mitmConsent.errorTitle","提权失败,请从管理员控制台运行"))}else at(((M=yt.json)==null?void 0:M.error)||`失败 (HTTP ${yt.status})`)}catch(yt){at(String((yt==null?void 0:yt.message)||yt))}finally{B("")}}},K=async()=>{var ct,st,it,ht,Tt;if(!O){B("disable"),at("");try{const W=await X("/api/mitm/consent",{method:"POST",body:JSON.stringify({enable:!1})});if(W.ok&&((ct=W.json)!=null&&ct.ok))await w();else{const j=await((ht=(it=(st=window.cicy)==null?void 0:st.mitm)==null?void 0:it.caExec)==null?void 0:ht.call(it,"uninstall"));j!=null&&j.ok?await w():at((j==null?void 0:j.stderr)||((Tt=W.json)==null?void 0:Tt.error)||"撤销失败")}}catch(W){at(String((W==null?void 0:W.message)||W))}finally{B("")}}},Q=E.consent&&E.trusted,L=E.consent&&!E.trusted,k=(ct,st)=>Y(`mitmConsent.${ct}`,st);if(U==="menu"){const ct=st=>{var it;(it=st==null?void 0:st.stopPropagation)==null||it.call(st),!O&&(Q?window.confirm(k("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&K():lt())};return s.jsxs("div",{className:"user-chip__mitm","data-id":"MitmConsentCard",children:[s.jsxs("div",{className:"user-chip__menu-item user-chip__mitm-row",title:k("scopeNote","仅解密 AI 厂商域名,数据留本地,随时可关闭。"),children:[s.jsx("span",{className:"user-chip__mitm-label",children:k("rowLabel","HTTPS 审计")}),s.jsx("button",{type:"button",role:"switch","aria-checked":Q?"true":"false","data-id":"MitmConsentCard-toggle",className:`mini-switch${Q?" is-on":""}${O?" is-busy":""}`,disabled:!!O,onClick:ct,children:s.jsx("span",{className:"mini-switch__knob"})})]}),L&&!O&&s.jsx("div",{className:"user-chip__mitm-note","data-id":"MitmConsentCard-note",children:k("partialNote","已同意但未安装,点开关重试")}),P&&s.jsx("div",{className:"user-chip__mitm-err","data-id":"MitmConsentCard-error",children:P})]})}return Q||O==="disable"?Kn.createPortal(s.jsxs("div",{"data-id":"MitmConsentCard",className:"mitm-pill",title:k("grantedDesc","HTTPS 审计已开启,仅对 CiCy 启动的 AI 工具生效;随时可关闭。"),children:[s.jsx("span",{className:"mitm-pill__dot","data-busy":O?"1":"0"}),s.jsx("span",{className:"mitm-pill__text","data-id":"MitmConsentCard-title",children:O==="disable"?k("processingRevoke","正在关闭…"):k("statePillOn","HTTPS 审计已开启")}),!O&&s.jsx("button",{type:"button","data-id":"MitmConsentCard-revoke",className:"mitm-pill__off",onClick:()=>{window.confirm(k("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&K()},children:k("turnOff","关闭")})]}),document.body):s.jsxs("div",{"data-id":"MitmConsentCard",className:`mitm-card${Q?" mitm-card--on":""}`,children:[s.jsxs("div",{className:"mitm-card__head",children:[s.jsx("span",{className:"mitm-card__dot","data-state":Q?"on":L?"warn":"off"}),s.jsx("span",{className:"mitm-card__title","data-id":"MitmConsentCard-title",children:O?k("stateProcessingTitle","处理中…"):Q?k("stateGrantedTitle","已启用"):`${k("cardTitle","HTTPS 流量本地审计")}${L?" — "+k("retry","重试"):""}`})]}),s.jsxs("p",{className:"mitm-card__desc","data-id":"MitmConsentCard-desc",children:[Q?k("grantedDesc","HTTPS 审计已开启,仅对 CiCy 启动的 AI 工具(claude / codex 等)生效;随时可关闭。"):k("body","启用后,CiCy 启动的 AI 工具(claude / codex 等)访问 AI 厂商(Claude / OpenAI / DeepSeek / Gemini)的 HTTPS 流量将被本地审计解密,数据留本地,随时可关闭。"),!Q&&s.jsxs(s.Fragment,{children:[s.jsx("br",{}),s.jsx("span",{className:"mitm-card__note",children:k("adminNote","通过环境变量对 CiCy 启动的 AI 工具生效,不修改系统、无需管理员授权。")}),s.jsx("br",{}),s.jsx("span",{className:"mitm-card__sub",children:k("scopeNote","仅解密上述 AI 厂商域名,其余一切流量不被解密、不被读取。")})]})]}),P&&s.jsxs("div",{className:"mitm-card__error","data-id":"MitmConsentCard-error",children:[k("errorTitle","操作失败"),": ",P]}),s.jsx("div",{className:"mitm-card__actions",children:Q?s.jsx("button",{"data-id":"MitmConsentCard-revoke",className:"mitm-card__btn mitm-card__btn--ghost",disabled:!!O,onClick:()=>{window.confirm(k("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&K()},children:O==="disable"?k("processingRevoke","正在关闭…"):k("revoke","撤销")}):s.jsx("button",{"data-id":"MitmConsentCard-enable",className:"mitm-card__btn",disabled:!!O,onClick:lt,children:O==="enable"?k("processingEnable","正在启用…"):L?k("retry","重试"):k("enable","同意并启用")})})]})}function Pd({team:o,onOpen:U,onRename:E,onRefresh:m}){var Xl,D,F,G;const B=(em[o.status]||em.error).tone,[P,at]=N.useState(!1),[z,b]=N.useState(o.name||""),[X,w]=N.useState(null),[lt,K]=N.useState(""),Q=X??o.name;N.useEffect(()=>{X!=null&&o.name===X&&w(null)},[o.name,X]);const L=C=>{var q;(q=C==null?void 0:C.stopPropagation)==null||q.call(C),b(Q||""),at(!0)},k=async()=>{at(!1);const C=String(z||"").trim();if(!E||!C||C===Q)return;w(C),K("saving");let q;try{q=await E(o.id,C)}catch($){q={ok:!1,error:$==null?void 0:$.message}}w(null),q&&q.ok?(K("saved"),setTimeout(()=>K($=>$==="saved"?"":$),1500)):(K(""),je.show({message:Y("localTeams.renameFailed","改名没保存,已恢复"),status:"error",ttl:4e3}))},st=!!((D=(Xl=window.cicy)==null?void 0:Xl.sidecar)!=null&&D.restart)&&Bs(o.base_url),it=o.status==="running",[ht,Tt]=N.useState(""),[W,j]=N.useState(!1),[bt,M]=N.useState({running:void 0,latest:null,installed:null}),yt=bt.latest,Qt=bt.running,[Xt,Zt]=N.useState(!1),pe=N.useRef(null),Yt=N.useRef(null),S=N.useRef(null),[R,tt]=N.useState({top:0,left:0}),xt=184,gt=()=>{if(!W&&Yt.current){const C=Yt.current.getBoundingClientRect(),q=Math.max(8,Math.min(C.right-xt,window.innerWidth-xt-8));tt({top:Math.round(C.bottom+4),left:Math.round(q)})}j(C=>!C)},r=N.useCallback(async(C=!1)=>{var q,$;if(!(!st||!(($=(q=window.cicy)==null?void 0:q.sidecar)!=null&&$.versions))){C&&Zt(!0);try{const et=await window.cicy.sidecar.versions();M({running:(et==null?void 0:et.running)??null,latest:(et==null?void 0:et.latest)??null,installed:(et==null?void 0:et.installed)??null}),C&&(et!=null&&et.running&&(et!=null&&et.latest)&&tm(et.latest,et.running)>0?je.show({message:`${Y("sidecar.found","发现新版本")} v${et.latest}`,status:"done",ttl:2500}):et!=null&&et.running?je.show({message:`${Y("sidecar.upToDate","已是最新")} v${et.running}`,status:"done",ttl:2500}):je.show({message:Y("sidecar.notRunning","cicy-code 未运行"),status:"error",ttl:4e3}))}catch{C&&je.show({message:Y("sidecar.checkFailed","检查更新失败"),status:"error",ttl:5e3})}finally{C&&Zt(!1)}}},[st]);N.useEffect(()=>{r(!1)},[r]),N.useEffect(()=>{it&&r(!1)},[it,r]);const x=!!(st&&yt&&Qt&&tm(yt,Qt)>0),H=!st&&!!((G=(F=window.cicy)==null?void 0:F.localTeams)!=null&&G.remove),Z=st||H,[nt,ft]=N.useState(!1);N.useEffect(()=>{if(!W)return;const C=q=>{var $,et;($=Yt.current)!=null&&$.contains(q.target)||(et=S.current)!=null&&et.contains(q.target)||j(!1)};return document.addEventListener("mousedown",C),()=>document.removeEventListener("mousedown",C)},[W]),N.useEffect(()=>{W||ft(!1)},[W]);const At=async()=>{var C,q,$;if(!nt){ft(!0);return}if(j(!1),ft(!1),!ht){Tt("remove");try{await(($=(q=(C=window.cicy)==null?void 0:C.localTeams)==null?void 0:q.remove)==null?void 0:$.call(q,o.id))}catch{}Tt(""),m==null||m()}},Ht=`sidecar-op:${o.id}`,Nt=async(C,q,$)=>{var Gt,Oe;if(j(!1),ht)return;Tt(C);const et=C==="update";let Wt=null;et?(hl.open({teamId:o.id,fromVer:Qt,toVer:yt,onRetry:()=>Nt("update",q,$)}),(Oe=(Gt=window.cicy)==null?void 0:Gt.sidecar)!=null&&Oe.onOpProgress&&(Wt=window.cicy.sidecar.onOpProgress(rt=>{(rt==null?void 0:rt.op)==="update"&&hl.push(rt)}))):je.show({id:Ht,message:Ne[C]||`${C}…`,status:"running",progress:void 0});try{const rt=await q(),Ve=!!(rt!=null&&rt.ok),ka=rt!=null&&rt.warning?`${$}(${rt.warning})`:$,Ja=Y("sidecar.failed","操作失败")+(rt!=null&&rt.error?`: ${rt.error}`:"");et?hl.finish({ok:Ve,message:Ve?ka:Ja}):je.show({id:Ht,message:Ve?ka:Ja,progress:void 0,status:Ve?"done":"error",ttl:Ve?4e3:8e3})}catch(rt){const Ve=Y("sidecar.failed","操作失败")+`: ${(rt==null?void 0:rt.message)||rt}`;et?hl.finish({ok:!1,message:Ve}):je.show({id:Ht,message:Ve,progress:void 0,status:"error",ttl:8e3})}finally{try{Wt&&Wt()}catch{}Tt(""),m==null||m(),(C==="update"||C==="restart"||C==="start")&&r(!1)}},Ne={start:"启动中…",restart:"重启中…",update:"更新中…",stop:"停止中…"},Ze=async()=>{var C,q;if(!ht){if(!it&&st&&((q=(C=window.cicy)==null?void 0:C.sidecar)!=null&&q.start)){Tt("start"),je.show({id:Ht,message:Ne.start,status:"running",progress:void 0});const $=await window.cicy.sidecar.start().catch(et=>({ok:!1,error:(et==null?void 0:et.message)||String(et)}));if(Tt(""),m==null||m(),!($!=null&&$.ok)||$!=null&&$.warning){je.show({id:Ht,message:Y("sidecar.startFailed","启动失败")+($!=null&&$.error?`: ${$.error}`:$!=null&&$.warning?`: ${$.warning}`:""),status:"error",ttl:8e3});return}je.dismiss(Ht)}U()}},gl=it?Y("localTeams.open","打开"):st?Y("localTeams.startOpen","启动并打开"):Y("localTeams.open","打开");return s.jsxs("div",{"data-id":"LocalTeamCard",className:`bcard ${st?"bcard--local":"bcard--custom"}${B==="ok"?" bcard--online":""}`,children:[s.jsx("div",{className:"bcard__accent"}),s.jsxs("div",{className:"bcard__top",children:[s.jsxs("div",{className:"bcard__pill",children:[s.jsx("span",{className:"bcard__dot","data-tone":B}),s.jsx(fm,{})]}),Z&&s.jsxs("div",{className:"bcard__menuwrap",ref:pe,onClick:C=>C.stopPropagation(),children:[s.jsx("button",{type:"button",ref:Yt,"data-id":"LocalTeamCard-menu-btn",className:`bcard__kebab${x?" has-dot":""}`,title:st?Y("localTeams.manage","管理本地 cicy-code"):Y("localTeams.more","更多"),disabled:!!ht,onClick:gt,children:ht?s.jsx(yl,{}):s.jsx(om,{})}),W&&Kn.createPortal(s.jsxs("div",{className:"bcard__menu bcard__menu--portal","data-id":"LocalTeamCard-menu",role:"menu",ref:S,style:{position:"fixed",top:R.top,left:R.left,width:xt},onClick:C=>C.stopPropagation(),children:[x&&s.jsxs("button",{type:"button","data-id":"LocalTeamCard-update",className:"bcard__menu-item is-accent",onClick:()=>Nt("update",()=>window.cicy.sidecar.update(),Y("sidecar.updated","已更新到最新")),children:[Y("sidecar.updateTo","更新到")," v",yt]}),st&&it&&s.jsxs(s.Fragment,{children:[s.jsx("button",{type:"button","data-id":"LocalTeamCard-reload",className:"bcard__menu-item",onClick:()=>Nt("reload",async()=>{const C=await window.cicy.localTeams.reload(o.id);return!(C!=null&&C.ok)&&(C==null?void 0:C.error)==="no_open_window"?window.cicy.localTeams.open(o.id):C},Y("localTeams.reloaded","已刷新窗口")),children:Y("localTeams.reloadWindow","刷新窗口")}),s.jsx("button",{type:"button","data-id":"LocalTeamCard-restart",className:"bcard__menu-item",onClick:()=>Nt("restart",()=>window.cicy.sidecar.restart(),Y("sidecar.restarted","已重启")),children:Y("sidecar.restart","重启")}),s.jsx("button",{type:"button","data-id":"LocalTeamCard-stop",className:"bcard__menu-item is-danger",onClick:()=>Nt("stop",()=>window.cicy.sidecar.stop(),Y("sidecar.stoppedDone","已停止")),children:Y("sidecar.stop","停止")})]}),st&&s.jsx("button",{type:"button","data-id":"LocalTeamCard-check-update",className:"bcard__menu-item",disabled:Xt,onClick:C=>{C.stopPropagation(),r(!0)},children:Xt?Y("sidecar.checking2","检查中…"):Y("sidecar.checkUpdate","检查更新")}),o.cloud_team_id&&s.jsx("button",{type:"button","data-id":"LocalTeamCard-billing",className:"bcard__menu-item",onClick:C=>{C.stopPropagation(),j(!1),hi(`?team=${encodeURIComponent(o.cloud_team_id)}`)},children:Y("localTeams.billing","账单")}),H&&s.jsx("button",{type:"button","data-id":"LocalTeamCard-remove",className:"bcard__menu-item is-danger",onClick:At,children:nt?Y("localTeams.removeConfirm","确认删除?"):Y("localTeams.remove","删除")})]}),document.body)]})]}),s.jsxs("div",{className:"bcard__body",children:[P?s.jsx("input",{"data-id":"LocalTeamCard-rename-input",autoFocus:!0,value:z,onChange:C=>b(C.target.value),onFocus:C=>C.target.select(),onBlur:k,onClick:C=>C.stopPropagation(),onKeyDown:C=>{C.key==="Enter"?k():C.key==="Escape"&&at(!1)},style:{width:"100%",font:"inherit",fontWeight:600,padding:"2px 6px",border:"1px solid #3b82f6",borderRadius:6,background:"#0d1117",color:"#e6edf3",boxSizing:"border-box"}}):s.jsxs("h3",{className:"bcard__name",title:Y("localTeams.renameHint","点名字或 ✎ 改名"),style:{display:"flex",alignItems:"center",gap:6},onDoubleClick:L,children:[s.jsx("span",{"data-id":"LocalTeamCard-name-text",onClick:L,style:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",cursor:"text"},children:Q}),lt==="saving"&&s.jsx("span",{"data-id":"LocalTeamCard-save-state",title:Y("localTeams.saving","保存中…"),style:{flex:"none",display:"inline-flex"},children:s.jsx(yl,{})}),lt==="saved"&&s.jsx("span",{"data-id":"LocalTeamCard-save-state",title:Y("localTeams.saved","已保存"),style:{flex:"none",color:"#3fb950",fontSize:13,lineHeight:1},children:"✓"}),!lt&&s.jsx("button",{type:"button","data-id":"LocalTeamCard-rename-btn",title:Y("localTeams.rename","重命名"),onClick:L,style:{flex:"none",cursor:"pointer",border:"none",background:"transparent",color:"#8b949e",fontSize:13,padding:0,lineHeight:1},children:"✎"})]}),s.jsx("div",{className:"bcard__host",children:o.base_url||"—"}),s.jsx("div",{className:"bcard__meta",children:(Qt||o.version)&&s.jsxs("span",{className:"bcard__ver","data-id":"LocalTeamCard-version",children:["v",Qt||o.version]})})]}),s.jsxs("button",{type:"button",className:"bcard__cta","data-id":"LocalTeamCard-open",disabled:!!ht||!o.base_url,onClick:Ze,children:[ht&&ht!=="stop"?s.jsx(yl,{}):s.jsx(Ys,{}),s.jsx("span",{children:ht&&Ne[ht]||gl})]})]})}function Bs(o){try{const U=new URL(o);return(U.hostname==="127.0.0.1"||U.hostname==="localhost"||U.hostname==="::1")&&(U.port==="8008"||U.port==="")}catch{return!1}}function tm(o,U){const E=String(o).split("."),m=String(U).split(".");for(let O=0;O<Math.max(E.length,m.length);O++){const B=(parseInt(E[O],10)||0)-(parseInt(m[O],10)||0);if(B)return B>0?1:-1}return 0}const em={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 Jy({team:o,onOpen:U}){const E=o.kind==="private",m=o.status==="active",O=o.name||o.title||"—",B=o.host_url||"",P=o.teamId||o.id,at=E?"私有云":o.team_kind==="personal"?"个人":"共享",z=E?B:o.workspace_url||o.workspace_direct_url,b=!!z,[X,w]=N.useState(!1),[lt,K]=N.useState(!1),[Q,L]=N.useState({top:0,left:0}),k=N.useRef(null),ct=N.useRef(null),st=N.useRef(null),it=184,ht=()=>{if(!X&&ct.current){const W=ct.current.getBoundingClientRect(),j=Math.max(8,Math.min(W.right-it,window.innerWidth-it-8));L({top:Math.round(W.bottom+4),left:Math.round(j)})}w(W=>!W)};N.useEffect(()=>{if(!X)return;const W=j=>{var bt,M;(bt=ct.current)!=null&&bt.contains(j.target)||(M=st.current)!=null&&M.contains(j.target)||w(!1)};return document.addEventListener("mousedown",W),()=>document.removeEventListener("mousedown",W)},[X]);const Tt=async()=>{var W,j,bt;if(!(!b||lt)){K(!0),w(!1);try{await((bt=(j=(W=window.cicy)==null?void 0:W.tabs)==null?void 0:j.reload)==null?void 0:bt.call(j,z,O))}catch{}finally{K(!1)}}};return s.jsxs("div",{"data-id":"TeamCard",className:`bcard bcard--cloud${m?" bcard--online":""}`,children:[s.jsx("div",{className:"bcard__accent"}),s.jsxs("div",{className:"bcard__top",children:[s.jsxs("div",{className:"bcard__pill",children:[s.jsx("span",{className:"bcard__dot","data-tone":m?"ok":"off"}),s.jsx(Wy,{})]}),s.jsxs("div",{className:"bcard__top-right",children:[o.is_trial&&s.jsx("span",{className:"bcard__badge",children:"trial"}),P!=null&&s.jsx("button",{type:"button","data-id":"TeamCard-billing",className:"bcard__billing-btn",title:Y("localTeams.billing","账单"),onClick:W=>{W.stopPropagation(),hi(`?team=${encodeURIComponent(P)}`)},children:Y("localTeams.billing","账单")}),b&&s.jsxs("div",{className:"bcard__menuwrap",ref:k,onClick:W=>W.stopPropagation(),children:[s.jsx("button",{type:"button",ref:ct,"data-id":"TeamCard-menu-btn",className:"bcard__kebab",title:Y("localTeams.more","更多"),disabled:lt,onClick:ht,children:lt?s.jsx(yl,{}):s.jsx(om,{})}),X&&Kn.createPortal(s.jsx("div",{className:"bcard__menu bcard__menu--portal","data-id":"TeamCard-menu",role:"menu",ref:st,style:{position:"fixed",top:Q.top,left:Q.left,width:it},onClick:W=>W.stopPropagation(),children:s.jsx("button",{type:"button","data-id":"TeamCard-reload",className:"bcard__menu-item",onClick:Tt,children:Y("localTeams.reloadWindow","刷新窗口")})}),document.body)]})]})]}),s.jsxs("div",{className:"bcard__body",children:[s.jsx("h3",{className:"bcard__name",title:O,children:O}),s.jsx("div",{className:"bcard__host",title:E&&B||"",children:E?B||Y("teamCard.noHost","未填访问地址"):o.runtime_region||o.region||"—"}),s.jsxs("div",{className:"bcard__meta",children:[s.jsx("span",{className:"bcard__chip",children:at}),!E&&o.membership_status&&o.membership_status!=="active"&&s.jsx("span",{className:"bcard__chip",children:o.membership_status})]})]}),s.jsxs("button",{type:"button",className:"bcard__cta",onClick:U,disabled:!b,children:[s.jsx(Ys,{}),s.jsx("span",{children:b?Y("localTeams.open","打开"):E?Y("teamCard.noHost","未填访问地址"):Y("teamCard.noUrl","无 URL")})]})]})}function Us(){return s.jsxs("div",{className:"brand",children:[s.jsx("div",{className:"brand-mark",children:s.jsx(sm,{})}),s.jsxs("div",{className:"brand-text",children:[s.jsx("div",{className:"brand-name",children:"CiCy Desktop"}),s.jsx("div",{className:"brand-sub",children:"团队 AI 协作工作台"})]})]})}function sm(){return s.jsx("svg",{width:"22",height:"22",viewBox:"0 0 96 96",fill:"none",children:s.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 Ys(){return s.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("line",{x1:"5",y1:"12",x2:"19",y2:"12"}),s.jsx("polyline",{points:"12 5 19 12 12 19"})]})}function yl(){return s.jsx("svg",{className:"spin",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.4",strokeLinecap:"round",children:s.jsx("path",{d:"M21 12a9 9 0 1 1-6.2-8.55"})})}function fm(){return s.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("rect",{x:"3",y:"4",width:"18",height:"12",rx:"2"}),s.jsx("line",{x1:"2",y1:"20",x2:"22",y2:"20"})]})}function om(){return s.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:[s.jsx("circle",{cx:"12",cy:"5",r:"1.7"}),s.jsx("circle",{cx:"12",cy:"12",r:"1.7"}),s.jsx("circle",{cx:"12",cy:"19",r:"1.7"})]})}function Wy(){return s.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("circle",{cx:"12",cy:"12",r:"10"}),s.jsx("line",{x1:"2",y1:"12",x2:"22",y2:"12"}),s.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 Vn(o){try{return localStorage.getItem(o)||null}catch{return null}}function lm(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 am;const $y=((am=window.cicy)==null?void 0:am.platform)||(()=>{const o=navigator.userAgent||"";return/Mac/i.test(o)?"darwin":/Windows/i.test(o)?"win32":"linux"})();document.documentElement.dataset.platform=$y;document.documentElement.dataset.fullscreen="0";var nm,um;(um=(nm=window.cicy)==null?void 0:nm.window)!=null&&um.onFullscreen&&window.cicy.window.onFullscreen(o=>{document.documentElement.dataset.fullscreen=o?"1":"0"});qy.createRoot(document.getElementById("root")).render(s.jsx(Qy,{}));
|
|
365
|
+
`},Y=(o,U)=>{var E,m;try{const O=(m=(E=window.cicyI18n)==null?void 0:E.t)==null?void 0:m.call(E,o);return O&&O!==o?O:U}catch{return U}},Ka="cicy_token",oi="cicy_access_token",ri="cicy_user_id",mi="https://cicy-ai.com";async function hi(o){var U,E,m;try{(m=(E=(U=window.cicy)==null?void 0:U.shell)==null?void 0:E.openExternal)==null||m.call(E,`${mi}/dash${o}`)}catch{}}const Rs=new Set;let Yy=0,Ql=[];const Zn=new Map;function $d(){Rs.forEach(o=>o(Ql))}const je={show(o={}){const U=o.id||`t${++Yy}`,E=Ql.find(B=>B.id===U),m={id:U,status:"running",...E,...o};Ql=E?Ql.map(B=>B.id===U?m:B):[...Ql,m],$d();const O=Zn.get(U);return O&&(clearTimeout(O),Zn.delete(U)),o.ttl&&Zn.set(U,setTimeout(()=>je.dismiss(U),o.ttl)),U},dismiss(o){Ql=Ql.filter(E=>E.id!==o);const U=Zn.get(o);U&&(clearTimeout(U),Zn.delete(o)),$d()}};function Ly(){const[o,U]=N.useState(Ql);return N.useEffect(()=>(Rs.add(U),()=>{Rs.delete(U)}),[]),o.length?s.jsx("div",{className:"toast-host","data-id":"ToastHost",children:o.map(E=>s.jsxs("div",{className:"toast","data-id":`Toast-${E.id}`,"data-status":E.status||"running",children:[s.jsx("button",{type:"button",className:"toast__x","data-id":"Toast-dismiss",onClick:()=>je.dismiss(E.id),"aria-label":"dismiss",children:"×"}),s.jsxs("span",{className:"toast__msg",children:[E.message,Number.isFinite(E.progress)?` ${E.progress}%`:""]}),Number.isFinite(E.progress)&&s.jsx("span",{className:"toast__bar",children:s.jsx("span",{style:{width:`${Math.min(100,E.progress)}%`}})})]},E.id))}):null}const Hs=new Set;let Fd=0,oe=null;function di(){Hs.forEach(o=>o(oe))}function Id(){return new Date().toTimeString().slice(0,8)}const hl={open({teamId:o,fromVer:U,toVer:E,onRetry:m}={}){oe={teamId:o,fromVer:U||null,toVer:E||null,status:"running",phase:"download",logs:[],onRetry:m||null,lastAt:Date.now()},di()},push(o={}){if(!oe)return;const U={id:++Fd,t:Id(),phase:o.phase||oe.phase,status:o.status||"running",message:o.message||""};oe={...oe,phase:o.phase||oe.phase,toVer:o.toVer||oe.toVer,logs:[...oe.logs,U],lastAt:Date.now()},di()},finish({ok:o,message:U}={}){if(!oe)return;const E=o?"done":"error",m={id:++Fd,t:Id(),phase:"done",status:E,message:U||(o?"更新完成":"更新失败")};oe={...oe,status:E,phase:"done",logs:[...oe.logs,m],lastAt:Date.now()},di()},close(){oe=null,di()}},ws=[["download","下载"],["swap","切换"],["done","完成"]];function Gy(){var at;const[o,U]=N.useState(oe);N.useEffect(()=>(Hs.add(U),()=>{Hs.delete(U)}),[]);const E=N.useRef(null);N.useEffect(()=>{const z=E.current;z&&(z.scrollTop=z.scrollHeight)},[(at=o==null?void 0:o.logs)==null?void 0:at.length]);const[m,O]=N.useState(!1);if(N.useEffect(()=>{if(!o||o.status!=="running"){O(!1);return}const z=setInterval(()=>O(Date.now()-(o.lastAt||0)>25e3),3e3);return()=>clearInterval(z)},[o==null?void 0:o.lastAt,o==null?void 0:o.status]),!o)return null;const B=o.status==="running",P=ws.findIndex(([z])=>z===o.phase);return s.jsx("div",{className:"drawer-scrim","data-id":"UpdateDrawer-scrim",onClick:()=>{B||hl.close()},children:s.jsxs("div",{className:"drawer","data-id":"UpdateDrawer","data-status":o.status,onClick:z=>z.stopPropagation(),children:[s.jsxs("div",{className:"drawer__head",children:[s.jsxs("div",{className:"drawer__title",children:[s.jsx("span",{className:`drawer__spark drawer__spark--${o.status}`,children:B?s.jsx(yl,{}):o.status==="done"?"✓":"!"}),s.jsxs("div",{children:[s.jsx("div",{className:"drawer__h",children:"更新 cicy-code"}),s.jsxs("div",{className:"drawer__sub",children:[o.fromVer?`v${o.fromVer}`:"当前"," → ",o.toVer?`v${o.toVer}`:"最新版"]})]})]}),s.jsx("button",{type:"button",className:"drawer__x","data-id":"UpdateDrawer-close",disabled:B,title:B?"更新进行中":"关闭",onClick:()=>hl.close(),"aria-label":"close",children:"×"})]}),s.jsx("div",{className:"drawer__steps","data-id":"UpdateDrawer-steps",children:ws.map(([z,b],X)=>{const w=o.status==="done"||X<P,lt=X===P&&B,K=o.status==="error"&&X===P;return s.jsxs("div",{className:`drawer__step${lt?" is-active":""}${w?" is-done":""}${K?" is-error":""}`,children:[s.jsx("span",{className:"drawer__step-dot",children:w?"✓":K?"!":X+1}),s.jsx("span",{className:"drawer__step-label",children:b}),X<ws.length-1&&s.jsx("span",{className:"drawer__step-bar"})]},z)})}),s.jsx("div",{className:"drawer__log","data-id":"UpdateDrawer-log",ref:E,children:o.logs.length===0?s.jsx("div",{className:"drawer__log-empty",children:"准备中…"}):o.logs.map(z=>s.jsxs("div",{className:"drawer__line","data-status":z.status,children:[s.jsx("span",{className:"drawer__t",children:z.t}),s.jsx("span",{className:`drawer__badge drawer__badge--${z.phase}`,children:{download:"下载",swap:"切换",done:"完成"}[z.phase]||z.phase}),s.jsx("span",{className:"drawer__linemsg",children:z.message})]},z.id))}),m&&B&&s.jsx("div",{className:"drawer__hint","data-id":"UpdateDrawer-stuck",children:"正在等待新版本就绪,耗时比平常久。可以放到后台继续,完成或失败都会提示。"}),s.jsx("div",{className:"drawer__foot",children:B?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"drawer__foot-status",children:"更新进行中…"}),s.jsx("button",{type:"button",className:"drawer__btn","data-id":"UpdateDrawer-background",onClick:()=>hl.close(),children:"在后台继续"})]}):o.status==="error"?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"drawer__foot-status is-error",children:"更新失败"}),o.onRetry&&s.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"UpdateDrawer-retry",onClick:()=>o.onRetry(),children:"重试"}),s.jsx("button",{type:"button",className:"drawer__btn","data-id":"UpdateDrawer-dismiss",onClick:()=>hl.close(),children:"关闭"})]}):s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"drawer__foot-status is-done",children:"已更新到最新"}),s.jsx("button",{type:"button",className:"drawer__btn is-accent","data-id":"UpdateDrawer-finish",onClick:()=>hl.close(),children:"完成"})]})})]})})}function Qy(){const[o,U]=N.useState(void 0);N.useEffect(()=>{var D,F;if(!((F=(D=window.cicy)==null?void 0:D.terms)!=null&&F.status)){U(!0);return}window.cicy.terms.status(cm).then(G=>U(!!(G!=null&&G.accepted))).catch(()=>U(!0))},[]),N.useEffect(()=>{var G,C,q,$;const D=document.documentElement;try{D.setAttribute("data-platform",((G=window.cicy)==null?void 0:G.platform)||"linux")}catch{}D.setAttribute("data-fullscreen","0");let F;try{F=($=(q=(C=window.cicy)==null?void 0:C.window)==null?void 0:q.onFullscreen)==null?void 0:$.call(q,et=>D.setAttribute("data-fullscreen",et?"1":"0"))}catch{}return()=>{try{F&&F()}catch{}}},[]);const[E,m]=N.useState(()=>Vn(Ka)),[O,B]=N.useState(()=>Vn(oi)),[P,at]=N.useState(()=>Vn(ri)),[z,b]=N.useState(()=>!Vn(Ka)),[X,w]=N.useState(!1),[lt,K]=N.useState(""),[Q,L]=N.useState(""),[k,ct]=N.useState(null),[st,it]=N.useState(null),[ht,Tt]=N.useState(!1),[W,j]=N.useState(""),bt=N.useRef(!1),[M,yt]=N.useState(null),[Qt,Xt]=N.useState(!1),[Zt,pe]=N.useState(!1),[Yt,S]=N.useState("all"),R=N.useCallback(async(D,F)=>{var C,q,$,et;if(!D)return;if(!((q=(C=window.cicy)==null?void 0:C.cloud)!=null&&q.fetch)){j("cloud fetch bridge missing");return}Tt(!0),j("");const G={Authorization:`Bearer ${D}`};try{const[Wt,Gt]=await Promise.all([window.cicy.cloud.fetch(`${mi}/api/user/self`,{headers:G}),window.cicy.cloud.fetch(`${mi}/api/teams`,{headers:G})]);if((Gt==null?void 0:Gt.status)===401){if(!bt.current&&((et=($=window.cicy)==null?void 0:$.auth)!=null&&et.loginStart)){bt.current=!0,j("会话已过期,正在重新登录…");try{await window.cicy.auth.loginStart()}catch{}}return}if(!(Gt!=null&&Gt.ok))throw new Error(`/api/teams ${(Gt==null?void 0:Gt.status)||"?"} ${(Gt==null?void 0:Gt.error)||""}`);const Oe=JSON.parse(Gt.body||"{}");if(it(Array.isArray(Oe==null?void 0:Oe.teams)?Oe.teams:[]),Wt!=null&&Wt.ok)try{const rt=JSON.parse(Wt.body||"{}");ct((rt==null?void 0:rt.success)===!1?null:(rt==null?void 0:rt.data)||null)}catch{ct(null)}else ct(null)}catch(Wt){j(Wt.message||String(Wt))}finally{Tt(!1)}},[]),tt=N.useRef("");N.useEffect(()=>{tt.current=E||O||""},[E,O]);const xt=N.useCallback(async()=>{var F,G;const D=tt.current;if(!(!D||!((G=(F=window.cicy)==null?void 0:F.cloud)!=null&&G.fetch)))try{const C=await window.cicy.cloud.fetch(`${mi}/api/teams`,{headers:{Authorization:`Bearer ${D}`}});if(C!=null&&C.ok){const q=JSON.parse(C.body||"{}");Array.isArray(q==null?void 0:q.teams)&&it(q.teams)}}catch{}},[]);N.useEffect(()=>{const D=E||O;D&&R(D,P)},[E,O,P,R]);const gt=N.useCallback(async()=>{var D,F;if((F=(D=window.cicy)==null?void 0:D.localTeams)!=null&&F.list){Xt(!0);try{const G=await window.cicy.localTeams.list({refresh:!0});yt(Array.isArray(G)?G:[])}catch{yt([])}finally{Xt(!1),pe(!0)}}},[]),r=N.useCallback(async(D,F)=>{var C,q;if(!((q=(C=window.cicy)==null?void 0:C.localTeams)!=null&&q.update))return{ok:!1,error:"no_bridge"};let G;try{G=await window.cicy.localTeams.update(D,{name:String(F||"").trim()||Y("localTeams.unnamed","未命名")})}catch($){G={ok:!1,error:($==null?void 0:$.message)||String($)}}return await gt(),G||{ok:!1,error:"no_result"}},[gt]);N.useEffect(()=>{let D,F=!1;const G=3e3,C=3e4,q=async()=>{var Wt,Gt,Oe;try{await((Oe=(Gt=(Wt=window.cicy)==null?void 0:Wt.localTeams)==null?void 0:Gt.syncCloud)==null?void 0:Oe.call(Gt))}catch{}await Promise.all([gt(),xt()])},$=()=>{if(F)return;const Wt=document.visibilityState==="visible";D=setTimeout(async()=>{document.visibilityState==="visible"?await q():await gt(),$()},Wt?G:C)};q(),$();const et=()=>{document.visibilityState==="visible"&&q()};return document.addEventListener("visibilitychange",et),window.addEventListener("focus",et),()=>{F=!0,clearTimeout(D),document.removeEventListener("visibilitychange",et),window.removeEventListener("focus",et)}},[gt,xt]),N.useEffect(()=>{var F,G;if(!((G=(F=window.cicy)==null?void 0:F.localTeams)!=null&&G.onWebviewRelay))return;const D=window.cicy.localTeams.onWebviewRelay(async({reqId:C,msg:q})=>{let $={ok:!1,error:"unknown relay type"};try{(q==null?void 0:q.type)==="localTeams:add"?$=await window.cicy.localTeams.add(q.spec||{}):(q==null?void 0:q.type)==="localTeams:remove"?$=await window.cicy.localTeams.remove(q.id):(q==null?void 0:q.type)==="localTeams:update"?$=await window.cicy.localTeams.update(q.id,q.patch||{}):(q==null?void 0:q.type)==="localTeams:upgrade"?$=await window.cicy.localTeams.upgrade(q.id):(q==null?void 0:q.type)==="localTeams:list"&&($={ok:!0,teams:await window.cicy.localTeams.list({refresh:!0})}),gt()}catch(et){$={ok:!1,error:(et==null?void 0:et.message)||String(et)}}try{window.cicy.localTeams.replyWebviewRelay(C,$)}catch{}});return()=>{try{D==null||D()}catch{}}},[gt]);const x=N.useCallback(async D=>{var F,G;if((G=(F=window.cicy)==null?void 0:F.localTeams)!=null&&G.open)try{await window.cicy.localTeams.open(D)}catch{}},[]);N.useEffect(()=>{var F,G;if(Vn(Ka)){b(!1);return}if(!((G=(F=window.cicy)==null?void 0:F.auth)!=null&&G.getSaved)){b(!1);return}let D=!1;return(async()=>{try{const C=await window.cicy.auth.getSaved();if(D)return;if(C!=null&&C.token){try{localStorage.setItem(Ka,C.token)}catch{}if(m(C.token),C.accessToken){try{localStorage.setItem(oi,C.accessToken)}catch{}B(C.accessToken)}if(C.userId){try{localStorage.setItem(ri,String(C.userId))}catch{}at(String(C.userId))}}}catch{}finally{D||b(!1)}})(),()=>{D=!0}},[]),N.useEffect(()=>{var D,F;if((F=(D=window.cicy)==null?void 0:D.auth)!=null&&F.onComplete)return window.cicy.auth.onComplete(G=>{if(w(!1),G!=null&&G.error){K(lm(G.error));return}if(G!=null&&G.token){bt.current=!1,j("");try{localStorage.setItem(Ka,G.token)}catch{}if(m(G.token),G.accessToken){try{localStorage.setItem(oi,G.accessToken)}catch{}B(G.accessToken)}if(G.userId){try{localStorage.setItem(ri,String(G.userId))}catch{}at(String(G.userId))}K(""),L(G.reused?"已恢复你之前的登录":"登录成功"),setTimeout(()=>L(""),3e3)}})},[]);async function H(){var F,G;if(!((G=(F=window.cicy)==null?void 0:F.auth)!=null&&G.loginStart)){K("auth bridge missing");return}K(""),w(!0);const D=await window.cicy.auth.loginStart();D!=null&&D.ok||(w(!1),K(lm((D==null?void 0:D.error)||"login start failed")))}function Z(){var D,F,G;try{localStorage.removeItem(Ka),localStorage.removeItem(oi),localStorage.removeItem(ri)}catch{}try{(G=(F=(D=window.cicy)==null?void 0:D.auth)==null?void 0:F.logout)==null||G.call(F)}catch{}m(null),B(null),at(null),ct(null),it(null),K(""),j("")}if(o===void 0)return s.jsxs("div",{className:"shell","data-id":"TermsCheckingSplash",children:[s.jsx("div",{className:"glow","aria-hidden":!0}),s.jsxs("div",{className:"card",children:[s.jsx(Us,{}),s.jsx("div",{className:"spinner-row",children:s.jsx(yl,{})})]})]});if(!o)return s.jsx(Ky,{onAgree:()=>U(!0)});if(!E&&z)return s.jsxs("div",{className:"shell","data-id":"AuthRestoringSplash",children:[s.jsx("div",{className:"glow","aria-hidden":!0}),s.jsxs("div",{className:"card",children:[s.jsx(Us,{}),s.jsxs("div",{className:"spinner-row",children:[s.jsx(yl,{}),s.jsx("span",{children:"正在恢复登录…"})]})]})]});if(!E)return s.jsxs("div",{className:"shell",children:[s.jsx("div",{className:"glow","aria-hidden":!0}),s.jsxs("div",{className:"card",children:[s.jsx(Us,{}),!X&&s.jsxs(s.Fragment,{children:[s.jsx("p",{className:"tagline",children:"登录以同步你的团队、配置与 AI 助手"}),s.jsxs("button",{className:"btn-primary",onClick:H,children:[s.jsx("span",{children:"使用浏览器登录"}),s.jsx(Ys,{})]}),s.jsx("p",{className:"hint",children:"点击后会自动打开浏览器"})]}),X&&s.jsxs(s.Fragment,{children:[s.jsx("p",{className:"tagline",children:"已在浏览器打开登录页,等待你完成…"}),s.jsxs("div",{className:"spinner-row",children:[s.jsx(yl,{}),s.jsx("span",{children:"等待回调"})]}),s.jsx("button",{className:"btn-ghost",onClick:()=>{var D,F,G;(G=(F=(D=window.cicy)==null?void 0:D.auth)==null?void 0:F.loginCancel)==null||G.call(F),w(!1)},children:"取消"})]}),lt&&s.jsx("div",{className:"error",children:lt})]})]});const nt=(M||[]).filter(D=>Bs(D.base_url)),ft=(M||[]).filter(D=>!Bs(D.base_url)),At=nt.length,Ht=ft.length,Nt=(st||[]).filter(D=>!D.is_local&&D.kind!=="local"),Ne=Nt.length,Ze=Yt==="all"||Yt==="local",gl=Yt==="all"||Yt==="custom",Xl=Yt==="all"||Yt==="cloud";return s.jsxs("div",{className:"shell shell--app",children:[s.jsx("div",{className:"glow glow--app","aria-hidden":!0}),s.jsxs("div",{className:"shell__left",children:[s.jsx(Vy,{me:k,welcome:Q,onLogout:Z,mitmTeam:nt.length>0?nt[0]:null}),s.jsxs("main",{className:"main",children:[s.jsxs("div",{className:"app__tabsrow",children:[s.jsx("div",{className:"app__tabs",children:[{k:"all",label:"全部",n:At+Ht+Ne},{k:"local",label:"本地",n:At},{k:"cloud",label:"私有云",n:Ne},{k:"custom",label:"自定义",n:Ht}].map(({k:D,label:F,n:G})=>s.jsxs("button",{type:"button",className:`app__tab ${Yt===D?"is-active":""}`,onClick:()=>S(D),children:[F,s.jsx("span",{className:"app__tab-count",children:G})]},D))}),s.jsxs("button",{type:"button","data-id":"AddTeamButton",className:"app__add-team",title:Y("teams.addHint","在云端新建私有云团队"),onClick:()=>hi("?tab=private"),children:["+ ",Y("teams.add","新加团队")]})]}),W&&s.jsxs("div",{className:"error",style:{marginBottom:12},children:["云端: ",W,s.jsx("button",{className:"btn-ghost",style:{marginLeft:8},onClick:()=>R(E||O,P),children:"重试"})]}),s.jsxs("div",{className:"app__grid",children:[Ze&&nt.map(D=>s.jsx(Pd,{team:D,onOpen:()=>x(D.id),onRename:r,onRefresh:gt},"local:"+D.id)),Ze&&nt.length===0&&s.jsxs("div",{"data-id":"LocalTeamPlaceholder",className:"bcard bcard--local",children:[s.jsx("div",{className:"bcard__accent"}),s.jsx("div",{className:"bcard__top",children:s.jsxs("div",{className:"bcard__pill",children:[s.jsx("span",{className:"bcard__dot","data-tone":"warn"}),s.jsx(fm,{})]})}),s.jsxs("div",{className:"bcard__body",children:[s.jsx("h3",{className:"bcard__name",children:"本地团队"}),s.jsx("div",{className:"bcard__host",children:"http://127.0.0.1:8008"}),s.jsx("div",{className:"bcard__meta"})]}),s.jsxs("button",{type:"button",className:"bcard__cta",disabled:!0,children:[s.jsx(yl,{}),s.jsx("span",{children:Zt?"正在启动,就绪后自动加入…":"检测中…"})]})]}),gl&&ft.map(D=>s.jsx(Pd,{team:D,onOpen:()=>x(D.id),onRename:r,onRefresh:gt},"custom:"+D.id)),Xl&&Nt.map(D=>s.jsx(Jy,{team:D,onOpen:()=>{var G,C,q;const F=D.kind==="private"?D.host_url:D.workspace_url||D.workspace_direct_url;F&&((q=(C=(G=window.cicy)==null?void 0:G.tabs)==null?void 0:C.open)==null||q.call(C,F,D.name||D.title||""))}},"cloud:"+D.id)),Ze&&s.jsxs("button",{type:"button",className:"add-card",onClick:()=>{alert("装本地 cicy-code(npx cicy-code / docker run)后会自动出现,或在云端创建团队。")},children:[s.jsx("span",{className:"add-card__plus",children:"+"}),s.jsx("span",{className:"add-card__label",children:"新建本地团队"})]})]}),!ht&&!W&&st&&st.length===0&&!(M!=null&&M.length)&&s.jsx("div",{className:"empty",style:{marginTop:14},children:"还没有团队 — 安装本地 cicy-code 起一个本地 team,或在云端创建。"})]})]}),s.jsx(Ly,{}),s.jsx(Gy,{})]})}function Xy({onClose:o}){const[U,E]=N.useState(null),[m,O]=N.useState(""),[B,P]=N.useState(!1),[at,z]=N.useState(""),b=typeof window<"u"&&window.cicy&&window.cicy.trustedOrigins||null,X=N.useCallback(async()=>{try{E(b&&await b.list()||[])}catch{E([])}},[b]);N.useEffect(()=>{X()},[X]);const w=async()=>{const Q=m.trim();if(!(!Q||B||!b)){P(!0),z("");try{const L=await b.add(Q);L&&L.ok===!1?z(L.error||Y("trustedSites.addFailed","添加失败")):(O(""),E(L&&L.origins||await b.list()))}catch(L){z(String(L&&L.message||L))}finally{P(!1)}}},lt=async Q=>{if(!(B||!b)){P(!0),z("");try{const L=await b.remove(Q);L&&L.ok===!1?z(L.error||Y("trustedSites.removeFailed","删除失败")):E(L&&L.origins||await b.list())}catch(L){z(String(L&&L.message||L))}finally{P(!1)}}},K={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:Q=>({flex:1,fontFamily:"ui-monospace,SFMono-Regular,Menlo,monospace",fontSize:13,color:Q?"#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 Kn.createPortal(s.jsx("div",{style:K.overlay,onClick:o,"data-id":"TrustedSitesModal",children:s.jsxs("div",{style:K.card,onClick:Q=>Q.stopPropagation(),children:[s.jsxs("div",{style:K.head,children:[s.jsx("h2",{style:K.title,children:Y("trustedSites.title","受信任站点")}),s.jsx("button",{type:"button",style:K.x,onClick:o,"aria-label":"close",children:"✕"})]}),s.jsx("div",{style:K.warn,children:Y("trustedSites.warn","⚠ 列表中的站点可以在你的电脑上执行命令(exec)。只添加你完全信任的地址。")}),s.jsxs("div",{style:K.addRow,children:[s.jsx("input",{"data-id":"trusted-sites-input",style:K.input,value:m,onChange:Q=>O(Q.target.value),onKeyDown:Q=>{Q.key==="Enter"&&w()},placeholder:Y("trustedSites.placeholder","添加站点,如 app.cicy-ai.com 或 my-cloud.example.org")}),s.jsx("button",{type:"button","data-id":"trusted-sites-add",style:{...K.addBtn,opacity:B||!m.trim()?.5:1},onClick:w,disabled:B||!m.trim(),children:Y("trustedSites.add","添加")})]}),at&&s.jsx("div",{style:K.err,children:at}),s.jsx("div",{style:K.listWrap,children:U===null?s.jsx("div",{style:K.muted,children:Y("trustedSites.loading","加载中…")}):U.length===0?s.jsx("div",{style:K.muted,children:Y("trustedSites.empty","暂无")}):U.map(Q=>s.jsxs("div",{style:K.row,"data-id":"trusted-sites-row",children:[s.jsx("span",{style:K.host(Q.builtin),children:Q.host}),Q.builtin?s.jsx("span",{style:K.tag,children:Y("trustedSites.builtin","系统")}):s.jsx("button",{type:"button",style:K.rm,onClick:()=>lt(Q.host),disabled:B,children:Y("trustedSites.remove","删除")})]},Q.host))})]})}),document.body)}function Zy({onClose:o}){const[U,E]=N.useState(null),[m,O]=N.useState(""),[B,P]=N.useState(""),[at,z]=N.useState(!1),b=typeof window<"u"&&window.cicy&&window.cicy.rpcAudit||null,X=N.useCallback(async()=>{z(!0),O("");try{const M=b&&await b.tail(400);!M||M.ok===!1?(O(M&&M.error||Y("audit.loadFailed","读取失败")),E([])):(E(M.entries||[]),P(M.path||""))}catch(M){O(String(M&&M.message||M)),E([])}finally{z(!1)}},[b]);N.useEffect(()=>{X()},[X]);const[w,lt]=N.useState("all"),[K,Q]=N.useState(""),L=M=>{try{return new Date(M).toLocaleString()}catch{return M||""}},k=M=>{if(M.kind==="auth"){const yt=/deny/.test(M.decision||"");return{text:M.decision||"auth",color:yt?"#fca5a5":"#86efac",bg:yt?"rgba(239,68,68,.14)":"rgba(34,197,94,.14)"}}if(M.kind==="rpc"){const yt=M.ok!==!1&&!M.error;return{text:yt?"ok":"err",color:yt?"#86efac":"#fca5a5",bg:yt?"rgba(34,197,94,.14)":"rgba(239,68,68,.14)"}}return{text:M.kind||"log",color:"#a1a1aa",bg:"rgba(255,255,255,.06)"}},ct=M=>M.kind==="auth"?`${M.gate||""}${M.decision?" · "+M.decision:""}`:M.kind==="rpc"?`${M.tool||""}${M.dangerous?" ⚠":""}`:M.kind||"",st=M=>M.error||M.args||(M.kind==="rpc"?M.channel:"")||"",it=U||[],ht=K.trim().toLowerCase(),Tt=it.filter(M=>w!=="all"&&M.kind!==w?!1:ht?[M.origin,M.host,M.tool,M.gate,M.decision,M.channel,M.args,M.error,M.kind].filter(Boolean).join(" ").toLowerCase().includes(ht):!0),W="186px 104px minmax(160px,1.1fr) minmax(150px,1.1fr) minmax(220px,1.8fr)",j={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:M=>({background:M?"rgba(255,255,255,.14)":"transparent",border:"1px solid rgba(255,255,255,.12)",borderRadius:999,padding:"6px 16px",color:M?"#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:W,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:W,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:M=>({justifySelf:"start",fontSize:11.5,color:M.color,background:M.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}},bt=M=>s.jsx("div",{children:M});return Kn.createPortal(s.jsx("div",{style:j.overlay,onClick:o,"data-id":"AuditLogModal",children:s.jsxs("div",{style:j.card,onClick:M=>M.stopPropagation(),children:[s.jsxs("div",{style:j.head,children:[s.jsxs("div",{style:j.titleWrap,children:[s.jsx("h2",{style:j.title,children:Y("audit.title","审计日志")}),B&&s.jsx("p",{style:j.subtitle,children:B})]}),s.jsxs("span",{style:j.count,children:[Y("audit.count","共")," ",Tt.length,w!=="all"||ht?` / ${it.length}`:""]}),s.jsx("button",{type:"button","data-id":"audit-refresh",style:{...j.refresh,opacity:at?.5:1},onClick:X,disabled:at,children:Y("audit.refresh","刷新")}),s.jsx("button",{type:"button",style:j.x,onClick:o,"aria-label":"close",children:"✕"})]}),s.jsxs("div",{style:j.toolbar,children:[s.jsxs("div",{style:j.chips,children:[s.jsx("button",{type:"button",style:j.chip(w==="all"),onClick:()=>lt("all"),children:Y("audit.all","全部")}),s.jsx("button",{type:"button",style:j.chip(w==="rpc"),onClick:()=>lt("rpc"),children:Y("audit.rpc","RPC 调用")}),s.jsx("button",{type:"button",style:j.chip(w==="auth"),onClick:()=>lt("auth"),children:Y("audit.auth","授权决定")})]}),s.jsx("input",{"data-id":"audit-search",style:j.search,value:K,onChange:M=>Q(M.target.value),placeholder:Y("audit.search","搜索来源 / 工具 / 命令…")})]}),m&&s.jsx("div",{style:j.err,children:m}),s.jsxs("div",{style:j.tableWrap,children:[s.jsxs("div",{style:j.theadRow,children:[bt(Y("audit.colTime","时间")),bt(Y("audit.colType","类型")),bt(Y("audit.colSource","来源")),bt(Y("audit.colOp","操作")),bt(Y("audit.colDetail","详情"))]}),U===null?s.jsx("div",{style:j.muted,children:Y("audit.loading","加载中…")}):Tt.length===0?s.jsx("div",{style:j.muted,children:it.length===0?Y("audit.empty","暂无审计记录"):Y("audit.noMatch","无匹配记录")}):Tt.map((M,yt)=>{const Qt=k(M);return s.jsxs("div",{style:j.row,"data-id":"audit-row",children:[s.jsx("span",{style:j.time,children:L(M.ts)}),s.jsx("span",{style:j.badge(Qt),children:Qt.text}),s.jsx("span",{style:j.cell,children:M.origin||M.host||"—"}),s.jsx("span",{style:j.cell,children:ct(M)||"—"}),s.jsx("span",{style:j.detail,children:st(M)||"—"})]},yt)})]})]})}),document.body)}function Vy({me:o,welcome:U,onLogout:E,mitmTeam:m}){const O=(o==null?void 0:o.display_name)||(o==null?void 0:o.username)||"…",B=O.slice(0,1).toUpperCase(),[P,at]=N.useState(!1),[z,b]=N.useState(!1),[X,w]=N.useState(!1),lt=N.useRef(null);N.useEffect(()=>{if(!P)return;const Q=L=>{lt.current&&!lt.current.contains(L.target)&&at(!1)};return document.addEventListener("mousedown",Q),()=>document.removeEventListener("mousedown",Q)},[P]);const K=Q=>{hi(Q),at(!1)};return s.jsxs("header",{className:"topbar",children:[s.jsxs("div",{className:"brand-mini",children:[s.jsx("div",{className:"brand-mark sm",children:s.jsx(sm,{})}),s.jsx("span",{className:"brand-name",children:"CiCy Desktop"})]}),s.jsxs("div",{className:"user-chip","data-id":"UserChip",ref:lt,children:[U&&s.jsx("span",{className:"welcome",children:U}),s.jsxs("button",{type:"button","data-id":"UserChip-trigger",className:`user-chip__trigger${P?" is-open":""}`,onClick:()=>at(Q=>!Q),children:[s.jsx("div",{className:"avatar",children:B}),s.jsx("span",{className:"user-name",children:O}),s.jsx("span",{className:"user-chip__caret","aria-hidden":!0,children:"▾"})]}),P&&s.jsxs("div",{className:"user-chip__menu","data-id":"UserChip-menu",role:"menu",children:[s.jsx("button",{type:"button","data-id":"UserChip-wallet",className:"user-chip__menu-item",onClick:()=>K("?view=wallet"),children:"我的钱包"}),s.jsx("button",{type:"button","data-id":"UserChip-billing",className:"user-chip__menu-item",onClick:()=>K("?view=usage"),children:"我的账单"}),s.jsx("button",{type:"button","data-id":"UserChip-trusted-sites",className:"user-chip__menu-item",onClick:()=>{at(!1),b(!0)},children:Y("trustedSites.menu","受信任站点")}),s.jsx("button",{type:"button","data-id":"UserChip-audit-log",className:"user-chip__menu-item",onClick:()=>{at(!1),w(!0)},children:Y("audit.menu","审计日志")}),m&&s.jsx("div",{className:"user-chip__menu-mitm","data-id":"UserChip-mitm",onClick:Q=>Q.stopPropagation(),children:s.jsx(ky,{team:m,variant:"menu"})}),s.jsx("div",{className:"user-chip__menu-sep","aria-hidden":!0}),s.jsx("button",{type:"button","data-id":"UserChip-logout",className:"user-chip__menu-item is-danger",onClick:()=>{at(!1),E()},children:"退出"})]})]}),z&&s.jsx(Xy,{onClose:()=>b(!1)}),X&&s.jsx(Zy,{onClose:()=>w(!1)})]})}function Ky({onAgree:o}){var Q;const[U,E]=N.useState(!1),[m,O]=N.useState(!1),[B,P]=N.useState(!1),at=(((Q=window.cicyI18n)==null?void 0:Q.locale)||"en").startsWith("zh")?"zh-CN":"en",z=(L,k)=>Y(`firstRunTerms.${L}`,k),b=[1,2,3,4,5,6].map(L=>z(`summary${L}`,"")),X=L=>{const k=L.currentTarget;k.scrollHeight-k.scrollTop-k.clientHeight<24&&E(!0)},w=N.useRef(null);N.useEffect(()=>{const L=w.current;L&&L.scrollHeight<=L.clientHeight+24&&E(!0)},[m]);const lt=async()=>{var L,k,ct;if(!(B||!U)){P(!0);try{await((ct=(k=(L=window.cicy)==null?void 0:L.terms)==null?void 0:k.agree)==null?void 0:ct.call(k,cm)),o==null||o()}catch{o==null||o()}}},K=()=>{var L,k,ct;try{(ct=(k=(L=window.cicy)==null?void 0:L.terms)==null?void 0:k.decline)==null||ct.call(k)}catch{}};return s.jsxs("div",{className:"shell terms-gate","data-id":"FirstRunTermsGate",children:[s.jsx("div",{className:"glow","aria-hidden":!0}),s.jsxs("div",{className:"terms-gate__panel",children:[s.jsx("h1",{className:"terms-gate__title","data-id":"FirstRunTermsGate-title",children:z("title","用户协议与授权说明")}),s.jsx("p",{className:"terms-gate__subtitle",children:z("subtitle","使用 CiCy Desktop 前,请阅读并同意以下条款")}),s.jsxs("div",{className:"terms-gate__body",ref:w,onScroll:X,"data-id":"FirstRunTermsGate-body",children:[s.jsx("h2",{className:"terms-gate__h2",children:z("summaryTitle","一眼看懂")}),s.jsx("ol",{className:"terms-gate__summary",children:b.filter(Boolean).map((L,k)=>s.jsx("li",{children:L},k))}),m?s.jsx("pre",{className:"terms-gate__fulltext","data-id":"FirstRunTermsGate-fulltext",children:Wd[at]||Wd.en}):s.jsx("button",{className:"terms-gate__viewfull","data-id":"FirstRunTermsGate-viewfull",onClick:()=>O(!0),children:z("viewFull","查看完整条款")})]}),!U&&s.jsx("div",{className:"terms-gate__scrollhint","data-id":"FirstRunTermsGate-scrollhint",children:z("scrollHint","请阅读至底部以继续")}),s.jsxs("div",{className:"terms-gate__actions",children:[s.jsx("button",{"data-id":"FirstRunTermsGate-decline",className:"terms-gate__btn terms-gate__btn--ghost",onClick:K,children:z("decline","不同意并退出")}),s.jsx("button",{"data-id":"FirstRunTermsGate-agree",className:"terms-gate__btn",disabled:!U||B,title:U?"":z("mustAgree","未同意则无法使用本软件。"),onClick:lt,children:z("agree","同意并继续")})]})]})]})}function ky({team:o,variant:U}){const[E,m]=N.useState(void 0),[O,B]=N.useState(""),[P,at]=N.useState(""),z=((o==null?void 0:o.base_url)||"").replace(/\/$/,""),b=(o==null?void 0:o.api_token)||"",X=N.useCallback(async(ct,st={})=>{var Tt,W;if(!((W=(Tt=window.cicy)==null?void 0:Tt.cloud)!=null&&W.fetch))throw new Error("bridge missing");const it=await window.cicy.cloud.fetch(`${z}${ct}`,{...st,headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json",...st.headers||{}}});let ht=null;try{ht=JSON.parse(it.body)}catch{}return{ok:it.ok,status:it.status,json:ht}},[z,b]),w=N.useCallback(async()=>{try{const ct=await X("/api/mitm/ca-status");m(ct.ok&&ct.json?ct.json:null)}catch{m(null)}},[X]);if(N.useEffect(()=>{z&&b&&w()},[z,b,w]),E===void 0||!E||!E.generated)return null;const lt=async()=>{var ct,st,it,ht,Tt,W,j,bt,M;if(!O){B("enable"),at("");try{const yt=await X("/api/mitm/consent",{method:"POST",body:JSON.stringify({enable:!0})}),Qt=`${((ct=yt.json)==null?void 0:ct.error)||""} ${((st=yt.json)==null?void 0:st.detail)||""}`,Xt=((it=yt.json)==null?void 0:it.error)==="need_elevation"||!yt.ok&&yt.status===403||/need_elevation|add-trusted-cert|write permission|SecCertificate|not permitted|requires admin|administrator/i.test(Qt);if(yt.ok&&((ht=yt.json)!=null&&ht.ok)&&((Tt=yt.json)!=null&&Tt.trusted))await w();else if(Xt){const Zt=await((bt=(j=(W=window.cicy)==null?void 0:W.mitm)==null?void 0:j.caExec)==null?void 0:bt.call(j,"install"));Zt!=null&&Zt.ok?await w():at(/cancel/i.test((Zt==null?void 0:Zt.stderr)||"")?Y("mitmConsent.errorAdminDenied","未获得管理员授权,已取消。"):(Zt==null?void 0:Zt.stderr)||Y("mitmConsent.errorTitle","提权失败,请从管理员控制台运行"))}else at(((M=yt.json)==null?void 0:M.error)||`失败 (HTTP ${yt.status})`)}catch(yt){at(String((yt==null?void 0:yt.message)||yt))}finally{B("")}}},K=async()=>{var ct,st,it,ht,Tt;if(!O){B("disable"),at("");try{const W=await X("/api/mitm/consent",{method:"POST",body:JSON.stringify({enable:!1})});if(W.ok&&((ct=W.json)!=null&&ct.ok))await w();else{const j=await((ht=(it=(st=window.cicy)==null?void 0:st.mitm)==null?void 0:it.caExec)==null?void 0:ht.call(it,"uninstall"));j!=null&&j.ok?await w():at((j==null?void 0:j.stderr)||((Tt=W.json)==null?void 0:Tt.error)||"撤销失败")}}catch(W){at(String((W==null?void 0:W.message)||W))}finally{B("")}}},Q=E.consent&&E.trusted,L=E.consent&&!E.trusted,k=(ct,st)=>Y(`mitmConsent.${ct}`,st);if(U==="menu"){const ct=st=>{var it;(it=st==null?void 0:st.stopPropagation)==null||it.call(st),!O&&(Q?window.confirm(k("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&K():lt())};return s.jsxs("div",{className:"user-chip__mitm","data-id":"MitmConsentCard",children:[s.jsxs("div",{className:"user-chip__menu-item user-chip__mitm-row",title:k("scopeNote","仅解密 AI 厂商域名,数据留本地,随时可关闭。"),children:[s.jsx("span",{className:"user-chip__mitm-label",children:k("rowLabel","HTTPS 审计")}),s.jsx("button",{type:"button",role:"switch","aria-checked":Q?"true":"false","data-id":"MitmConsentCard-toggle",className:`mini-switch${Q?" is-on":""}${O?" is-busy":""}`,disabled:!!O,onClick:ct,children:s.jsx("span",{className:"mini-switch__knob"})})]}),L&&!O&&s.jsx("div",{className:"user-chip__mitm-note","data-id":"MitmConsentCard-note",children:k("partialNote","已同意但未安装,点开关重试")}),P&&s.jsx("div",{className:"user-chip__mitm-err","data-id":"MitmConsentCard-error",children:P})]})}return Q||O==="disable"?Kn.createPortal(s.jsxs("div",{"data-id":"MitmConsentCard",className:"mitm-pill",title:k("grantedDesc","HTTPS 审计已开启,仅对 CiCy 启动的 AI 工具生效;随时可关闭。"),children:[s.jsx("span",{className:"mitm-pill__dot","data-busy":O?"1":"0"}),s.jsx("span",{className:"mitm-pill__text","data-id":"MitmConsentCard-title",children:O==="disable"?k("processingRevoke","正在关闭…"):k("statePillOn","HTTPS 审计已开启")}),!O&&s.jsx("button",{type:"button","data-id":"MitmConsentCard-revoke",className:"mitm-pill__off",onClick:()=>{window.confirm(k("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&K()},children:k("turnOff","关闭")})]}),document.body):s.jsxs("div",{"data-id":"MitmConsentCard",className:`mitm-card${Q?" mitm-card--on":""}`,children:[s.jsxs("div",{className:"mitm-card__head",children:[s.jsx("span",{className:"mitm-card__dot","data-state":Q?"on":L?"warn":"off"}),s.jsx("span",{className:"mitm-card__title","data-id":"MitmConsentCard-title",children:O?k("stateProcessingTitle","处理中…"):Q?k("stateGrantedTitle","已启用"):`${k("cardTitle","HTTPS 流量本地审计")}${L?" — "+k("retry","重试"):""}`})]}),s.jsxs("p",{className:"mitm-card__desc","data-id":"MitmConsentCard-desc",children:[Q?k("grantedDesc","HTTPS 审计已开启,仅对 CiCy 启动的 AI 工具(claude / codex 等)生效;随时可关闭。"):k("body","启用后,CiCy 启动的 AI 工具(claude / codex 等)访问 AI 厂商(Claude / OpenAI / DeepSeek / Gemini)的 HTTPS 流量将被本地审计解密,数据留本地,随时可关闭。"),!Q&&s.jsxs(s.Fragment,{children:[s.jsx("br",{}),s.jsx("span",{className:"mitm-card__note",children:k("adminNote","通过环境变量对 CiCy 启动的 AI 工具生效,不修改系统、无需管理员授权。")}),s.jsx("br",{}),s.jsx("span",{className:"mitm-card__sub",children:k("scopeNote","仅解密上述 AI 厂商域名,其余一切流量不被解密、不被读取。")})]})]}),P&&s.jsxs("div",{className:"mitm-card__error","data-id":"MitmConsentCard-error",children:[k("errorTitle","操作失败"),": ",P]}),s.jsx("div",{className:"mitm-card__actions",children:Q?s.jsx("button",{"data-id":"MitmConsentCard-revoke",className:"mitm-card__btn mitm-card__btn--ghost",disabled:!!O,onClick:()=>{window.confirm(k("revokeConfirm","撤销后将停止解密审计并清除同意标记。确定?"))&&K()},children:O==="disable"?k("processingRevoke","正在关闭…"):k("revoke","撤销")}):s.jsx("button",{"data-id":"MitmConsentCard-enable",className:"mitm-card__btn",disabled:!!O,onClick:lt,children:O==="enable"?k("processingEnable","正在启用…"):L?k("retry","重试"):k("enable","同意并启用")})})]})}function Pd({team:o,onOpen:U,onRename:E,onRefresh:m}){var Xl,D,F,G;const B=(em[o.status]||em.error).tone,[P,at]=N.useState(!1),[z,b]=N.useState(o.name||""),[X,w]=N.useState(null),[lt,K]=N.useState(""),Q=X??o.name;N.useEffect(()=>{X!=null&&o.name===X&&w(null)},[o.name,X]);const L=C=>{var q;(q=C==null?void 0:C.stopPropagation)==null||q.call(C),b(Q||""),at(!0)},k=async()=>{at(!1);const C=String(z||"").trim();if(!E||!C||C===Q)return;w(C),K("saving");let q;try{q=await E(o.id,C)}catch($){q={ok:!1,error:$==null?void 0:$.message}}w(null),q&&q.ok?(K("saved"),setTimeout(()=>K($=>$==="saved"?"":$),1500)):(K(""),je.show({message:Y("localTeams.renameFailed","改名没保存,已恢复"),status:"error",ttl:4e3}))},st=!!((D=(Xl=window.cicy)==null?void 0:Xl.sidecar)!=null&&D.restart)&&Bs(o.base_url),it=o.status==="running",[ht,Tt]=N.useState(""),[W,j]=N.useState(!1),[bt,M]=N.useState({running:void 0,latest:null,installed:null}),yt=bt.latest,Qt=bt.running,[Xt,Zt]=N.useState(!1),pe=N.useRef(null),Yt=N.useRef(null),S=N.useRef(null),[R,tt]=N.useState({top:0,left:0}),xt=184,gt=()=>{if(!W&&Yt.current){const C=Yt.current.getBoundingClientRect(),q=Math.max(8,Math.min(C.right-xt,window.innerWidth-xt-8));tt({top:Math.round(C.bottom+4),left:Math.round(q)})}j(C=>!C)},r=N.useCallback(async(C=!1)=>{var q,$;if(!(!st||!(($=(q=window.cicy)==null?void 0:q.sidecar)!=null&&$.versions))){C&&Zt(!0);try{const et=await window.cicy.sidecar.versions();M({running:(et==null?void 0:et.running)??null,latest:(et==null?void 0:et.latest)??null,installed:(et==null?void 0:et.installed)??null}),C&&(et!=null&&et.running&&(et!=null&&et.latest)&&tm(et.latest,et.running)>0?je.show({message:`${Y("sidecar.found","发现新版本")} v${et.latest}`,status:"done",ttl:2500}):et!=null&&et.running?je.show({message:`${Y("sidecar.upToDate","已是最新")} v${et.running}`,status:"done",ttl:2500}):je.show({message:Y("sidecar.notRunning","cicy-code 未运行"),status:"error",ttl:4e3}))}catch{C&&je.show({message:Y("sidecar.checkFailed","检查更新失败"),status:"error",ttl:5e3})}finally{C&&Zt(!1)}}},[st]);N.useEffect(()=>{r(!1)},[r]),N.useEffect(()=>{it&&r(!1)},[it,r]);const x=!!(st&&yt&&Qt&&tm(yt,Qt)>0),H=!st&&!!((G=(F=window.cicy)==null?void 0:F.localTeams)!=null&&G.remove),Z=st||H,[nt,ft]=N.useState(!1);N.useEffect(()=>{if(!W)return;const C=q=>{var $,et;($=Yt.current)!=null&&$.contains(q.target)||(et=S.current)!=null&&et.contains(q.target)||j(!1)};return document.addEventListener("mousedown",C),()=>document.removeEventListener("mousedown",C)},[W]),N.useEffect(()=>{W||ft(!1)},[W]);const At=async()=>{var C,q,$;if(!nt){ft(!0);return}if(j(!1),ft(!1),!ht){Tt("remove");try{await(($=(q=(C=window.cicy)==null?void 0:C.localTeams)==null?void 0:q.remove)==null?void 0:$.call(q,o.id))}catch{}Tt(""),m==null||m()}},Ht=`sidecar-op:${o.id}`,Nt=async(C,q,$)=>{var Gt,Oe;if(j(!1),ht)return;Tt(C);const et=C==="update";let Wt=null;et?(hl.open({teamId:o.id,fromVer:Qt,toVer:yt,onRetry:()=>Nt("update",q,$)}),(Oe=(Gt=window.cicy)==null?void 0:Gt.sidecar)!=null&&Oe.onOpProgress&&(Wt=window.cicy.sidecar.onOpProgress(rt=>{(rt==null?void 0:rt.op)==="update"&&hl.push(rt)}))):je.show({id:Ht,message:Ne[C]||`${C}…`,status:"running",progress:void 0});try{const rt=await q(),Ve=!!(rt!=null&&rt.ok),ka=rt!=null&&rt.warning?`${$}(${rt.warning})`:$,Ja=Y("sidecar.failed","操作失败")+(rt!=null&&rt.error?`: ${rt.error}`:"");et?hl.finish({ok:Ve,message:Ve?ka:Ja}):je.show({id:Ht,message:Ve?ka:Ja,progress:void 0,status:Ve?"done":"error",ttl:Ve?4e3:8e3})}catch(rt){const Ve=Y("sidecar.failed","操作失败")+`: ${(rt==null?void 0:rt.message)||rt}`;et?hl.finish({ok:!1,message:Ve}):je.show({id:Ht,message:Ve,progress:void 0,status:"error",ttl:8e3})}finally{try{Wt&&Wt()}catch{}Tt(""),m==null||m(),(C==="update"||C==="restart"||C==="start")&&r(!1)}},Ne={start:"启动中…",restart:"重启中…",update:"更新中…",stop:"停止中…"},Ze=async()=>{var C,q;if(!ht){if(!it&&st&&((q=(C=window.cicy)==null?void 0:C.sidecar)!=null&&q.start)){Tt("start"),je.show({id:Ht,message:Ne.start,status:"running",progress:void 0});const $=await window.cicy.sidecar.start().catch(et=>({ok:!1,error:(et==null?void 0:et.message)||String(et)}));if(Tt(""),m==null||m(),!($!=null&&$.ok)||$!=null&&$.warning){je.show({id:Ht,message:Y("sidecar.startFailed","启动失败")+($!=null&&$.error?`: ${$.error}`:$!=null&&$.warning?`: ${$.warning}`:""),status:"error",ttl:8e3});return}je.dismiss(Ht)}U()}},gl=it?Y("localTeams.open","打开"):st?Y("localTeams.startOpen","启动并打开"):Y("localTeams.open","打开");return s.jsxs("div",{"data-id":"LocalTeamCard",className:`bcard ${st?"bcard--local":"bcard--custom"}${B==="ok"?" bcard--online":""}`,children:[s.jsx("div",{className:"bcard__accent"}),s.jsxs("div",{className:"bcard__top",children:[s.jsxs("div",{className:"bcard__pill",children:[s.jsx("span",{className:"bcard__dot","data-tone":B}),s.jsx(fm,{})]}),Z&&s.jsxs("div",{className:"bcard__menuwrap",ref:pe,onClick:C=>C.stopPropagation(),children:[s.jsx("button",{type:"button",ref:Yt,"data-id":"LocalTeamCard-menu-btn",className:`bcard__kebab${x?" has-dot":""}`,title:st?Y("localTeams.manage","管理本地 cicy-code"):Y("localTeams.more","更多"),disabled:!!ht,onClick:gt,children:ht?s.jsx(yl,{}):s.jsx(om,{})}),W&&Kn.createPortal(s.jsxs("div",{className:"bcard__menu bcard__menu--portal","data-id":"LocalTeamCard-menu",role:"menu",ref:S,style:{position:"fixed",top:R.top,left:R.left,width:xt},onClick:C=>C.stopPropagation(),children:[x&&s.jsxs("button",{type:"button","data-id":"LocalTeamCard-update",className:"bcard__menu-item is-accent",onClick:()=>Nt("update",()=>window.cicy.sidecar.update(),Y("sidecar.updated","已更新到最新")),children:[Y("sidecar.updateTo","更新到")," v",yt]}),st&&it&&s.jsxs(s.Fragment,{children:[s.jsx("button",{type:"button","data-id":"LocalTeamCard-reload",className:"bcard__menu-item",onClick:()=>Nt("reload",async()=>{const C=await window.cicy.localTeams.reload(o.id);return!(C!=null&&C.ok)&&(C==null?void 0:C.error)==="no_open_window"?window.cicy.localTeams.open(o.id):C},Y("localTeams.reloaded","已刷新窗口")),children:Y("localTeams.reloadWindow","刷新窗口")}),s.jsx("button",{type:"button","data-id":"LocalTeamCard-restart",className:"bcard__menu-item",onClick:()=>Nt("restart",()=>window.cicy.sidecar.restart(),Y("sidecar.restarted","已重启")),children:Y("sidecar.restart","重启")}),s.jsx("button",{type:"button","data-id":"LocalTeamCard-stop",className:"bcard__menu-item is-danger",onClick:()=>Nt("stop",()=>window.cicy.sidecar.stop(),Y("sidecar.stoppedDone","已停止")),children:Y("sidecar.stop","停止")})]}),st&&s.jsx("button",{type:"button","data-id":"LocalTeamCard-check-update",className:"bcard__menu-item",disabled:Xt,onClick:C=>{C.stopPropagation(),r(!0)},children:Xt?Y("sidecar.checking2","检查中…"):Y("sidecar.checkUpdate","检查更新")}),o.cloud_team_id&&s.jsx("button",{type:"button","data-id":"LocalTeamCard-billing",className:"bcard__menu-item",onClick:C=>{C.stopPropagation(),j(!1),hi(`?team=${encodeURIComponent(o.cloud_team_id)}`)},children:Y("localTeams.billing","账单")}),H&&s.jsx("button",{type:"button","data-id":"LocalTeamCard-remove",className:"bcard__menu-item is-danger",onClick:At,children:nt?Y("localTeams.removeConfirm","确认删除?"):Y("localTeams.remove","删除")})]}),document.body)]})]}),s.jsxs("div",{className:"bcard__body",children:[P?s.jsx("input",{"data-id":"LocalTeamCard-rename-input",autoFocus:!0,value:z,onChange:C=>b(C.target.value),onFocus:C=>C.target.select(),onBlur:k,onClick:C=>C.stopPropagation(),onKeyDown:C=>{C.nativeEvent.isComposing||C.keyCode===229||(C.key==="Enter"?k():C.key==="Escape"&&at(!1))},style:{width:"100%",font:"inherit",fontWeight:600,padding:"2px 6px",border:"1px solid #3b82f6",borderRadius:6,background:"#0d1117",color:"#e6edf3",boxSizing:"border-box"}}):s.jsxs("h3",{className:"bcard__name",title:Y("localTeams.renameHint","点名字或 ✎ 改名"),style:{display:"flex",alignItems:"center",gap:6},onDoubleClick:L,children:[s.jsx("span",{"data-id":"LocalTeamCard-name-text",onClick:L,style:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",cursor:"text"},children:Q}),lt==="saving"&&s.jsx("span",{"data-id":"LocalTeamCard-save-state",title:Y("localTeams.saving","保存中…"),style:{flex:"none",display:"inline-flex"},children:s.jsx(yl,{})}),lt==="saved"&&s.jsx("span",{"data-id":"LocalTeamCard-save-state",title:Y("localTeams.saved","已保存"),style:{flex:"none",color:"#3fb950",fontSize:13,lineHeight:1},children:"✓"}),!lt&&s.jsx("button",{type:"button","data-id":"LocalTeamCard-rename-btn",title:Y("localTeams.rename","重命名"),onClick:L,style:{flex:"none",cursor:"pointer",border:"none",background:"transparent",color:"#8b949e",fontSize:13,padding:0,lineHeight:1},children:"✎"})]}),s.jsx("div",{className:"bcard__host",children:o.base_url||"—"}),s.jsx("div",{className:"bcard__meta",children:(Qt||o.version)&&s.jsxs("span",{className:"bcard__ver","data-id":"LocalTeamCard-version",children:["v",Qt||o.version]})})]}),s.jsxs("button",{type:"button",className:"bcard__cta","data-id":"LocalTeamCard-open",disabled:!!ht||!o.base_url,onClick:Ze,children:[ht&&ht!=="stop"?s.jsx(yl,{}):s.jsx(Ys,{}),s.jsx("span",{children:ht&&Ne[ht]||gl})]})]})}function Bs(o){try{const U=new URL(o);return(U.hostname==="127.0.0.1"||U.hostname==="localhost"||U.hostname==="::1")&&(U.port==="8008"||U.port==="")}catch{return!1}}function tm(o,U){const E=String(o).split("."),m=String(U).split(".");for(let O=0;O<Math.max(E.length,m.length);O++){const B=(parseInt(E[O],10)||0)-(parseInt(m[O],10)||0);if(B)return B>0?1:-1}return 0}const em={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 Jy({team:o,onOpen:U}){const E=o.kind==="private",m=o.status==="active",O=o.name||o.title||"—",B=o.host_url||"",P=o.teamId||o.id,at=E?"私有云":o.team_kind==="personal"?"个人":"共享",z=E?B:o.workspace_url||o.workspace_direct_url,b=!!z,[X,w]=N.useState(!1),[lt,K]=N.useState(!1),[Q,L]=N.useState({top:0,left:0}),k=N.useRef(null),ct=N.useRef(null),st=N.useRef(null),it=184,ht=()=>{if(!X&&ct.current){const W=ct.current.getBoundingClientRect(),j=Math.max(8,Math.min(W.right-it,window.innerWidth-it-8));L({top:Math.round(W.bottom+4),left:Math.round(j)})}w(W=>!W)};N.useEffect(()=>{if(!X)return;const W=j=>{var bt,M;(bt=ct.current)!=null&&bt.contains(j.target)||(M=st.current)!=null&&M.contains(j.target)||w(!1)};return document.addEventListener("mousedown",W),()=>document.removeEventListener("mousedown",W)},[X]);const Tt=async()=>{var W,j,bt;if(!(!b||lt)){K(!0),w(!1);try{await((bt=(j=(W=window.cicy)==null?void 0:W.tabs)==null?void 0:j.reload)==null?void 0:bt.call(j,z,O))}catch{}finally{K(!1)}}};return s.jsxs("div",{"data-id":"TeamCard",className:`bcard bcard--cloud${m?" bcard--online":""}`,children:[s.jsx("div",{className:"bcard__accent"}),s.jsxs("div",{className:"bcard__top",children:[s.jsxs("div",{className:"bcard__pill",children:[s.jsx("span",{className:"bcard__dot","data-tone":m?"ok":"off"}),s.jsx(Wy,{})]}),s.jsxs("div",{className:"bcard__top-right",children:[o.is_trial&&s.jsx("span",{className:"bcard__badge",children:"trial"}),P!=null&&s.jsx("button",{type:"button","data-id":"TeamCard-billing",className:"bcard__billing-btn",title:Y("localTeams.billing","账单"),onClick:W=>{W.stopPropagation(),hi(`?team=${encodeURIComponent(P)}`)},children:Y("localTeams.billing","账单")}),b&&s.jsxs("div",{className:"bcard__menuwrap",ref:k,onClick:W=>W.stopPropagation(),children:[s.jsx("button",{type:"button",ref:ct,"data-id":"TeamCard-menu-btn",className:"bcard__kebab",title:Y("localTeams.more","更多"),disabled:lt,onClick:ht,children:lt?s.jsx(yl,{}):s.jsx(om,{})}),X&&Kn.createPortal(s.jsx("div",{className:"bcard__menu bcard__menu--portal","data-id":"TeamCard-menu",role:"menu",ref:st,style:{position:"fixed",top:Q.top,left:Q.left,width:it},onClick:W=>W.stopPropagation(),children:s.jsx("button",{type:"button","data-id":"TeamCard-reload",className:"bcard__menu-item",onClick:Tt,children:Y("localTeams.reloadWindow","刷新窗口")})}),document.body)]})]})]}),s.jsxs("div",{className:"bcard__body",children:[s.jsx("h3",{className:"bcard__name",title:O,children:O}),s.jsx("div",{className:"bcard__host",title:E&&B||"",children:E?B||Y("teamCard.noHost","未填访问地址"):o.runtime_region||o.region||"—"}),s.jsxs("div",{className:"bcard__meta",children:[s.jsx("span",{className:"bcard__chip",children:at}),!E&&o.membership_status&&o.membership_status!=="active"&&s.jsx("span",{className:"bcard__chip",children:o.membership_status})]})]}),s.jsxs("button",{type:"button",className:"bcard__cta",onClick:U,disabled:!b,children:[s.jsx(Ys,{}),s.jsx("span",{children:b?Y("localTeams.open","打开"):E?Y("teamCard.noHost","未填访问地址"):Y("teamCard.noUrl","无 URL")})]})]})}function Us(){return s.jsxs("div",{className:"brand",children:[s.jsx("div",{className:"brand-mark",children:s.jsx(sm,{})}),s.jsxs("div",{className:"brand-text",children:[s.jsx("div",{className:"brand-name",children:"CiCy Desktop"}),s.jsx("div",{className:"brand-sub",children:"团队 AI 协作工作台"})]})]})}function sm(){return s.jsx("svg",{width:"22",height:"22",viewBox:"0 0 96 96",fill:"none",children:s.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 Ys(){return s.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("line",{x1:"5",y1:"12",x2:"19",y2:"12"}),s.jsx("polyline",{points:"12 5 19 12 12 19"})]})}function yl(){return s.jsx("svg",{className:"spin",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.4",strokeLinecap:"round",children:s.jsx("path",{d:"M21 12a9 9 0 1 1-6.2-8.55"})})}function fm(){return s.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("rect",{x:"3",y:"4",width:"18",height:"12",rx:"2"}),s.jsx("line",{x1:"2",y1:"20",x2:"22",y2:"20"})]})}function om(){return s.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:[s.jsx("circle",{cx:"12",cy:"5",r:"1.7"}),s.jsx("circle",{cx:"12",cy:"12",r:"1.7"}),s.jsx("circle",{cx:"12",cy:"19",r:"1.7"})]})}function Wy(){return s.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("circle",{cx:"12",cy:"12",r:"10"}),s.jsx("line",{x1:"2",y1:"12",x2:"22",y2:"12"}),s.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 Vn(o){try{return localStorage.getItem(o)||null}catch{return null}}function lm(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 am;const $y=((am=window.cicy)==null?void 0:am.platform)||(()=>{const o=navigator.userAgent||"";return/Mac/i.test(o)?"darwin":/Windows/i.test(o)?"win32":"linux"})();document.documentElement.dataset.platform=$y;document.documentElement.dataset.fullscreen="0";var nm,um;(um=(nm=window.cicy)==null?void 0:nm.window)!=null&&um.onFullscreen&&window.cicy.window.onFullscreen(o=>{document.documentElement.dataset.fullscreen=o?"1":"0"});qy.createRoot(document.getElementById("root")).render(s.jsx(Qy,{}));
|
|
@@ -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-xHkT3-tl.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="./assets/index-CKpaMBKz.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
package/src/main.js
CHANGED
|
@@ -1048,6 +1048,21 @@ electronApp.whenReady().then(async () => {
|
|
|
1048
1048
|
} catch (e) { log.warn(`[thumbs] start failed: ${e.message}`); }
|
|
1049
1049
|
}
|
|
1050
1050
|
|
|
1051
|
+
// Periodic WHOLE-desktop snapshot → ~/cicy-files/desktop-snapshot/desktop.b64
|
|
1052
|
+
// (≤600px wide JPEG). ALL PLATFORMS: the cloud (cicy-code) fetches it via the
|
|
1053
|
+
// dedicated non-dangerous `desktop_snapshot` RPC tool, which reads this fresh
|
|
1054
|
+
// file — so there's no per-call screen capture, hence no macOS Screen-Recording
|
|
1055
|
+
// prompt and no consent dialog. On Windows the capture runs in a --disable-gpu
|
|
1056
|
+
// child electron (GDI path, works over RDP; main app GPU intact); on mac/linux
|
|
1057
|
+
// it's an in-process native-capture loop (screencapture / scrot).
|
|
1058
|
+
if (!global.__cicyDesktopSnapStarted) {
|
|
1059
|
+
global.__cicyDesktopSnapStarted = true;
|
|
1060
|
+
try {
|
|
1061
|
+
const info = require("./utils/desktop-snapshot").startDesktopSnapshots();
|
|
1062
|
+
log.info(`[desktop-snap] desktop snapshots → ${info.dir} (every ${info.intervalMs}ms, maxW ${info.maxWidth}, mode ${info.mode})`);
|
|
1063
|
+
} catch (e) { log.warn(`[desktop-snap] start failed: ${e.message}`); }
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1051
1066
|
// Local-team discovery — reads ~/cicy-ai/global.json's cicyDesktopNodes
|
|
1052
1067
|
// and probes each via /api/health. Pure local, never talks to the cloud
|
|
1053
1068
|
// and never runs docker shells.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const { z } = require("zod");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
|
|
5
|
+
// desktop_snapshot — DEDICATED, NON-dangerous full-desktop screenshot tool for the
|
|
6
|
+
// cloud (cicy-code). It returns a base64 JPEG of the whole screen so cicy-code can
|
|
7
|
+
// preview a device's desktop WITHOUT going through exec_shell / file_read.
|
|
8
|
+
//
|
|
9
|
+
// Why a dedicated tool (主人令 + w-10135):
|
|
10
|
+
// exec_*/file_* are in rpc-guard's DANGEROUS_TOOLS, so each call pops the
|
|
11
|
+
// "敏感操作请求" consent dialog, and on macOS the live `screencapture` shell also
|
|
12
|
+
// trips the OS Screen-Recording prompt. This tool is NOT dangerous (deliberately
|
|
13
|
+
// absent from DANGEROUS_TOOLS), and it reuses cicy-desktop's own native capturer
|
|
14
|
+
// (which already holds the OS grant) — so: no shell, no consent dialog, no
|
|
15
|
+
// per-call permission prompt.
|
|
16
|
+
//
|
|
17
|
+
// Source of the image (preferred → fallback):
|
|
18
|
+
// 1) ~/cicy-files/desktop-snapshot/desktop.b64 — written every few seconds by the
|
|
19
|
+
// desktop-snapshot daemon (src/utils/desktop-snapshot.js, started in main.js).
|
|
20
|
+
// On Windows this is the ONLY valid source (in-process desktopCapturer fails
|
|
21
|
+
// over RDP without --disable-gpu; the daemon is a --disable-gpu child).
|
|
22
|
+
// 2) live in-process capture (mac/linux only) when the file is stale/missing.
|
|
23
|
+
const snap = require("../utils/desktop-snapshot");
|
|
24
|
+
|
|
25
|
+
// Treat the daemon file as good if written within this window. The daemon ticks
|
|
26
|
+
// every ~8s, so 30s tolerates a couple of missed/slow ticks before we live-capture.
|
|
27
|
+
const FRESH_MS = 30_000;
|
|
28
|
+
|
|
29
|
+
function readDaemonB64() {
|
|
30
|
+
try {
|
|
31
|
+
const p = path.join(snap.snapDir(), "desktop.b64");
|
|
32
|
+
const st = fs.statSync(p);
|
|
33
|
+
const ageMs = Date.now() - st.mtimeMs;
|
|
34
|
+
const b64 = fs.readFileSync(p, "utf8").trim();
|
|
35
|
+
if (b64.length > 256) return { b64, ageMs };
|
|
36
|
+
} catch (_) {}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = (registerTool) => {
|
|
41
|
+
registerTool(
|
|
42
|
+
"desktop_snapshot",
|
|
43
|
+
"返回整屏桌面截图(base64 JPEG,≤600px 宽)。优先读 desktop-snapshot daemon 写的 desktop.b64(秒回、不弹 consent / 屏幕录制),过期或缺失时 mac/linux 即时抓屏。",
|
|
44
|
+
z.object({ maxWidth: z.number().optional().describe("最大宽度(px),默认 600") }),
|
|
45
|
+
async ({ maxWidth }) => {
|
|
46
|
+
try {
|
|
47
|
+
const fresh = readDaemonB64();
|
|
48
|
+
if (fresh && fresh.ageMs <= FRESH_MS) {
|
|
49
|
+
return { content: [{ type: "text", text: fresh.b64 }] };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Stale/missing. mac/linux can capture live in-process; win32 cannot (needs
|
|
53
|
+
// the --disable-gpu daemon), so there we fall back to whatever file exists.
|
|
54
|
+
if (process.platform !== "win32") {
|
|
55
|
+
try {
|
|
56
|
+
const r = await snap.captureB64(maxWidth);
|
|
57
|
+
return { content: [{ type: "text", text: r.b64 }] };
|
|
58
|
+
} catch (e) {
|
|
59
|
+
if (fresh) return { content: [{ type: "text", text: fresh.b64 }] }; // stale but real
|
|
60
|
+
throw e;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (fresh) return { content: [{ type: "text", text: fresh.b64 }] }; // win32: stale is better than nothing
|
|
65
|
+
throw new Error("no desktop snapshot yet (daemon warming up?)");
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{ tag: "Desktop" }
|
|
71
|
+
);
|
|
72
|
+
};
|
package/src/tools/index.js
CHANGED
|
@@ -104,9 +104,14 @@ class TabManager {
|
|
|
104
104
|
managerByHost.set(this.win.webContents.id, this);
|
|
105
105
|
// Fullscreen hides the OS window controls (mac traffic lights / win caption
|
|
106
106
|
// buttons) → tell the shell so it reclaims the reserved gutter (CSS
|
|
107
|
-
// body.is-fullscreen). Fires on both mac and Windows.
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
// body.is-fullscreen). Fires on both mac and Windows. MUST also reach every
|
|
108
|
+
// TAB's renderer: the homepage SPA (homepage-preload) toggles its own
|
|
109
|
+
// data-fullscreen attr to drop the 34px traffic-light gutter — and the
|
|
110
|
+
// homepage now runs as a resident BrowserView TAB, not this.win.webContents.
|
|
111
|
+
// Without forwarding to the tab, the gutter stays in fullscreen = a blank
|
|
112
|
+
// strip across the top of 我的团队 (reported on mac fullscreen).
|
|
113
|
+
this.win.on("enter-full-screen", () => this.sendFullscreen(true));
|
|
114
|
+
this.win.on("leave-full-screen", () => this.sendFullscreen(false));
|
|
110
115
|
this.win.on("resize", () => this.layout());
|
|
111
116
|
this.win.on("closed", () => {
|
|
112
117
|
managers.delete(accountIdx);
|
|
@@ -114,6 +119,16 @@ class TabManager {
|
|
|
114
119
|
});
|
|
115
120
|
}
|
|
116
121
|
|
|
122
|
+
// Broadcast the window's fullscreen state to the shell chrome AND every tab's
|
|
123
|
+
// renderer. The homepage tab's SPA needs it to collapse the mac traffic-light
|
|
124
|
+
// gutter; other tabs simply ignore the message.
|
|
125
|
+
sendFullscreen(isFs) {
|
|
126
|
+
try { this.win.webContents.send("window:fullscreen", isFs); } catch (e) {}
|
|
127
|
+
for (const t of this.tabs) {
|
|
128
|
+
try { t.view.webContents.send("window:fullscreen", isFs); } catch (e) {}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
117
132
|
pushState() {
|
|
118
133
|
const active = this.tabs.find((t) => t.id === this.activeId);
|
|
119
134
|
let wc = null;
|
|
@@ -177,6 +192,10 @@ class TabManager {
|
|
|
177
192
|
wc.on("page-favicon-updated", (_e, favs) => { tab.favicon = (favs && favs[0]) || ""; this.pushState(); });
|
|
178
193
|
wc.on("did-start-loading", () => { tab.loading = true; this.pushState(); });
|
|
179
194
|
wc.on("did-stop-loading", () => { tab.loading = false; this.pushState(); });
|
|
195
|
+
// Re-sync fullscreen state after each (re)load: the SPA resets data-fullscreen
|
|
196
|
+
// to "0" on mount, so a homepage reload while the window is fullscreen would
|
|
197
|
+
// otherwise re-show the 34px traffic-light gutter (blank top strip).
|
|
198
|
+
wc.on("did-finish-load", () => { try { wc.send("window:fullscreen", !!this.win.isFullScreen()); } catch (e) {} });
|
|
180
199
|
wc.on("did-navigate", (_e, u) => { tab.url = u; tab.favicon = ""; this.pushState(); });
|
|
181
200
|
wc.on("did-navigate-in-page", (_e, u) => { tab.url = u; this.pushState(); });
|
|
182
201
|
// popups / window.open → open as a new tab. In profile 0 the new tab carries
|
|
@@ -247,13 +266,10 @@ function findManagerByTab(webContentsId) {
|
|
|
247
266
|
}
|
|
248
267
|
|
|
249
268
|
// ── programmatic API (team-open reroute / homepage) ──────────────────────────
|
|
250
|
-
// profile 0 is the system tab window
|
|
251
|
-
// (team open
|
|
252
|
-
//
|
|
269
|
+
// profile 0 is the system tab window. It used to accept tabs ONLY from the
|
|
270
|
+
// homepage (team open, systemOpen:true); that restriction is lifted so the "+"
|
|
271
|
+
// button / electron_tab_open / the panel can add tabs to profile 0 too.
|
|
253
272
|
async function openTab(accountIdx, url, opts = {}) {
|
|
254
|
-
if (accountIdx === 0 && !opts.systemOpen) {
|
|
255
|
-
throw new Error("profile 0 的标签只能从首页点开 team");
|
|
256
|
-
}
|
|
257
273
|
const m = ensureManager(accountIdx);
|
|
258
274
|
const id = m.addTab(url, { trusted: !!opts.trusted, home: !!opts.home, title: opts.title || "" });
|
|
259
275
|
try { m.win.show(); m.win.focus(); } catch (e) {}
|
|
@@ -280,7 +296,7 @@ function installIpc() {
|
|
|
280
296
|
ipcInstalled = true;
|
|
281
297
|
const mgr = (e) => managerByHost.get(e.sender.id);
|
|
282
298
|
ipcMain.on("tabwin:ready", (e) => { const m = mgr(e); if (m) m.pushState(); });
|
|
283
|
-
ipcMain.on("tabwin:new", (e, { url }) => { const m = mgr(e); if (m
|
|
299
|
+
ipcMain.on("tabwin:new", (e, { url }) => { const m = mgr(e); if (m) m.addTab(url || ""); });
|
|
284
300
|
ipcMain.on("tabwin:activate", (e, { id }) => { const m = mgr(e); if (m) m.activate(id); });
|
|
285
301
|
ipcMain.on("tabwin:close", (e, { id }) => { const m = mgr(e); if (m) m.close(id); });
|
|
286
302
|
ipcMain.on("tabwin:navigate", (e, { url }) => { const m = mgr(e); const wc = m && m.activeWc(); if (wc && url) wc.loadURL(String(url)); });
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// desktop-snapshot.js
|
|
2
|
+
// Periodically captures the WHOLE desktop (not a window) to disk so the cloud
|
|
3
|
+
// (cicy-code) can fetch a recent screen preview by just reading a file — no live
|
|
4
|
+
// per-request capture. Fixes Windows, where the old on-demand path (server →
|
|
5
|
+
// exec_shell → PowerShell CopyFromScreen) returns empty under AppLocker / a
|
|
6
|
+
// display-less RDP session.
|
|
7
|
+
//
|
|
8
|
+
// Output (<dir> = ~/cicy-files/desktop-snapshot, override CICY_DESKTOP_SNAP_DIR):
|
|
9
|
+
// <dir>/desktop.jpg the image (≤600px wide JPEG)
|
|
10
|
+
// <dir>/desktop.b64 its base64 text ← the cloud reads THIS (cat / type)
|
|
11
|
+
//
|
|
12
|
+
// Capture per platform (all from inside the user's session):
|
|
13
|
+
// darwin → `screencapture -x -t jpg` (in-process)
|
|
14
|
+
// linux → scrot / ImageMagick import (in-process)
|
|
15
|
+
// win32 → Electron desktopCapturer, but run in a SEPARATE `--disable-gpu`
|
|
16
|
+
// child electron: an RDP session has no DXGI desktop-duplication, so
|
|
17
|
+
// the in-process (GPU) capturer fails ("Duplication failed"); the GDI
|
|
18
|
+
// path needs --disable-gpu. We isolate that to a child daemon so the
|
|
19
|
+
// MAIN app keeps hardware acceleration. (主人令: 不禁用主 app 的 GPU。)
|
|
20
|
+
|
|
21
|
+
const fs = require("fs");
|
|
22
|
+
const path = require("path");
|
|
23
|
+
const os = require("os");
|
|
24
|
+
const { execFileSync, spawn } = require("child_process");
|
|
25
|
+
const electron = require("electron");
|
|
26
|
+
|
|
27
|
+
const MAX_W = 600; // 主人令: 压到 600 以下宽度
|
|
28
|
+
const QUALITY = 60;
|
|
29
|
+
const DEFAULT_INTERVAL_MS = 8000;
|
|
30
|
+
|
|
31
|
+
function snapDir() {
|
|
32
|
+
const fromEnv = (process.env.CICY_DESKTOP_SNAP_DIR || "").trim();
|
|
33
|
+
return fromEnv || path.join(os.homedir(), "cicy-files", "desktop-snapshot");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function intervalMs(opt) {
|
|
37
|
+
const fromEnv = parseInt(process.env.CICY_DESKTOP_SNAP_INTERVAL_MS || "", 10);
|
|
38
|
+
return (opt && opt.intervalMs) || (fromEnv > 0 ? fromEnv : DEFAULT_INTERVAL_MS);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Grab the primary screen as a NativeImage. win32 uses desktopCapturer (only
|
|
42
|
+
// valid when GPU is off → run from the --disable-gpu daemon); mac/linux shell out.
|
|
43
|
+
async function grabScreenImage() {
|
|
44
|
+
const { desktopCapturer, screen, nativeImage } = electron;
|
|
45
|
+
if (process.platform === "win32") {
|
|
46
|
+
const d = screen.getPrimaryDisplay();
|
|
47
|
+
const sf = d.scaleFactor || 1;
|
|
48
|
+
const w = Math.max(1, Math.round(d.size.width * sf));
|
|
49
|
+
const h = Math.max(1, Math.round(d.size.height * sf));
|
|
50
|
+
const sources = await desktopCapturer.getSources({
|
|
51
|
+
types: ["screen"],
|
|
52
|
+
thumbnailSize: { width: w, height: h },
|
|
53
|
+
});
|
|
54
|
+
if (!sources.length) throw new Error("no screen source");
|
|
55
|
+
let img = sources[0].thumbnail;
|
|
56
|
+
for (const s of sources) { if (!s.thumbnail.isEmpty()) { img = s.thumbnail; break; } }
|
|
57
|
+
if (img.isEmpty()) throw new Error("empty screen capture (no display?)");
|
|
58
|
+
return img;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const tmp = path.join(os.tmpdir(), `cicy_desktop_snap_${process.pid}.jpg`);
|
|
62
|
+
try { fs.unlinkSync(tmp); } catch (_) {}
|
|
63
|
+
if (process.platform === "darwin") {
|
|
64
|
+
execFileSync("/usr/sbin/screencapture", ["-x", "-t", "jpg", tmp], { stdio: "ignore" });
|
|
65
|
+
} else {
|
|
66
|
+
const env = { ...process.env, DISPLAY: process.env.DISPLAY || ":0" };
|
|
67
|
+
try { execFileSync("scrot", ["-o", tmp], { stdio: "ignore", env }); }
|
|
68
|
+
catch (_) { execFileSync("import", ["-window", "root", tmp], { stdio: "ignore", env }); }
|
|
69
|
+
}
|
|
70
|
+
const buf = fs.readFileSync(tmp);
|
|
71
|
+
try { fs.unlinkSync(tmp); } catch (_) {}
|
|
72
|
+
const img = electron.nativeImage.createFromBuffer(buf);
|
|
73
|
+
if (img.isEmpty()) throw new Error("empty native capture");
|
|
74
|
+
return img;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function captureOnce() {
|
|
78
|
+
const dir = snapDir();
|
|
79
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
80
|
+
let img = await grabScreenImage();
|
|
81
|
+
if (img.getSize().width > MAX_W) {
|
|
82
|
+
img = img.resize({ width: MAX_W, quality: "good" }); // width-only → keeps aspect
|
|
83
|
+
}
|
|
84
|
+
const jpeg = img.toJPEG(QUALITY);
|
|
85
|
+
if (!jpeg || jpeg.length < 256) throw new Error("encoded jpeg too small");
|
|
86
|
+
fs.writeFileSync(path.join(dir, "desktop.jpg"), jpeg);
|
|
87
|
+
const b64Path = path.join(dir, "desktop.b64");
|
|
88
|
+
const tmpB64 = b64Path + ".tmp";
|
|
89
|
+
fs.writeFileSync(tmpB64, jpeg.toString("base64"));
|
|
90
|
+
fs.renameSync(tmpB64, b64Path); // atomic swap so readers never see a partial file
|
|
91
|
+
const o = img.getSize();
|
|
92
|
+
return { dir, w: o.width, h: o.height, bytes: jpeg.length };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// One-shot in-process capture that RETURNS the base64 JPEG (does not touch disk).
|
|
96
|
+
// Used by the `desktop_snapshot` RPC tool as the live fallback when the daemon's
|
|
97
|
+
// desktop.b64 file is missing/stale. NOT valid on win32 in the main process —
|
|
98
|
+
// desktopCapturer needs the --disable-gpu daemon there (see grabScreenImage);
|
|
99
|
+
// the tool guards that and reads the daemon file instead.
|
|
100
|
+
async function captureB64(maxWidth) {
|
|
101
|
+
const mw = maxWidth > 0 ? maxWidth : MAX_W;
|
|
102
|
+
let img = await grabScreenImage();
|
|
103
|
+
const o = img.getSize();
|
|
104
|
+
if (o.width > mw) img = img.resize({ width: mw, quality: "good" });
|
|
105
|
+
const jpeg = img.toJPEG(QUALITY);
|
|
106
|
+
if (!jpeg || jpeg.length < 256) throw new Error("encoded jpeg too small");
|
|
107
|
+
return { b64: jpeg.toString("base64"), w: o.width, h: o.height, bytes: jpeg.length };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── parent (started from main.js) ─────────────────────────────────────────────
|
|
111
|
+
let child = null;
|
|
112
|
+
let timer = null;
|
|
113
|
+
let kickTimer = null;
|
|
114
|
+
let running = false;
|
|
115
|
+
|
|
116
|
+
async function tick() {
|
|
117
|
+
if (running) return;
|
|
118
|
+
running = true;
|
|
119
|
+
try { await captureOnce(); } catch (_) { /* keep last good file */ }
|
|
120
|
+
finally { running = false; }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function startDesktopSnapshots(options = {}) {
|
|
124
|
+
const ms = intervalMs(options);
|
|
125
|
+
stopDesktopSnapshots();
|
|
126
|
+
|
|
127
|
+
if (process.platform === "win32") {
|
|
128
|
+
// Spawn ONE persistent --disable-gpu child electron that runs this file as a
|
|
129
|
+
// capture daemon (GDI path, works over RDP). Main app's GPU is untouched.
|
|
130
|
+
const dir = snapDir();
|
|
131
|
+
try { fs.mkdirSync(dir, { recursive: true }); } catch (_) {}
|
|
132
|
+
let stdio = "ignore";
|
|
133
|
+
let logFd = null;
|
|
134
|
+
try { logFd = fs.openSync(path.join(dir, "daemon.log"), "a"); stdio = ["ignore", logFd, logFd]; } catch (_) {}
|
|
135
|
+
child = spawn(process.execPath, ["--disable-gpu", "--disable-logging", __filename], {
|
|
136
|
+
env: { ...process.env, CICY_SNAP_DAEMON: "1", CICY_DESKTOP_SNAP_INTERVAL_MS: String(ms) },
|
|
137
|
+
stdio,
|
|
138
|
+
windowsHide: true,
|
|
139
|
+
});
|
|
140
|
+
child.on("exit", () => { child = null; });
|
|
141
|
+
return { dir, intervalMs: ms, maxWidth: MAX_W, mode: "win-daemon", execPath: process.execPath };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// mac / linux: in-process native capture loop (no GPU concern).
|
|
145
|
+
const kick = () => { tick().catch(() => {}); };
|
|
146
|
+
timer = setInterval(kick, ms);
|
|
147
|
+
if (timer.unref) timer.unref();
|
|
148
|
+
kickTimer = setTimeout(kick, 2500);
|
|
149
|
+
if (kickTimer.unref) kickTimer.unref();
|
|
150
|
+
return { dir: snapDir(), intervalMs: ms, maxWidth: MAX_W, mode: "inproc" };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function stopDesktopSnapshots() {
|
|
154
|
+
if (timer) { clearInterval(timer); timer = null; }
|
|
155
|
+
if (kickTimer) { clearTimeout(kickTimer); kickTimer = null; }
|
|
156
|
+
if (child) { try { child.kill(); } catch (_) {} child = null; }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── daemon mode ───────────────────────────────────────────────────────────────
|
|
160
|
+
// Entered when this file is the entry of `electron --disable-gpu
|
|
161
|
+
// desktop-snapshot.js` (Windows capture child). Detected purely via the env flag
|
|
162
|
+
// the parent sets — NOT require.main, which is `undefined` in Electron's main
|
|
163
|
+
// process. The main worker requires this module WITHOUT that env, so it's skipped
|
|
164
|
+
// there.
|
|
165
|
+
if (process.env.CICY_SNAP_DAEMON === "1") {
|
|
166
|
+
const { app } = electron;
|
|
167
|
+
try { app.disableHardwareAcceleration(); } catch (_) {}
|
|
168
|
+
let firstOk = true;
|
|
169
|
+
let lastErr = "";
|
|
170
|
+
const once = async () => {
|
|
171
|
+
try {
|
|
172
|
+
const r = await captureOnce();
|
|
173
|
+
if (firstOk) { firstOk = false; console.error("[snap-daemon] capturing → " + JSON.stringify(r)); }
|
|
174
|
+
} catch (e) {
|
|
175
|
+
const msg = (e && e.message) || String(e);
|
|
176
|
+
if (msg !== lastErr) { lastErr = msg; console.error("[snap-daemon] ERR " + msg); } // dedupe spam
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
console.error("[snap-daemon] booting, dir=" + snapDir());
|
|
180
|
+
app.whenReady().then(() => {
|
|
181
|
+
once();
|
|
182
|
+
const t = setInterval(once, intervalMs());
|
|
183
|
+
if (t.unref) t.unref();
|
|
184
|
+
});
|
|
185
|
+
// No window is ever created; the interval keeps the process alive. Quit if the
|
|
186
|
+
// GPU/renderer dies so we don't linger as a zombie.
|
|
187
|
+
app.on("render-process-gone", () => app.quit());
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = { startDesktopSnapshots, stopDesktopSnapshots, snapDir, captureOnce, captureB64, MAX_W };
|
|
@@ -1729,7 +1729,7 @@ function LocalTeamCard({ team, onOpen, onRename, onRefresh }) {
|
|
|
1729
1729
|
onFocus={(e) => e.target.select()}
|
|
1730
1730
|
onBlur={commit}
|
|
1731
1731
|
onClick={(e) => e.stopPropagation()}
|
|
1732
|
-
onKeyDown={(e) => { if (e.key === "Enter") commit(); else if (e.key === "Escape") setEditing(false); }}
|
|
1732
|
+
onKeyDown={(e) => { if (e.nativeEvent.isComposing || e.keyCode === 229) return; if (e.key === "Enter") commit(); else if (e.key === "Escape") setEditing(false); }}
|
|
1733
1733
|
style={{ width: "100%", font: "inherit", fontWeight: 600, padding: "2px 6px", border: "1px solid #3b82f6", borderRadius: 6, background: "#0d1117", color: "#e6edf3", boxSizing: "border-box" }}
|
|
1734
1734
|
/>
|
|
1735
1735
|
) : (
|