@shaohui_jin/git-cockpit-mcp-server 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-4QEU6AVI.js → chunk-4WZKRLKI.js} +2 -2
- package/dist/{chunk-ASBVWOQY.js → chunk-5CZ3BO3H.js} +33 -2
- package/dist/chunk-5CZ3BO3H.js.map +1 -0
- package/dist/cli-entry.js +2 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/mcpServer.d.ts +1 -1
- package/dist/{runtime-CmdOmv60.d.ts → runtime-BzkM1IAj.d.ts} +2 -0
- package/dist/web/assets/{index-CBdX3yjE.css → index-CNUTv7F2.css} +1 -1
- package/dist/web/assets/index-XiZddJPB.js +4 -0
- package/dist/web/index.html +2 -2
- package/dist/webServer.d.ts +1 -1
- package/dist/webServer.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-ASBVWOQY.js.map +0 -1
- package/dist/web/assets/index-CSq1S2W2.js +0 -4
- /package/dist/{chunk-4QEU6AVI.js.map → chunk-4WZKRLKI.js.map} +0 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{az as We,c as E,G as oe,d as ce,a6 as Wt,O as S,W as t,aj as g,aA as Mt,aB as Me,L as o,a0 as l,P as i,$ as y,u as L,M as k,F as M,ae as Q,_ as p,Y as Gt,k as O,aq as Ge,Z as R,S as he,w as ue,i as Ht,e as Fe,X as ge,ac as Te,ak as Ee,aC as jt,aD as Jt,aw as Kt,aE as Yt}from"./vue-CYNDxJyv.js";import{E as W,a as He,e as Xt}from"./element-plus-D8uC5V5h.js";(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))h(u);new MutationObserver(u=>{for(const C of u)if(C.type==="childList")for(const D of C.addedNodes)D.tagName==="LINK"&&D.rel==="modulepreload"&&h(D)}).observe(document,{childList:!0,subtree:!0});function r(u){const C={};return u.integrity&&(C.integrity=u.integrity),u.referrerPolicy&&(C.referrerPolicy=u.referrerPolicy),u.crossOrigin==="use-credentials"?C.credentials="include":u.crossOrigin==="anonymous"?C.credentials="omit":C.credentials="same-origin",C}function h(u){if(u.ep)return;u.ep=!0;const C=r(u);fetch(u.href,C)}})();class ut extends Error{constructor(n,r,h,u){super(n),this.status=r,this.code=h,this.requiredApproval=u,this.name="ApiError"}}async function K(a,n,r){let h;try{h=await fetch(n,{method:a,headers:r!==void 0?{"Content-Type":"application/json"}:void 0,body:r!==void 0?JSON.stringify(r):void 0})}catch(u){throw new ut(`无法连接后端服务(${n}):${u instanceof Error?u.message:String(u)}`,0)}if(!h.ok){let u=h.statusText,C,D;try{const N=await h.json();u=N.error??u,C=N.code,D=N.requiredApproval}catch{}throw new ut(u,h.status,C,D)}return await h.json()}function Zt(){return K("GET","/api/repos")}function Qt(a){return K("POST","/api/repos/open",{path:a})}function el(a){return K("POST",`/api/repos/${a}/activate`)}function tl(a){return K("DELETE",`/api/repos/${a}`)}function ll(a){return K("GET",`/api/repos/${a}/status`)}function nl(a,n={}){const r=new URLSearchParams;n.maxCount&&r.set("maxCount",String(n.maxCount)),n.all&&r.set("all","true"),n.path&&r.set("path",n.path),n.from&&r.set("from",n.from),n.to&&r.set("to",n.to),n.author&&r.set("author",n.author);const h=r.toString();return K("GET",`/api/repos/${a}/log${h?`?${h}`:""}`)}function sl(a,n={}){const r=new URLSearchParams;n.from&&r.set("from",n.from),n.to&&r.set("to",n.to),n.path&&r.set("path",n.path),n.staged&&r.set("staged","true");const h=r.toString();return K("GET",`/api/repos/${a}/diff${h?`?${h}`:""}`)}function al(a,n){return K("GET",`/api/repos/${a}/show/${encodeURIComponent(n)}`)}function ct(a){return K("GET",`/api/repos/${a}/branches`)}function ol(a){return K("GET",`/api/repos/${a}/stashes`)}function Oe(a,n,r={}){return K("POST",`/api/repos/${a}/tools/${n}`,{params:r})}function il(a={}){const n=new URLSearchParams;a.limit&&n.set("limit",String(a.limit)),a.tool&&n.set("tool",a.tool);const r=n.toString();return K("GET",`/api/logs${r?`?${r}`:""}`)}function rl(){return K("GET","/api/settings")}function ul(a){return K("PUT","/api/settings",{permissions:a})}function dl(){return K("GET","/api/health")}function cl(a){const n=new EventSource("/api/events");return a.onRepoChanged&&n.addEventListener("repo-changed",r=>{var h;return(h=a.onRepoChanged)==null?void 0:h.call(a,JSON.parse(r.data))}),a.onLog&&n.addEventListener("log",()=>{var r;return(r=a.onLog)==null?void 0:r.call(a)}),a.onError&&(n.onerror=r=>{var h;return(h=a.onError)==null?void 0:h.call(a,r)}),()=>n.close()}const qe="git-cockpit:currentRepoId";function dt(){try{const a=Number(window.localStorage.getItem(qe));return Number.isInteger(a)&&a>0?a:null}catch{return null}}function Pe(a){try{a===null?window.localStorage.removeItem(qe):window.localStorage.setItem(qe,String(a))}catch{}}const ze=We("repos",{state:()=>({repos:[],currentId:dt(),loading:!1,error:null,healthOk:null}),getters:{current(a){return a.repos.find(n=>n.id===a.currentId)??null},currentPath(a){var n;return((n=a.repos.find(r=>r.id===a.currentId))==null?void 0:n.path)??null}},actions:{async checkHealth(){try{const a=await dl();this.healthOk=a.ok}catch{this.healthOk=!1}return this.healthOk===!0},async load(){var a;this.loading=!0,this.error=null;try{const{repos:n}=await Zt();this.repos=n;const r=dt();r!==null&&n.some(h=>h.id===r)?this.currentId=r:(!this.currentId||!n.some(h=>h.id===this.currentId))&&(this.currentId=((a=n[0])==null?void 0:a.id)??null),Pe(this.currentId)}catch(n){this.error=n instanceof Error?n.message:String(n)}finally{this.loading=!1}},async open(a){const{repo:n}=await Qt(a);return await this.load(),n},async activate(a){const{repo:n}=await el(a);return this.currentId=a,Pe(a),await this.load(),n},async remove(a){await tl(a),this.currentId===a&&(this.currentId=null),await this.load()},switchTo(a){this.currentId=a,Pe(a)}}}),ft=We("settings",{state:()=>({permissions:null,tools:[],loading:!1,saving:!1,error:null}),actions:{async load(){this.loading=!0,this.error=null;try{const a=await rl();this.permissions=a.permissions,this.tools=a.tools}catch(a){this.error=a instanceof Error?a.message:String(a)}finally{this.loading=!1}},async save(a){if(this.permissions){this.saving=!0,this.error=null;try{await ul(a),this.permissions={...this.permissions,...a}}catch(n){throw this.error=n instanceof Error?n.message:String(n),n}finally{this.saving=!1}}}}}),pt=E(0),fl=()=>{pt.value+=1};function De(){return{revision:pt,bump:fl}}const pl={class:"repo-box"},ml=["title"],vl={class:"menu-icon"},_l={class:"aside-footer"},hl=oe({__name:"App",setup(a){const n=ze(),r=ft(),h=Mt(),u=Me(),{bump:C}=De(),D=[{path:"/status",label:"状态",icon:"◧"},{path:"/history",label:"历史",icon:"◫"},{path:"/repos",label:"仓库管理",icon:"▤"},{path:"/logs",label:"操作日志",icon:"≡"},{path:"/settings",label:"设置",icon:"⚙"}],N=O(()=>{if(!n.currentId)return"未选择仓库";const c=n.repos.find(d=>d.id===n.currentId);return(c==null?void 0:c.path)??"未选择仓库"});let U=null;return ce(async()=>{await n.checkHealth(),await Promise.all([n.load(),r.load().catch(()=>{})]),U=cl({onRepoChanged:()=>C(),onLog:()=>C(),onError:()=>{}})}),Wt(()=>{U==null||U()}),(c,d)=>{const f=g("el-menu-item"),_=g("el-menu"),m=g("el-tag"),x=g("el-aside"),A=g("router-view"),q=g("el-main"),H=g("el-container");return o(),S(H,{class:"app-shell"},{default:t(()=>[l(x,{width:"220px",class:"app-aside"},{default:t(()=>[d[4]||(d[4]=i("div",{class:"brand"},[i("span",{class:"brand-mark"},"⌘"),i("span",{class:"brand-name"},"Git Cockpit")],-1)),i("div",pl,[d[0]||(d[0]=i("div",{class:"repo-box-label"},"当前仓库",-1)),i("div",{class:"repo-box-path mono",title:N.value},y(N.value),9,ml)]),l(_,{"default-active":L(h).path,class:"nav-menu"},{default:t(()=>[(o(),k(M,null,Q(D,I=>l(f,{key:I.path,index:I.path,onClick:j=>L(u).push(I.path)},{default:t(()=>[i("span",vl,y(I.icon),1),i("span",null,y(I.label),1)]),_:2},1032,["index","onClick"])),64))]),_:1},8,["default-active"]),i("div",_l,[L(n).healthOk?(o(),S(m,{key:0,size:"small",type:"success"},{default:t(()=>[...d[1]||(d[1]=[p("后端已连接",-1)])]),_:1})):L(n).healthOk===!1?(o(),S(m,{key:1,size:"small",type:"danger"},{default:t(()=>[...d[2]||(d[2]=[p("后端离线",-1)])]),_:1})):(o(),S(m,{key:2,size:"small",type:"info"},{default:t(()=>[...d[3]||(d[3]=[p("连接中…",-1)])]),_:1}))])]),_:1}),l(H,{class:"app-main"},{default:t(()=>[l(q,{class:"main-content"},{default:t(()=>[l(A,null,{default:t(({Component:I})=>[(o(),S(Gt(I)))]),_:1})]),_:1})]),_:1})]),_:1})}}}),ie=(a,n)=>{const r=a.__vccOpts||a;for(const[h,u]of n)r[h]=u;return r},gl=ie(hl,[["__scopeId","data-v-17b27ba6"]]),yl={class:"page"},bl={class:"open-row"},kl={class:"list-header"},wl={class:"repo-path mono"},Cl=oe({__name:"ReposView",setup(a){const n=ze(),r=Me(),h=E(""),u=E(!1);async function C(){const d=h.value.trim();if(!d){W.warning("请输入仓库路径");return}u.value=!0;try{const f=await n.open(d);W.success(`已打开 ${f.path}`),n.switchTo(f.id),await r.push("/status")}catch(f){W.error(f instanceof Error?f.message:String(f))}finally{u.value=!1}}async function D(d,f){try{await He.confirm(`确定从列表移除 ${f}?注意:不会删除磁盘上的仓库文件。`,"移除仓库",{type:"warning",confirmButtonText:"移除",cancelButtonText:"取消"})}catch{return}try{await n.remove(d),W.success("已移除")}catch(_){W.error(_ instanceof Error?_.message:String(_))}}async function N(d){try{await n.activate(d)}catch{n.switchTo(d)}r.push("/status")}function U(d){const f=new Date(d);return Number.isNaN(f.getTime())?d:f.toLocaleString()}function c({row:d}){return d.id===n.currentId?"is-current":""}return ce(()=>n.load()),(d,f)=>{const _=g("el-input"),m=g("el-button"),x=g("el-card"),A=g("el-empty"),q=g("el-table-column"),H=g("el-table");return o(),k("div",yl,[f[8]||(f[8]=i("h2",{class:"page-title"},"仓库管理",-1)),l(x,{class:"open-card",shadow:"never"},{header:t(()=>[...f[2]||(f[2]=[p("打开本地仓库",-1)])]),default:t(()=>[i("div",bl,[l(_,{modelValue:h.value,"onUpdate:modelValue":f[0]||(f[0]=I=>h.value=I),placeholder:"输入本地 Git 仓库路径,例如 D:\\project\\repo 或 /Users/me/code/app",clearable:"",onKeyup:Ge(C,["enter"])},null,8,["modelValue"]),l(m,{type:"primary",loading:u.value,onClick:C},{default:t(()=>[...f[3]||(f[3]=[p("打开",-1)])]),_:1},8,["loading"])])]),_:1}),l(x,{shadow:"never",class:"list-card"},{header:t(()=>[i("div",kl,[f[5]||(f[5]=i("span",null,"最近打开的仓库",-1)),l(m,{text:"",type:"primary",loading:L(n).loading,onClick:f[1]||(f[1]=I=>L(n).load())},{default:t(()=>[...f[4]||(f[4]=[p("刷新",-1)])]),_:1},8,["loading"])])]),default:t(()=>[!L(n).loading&&L(n).repos.length===0?(o(),S(A,{key:0,description:"尚未打开任何仓库"})):(o(),S(H,{key:1,data:L(n).repos,size:"default","highlight-current-row":"","row-class-name":c},{default:t(()=>[l(q,{label:"路径","min-width":"320"},{default:t(({row:I})=>[i("div",wl,y(I.path),1)]),_:1}),l(q,{label:"最近打开",width:"190"},{default:t(({row:I})=>[p(y(U(I.lastOpenedAt)),1)]),_:1}),l(q,{label:"操作",width:"180",align:"right"},{default:t(({row:I})=>[l(m,{size:"small",text:"",type:"primary",onClick:j=>N(I.id)},{default:t(()=>[...f[6]||(f[6]=[p("进入",-1)])]),_:1},8,["onClick"]),l(m,{size:"small",text:"",type:"danger",onClick:j=>D(I.id,I.path)},{default:t(()=>[...f[7]||(f[7]=[p("移除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"]))]),_:1})])}}}),$l=ie(Cl,[["__scopeId","data-v-0ea529f0"]]);function xl(a){const n=Me(),r=E(!1),h=E(null),u=E(!1),C=O(()=>a()!==null);async function D(f,_={}){const m=a();if(m===null)return W.warning("请先选择仓库"),!1;try{const x=await Oe(m,f,{..._,dryRun:!0});return x.success?(h.value={tool:f,params:_,preview:x.preview??x.result},r.value=!0,!0):(c(x),!1)}catch(x){return d(x),!1}}async function N(){const f=a(),_=h.value;if(f===null||!_)return null;u.value=!0,r.value=!1;try{const m=await Oe(f,_.tool,_.params);return m.success?(m.backupCreated?W.success(`${_.tool} 执行成功(已自动备份:${m.backupCreated.branch??"分支"}${m.backupCreated.stashRef?` / ${m.backupCreated.stashRef}`:""})`):W.success(`${_.tool} 执行成功`),m):(c(m),m)}catch(m){return d(m),null}finally{u.value=!1,h.value=null}}function U(){r.value=!1,h.value=null}function c(f){var _,m,x;if(((_=f.error)==null?void 0:_.code)==="PERMISSION_DENIED"){f.error.requiredApproval?He.confirm(`操作 ${f.tool} 属于高风险操作,当前默认禁用,需要在设置中开启后才能执行。是否前往设置?`,"需要审批",{confirmButtonText:"去开启",cancelButtonText:"取消",type:"warning"}).then(()=>n.push("/settings")).catch(()=>{}):W.error(((m=f.error)==null?void 0:m.message)??"操作被拒绝");return}W.error(((x=f.error)==null?void 0:x.message)??"操作失败")}function d(f){const _=f instanceof Error?f.message:String(f);W.error(_)}return{confirmVisible:r,pending:h,executing:u,canRun:C,previewAndConfirm:D,executeConfirmed:N,cancel:U}}const Vl={key:2,class:"cmd-box"},Sl={class:"cmd-pre mono"},Tl={key:3,class:"files-box"},El={class:"cmd-label"},zl={key:4,class:"note-text"},Dl={class:"risk-tag"},Il=oe({__name:"ConfirmDialog",props:{visible:{type:Boolean},tool:{},preview:{default:null},note:{default:""}},emits:["update:visible","confirm","cancel"],setup(a,{emit:n}){const r=a,h=n,u=O(()=>{const d=r.preview;return d&&"command"in d&&typeof d.command=="string"?d.command:""}),C=O(()=>{const d=r.preview;return d&&"risk"in d&&typeof d.risk=="string"?d.risk:"write"}),D=O(()=>{const d=r.preview;return d&&"affectedFiles"in d&&Array.isArray(d.affectedFiles)?d.affectedFiles:[]}),N=O(()=>C.value==="dangerous"?{type:"danger",label:"高风险"}:C.value==="write"?{type:"warning",label:"写操作"}:{type:"info",label:"只读"});function U(){h("confirm")}function c(){h("cancel"),h("update:visible",!1),W.info("操作已取消")}return(d,f)=>{const _=g("el-alert"),m=g("el-scrollbar"),x=g("el-tag"),A=g("el-button"),q=g("el-dialog");return o(),S(q,{"model-value":a.visible,title:`执行操作:${a.tool}`,width:"560px","close-on-click-modal":!1,onClosed:f[0]||(f[0]=H=>h("update:visible",!1))},{footer:t(()=>[i("span",Dl,[l(x,{type:N.value.type,size:"small",effect:"dark"},{default:t(()=>[p(y(N.value.label),1)]),_:1},8,["type"])]),l(A,{onClick:c},{default:t(()=>[...f[4]||(f[4]=[p("取消",-1)])]),_:1}),l(A,{type:"primary",danger:C.value==="dangerous",onClick:U},{default:t(()=>[...f[5]||(f[5]=[p("确认执行",-1)])]),_:1},8,["danger"])]),default:t(()=>[C.value==="dangerous"?(o(),S(_,{key:0,type:"error",closable:!1,"show-icon":"",class:"risk-alert"},{title:t(()=>[...f[1]||(f[1]=[p("这是",-1),i("b",null,"高风险",-1),p("操作,将自动创建备份(临时分支 + stash)",-1)])]),_:1})):C.value==="write"?(o(),S(_,{key:1,type:"warning",closable:!1,"show-icon":"",class:"risk-alert"},{title:t(()=>[...f[2]||(f[2]=[p("写操作:执行后将修改仓库状态",-1)])]),_:1})):R("",!0),u.value?(o(),k("div",Vl,[f[3]||(f[3]=i("div",{class:"cmd-label"},"将执行的命令(dry-run 预览,未实际执行)",-1)),i("pre",Sl,y(u.value),1)])):R("",!0),D.value.length?(o(),k("div",Tl,[i("div",El,"涉及文件("+y(D.value.length)+")",1),l(m,{"max-height":"140px"},{default:t(()=>[(o(!0),k(M,null,Q(D.value,H=>(o(),k("div",{key:H,class:"file-item mono"},y(H),1))),128))]),_:1})])):R("",!0),a.note?(o(),k("div",zl,y(a.note),1)):R("",!0)]),_:1},8,["model-value","title"])}}}),Rl=ie(Il,[["__scopeId","data-v-5d42c09b"]]),Ll={class:"diff-pre mono"},Nl=oe({__name:"DiffViewer",props:{patch:{}},setup(a){const n=a,r=O(()=>{const h=[];for(const u of n.patch.split(`
|
|
2
|
+
`)){const C=u.startsWith("diff --git")||u.startsWith("index ")||u.startsWith("---")||u.startsWith("+++")||u.startsWith("new file")||u.startsWith("deleted file")||u.startsWith("similarity")||u.startsWith("rename ")?"meta":u.startsWith("@@")?"hunk":u.startsWith("+")?"add":u.startsWith("-")?"del":"plain";h.push({type:C,text:u})}return h});return(h,u)=>(o(),k("pre",Ll,[(o(!0),k(M,null,Q(r.value,(C,D)=>(o(),k(M,{key:D},[i("span",{class:he(C.type)},y(C.text),3),u[0]||(u[0]=p(`
|
|
3
|
+
`,-1))],64))),128))]))}}),Be=ie(Nl,[["__scopeId","data-v-cd09efab"]]),Al={class:"page status-page"},Ul={class:"page-head"},Pl={class:"head-branch"},Fl={key:0,class:"tracking mono"},Ol={key:1,class:"sync ahead"},ql={key:2,class:"sync behind"},Bl={class:"repo-path mono"},Wl={key:1,class:"empty-tip"},Ml={key:2,class:"status-layout"},Gl={class:"panel-head"},Hl={class:"panel-actions"},jl={class:"tree-scroll"},Jl={class:"group-label"},Kl={key:0,class:"tree-dir"},Yl=["title","onDblclick"],Xl={class:"node-name"},Zl={class:"node-actions"},Ql={key:1,class:"pane-empty"},en={class:"status-content"},tn={class:"toolbar"},ln={class:"toolbar-group"},nn={class:"card-head"},sn={class:"card-actions"},an={class:"tab-label"},on={class:"tab-hint"},rn={key:0,class:"file-empty"},un={key:1,class:"file-list"},dn=["title"],cn={class:"card-head"},fn={class:"card-title"},pn={class:"card-actions"},mn={key:0,class:"file-empty"},vn={key:1,class:"stash-list mono"},_n={class:"stash-main"},hn={class:"stash-ref"},gn=["title"],yn={key:0,class:"stash-date"},bn={class:"stash-actions"},kn={class:"diff-wrap"},wn={class:"diff-summary"},Cn={class:"add"},$n={class:"del"},xn={key:0,class:"warn"},Vn={key:0,class:"file-empty"},Sn={key:0,class:"stash-files"},Tn={class:"stash-files-head"},En={class:"stash-files-title"},zn={class:"card-actions"},Dn={class:"file-list compact"},In={class:"file-path mono"},Rn=oe({__name:"StatusView",setup(a){const n=ze(),{revision:r}=De(),h=()=>n.currentId,u=E(null),C=E([]),D=E([]),N=E(!1),U=E(""),c=Fe({visible:!1,title:"",diff:null,patch:"",loading:!1}),d=E(!1),f=E(""),_=E(!1),m=E(""),x=E(""),A=E(!1),q=E(""),H=E(!1),I=E(!1),j=E(""),w=Fe(new Set),ne=O(()=>w.size),ae=E("unstaged"),{confirmVisible:B,pending:$,canRun:ee,previewAndConfirm:F,executeConfirmed:fe,cancel:de}=xl(h),ye=O(()=>{var s;return((s=n.current)==null?void 0:s.path)??""}),Re=O(()=>{var s,e;return((s=u.value)==null?void 0:s.currentShort)??((e=u.value)==null?void 0:e.current)??""}),be=O(()=>{var s;return((s=u.value)==null?void 0:s.ahead)??0}),pe=O(()=>{var s;return((s=u.value)==null?void 0:s.behind)??0}),X=O(()=>{const s=u.value;if(!s)return{unstaged:[],staged:[],untracked:[],all:[]};const e=s.untracked.map(G=>({path:G,status:"??",indexStatus:"?",workTreeStatus:"?",staged:!1,untracked:!0,conflicted:!1})),T=[...s.staged,...s.unstaged,...e],b=new Set,z=T.filter(G=>b.has(G.path)?!1:(b.add(G.path),G.path!==""));return{unstaged:s.unstaged,staged:s.staged,untracked:e,all:z}}),Le=O(()=>[{key:"unstaged",label:"未暂存",hint:"已修改,未 git add",count:X.value.unstaged.length},{key:"staged",label:"已暂存",hint:"已 git add,未提交",count:X.value.staged.length},{key:"untracked",label:"未跟踪",hint:"工作区新文件",count:X.value.untracked.length},{key:"all",label:"全部",hint:"全部更改",count:X.value.all.length}]),Ne=O(()=>X.value[ae.value]??[]),ke=O(()=>X.value.all.length);function we(s){const e=[];for(const{name:T,branch:b}of s){const z=T.split("/").filter(te=>te.length>0);if(z.length===0)continue;let G=e,Y="";for(const[te,re]of z.entries()){Y=Y?`${Y}/${re}`:re;const xe=G.find(Ae=>Ae.label===re),le=xe??{key:"node:"+Y,label:re};xe||G.push(le),te===z.length-1?(le.branch=b,le.fullName=T,le.remote=b.remote):(le.children||(le.children=[]),G=le.children)}}return Ce(e),e}function Ce(s){s.sort((e,T)=>{const b=e.branch?1:0,z=T.branch?1:0;return b!==z?b-z:e.label.localeCompare(T.label)});for(const e of s)e.children&&Ce(e.children)}function me(s){let e=0;for(const T of s)e+=T.branch?1:me(T.children??[]);return e}const P=O(()=>{const s=C.value.filter(z=>!z.remote),e=C.value.filter(z=>z.remote),T=new Map;for(const z of e){const G=z.name.indexOf("/"),Y=G===-1?"":z.name.slice(0,G);if(!Y)continue;const te=T.get(Y)??[];te.push({name:z.name.slice(G+1),branch:z}),T.set(Y,te)}const b=[];s.length&&b.push({key:"local",title:`本地分支(${s.length})`,tree:we(s.map(z=>({name:z.name,branch:z})))});for(const[z,G]of T){const Y=we(G);b.push({key:"remote:"+z,title:`远程 · ${z}(${me(Y)})`,tree:Y})}return b});function Z(s){if(!s||s.remote||!s.upstream)return"";const e=[];return(s.ahead??0)>0&&e.push(`↑${s.ahead}`),(s.behind??0)>0&&e.push(`↓${s.behind}`),e.join(" ")}async function vt(){const s=h();if(s!==null){N.value=!0,U.value="";try{u.value=await ll(s)}catch(e){U.value=e instanceof Error?e.message:String(e),u.value=null}finally{N.value=!1}}}async function je(){const s=h();if(s!==null)try{const{branches:e}=await ct(s);C.value=e}catch{}}async function Je(){const s=h();if(s!==null)try{D.value=await ol(s)}catch{}}async function ve(){await Promise.all([vt(),je(),Je()])}async function J(s,e={}){await F(s,e)&&W.info("请在确认对话框中查看 dry-run 预览")}async function _t(){const s=await fe();s!=null&&s.success&&await ve()}function Ke(s){J("git_add",{paths:[s.path]})}function ht(s){J("git_unstage",{paths:[s.path]})}function gt(s){s.staged?ht(s):Ke(s)}function yt(){J("git_add",{})}function bt(){var e;const s=((e=u.value)==null?void 0:e.staged.map(T=>T.path))??[];if(s.length===0){W.info("没有已暂存的文件");return}J("git_unstage",{paths:s})}async function kt(s){const e=h();if(e!==null){c.title=`${s.path}(${s.staged?"已 git add":"工作区修改"})`,c.diff=null,c.patch="",c.visible=!0,c.loading=!0;try{c.diff=await sl(e,{path:s.path,staged:s.staged})}catch(T){W.error(T instanceof Error?T.message:String(T)),c.visible=!1}finally{c.loading=!1}}}async function wt(s){var T;const e=h();if(e!==null){c.title=`${s.ref} · ${s.message||"(无说明)"}`,c.diff=null,c.patch="",c.visible=!0,c.loading=!0;try{const b=await Oe(e,"git_stash_show",{index:s.index});if(b.success){const z=b.result??{};c.patch=z.patch??""}else W.error(((T=b.error)==null?void 0:T.message)??"查看差异失败"),c.visible=!1}catch(b){W.error(b instanceof Error?b.message:String(b)),c.visible=!1}finally{c.loading=!1}}}function Ye(s){w.has(s)?w.delete(s):w.add(s)}function Xe(s){return w.has(s)}function Ze(){w.clear()}function Ct(){for(const s of Ne.value)w.add(s.path)}function $t(){for(const s of $e.value)w.add(s)}const $e=O(()=>{const s=u.value;if(!s)return[];const e=new Set;return[...s.unstaged.map(T=>T.path),...s.untracked].filter(T=>e.has(T)?!1:(e.add(T),T!==""))});function xt(){A.value=!0}function Vt(){const s=$e.value,e=[...w].filter(z=>s.includes(z)),T=e.some(z=>{var G;return((G=u.value)==null?void 0:G.untracked.includes(z))??!1}),b={};q.value.trim()&&(b.message=q.value.trim()),e.length&&(b.paths=e),(H.value||T)&&(b.includeUntracked=!0),A.value=!1,w.clear(),J("git_stash",b)}function St(){J("git_stash",{})}function Tt(s){J("git_stash_apply",{index:s.index})}function Et(s){J("git_stash_pop",{index:s.index})}function zt(s){He.confirm(`确定删除 ${s.ref} 吗?删除后不可直接恢复。`,"删除 stash",{type:"warning",confirmButtonText:"继续",cancelButtonText:"取消"}).then(()=>void J("git_stash_drop",{index:s.index})).catch(()=>{})}function Qe(s){s.branch&&J("git_checkout",{branch:s.branch.name})}function Dt(){J("git_pull")}function It(){J("git_push")}function et(){const s=f.value.trim();if(!s){W.warning("请输入提交信息");return}d.value=!1,J("git_commit",{message:s})}function Rt(){const s=m.value.trim();if(!s){W.warning("请输入分支名");return}_.value=!1,J("git_branch_create",x.value.trim()?{name:s,startPoint:x.value.trim()}:{name:s})}function Lt(){const s=j.value.trim()||"HEAD";I.value=!1,j.value="",J("git_reset_hard",{target:s})}function Nt(s){return s.untracked?"?":s.staged?s.indexStatus||"M":s.workTreeStatus||"M"}function At(s){return s.untracked?"new":s.conflicted?"conflict":s.staged?"add":"mod"}function Ut(s){if(!s)return"";const e=new Date(s);return Number.isNaN(e.getTime())?s:e.toLocaleString()}function Pt(s){return s.split(`
|
|
4
|
+
`)[0]??""}return ue(h,()=>{c.diff=null,c.patch="",c.visible=!1,w.clear(),ve()}),ue(r,()=>{ve()}),ce(()=>{ve()}),(s,e)=>{var lt,nt,st,at,ot;const T=g("el-tag"),b=g("el-button"),z=g("el-alert"),G=g("el-tree"),Y=g("el-empty"),te=g("el-card"),re=g("el-badge"),xe=g("el-tooltip"),le=g("el-checkbox"),Ae=g("el-tab-pane"),Ft=g("el-tabs"),Ot=g("el-drawer"),_e=g("el-input"),Ve=g("el-dialog"),Se=g("el-form-item"),tt=g("el-form"),qt=Ee("loading");return o(),k("div",Al,[i("div",Ul,[e[26]||(e[26]=i("h2",{class:"page-title"},"仓库状态",-1)),i("div",Pl,[l(T,{type:"primary",effect:"dark",class:"branch-tag"},{default:t(()=>[p(y(Re.value||"—"),1)]),_:1}),(lt=u.value)!=null&<.tracking?(o(),k("span",Fl,y(u.value.tracking),1)):R("",!0),be.value?(o(),k("span",Ol,"↑"+y(be.value),1)):R("",!0),pe.value?(o(),k("span",ql,"↓"+y(pe.value),1)):R("",!0),(nt=u.value)!=null&&nt.isClean?(o(),S(T,{key:3,type:"success",effect:"plain",size:"small"},{default:t(()=>[...e[24]||(e[24]=[p("工作区干净",-1)])]),_:1})):u.value?(o(),S(T,{key:4,type:"warning",effect:"plain",size:"small"},{default:t(()=>[p(y(ke.value)+" 处更改",1)]),_:1})):R("",!0),i("span",Bl,y(ye.value),1)]),l(b,{loading:N.value,onClick:ve},{default:t(()=>[...e[25]||(e[25]=[p("刷新",-1)])]),_:1},8,["loading"])]),U.value?(o(),S(z,{key:0,title:U.value,type:"error",closable:!1,"show-icon":"",class:"mb"},null,8,["title"])):R("",!0),L(ee)?(o(),k("div",Ml,[l(te,{shadow:"never",class:"branch-panel"},{header:t(()=>[i("div",Gl,[e[29]||(e[29]=i("span",{class:"panel-title"},"分支 Branch",-1)),i("div",Hl,[l(b,{size:"small",text:"",onClick:je},{default:t(()=>[...e[27]||(e[27]=[p("刷新",-1)])]),_:1}),l(b,{size:"small",text:"",type:"primary",onClick:e[0]||(e[0]=v=>_.value=!0)},{default:t(()=>[...e[28]||(e[28]=[p("+ 新建",-1)])]),_:1})])])]),default:t(()=>[i("div",jl,[(o(!0),k(M,null,Q(P.value,v=>(o(),k(M,{key:v.key},[i("div",Jl,y(v.title),1),v.tree.length?(o(),S(G,{key:0,data:v.tree,props:{label:"label",children:"children"},"node-key":"key","default-expand-all":"",indent:14,class:"branch-tree"},{default:t(({data:V})=>{var se,Ue,it;return[V.branch?(o(),k("div",{key:1,class:he(["branch-node",{current:(se=V.branch)==null?void 0:se.current,remote:V.remote}]),title:V.fullName,onDblclick:Bt=>{var rt;return((rt=V.branch)==null?void 0:rt.current)||Qe(V)}},[i("span",Xl,y(V.label),1),V.remote?R("",!0):(o(),k("span",{key:0,class:he(["node-sync",{clean:!Z(V.branch)&&((Ue=V.branch)==null?void 0:Ue.upstream)}])},y(Z(V.branch)),3)),i("span",Zl,[(it=V.branch)!=null&&it.current?(o(),k(M,{key:0},[l(b,{size:"small",text:"",type:"success",onClick:Te(Dt,["stop"])},{default:t(()=>[...e[30]||(e[30]=[p("拉取",-1)])]),_:1}),l(b,{size:"small",text:"",type:"primary",onClick:Te(It,["stop"])},{default:t(()=>[...e[31]||(e[31]=[p("推送",-1)])]),_:1})],64)):(o(),S(b,{key:1,size:"small",text:"",type:"primary",onClick:Te(Bt=>Qe(V),["stop"])},{default:t(()=>[...e[32]||(e[32]=[p("切换",-1)])]),_:1},8,["onClick"]))])],42,Yl)):(o(),k("span",Kl,y(V.label),1))]}),_:1},8,["data"])):(o(),k("div",Ql,"暂无分支"))],64))),128)),P.value.length===0?(o(),S(Y,{key:0,description:"暂无分支","image-size":60})):R("",!0)])]),_:1}),i("div",en,[l(te,{shadow:"never",class:"toolbar-card"},{default:t(()=>{var v;return[i("div",tn,[i("div",ln,[l(b,{type:"primary",onClick:e[1]||(e[1]=V=>d.value=!0)},{default:t(()=>[...e[33]||(e[33]=[p("提交 Commit",-1)])]),_:1}),l(b,{plain:"",onClick:yt},{default:t(()=>[...e[34]||(e[34]=[p("暂存全部",-1)])]),_:1}),l(b,{plain:"",disabled:(((v=u.value)==null?void 0:v.staged.length)??0)===0,onClick:bt},{default:t(()=>[...e[35]||(e[35]=[p("取消暂存全部",-1)])]),_:1},8,["disabled"]),l(re,{value:ne.value,hidden:ne.value===0,offset:[2,4]},{default:t(()=>[l(b,{type:"success",plain:"",onClick:xt},{default:t(()=>[...e[36]||(e[36]=[p("暂存改动 Stash",-1)])]),_:1})]),_:1},8,["value","hidden"]),l(xe,{content:"Shelve Silently:使用默认说明,一键暂存全部更改",placement:"top"},{default:t(()=>[l(b,{plain:"",onClick:St},{default:t(()=>[...e[37]||(e[37]=[p("静默 Stash",-1)])]),_:1})]),_:1}),l(b,{type:"danger",plain:"",onClick:e[2]||(e[2]=V=>I.value=!0)},{default:t(()=>[...e[38]||(e[38]=[p("硬重置",-1)])]),_:1}),l(b,{type:"danger",plain:"",onClick:e[3]||(e[3]=V=>J("git_clean"))},{default:t(()=>[...e[39]||(e[39]=[p("清理未跟踪",-1)])]),_:1})])])]}),_:1}),(st=u.value)!=null&&st.conflicted.length?(o(),S(z,{key:0,title:"存在合并冲突,请先解决冲突(修改文件后重新 git add 并提交)",type:"error",closable:!1,"show-icon":"",class:"mb"})):R("",!0),l(te,{shadow:"never",class:"changes-card"},{header:t(()=>[i("div",nn,[e[42]||(e[42]=i("span",{class:"card-title"},"更改 Changes",-1)),i("div",sn,[ne.value?(o(),S(T,{key:0,size:"small",type:"primary",effect:"plain"},{default:t(()=>[p("已选 "+y(ne.value),1)]),_:1})):R("",!0),l(b,{size:"small",text:"",onClick:Ct},{default:t(()=>[...e[40]||(e[40]=[p("全选本组",-1)])]),_:1}),ne.value?(o(),S(b,{key:1,size:"small",text:"",type:"danger",onClick:Ze},{default:t(()=>[...e[41]||(e[41]=[p("清除选择",-1)])]),_:1})):R("",!0)])])]),default:t(()=>[l(Ft,{modelValue:ae.value,"onUpdate:modelValue":e[4]||(e[4]=v=>ae.value=v),class:"changes-tabs"},{default:t(()=>[(o(!0),k(M,null,Q(Le.value,v=>(o(),S(Ae,{key:v.key,name:v.key},{label:t(()=>[i("span",an,[p(y(v.label)+" ",1),l(re,{value:v.count,hidden:v.count===0,type:"primary",class:"tab-badge"},null,8,["value","hidden"])])]),default:t(()=>[i("div",on,"「"+y(v.hint)+"」",1),v.count===0?(o(),k("div",rn,"没有"+y(v.label)+"的文件",1)):(o(),k("div",un,[(o(!0),k(M,null,Q(X.value[v.key],V=>(o(),k("div",{key:v.key+V.path,class:"file-row"},[l(le,{"model-value":Xe(V.path),onChange:se=>Ye(V.path)},null,8,["model-value","onChange"]),i("span",{class:he(["file-status",At(V)])},y(Nt(V)),3),i("span",{class:"file-path mono",title:V.path},y(V.path),9,dn),l(b,{size:"small",text:"",type:"primary",onClick:se=>kt(V)},{default:t(()=>[...e[43]||(e[43]=[p("查看差异",-1)])]),_:1},8,["onClick"]),V.conflicted?(o(),S(b,{key:0,size:"small",text:"",type:"warning",onClick:se=>Ke(V)},{default:t(()=>[...e[44]||(e[44]=[p("标记已解决(git add)",-1)])]),_:1},8,["onClick"])):(o(),S(b,{key:1,size:"small",text:"",type:V.staged?"warning":"success",onClick:se=>gt(V)},{default:t(()=>[p(y(V.staged?"取消暂存":"暂存"),1)]),_:2},1032,["type","onClick"]))]))),128))]))]),_:2},1032,["name"]))),128))]),_:1},8,["modelValue"])]),_:1}),l(te,{shadow:"never",class:"stash-card"},{header:t(()=>[i("div",cn,[i("span",fn,"暂存列表 Stash("+y(D.value.length)+")",1),i("div",pn,[l(b,{size:"small",text:"",onClick:Je},{default:t(()=>[...e[45]||(e[45]=[p("刷新",-1)])]),_:1})])])]),default:t(()=>[D.value.length===0?(o(),k("div",mn,"没有暂存的更改")):(o(),k("div",vn,[(o(!0),k(M,null,Q(D.value,v=>(o(),k("div",{key:v.ref,class:"stash-row"},[i("div",_n,[i("span",hn,y(v.ref),1),i("span",{class:"stash-msg",title:v.message},y(Pt(v.message)||"(无说明)"),9,gn),v.date?(o(),k("span",yn,y(Ut(v.date)),1)):R("",!0)]),i("div",bn,[l(b,{size:"small",text:"",type:"primary",onClick:V=>wt(v)},{default:t(()=>[...e[46]||(e[46]=[p("差异",-1)])]),_:1},8,["onClick"]),l(b,{size:"small",text:"",type:"success",onClick:V=>Tt(v)},{default:t(()=>[...e[47]||(e[47]=[p("应用",-1)])]),_:1},8,["onClick"]),l(b,{size:"small",text:"",type:"warning",onClick:V=>Et(v)},{default:t(()=>[...e[48]||(e[48]=[p("恢复",-1)])]),_:1},8,["onClick"]),l(b,{size:"small",text:"",type:"danger",onClick:V=>zt(v)},{default:t(()=>[...e[49]||(e[49]=[p("删除",-1)])]),_:1},8,["onClick"])])]))),128))]))]),_:1})])])):(o(),k("div",Wl,"请先在「仓库管理」中打开一个仓库")),l(Rl,{visible:L(B),"onUpdate:visible":e[5]||(e[5]=v=>Ht(B)?B.value=v:null),tool:((at=L($))==null?void 0:at.tool)??"",preview:((ot=L($))==null?void 0:ot.preview)??null,onConfirm:_t,onCancel:L(de)},null,8,["visible","tool","preview","onCancel"]),l(Ot,{modelValue:c.visible,"onUpdate:modelValue":e[6]||(e[6]=v=>c.visible=v),title:c.title,size:"56%","destroy-on-close":""},{default:t(()=>[ge((o(),k("div",kn,[c.diff?(o(),k(M,{key:0},[i("div",wn,[i("span",null,y(c.diff.files.length)+" 个文件",1),i("span",Cn,"+"+y(c.diff.insertions),1),i("span",$n,"-"+y(c.diff.deletions),1),c.diff.truncated?(o(),k("span",xn,"(已截断)")):R("",!0)]),l(Be,{patch:c.diff.rawPatch},null,8,["patch"])],64)):c.patch?(o(),k(M,{key:1},[c.patch?(o(),S(Be,{key:1,patch:c.patch},null,8,["patch"])):(o(),k("div",Vn,"无差异"))],64)):c.loading?R("",!0):(o(),S(Y,{key:2,description:"无差异","image-size":60}))])),[[qt,c.loading]])]),_:1},8,["modelValue","title"]),l(Ve,{modelValue:d.value,"onUpdate:modelValue":e[9]||(e[9]=v=>d.value=v),title:"提交 Commit",width:"520px",onClosed:e[10]||(e[10]=v=>f.value="")},{footer:t(()=>[l(b,{onClick:e[8]||(e[8]=v=>d.value=!1)},{default:t(()=>[...e[50]||(e[50]=[p("取消",-1)])]),_:1}),l(b,{type:"primary",onClick:et},{default:t(()=>[...e[51]||(e[51]=[p("下一步(dry-run 预览)",-1)])]),_:1})]),default:t(()=>[l(_e,{modelValue:f.value,"onUpdate:modelValue":e[7]||(e[7]=v=>f.value=v),type:"textarea",rows:5,placeholder:"输入提交信息(message)",onKeydown:Ge(Te(et,["ctrl"]),["enter"])},null,8,["modelValue","onKeydown"])]),_:1},8,["modelValue"]),l(Ve,{modelValue:_.value,"onUpdate:modelValue":e[14]||(e[14]=v=>_.value=v),title:"新建分支",width:"480px",onClosed:e[15]||(e[15]=v=>{m.value="",x.value=""})},{footer:t(()=>[l(b,{onClick:e[13]||(e[13]=v=>_.value=!1)},{default:t(()=>[...e[52]||(e[52]=[p("取消",-1)])]),_:1}),l(b,{type:"primary",onClick:Rt},{default:t(()=>[...e[53]||(e[53]=[p("下一步(dry-run 预览)",-1)])]),_:1})]),default:t(()=>[l(tt,{"label-width":"100px"},{default:t(()=>[l(Se,{label:"分支名"},{default:t(()=>[l(_e,{modelValue:m.value,"onUpdate:modelValue":e[11]||(e[11]=v=>m.value=v),placeholder:"feature/xxx"},null,8,["modelValue"])]),_:1}),l(Se,{label:"起点(可选)"},{default:t(()=>[l(_e,{modelValue:x.value,"onUpdate:modelValue":e[12]||(e[12]=v=>x.value=v),placeholder:"默认当前 HEAD,如 main 或 commit hash"},null,8,["modelValue"])]),_:1})]),_:1})]),_:1},8,["modelValue"]),l(Ve,{modelValue:A.value,"onUpdate:modelValue":e[19]||(e[19]=v=>A.value=v),title:"暂存改动 Stash",width:"560px"},{footer:t(()=>[l(b,{onClick:e[18]||(e[18]=v=>A.value=!1)},{default:t(()=>[...e[57]||(e[57]=[p("取消",-1)])]),_:1}),l(b,{type:"primary",onClick:Vt},{default:t(()=>[...e[58]||(e[58]=[p("下一步(dry-run 预览)",-1)])]),_:1})]),default:t(()=>[l(z,{class:"stash-tip",title:"可勾选左侧更改列表的文件随本次 stash 一起保存;不勾选则保存全部更改。",type:"info",closable:!1,"show-icon":""}),l(tt,{"label-width":"90px"},{default:t(()=>[l(Se,{label:"说明(可选)"},{default:t(()=>[l(_e,{modelValue:q.value,"onUpdate:modelValue":e[16]||(e[16]=v=>q.value=v),placeholder:"留空则使用默认说明(WIP on 分支: 提交)"},null,8,["modelValue"])]),_:1}),l(Se,{label:"包含未跟踪"},{default:t(()=>[l(le,{modelValue:H.value,"onUpdate:modelValue":e[17]||(e[17]=v=>H.value=v)},{default:t(()=>[...e[54]||(e[54]=[p("包含未跟踪文件(-u)。勾选文件后会自动包含其中的未跟踪文件",-1)])]),_:1},8,["modelValue"])]),_:1})]),_:1}),$e.value.length?(o(),k("div",Sn,[i("div",Tn,[i("span",En,"选择文件(已选 "+y(ne.value)+")",1),i("div",zn,[l(b,{size:"small",text:"",onClick:$t},{default:t(()=>[...e[55]||(e[55]=[p("全选",-1)])]),_:1}),l(b,{size:"small",text:"",onClick:Ze},{default:t(()=>[...e[56]||(e[56]=[p("清除选择",-1)])]),_:1})])]),i("div",Dn,[(o(!0),k(M,null,Q($e.value,v=>{var V,se;return o(),k("div",{key:v,class:"file-row"},[l(le,{"model-value":Xe(v),onChange:Ue=>Ye(v)},null,8,["model-value","onChange"]),i("span",{class:he(["file-status",(V=u.value)!=null&&V.untracked.includes(v)?"new":"mod"])},y((se=u.value)!=null&&se.untracked.includes(v)?"?":"M"),3),i("span",In,y(v),1)])}),128))])])):R("",!0)]),_:1},8,["modelValue"]),l(Ve,{modelValue:I.value,"onUpdate:modelValue":e[22]||(e[22]=v=>I.value=v),title:"硬重置(高风险)",width:"480px",onClosed:e[23]||(e[23]=v=>j.value="")},{footer:t(()=>[l(b,{onClick:e[21]||(e[21]=v=>I.value=!1)},{default:t(()=>[...e[59]||(e[59]=[p("取消",-1)])]),_:1}),l(b,{type:"danger",onClick:Lt},{default:t(()=>[...e[60]||(e[60]=[p("下一步(dry-run 预览)",-1)])]),_:1})]),default:t(()=>[l(z,{title:"将丢弃索引与工作区所有更改(不可逆),执行前会自动创建备份",type:"error",closable:!1,"show-icon":"",class:"mb"}),l(_e,{modelValue:j.value,"onUpdate:modelValue":e[20]||(e[20]=v=>j.value=v),placeholder:"重置目标,默认 HEAD,如 HEAD~1 / commit hash / main"},null,8,["modelValue"])]),_:1},8,["modelValue"])])}}}),Ln=ie(Rn,[["__scopeId","data-v-889338b7"]]),Nn={class:"page"},An={class:"filter-bar"},Un={class:"mono hash"},Pn={class:"subject"},Fn={key:0,class:"refs"},On={class:"sub"},qn={class:"commit-head"},Bn={class:"mono commit-hash"},Wn={class:"commit-subject"},Mn={class:"commit-meta"},Gn={key:0},Hn={key:0,class:"commit-body"},jn={class:"diff-wrap"},Jn={class:"diff-summary"},Kn={class:"add"},Yn={class:"del"},Xn={key:0,class:"warn"},Zn=oe({__name:"HistoryView",setup(a){const n=ze(),{revision:r}=De(),h=()=>n.currentId,u=E([]),C=E([]),D=E(!1),N=E(""),U=E("head"),c=E(""),d=E(""),f=E(50),_=E(!1),m=E(null),x=E(null),A=E(!1),q=O(()=>C.value.filter(B=>!B.remote)),H=O(()=>{var B;return((B=C.value.find($=>$.current))==null?void 0:B.name)??""});async function I(){const B=h();if(B!==null)try{const{branches:$}=await ct(B);C.value=$}catch{}}async function j(){const B=h();if(B!==null){D.value=!0,N.value="";try{const $={maxCount:f.value};U.value==="all"?$.all=!0:U.value==="branch"&&c.value&&($.from=c.value),d.value.trim()&&($.path=d.value.trim()),u.value=await nl(B,$)}catch($){N.value=$ instanceof Error?$.message:String($),u.value=[]}finally{D.value=!1}}}async function w(){await Promise.all([I(),j()])}async function ne(B){const $=h();if($!==null){_.value=!0,m.value=B,x.value=null,A.value=!0;try{const{diff:ee}=await al($,B.hash);x.value=ee}catch(ee){W.error(ee instanceof Error?ee.message:String(ee))}finally{A.value=!1}}}function ae(B){const $=new Date(B);return Number.isNaN($.getTime())?B:$.toLocaleString()}return ue(h,()=>void w()),ue(r,()=>void j()),ue([U,c,f],()=>void j()),ce(()=>void w()),(B,$)=>{const ee=g("el-alert"),F=g("el-radio-button"),fe=g("el-radio-group"),de=g("el-option"),ye=g("el-select"),Re=g("el-input"),be=g("el-button"),pe=g("el-card"),X=g("el-table-column"),Le=g("el-tag"),Ne=g("el-table"),ke=g("el-empty"),we=g("el-divider"),Ce=g("el-drawer"),me=Ee("loading");return o(),k("div",Nn,[$[8]||($[8]=i("h2",{class:"page-title"},"提交历史",-1)),N.value?(o(),S(ee,{key:0,title:N.value,type:"error",closable:!1,"show-icon":"",class:"mb"},null,8,["title"])):R("",!0),l(pe,{shadow:"never",class:"mb"},{default:t(()=>[i("div",An,[l(fe,{modelValue:U.value,"onUpdate:modelValue":$[0]||($[0]=P=>U.value=P)},{default:t(()=>[l(F,{value:"head"},{default:t(()=>[p("当前分支("+y(H.value)+")",1)]),_:1}),l(F,{value:"all"},{default:t(()=>[...$[5]||($[5]=[p("全部分支",-1)])]),_:1}),l(F,{value:"branch"},{default:t(()=>[...$[6]||($[6]=[p("指定分支",-1)])]),_:1})]),_:1},8,["modelValue"]),U.value==="branch"?(o(),S(ye,{key:0,modelValue:c.value,"onUpdate:modelValue":$[1]||($[1]=P=>c.value=P),filterable:"",placeholder:"选择分支",class:"branch-select"},{default:t(()=>[(o(!0),k(M,null,Q(q.value,P=>(o(),S(de,{key:P.name,label:P.name,value:P.name},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])):R("",!0),l(Re,{modelValue:d.value,"onUpdate:modelValue":$[2]||($[2]=P=>d.value=P),clearable:"",placeholder:"按路径过滤(可选)",class:"path-input",onKeyup:Ge(j,["enter"])},null,8,["modelValue"]),l(ye,{modelValue:f.value,"onUpdate:modelValue":$[3]||($[3]=P=>f.value=P),class:"count-select"},{default:t(()=>[l(de,{label:"20 条",value:20}),l(de,{label:"50 条",value:50}),l(de,{label:"100 条",value:100})]),_:1},8,["modelValue"]),l(be,{loading:D.value,onClick:j},{default:t(()=>[...$[7]||($[7]=[p("刷新",-1)])]),_:1},8,["loading"])])]),_:1}),l(pe,{shadow:"never"},{default:t(()=>[ge((o(),S(Ne,{data:u.value,size:"default","highlight-current-row":"",onRowClick:ne},{default:t(()=>[l(X,{label:"提交",width:"100"},{default:t(({row:P})=>[i("span",Un,y(P.shortHash),1)]),_:1}),l(X,{label:"信息","min-width":"300"},{default:t(({row:P})=>[i("div",Pn,y(P.subject),1),P.refs?(o(),k("div",Fn,[(o(!0),k(M,null,Q(P.refs.split(",").map(Z=>Z.trim()).filter(Boolean),Z=>(o(),S(Le,{key:Z,size:"small",effect:"plain",class:"ref-tag"},{default:t(()=>[p(y(Z.startsWith("tag: ")?Z:Z.includes("/")&&!Z.startsWith("HEAD")?Z.split("/").pop():Z),1)]),_:2},1024))),128))])):R("",!0)]),_:1}),l(X,{label:"作者",width:"170"},{default:t(({row:P})=>[i("div",null,y(P.authorName),1),i("div",On,y(P.authorEmail),1)]),_:1}),l(X,{label:"时间",width:"180"},{default:t(({row:P})=>[p(y(ae(P.authorDate)),1)]),_:1})]),_:1},8,["data"])),[[me,D.value]]),!D.value&&u.value.length===0?(o(),S(ke,{key:0,description:"暂无提交记录"})):R("",!0)]),_:1}),l(Ce,{modelValue:_.value,"onUpdate:modelValue":$[4]||($[4]=P=>_.value=P),size:"60%","destroy-on-close":""},{default:t(()=>[m.value?(o(),k(M,{key:0},[i("div",qn,[i("div",Bn,y(m.value.hash),1),i("h3",Wn,y(m.value.subject),1),i("div",Mn,[i("span",null,y(m.value.authorName)+" <"+y(m.value.authorEmail)+">",1),i("span",null,y(ae(m.value.authorDate)),1),m.value.refs?(o(),k("span",Gn,"refs: "+y(m.value.refs),1)):R("",!0)]),m.value.body?(o(),k("pre",Hn,y(m.value.body),1)):R("",!0)]),l(we),ge((o(),k("div",jn,[x.value?(o(),k(M,{key:0},[i("div",Jn,[i("span",null,y(x.value.files.length)+" 个文件",1),i("span",Kn,"+"+y(x.value.insertions),1),i("span",Yn,"-"+y(x.value.deletions),1),x.value.truncated?(o(),k("span",Xn,"(已截断)")):R("",!0)]),l(Be,{patch:x.value.rawPatch},null,8,["patch"])],64)):A.value?R("",!0):(o(),S(ke,{key:1,description:"无差异","image-size":60}))])),[[me,A.value]])],64)):R("",!0)]),_:1},8,["modelValue"])])}}}),Qn=ie(Zn,[["__scopeId","data-v-47ac0af2"]]),es=We("logs",{state:()=>({logs:[],loading:!1,error:null,limit:50,toolFilter:""}),actions:{async load(){this.loading=!0,this.error=null;try{const{logs:a}=await il({limit:this.limit,tool:this.toolFilter||void 0});this.logs=a}catch(a){this.error=a instanceof Error?a.message:String(a)}finally{this.loading=!1}}}}),ts={class:"page"},ls={class:"filter-bar"},ns={class:"tool-cell"},ss={class:"mono repo-path"},as=["title"],os=["title"],is=["title"],rs=oe({__name:"LogsView",setup(a){const n=es(),{revision:r}=De(),h=O(()=>{const c=new Set;for(const d of n.logs)c.add(d.tool);return[...c].sort()}),u=c=>c.includes("reset")||c.includes("clean")||c.includes("force")||c.includes("rebase")?"dangerous":"write";function C(c){return u(c)==="dangerous"?"danger":c.startsWith("git_")?"primary":"success"}function D(c){const d=new Date(c);return Number.isNaN(d.getTime())?c:d.toLocaleString()}function N(c){return c==="mcp"?"primary":c==="web"?"success":"warning"}function U(c){if(c==null)return"";if(typeof c=="string")return c;try{const d=JSON.stringify(c);return d===void 0?"":d}catch{return String(c)}}return ue(r,()=>{n.load()}),ce(()=>{n.load()}),(c,d)=>{const f=g("el-option"),_=g("el-select"),m=g("el-button"),x=g("el-card"),A=g("el-table-column"),q=g("el-tag"),H=g("el-table"),I=g("el-empty"),j=Ee("loading");return o(),k("div",ts,[d[7]||(d[7]=i("h2",{class:"page-title"},"操作日志",-1)),l(x,{shadow:"never",class:"mb"},{default:t(()=>[i("div",ls,[l(_,{modelValue:L(n).toolFilter,"onUpdate:modelValue":d[0]||(d[0]=w=>L(n).toolFilter=w),clearable:"",placeholder:"按工具过滤",class:"tool-select",onChange:L(n).load},{default:t(()=>[(o(!0),k(M,null,Q(h.value,w=>(o(),S(f,{key:w,label:w,value:w},null,8,["label","value"]))),128))]),_:1},8,["modelValue","onChange"]),l(_,{modelValue:L(n).limit,"onUpdate:modelValue":d[1]||(d[1]=w=>L(n).limit=w),class:"limit-select",onChange:L(n).load},{default:t(()=>[l(f,{label:"最近 20 条",value:20}),l(f,{label:"最近 50 条",value:50}),l(f,{label:"最近 100 条",value:100})]),_:1},8,["modelValue","onChange"]),l(m,{loading:L(n).loading,onClick:L(n).load},{default:t(()=>[...d[2]||(d[2]=[p("刷新",-1)])]),_:1},8,["loading","onClick"]),d[3]||(d[3]=i("span",{class:"tip"},"记录来源:Web / MCP / CLI 全部写入操作(只读操作不记录)",-1))])]),_:1}),l(x,{shadow:"never"},{default:t(()=>[ge((o(),S(H,{data:L(n).logs,size:"default"},{default:t(()=>[l(A,{label:"时间",width:"180"},{default:t(({row:w})=>[p(y(D(w.timestamp)),1)]),_:1}),l(A,{label:"来源",width:"90"},{default:t(({row:w})=>[l(q,{type:N(w.source),size:"small",effect:"plain"},{default:t(()=>[p(y(w.source.toUpperCase()),1)]),_:2},1032,["type"])]),_:1}),l(A,{label:"工具",width:"200"},{default:t(({row:w})=>[i("div",ns,[l(q,{type:C(w.tool),size:"small",effect:"dark"},{default:t(()=>[p(y(w.tool),1)]),_:2},1032,["type"]),w.dryRun?(o(),S(q,{key:0,size:"small",type:"info",effect:"plain",class:"dry-tag"},{default:t(()=>[...d[4]||(d[4]=[p("dry-run",-1)])]),_:1})):R("",!0)])]),_:1}),l(A,{label:"仓库","min-width":"200"},{default:t(({row:w})=>[i("span",ss,y(w.repoPath??"—"),1)]),_:1}),l(A,{label:"参数(脱敏)","min-width":"180"},{default:t(({row:w})=>[i("span",{class:"mono params",title:U(w.params)},y(U(w.params)||"—"),9,as)]),_:1}),l(A,{label:"结果","min-width":"150"},{default:t(({row:w})=>[w.error?(o(),k(M,{key:0},[l(q,{type:"danger",size:"small",effect:"plain",class:"result-tag"},{default:t(()=>[...d[5]||(d[5]=[p("失败",-1)])]),_:1}),i("div",{class:"err-text",title:w.error},y(w.error),9,os)],64)):(o(),k(M,{key:1},[l(q,{type:"success",size:"small",effect:"plain",class:"result-tag"},{default:t(()=>[...d[6]||(d[6]=[p("成功",-1)])]),_:1}),i("div",{class:"ok-text",title:w.result},y(w.result),9,is)],64))]),_:1}),l(A,{label:"耗时",width:"90"},{default:t(({row:w})=>[p(y(w.durationMs)+" ms",1)]),_:1})]),_:1},8,["data"])),[[j,L(n).loading]]),!L(n).loading&&L(n).logs.length===0?(o(),S(I,{key:0,description:"暂无操作日志"})):R("",!0)]),_:1})])}}}),us=ie(rs,[["__scopeId","data-v-79ac665b"]]),ds={class:"page"},cs={class:"card-head"},fs={class:"mono"},ps={class:"desc"},ms={key:1,class:"action-bar"},vs=oe({__name:"SettingsView",setup(a){const n=ft(),r=Fe({disabledTools:[],requireApprovalFor:[],dryRunDefault:!1}),h=E(!1),u=O(()=>{const _=n.permissions;return _?JSON.stringify([...r.disabledTools].sort())!==JSON.stringify([..._.disabledTools].sort())||JSON.stringify([...r.requireApprovalFor].sort())!==JSON.stringify([..._.requireApprovalFor].sort())||r.dryRunDefault!==_.dryRunDefault:!1}),C=O(()=>n.tools.filter(_=>_.riskLevel==="dangerous")),D=O(()=>C.value.map(_=>_.name));function N(){const _=n.permissions;_&&(r.disabledTools=[..._.disabledTools],r.requireApprovalFor=[..._.requireApprovalFor],r.dryRunDefault=_.dryRunDefault,h.value=!0)}ue(()=>n.permissions,_=>{_&&!u.value&&N()});async function U(_,m){m?r.disabledTools=r.disabledTools.filter(x=>x!==_.name):(r.disabledTools.includes(_.name)||r.disabledTools.push(_.name),r.requireApprovalFor=r.requireApprovalFor.filter(x=>x!==_.name))}function c(_){return _==="readonly"?"success":_==="write"?"primary":"danger"}function d(_){return _==="readonly"?"只读":_==="write"?"写操作":"高风险"}async function f(){try{await n.save({disabledTools:[...r.disabledTools],requireApprovalFor:[...r.requireApprovalFor],dryRunDefault:r.dryRunDefault}),W.success("设置已保存")}catch(_){W.error(_ instanceof Error?_.message:String(_))}}return ce(async()=>{await n.load(),N()}),(_,m)=>{const x=g("el-alert"),A=g("el-switch"),q=g("el-form-item"),H=g("el-form"),I=g("el-card"),j=g("el-tag"),w=g("el-table-column"),ne=g("el-table"),ae=g("el-option"),B=g("el-select"),$=g("el-button"),ee=Ee("loading");return o(),k("div",ds,[m[10]||(m[10]=i("h2",{class:"page-title"},"设置",-1)),L(n).error?(o(),S(x,{key:0,title:L(n).error,type:"error",closable:!1,"show-icon":"",class:"mb"},null,8,["title"])):R("",!0),l(I,{shadow:"never",class:"mb"},{header:t(()=>[...m[2]||(m[2]=[p("默认行为",-1)])]),default:t(()=>[l(H,{"label-width":"180px","label-position":"left"},{default:t(()=>[l(q,{label:"写操作默认 dry-run 预览"},{default:t(()=>[l(A,{modelValue:r.dryRunDefault,"onUpdate:modelValue":m[0]||(m[0]=F=>r.dryRunDefault=F)},null,8,["modelValue"]),m[3]||(m[3]=i("span",{class:"form-tip"},"开启后所有写操作(MCP/CLI)默认只生成预览,不真正执行",-1))]),_:1})]),_:1})]),_:1}),l(I,{shadow:"never",class:"mb"},{header:t(()=>[i("div",cs,[m[5]||(m[5]=i("span",null,"工具开关与风险等级",-1)),l(j,{size:"small",type:"warning",effect:"plain"},{default:t(()=>[...m[4]||(m[4]=[p("高危工具默认禁用,需在下方开启",-1)])]),_:1})])]),default:t(()=>[ge((o(),S(ne,{data:L(n).tools,size:"default"},{default:t(()=>[l(w,{label:"工具",width:"230"},{default:t(({row:F})=>[i("span",fs,y(F.name),1)]),_:1}),l(w,{label:"风险",width:"90"},{default:t(({row:F})=>[l(j,{type:c(F.riskLevel),size:"small",effect:"dark"},{default:t(()=>[p(y(d(F.riskLevel)),1)]),_:2},1032,["type"])]),_:1}),l(w,{label:"描述","min-width":"240"},{default:t(({row:F})=>[i("span",ps,y(F.description),1)]),_:1}),l(w,{label:"启用",width:"100",align:"center"},{default:t(({row:F})=>[l(A,{"model-value":!r.disabledTools.includes(F.name),onChange:fe=>U(F,fe===!0)},null,8,["model-value","onChange"])]),_:1})]),_:1},8,["data"])),[[ee,L(n).loading]])]),_:1}),l(I,{shadow:"never",class:"mb"},{header:t(()=>[...m[6]||(m[6]=[p("审批规则",-1)])]),default:t(()=>[l(H,{"label-position":"top"},{default:t(()=>[l(q,{label:"执行前需要审批的工具(勾选后,执行时需在确认框中手工确认)"},{default:t(()=>[l(B,{modelValue:r.requireApprovalFor,"onUpdate:modelValue":m[1]||(m[1]=F=>r.requireApprovalFor=F),multiple:"",filterable:"","collapse-tags":"",placeholder:"选择需要审批的工具",style:{width:"100%"}},{default:t(()=>[(o(!0),k(M,null,Q(D.value,F=>(o(),S(ae,{key:F,label:F,value:F,disabled:r.disabledTools.includes(F)},null,8,["label","value","disabled"]))),128))]),_:1},8,["modelValue"]),m[7]||(m[7]=i("span",{class:"form-tip"},"审批仅对已启用的高危工具生效;禁用后自动移除",-1))]),_:1})]),_:1})]),_:1}),h.value?(o(),k("div",ms,[l($,{type:"primary",loading:L(n).saving,disabled:!u.value,onClick:f},{default:t(()=>[...m[8]||(m[8]=[p("保存设置",-1)])]),_:1},8,["loading","disabled"]),l($,{disabled:!u.value,onClick:N},{default:t(()=>[...m[9]||(m[9]=[p("放弃修改",-1)])]),_:1},8,["disabled"])])):R("",!0)])}}}),_s=ie(vs,[["__scopeId","data-v-eb3ae411"]]),mt=jt({history:Jt(),routes:[{path:"/",redirect:"/status"},{path:"/repos",name:"repos",component:$l,meta:{title:"仓库管理"}},{path:"/status",name:"status",component:Ln,meta:{title:"状态"}},{path:"/history",name:"history",component:Qn,meta:{title:"历史"}},{path:"/logs",name:"logs",component:us,meta:{title:"操作日志"}},{path:"/settings",name:"settings",component:_s,meta:{title:"设置"}}]});mt.afterEach(a=>{const n=a.meta.title;document.title=n?`${n} · Git Cockpit`:"Git Cockpit"});document.documentElement.classList.add("dark");const Ie=Kt(gl);Ie.use(Yt());Ie.use(mt);Ie.use(Xt,{size:"default"});Ie.mount("#app");
|
package/dist/web/index.html
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Git Cockpit</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-XiZddJPB.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/assets/vue-CYNDxJyv.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/element-plus-D8uC5V5h.js">
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CNUTv7F2.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="app"></div>
|
package/dist/webServer.d.ts
CHANGED
package/dist/webServer.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shaohui_jin/git-cockpit-mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Git Cockpit - 基于 MCP 的 Git 可视化操作工具(MCP Server + Web 服务 + CLI)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
31
31
|
"fastify": "^5.2.1",
|
|
32
32
|
"zod": "^3.24.1",
|
|
33
|
-
"@shaohui_jin/git-cockpit-core": "^0.1.
|
|
33
|
+
"@shaohui_jin/git-cockpit-core": "^0.1.6"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"simple-git": "^3.27.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/webServer.ts","../src/runtime.ts","../src/config.ts","../src/repoManager.ts"],"sourcesContent":["/**\n * Fastify Web 服务:\n * - REST API 供 Web 前端调用(仓库管理、状态/历史/差异/branch 等只读查询、通用写操作入口);\n * - SSE(GET /api/events)推送仓库变化事件;\n * - 挂载 MCP Streamable HTTP 传输(/mcp),供 AI 客户端(Cursor/Claude Desktop)连接;\n * - 托管 Web 前端构建产物(packages/web/dist,若存在)。\n */\nimport * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport Fastify from 'fastify';\nimport type { FastifyInstance, FastifyReply } from 'fastify';\nimport cors from '@fastify/cors';\nimport fastifyStatic from '@fastify/static';\nimport {\n GitOperationError,\n GitService,\n PermissionManager,\n RepoNotFoundError\n} from '@shaohui_jin/git-cockpit-core';\nimport type { OpenedRepo } from '@shaohui_jin/git-cockpit-core';\nimport { disposeRuntime } from './runtime.js';\nimport type { Runtime } from './runtime.js';\nimport { McpHttpHandler } from './mcpServer.js';\nimport { executeTool } from './tools/handlers.js';\nimport { TOOL_DEF_MAP, toolSummaries } from './tools/index.js';\n\nexport interface WebServerHandle {\n app: FastifyInstance;\n /** 关闭 HTTP 服务与相关资源 */\n close: () => Promise<void>;\n}\n\n/** 静态资源目录:优先 Web 构建产物;不存在则仅提供 API。\n * 解析基于脚本自身位置(而非 process.cwd()),保证任意启动目录都能找到前端。 */\nfunction resolveWebDist(): string | null {\n // 源码布局下脚本位于 packages/mcp-server/dist,../.. 回到 packages,再进 web/dist\n const here = path.resolve(path.dirname(fileURLToPath(import.meta.url)));\n const candidates = [\n path.resolve(here, '..', '..', 'web', 'dist'), // 源码布局:packages/mcp-server/dist → packages/web/dist\n path.resolve(here, 'web'), // 发布布局:产物由 copy-web.mjs 打入 dist/web(含 index.html)\n path.resolve(here, 'web', 'dist'), // 兼容旧式 dist/web/dist 布局\n path.resolve(here, '..', 'web', 'dist'), // 兼容 npm 全局安装目录的旧式布局\n path.resolve(process.cwd(), 'dist', 'web'),\n path.resolve(process.cwd(), '..', 'web', 'dist'),\n path.resolve(process.cwd(), 'node_modules', '@shaohui_jin', 'git-cockpit-web', 'dist')\n ];\n for (const c of candidates) {\n if (fs.existsSync(path.join(c, 'index.html'))) return c;\n }\n return null;\n}\n\nexport async function createWebServer(\n runtime: Runtime,\n options: { host?: string; port?: number; staticDir?: string | null; noListen?: boolean } = {}\n): Promise<WebServerHandle> {\n const app = Fastify({ logger: false, bodyLimit: 2 * 1024 * 1024 });\n\n await app.register(cors, {\n origin: true,\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],\n allowedHeaders: ['Content-Type', 'Mcp-Session-Id', 'Authorization']\n });\n\n const staticDir = options.staticDir !== undefined ? options.staticDir : resolveWebDist();\n if (staticDir && fs.existsSync(staticDir)) {\n await app.register(fastifyStatic, { root: staticDir, prefix: '/' });\n }\n\n const mcpHttp = new McpHttpHandler(runtime);\n\n // Health\n app.get('/api/health', async () => ({\n ok: true,\n service: 'git-cockpit',\n version: '0.1.0',\n uptimeMs: process.uptime() * 1000\n }));\n\n // ---------------------------------------------------------------------------\n // 仓库管理\n // ---------------------------------------------------------------------------\n app.get('/api/repos', async () => ({\n repos: runtime.repoManager.list()\n }));\n\n app.post<{ Body: { path?: string } }>('/api/repos/open', async (req, reply) => {\n const target = req.body?.path?.trim();\n if (!target) {\n return reply.code(400).send({ error: '缺少 path 参数' } as never);\n }\n try {\n const handle = await runtime.repoManager.open(target);\n return { repo: handle.record };\n } catch (err) {\n return reply.code(400).send({ error: toError(err) } as never);\n }\n });\n\n app.delete<{ Params: { id: string } }>('/api/repos/:id', async (req, reply) => {\n const id = Number(req.params.id);\n if (!Number.isInteger(id)) {\n return reply.code(400).send({ error: '非法仓库 id' } as never);\n }\n runtime.repoManager.remove(id);\n return { ok: true };\n });\n\n // ---------------------------------------------------------------------------\n // 只读查询(按仓库 id)\n // ---------------------------------------------------------------------------\n async function withRepo<T>(\n req: { params: { id: string } },\n reply: FastifyReply,\n fn: (handle: { service: GitService; record: OpenedRepo }) => Promise<T>\n ): Promise<T | void> {\n const id = Number(req.params.id);\n const handle = await runtime.repoManager.getById(id);\n if (!handle) {\n await reply.code(404).send({ error: '仓库不存在或已失效' });\n return undefined;\n }\n try {\n return await fn(handle);\n } catch (err) {\n await reply.code(400).send({ error: toError(err) });\n return undefined;\n }\n }\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/status', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.getStatus())\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>('/api/repos/:id/log', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => {\n const q = req.query;\n return service.getLog({\n maxCount: q.maxCount ? Math.min(Number(q.maxCount) || 100, 10000) : undefined,\n from: q.from,\n to: q.to,\n author: q.author,\n path: q.path,\n all: q.all === 'true'\n });\n })\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>('/api/repos/:id/diff', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => {\n const q = req.query;\n return service.getDiff({\n from: q.from,\n to: q.to,\n path: q.path,\n staged: q.staged === 'true',\n maxPatchBytes: q.maxPatchBytes ? Number(q.maxPatchBytes) : undefined\n });\n })\n );\n\n app.get<{ Params: { id: string; commit: string }; Querystring: Record<string, string | undefined> }>(\n '/api/repos/:id/show/:commit',\n async (req, reply) =>\n withRepo(req, reply, async ({ service }) =>\n service.getShow(req.params.commit, {\n path: req.query.path,\n maxPatchBytes: req.query.maxPatchBytes ? Number(req.query.maxPatchBytes) : undefined\n })\n )\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/branches', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.listBranches())\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/tags', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.listTags())\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/remotes', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.listRemotes())\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>('/api/repos/:id/graph', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.getGraph(req.query.maxCount ? Number(req.query.maxCount) : 500))\n );\n\n app.get<{ Params: { id: string }; Querystring: { commit?: string; path?: string } }>('/api/repos/:id/file', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => {\n if (!req.query.commit || !req.query.path) {\n return { error: '需要 commit 与 path 参数' };\n }\n return service.getFileContent(req.query.commit, req.query.path);\n })\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/stashes', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.listStashes())\n );\n\n // ---------------------------------------------------------------------------\n // 通用写操作入口(复用 MCP 同款安全链路)\n // ---------------------------------------------------------------------------\n app.post<{ Params: { id: string; tool: string }; Body: { params?: Record<string, unknown> } }>(\n '/api/repos/:id/tools/:tool',\n async (req, reply) => {\n const def = TOOL_DEF_MAP.get(req.params.tool);\n if (!def) return reply.code(404).send({ error: `未知工具: ${req.params.tool}` });\n const params = req.body?.params ?? {};\n const exec = await executeTool(def, params, {\n runtime,\n source: 'web',\n repoId: Number(req.params.id)\n });\n if (!exec.success) {\n const status = exec.error?.requiredApproval ? 403 : 400;\n return reply\n .code(status)\n .send({ error: exec.error?.message, requiredApproval: exec.error?.requiredApproval, code: exec.error?.code });\n }\n return { ...exec };\n }\n );\n\n // ---------------------------------------------------------------------------\n // 工具注册表信息(供前端渲染可操作按钮)\n // ---------------------------------------------------------------------------\n app.get('/api/tools', async () => ({\n tools: toolSummaries().map((t) => ({\n ...t,\n riskLevel: runtime.permissions.getRiskLevel(t.name),\n enabled: runtime.permissions.isEnabled(t.name)\n }))\n }));\n\n // ---------------------------------------------------------------------------\n // 操作日志\n // ---------------------------------------------------------------------------\n app.get<{ Querystring: Record<string, string | undefined> }>('/api/logs', async (req) => {\n const q = req.query;\n return {\n logs: runtime.auditLogger.list({\n limit: q.limit ? Number(q.limit) : undefined,\n offset: q.offset ? Number(q.offset) : undefined,\n tool: q.tool,\n source: (q.source as 'mcp' | 'web' | 'cli' | undefined) ?? undefined\n })\n };\n });\n\n // ---------------------------------------------------------------------------\n // 配置 / 权限\n // ---------------------------------------------------------------------------\n app.get('/api/settings', async () => ({\n config: runtime.configStore.snapshot(),\n permissions: {\n disabledTools: runtime.config.permissions.disabledTools,\n requireApprovalFor: runtime.config.permissions.requireApprovalFor,\n dryRunDefault: runtime.config.permissions.dryRunDefault\n },\n tools: toolSummaries().map((t) => ({\n name: t.name,\n description: t.description,\n riskLevel: runtime.permissions.getRiskLevel(t.name),\n enabled: runtime.permissions.isEnabled(t.name)\n }))\n }));\n\n app.put<{\n Body: { permissions?: { disabledTools?: string[]; requireApprovalFor?: string[]; dryRunDefault?: boolean } };\n }>('/api/settings', async (req, reply) => {\n const patch = req.body ?? {};\n if (patch.permissions === undefined) {\n return reply.code(400).send({ error: '仅支持更新 permissions 段' });\n }\n runtime.configStore.update({ permissions: patch.permissions });\n runtime.config = runtime.configStore.get();\n runtime.permissions = new PermissionManager(runtime.config);\n return { ok: true, config: runtime.configStore.snapshot() };\n });\n\n // ---------------------------------------------------------------------------\n // SSE:仓库变化推送\n // ---------------------------------------------------------------------------\n app.get('/api/events', async (req, reply) => {\n reply.hijack();\n const raw = reply.raw;\n raw.writeHead(200, {\n 'Content-Type': 'text/event-stream; charset=utf-8',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no'\n });\n raw.write(': connected\\n\\n');\n\n const listener = (payload: unknown) => {\n raw.write(`event: repo-changed\\ndata: ${JSON.stringify(payload)}\\n\\n`);\n };\n const logListener = () => {\n raw.write('event: log\\ndata: {}\\n\\n');\n };\n runtime.eventBus.on('repo-changed', listener);\n runtime.eventBus.on('log', logListener);\n\n const keepAlive = setInterval(() => raw.write(': ping\\n\\n'), 15_000);\n\n req.raw.on('close', () => {\n clearInterval(keepAlive);\n runtime.eventBus.off('repo-changed', listener);\n runtime.eventBus.off('log', logListener);\n });\n });\n\n // ---------------------------------------------------------------------------\n // MCP Streamable HTTP\n // ---------------------------------------------------------------------------\n app.get('/mcp', async (req, reply) => {\n reply.hijack();\n await mcpHttp.handle(req.raw, reply.raw, undefined);\n });\n app.post('/mcp', async (req, reply) => {\n // fastify 预读并解析了请求体(req.body);SDK transport 需要 parsedBody 透传\n // (handleRequest(req, res, parsedBody)),否则它再读 req.raw 时流已空,initialize 无响应。\n reply.hijack();\n await mcpHttp.handle(req.raw, reply.raw, req.body);\n });\n\n // SPA fallback(静态托管存在时)\n if (staticDir) {\n app.setNotFoundHandler(async (req, reply) => {\n if (req.url.startsWith('/api') || req.url.startsWith('/mcp')) {\n return reply.code(404).send({ error: 'Not Found' });\n }\n const index = path.join(staticDir, 'index.html');\n if (fs.existsSync(index)) {\n reply.type('text/html').send(fs.readFileSync(index, 'utf8'));\n return;\n }\n return reply.code(404).send({ error: 'Not Found' });\n });\n }\n\n const port = options.port ?? runtime.config.server.port;\n const host = options.host ?? runtime.config.server.host;\n if (!options.noListen) {\n await app.listen({ port, host });\n }\n\n return {\n app,\n close: async () => {\n mcpHttp.dispose();\n await app.close();\n disposeRuntime(runtime);\n }\n };\n}\n\nfunction toError(err: unknown): { code: string; message: string; requiredApproval?: boolean } {\n if (err instanceof GitOperationError) return { code: err.code, message: err.message };\n if (err instanceof RepoNotFoundError) return { code: 'REPO_NOT_FOUND', message: err.message };\n if (err instanceof Error) return { code: 'INTERNAL_ERROR', message: err.message.slice(0, 500) };\n return { code: 'INTERNAL_ERROR', message: String(err) };\n}","/**\n * Runtime:mcp-server 共享运行时上下文。\n * - 唯一数据库实例、审计日志、权限管理器、仓库存储与其管理模式;\n * - eventBus 承载 Git 变更事件(changed / log),被 Web SSE 与订阅者消费。\n */\nimport { EventEmitter } from 'node:events';\nimport type { DatabaseSync } from 'node:sqlite';\nimport {\n AuditLogger,\n PermissionManager,\n RepoStore,\n openDatabase\n} from '@shaohui_jin/git-cockpit-core';\nimport type { GitCockpitConfig } from '@shaohui_jin/git-cockpit-core';\nimport { ConfigStore } from './config.js';\nimport { RepoManager } from './repoManager.js';\n\nexport interface RuntimeOptions {\n dataDir?: string;\n configOverrides?: ConstructorParameters<typeof ConfigStore>[1];\n db?: DatabaseSync;\n}\n\nexport interface Runtime {\n configStore: ConfigStore;\n config: GitCockpitConfig;\n db: DatabaseSync;\n auditLogger: AuditLogger;\n permissions: PermissionManager;\n repoStore: RepoStore;\n repoManager: RepoManager;\n /** 全局事件总线:'repo-changed' | 'log' */\n eventBus: EventEmitter;\n}\n\n/** 创建运行时(唯一后端共享);须在进程生命周期内调用一次 */\nexport function createRuntime(options: RuntimeOptions = {}): Runtime {\n const configStore = new ConfigStore(options.dataDir ?? '~/.git-cockpit', options.configOverrides);\n const config = configStore.get();\n const db = options.db ?? openDatabase(config.storage.dataDir);\n const eventBus = new EventEmitter();\n eventBus.setMaxListeners(200);\n\n const auditLogger = new AuditLogger(db, config.logging);\n const permissions = new PermissionManager(config);\n const repoStore = new RepoStore(db);\n const repoManager = new RepoManager({ repoStore, eventBus, config });\n\n return {\n configStore,\n config,\n db,\n auditLogger,\n permissions,\n repoStore,\n repoManager,\n eventBus\n };\n}\n\n/** 关闭运行时占用的资源(数据库等) */\nexport function disposeRuntime(runtime: Runtime): void {\n try {\n runtime.eventBus.removeAllListeners();\n runtime.repoManager.dispose();\n } catch {\n /* ignore */\n }\n try {\n runtime.db.close();\n } catch {\n /* ignore */\n }\n}","/**\n * 配置管理:默认值 <- 配置文件(config.json)合并(浅/深合并)。\n * 运行时通过 ConfigStore 读写,权限配置界面直接修改其内部状态并持久化。\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { DEFAULT_CONFIG, expandHome } from '@shaohui_jin/git-cockpit-core';\nimport type { GitCockpitConfig } from '@shaohui_jin/git-cockpit-core';\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n\n/** 递归合并:target 上已存在的值优先保留,仅用 source 填充缺失字段与新增对象 */\nfunction deepMerge<T>(target: T, source: unknown): T {\n if (source === null || source === undefined) return target;\n if (Array.isArray(source)) {\n return (source as unknown[]) as T;\n }\n if (typeof source === 'object' && typeof target === 'object' && target !== null) {\n const out = { ...(target as Record<string, unknown>) } as Record<string, unknown>;\n for (const [k, v] of Object.entries(source as Record<string, unknown>)) {\n if (v !== undefined) out[k] = deepMerge(out[k], v);\n }\n return out as T;\n }\n return source as T;\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/**\n * ConfigStore:\n * - 配置存储在 <dataDir>/config.json;\n * - 数据目录默认 ~/.git-cockpit(支持 ~ 展开)。\n */\nexport class ConfigStore {\n private config: GitCockpitConfig;\n\n constructor(\n private readonly dataDir: string,\n overrides?: DeepPartial<GitCockpitConfig>\n ) {\n const dir = expandHome(dataDir);\n const file = path.join(dir, 'config.json');\n // 数据目录同时写入 storage.dataDir,保证数据库与配置存放在同一目录\n this.config = {\n ...this.load(file, overrides),\n storage: { dataDir: dir }\n };\n }\n\n private load(file: string, overrides?: DeepPartial<GitCockpitConfig>): GitCockpitConfig {\n let fileConfig: unknown = {};\n try {\n if (fs.existsSync(file)) {\n fileConfig = JSON.parse(fs.readFileSync(file, 'utf8'));\n }\n } catch (err) {\n // 配置文件损坏时回退默认(不阻断启动)\n console.warn(`[git-cockpit] 配置文件 ${file} 无法解析,已使用默认配置:`, (err as Error).message);\n }\n let merged = deepMerge(structuredClone(DEFAULT_CONFIG), fileConfig);\n if (overrides) merged = deepMerge(merged, overrides);\n return merged;\n }\n\n get(): GitCockpitConfig {\n return this.config;\n }\n\n /** 合并并持久化;返回新配置 */\n update(patch: DeepPartial<GitCockpitConfig>): GitCockpitConfig {\n this.config = deepMerge(this.config, patch);\n this.save();\n return this.config;\n }\n\n save(): void {\n const dir = expandHome(this.config.storage.dataDir);\n fs.mkdirSync(dir, { recursive: true });\n const file = path.join(dir, 'config.json');\n fs.writeFileSync(file, JSON.stringify(this.config, null, 2), 'utf8');\n }\n\n /** 供日志/调试:仅返回脱敏后的配置快照(隐藏敏感键名) */\n snapshot(): Record<string, unknown> {\n const copy = structuredClone(this.config);\n const redact = (v: unknown): unknown => {\n if (isPlainObject(v)) {\n const out: Record<string, unknown> = {};\n for (const [k, val] of Object.entries(v)) {\n const lower = k.toLowerCase();\n if (/password|token|secret|authorization|url/i.test(lower)) out[k] = '[REDACTED]';\n else out[k] = redact(val);\n }\n return out;\n }\n if (Array.isArray(v)) return v.map(redact);\n return v;\n };\n return redact(copy) as Record<string, unknown>;\n }\n}","/**\n * RepoManager:GitService 实例池 + 已打开仓库存储。\n * - 每个仓库根路径对应一个 GitService 实例(内含串行队列),Web 与 MCP 共用,\n * 保证\"唯一后端\"与锁全局有效;\n * - 打开仓库时校验合法性(GitService.open),并在 SQLite 中记录最近列表;\n * - 监听 Git 变更事件并转发到全局 eventBus,驱动前端实时刷新。\n */\nimport { EventEmitter } from 'node:events';\nimport * as path from 'node:path';\nimport {\n GitService,\n RepoNotFoundError,\n RepoStore\n} from '@shaohui_jin/git-cockpit-core';\nimport type { GitCockpitConfig, OpenedRepo } from '@shaohui_jin/git-cockpit-core';\n\nexport interface RepoHandle {\n service: GitService;\n record: OpenedRepo;\n}\n\nexport interface RepoManagerOptions {\n repoStore: RepoStore;\n eventBus: EventEmitter;\n config: GitCockpitConfig;\n}\n\nexport class RepoManager {\n /** repo 根路径(规范化绝对路径) -> repoId */\n private readonly pathToId = new Map<string, number>();\n private readonly idToService = new Map<number, GitService>();\n private readonly eventBus: EventEmitter;\n\n constructor(private readonly options: RepoManagerOptions) {\n this.eventBus = options.eventBus;\n // 为历史记录建立 路径->id 索引(服务实例懒创建)\n for (const repo of options.repoStore.list()) {\n this.pathToId.set(normalizePath(repo.path), repo.id);\n }\n }\n\n list(): (OpenedRepo & { available: boolean })[] {\n return this.options.repoStore.list().map((r) => ({\n ...r,\n available: r.path === '' || true\n }));\n }\n\n /** 取最近打开的仓库(懒创建服务) */\n async getCurrent(): Promise<RepoHandle | null> {\n const record = this.options.repoStore.list()[0];\n return record ? this.getByRecord(record) : null;\n }\n\n /** 按 id 取仓库(懒创建服务) */\n async getById(id: number): Promise<RepoHandle | null> {\n const record = this.options.repoStore.getById(id);\n if (!record) return null;\n try {\n return await this.getByRecord(record);\n } catch (err) {\n if (err instanceof Error && /NOT_A_GIT_REPO|REPO_NOT_FOUND/.test(err.message)) {\n return null;\n }\n throw err;\n }\n }\n\n /** 打开仓库:校验并记录;重复打开返回既有实例 */\n async open(repoPath: string): Promise<RepoHandle> {\n const service = await GitService.open(repoPath);\n const root = service.repoPath;\n const key = normalizePath(root);\n const existingId = this.pathToId.get(key);\n if (existingId !== undefined && existingId !== 0) {\n const record = this.options.repoStore.getById(existingId);\n if (record) {\n this.options.repoStore.open(root); // 刷新最近打开时间\n this.idToService.set(record.id, service);\n this.attach(service);\n return { service, record: this.options.repoStore.getById(record.id) ?? record };\n }\n }\n const record = this.options.repoStore.open(root);\n this.pathToId.set(key, record.id);\n this.idToService.set(record.id, service);\n this.attach(service);\n return { service, record };\n }\n\n /** 按路径取服务,未打开则报错(避免隐式副作用) */\n getByPath(repoPath: string): RepoHandle {\n const key = normalizePath(repoPath);\n const id = this.pathToId.get(key);\n if (id !== undefined && id !== 0) {\n const record = this.options.repoStore.getById(id);\n if (record) {\n const service = this.idToService.get(id);\n if (service) return { service, record };\n }\n }\n throw new RepoNotFoundError(`仓库未打开: ${repoPath}。请先打开仓库(POST /api/repos/open)。`);\n }\n\n /** 关闭:仅从内存服务池移除;保留历史记录 */\n close(id: number): void {\n const record = this.options.repoStore.getById(id);\n if (record) this.pathToId.delete(normalizePath(record.path));\n const svc = this.idToService.get(id);\n if (svc) svc.removeAllListeners('changed');\n this.idToService.delete(id);\n }\n\n /** 从历史记录中删除并关闭 */\n remove(id: number): void {\n this.close(id);\n this.options.repoStore.remove(id);\n }\n\n dispose(): void {\n for (const id of [...this.idToService.keys()]) this.close(id);\n }\n\n /** 按记录取句柄;服务缺失时(合法)创建并缓存 */\n private async getByRecord(record: OpenedRepo): Promise<RepoHandle> {\n let service = this.idToService.get(record.id);\n if (!service) {\n service = await GitService.open(record.path);\n this.pathToId.set(normalizePath(service.repoPath), record.id);\n this.idToService.set(record.id, service);\n this.attach(service);\n }\n return { service, record };\n }\n\n private attach(service: GitService): void {\n service.on('changed', (payload: { repoPath: string; command: string[] }) => {\n this.eventBus.emit('repo-changed', {\n repoPath: payload.repoPath,\n command: payload.command,\n at: new Date().toISOString()\n });\n });\n }\n}\n\nfunction normalizePath(p: string): string {\n let normalized = path.resolve(p);\n if (process.platform === 'win32') normalized = normalized.toLowerCase();\n return normalized;\n}"],"mappings":"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","names":["path","fs","PermissionManager","RepoNotFoundError","EventEmitter","RepoStore","path","record","EventEmitter","RepoStore","PermissionManager","RepoNotFoundError"]}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{az as qe,c as T,G as ae,d as ue,a6 as Nt,O as S,W as t,aj as g,aA as It,aB as Be,L as o,a0 as l,P as i,$ as h,u as A,M as b,F as P,ae as X,_ as f,Y as Ft,k as q,aq as We,Z as z,S as ve,w as ie,i as Ot,e as Fe,X as _e,ac as me,ak as Se,aC as Pt,aD as qt,aw as Bt,aE as Wt}from"./vue-CYNDxJyv.js";import{E as G,a as Ge,e as Gt}from"./element-plus-D8uC5V5h.js";(function(){const s=document.createElement("link").relList;if(s&&s.supports&&s.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))y(r);new MutationObserver(r=>{for(const C of r)if(C.type==="childList")for(const E of C.addedNodes)E.tagName==="LINK"&&E.rel==="modulepreload"&&y(E)}).observe(document,{childList:!0,subtree:!0});function u(r){const C={};return r.integrity&&(C.integrity=r.integrity),r.referrerPolicy&&(C.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?C.credentials="include":r.crossOrigin==="anonymous"?C.credentials="omit":C.credentials="same-origin",C}function y(r){if(r.ep)return;r.ep=!0;const C=u(r);fetch(r.href,C)}})();class nt extends Error{constructor(s,u,y,r){super(s),this.status=u,this.code=y,this.requiredApproval=r,this.name="ApiError"}}async function K(a,s,u){let y;try{y=await fetch(s,{method:a,headers:u!==void 0?{"Content-Type":"application/json"}:void 0,body:u!==void 0?JSON.stringify(u):void 0})}catch(r){throw new nt(`无法连接后端服务(${s}):${r instanceof Error?r.message:String(r)}`,0)}if(!y.ok){let r=y.statusText,C,E;try{const L=await y.json();r=L.error??r,C=L.code,E=L.requiredApproval}catch{}throw new nt(r,y.status,C,E)}return await y.json()}function Mt(){return K("GET","/api/repos")}function Ht(a){return K("POST","/api/repos/open",{path:a})}function jt(a){return K("DELETE",`/api/repos/${a}`)}function Jt(a){return K("GET",`/api/repos/${a}/status`)}function Kt(a,s={}){const u=new URLSearchParams;s.maxCount&&u.set("maxCount",String(s.maxCount)),s.all&&u.set("all","true"),s.path&&u.set("path",s.path),s.from&&u.set("from",s.from),s.to&&u.set("to",s.to),s.author&&u.set("author",s.author);const y=u.toString();return K("GET",`/api/repos/${a}/log${y?`?${y}`:""}`)}function Xt(a,s={}){const u=new URLSearchParams;s.from&&u.set("from",s.from),s.to&&u.set("to",s.to),s.path&&u.set("path",s.path),s.staged&&u.set("staged","true");const y=u.toString();return K("GET",`/api/repos/${a}/diff${y?`?${y}`:""}`)}function Yt(a,s){return K("GET",`/api/repos/${a}/show/${encodeURIComponent(s)}`)}function at(a){return K("GET",`/api/repos/${a}/branches`)}function Zt(a){return K("GET",`/api/repos/${a}/stashes`)}function Oe(a,s,u={}){return K("POST",`/api/repos/${a}/tools/${s}`,{params:u})}function Qt(a={}){const s=new URLSearchParams;a.limit&&s.set("limit",String(a.limit)),a.tool&&s.set("tool",a.tool);const u=s.toString();return K("GET",`/api/logs${u?`?${u}`:""}`)}function el(){return K("GET","/api/settings")}function tl(a){return K("PUT","/api/settings",{permissions:a})}function ll(){return K("GET","/api/health")}function sl(a){const s=new EventSource("/api/events");return a.onRepoChanged&&s.addEventListener("repo-changed",u=>{var y;return(y=a.onRepoChanged)==null?void 0:y.call(a,JSON.parse(u.data))}),a.onLog&&s.addEventListener("log",()=>{var u;return(u=a.onLog)==null?void 0:u.call(a)}),a.onError&&(s.onerror=u=>{var y;return(y=a.onError)==null?void 0:y.call(a,u)}),()=>s.close()}const Te=qe("repos",{state:()=>({repos:[],currentId:null,loading:!1,error:null,healthOk:null}),getters:{current(a){return a.repos.find(s=>s.id===a.currentId)??null},currentPath(a){var s;return((s=a.repos.find(u=>u.id===a.currentId))==null?void 0:s.path)??null}},actions:{async checkHealth(){try{const a=await ll();this.healthOk=a.ok}catch{this.healthOk=!1}return this.healthOk===!0},async load(){var a;this.loading=!0,this.error=null;try{const{repos:s}=await Mt();this.repos=s,(!this.currentId||!s.some(u=>u.id===this.currentId))&&(this.currentId=((a=s[0])==null?void 0:a.id)??null)}catch(s){this.error=s instanceof Error?s.message:String(s)}finally{this.loading=!1}},async open(a){const{repo:s}=await Ht(a);return await this.load(),s},async remove(a){await jt(a),this.currentId===a&&(this.currentId=null),await this.load()},switchTo(a){this.currentId=a}}}),ot=qe("settings",{state:()=>({permissions:null,tools:[],loading:!1,saving:!1,error:null}),actions:{async load(){this.loading=!0,this.error=null;try{const a=await el();this.permissions=a.permissions,this.tools=a.tools}catch(a){this.error=a instanceof Error?a.message:String(a)}finally{this.loading=!1}},async save(a){if(this.permissions){this.saving=!0,this.error=null;try{await tl(a),this.permissions={...this.permissions,...a}}catch(s){throw this.error=s instanceof Error?s.message:String(s),s}finally{this.saving=!1}}}}}),it=T(0),nl=()=>{it.value+=1};function Ee(){return{revision:it,bump:nl}}const al={class:"repo-box"},ol=["title"],il={class:"menu-icon"},rl={class:"aside-footer"},ul=ae({__name:"App",setup(a){const s=Te(),u=ot(),y=It(),r=Be(),{bump:C}=Ee(),E=[{path:"/status",label:"状态",icon:"◧"},{path:"/history",label:"历史",icon:"◫"},{path:"/repos",label:"仓库管理",icon:"▤"},{path:"/logs",label:"操作日志",icon:"≡"},{path:"/settings",label:"设置",icon:"⚙"}],L=q(()=>{if(!s.currentId)return"未选择仓库";const c=s.repos.find(d=>d.id===s.currentId);return(c==null?void 0:c.path)??"未选择仓库"});let I=null;return ue(async()=>{await s.checkHealth(),await Promise.all([s.load(),u.load().catch(()=>{})]),I=sl({onRepoChanged:()=>C(),onLog:()=>C(),onError:()=>{}})}),Nt(()=>{I==null||I()}),(c,d)=>{const p=g("el-menu-item"),_=g("el-menu"),m=g("el-tag"),x=g("el-aside"),U=g("router-view"),B=g("el-main"),M=g("el-container");return o(),S(M,{class:"app-shell"},{default:t(()=>[l(x,{width:"220px",class:"app-aside"},{default:t(()=>[d[4]||(d[4]=i("div",{class:"brand"},[i("span",{class:"brand-mark"},"⌘"),i("span",{class:"brand-name"},"Git Cockpit")],-1)),i("div",al,[d[0]||(d[0]=i("div",{class:"repo-box-label"},"当前仓库",-1)),i("div",{class:"repo-box-path mono",title:L.value},h(L.value),9,ol)]),l(_,{"default-active":A(y).path,class:"nav-menu"},{default:t(()=>[(o(),b(P,null,X(E,D=>l(p,{key:D.path,index:D.path,onClick:H=>A(r).push(D.path)},{default:t(()=>[i("span",il,h(D.icon),1),i("span",null,h(D.label),1)]),_:2},1032,["index","onClick"])),64))]),_:1},8,["default-active"]),i("div",rl,[A(s).healthOk?(o(),S(m,{key:0,size:"small",type:"success"},{default:t(()=>[...d[1]||(d[1]=[f("后端已连接",-1)])]),_:1})):A(s).healthOk===!1?(o(),S(m,{key:1,size:"small",type:"danger"},{default:t(()=>[...d[2]||(d[2]=[f("后端离线",-1)])]),_:1})):(o(),S(m,{key:2,size:"small",type:"info"},{default:t(()=>[...d[3]||(d[3]=[f("连接中…",-1)])]),_:1}))])]),_:1}),l(M,{class:"app-main"},{default:t(()=>[l(B,{class:"main-content"},{default:t(()=>[l(U,null,{default:t(({Component:D})=>[(o(),S(Ft(D)))]),_:1})]),_:1})]),_:1})]),_:1})}}}),oe=(a,s)=>{const u=a.__vccOpts||a;for(const[y,r]of s)u[y]=r;return u},dl=oe(ul,[["__scopeId","data-v-b0a0ed56"]]),cl={class:"page"},pl={class:"open-row"},fl={class:"list-header"},ml={class:"repo-path mono"},vl=ae({__name:"ReposView",setup(a){const s=Te(),u=Be(),y=T(""),r=T(!1);async function C(){const d=y.value.trim();if(!d){G.warning("请输入仓库路径");return}r.value=!0;try{const p=await s.open(d);G.success(`已打开 ${p.path}`),s.switchTo(p.id),await u.push("/status")}catch(p){G.error(p instanceof Error?p.message:String(p))}finally{r.value=!1}}async function E(d,p){try{await Ge.confirm(`确定从列表移除 ${p}?注意:不会删除磁盘上的仓库文件。`,"移除仓库",{type:"warning",confirmButtonText:"移除",cancelButtonText:"取消"})}catch{return}try{await s.remove(d),G.success("已移除")}catch(_){G.error(_ instanceof Error?_.message:String(_))}}function L(d){s.switchTo(d),u.push("/status")}function I(d){const p=new Date(d);return Number.isNaN(p.getTime())?d:p.toLocaleString()}function c({row:d}){return d.id===s.currentId?"is-current":""}return ue(()=>s.load()),(d,p)=>{const _=g("el-input"),m=g("el-button"),x=g("el-card"),U=g("el-empty"),B=g("el-table-column"),M=g("el-table");return o(),b("div",cl,[p[8]||(p[8]=i("h2",{class:"page-title"},"仓库管理",-1)),l(x,{class:"open-card",shadow:"never"},{header:t(()=>[...p[2]||(p[2]=[f("打开本地仓库",-1)])]),default:t(()=>[i("div",pl,[l(_,{modelValue:y.value,"onUpdate:modelValue":p[0]||(p[0]=D=>y.value=D),placeholder:"输入本地 Git 仓库路径,例如 D:\\project\\repo 或 /Users/me/code/app",clearable:"",onKeyup:We(C,["enter"])},null,8,["modelValue"]),l(m,{type:"primary",loading:r.value,onClick:C},{default:t(()=>[...p[3]||(p[3]=[f("打开",-1)])]),_:1},8,["loading"])])]),_:1}),l(x,{shadow:"never",class:"list-card"},{header:t(()=>[i("div",fl,[p[5]||(p[5]=i("span",null,"最近打开的仓库",-1)),l(m,{text:"",type:"primary",loading:A(s).loading,onClick:p[1]||(p[1]=D=>A(s).load())},{default:t(()=>[...p[4]||(p[4]=[f("刷新",-1)])]),_:1},8,["loading"])])]),default:t(()=>[!A(s).loading&&A(s).repos.length===0?(o(),S(U,{key:0,description:"尚未打开任何仓库"})):(o(),S(M,{key:1,data:A(s).repos,size:"default","highlight-current-row":"","row-class-name":c},{default:t(()=>[l(B,{label:"路径","min-width":"320"},{default:t(({row:D})=>[i("div",ml,h(D.path),1)]),_:1}),l(B,{label:"最近打开",width:"190"},{default:t(({row:D})=>[f(h(I(D.lastOpenedAt)),1)]),_:1}),l(B,{label:"操作",width:"180",align:"right"},{default:t(({row:D})=>[l(m,{size:"small",text:"",type:"primary",onClick:H=>L(D.id)},{default:t(()=>[...p[6]||(p[6]=[f("进入",-1)])]),_:1},8,["onClick"]),l(m,{size:"small",text:"",type:"danger",onClick:H=>E(D.id,D.path)},{default:t(()=>[...p[7]||(p[7]=[f("移除",-1)])]),_:1},8,["onClick"])]),_:1})]),_:1},8,["data"]))]),_:1})])}}}),_l=oe(vl,[["__scopeId","data-v-ff70c4b1"]]);function hl(a){const s=Be(),u=T(!1),y=T(null),r=T(!1),C=q(()=>a()!==null);async function E(p,_={}){const m=a();if(m===null)return G.warning("请先选择仓库"),!1;try{const x=await Oe(m,p,{..._,dryRun:!0});return x.success?(y.value={tool:p,params:_,preview:x.preview??x.result},u.value=!0,!0):(c(x),!1)}catch(x){return d(x),!1}}async function L(){const p=a(),_=y.value;if(p===null||!_)return null;r.value=!0,u.value=!1;try{const m=await Oe(p,_.tool,_.params);return m.success?(m.backupCreated?G.success(`${_.tool} 执行成功(已自动备份:${m.backupCreated.branch??"分支"}${m.backupCreated.stashRef?` / ${m.backupCreated.stashRef}`:""})`):G.success(`${_.tool} 执行成功`),m):(c(m),m)}catch(m){return d(m),null}finally{r.value=!1,y.value=null}}function I(){u.value=!1,y.value=null}function c(p){var _,m,x;if(((_=p.error)==null?void 0:_.code)==="PERMISSION_DENIED"){p.error.requiredApproval?Ge.confirm(`操作 ${p.tool} 属于高风险操作,当前默认禁用,需要在设置中开启后才能执行。是否前往设置?`,"需要审批",{confirmButtonText:"去开启",cancelButtonText:"取消",type:"warning"}).then(()=>s.push("/settings")).catch(()=>{}):G.error(((m=p.error)==null?void 0:m.message)??"操作被拒绝");return}G.error(((x=p.error)==null?void 0:x.message)??"操作失败")}function d(p){const _=p instanceof Error?p.message:String(p);G.error(_)}return{confirmVisible:u,pending:y,executing:r,canRun:C,previewAndConfirm:E,executeConfirmed:L,cancel:I}}const gl={key:2,class:"cmd-box"},yl={class:"cmd-pre mono"},bl={key:3,class:"files-box"},kl={class:"cmd-label"},wl={key:4,class:"note-text"},Cl={class:"risk-tag"},$l=ae({__name:"ConfirmDialog",props:{visible:{type:Boolean},tool:{},preview:{default:null},note:{default:""}},emits:["update:visible","confirm","cancel"],setup(a,{emit:s}){const u=a,y=s,r=q(()=>{const d=u.preview;return d&&"command"in d&&typeof d.command=="string"?d.command:""}),C=q(()=>{const d=u.preview;return d&&"risk"in d&&typeof d.risk=="string"?d.risk:"write"}),E=q(()=>{const d=u.preview;return d&&"affectedFiles"in d&&Array.isArray(d.affectedFiles)?d.affectedFiles:[]}),L=q(()=>C.value==="dangerous"?{type:"danger",label:"高风险"}:C.value==="write"?{type:"warning",label:"写操作"}:{type:"info",label:"只读"});function I(){y("confirm")}function c(){y("cancel"),y("update:visible",!1),G.info("操作已取消")}return(d,p)=>{const _=g("el-alert"),m=g("el-scrollbar"),x=g("el-tag"),U=g("el-button"),B=g("el-dialog");return o(),S(B,{"model-value":a.visible,title:`执行操作:${a.tool}`,width:"560px","close-on-click-modal":!1,onClosed:p[0]||(p[0]=M=>y("update:visible",!1))},{footer:t(()=>[i("span",Cl,[l(x,{type:L.value.type,size:"small",effect:"dark"},{default:t(()=>[f(h(L.value.label),1)]),_:1},8,["type"])]),l(U,{onClick:c},{default:t(()=>[...p[4]||(p[4]=[f("取消",-1)])]),_:1}),l(U,{type:"primary",danger:C.value==="dangerous",onClick:I},{default:t(()=>[...p[5]||(p[5]=[f("确认执行",-1)])]),_:1},8,["danger"])]),default:t(()=>[C.value==="dangerous"?(o(),S(_,{key:0,type:"error",closable:!1,"show-icon":"",class:"risk-alert"},{title:t(()=>[...p[1]||(p[1]=[f("这是",-1),i("b",null,"高风险",-1),f("操作,将自动创建备份(临时分支 + stash)",-1)])]),_:1})):C.value==="write"?(o(),S(_,{key:1,type:"warning",closable:!1,"show-icon":"",class:"risk-alert"},{title:t(()=>[...p[2]||(p[2]=[f("写操作:执行后将修改仓库状态",-1)])]),_:1})):z("",!0),r.value?(o(),b("div",gl,[p[3]||(p[3]=i("div",{class:"cmd-label"},"将执行的命令(dry-run 预览,未实际执行)",-1)),i("pre",yl,h(r.value),1)])):z("",!0),E.value.length?(o(),b("div",bl,[i("div",kl,"涉及文件("+h(E.value.length)+")",1),l(m,{"max-height":"140px"},{default:t(()=>[(o(!0),b(P,null,X(E.value,M=>(o(),b("div",{key:M,class:"file-item mono"},h(M),1))),128))]),_:1})])):z("",!0),a.note?(o(),b("div",wl,h(a.note),1)):z("",!0)]),_:1},8,["model-value","title"])}}}),xl=oe($l,[["__scopeId","data-v-0e5cd08f"]]),Vl={class:"diff-pre mono"},Sl=ae({__name:"DiffViewer",props:{patch:{}},setup(a){const s=a,u=q(()=>{const y=[];for(const r of s.patch.split(`
|
|
2
|
-
`)){const C=r.startsWith("diff --git")||r.startsWith("index ")||r.startsWith("---")||r.startsWith("+++")||r.startsWith("new file")||r.startsWith("deleted file")||r.startsWith("similarity")||r.startsWith("rename ")?"meta":r.startsWith("@@")?"hunk":r.startsWith("+")?"add":r.startsWith("-")?"del":"plain";y.push({type:C,text:r})}return y});return(y,r)=>(o(),b("pre",Vl,[(o(!0),b(P,null,X(u.value,(C,E)=>(o(),b(P,{key:E},[i("span",{class:ve(C.type)},h(C.text),3),r[0]||(r[0]=f(`
|
|
3
|
-
`,-1))],64))),128))]))}}),Pe=oe(Sl,[["__scopeId","data-v-cd09efab"]]),Tl={class:"page status-page"},El={class:"page-head"},zl={class:"head-branch"},Dl={key:0,class:"tracking mono"},Al={key:1,class:"sync ahead"},Rl={key:2,class:"sync behind"},Ll={class:"repo-path mono"},Ul={key:1,class:"empty-tip"},Nl={key:2,class:"status-layout"},Il={class:"panel-head"},Fl={class:"panel-actions"},Ol={class:"tree-scroll"},Pl={class:"group-label"},ql=["onDblclick"],Bl={class:"node-name"},Wl={class:"node-actions"},Gl=["onDblclick"],Ml={class:"node-name"},Hl={class:"node-actions"},jl={class:"status-content"},Jl={class:"toolbar"},Kl={class:"toolbar-group"},Xl={class:"card-head"},Yl={class:"card-actions"},Zl={class:"tab-label"},Ql={class:"tab-hint"},es={key:0,class:"file-empty"},ts={key:1,class:"file-list"},ls=["title"],ss={class:"card-head"},ns={class:"card-title"},as={class:"card-actions"},os={key:0,class:"file-empty"},is={key:1,class:"stash-list mono"},rs={class:"stash-main"},us={class:"stash-ref"},ds=["title"],cs={key:0,class:"stash-date"},ps={class:"stash-actions"},fs={class:"diff-wrap"},ms={class:"diff-summary"},vs={class:"add"},_s={class:"del"},hs={key:0,class:"warn"},gs={key:0,class:"file-empty"},ys={key:0,class:"stash-files"},bs={class:"stash-files-head"},ks={class:"stash-files-title"},ws={class:"card-actions"},Cs={class:"file-list compact"},$s={class:"file-path mono"},xs=ae({__name:"StatusView",setup(a){const s=Te(),{revision:u}=Ee(),y=()=>s.currentId,r=T(null),C=T([]),E=T([]),L=T(!1),I=T(""),c=Fe({visible:!1,title:"",diff:null,patch:"",loading:!1}),d=T(!1),p=T(""),_=T(!1),m=T(""),x=T(""),U=T(!1),B=T(""),M=T(!1),D=T(!1),H=T(""),w=Fe(new Set),te=q(()=>w.size),ne=T("unstaged"),{confirmVisible:W,pending:$,canRun:Q,previewAndConfirm:O,executeConfirmed:de,cancel:re}=hl(y),he=q(()=>{var n;return((n=s.current)==null?void 0:n.path)??""}),De=q(()=>{var n,e;return((n=r.value)==null?void 0:n.currentShort)??((e=r.value)==null?void 0:e.current)??""}),ge=q(()=>{var n;return((n=r.value)==null?void 0:n.ahead)??0}),ce=q(()=>{var n;return((n=r.value)==null?void 0:n.behind)??0}),Y=q(()=>{const n=r.value;if(!n)return{unstaged:[],staged:[],untracked:[],all:[]};const e=n.untracked.map(J=>({path:J,status:"??",indexStatus:"?",workTreeStatus:"?",staged:!1,untracked:!0,conflicted:!1})),N=[...n.staged,...n.unstaged,...e],k=new Set,R=N.filter(J=>k.has(J.path)?!1:(k.add(J.path),J.path!==""));return{unstaged:n.unstaged,staged:n.staged,untracked:e,all:R}}),Ae=q(()=>[{key:"unstaged",label:"未暂存",hint:"已修改,未 git add",count:Y.value.unstaged.length},{key:"staged",label:"已暂存",hint:"已 git add,未提交",count:Y.value.staged.length},{key:"untracked",label:"未跟踪",hint:"工作区新文件",count:Y.value.untracked.length},{key:"all",label:"全部",hint:"全部更改",count:Y.value.all.length}]),Re=q(()=>Y.value[ne.value]??[]),ye=q(()=>Y.value.all.length),be=q(()=>{const n=C.value.filter(R=>!R.remote),e=C.value.filter(R=>R.remote),N=new Map;for(const R of e){const J=R.name.indexOf("/"),le=J===-1?R.name:R.name.slice(0,J),ee=N.get(le)??[];ee.push(R),N.set(le,ee)}const k=[];k.push({key:"group:local",type:"group",label:`本地分支(${n.length})`,children:n.map(R=>({key:"l:"+R.name,type:"local",label:R.name,branch:R}))});for(const[R,J]of N){const le=[...J].sort((ee,Le)=>ee.name.localeCompare(Le.name));k.push({key:"group:remote:"+R,type:"group",label:`远程 · ${R}(${le.length})`,children:le.map(ee=>({key:"r:"+ee.name,type:"remote",label:ee.name.slice(R.length+1),branch:ee}))})}return k});function ke(n){if(!n||n.remote||!n.upstream)return"";const e=[];return(n.ahead??0)>0&&e.push(`↑${n.ahead}`),(n.behind??0)>0&&e.push(`↓${n.behind}`),e.join(" ")}async function we(){const n=y();if(n!==null){L.value=!0,I.value="";try{r.value=await Jt(n)}catch(e){I.value=e instanceof Error?e.message:String(e),r.value=null}finally{L.value=!1}}}async function F(){const n=y();if(n!==null)try{const{branches:e}=await at(n);C.value=e}catch{}}async function Z(){const n=y();if(n!==null)try{E.value=await Zt(n)}catch{}}async function pe(){await Promise.all([we(),F(),Z()])}async function j(n,e={}){await O(n,e)&&G.info("请在确认对话框中查看 dry-run 预览")}async function ut(){const n=await de();n!=null&&n.success&&await pe()}function Me(n){j("git_add",{paths:[n.path]})}function dt(n){j("git_unstage",{paths:[n.path]})}function ct(n){n.staged?dt(n):Me(n)}function pt(){j("git_add",{})}function ft(){var e;const n=((e=r.value)==null?void 0:e.staged.map(N=>N.path))??[];if(n.length===0){G.info("没有已暂存的文件");return}j("git_unstage",{paths:n})}async function mt(n){const e=y();if(e!==null){c.title=`${n.path}(${n.staged?"已 git add":"工作区修改"})`,c.diff=null,c.patch="",c.visible=!0,c.loading=!0;try{c.diff=await Xt(e,{path:n.path,staged:n.staged})}catch(N){G.error(N instanceof Error?N.message:String(N)),c.visible=!1}finally{c.loading=!1}}}async function vt(n){var N;const e=y();if(e!==null){c.title=`${n.ref} · ${n.message||"(无说明)"}`,c.diff=null,c.patch="",c.visible=!0,c.loading=!0;try{const k=await Oe(e,"git_stash_show",{index:n.index});if(k.success){const R=k.result??{};c.patch=R.patch??""}else G.error(((N=k.error)==null?void 0:N.message)??"查看差异失败"),c.visible=!1}catch(k){G.error(k instanceof Error?k.message:String(k)),c.visible=!1}finally{c.loading=!1}}}function He(n){w.has(n)?w.delete(n):w.add(n)}function je(n){return w.has(n)}function Je(){w.clear()}function _t(){for(const n of Re.value)w.add(n.path)}function ht(){for(const n of Ce.value)w.add(n)}const Ce=q(()=>{const n=r.value;if(!n)return[];const e=new Set;return[...n.unstaged.map(N=>N.path),...n.untracked].filter(N=>e.has(N)?!1:(e.add(N),N!==""))});function gt(){U.value=!0}function yt(){const n=Ce.value,e=[...w].filter(R=>n.includes(R)),N=e.some(R=>{var J;return((J=r.value)==null?void 0:J.untracked.includes(R))??!1}),k={};B.value.trim()&&(k.message=B.value.trim()),e.length&&(k.paths=e),(M.value||N)&&(k.includeUntracked=!0),U.value=!1,w.clear(),j("git_stash",k)}function bt(){j("git_stash",{})}function kt(n){j("git_stash_apply",{index:n.index})}function wt(n){j("git_stash_pop",{index:n.index})}function Ct(n){Ge.confirm(`确定删除 ${n.ref} 吗?删除后不可直接恢复。`,"删除 stash",{type:"warning",confirmButtonText:"继续",cancelButtonText:"取消"}).then(()=>void j("git_stash_drop",{index:n.index})).catch(()=>{})}function $e(n){n.branch&&j("git_checkout",{branch:n.branch.name})}function $t(){j("git_pull")}function xt(){j("git_push")}function Ke(){const n=p.value.trim();if(!n){G.warning("请输入提交信息");return}d.value=!1,j("git_commit",{message:n})}function Vt(){const n=m.value.trim();if(!n){G.warning("请输入分支名");return}_.value=!1,j("git_branch_create",x.value.trim()?{name:n,startPoint:x.value.trim()}:{name:n})}function St(){const n=H.value.trim()||"HEAD";D.value=!1,H.value="",j("git_reset_hard",{target:n})}function Tt(n){return n.untracked?"?":n.staged?n.indexStatus||"M":n.workTreeStatus||"M"}function Et(n){return n.untracked?"new":n.conflicted?"conflict":n.staged?"add":"mod"}function zt(n){if(!n)return"";const e=new Date(n);return Number.isNaN(e.getTime())?n:e.toLocaleString()}function Dt(n){return n.split(`
|
|
4
|
-
`)[0]??""}return ie(y,()=>{c.diff=null,c.patch="",c.visible=!1,w.clear(),pe()}),ie(u,()=>{pe()}),ue(()=>{pe()}),(n,e)=>{var Ye,Ze,Qe,et,tt;const N=g("el-tag"),k=g("el-button"),R=g("el-alert"),J=g("el-empty"),le=g("el-card"),ee=g("el-badge"),Le=g("el-tooltip"),Ue=g("el-checkbox"),At=g("el-tab-pane"),Rt=g("el-tabs"),Lt=g("el-drawer"),fe=g("el-input"),xe=g("el-dialog"),Ve=g("el-form-item"),Xe=g("el-form"),Ut=Se("loading");return o(),b("div",Tl,[i("div",El,[e[26]||(e[26]=i("h2",{class:"page-title"},"仓库状态",-1)),i("div",zl,[l(N,{type:"primary",effect:"dark",class:"branch-tag"},{default:t(()=>[f(h(De.value||"—"),1)]),_:1}),(Ye=r.value)!=null&&Ye.tracking?(o(),b("span",Dl,h(r.value.tracking),1)):z("",!0),ge.value?(o(),b("span",Al,"↑"+h(ge.value),1)):z("",!0),ce.value?(o(),b("span",Rl,"↓"+h(ce.value),1)):z("",!0),(Ze=r.value)!=null&&Ze.isClean?(o(),S(N,{key:3,type:"success",effect:"plain",size:"small"},{default:t(()=>[...e[24]||(e[24]=[f("工作区干净",-1)])]),_:1})):r.value?(o(),S(N,{key:4,type:"warning",effect:"plain",size:"small"},{default:t(()=>[f(h(ye.value)+" 处更改",1)]),_:1})):z("",!0),i("span",Ll,h(he.value),1)]),l(k,{loading:L.value,onClick:pe},{default:t(()=>[...e[25]||(e[25]=[f("刷新",-1)])]),_:1},8,["loading"])]),I.value?(o(),S(R,{key:0,title:I.value,type:"error",closable:!1,"show-icon":"",class:"mb"},null,8,["title"])):z("",!0),A(Q)?(o(),b("div",Nl,[l(le,{shadow:"never",class:"branch-panel"},{header:t(()=>[i("div",Il,[e[29]||(e[29]=i("span",{class:"panel-title"},"分支 Branch",-1)),i("div",Fl,[l(k,{size:"small",text:"",onClick:F},{default:t(()=>[...e[27]||(e[27]=[f("刷新",-1)])]),_:1}),l(k,{size:"small",text:"",type:"primary",onClick:e[0]||(e[0]=v=>_.value=!0)},{default:t(()=>[...e[28]||(e[28]=[f("+ 新建",-1)])]),_:1})])])]),default:t(()=>[i("div",Ol,[(o(!0),b(P,null,X(be.value,v=>(o(),b(P,{key:v.key},[v.type==="group"?(o(),b(P,{key:0},[i("div",Pl,h(v.label),1),(o(!0),b(P,null,X(v.children,V=>{var se,Ne,lt;return o(),b(P,{key:V.key},[V.type==="local"?(o(),b("div",{key:0,class:ve(["branch-node",{current:(se=V.branch)==null?void 0:se.current}]),onDblclick:Ie=>{var st;return((st=V.branch)==null?void 0:st.current)||$e(V)}},[i("span",Bl,h(V.label),1),i("span",{class:ve(["node-sync",{clean:!ke(V.branch)&&((Ne=V.branch)==null?void 0:Ne.upstream)}])},h(ke(V.branch)),3),i("span",Wl,[(lt=V.branch)!=null&<.current?(o(),b(P,{key:0},[l(k,{size:"small",text:"",type:"success",onClick:me($t,["stop"])},{default:t(()=>[...e[30]||(e[30]=[f("拉取",-1)])]),_:1}),l(k,{size:"small",text:"",type:"primary",onClick:me(xt,["stop"])},{default:t(()=>[...e[31]||(e[31]=[f("推送",-1)])]),_:1})],64)):(o(),S(k,{key:1,size:"small",text:"",type:"primary",onClick:me(Ie=>$e(V),["stop"])},{default:t(()=>[...e[32]||(e[32]=[f("切换",-1)])]),_:1},8,["onClick"]))])],42,ql)):z("",!0),V.type==="remote"?(o(),b("div",{key:1,class:"branch-node remote",onDblclick:Ie=>$e(V)},[i("span",Ml,h(V.label),1),i("span",Hl,[l(k,{size:"small",text:"",type:"primary",onClick:me(Ie=>$e(V),["stop"])},{default:t(()=>[...e[33]||(e[33]=[f("切换",-1)])]),_:1},8,["onClick"])])],40,Gl)):z("",!0)],64)}),128))],64)):z("",!0)],64))),128)),be.value.length===0?(o(),S(J,{key:0,description:"暂无分支","image-size":60})):z("",!0)])]),_:1}),i("div",jl,[l(le,{shadow:"never",class:"toolbar-card"},{default:t(()=>{var v;return[i("div",Jl,[i("div",Kl,[l(k,{type:"primary",onClick:e[1]||(e[1]=V=>d.value=!0)},{default:t(()=>[...e[34]||(e[34]=[f("提交 Commit",-1)])]),_:1}),l(k,{plain:"",onClick:pt},{default:t(()=>[...e[35]||(e[35]=[f("暂存全部",-1)])]),_:1}),l(k,{plain:"",disabled:(((v=r.value)==null?void 0:v.staged.length)??0)===0,onClick:ft},{default:t(()=>[...e[36]||(e[36]=[f("取消暂存全部",-1)])]),_:1},8,["disabled"]),l(ee,{value:te.value,hidden:te.value===0,offset:[2,4]},{default:t(()=>[l(k,{type:"success",plain:"",onClick:gt},{default:t(()=>[...e[37]||(e[37]=[f("暂存改动 Stash",-1)])]),_:1})]),_:1},8,["value","hidden"]),l(Le,{content:"Shelve Silently:使用默认说明,一键暂存全部更改",placement:"top"},{default:t(()=>[l(k,{plain:"",onClick:bt},{default:t(()=>[...e[38]||(e[38]=[f("静默 Stash",-1)])]),_:1})]),_:1}),l(k,{type:"danger",plain:"",onClick:e[2]||(e[2]=V=>D.value=!0)},{default:t(()=>[...e[39]||(e[39]=[f("硬重置",-1)])]),_:1}),l(k,{type:"danger",plain:"",onClick:e[3]||(e[3]=V=>j("git_clean"))},{default:t(()=>[...e[40]||(e[40]=[f("清理未跟踪",-1)])]),_:1})])])]}),_:1}),(Qe=r.value)!=null&&Qe.conflicted.length?(o(),S(R,{key:0,title:"存在合并冲突,请先解决冲突(修改文件后重新 git add 并提交)",type:"error",closable:!1,"show-icon":"",class:"mb"})):z("",!0),l(le,{shadow:"never",class:"changes-card"},{header:t(()=>[i("div",Xl,[e[43]||(e[43]=i("span",{class:"card-title"},"更改 Changes",-1)),i("div",Yl,[te.value?(o(),S(N,{key:0,size:"small",type:"primary",effect:"plain"},{default:t(()=>[f("已选 "+h(te.value),1)]),_:1})):z("",!0),l(k,{size:"small",text:"",onClick:_t},{default:t(()=>[...e[41]||(e[41]=[f("全选本组",-1)])]),_:1}),te.value?(o(),S(k,{key:1,size:"small",text:"",type:"danger",onClick:Je},{default:t(()=>[...e[42]||(e[42]=[f("清除选择",-1)])]),_:1})):z("",!0)])])]),default:t(()=>[l(Rt,{modelValue:ne.value,"onUpdate:modelValue":e[4]||(e[4]=v=>ne.value=v),class:"changes-tabs"},{default:t(()=>[(o(!0),b(P,null,X(Ae.value,v=>(o(),S(At,{key:v.key,name:v.key},{label:t(()=>[i("span",Zl,[f(h(v.label)+" ",1),l(ee,{value:v.count,hidden:v.count===0,type:"primary",class:"tab-badge"},null,8,["value","hidden"])])]),default:t(()=>[i("div",Ql,"「"+h(v.hint)+"」",1),v.count===0?(o(),b("div",es,"没有"+h(v.label)+"的文件",1)):(o(),b("div",ts,[(o(!0),b(P,null,X(Y.value[v.key],V=>(o(),b("div",{key:v.key+V.path,class:"file-row"},[l(Ue,{"model-value":je(V.path),onChange:se=>He(V.path)},null,8,["model-value","onChange"]),i("span",{class:ve(["file-status",Et(V)])},h(Tt(V)),3),i("span",{class:"file-path mono",title:V.path},h(V.path),9,ls),l(k,{size:"small",text:"",type:"primary",onClick:se=>mt(V)},{default:t(()=>[...e[44]||(e[44]=[f("查看差异",-1)])]),_:1},8,["onClick"]),V.conflicted?(o(),S(k,{key:0,size:"small",text:"",type:"warning",onClick:se=>Me(V)},{default:t(()=>[...e[45]||(e[45]=[f("标记已解决(git add)",-1)])]),_:1},8,["onClick"])):(o(),S(k,{key:1,size:"small",text:"",type:V.staged?"warning":"success",onClick:se=>ct(V)},{default:t(()=>[f(h(V.staged?"取消暂存":"暂存"),1)]),_:2},1032,["type","onClick"]))]))),128))]))]),_:2},1032,["name"]))),128))]),_:1},8,["modelValue"])]),_:1}),l(le,{shadow:"never",class:"stash-card"},{header:t(()=>[i("div",ss,[i("span",ns,"暂存列表 Stash("+h(E.value.length)+")",1),i("div",as,[l(k,{size:"small",text:"",onClick:Z},{default:t(()=>[...e[46]||(e[46]=[f("刷新",-1)])]),_:1})])])]),default:t(()=>[E.value.length===0?(o(),b("div",os,"没有暂存的更改")):(o(),b("div",is,[(o(!0),b(P,null,X(E.value,v=>(o(),b("div",{key:v.ref,class:"stash-row"},[i("div",rs,[i("span",us,h(v.ref),1),i("span",{class:"stash-msg",title:v.message},h(Dt(v.message)||"(无说明)"),9,ds),v.date?(o(),b("span",cs,h(zt(v.date)),1)):z("",!0)]),i("div",ps,[l(k,{size:"small",text:"",type:"primary",onClick:V=>vt(v)},{default:t(()=>[...e[47]||(e[47]=[f("差异",-1)])]),_:1},8,["onClick"]),l(k,{size:"small",text:"",type:"success",onClick:V=>kt(v)},{default:t(()=>[...e[48]||(e[48]=[f("应用",-1)])]),_:1},8,["onClick"]),l(k,{size:"small",text:"",type:"warning",onClick:V=>wt(v)},{default:t(()=>[...e[49]||(e[49]=[f("恢复",-1)])]),_:1},8,["onClick"]),l(k,{size:"small",text:"",type:"danger",onClick:V=>Ct(v)},{default:t(()=>[...e[50]||(e[50]=[f("删除",-1)])]),_:1},8,["onClick"])])]))),128))]))]),_:1})])])):(o(),b("div",Ul,"请先在「仓库管理」中打开一个仓库")),l(xl,{visible:A(W),"onUpdate:visible":e[5]||(e[5]=v=>Ot(W)?W.value=v:null),tool:((et=A($))==null?void 0:et.tool)??"",preview:((tt=A($))==null?void 0:tt.preview)??null,onConfirm:ut,onCancel:A(re)},null,8,["visible","tool","preview","onCancel"]),l(Lt,{modelValue:c.visible,"onUpdate:modelValue":e[6]||(e[6]=v=>c.visible=v),title:c.title,size:"56%","destroy-on-close":""},{default:t(()=>[_e((o(),b("div",fs,[c.diff?(o(),b(P,{key:0},[i("div",ms,[i("span",null,h(c.diff.files.length)+" 个文件",1),i("span",vs,"+"+h(c.diff.insertions),1),i("span",_s,"-"+h(c.diff.deletions),1),c.diff.truncated?(o(),b("span",hs,"(已截断)")):z("",!0)]),l(Pe,{patch:c.diff.rawPatch},null,8,["patch"])],64)):c.patch?(o(),b(P,{key:1},[c.patch?(o(),S(Pe,{key:1,patch:c.patch},null,8,["patch"])):(o(),b("div",gs,"无差异"))],64)):c.loading?z("",!0):(o(),S(J,{key:2,description:"无差异","image-size":60}))])),[[Ut,c.loading]])]),_:1},8,["modelValue","title"]),l(xe,{modelValue:d.value,"onUpdate:modelValue":e[9]||(e[9]=v=>d.value=v),title:"提交 Commit",width:"520px",onClosed:e[10]||(e[10]=v=>p.value="")},{footer:t(()=>[l(k,{onClick:e[8]||(e[8]=v=>d.value=!1)},{default:t(()=>[...e[51]||(e[51]=[f("取消",-1)])]),_:1}),l(k,{type:"primary",onClick:Ke},{default:t(()=>[...e[52]||(e[52]=[f("下一步(dry-run 预览)",-1)])]),_:1})]),default:t(()=>[l(fe,{modelValue:p.value,"onUpdate:modelValue":e[7]||(e[7]=v=>p.value=v),type:"textarea",rows:5,placeholder:"输入提交信息(message)",onKeydown:We(me(Ke,["ctrl"]),["enter"])},null,8,["modelValue","onKeydown"])]),_:1},8,["modelValue"]),l(xe,{modelValue:_.value,"onUpdate:modelValue":e[14]||(e[14]=v=>_.value=v),title:"新建分支",width:"480px",onClosed:e[15]||(e[15]=v=>{m.value="",x.value=""})},{footer:t(()=>[l(k,{onClick:e[13]||(e[13]=v=>_.value=!1)},{default:t(()=>[...e[53]||(e[53]=[f("取消",-1)])]),_:1}),l(k,{type:"primary",onClick:Vt},{default:t(()=>[...e[54]||(e[54]=[f("下一步(dry-run 预览)",-1)])]),_:1})]),default:t(()=>[l(Xe,{"label-width":"100px"},{default:t(()=>[l(Ve,{label:"分支名"},{default:t(()=>[l(fe,{modelValue:m.value,"onUpdate:modelValue":e[11]||(e[11]=v=>m.value=v),placeholder:"feature/xxx"},null,8,["modelValue"])]),_:1}),l(Ve,{label:"起点(可选)"},{default:t(()=>[l(fe,{modelValue:x.value,"onUpdate:modelValue":e[12]||(e[12]=v=>x.value=v),placeholder:"默认当前 HEAD,如 main 或 commit hash"},null,8,["modelValue"])]),_:1})]),_:1})]),_:1},8,["modelValue"]),l(xe,{modelValue:U.value,"onUpdate:modelValue":e[19]||(e[19]=v=>U.value=v),title:"暂存改动 Stash",width:"560px"},{footer:t(()=>[l(k,{onClick:e[18]||(e[18]=v=>U.value=!1)},{default:t(()=>[...e[58]||(e[58]=[f("取消",-1)])]),_:1}),l(k,{type:"primary",onClick:yt},{default:t(()=>[...e[59]||(e[59]=[f("下一步(dry-run 预览)",-1)])]),_:1})]),default:t(()=>[l(R,{class:"stash-tip",title:"可勾选左侧更改列表的文件随本次 stash 一起保存;不勾选则保存全部更改。",type:"info",closable:!1,"show-icon":""}),l(Xe,{"label-width":"90px"},{default:t(()=>[l(Ve,{label:"说明(可选)"},{default:t(()=>[l(fe,{modelValue:B.value,"onUpdate:modelValue":e[16]||(e[16]=v=>B.value=v),placeholder:"留空则使用默认说明(WIP on 分支: 提交)"},null,8,["modelValue"])]),_:1}),l(Ve,{label:"包含未跟踪"},{default:t(()=>[l(Ue,{modelValue:M.value,"onUpdate:modelValue":e[17]||(e[17]=v=>M.value=v)},{default:t(()=>[...e[55]||(e[55]=[f("包含未跟踪文件(-u)。勾选文件后会自动包含其中的未跟踪文件",-1)])]),_:1},8,["modelValue"])]),_:1})]),_:1}),Ce.value.length?(o(),b("div",ys,[i("div",bs,[i("span",ks,"选择文件(已选 "+h(te.value)+")",1),i("div",ws,[l(k,{size:"small",text:"",onClick:ht},{default:t(()=>[...e[56]||(e[56]=[f("全选",-1)])]),_:1}),l(k,{size:"small",text:"",onClick:Je},{default:t(()=>[...e[57]||(e[57]=[f("清除选择",-1)])]),_:1})])]),i("div",Cs,[(o(!0),b(P,null,X(Ce.value,v=>{var V,se;return o(),b("div",{key:v,class:"file-row"},[l(Ue,{"model-value":je(v),onChange:Ne=>He(v)},null,8,["model-value","onChange"]),i("span",{class:ve(["file-status",(V=r.value)!=null&&V.untracked.includes(v)?"new":"mod"])},h((se=r.value)!=null&&se.untracked.includes(v)?"?":"M"),3),i("span",$s,h(v),1)])}),128))])])):z("",!0)]),_:1},8,["modelValue"]),l(xe,{modelValue:D.value,"onUpdate:modelValue":e[22]||(e[22]=v=>D.value=v),title:"硬重置(高风险)",width:"480px",onClosed:e[23]||(e[23]=v=>H.value="")},{footer:t(()=>[l(k,{onClick:e[21]||(e[21]=v=>D.value=!1)},{default:t(()=>[...e[60]||(e[60]=[f("取消",-1)])]),_:1}),l(k,{type:"danger",onClick:St},{default:t(()=>[...e[61]||(e[61]=[f("下一步(dry-run 预览)",-1)])]),_:1})]),default:t(()=>[l(R,{title:"将丢弃索引与工作区所有更改(不可逆),执行前会自动创建备份",type:"error",closable:!1,"show-icon":"",class:"mb"}),l(fe,{modelValue:H.value,"onUpdate:modelValue":e[20]||(e[20]=v=>H.value=v),placeholder:"重置目标,默认 HEAD,如 HEAD~1 / commit hash / main"},null,8,["modelValue"])]),_:1},8,["modelValue"])])}}}),Vs=oe(xs,[["__scopeId","data-v-950822e0"]]),Ss={class:"page"},Ts={class:"filter-bar"},Es={class:"mono hash"},zs={class:"subject"},Ds={key:0,class:"refs"},As={class:"sub"},Rs={class:"commit-head"},Ls={class:"mono commit-hash"},Us={class:"commit-subject"},Ns={class:"commit-meta"},Is={key:0},Fs={key:0,class:"commit-body"},Os={class:"diff-wrap"},Ps={class:"diff-summary"},qs={class:"add"},Bs={class:"del"},Ws={key:0,class:"warn"},Gs=ae({__name:"HistoryView",setup(a){const s=Te(),{revision:u}=Ee(),y=()=>s.currentId,r=T([]),C=T([]),E=T(!1),L=T(""),I=T("head"),c=T(""),d=T(""),p=T(50),_=T(!1),m=T(null),x=T(null),U=T(!1),B=q(()=>C.value.filter(W=>!W.remote)),M=q(()=>{var W;return((W=C.value.find($=>$.current))==null?void 0:W.name)??""});async function D(){const W=y();if(W!==null)try{const{branches:$}=await at(W);C.value=$}catch{}}async function H(){const W=y();if(W!==null){E.value=!0,L.value="";try{const $={maxCount:p.value};I.value==="all"?$.all=!0:I.value==="branch"&&c.value&&($.from=c.value),d.value.trim()&&($.path=d.value.trim()),r.value=await Kt(W,$)}catch($){L.value=$ instanceof Error?$.message:String($),r.value=[]}finally{E.value=!1}}}async function w(){await Promise.all([D(),H()])}async function te(W){const $=y();if($!==null){_.value=!0,m.value=W,x.value=null,U.value=!0;try{const{diff:Q}=await Yt($,W.hash);x.value=Q}catch(Q){G.error(Q instanceof Error?Q.message:String(Q))}finally{U.value=!1}}}function ne(W){const $=new Date(W);return Number.isNaN($.getTime())?W:$.toLocaleString()}return ie(y,()=>void w()),ie(u,()=>void H()),ie([I,c,p],()=>void H()),ue(()=>void w()),(W,$)=>{const Q=g("el-alert"),O=g("el-radio-button"),de=g("el-radio-group"),re=g("el-option"),he=g("el-select"),De=g("el-input"),ge=g("el-button"),ce=g("el-card"),Y=g("el-table-column"),Ae=g("el-tag"),Re=g("el-table"),ye=g("el-empty"),be=g("el-divider"),ke=g("el-drawer"),we=Se("loading");return o(),b("div",Ss,[$[8]||($[8]=i("h2",{class:"page-title"},"提交历史",-1)),L.value?(o(),S(Q,{key:0,title:L.value,type:"error",closable:!1,"show-icon":"",class:"mb"},null,8,["title"])):z("",!0),l(ce,{shadow:"never",class:"mb"},{default:t(()=>[i("div",Ts,[l(de,{modelValue:I.value,"onUpdate:modelValue":$[0]||($[0]=F=>I.value=F)},{default:t(()=>[l(O,{value:"head"},{default:t(()=>[f("当前分支("+h(M.value)+")",1)]),_:1}),l(O,{value:"all"},{default:t(()=>[...$[5]||($[5]=[f("全部分支",-1)])]),_:1}),l(O,{value:"branch"},{default:t(()=>[...$[6]||($[6]=[f("指定分支",-1)])]),_:1})]),_:1},8,["modelValue"]),I.value==="branch"?(o(),S(he,{key:0,modelValue:c.value,"onUpdate:modelValue":$[1]||($[1]=F=>c.value=F),filterable:"",placeholder:"选择分支",class:"branch-select"},{default:t(()=>[(o(!0),b(P,null,X(B.value,F=>(o(),S(re,{key:F.name,label:F.name,value:F.name},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])):z("",!0),l(De,{modelValue:d.value,"onUpdate:modelValue":$[2]||($[2]=F=>d.value=F),clearable:"",placeholder:"按路径过滤(可选)",class:"path-input",onKeyup:We(H,["enter"])},null,8,["modelValue"]),l(he,{modelValue:p.value,"onUpdate:modelValue":$[3]||($[3]=F=>p.value=F),class:"count-select"},{default:t(()=>[l(re,{label:"20 条",value:20}),l(re,{label:"50 条",value:50}),l(re,{label:"100 条",value:100})]),_:1},8,["modelValue"]),l(ge,{loading:E.value,onClick:H},{default:t(()=>[...$[7]||($[7]=[f("刷新",-1)])]),_:1},8,["loading"])])]),_:1}),l(ce,{shadow:"never"},{default:t(()=>[_e((o(),S(Re,{data:r.value,size:"default","highlight-current-row":"",onRowClick:te},{default:t(()=>[l(Y,{label:"提交",width:"100"},{default:t(({row:F})=>[i("span",Es,h(F.shortHash),1)]),_:1}),l(Y,{label:"信息","min-width":"300"},{default:t(({row:F})=>[i("div",zs,h(F.subject),1),F.refs?(o(),b("div",Ds,[(o(!0),b(P,null,X(F.refs.split(",").map(Z=>Z.trim()).filter(Boolean),Z=>(o(),S(Ae,{key:Z,size:"small",effect:"plain",class:"ref-tag"},{default:t(()=>[f(h(Z.startsWith("tag: ")?Z:Z.includes("/")&&!Z.startsWith("HEAD")?Z.split("/").pop():Z),1)]),_:2},1024))),128))])):z("",!0)]),_:1}),l(Y,{label:"作者",width:"170"},{default:t(({row:F})=>[i("div",null,h(F.authorName),1),i("div",As,h(F.authorEmail),1)]),_:1}),l(Y,{label:"时间",width:"180"},{default:t(({row:F})=>[f(h(ne(F.authorDate)),1)]),_:1})]),_:1},8,["data"])),[[we,E.value]]),!E.value&&r.value.length===0?(o(),S(ye,{key:0,description:"暂无提交记录"})):z("",!0)]),_:1}),l(ke,{modelValue:_.value,"onUpdate:modelValue":$[4]||($[4]=F=>_.value=F),size:"60%","destroy-on-close":""},{default:t(()=>[m.value?(o(),b(P,{key:0},[i("div",Rs,[i("div",Ls,h(m.value.hash),1),i("h3",Us,h(m.value.subject),1),i("div",Ns,[i("span",null,h(m.value.authorName)+" <"+h(m.value.authorEmail)+">",1),i("span",null,h(ne(m.value.authorDate)),1),m.value.refs?(o(),b("span",Is,"refs: "+h(m.value.refs),1)):z("",!0)]),m.value.body?(o(),b("pre",Fs,h(m.value.body),1)):z("",!0)]),l(be),_e((o(),b("div",Os,[x.value?(o(),b(P,{key:0},[i("div",Ps,[i("span",null,h(x.value.files.length)+" 个文件",1),i("span",qs,"+"+h(x.value.insertions),1),i("span",Bs,"-"+h(x.value.deletions),1),x.value.truncated?(o(),b("span",Ws,"(已截断)")):z("",!0)]),l(Pe,{patch:x.value.rawPatch},null,8,["patch"])],64)):U.value?z("",!0):(o(),S(ye,{key:1,description:"无差异","image-size":60}))])),[[we,U.value]])],64)):z("",!0)]),_:1},8,["modelValue"])])}}}),Ms=oe(Gs,[["__scopeId","data-v-2be2a7e8"]]),Hs=qe("logs",{state:()=>({logs:[],loading:!1,error:null,limit:50,toolFilter:""}),actions:{async load(){this.loading=!0,this.error=null;try{const{logs:a}=await Qt({limit:this.limit,tool:this.toolFilter||void 0});this.logs=a}catch(a){this.error=a instanceof Error?a.message:String(a)}finally{this.loading=!1}}}}),js={class:"page"},Js={class:"filter-bar"},Ks={class:"tool-cell"},Xs={class:"mono repo-path"},Ys=["title"],Zs=["title"],Qs=["title"],en=ae({__name:"LogsView",setup(a){const s=Hs(),{revision:u}=Ee(),y=q(()=>{const c=new Set;for(const d of s.logs)c.add(d.tool);return[...c].sort()}),r=c=>c.includes("reset")||c.includes("clean")||c.includes("force")||c.includes("rebase")?"dangerous":"write";function C(c){return r(c)==="dangerous"?"danger":c.startsWith("git_")?"primary":"success"}function E(c){const d=new Date(c);return Number.isNaN(d.getTime())?c:d.toLocaleString()}function L(c){return c==="mcp"?"primary":c==="web"?"success":"warning"}function I(c){if(c==null)return"";if(typeof c=="string")return c;try{const d=JSON.stringify(c);return d===void 0?"":d}catch{return String(c)}}return ie(u,()=>{s.load()}),ue(()=>{s.load()}),(c,d)=>{const p=g("el-option"),_=g("el-select"),m=g("el-button"),x=g("el-card"),U=g("el-table-column"),B=g("el-tag"),M=g("el-table"),D=g("el-empty"),H=Se("loading");return o(),b("div",js,[d[7]||(d[7]=i("h2",{class:"page-title"},"操作日志",-1)),l(x,{shadow:"never",class:"mb"},{default:t(()=>[i("div",Js,[l(_,{modelValue:A(s).toolFilter,"onUpdate:modelValue":d[0]||(d[0]=w=>A(s).toolFilter=w),clearable:"",placeholder:"按工具过滤",class:"tool-select",onChange:A(s).load},{default:t(()=>[(o(!0),b(P,null,X(y.value,w=>(o(),S(p,{key:w,label:w,value:w},null,8,["label","value"]))),128))]),_:1},8,["modelValue","onChange"]),l(_,{modelValue:A(s).limit,"onUpdate:modelValue":d[1]||(d[1]=w=>A(s).limit=w),class:"limit-select",onChange:A(s).load},{default:t(()=>[l(p,{label:"最近 20 条",value:20}),l(p,{label:"最近 50 条",value:50}),l(p,{label:"最近 100 条",value:100})]),_:1},8,["modelValue","onChange"]),l(m,{loading:A(s).loading,onClick:A(s).load},{default:t(()=>[...d[2]||(d[2]=[f("刷新",-1)])]),_:1},8,["loading","onClick"]),d[3]||(d[3]=i("span",{class:"tip"},"记录来源:Web / MCP / CLI 全部写入操作(只读操作不记录)",-1))])]),_:1}),l(x,{shadow:"never"},{default:t(()=>[_e((o(),S(M,{data:A(s).logs,size:"default"},{default:t(()=>[l(U,{label:"时间",width:"180"},{default:t(({row:w})=>[f(h(E(w.timestamp)),1)]),_:1}),l(U,{label:"来源",width:"90"},{default:t(({row:w})=>[l(B,{type:L(w.source),size:"small",effect:"plain"},{default:t(()=>[f(h(w.source.toUpperCase()),1)]),_:2},1032,["type"])]),_:1}),l(U,{label:"工具",width:"200"},{default:t(({row:w})=>[i("div",Ks,[l(B,{type:C(w.tool),size:"small",effect:"dark"},{default:t(()=>[f(h(w.tool),1)]),_:2},1032,["type"]),w.dryRun?(o(),S(B,{key:0,size:"small",type:"info",effect:"plain",class:"dry-tag"},{default:t(()=>[...d[4]||(d[4]=[f("dry-run",-1)])]),_:1})):z("",!0)])]),_:1}),l(U,{label:"仓库","min-width":"200"},{default:t(({row:w})=>[i("span",Xs,h(w.repoPath??"—"),1)]),_:1}),l(U,{label:"参数(脱敏)","min-width":"180"},{default:t(({row:w})=>[i("span",{class:"mono params",title:I(w.params)},h(I(w.params)||"—"),9,Ys)]),_:1}),l(U,{label:"结果","min-width":"150"},{default:t(({row:w})=>[w.error?(o(),b(P,{key:0},[l(B,{type:"danger",size:"small",effect:"plain",class:"result-tag"},{default:t(()=>[...d[5]||(d[5]=[f("失败",-1)])]),_:1}),i("div",{class:"err-text",title:w.error},h(w.error),9,Zs)],64)):(o(),b(P,{key:1},[l(B,{type:"success",size:"small",effect:"plain",class:"result-tag"},{default:t(()=>[...d[6]||(d[6]=[f("成功",-1)])]),_:1}),i("div",{class:"ok-text",title:w.result},h(w.result),9,Qs)],64))]),_:1}),l(U,{label:"耗时",width:"90"},{default:t(({row:w})=>[f(h(w.durationMs)+" ms",1)]),_:1})]),_:1},8,["data"])),[[H,A(s).loading]]),!A(s).loading&&A(s).logs.length===0?(o(),S(D,{key:0,description:"暂无操作日志"})):z("",!0)]),_:1})])}}}),tn=oe(en,[["__scopeId","data-v-bb89f017"]]),ln={class:"page"},sn={class:"card-head"},nn={class:"mono"},an={class:"desc"},on={key:1,class:"action-bar"},rn=ae({__name:"SettingsView",setup(a){const s=ot(),u=Fe({disabledTools:[],requireApprovalFor:[],dryRunDefault:!1}),y=T(!1),r=q(()=>{const _=s.permissions;return _?JSON.stringify([...u.disabledTools].sort())!==JSON.stringify([..._.disabledTools].sort())||JSON.stringify([...u.requireApprovalFor].sort())!==JSON.stringify([..._.requireApprovalFor].sort())||u.dryRunDefault!==_.dryRunDefault:!1}),C=q(()=>s.tools.filter(_=>_.risk==="dangerous")),E=q(()=>C.value.map(_=>_.name));function L(){const _=s.permissions;_&&(u.disabledTools=[..._.disabledTools],u.requireApprovalFor=[..._.requireApprovalFor],u.dryRunDefault=_.dryRunDefault,y.value=!0)}ie(()=>s.permissions,_=>{_&&!r.value&&L()});async function I(_,m){m?u.disabledTools=u.disabledTools.filter(x=>x!==_.name):(u.disabledTools.includes(_.name)||u.disabledTools.push(_.name),u.requireApprovalFor=u.requireApprovalFor.filter(x=>x!==_.name))}function c(_){return _==="readonly"?"success":_==="write"?"primary":"danger"}function d(_){return _==="readonly"?"只读":_==="write"?"写操作":"高风险"}async function p(){try{await s.save({disabledTools:[...u.disabledTools],requireApprovalFor:[...u.requireApprovalFor],dryRunDefault:u.dryRunDefault}),G.success("设置已保存")}catch(_){G.error(_ instanceof Error?_.message:String(_))}}return ue(async()=>{await s.load(),L()}),(_,m)=>{const x=g("el-alert"),U=g("el-switch"),B=g("el-form-item"),M=g("el-form"),D=g("el-card"),H=g("el-tag"),w=g("el-table-column"),te=g("el-table"),ne=g("el-option"),W=g("el-select"),$=g("el-button"),Q=Se("loading");return o(),b("div",ln,[m[10]||(m[10]=i("h2",{class:"page-title"},"设置",-1)),A(s).error?(o(),S(x,{key:0,title:A(s).error,type:"error",closable:!1,"show-icon":"",class:"mb"},null,8,["title"])):z("",!0),l(D,{shadow:"never",class:"mb"},{header:t(()=>[...m[2]||(m[2]=[f("默认行为",-1)])]),default:t(()=>[l(M,{"label-width":"180px","label-position":"left"},{default:t(()=>[l(B,{label:"写操作默认 dry-run 预览"},{default:t(()=>[l(U,{modelValue:u.dryRunDefault,"onUpdate:modelValue":m[0]||(m[0]=O=>u.dryRunDefault=O)},null,8,["modelValue"]),m[3]||(m[3]=i("span",{class:"form-tip"},"开启后所有写操作(MCP/CLI)默认只生成预览,不真正执行",-1))]),_:1})]),_:1})]),_:1}),l(D,{shadow:"never",class:"mb"},{header:t(()=>[i("div",sn,[m[5]||(m[5]=i("span",null,"工具开关与风险等级",-1)),l(H,{size:"small",type:"warning",effect:"plain"},{default:t(()=>[...m[4]||(m[4]=[f("高危工具默认禁用,需在下方开启",-1)])]),_:1})])]),default:t(()=>[_e((o(),S(te,{data:A(s).tools,size:"default"},{default:t(()=>[l(w,{label:"工具",width:"230"},{default:t(({row:O})=>[i("span",nn,h(O.name),1)]),_:1}),l(w,{label:"风险",width:"90"},{default:t(({row:O})=>[l(H,{type:c(O.risk),size:"small",effect:"dark"},{default:t(()=>[f(h(d(O.risk)),1)]),_:2},1032,["type"])]),_:1}),l(w,{label:"描述","min-width":"240"},{default:t(({row:O})=>[i("span",an,h(O.description),1)]),_:1}),l(w,{label:"启用",width:"100",align:"center"},{default:t(({row:O})=>[l(U,{"model-value":!u.disabledTools.includes(O.name),onChange:de=>I(O,de===!0)},null,8,["model-value","onChange"])]),_:1})]),_:1},8,["data"])),[[Q,A(s).loading]])]),_:1}),l(D,{shadow:"never",class:"mb"},{header:t(()=>[...m[6]||(m[6]=[f("审批规则",-1)])]),default:t(()=>[l(M,{"label-position":"top"},{default:t(()=>[l(B,{label:"执行前需要审批的工具(勾选后,执行时需在确认框中手工确认)"},{default:t(()=>[l(W,{modelValue:u.requireApprovalFor,"onUpdate:modelValue":m[1]||(m[1]=O=>u.requireApprovalFor=O),multiple:"",filterable:"","collapse-tags":"",placeholder:"选择需要审批的工具",style:{width:"100%"}},{default:t(()=>[(o(!0),b(P,null,X(E.value,O=>(o(),S(ne,{key:O,label:O,value:O,disabled:u.disabledTools.includes(O)},null,8,["label","value","disabled"]))),128))]),_:1},8,["modelValue"]),m[7]||(m[7]=i("span",{class:"form-tip"},"审批仅对已启用的高危工具生效;禁用后自动移除",-1))]),_:1})]),_:1})]),_:1}),y.value?(o(),b("div",on,[l($,{type:"primary",loading:A(s).saving,disabled:!r.value,onClick:p},{default:t(()=>[...m[8]||(m[8]=[f("保存设置",-1)])]),_:1},8,["loading","disabled"]),l($,{disabled:!r.value,onClick:L},{default:t(()=>[...m[9]||(m[9]=[f("放弃修改",-1)])]),_:1},8,["disabled"])])):z("",!0)])}}}),un=oe(rn,[["__scopeId","data-v-c9221e05"]]),rt=Pt({history:qt(),routes:[{path:"/",redirect:"/status"},{path:"/repos",name:"repos",component:_l,meta:{title:"仓库管理"}},{path:"/status",name:"status",component:Vs,meta:{title:"状态"}},{path:"/history",name:"history",component:Ms,meta:{title:"历史"}},{path:"/logs",name:"logs",component:tn,meta:{title:"操作日志"}},{path:"/settings",name:"settings",component:un,meta:{title:"设置"}}]});rt.afterEach(a=>{const s=a.meta.title;document.title=s?`${s} · Git Cockpit`:"Git Cockpit"});const ze=Bt(dl);ze.use(Wt());ze.use(rt);ze.use(Gt,{size:"default"});ze.mount("#app");
|
|
File without changes
|