aws-runtime-bridge 1.9.139 → 1.9.141

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.
Files changed (51) hide show
  1. package/dist/adapter/AcodeSdkAdapter.d.ts +2 -1
  2. package/dist/adapter/AcodeSdkAdapter.js +2 -2
  3. package/dist/adapter/types.js +1 -1
  4. package/dist/config.d.ts +22 -6
  5. package/dist/config.js +2 -2
  6. package/dist/index.js +1 -1
  7. package/dist/remote-desktop/computer-control.d.ts +1 -1
  8. package/dist/remote-desktop/index.js +2 -2
  9. package/dist/remote-desktop/screen-capture.js +1 -1
  10. package/dist/remote-desktop/session-manager.js +1 -1
  11. package/dist/remote-desktop/types.d.ts +1 -1
  12. package/dist/remote-desktop/ui-automation.d.ts +2 -2
  13. package/dist/remote-desktop/ui-automation.js +5 -5
  14. package/dist/routes/ai-sources.js +1 -1
  15. package/dist/routes/background-tasks.js +5 -5
  16. package/dist/routes/dashboard.js +1 -1
  17. package/dist/routes/git.js +20 -20
  18. package/dist/routes/terminal.js +27 -27
  19. package/dist/services/agent-capability-sync.js +1 -1
  20. package/dist/services/ai-config-service.js +2 -2
  21. package/dist/services/auto-register.js +1 -1
  22. package/dist/services/aws-client-agent-mcp.d.ts +5 -0
  23. package/dist/services/aws-client-agent-mcp.js +2 -2
  24. package/dist/services/browser-session-manager.js +3 -3
  25. package/dist/services/capability-config.js +2 -2
  26. package/dist/services/event-delta-pusher.d.ts +1 -1
  27. package/dist/services/fileSnapshotStore.js +1 -1
  28. package/dist/services/instance-ai-config-service.js +1 -1
  29. package/dist/services/instance-init-service.js +1 -1
  30. package/dist/services/instance-state.d.ts +1 -1
  31. package/dist/services/instance-state.js +1 -1
  32. package/dist/services/legacy-path-migration.d.ts +0 -2
  33. package/dist/services/legacy-path-migration.js +1 -1
  34. package/dist/services/mcp-deploy.js +2 -2
  35. package/dist/services/plugin-manager.d.ts +1 -1
  36. package/dist/services/plugin-manager.js +9 -9
  37. package/dist/services/skill-manager.js +2 -2
  38. package/package/acode/dist/cli.js +2 -2
  39. package/package/acode/dist/runtime.d.ts +2 -0
  40. package/package/acode/dist/runtime.js +12 -12
  41. package/package/acode/dist/tool-allocation.js +1 -1
  42. package/package/acode/dist/types.d.ts +2 -0
  43. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +2 -4
  44. package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
  45. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +0 -1
  46. package/package/aws-client-agent-mcp/dist/mcp-server.js +3 -3
  47. package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +0 -2
  48. package/package/aws-client-agent-mcp/dist/mcp-tools.js +8 -8
  49. package/package/aws-client-agent-mcp/dist/status-reporter.js +1 -1
  50. package/package/aws-client-agent-mcp/dist/types.d.ts +9 -7
  51. package/package.json +1 -1
@@ -1,37 +1,37 @@
1
- import{spawn as st}from"node:child_process";import{promises as b}from"node:fs";import ot from"node:os";import R from"node:path";import L from"axios";import{Router as it}from"express";import{validateToken as k}from"../middleware/auth.js";import{logger as m}from"../utils/logger.js";const S=it(),at=1e4;function nt(){const r=Number(process.env.AWS_RUNTIME_GIT_COMMAND_TIMEOUT_MS);return Number.isFinite(r)&&r>=1e3?r:at}async function ct(r){let t;try{t=await b.stat(r)}catch(e){const i=e;throw i.code==="ENOENT"?new Error(`workspace path does not exist: ${r}`):new Error(`workspace path is not accessible: ${r}${i.message?` (${i.message})`:""}`)}if(!t.isDirectory())throw new Error(`workspace path is not a directory: ${r}`)}const ut=/^[0-9a-f]{40}$/i;function lt(r,t,e,i){return r==="added"||r==="deleted"||r==="renamed"?!0:i||t>0||e>0}function ft(r){const t=String(r||"").toLowerCase();return t.includes("you do not have the initial commit yet")||t.includes("bad revision")&&t.includes("head")||t.includes("ambiguous argument")&&t.includes("head")}function ht(){return"\u5F53\u524D Git \u4ED3\u5E93\u8FD8\u6CA1\u6709\u521D\u59CB\u63D0\u4EA4\uFF0C\u65E0\u6CD5\u521B\u5EFA\u5FEB\u7167\u3002\u8BF7\u5148\u63D0\u4EA4\u4E00\u6B21\u521D\u59CB\u63D0\u4EA4\u540E\u518D\u521B\u5EFA\u5FEB\u7167\u3002"}function dt(r){const t=String(r||"").toLowerCase();return t.includes("no local changes to save")||t.includes("no changes to save")||t.includes("\u6CA1\u6709\u672C\u5730\u53D8\u66F4\u9700\u8981\u4FDD\u5B58")}function pt(r){const t=String(r||"").trim();return ut.test(t)?`refs/agentswork/stash-snapshots/${t.toLowerCase()}`:null}function _(r){const t=String(r||"").trim();if(!t)return null;const e=t.split("\0");if(e.length>=3){const a=e[0]?.trim(),o=e[1]?.trim(),c=e[2]?.trim()||o,u=e[3]?.trim()||null;if(!a||!o)return null;const f=O(c||"");return{ref:a,stashName:o,message:f.message,branch:f.branch,createdAt:u}}const i=t.match(/^(stash@\{\d+\})(?::\s*)?(.*)$/);if(!i)return null;const s=i[1],n=O(i[2]?.trim()||s);return{ref:s,stashName:s,message:n.message,branch:n.branch,createdAt:null}}function O(r){const t=String(r||"").trim(),e=t.match(/^(?:On|WIP on)\s+([^:]+):\s*(.*)$/);return e?{branch:e[1].trim(),message:e[2].trim()||t}:{branch:"",message:t}}function I(r){const t=String(r||"").split(`
2
- `).find(i=>i.trim());if(!t)return null;const e=_(t);return e?{ref:e.ref,stashName:e.stashName||e.ref,message:e.message||e.ref}:null}async function h(r,t,e){return new Promise(i=>{let s=!1,n=null;const a=l=>{s||(s=!0,n&&clearTimeout(n),i(l))},o=st("git",t,{cwd:r,stdio:["ignore","pipe","pipe"]});let c="",u="";o.stdout?.on("data",l=>{c+=String(l||"")}),o.stderr?.on("data",l=>{u+=String(l||"")});const f=e??nt();n=setTimeout(()=>{m.warn(`[runtime-bridge] git command timed out after ${f}ms: git ${t.join(" ")} (cwd=${r})`),o.kill("SIGKILL"),a({stdout:c,stderr:u||`git command timed out after ${f}ms`,exitCode:124})},f),o.on("error",l=>{a({stdout:"",stderr:l.message,exitCode:1})}),o.on("close",l=>{a({stdout:c,stderr:u,exitCode:l??1})})})}const mt=6e4;function gt(){const r=Number(process.env.AWS_RUNTIME_GIT_PUSH_TIMEOUT_MS);return Number.isFinite(r)&&r>=1e3?r:mt}async function z(r,t){const e=pt(t);if(!e)return null;const i=await h(r,["update-ref",e,t]);return i.exitCode!==0?(m.warn(`[Git] \u6301\u4E45\u5316 stash \u5FEB\u7167 ref \u5931\u8D25: ref=${e}, error=${i.stderr||i.stdout}`),null):e}function x(r){return String(r||"").replace(/\\/g,"/").replace(/^\.\//,"").trim()}async function Yt(r){const t=R.join(r,".git");try{const e=await b.stat(t);return e.isDirectory()||e.isFile()}catch{return!1}}async function j(r){const t=R.resolve(String(r).trim());await ct(t);const e=await h(t,["rev-parse","--show-toplevel"]);if(e.exitCode!==0)return{workspacePath:t,repositoryRootPath:null,hasRepository:!1,isGitRepo:!1,usingParentGitRepo:!1,relativeWorkspacePath:""};const i=R.resolve(e.stdout.trim()),s=i===t,n=s?"":x(R.relative(i,t));return{workspacePath:t,repositoryRootPath:i,hasRepository:!0,isGitRepo:s,usingParentGitRepo:!s,relativeWorkspacePath:n}}function A(r,t){if(!t.relativeWorkspacePath)return[...r];const e=!r.includes("status");return[...r,...e?[`--relative=${t.relativeWorkspacePath}`]:[],"--",t.relativeWorkspacePath]}function E(r,t){const e=x(r);return t.relativeWorkspacePath?x(R.posix.join(t.relativeWorkspacePath,e)):e}function M(r){const t=x(r);if(R.posix.isAbsolute(t)||t.split("/").some(e=>e===".."||e==="."))throw new Error(`unsafe repository relative path: ${r}`)}function yt(r){const t=String(r||"").trim().toUpperCase();return t.startsWith("A")?"added":t.startsWith("D")?"deleted":t.startsWith("R")?"renamed":"modified"}function F(r){const t=x(r);if(!t.includes("=>"))return t;const e=t.match(/^(.*)\{([^{}]+)=>([^{}]+)\}(.*)$/);if(e){const s=e[1],n=e[3],a=e[4];return x(`${s}${n}${a}`)}const i=t.lastIndexOf("=>");return i>=0?x(t.slice(i+2)):t}function wt(r){const t=new Set,e=String(r||"").split(`
1
+ import{spawn as st}from"node:child_process";import{promises as b}from"node:fs";import ot from"node:os";import x from"node:path";import L from"axios";import{Router as it}from"express";import{validateToken as k}from"../middleware/auth.js";import{logger as m}from"../utils/logger.js";const S=it(),at=1e4;function nt(){const r=Number(process.env.AWS_RUNTIME_GIT_COMMAND_TIMEOUT_MS);return Number.isFinite(r)&&r>=1e3?r:at}async function ct(r){let t;try{t=await b.stat(r)}catch(e){const i=e;throw i.code==="ENOENT"?new Error(`workspace path does not exist: ${r}`):new Error(`workspace path is not accessible: ${r}${i.message?` (${i.message})`:""}`)}if(!t.isDirectory())throw new Error(`workspace path is not a directory: ${r}`)}const ut=/^[0-9a-f]{40}$/i;function lt(r,t,e,i){return r==="added"||r==="deleted"||r==="renamed"?!0:i||t>0||e>0}function ft(r){const t=String(r||"").toLowerCase();return t.includes("you do not have the initial commit yet")||t.includes("bad revision")&&t.includes("head")||t.includes("ambiguous argument")&&t.includes("head")}function ht(){return"\u5F53\u524D Git \u4ED3\u5E93\u8FD8\u6CA1\u6709\u521D\u59CB\u63D0\u4EA4\uFF0C\u65E0\u6CD5\u521B\u5EFA\u5FEB\u7167\u3002\u8BF7\u5148\u63D0\u4EA4\u4E00\u6B21\u521D\u59CB\u63D0\u4EA4\u540E\u518D\u521B\u5EFA\u5FEB\u7167\u3002"}function dt(r){const t=String(r||"").toLowerCase();return t.includes("no local changes to save")||t.includes("no changes to save")||t.includes("\u6CA1\u6709\u672C\u5730\u53D8\u66F4\u9700\u8981\u4FDD\u5B58")}function pt(r){const t=String(r||"").trim();return ut.test(t)?`refs/agentswork/stash-snapshots/${t.toLowerCase()}`:null}function _(r){const t=String(r||"").trim();if(!t)return null;const e=t.split("\0");if(e.length>=3){const a=e[0]?.trim(),o=e[1]?.trim(),c=e[2]?.trim()||o,u=e[3]?.trim()||null;if(!a||!o)return null;const l=O(c||"");return{ref:a,stashName:o,message:l.message,branch:l.branch,createdAt:u}}const i=t.match(/^(stash@\{\d+\})(?::\s*)?(.*)$/);if(!i)return null;const s=i[1],n=O(i[2]?.trim()||s);return{ref:s,stashName:s,message:n.message,branch:n.branch,createdAt:null}}function O(r){const t=String(r||"").trim(),e=t.match(/^(?:On|WIP on)\s+([^:]+):\s*(.*)$/);return e?{branch:e[1].trim(),message:e[2].trim()||t}:{branch:"",message:t}}function I(r){const t=String(r||"").split(`
2
+ `).find(i=>i.trim());if(!t)return null;const e=_(t);return e?{ref:e.ref,stashName:e.stashName||e.ref,message:e.message||e.ref}:null}async function h(r,t,e){return new Promise(i=>{let s=!1,n=null;const a=f=>{s||(s=!0,n&&clearTimeout(n),i(f))},o=st("git",t,{cwd:r,stdio:["ignore","pipe","pipe"]});let c="",u="";o.stdout?.on("data",f=>{c+=String(f||"")}),o.stderr?.on("data",f=>{u+=String(f||"")});const l=e??nt();n=setTimeout(()=>{m.warn(`[runtime-bridge] git command timed out after ${l}ms: git ${t.join(" ")} (cwd=${r})`),o.kill("SIGKILL"),a({stdout:c,stderr:u||`git command timed out after ${l}ms`,exitCode:124})},l),o.on("error",f=>{a({stdout:"",stderr:f.message,exitCode:1})}),o.on("close",f=>{a({stdout:c,stderr:u,exitCode:f??1})})})}const mt=6e4;function gt(){const r=Number(process.env.AWS_RUNTIME_GIT_PUSH_TIMEOUT_MS);return Number.isFinite(r)&&r>=1e3?r:mt}async function z(r,t){const e=pt(t);if(!e)return null;const i=await h(r,["update-ref",e,t]);return i.exitCode!==0?(m.warn(`[Git] \u6301\u4E45\u5316 stash \u5FEB\u7167 ref \u5931\u8D25: ref=${e}, error=${i.stderr||i.stdout}`),null):e}function R(r){return String(r||"").replace(/\\/g,"/").replace(/^\.\//,"").trim()}async function j(r){const t=x.resolve(String(r).trim());await ct(t);const e=await h(t,["rev-parse","--show-toplevel"]);if(e.exitCode!==0)return{workspacePath:t,repositoryRootPath:null,hasRepository:!1,isGitRepo:!1,usingParentGitRepo:!1,relativeWorkspacePath:""};const i=x.resolve(e.stdout.trim()),s=i===t,n=s?"":R(x.relative(i,t));return{workspacePath:t,repositoryRootPath:i,hasRepository:!0,isGitRepo:s,usingParentGitRepo:!s,relativeWorkspacePath:n}}function A(r,t){if(!t.relativeWorkspacePath)return[...r];const e=!r.includes("status");return[...r,...e?[`--relative=${t.relativeWorkspacePath}`]:[],"--",t.relativeWorkspacePath]}function E(r,t){const e=R(r);return t.relativeWorkspacePath?R(x.posix.join(t.relativeWorkspacePath,e)):e}function D(r){const t=R(r);if(x.posix.isAbsolute(t)||t.split("/").some(e=>e===".."||e==="."))throw new Error(`unsafe repository relative path: ${r}`)}function yt(r){const t=String(r||"").trim().toUpperCase();return t.startsWith("A")?"added":t.startsWith("D")?"deleted":t.startsWith("R")?"renamed":"modified"}function F(r){const t=R(r);if(!t.includes("=>"))return t;const e=t.match(/^(.*)\{([^{}]+)=>([^{}]+)\}(.*)$/);if(e){const s=e[1],n=e[3],a=e[4];return R(`${s}${n}${a}`)}const i=t.lastIndexOf("=>");return i>=0?R(t.slice(i+2)):t}function Pt(r){const t=new Set,e=String(r||"").split(`
3
3
  `).filter(i=>i.trim());for(const i of e){const s=i[0]||" ";if(s===" "||s==="?")continue;const n=i.slice(3).trim(),a=n.split(" -> "),o=F(a[a.length-1]||n);o&&t.add(o)}return t}function H(r,t,e=new Set,i=!1){const s=new Map,n=r.split(`
4
- `).filter(c=>c.trim());for(const c of n){const u=c.split(" ");if(u.length<2)continue;const f=yt(u[0]),l=f==="renamed"&&u.length>=3?2:1,d=F(u[l]);d&&s.set(d,f)}const a=[],o=t.split(`
5
- `).filter(c=>c.trim());for(const c of o){const u=c.split(" ");if(u.length<3)continue;const f=u[0]==="-"||u[1]==="-",l=u[0]==="-"?0:parseInt(u[0],10)||0,d=u[1]==="-"?0:parseInt(u[1],10)||0,p=F(u.slice(2).join(" ").trim()),g=s.get(p)||"modified";!p||!lt(g,l,d,f)||a.push({path:p,status:g,additions:l,deletions:d,staged:i||e.has(p)})}return a}function Pt(r){const t=[],e=String(r||"").split(`
4
+ `).filter(c=>c.trim());for(const c of n){const u=c.split(" ");if(u.length<2)continue;const l=yt(u[0]),f=l==="renamed"&&u.length>=3?2:1,d=F(u[f]);d&&s.set(d,l)}const a=[],o=t.split(`
5
+ `).filter(c=>c.trim());for(const c of o){const u=c.split(" ");if(u.length<3)continue;const l=u[0]==="-"||u[1]==="-",f=u[0]==="-"?0:parseInt(u[0],10)||0,d=u[1]==="-"?0:parseInt(u[1],10)||0,p=F(u.slice(2).join(" ").trim()),y=s.get(p)||"modified";!p||!lt(y,f,d,l)||a.push({path:p,status:y,additions:f,deletions:d,staged:i||e.has(p)})}return a}function wt(r){const t=[],e=String(r||"").split(`
6
6
  `).filter(i=>i.trim());for(const i of e)if(i.startsWith("?? ")){const s=i.slice(3).trim();s&&t.push(s.replace(/\/$/,""))}return t}function kt(r){if(!r||r.includes("\0"))return 0;let t=0;for(let e=0;e<r.length;e++)r.charCodeAt(e)===10&&t++;return r.endsWith(`
7
- `)?t:t+1}async function q(r){try{const t=await b.readFile(r,"utf8");return kt(t)}catch{return 0}}async function St(r,t,e,i=10){const s=[];async function n(a,o){if(o>i)return;const c=R.join(r,a);let u;try{u=await b.readdir(c,{withFileTypes:!0})}catch{return}for(const f of u){if(f.name===".git"||f.name==="node_modules")continue;const l=a?`${a}/${f.name}`:f.name;if(f.isDirectory())await n(l,o+1);else if(f.isFile()){const d=x(l);d&&!e.has(d)&&s.push({path:d,status:"added",additions:await q(R.join(c,f.name)),deletions:0,staged:!1})}}}for(const a of t){const o=x(a);if(!o)continue;const c=R.join(r,a);let u;try{u=await b.stat(c)}catch{continue}u.isFile()?e.has(o)||s.push({path:o,status:"added",additions:await q(c),deletions:0,staged:!1}):u.isDirectory()&&await n(o,0)}return s}function Rt(r,t){const e=new Map;for(const i of r)e.set(i.path,{...i});for(const i of t){const s=e.get(i.path);if(!s){e.set(i.path,{...i,staged:!0});continue}e.set(i.path,{...s,status:s.status==="modified"?i.status:s.status,additions:s.additions+i.additions,deletions:s.deletions+i.deletions,staged:!0})}return[...e.values()]}function B(r){return String(r||"").trim()}function xt(r){return String(r||"").trim()}function jt(r){return["--",r.relativeWorkspacePath||"."]}function G(r,t){return["--",E(r,t)]}function Ct(r){const t=r.split("\0"),e=[];for(let i=0;i+6<t.length;i+=7){const s=t[i]?.trim();if(!s)continue;const n=(t[i+5]?.trim()||"").split(/\s+/).filter(Boolean);e.push({hash:s,shortHash:t[i+1]?.trim()||s.slice(0,8),authorName:t[i+2]?.trim()||"unknown",authorDate:t[i+3]?.trim()||"",subject:t[i+4]?.trim()||s,body:t[i+6]?.trim()||"",parents:n})}return e}async function U(r){const t=new Set,e=await h(r,["for-each-ref","--format=%(refname)","refs/remotes"]);if(e.exitCode!==0)return t;const i=e.stdout.split(`
7
+ `)?t:t+1}async function q(r){try{const t=await b.readFile(r,"utf8");return kt(t)}catch{return 0}}async function St(r,t,e,i=10){const s=[];async function n(a,o){if(o>i)return;const c=x.join(r,a);let u;try{u=await b.readdir(c,{withFileTypes:!0})}catch{return}for(const l of u){if(l.name===".git"||l.name==="node_modules")continue;const f=a?`${a}/${l.name}`:l.name;if(l.isDirectory())await n(f,o+1);else if(l.isFile()){const d=R(f);d&&!e.has(d)&&s.push({path:d,status:"added",additions:await q(x.join(c,l.name)),deletions:0,staged:!1})}}}for(const a of t){const o=R(a);if(!o)continue;const c=x.join(r,a);let u;try{u=await b.stat(c)}catch{continue}u.isFile()?e.has(o)||s.push({path:o,status:"added",additions:await q(c),deletions:0,staged:!1}):u.isDirectory()&&await n(o,0)}return s}function Rt(r,t){const e=new Map;for(const i of r)e.set(i.path,{...i});for(const i of t){const s=e.get(i.path);if(!s){e.set(i.path,{...i,staged:!0});continue}e.set(i.path,{...s,status:s.status==="modified"?i.status:s.status,additions:s.additions+i.additions,deletions:s.deletions+i.deletions,staged:!0})}return[...e.values()]}function B(r){return String(r||"").trim()}function xt(r){return String(r||"").trim()}function jt(r){return["--",r.relativeWorkspacePath||"."]}function G(r,t){return["--",E(r,t)]}function Ct(r){const t=r.split("\0"),e=[];for(let i=0;i+6<t.length;i+=7){const s=t[i]?.trim();if(!s)continue;const n=(t[i+5]?.trim()||"").split(/\s+/).filter(Boolean);e.push({hash:s,shortHash:t[i+1]?.trim()||s.slice(0,8),authorName:t[i+2]?.trim()||"unknown",authorDate:t[i+3]?.trim()||"",subject:t[i+4]?.trim()||s,body:t[i+6]?.trim()||"",parents:n})}return e}async function U(r){const t=new Set,e=await h(r,["for-each-ref","--format=%(refname)","refs/remotes"]);if(e.exitCode!==0)return t;const i=e.stdout.split(`
8
8
  `).map(n=>n.trim()).filter(Boolean);if(i.length===0)return t;const s=await h(r,["rev-list",...i]);if(s.exitCode!==0)return t;for(const n of s.stdout.split(`
9
9
  `)){const a=n.trim();a&&t.add(a)}return t}async function N(r){const t=await h(r,["rev-parse","--abbrev-ref","HEAD"]);return t.exitCode!==0||!t.stdout.trim()?"":t.stdout.trim()}async function J(r){const t=await h(r,["for-each-ref","--format=%(refname)%00%(objectname)%00%(HEAD)%00%(upstream:short)","refs/heads","refs/remotes"]);if(t.exitCode!==0)return[];const e=[];for(const i of t.stdout.split(`
10
10
  `)){const s=i.split("\0"),n=s[0]?.trim(),a=s[1]?.trim();if(!n||!a)continue;const o=n.startsWith("refs/remotes/"),c=o?n.slice(13):n.slice(11);e.push({name:c,ref:n,hash:a,isCurrent:s[2]?.trim()==="*",isRemote:o,upstream:s[3]?.trim()||""})}return e}function bt(r,t){return["diff",t?r:`${r}^!`]}async function $t(r){const t=await h(r,["rev-parse","--abbrev-ref","HEAD"]),e=t.stdout.trim();if(t.exitCode!==0||!e||e==="HEAD")throw new Error("\u5F53\u524D\u5904\u4E8E detached HEAD\uFF08\u5206\u79BB\u5934\u6307\u9488\uFF09\u72B6\u6001\uFF0C\u65E0\u6CD5\u63A8\u9001");const i=await h(r,["rev-parse","--abbrev-ref","--symbolic-full-name","@{u}"]);if(i.exitCode!==0||!i.stdout.trim())throw new Error(`\u5206\u652F ${e} \u672A\u914D\u7F6E\u4E0A\u6E38\u5206\u652F\uFF0C\u65E0\u6CD5\u63A8\u9001\u3002\u8BF7\u5148\u624B\u52A8\u6267\u884C\u4E00\u6B21 git push -u origin ${e} \u8BBE\u7F6E\u4E0A\u6E38\u5206\u652F`);const s=await h(r,["push"],gt());if(s.exitCode!==0)throw new Error(s.stderr||s.stdout||"git push failed");const n=`${s.stdout}
11
11
  ${s.stderr}`.trim(),a=/everything up-to-date|up to date/i.test(n);return{branch:e,pushed:!a,message:a?"\u6CA1\u6709\u9700\u8981\u63A8\u9001\u7684\u63D0\u4EA4":"\u63A8\u9001\u5B8C\u6210",output:n}}S.post("/git/check",k,async(r,t)=>{const{workspacePath:e}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const i=await j(String(e).trim());t.json({ok:!0,isGitRepo:i.isGitRepo,hasRepository:i.hasRepository,usingParentGitRepo:i.usingParentGitRepo,workspacePath:i.workspacePath,repositoryRootPath:i.repositoryRootPath,relativeWorkspacePath:i.relativeWorkspacePath});return}catch(i){const s=i;t.status(500).json({error:s.message||"check git repo failed"})}});async function vt(r,t){const e=`\u5FEB\u7167\uFF08${new Date().toLocaleString("zh-CN",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})}\uFF09`,i=t&&String(t).trim()?`\u5FEB\u7167: ${String(t).trim()}`:e,s=await h(r,["stash","push","-m",i]);if(dt(`${s.stdout}
12
- ${s.stderr}`)){const u=await h(r,["stash","list","-n","1","--format=%H%x00%gd%x00%gs%x00%cr"]),f=u.exitCode===0?I(u.stdout):null;return{stashRef:(f?await z(r,f.ref):null)??f?.ref??null,stashName:f?.stashName??null,noChanges:!0,reusedLatestStash:!!f,message:f?`\u6CA1\u6709\u672C\u5730\u53D8\u66F4\u9700\u8981\u4FDD\u5B58\uFF0C\u5DF2\u590D\u7528\u6700\u8FD1\u5FEB\u7167 ${f.stashName}`:"\u6CA1\u6709\u672C\u5730\u53D8\u66F4\u9700\u8981\u4FDD\u5B58"}}if(s.exitCode!==0)throw ft(`${s.stdout}
13
- ${s.stderr}`)?new Error(ht()):new Error(s.stderr||"git stash push failed");const n=await h(r,["stash","list","-n","1","--format=%H%x00%gd%x00%gs%x00%cr"]),a=n.exitCode===0?I(n.stdout):null;return{stashRef:(a?await z(r,a.ref):null)??a?.ref??"stash@{0}",stashName:a?.stashName??null,noChanges:!1,reusedLatestStash:!1,message:i}}S.post("/git/stash/create",k,async(r,t)=>{const{workspacePath:e,message:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=R.resolve(String(e).trim()),n=await vt(s,i?String(i):void 0);t.json({ok:!0,stashRef:n.stashRef,stashName:n.stashName,message:n.message,noChanges:n.noChanges,reusedLatestStash:n.reusedLatestStash,workspacePath:s})}catch(s){const n=s;t.status(400).json({error:n.message||"create stash failed"})}}),S.post("/git/stash/list",k,async(r,t)=>{const{workspacePath:e,limit:i=20}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=R.resolve(String(e).trim()),n=Math.min(Math.max(1,Number(i)||20),100),a=await h(s,["stash","list","-n",String(n),"--format=%H%x00%gd%x00%gs%x00%cr"]);if(a.exitCode!==0){t.status(400).json({error:a.stderr||"git stash list failed"});return}const o=a.stdout.split(`
14
- `).map(_).filter(c=>c!==null);t.json({ok:!0,stashes:o,count:o.length,workspacePath:s})}catch(s){const n=s;t.status(500).json({error:n.message||"list stash failed"})}});async function K(r,t,e){const{workspacePath:i,ref:s}=r.body||{};if(!i||!String(i).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const n=R.resolve(String(i).trim()),a=s&&String(s).trim()?String(s).trim():"stash@{0}";await h(n,["checkout","--","."]);const o=await h(n,["stash",e,a]);if(o.exitCode!==0){if(o.stdout.includes("CONFLICT")||o.stderr.includes("CONFLICT")){t.json({ok:!1,hasConflict:!0,message:"\u6062\u590D\u65F6\u53D1\u751F\u51B2\u7A81\uFF0C\u8BF7\u624B\u52A8\u89E3\u51B3",details:o.stdout||o.stderr,workspacePath:n});return}if(o.stderr.includes("is not a valid reference")){t.status(404).json({error:`\u5FEB\u7167 ${a} \u4E0D\u5B58\u5728`});return}t.status(400).json({error:o.stderr||`git stash ${e} failed`});return}t.json({ok:!0,stashRef:a,message:e==="apply"?`\u5DF2\u5E94\u7528 ${a}`:`\u5DF2\u6062\u590D ${a}`,preservedStash:e==="apply",workspacePath:n})}catch(n){const a=n;t.status(500).json({error:a.message||`${e} stash failed`})}}S.post("/git/stash/apply",k,async(r,t)=>{await K(r,t,"apply")}),S.post("/git/stash/pop",k,async(r,t)=>{await K(r,t,"pop")}),S.post("/git/stash/drop",k,async(r,t)=>{const{workspacePath:e,ref:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=R.resolve(String(e).trim()),n=i&&String(i).trim()?String(i).trim():"stash@{0}",a=await h(s,["stash","drop",n]);if(a.exitCode!==0){if(a.stderr.includes("is not a valid reference")){t.status(404).json({error:`\u5FEB\u7167 ${n} \u4E0D\u5B58\u5728`});return}t.status(400).json({error:a.stderr||"git stash drop failed"});return}t.json({ok:!0,stashRef:n,message:`\u5DF2\u5220\u9664 ${n}`,workspacePath:s})}catch(s){const n=s;t.status(500).json({error:n.message||"drop stash failed"})}}),S.post("/git/diff",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await j(String(i||e).trim());if(!s.hasRepository||!s.repositoryRootPath){t.json({ok:!0,isGitRepo:s.isGitRepo,hasRepository:!1,usingParentGitRepo:!1,hasChanges:!1,files:[],workspacePath:s.workspacePath,repositoryRootPath:null,relativeWorkspacePath:""});return}const n=await h(s.repositoryRootPath,A(["diff","--name-status"],s));if(n.exitCode!==0){t.status(400).json({error:n.stderr||"git diff failed"});return}const a=await h(s.repositoryRootPath,A(["diff","--numstat"],s));if(a.exitCode!==0){t.status(400).json({error:a.stderr||"git diff failed"});return}const o=await h(s.repositoryRootPath,A(["diff","--cached","--name-status"],s));if(o.exitCode!==0){t.status(400).json({error:o.stderr||"git staged diff failed"});return}const c=await h(s.repositoryRootPath,A(["diff","--cached","--numstat"],s));if(c.exitCode!==0){t.status(400).json({error:c.stderr||"git staged diff failed"});return}const u=await h(s.repositoryRootPath,A(["status","--porcelain=v1"],s));if(u.exitCode!==0){t.status(400).json({error:u.stderr||"git status failed"});return}const f=wt(u.stdout),l=Rt(H(n.stdout,a.stdout,f),H(o.stdout,c.stdout,f,!0)),d=Pt(u.stdout);if(d.length>0){const p=new Set(l.map(y=>y.path)),g=await St(s.repositoryRootPath,d,p);l.push(...g)}t.json({ok:!0,isGitRepo:s.isGitRepo,hasRepository:!0,usingParentGitRepo:s.usingParentGitRepo,hasChanges:l.length>0,files:l,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const n=s;t.status(500).json({error:n.message||"git diff failed"})}});async function Y(r){const t=[],e=await h(r,["config","user.name"]);(e.exitCode!==0||!e.stdout.trim())&&t.push("-c","user.name=AgentsWork");const i=await h(r,["config","user.email"]);return(i.exitCode!==0||!i.stdout.trim())&&t.push("-c","user.email=agent@agentswork.com"),t}async function At(r,t,e){if(!t)throw new Error("commit message is required");if(e.length===0)throw new Error("filePaths must be valid workspace-relative paths");const i=e.map(c=>`:(literal)${c}`),s=await h(r,["add","--",...i]);if(s.exitCode!==0)throw new Error(s.stderr||"git add failed");const n=await h(r,["diff","--cached","--quiet","--",...i]);if(n.exitCode===0)throw new Error("\u6240\u9009\u6587\u4EF6\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4");if(n.exitCode!==1)throw new Error(n.stderr||"git staged diff failed");const a=await Y(r),o=await h(r,[...a,"commit","-m",t,"--",...i]);if(o.exitCode!==0)throw new Error(o.stderr||o.stdout||"git commit failed");return{committedPaths:[...e],output:o.stdout}}S.post("/git/commit",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,message:s,filePaths:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=xt(s);if(!a){t.status(400).json({error:"commit message is required"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=Array.isArray(n)?n.map(g=>x(String(g||"").trim())).filter(Boolean):[];if(c.length>0){const g=c.map(w=>E(w,o)),y=[];for(const w of g)if(w)try{M(w),y.push(w)}catch{t.status(400).json({error:`unsafe file path: ${w}`});return}if(y.length===0){t.status(400).json({error:"filePaths must be valid workspace-relative paths"});return}try{const w=await At(o.repositoryRootPath,a,y);t.json({ok:!0,message:"\u63D0\u4EA4\u5B8C\u6210",committedPaths:c,output:w.output,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(w){const P=w;t.status(400).json({error:P.message||"git commit failed"})}return}const u=await Y(o.repositoryRootPath),f=jt(o),l=await h(o.repositoryRootPath,["add","--all",...f]);if(l.exitCode!==0){t.status(400).json({error:l.stderr||"git add failed"});return}const d=await h(o.repositoryRootPath,["diff","--cached","--quiet",...f]);if(d.exitCode===0){t.status(400).json({error:"\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4"});return}if(d.exitCode!==1){t.status(400).json({error:d.stderr||"git staged diff failed"});return}const p=await h(o.repositoryRootPath,[...u,"commit","-m",a,...f]);if(p.exitCode!==0){t.status(400).json({error:p.stderr||p.stdout||"git commit failed"});return}t.json({ok:!0,message:"\u63D0\u4EA4\u5B8C\u6210",output:p.stdout,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git commit failed"})}}),S.post("/git/add-file",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePath:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const n=x(String(s||"").trim());if(!n){t.status(400).json({error:"filePath is required"});return}try{const a=await j(String(i||e).trim());if(!a.hasRepository||!a.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const o=await h(a.repositoryRootPath,["add",...G(n,a)]);if(o.exitCode!==0){t.status(400).json({error:o.stderr||"git add file failed"});return}t.json({ok:!0,filePath:n,workspacePath:a.workspacePath,repositoryRootPath:a.repositoryRootPath,relativeWorkspacePath:a.relativeWorkspacePath})}catch(a){const o=a;t.status(500).json({error:o.message||"git add file failed"})}}),S.post("/git/add-files",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePaths:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=(Array.isArray(s)?s:[]).map(o=>x(String(o||"").trim())).filter(o=>o.length>0);if(a.length===0){t.status(400).json({error:"filePaths is required and must be a non-empty array"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=a.flatMap(d=>G(d,o)),u=["add"],f=[];for(let d=0;d<c.length;d++)c[d]!=="--"&&f.push(c[d]);u.push("--",...f);const l=await h(o.repositoryRootPath,u);if(l.exitCode!==0){t.status(400).json({error:l.stderr||"git add files failed"});return}t.json({ok:!0,filePaths:a,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git add files failed"})}}),S.post("/git/commits",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,limit:s,branch:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const a=await j(String(i||e).trim());if(!a.hasRepository||!a.repositoryRootPath){t.json({ok:!0,commits:[],currentBranch:"",branchName:null,workspacePath:a.workspacePath,repositoryRootPath:null});return}const o=Number(s),c=!!r.body?.all,u=c?200:100,f=Number.isInteger(o)&&o>0&&o<=u?o:c?100:30,l=await N(a.repositoryRootPath),p=(n&&String(n).trim()?String(n).trim():null)||l||"HEAD",g="%H%x00%h%x00%an%x00%ad%x00%s%x00%P%x00%b%x00";let y;if(c?y=await h(a.repositoryRootPath,["log",`-${f}`,"--all","--topo-order","--date=iso-strict",`--pretty=format:${g}`]):y=await h(a.repositoryRootPath,["log",`-${f}`,"--date=iso-strict",`--pretty=format:${g}`,"--end-of-options",p,"--",a.relativeWorkspacePath||"."]),y.exitCode!==0){t.status(400).json({error:y.stderr||"git log failed"});return}const w=await U(a.repositoryRootPath),P=Ct(y.stdout).map(v=>({...v,isRemote:w.has(v.hash)})),C=c?await J(a.repositoryRootPath):void 0;t.json({ok:!0,commits:P,currentBranch:l,branchName:c?null:p,...C?{branches:C}:{},workspacePath:a.workspacePath,repositoryRootPath:a.repositoryRootPath,relativeWorkspacePath:a.relativeWorkspacePath})}catch(a){const o=a;t.status(500).json({error:o.message||"git commits failed"})}}),S.post("/git/branches",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await j(String(i||e).trim());if(!s.hasRepository||!s.repositoryRootPath){t.json({ok:!0,branches:[],currentBranch:"",workspacePath:s.workspacePath,repositoryRootPath:null});return}const n=await J(s.repositoryRootPath),a=await N(s.repositoryRootPath);t.json({ok:!0,branches:n,currentBranch:a,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const n=s;t.status(500).json({error:n.message||"git branches failed"})}}),S.post("/git/push",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await j(String(i||e).trim());if(!s.hasRepository||!s.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const n=await $t(s.repositoryRootPath);t.json({ok:!0,branch:n.branch,pushed:n.pushed,message:n.message,output:n.output,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const n=s;if(n.message&&/上游分支|detached HEAD|分离头指针|不存在|未配置/.test(n.message)){t.status(400).json({error:n.message});return}t.status(500).json({error:n.message||"git push failed"})}});const Et=/^[0-9a-fA-F]{7,40}$/;function Gt(r){const t=String(r||"").trim();return Et.test(t)?t:null}async function V(r,t){const e=await N(r);if(!e||e==="HEAD")throw new Error("\u5F53\u524D\u5904\u4E8E detached HEAD\uFF08\u5206\u79BB\u5934\u6307\u9488\uFF09\u72B6\u6001\uFF0C\u65E0\u6CD5\u56DE\u9000");const i=await h(r,["rev-parse","--verify","--quiet",`${t}^{commit}`]);if(i.exitCode!==0||!i.stdout.trim())throw new Error("\u76EE\u6807\u63D0\u4EA4\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5237\u65B0\u63D0\u4EA4\u5217\u8868\u540E\u91CD\u8BD5");const s=i.stdout.trim();if((await h(r,["rev-parse","HEAD"])).stdout.trim()===s)throw new Error("\u76EE\u6807\u63D0\u4EA4\u5DF2\u662F\u5F53\u524D HEAD\uFF0C\u65E0\u9700\u56DE\u9000");if((await h(r,["merge-base","--is-ancestor",s,"HEAD"])).exitCode!==0)throw new Error("\u76EE\u6807\u63D0\u4EA4\u4E0D\u5728\u5F53\u524D\u5206\u652F\u5386\u53F2\u4E2D\uFF0C\u65E0\u6CD5\u56DE\u9000");const o=await h(r,["rev-list","--count",`${s}..HEAD`]),c=parseInt(o.stdout.trim()||"0",10);let u=!1;const f=await U(r);if(f.size>0){const l=await h(r,["rev-list",`${s}..HEAD`]);if(l.exitCode===0){for(const d of l.stdout.split(`
15
- `))if(f.has(d.trim())){u=!0;break}}}return{resolvedHash:s,branch:e,resetCount:c,affectsRemote:u}}async function X(r){const t=await h(r,["status","--porcelain"]);return t.exitCode===0&&!!t.stdout.trim()}async function Tt(r,t,e){const i=await V(r,t),s=await X(r);return{mode:e,commitHash:i.resolvedHash,branch:i.branch,resetCount:i.resetCount,hasLocalChanges:s,affectsRemote:i.affectsRemote}}async function Dt(r,t,e,i=""){const s=await V(r,t),n=await X(r),a=e==="hard"?["reset","--hard",s.resolvedHash]:["reset","--soft",s.resolvedHash],o=await h(r,a);if(o.exitCode!==0)throw new Error(o.stderr||o.stdout||"git reset \u6267\u884C\u5931\u8D25");if(e==="hard"){const c=["clean","-fd"];i&&c.push("--",i);const u=await h(r,c);u.exitCode!==0&&m.warn(`[Git] \u786C\u56DE\u9000\u540E\u6E05\u7406\u672A\u8DDF\u8E2A\u6587\u4EF6\u5931\u8D25: cwd=${r}, scope=${i||"(\u6574\u4ED3)"}, error=${u.stderr||u.stdout}`)}return{mode:e,commitHash:s.resolvedHash,branch:s.branch,resetCount:s.resetCount,hasLocalChanges:n,affectsRemote:s.affectsRemote}}S.post("/git/reset",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,commitHash:s,mode:n,preview:a}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const o=Gt(s);if(!o){t.status(400).json({error:"commitHash \u683C\u5F0F\u975E\u6CD5\uFF0C\u65E0\u6CD5\u56DE\u9000"});return}const c=String(n||"").trim();if(c!=="soft"&&c!=="hard"){t.status(400).json({error:"mode must be soft or hard"});return}const u=a===!0||a==="true";try{const f=await j(String(i||e).trim());if(!f.hasRepository||!f.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}if(u){const g=await Tt(f.repositoryRootPath,o,c);t.json({ok:!0,preview:!0,...g,workspacePath:f.workspacePath,repositoryRootPath:f.repositoryRootPath,relativeWorkspacePath:f.relativeWorkspacePath});return}const l=await Dt(f.repositoryRootPath,o,c,f.relativeWorkspacePath),d=l.commitHash.slice(0,7),p=l.mode==="hard"?`\u5DF2\u786C\u56DE\u9000\u5230 ${d}\uFF0C\u5171\u56DE\u9000 ${l.resetCount} \u4E2A\u63D0\u4EA4\uFF0C\u5DE5\u4F5C\u533A\u5DF2\u5B8C\u5168\u6062\u590D\u5230\u8BE5\u63D0\u4EA4\u72B6\u6001`:`\u5DF2\u8F6F\u56DE\u9000\u5230 ${d}\uFF0C\u5171\u56DE\u9000 ${l.resetCount} \u4E2A\u63D0\u4EA4\uFF0C\u5DE5\u4F5C\u533A\u6587\u4EF6\u4FDD\u6301\u4E0D\u53D8`;t.json({ok:!0,...l,message:p,workspacePath:f.workspacePath,repositoryRootPath:f.repositoryRootPath,relativeWorkspacePath:f.relativeWorkspacePath})}catch(f){const l=f;if(l.message&&/回退|快照|detached HEAD|分离头指针|不存在|无需|HEAD/.test(l.message)){t.status(400).json({error:l.message});return}t.status(500).json({error:l.message||"git reset failed"})}}),S.post("/git/commit-diff",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,commitHash:s,aggregate:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=B(s);if(!a){t.status(400).json({error:"commitHash is required"});return}const o=n===!0||n==="true";try{const c=await j(String(i||e).trim());if(!c.hasRepository||!c.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const u=bt(a,o),f=await h(c.repositoryRootPath,A([...u,"--name-status"],c));if(f.exitCode!==0){t.status(400).json({error:f.stderr||"git commit diff failed"});return}const l=await h(c.repositoryRootPath,A([...u,"--numstat"],c));if(l.exitCode!==0){t.status(400).json({error:l.stderr||"git commit diff failed"});return}const d=H(f.stdout,l.stdout);t.json({ok:!0,commitHash:a,isGitRepo:c.isGitRepo,hasRepository:!0,usingParentGitRepo:c.usingParentGitRepo,hasChanges:d.length>0,files:d,workspacePath:c.workspacePath,repositoryRootPath:c.repositoryRootPath,relativeWorkspacePath:c.relativeWorkspacePath})}catch(c){const u=c;t.status(500).json({error:u.message||"git commit diff failed"})}}),S.post("/git/diff-file",k,async(r,t)=>{const{workspacePath:e,filePath:i,commitHash:s,aggregate:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!i||!String(i).trim()){t.status(400).json({error:"filePath is required"});return}try{const a=await j(String(e).trim()),o=x(String(i).trim());if(!a.hasRepository||!a.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=E(o,a),u=B(s),f=!!(u&&(n===!0||n==="true")),l=!u||f,d=u?f?["diff",u,"--",c]:["show","--format=","--no-ext-diff",u,"--",c]:["diff","HEAD","--",c];let p=await h(a.repositoryRootPath,d);if(l&&p.exitCode!==0&&(p=await h(a.repositoryRootPath,["diff","--",c])),p.exitCode!==0){t.status(400).json({error:p.stderr||"git diff file failed"});return}if(!u&&!p.stdout.trim()){const y=await h(a.repositoryRootPath,["diff","--cached","--",c]);if(y.exitCode!==0){t.status(400).json({error:y.stderr||"git staged diff file failed"});return}p=y}if(l&&!p.stdout.trim()){const y=R.join(a.repositoryRootPath,c);try{const w=await b.readFile(y,"utf-8");if(w){const P=w.split(`
12
+ ${s.stderr}`)){const u=await h(r,["stash","list","-n","1","--format=%H%x00%gd%x00%gs%x00%cr"]),l=u.exitCode===0?I(u.stdout):null;return{stashRef:(l?await z(r,l.ref):null)??l?.ref??null,stashName:l?.stashName??null,noChanges:!0,reusedLatestStash:!!l,message:l?`\u6CA1\u6709\u672C\u5730\u53D8\u66F4\u9700\u8981\u4FDD\u5B58\uFF0C\u5DF2\u590D\u7528\u6700\u8FD1\u5FEB\u7167 ${l.stashName}`:"\u6CA1\u6709\u672C\u5730\u53D8\u66F4\u9700\u8981\u4FDD\u5B58"}}if(s.exitCode!==0)throw ft(`${s.stdout}
13
+ ${s.stderr}`)?new Error(ht()):new Error(s.stderr||"git stash push failed");const n=await h(r,["stash","list","-n","1","--format=%H%x00%gd%x00%gs%x00%cr"]),a=n.exitCode===0?I(n.stdout):null;return{stashRef:(a?await z(r,a.ref):null)??a?.ref??"stash@{0}",stashName:a?.stashName??null,noChanges:!1,reusedLatestStash:!1,message:i}}S.post("/git/stash/create",k,async(r,t)=>{const{workspacePath:e,message:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=x.resolve(String(e).trim()),n=await vt(s,i?String(i):void 0);t.json({ok:!0,stashRef:n.stashRef,stashName:n.stashName,message:n.message,noChanges:n.noChanges,reusedLatestStash:n.reusedLatestStash,workspacePath:s})}catch(s){const n=s;t.status(400).json({error:n.message||"create stash failed"})}}),S.post("/git/stash/list",k,async(r,t)=>{const{workspacePath:e,limit:i=20}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=x.resolve(String(e).trim()),n=Math.min(Math.max(1,Number(i)||20),100),a=await h(s,["stash","list","-n",String(n),"--format=%H%x00%gd%x00%gs%x00%cr"]);if(a.exitCode!==0){t.status(400).json({error:a.stderr||"git stash list failed"});return}const o=a.stdout.split(`
14
+ `).map(_).filter(c=>c!==null);t.json({ok:!0,stashes:o,count:o.length,workspacePath:s})}catch(s){const n=s;t.status(500).json({error:n.message||"list stash failed"})}});async function K(r,t,e){const{workspacePath:i,ref:s}=r.body||{};if(!i||!String(i).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const n=x.resolve(String(i).trim()),a=s&&String(s).trim()?String(s).trim():"stash@{0}";await h(n,["checkout","--","."]);const o=await h(n,["stash",e,a]);if(o.exitCode!==0){if(o.stdout.includes("CONFLICT")||o.stderr.includes("CONFLICT")){t.json({ok:!1,hasConflict:!0,message:"\u6062\u590D\u65F6\u53D1\u751F\u51B2\u7A81\uFF0C\u8BF7\u624B\u52A8\u89E3\u51B3",details:o.stdout||o.stderr,workspacePath:n});return}if(o.stderr.includes("is not a valid reference")){t.status(404).json({error:`\u5FEB\u7167 ${a} \u4E0D\u5B58\u5728`});return}t.status(400).json({error:o.stderr||`git stash ${e} failed`});return}t.json({ok:!0,stashRef:a,message:e==="apply"?`\u5DF2\u5E94\u7528 ${a}`:`\u5DF2\u6062\u590D ${a}`,preservedStash:e==="apply",workspacePath:n})}catch(n){const a=n;t.status(500).json({error:a.message||`${e} stash failed`})}}S.post("/git/stash/apply",k,async(r,t)=>{await K(r,t,"apply")}),S.post("/git/stash/pop",k,async(r,t)=>{await K(r,t,"pop")}),S.post("/git/stash/drop",k,async(r,t)=>{const{workspacePath:e,ref:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=x.resolve(String(e).trim()),n=i&&String(i).trim()?String(i).trim():"stash@{0}",a=await h(s,["stash","drop",n]);if(a.exitCode!==0){if(a.stderr.includes("is not a valid reference")){t.status(404).json({error:`\u5FEB\u7167 ${n} \u4E0D\u5B58\u5728`});return}t.status(400).json({error:a.stderr||"git stash drop failed"});return}t.json({ok:!0,stashRef:n,message:`\u5DF2\u5220\u9664 ${n}`,workspacePath:s})}catch(s){const n=s;t.status(500).json({error:n.message||"drop stash failed"})}}),S.post("/git/diff",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await j(String(i||e).trim());if(!s.hasRepository||!s.repositoryRootPath){t.json({ok:!0,isGitRepo:s.isGitRepo,hasRepository:!1,usingParentGitRepo:!1,hasChanges:!1,files:[],workspacePath:s.workspacePath,repositoryRootPath:null,relativeWorkspacePath:""});return}const n=await h(s.repositoryRootPath,A(["diff","--name-status"],s));if(n.exitCode!==0){t.status(400).json({error:n.stderr||"git diff failed"});return}const a=await h(s.repositoryRootPath,A(["diff","--numstat"],s));if(a.exitCode!==0){t.status(400).json({error:a.stderr||"git diff failed"});return}const o=await h(s.repositoryRootPath,A(["diff","--cached","--name-status"],s));if(o.exitCode!==0){t.status(400).json({error:o.stderr||"git staged diff failed"});return}const c=await h(s.repositoryRootPath,A(["diff","--cached","--numstat"],s));if(c.exitCode!==0){t.status(400).json({error:c.stderr||"git staged diff failed"});return}const u=await h(s.repositoryRootPath,A(["status","--porcelain=v1"],s));if(u.exitCode!==0){t.status(400).json({error:u.stderr||"git status failed"});return}const l=Pt(u.stdout),f=Rt(H(n.stdout,a.stdout,l),H(o.stdout,c.stdout,l,!0)),d=wt(u.stdout);if(d.length>0){const p=new Set(f.map(g=>g.path)),y=await St(s.repositoryRootPath,d,p);f.push(...y)}t.json({ok:!0,isGitRepo:s.isGitRepo,hasRepository:!0,usingParentGitRepo:s.usingParentGitRepo,hasChanges:f.length>0,files:f,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const n=s;t.status(500).json({error:n.message||"git diff failed"})}});async function Y(r){const t=[],e=await h(r,["config","user.name"]);(e.exitCode!==0||!e.stdout.trim())&&t.push("-c","user.name=AgentsWork");const i=await h(r,["config","user.email"]);return(i.exitCode!==0||!i.stdout.trim())&&t.push("-c","user.email=agent@agentswork.com"),t}async function At(r,t,e){if(!t)throw new Error("commit message is required");if(e.length===0)throw new Error("filePaths must be valid workspace-relative paths");const i=e.map(c=>`:(literal)${c}`),s=await h(r,["add","--",...i]);if(s.exitCode!==0)throw new Error(s.stderr||"git add failed");const n=await h(r,["diff","--cached","--quiet","--",...i]);if(n.exitCode===0)throw new Error("\u6240\u9009\u6587\u4EF6\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4");if(n.exitCode!==1)throw new Error(n.stderr||"git staged diff failed");const a=await Y(r),o=await h(r,[...a,"commit","-m",t,"--",...i]);if(o.exitCode!==0)throw new Error(o.stderr||o.stdout||"git commit failed");return{committedPaths:[...e],output:o.stdout}}S.post("/git/commit",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,message:s,filePaths:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=xt(s);if(!a){t.status(400).json({error:"commit message is required"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=Array.isArray(n)?n.map(y=>R(String(y||"").trim())).filter(Boolean):[];if(c.length>0){const y=c.map(w=>E(w,o)),g=[];for(const w of y)if(w)try{D(w),g.push(w)}catch{t.status(400).json({error:`unsafe file path: ${w}`});return}if(g.length===0){t.status(400).json({error:"filePaths must be valid workspace-relative paths"});return}try{const w=await At(o.repositoryRootPath,a,g);t.json({ok:!0,message:"\u63D0\u4EA4\u5B8C\u6210",committedPaths:c,output:w.output,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(w){const P=w;t.status(400).json({error:P.message||"git commit failed"})}return}const u=await Y(o.repositoryRootPath),l=jt(o),f=await h(o.repositoryRootPath,["add","--all",...l]);if(f.exitCode!==0){t.status(400).json({error:f.stderr||"git add failed"});return}const d=await h(o.repositoryRootPath,["diff","--cached","--quiet",...l]);if(d.exitCode===0){t.status(400).json({error:"\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4"});return}if(d.exitCode!==1){t.status(400).json({error:d.stderr||"git staged diff failed"});return}const p=await h(o.repositoryRootPath,[...u,"commit","-m",a,...l]);if(p.exitCode!==0){t.status(400).json({error:p.stderr||p.stdout||"git commit failed"});return}t.json({ok:!0,message:"\u63D0\u4EA4\u5B8C\u6210",output:p.stdout,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git commit failed"})}}),S.post("/git/add-file",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePath:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const n=R(String(s||"").trim());if(!n){t.status(400).json({error:"filePath is required"});return}try{const a=await j(String(i||e).trim());if(!a.hasRepository||!a.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const o=await h(a.repositoryRootPath,["add",...G(n,a)]);if(o.exitCode!==0){t.status(400).json({error:o.stderr||"git add file failed"});return}t.json({ok:!0,filePath:n,workspacePath:a.workspacePath,repositoryRootPath:a.repositoryRootPath,relativeWorkspacePath:a.relativeWorkspacePath})}catch(a){const o=a;t.status(500).json({error:o.message||"git add file failed"})}}),S.post("/git/add-files",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePaths:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=(Array.isArray(s)?s:[]).map(o=>R(String(o||"").trim())).filter(o=>o.length>0);if(a.length===0){t.status(400).json({error:"filePaths is required and must be a non-empty array"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=a.flatMap(d=>G(d,o)),u=["add"],l=[];for(let d=0;d<c.length;d++)c[d]!=="--"&&l.push(c[d]);u.push("--",...l);const f=await h(o.repositoryRootPath,u);if(f.exitCode!==0){t.status(400).json({error:f.stderr||"git add files failed"});return}t.json({ok:!0,filePaths:a,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git add files failed"})}}),S.post("/git/commits",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,limit:s,branch:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const a=await j(String(i||e).trim());if(!a.hasRepository||!a.repositoryRootPath){t.json({ok:!0,commits:[],currentBranch:"",branchName:null,workspacePath:a.workspacePath,repositoryRootPath:null});return}const o=Number(s),c=!!r.body?.all,u=c?200:100,l=Number.isInteger(o)&&o>0&&o<=u?o:c?100:30,f=await N(a.repositoryRootPath),p=(n&&String(n).trim()?String(n).trim():null)||f||"HEAD",y="%H%x00%h%x00%an%x00%ad%x00%s%x00%P%x00%b%x00";let g;if(c?g=await h(a.repositoryRootPath,["log",`-${l}`,"--all","--topo-order","--date=iso-strict",`--pretty=format:${y}`]):g=await h(a.repositoryRootPath,["log",`-${l}`,"--date=iso-strict",`--pretty=format:${y}`,"--end-of-options",p,"--",a.relativeWorkspacePath||"."]),g.exitCode!==0){t.status(400).json({error:g.stderr||"git log failed"});return}const w=await U(a.repositoryRootPath),P=Ct(g.stdout).map(v=>({...v,isRemote:w.has(v.hash)})),C=c?await J(a.repositoryRootPath):void 0;t.json({ok:!0,commits:P,currentBranch:f,branchName:c?null:p,...C?{branches:C}:{},workspacePath:a.workspacePath,repositoryRootPath:a.repositoryRootPath,relativeWorkspacePath:a.relativeWorkspacePath})}catch(a){const o=a;t.status(500).json({error:o.message||"git commits failed"})}}),S.post("/git/branches",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await j(String(i||e).trim());if(!s.hasRepository||!s.repositoryRootPath){t.json({ok:!0,branches:[],currentBranch:"",workspacePath:s.workspacePath,repositoryRootPath:null});return}const n=await J(s.repositoryRootPath),a=await N(s.repositoryRootPath);t.json({ok:!0,branches:n,currentBranch:a,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const n=s;t.status(500).json({error:n.message||"git branches failed"})}}),S.post("/git/push",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await j(String(i||e).trim());if(!s.hasRepository||!s.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const n=await $t(s.repositoryRootPath);t.json({ok:!0,branch:n.branch,pushed:n.pushed,message:n.message,output:n.output,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const n=s;if(n.message&&/上游分支|detached HEAD|分离头指针|不存在|未配置/.test(n.message)){t.status(400).json({error:n.message});return}t.status(500).json({error:n.message||"git push failed"})}});const Et=/^[0-9a-fA-F]{7,40}$/;function Gt(r){const t=String(r||"").trim();return Et.test(t)?t:null}async function V(r,t){const e=await N(r);if(!e||e==="HEAD")throw new Error("\u5F53\u524D\u5904\u4E8E detached HEAD\uFF08\u5206\u79BB\u5934\u6307\u9488\uFF09\u72B6\u6001\uFF0C\u65E0\u6CD5\u56DE\u9000");const i=await h(r,["rev-parse","--verify","--quiet",`${t}^{commit}`]);if(i.exitCode!==0||!i.stdout.trim())throw new Error("\u76EE\u6807\u63D0\u4EA4\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5237\u65B0\u63D0\u4EA4\u5217\u8868\u540E\u91CD\u8BD5");const s=i.stdout.trim();if((await h(r,["rev-parse","HEAD"])).stdout.trim()===s)throw new Error("\u76EE\u6807\u63D0\u4EA4\u5DF2\u662F\u5F53\u524D HEAD\uFF0C\u65E0\u9700\u56DE\u9000");if((await h(r,["merge-base","--is-ancestor",s,"HEAD"])).exitCode!==0)throw new Error("\u76EE\u6807\u63D0\u4EA4\u4E0D\u5728\u5F53\u524D\u5206\u652F\u5386\u53F2\u4E2D\uFF0C\u65E0\u6CD5\u56DE\u9000");const o=await h(r,["rev-list","--count",`${s}..HEAD`]),c=parseInt(o.stdout.trim()||"0",10);let u=!1;const l=await U(r);if(l.size>0){const f=await h(r,["rev-list",`${s}..HEAD`]);if(f.exitCode===0){for(const d of f.stdout.split(`
15
+ `))if(l.has(d.trim())){u=!0;break}}}return{resolvedHash:s,branch:e,resetCount:c,affectsRemote:u}}async function X(r){const t=await h(r,["status","--porcelain"]);return t.exitCode===0&&!!t.stdout.trim()}async function Tt(r,t,e){const i=await V(r,t),s=await X(r);return{mode:e,commitHash:i.resolvedHash,branch:i.branch,resetCount:i.resetCount,hasLocalChanges:s,affectsRemote:i.affectsRemote}}async function Mt(r,t,e,i=""){const s=await V(r,t),n=await X(r),a=e==="hard"?["reset","--hard",s.resolvedHash]:["reset","--soft",s.resolvedHash],o=await h(r,a);if(o.exitCode!==0)throw new Error(o.stderr||o.stdout||"git reset \u6267\u884C\u5931\u8D25");if(e==="hard"){const c=["clean","-fd"];i&&c.push("--",i);const u=await h(r,c);u.exitCode!==0&&m.warn(`[Git] \u786C\u56DE\u9000\u540E\u6E05\u7406\u672A\u8DDF\u8E2A\u6587\u4EF6\u5931\u8D25: cwd=${r}, scope=${i||"(\u6574\u4ED3)"}, error=${u.stderr||u.stdout}`)}return{mode:e,commitHash:s.resolvedHash,branch:s.branch,resetCount:s.resetCount,hasLocalChanges:n,affectsRemote:s.affectsRemote}}S.post("/git/reset",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,commitHash:s,mode:n,preview:a}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const o=Gt(s);if(!o){t.status(400).json({error:"commitHash \u683C\u5F0F\u975E\u6CD5\uFF0C\u65E0\u6CD5\u56DE\u9000"});return}const c=String(n||"").trim();if(c!=="soft"&&c!=="hard"){t.status(400).json({error:"mode must be soft or hard"});return}const u=a===!0||a==="true";try{const l=await j(String(i||e).trim());if(!l.hasRepository||!l.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}if(u){const y=await Tt(l.repositoryRootPath,o,c);t.json({ok:!0,preview:!0,...y,workspacePath:l.workspacePath,repositoryRootPath:l.repositoryRootPath,relativeWorkspacePath:l.relativeWorkspacePath});return}const f=await Mt(l.repositoryRootPath,o,c,l.relativeWorkspacePath),d=f.commitHash.slice(0,7),p=f.mode==="hard"?`\u5DF2\u786C\u56DE\u9000\u5230 ${d}\uFF0C\u5171\u56DE\u9000 ${f.resetCount} \u4E2A\u63D0\u4EA4\uFF0C\u5DE5\u4F5C\u533A\u5DF2\u5B8C\u5168\u6062\u590D\u5230\u8BE5\u63D0\u4EA4\u72B6\u6001`:`\u5DF2\u8F6F\u56DE\u9000\u5230 ${d}\uFF0C\u5171\u56DE\u9000 ${f.resetCount} \u4E2A\u63D0\u4EA4\uFF0C\u5DE5\u4F5C\u533A\u6587\u4EF6\u4FDD\u6301\u4E0D\u53D8`;t.json({ok:!0,...f,message:p,workspacePath:l.workspacePath,repositoryRootPath:l.repositoryRootPath,relativeWorkspacePath:l.relativeWorkspacePath})}catch(l){const f=l;if(f.message&&/回退|快照|detached HEAD|分离头指针|不存在|无需|HEAD/.test(f.message)){t.status(400).json({error:f.message});return}t.status(500).json({error:f.message||"git reset failed"})}}),S.post("/git/commit-diff",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,commitHash:s,aggregate:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=B(s);if(!a){t.status(400).json({error:"commitHash is required"});return}const o=n===!0||n==="true";try{const c=await j(String(i||e).trim());if(!c.hasRepository||!c.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const u=bt(a,o),l=await h(c.repositoryRootPath,A([...u,"--name-status"],c));if(l.exitCode!==0){t.status(400).json({error:l.stderr||"git commit diff failed"});return}const f=await h(c.repositoryRootPath,A([...u,"--numstat"],c));if(f.exitCode!==0){t.status(400).json({error:f.stderr||"git commit diff failed"});return}const d=H(l.stdout,f.stdout);t.json({ok:!0,commitHash:a,isGitRepo:c.isGitRepo,hasRepository:!0,usingParentGitRepo:c.usingParentGitRepo,hasChanges:d.length>0,files:d,workspacePath:c.workspacePath,repositoryRootPath:c.repositoryRootPath,relativeWorkspacePath:c.relativeWorkspacePath})}catch(c){const u=c;t.status(500).json({error:u.message||"git commit diff failed"})}}),S.post("/git/diff-file",k,async(r,t)=>{const{workspacePath:e,filePath:i,commitHash:s,aggregate:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!i||!String(i).trim()){t.status(400).json({error:"filePath is required"});return}try{const a=await j(String(e).trim()),o=R(String(i).trim());if(!a.hasRepository||!a.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=E(o,a),u=B(s),l=!!(u&&(n===!0||n==="true")),f=!u||l,d=u?l?["diff",u,"--",c]:["show","--format=","--no-ext-diff",u,"--",c]:["diff","HEAD","--",c];let p=await h(a.repositoryRootPath,d);if(f&&p.exitCode!==0&&(p=await h(a.repositoryRootPath,["diff","--",c])),p.exitCode!==0){t.status(400).json({error:p.stderr||"git diff file failed"});return}if(!u&&!p.stdout.trim()){const g=await h(a.repositoryRootPath,["diff","--cached","--",c]);if(g.exitCode!==0){t.status(400).json({error:g.stderr||"git staged diff file failed"});return}p=g}if(f&&!p.stdout.trim()){const g=x.join(a.repositoryRootPath,c);try{const w=await b.readFile(g,"utf-8");if(w){const P=w.split(`
16
16
  `).map(v=>`+${v}`).join(`
17
17
  `),C=w.split(`
18
18
  `).length;p={stdout:[`diff --git a/${c} b/${c}`,"new file mode 100644","--- /dev/null",`+++ b/${c}`,`@@ -0,0 +1,${C} @@`,P].join(`
19
- `),stderr:"",exitCode:0}}}catch{}}M(c);let g="";if(l){const y=R.join(a.repositoryRootPath,c);try{g=await b.readFile(y,"utf-8")}catch{}}else{const y=await h(a.repositoryRootPath,["show",`${u}:${c}`]);y.exitCode===0&&(g=y.stdout)}t.json({ok:!0,filePath:o,repositoryRelativeFilePath:c,commitHash:u||null,rangeFromCommit:f,diffContent:p.stdout,fileContent:g,workspacePath:a.workspacePath,repositoryRootPath:a.repositoryRootPath,usingParentGitRepo:a.usingParentGitRepo})}catch(a){const o=a;t.status(500).json({error:o.message||"git diff file failed"})}});const Z=["STRICTLY act as a Git commit message generator. NEVER explain, narrate, or describe your task.",'NEVER start your reply with phrases like "\u6211\u4EEC\u88AB\u95EE\u5230", "\u4EE5\u4E0B\u662F", "\u597D\u7684", "Sure", "Here is", "I will", "I can", "Let me", "\u6839\u636E", "\u4E0B\u9762".',"NEVER include any meta-language or thinking out loud. Output ONLY the commit message text.","","Format (Conventional Commits, 1 line, optional body):"," <type>(<scope>): <description>","","Allowed types: feat, fix, refactor, test, docs, style, chore, perf, ci, build, revert.","First line MUST be <= 72 characters. <description> MUST be in Chinese.","Add a body (separated by a blank line) ONLY when the change needs more context."].join(`
19
+ `),stderr:"",exitCode:0}}}catch{}}D(c);let y="";if(f){const g=x.join(a.repositoryRootPath,c);try{y=await b.readFile(g,"utf-8")}catch{}}else{const g=await h(a.repositoryRootPath,["show",`${u}:${c}`]);g.exitCode===0&&(y=g.stdout)}t.json({ok:!0,filePath:o,repositoryRelativeFilePath:c,commitHash:u||null,rangeFromCommit:l,diffContent:p.stdout,fileContent:y,workspacePath:a.workspacePath,repositoryRootPath:a.repositoryRootPath,usingParentGitRepo:a.usingParentGitRepo})}catch(a){const o=a;t.status(500).json({error:o.message||"git diff file failed"})}});const Z=["STRICTLY act as a Git commit message generator. NEVER explain, narrate, or describe your task.",'NEVER start your reply with phrases like "\u6211\u4EEC\u88AB\u95EE\u5230", "\u4EE5\u4E0B\u662F", "\u597D\u7684", "Sure", "Here is", "I will", "I can", "Let me", "\u6839\u636E", "\u4E0B\u9762".',"NEVER include any meta-language or thinking out loud. Output ONLY the commit message text.","","Format (Conventional Commits, 1 line, optional body):"," <type>(<scope>): <description>","","Allowed types: feat, fix, refactor, test, docs, style, chore, perf, ci, build, revert.","First line MUST be <= 72 characters. <description> MUST be in Chinese.","Add a body (separated by a blank line) ONLY when the change needs more context."].join(`
20
20
  `);function Q(r){return["\u8BF7\u76F4\u63A5\u8F93\u51FA\u4E0A\u8FF0\u683C\u5F0F\u7684\u63D0\u4EA4\u4FE1\u606F,\u4E0D\u8981\u4EFB\u4F55\u989D\u5916\u6587\u5B57\u3002","\u4EE5\u4E0B\u662F\u6682\u5B58\u533A diff:","",r].join(`
21
- `)}const tt=/\b(?:feat|fix|chore|docs|style|refactor|perf|test|build|ci|revert)(?:\s*\([^)]*\))?\s*:/i,Mt=/^(?:feat|fix|chore|docs|style|refactor|perf|test|build|ci|revert)\s*:/i;function Ft(r){const t=r.split(`
21
+ `)}const tt=/\b(?:feat|fix|chore|docs|style|refactor|perf|test|build|ci|revert)(?:\s*\([^)]*\))?\s*:/i,Dt=/^(?:feat|fix|chore|docs|style|refactor|perf|test|build|ci|revert)\s*:/i;function Ft(r){const t=r.split(`
22
22
  `)[0]?.trim()??"";if(!t||t.length<3)return!1;const e=(t.match(/[\u4e00-\u9fa5]/g)||[]).length,i=(t.match(/[a-zA-Z]{2,}/g)||[]).length;if(e===0&&i===0)return!1;const s=[/^以下是/,/^上面是/,/^生成的/,/^这是/,/^下面/,/^(?:Here|This|This is|That is)/i];for(const n of s)if(n.test(t))return!1;return e>=2||i>=2}const Ht=[/^\s*(?:我们|我)\s*(?:被\s*)?(?:问[到过]?|需要|要|将|来|先)[^.:\n]{0,60}?(?:提交信息|commit\s*message|[::,,。])/i,/^\s*(?:好的|好)\s*[,,::]/i,/^\s*(?:好的|好)\s*(?:以下是|上面是|提供|生成|给出)[^.:\n]{0,20}?[::]?/i,/^\s*是\s*[::]/i,/^\s*(?:Sure|Here(?:'s|\s+is)|We(?:'re| are)|I\s+(?:will|can|'ll)|Let\s+me|Certainly|Of\s+course)[^.:\n]{0,40}?[,,::.]/i,/^\s*根据\s*(?:您|你|您所|你所|所)?\s*(?:提供|上述|给[我您你])?\s*(?:的)?\s*diff\s*[,,::]?/i,/^\s*(?:以下|下面)[^,,::\n]{0,30}?提交信息\s*[,,::]?/i,/^\s*(?:以下|下面)\s*是\s*[,,::]/i,/^\s*生成\s*(?:一条|一个|了)?\s*(?:符合|新的)?\s*(?:conventional\s*commit|git)?\s*提交信息\s*[,,::]?/i,/^\s*```(?:text|markdown|bash|shell)?\s*\n?/i];function T(r){if(!r)return m.info("[Git/sanitize] \u8F93\u5165\u4E3A\u7A7A, \u8FD4\u56DE fallback"),"chore: update staged changes";let t=r.replace(/\r\n/g,`
23
- `).trim();const e=JSON.stringify(r.substring(0,120)),i=t.match(tt);if(i&&i.index!==void 0&&i.index<=80)t=t.slice(i.index);else{for(let l=0;l<5;l+=1){const d=t;for(const p of Ht)t=t.replace(p,"");if(t=t.replace(/^[\s*\-`,,']+/,"").trim(),t===d)break}const a=t.split(`
24
- `)[0]?.trim()??"",o=!!t.match(tt),c=!!t.match(Mt),u=Ft(t),f=a.length>100;if(!o&&!c&&!u&&!f)return m.info(`[Git/sanitize] \u5265\u79BB\u540E\u65E0\u6709\u6548\u5185\u5BB9, \u8FD4\u56DE fallback. raw=${e}, afterStrip=${JSON.stringify(t.substring(0,100))}`),"chore: update staged changes";!o&&(c||u||f)&&(t=a||t)}const s=t.search(/\n\s*\n/);if(s>0&&(t=t.slice(0,s)),t=t.replace(/\n+\s*```\s*$/g,"").trim(),!t)return m.info(`[Git/sanitize] \u622A\u65AD\u540E\u4E3A\u7A7A, \u8FD4\u56DE fallback. raw=${e}`),"chore: update staged changes";const n=t.split(`
25
- `)[0]?.trim()??"";return n.length>100?(m.info(`[Git/sanitize] \u9996\u884C\u8D85\u8FC7 100 \u5B57\u7B26, \u4EC5\u4FDD\u7559\u9996\u884C. raw=${e}, firstLine=${JSON.stringify(n.substring(0,100))}`),n):(m.info(`[Git/sanitize] \u7ED3\u679C: ${JSON.stringify(t.substring(0,120))} (from raw=${e})`),t)}function et(r){const t=r.split(/\r?\n/);let e="";for(const i of t){const s=i.trim();if(!s.startsWith("data:"))continue;const n=s.slice(5).trim();if(!(!n||n==="[DONE]"))try{const o=JSON.parse(n)?.choices?.[0]?.delta?.content;typeof o=="string"&&o.length>0&&(e+=o)}catch{}}return e}function Nt(r,t){if(t>5)return;const e=r.split(/\r?\n/);for(const i of e){const s=i.trim();if(!s.startsWith("data:"))continue;const n=s.slice(5).trim();if(!(!n||n==="[DONE]"))try{const a=JSON.parse(n),o=Object.keys(a),c=a?.choices,u=Array.isArray(c)?c[0]:null,f=u?.delta?Object.keys(u.delta):null,l=u?.delta?.content;m.info(`[Git/extract] chunk#${t} structure: topKeys=${JSON.stringify(o)}, choicesLen=${Array.isArray(c)?c.length:typeof c}, deltaKeys=${JSON.stringify(f)}, content=${JSON.stringify(l?.substring(0,50))}, finishReason=${JSON.stringify(u?.finish_reason)}`)}catch{}}}async function Wt(r,t,e,i){const s=`${r.replace(/\/+$/,"")}/chat/completions`,o=(await L.post(s,{model:e,stream:!1,messages:[{role:"system",content:Z},{role:"user",content:Q(i)}],max_tokens:200,temperature:.2},{headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},timeout:3e4})).data?.choices?.[0]?.message?.content?.trim()||"";return T(o)}async function Lt(r,t,e,i,s,n){const a=`${r.replace(/\/+$/,"")}/chat/completions`,c=(await L.post(a,{model:e,stream:!0,messages:[{role:"system",content:Z},{role:"user",content:Q(i)}],max_tokens:200,temperature:.2},{headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,Accept:"text/event-stream"},timeout:Number(process.env.AWS_AI_STREAM_TIMEOUT_MS)||6e4,responseType:"stream",signal:n})).data;let u="",f="",l="",d=0,p=0;return m.info(`[Git/streamAi] AI \u6D41\u5F00\u59CB: aiModel=${e}`),await new Promise((g,y)=>{const w=P=>{c.removeAllListeners("data"),c.removeAllListeners("end"),c.removeAllListeners("error"),c.removeAllListeners("aborted"),m.info(`[Git/streamAi] AI \u6D41\u7ED3\u675F: totalChunks=${d}, totalDeltas=${p}, bufferLength=${u.length}, sanitized=${JSON.stringify(P).substring(0,200)}`),g(P)};c.on("data",P=>{const C=typeof P=="string"?P:P.toString("utf8");d++,l+=C,d<=3&&m.info(`[Git/streamAi] chunk#${d}: length=${C.length}, preview=${JSON.stringify(C.substring(0,80))}`),Nt(C,d);const v=l.split(/\n\n/);l=v.pop()??"";for(const rt of v){const D=et(rt);D&&(p++,u+=D,f=T(u),s(D,f))}}),c.on("end",()=>{if(l.trim()){const P=et(l);P?(m.info(`[Git/streamAi] \u5C3E\u90E8\u6B8B\u7559 delta: length=${P.length}`),u+=P):m.info(`[Git/streamAi] \u5C3E\u90E8\u6B8B\u7559\u65E0 delta, pendingData=${JSON.stringify(l.substring(0,100))}`)}w(T(u))}),c.on("error",P=>{m.error(`[Git/streamAi] \u6D41\u9519\u8BEF: ${P.message}`),y(P)}),c.on("aborted",()=>{m.info(`[Git/streamAi] \u6D41\u88AB\u4E2D\u6B62, bufferLength=${u.length}`),w(T(u))})})}async function W(r,t,e){const i=await j(String(r||t).trim());if(!i.hasRepository||!i.repositoryRootPath)return m.warn(`[Git/collectDiff] \u4E0D\u5728 Git \u4ED3\u5E93\u4E2D: workspacePath=${t}`),{truncatedDiff:"",error:{status:400,message:"workspace is not inside a git repository"}};const s=e.map(u=>u.path).filter(Boolean);m.info(`[Git/collectDiff] \u5F00\u59CB\u6536\u96C6 ${s.length} \u4E2A\u6682\u5B58\u6587\u4EF6\u5DEE\u5F02: ${s.join(", ").substring(0,200)}`);const n=[];for(const u of s){const f=E(u,i),l=await h(i.repositoryRootPath,["diff","--cached","--",f]);l.exitCode===0&&l.stdout.trim()?n.push(`--- ${u} ---
26
- ${l.stdout.trim()}`):m.info(`[Git/collectDiff] \u6587\u4EF6\u65E0\u6682\u5B58\u5DEE\u5F02\u6216\u83B7\u53D6\u5931\u8D25: ${u}, exitCode=${l.exitCode}, stdout.length=${l.stdout.length}`)}if(n.length===0){m.info("[Git/collectDiff] \u9010\u6587\u4EF6 diff \u4E3A\u7A7A, \u56DE\u9000\u5230\u5168\u91CF diff --cached");const u=await h(i.repositoryRootPath,["diff","--cached"]);u.exitCode===0&&u.stdout.trim()?(n.push(u.stdout.trim()),m.info(`[Git/collectDiff] \u5168\u91CF diff \u83B7\u53D6\u6210\u529F: length=${u.stdout.trim().length}`)):m.info(`[Git/collectDiff] \u5168\u91CF diff \u4E5F\u4E3A\u7A7A, exitCode=${u.exitCode}`)}if(n.length===0)return m.info("[Git/collectDiff] \u6682\u5B58\u533A\u6CA1\u6709\u5DEE\u5F02\u5185\u5BB9 (diffParts \u4E3A\u7A7A)"),{truncatedDiff:""};const a=n.join(`
23
+ `).trim();const e=JSON.stringify(r.substring(0,120)),i=t.match(tt);if(i&&i.index!==void 0&&i.index<=80)t=t.slice(i.index);else{for(let f=0;f<5;f+=1){const d=t;for(const p of Ht)t=t.replace(p,"");if(t=t.replace(/^[\s*\-`,,']+/,"").trim(),t===d)break}const a=t.split(`
24
+ `)[0]?.trim()??"",o=!!t.match(tt),c=!!t.match(Dt),u=Ft(t),l=a.length>100;if(!o&&!c&&!u&&!l)return m.info(`[Git/sanitize] \u5265\u79BB\u540E\u65E0\u6709\u6548\u5185\u5BB9, \u8FD4\u56DE fallback. raw=${e}, afterStrip=${JSON.stringify(t.substring(0,100))}`),"chore: update staged changes";!o&&(c||u||l)&&(t=a||t)}const s=t.search(/\n\s*\n/);if(s>0&&(t=t.slice(0,s)),t=t.replace(/\n+\s*```\s*$/g,"").trim(),!t)return m.info(`[Git/sanitize] \u622A\u65AD\u540E\u4E3A\u7A7A, \u8FD4\u56DE fallback. raw=${e}`),"chore: update staged changes";const n=t.split(`
25
+ `)[0]?.trim()??"";return n.length>100?(m.info(`[Git/sanitize] \u9996\u884C\u8D85\u8FC7 100 \u5B57\u7B26, \u4EC5\u4FDD\u7559\u9996\u884C. raw=${e}, firstLine=${JSON.stringify(n.substring(0,100))}`),n):(m.info(`[Git/sanitize] \u7ED3\u679C: ${JSON.stringify(t.substring(0,120))} (from raw=${e})`),t)}function et(r){const t=r.split(/\r?\n/);let e="";for(const i of t){const s=i.trim();if(!s.startsWith("data:"))continue;const n=s.slice(5).trim();if(!(!n||n==="[DONE]"))try{const o=JSON.parse(n)?.choices?.[0]?.delta?.content;typeof o=="string"&&o.length>0&&(e+=o)}catch{}}return e}function Nt(r,t){if(t>5)return;const e=r.split(/\r?\n/);for(const i of e){const s=i.trim();if(!s.startsWith("data:"))continue;const n=s.slice(5).trim();if(!(!n||n==="[DONE]"))try{const a=JSON.parse(n),o=Object.keys(a),c=a?.choices,u=Array.isArray(c)?c[0]:null,l=u?.delta?Object.keys(u.delta):null,f=u?.delta?.content;m.info(`[Git/extract] chunk#${t} structure: topKeys=${JSON.stringify(o)}, choicesLen=${Array.isArray(c)?c.length:typeof c}, deltaKeys=${JSON.stringify(l)}, content=${JSON.stringify(f?.substring(0,50))}, finishReason=${JSON.stringify(u?.finish_reason)}`)}catch{}}}async function Wt(r,t,e,i){const s=`${r.replace(/\/+$/,"")}/chat/completions`,o=(await L.post(s,{model:e,stream:!1,messages:[{role:"system",content:Z},{role:"user",content:Q(i)}],max_tokens:200,temperature:.2},{headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},timeout:3e4})).data?.choices?.[0]?.message?.content?.trim()||"";return T(o)}async function Lt(r,t,e,i,s,n){const a=`${r.replace(/\/+$/,"")}/chat/completions`,c=(await L.post(a,{model:e,stream:!0,messages:[{role:"system",content:Z},{role:"user",content:Q(i)}],max_tokens:200,temperature:.2},{headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,Accept:"text/event-stream"},timeout:Number(process.env.AWS_AI_STREAM_TIMEOUT_MS)||6e4,responseType:"stream",signal:n})).data;let u="",l="",f="",d=0,p=0;return m.info(`[Git/streamAi] AI \u6D41\u5F00\u59CB: aiModel=${e}`),await new Promise((y,g)=>{const w=P=>{c.removeAllListeners("data"),c.removeAllListeners("end"),c.removeAllListeners("error"),c.removeAllListeners("aborted"),m.info(`[Git/streamAi] AI \u6D41\u7ED3\u675F: totalChunks=${d}, totalDeltas=${p}, bufferLength=${u.length}, sanitized=${JSON.stringify(P).substring(0,200)}`),y(P)};c.on("data",P=>{const C=typeof P=="string"?P:P.toString("utf8");d++,f+=C,d<=3&&m.info(`[Git/streamAi] chunk#${d}: length=${C.length}, preview=${JSON.stringify(C.substring(0,80))}`),Nt(C,d);const v=f.split(/\n\n/);f=v.pop()??"";for(const rt of v){const M=et(rt);M&&(p++,u+=M,l=T(u),s(M,l))}}),c.on("end",()=>{if(f.trim()){const P=et(f);P?(m.info(`[Git/streamAi] \u5C3E\u90E8\u6B8B\u7559 delta: length=${P.length}`),u+=P):m.info(`[Git/streamAi] \u5C3E\u90E8\u6B8B\u7559\u65E0 delta, pendingData=${JSON.stringify(f.substring(0,100))}`)}w(T(u))}),c.on("error",P=>{m.error(`[Git/streamAi] \u6D41\u9519\u8BEF: ${P.message}`),g(P)}),c.on("aborted",()=>{m.info(`[Git/streamAi] \u6D41\u88AB\u4E2D\u6B62, bufferLength=${u.length}`),w(T(u))})})}async function W(r,t,e){const i=await j(String(r||t).trim());if(!i.hasRepository||!i.repositoryRootPath)return m.warn(`[Git/collectDiff] \u4E0D\u5728 Git \u4ED3\u5E93\u4E2D: workspacePath=${t}`),{truncatedDiff:"",error:{status:400,message:"workspace is not inside a git repository"}};const s=e.map(u=>u.path).filter(Boolean);m.info(`[Git/collectDiff] \u5F00\u59CB\u6536\u96C6 ${s.length} \u4E2A\u6682\u5B58\u6587\u4EF6\u5DEE\u5F02: ${s.join(", ").substring(0,200)}`);const n=[];for(const u of s){const l=E(u,i),f=await h(i.repositoryRootPath,["diff","--cached","--",l]);f.exitCode===0&&f.stdout.trim()?n.push(`--- ${u} ---
26
+ ${f.stdout.trim()}`):m.info(`[Git/collectDiff] \u6587\u4EF6\u65E0\u6682\u5B58\u5DEE\u5F02\u6216\u83B7\u53D6\u5931\u8D25: ${u}, exitCode=${f.exitCode}, stdout.length=${f.stdout.length}`)}if(n.length===0){m.info("[Git/collectDiff] \u9010\u6587\u4EF6 diff \u4E3A\u7A7A, \u56DE\u9000\u5230\u5168\u91CF diff --cached");const u=await h(i.repositoryRootPath,["diff","--cached"]);u.exitCode===0&&u.stdout.trim()?(n.push(u.stdout.trim()),m.info(`[Git/collectDiff] \u5168\u91CF diff \u83B7\u53D6\u6210\u529F: length=${u.stdout.trim().length}`)):m.info(`[Git/collectDiff] \u5168\u91CF diff \u4E5F\u4E3A\u7A7A, exitCode=${u.exitCode}`)}if(n.length===0)return m.info("[Git/collectDiff] \u6682\u5B58\u533A\u6CA1\u6709\u5DEE\u5F02\u5185\u5BB9 (diffParts \u4E3A\u7A7A)"),{truncatedDiff:""};const a=n.join(`
27
27
 
28
28
  `),o=15e3,c=a.length>o?`${a.slice(0,o)}
29
29
  ...(diff truncated due to length)`:a;return m.info(`[Git/collectDiff] \u6536\u96C6\u5B8C\u6210: diffContent.length=${a.length}, truncated=${c.length}`),{truncatedDiff:c}}function $(r,t,e){try{r.write(`event: ${t}
30
30
  `),r.write(`data: ${JSON.stringify(e)}
31
31
 
32
- `)}catch{}}S.post("/git/generate-commit-message",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,stagedFiles:s,aiBaseUrl:n,aiApiKey:a,aiModel:o}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!Array.isArray(s)||s.length===0){t.status(400).json({error:"stagedFiles is required and must be a non-empty array"});return}if(!n||!String(n).trim()||!a||!String(a).trim()||!o||!String(o).trim()){t.status(400).json({error:"aiBaseUrl, aiApiKey and aiModel are required"});return}try{const{truncatedDiff:c,error:u}=await W(String(i||""),String(e),s);if(u){t.status(u.status).json({error:u.message});return}if(!c){t.json({message:"\u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9"});return}const f=await Wt(n,a,o,c);t.json({message:f})}catch(c){const u=c;m.error(`[Git] generate commit message failed: ${u.message}`),t.status(500).json({error:u.message||"generate commit message failed"})}}),S.post("/git/generate-commit-message-stream",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,stagedFiles:s,aiBaseUrl:n,aiApiKey:a,aiModel:o}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!Array.isArray(s)||s.length===0){t.status(400).json({error:"stagedFiles is required and must be a non-empty array"});return}if(!n||!String(n).trim()||!a||!String(a).trim()||!o||!String(o).trim()){t.status(400).json({error:"aiBaseUrl, aiApiKey and aiModel are required"});return}m.info(`[Git/SSE] \u6536\u5230 generate-commit-message-stream \u8BF7\u6C42: workspacePath=${String(e||"").substring(0,40)}, stagedFiles=${s?.length??0}, aiModel=${o}, aiBaseUrl=${n}`),t.status(200),t.setHeader("Content-Type","text/event-stream; charset=utf-8"),t.setHeader("Cache-Control","no-cache, no-transform"),t.setHeader("Connection","keep-alive"),t.setHeader("X-Accel-Buffering","no"),r.socket?.setNoDelay(!0),t.flushHeaders?.();let c=!1,u=null;const f=!1,l={delta:0},d=`${r.ip}:${r.socket?.remotePort??"?"}`;r.on("close",()=>{c||(c=!0,m.info(`[Git/SSE] \u5BA2\u6237\u7AEF\u65AD\u5F00\u8FDE\u63A5: workspacePath=${String(e||"").substring(0,40)}, deltaEventsSent=${l.delta}, peer=${d}`))}),$(t,"init",{status:"generating"});try{const{truncatedDiff:p,error:g}=await W(String(i||""),String(e),s);if(g){m.warn(`[Git/SSE] collectStagedDiffForAi \u8FD4\u56DE\u9519\u8BEF: ${g.message}`),$(t,"error",{message:g.message}),t.end();return}if(!p){m.info("[Git/SSE] \u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9, \u76F4\u63A5\u8FD4\u56DE done"),$(t,"delta",{contentDelta:"",message:"\u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9"}),$(t,"done",{message:"\u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9"}),t.end();return}m.info(`[Git/SSE] \u6536\u96C6\u5230 diff: truncatedDiff.length=${p.length}`),c&&m.info("[Git/SSE] \u5BA2\u6237\u7AEF\u5728 collectDiff \u671F\u95F4\u65AD\u5F00, \u4F46\u4ECD\u5C1D\u8BD5 AI \u8C03\u7528"),m.info(`[Git/SSE] \u5F00\u59CB\u8C03\u7528 AI streamAiForCommitMessage: aiModel=${o}`),u=new AbortController;let y="";const w=await Lt(n,a,o,p,(P,C)=>{l.delta++,C!==y?(y=C,$(t,"delta",{contentDelta:P,message:C})):$(t,"delta",{contentDelta:P,message:C})},u.signal);m.info(`[Git/SSE] AI streamAiForCommitMessage \u5B8C\u6210: finalMessage=${JSON.stringify(w).substring(0,200)}, deltaEventsSent=${l.delta}`),c||$(t,"done",{message:w}),t.end()}catch(p){const g=p;m.error(`[Git/SSE] stream commit message failed: ${g.message}${g.stack?`
33
- `+g.stack:""}`),c||$(t,"error",{message:g.message||"generate commit message failed"}),t.end()}}),S.post("/git/unstage-files",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePaths:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=(Array.isArray(s)?s:[]).map(o=>x(String(o||"").trim())).filter(o=>o.length>0);if(a.length===0){t.status(400).json({error:"filePaths is required and must be a non-empty array"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}let c="";for(const u of a){const f=G(u,o),l=await h(o.repositoryRootPath,["restore","--staged",...f]);l.exitCode!==0&&(c=l.stderr||`git restore --staged failed for ${u}`)}if(c){t.status(400).json({error:c});return}t.json({ok:!0,filePaths:a,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git unstage files failed"})}}),S.post("/git/restore-files",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePaths:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=(Array.isArray(s)?s:[]).map(o=>x(String(o||"").trim())).filter(o=>o.length>0);if(a.length===0){t.status(400).json({error:"filePaths is required and must be a non-empty array"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}let c="";for(const u of a){const f=E(u,o),l=G(u,o),p=(await h(o.repositoryRootPath,["ls-files","--error-unmatch","--",f])).exitCode===0,g=p?await h(o.repositoryRootPath,["restore",...l]):await h(o.repositoryRootPath,["clean","-f",...l]);g.exitCode!==0&&(c=g.stderr||`${p?"git restore":"git clean -f"} failed for ${u}`)}if(c){t.status(400).json({error:c});return}t.json({ok:!0,filePaths:a,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git restore files failed"})}});function _t(r){const t=r.split(`
34
- `),e=[];for(let i=0;i<t.length;i++){const s=t[i],n=s.endsWith("\r")?"\r":"",o=(n?s.slice(0,-1):s).match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);if(!o){e.push(s);continue}let c=0,u=0;for(let f=i+1;f<t.length;f++){const l=t[f].replace(/\r$/,"");if(/^diff --git /.test(l)||/^@@ /.test(l))break;l.startsWith("+")?u++:l.startsWith("-")?c++:l.startsWith(" ")&&(c++,u++)}e.push(`@@ -${o[1]},${c} +${o[2]},${u} @@${n}`)}return e.join(`
35
- `)}S.post("/git/revert-hunk",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePath:s,patch:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!s||!String(s).trim()){t.status(400).json({error:"filePath is required"});return}const a=String(n||"").replace(/^\uFEFF/,"").trim();if(!a){t.status(400).json({error:"patch is required"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=x(String(s).trim()),u=E(c,o);if(M(u),(a.match(/^diff --git /gm)||[]).length!==1){t.status(400).json({error:"patch must contain exactly one file diff"});return}const l=a.match(/^\+\+\+ b\/(.+)$/m),d=l?x(l[1].trim()):"";if(!d||d!==u){t.status(400).json({error:"patch target path does not match filePath"});return}let p=null;try{p=await b.mkdtemp(R.join(ot.tmpdir(),"agentswork-git-revert-"));const g=R.join(p,"revert.patch");await b.writeFile(g,_t(a)+`
36
- `,"utf-8");const y=await h(o.repositoryRootPath,["apply","--reverse",g]);if(y.exitCode!==0){t.status(400).json({error:y.stderr||"git apply --reverse failed; \u8BE5\u5904\u5185\u5BB9\u53EF\u80FD\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u5DEE\u5F02\u540E\u91CD\u8BD5"});return}t.json({ok:!0,filePath:c,repositoryRelativeFilePath:u,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath})}finally{p&&await b.rm(p,{recursive:!0,force:!0}).catch(()=>{})}}catch(o){const c=o;t.status(500).json({error:c.message||"git revert hunk failed"})}}),S.post("/git/diff-content",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,stagedFiles:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!Array.isArray(s)||s.length===0){t.status(400).json({error:"stagedFiles is required and must be a non-empty array"});return}try{const{truncatedDiff:n,error:a}=await W(String(i||""),String(e),s);if(a){t.status(a.status).json({error:a.message});return}t.json({ok:!0,diff:n||""})}catch(n){const a=n;m.error(`[Git/diff-content] \u83B7\u53D6\u6682\u5B58\u533A\u5DEE\u5F02\u5931\u8D25: ${a.message}`),t.status(500).json({error:a.message||"get diff content failed"})}});export{ct as assertGitWorkspacePathAccessible,bt as buildCommitDiffBaseArgs,Wt as callAiForCommitMessage,St as collectUntrackedFiles,At as commitSelectedFiles,kt as countUntrackedFileAdditions,pt as createAgentsWorkStashSnapshotRef,vt as createGitStashSnapshot,ht as createMissingInitialCommitSnapshotError,G as createScopedFilePathspecArgs,jt as createScopedGitPathspecArgs,Nt as debugAiStreamChunk,et as extractOpenAiStreamDelta,S as gitRouter,ft as isGitStashMissingInitialCommitError,dt as isGitStashNoChangesOutput,J as listGitBranches,Rt as mergeGitDiffSummaryFiles,xt as normalizeGitCommitMessage,H as parseGitDiffSummary,_ as parseGitStashListLine,wt as parseGitStatusStagedPaths,Pt as parseGitStatusUntrackedPaths,I as parseLatestGitStash,z as persistAgentsWorkStashSnapshotRef,Tt as previewRepositoryReset,$t as pushBranchToRemote,Dt as resetRepositoryTo,N as resolveCurrentBranchName,nt as resolveGitCommandTimeoutMs,gt as resolveGitPushTimeoutMs,U as resolveRemoteCommitHashes,T as sanitizeCommitMessage,lt as shouldIncludeGitDiffSummaryFile,Lt as streamAiForCommitMessage};
32
+ `)}catch{}}S.post("/git/generate-commit-message",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,stagedFiles:s,aiBaseUrl:n,aiApiKey:a,aiModel:o}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!Array.isArray(s)||s.length===0){t.status(400).json({error:"stagedFiles is required and must be a non-empty array"});return}if(!n||!String(n).trim()||!a||!String(a).trim()||!o||!String(o).trim()){t.status(400).json({error:"aiBaseUrl, aiApiKey and aiModel are required"});return}try{const{truncatedDiff:c,error:u}=await W(String(i||""),String(e),s);if(u){t.status(u.status).json({error:u.message});return}if(!c){t.json({message:"\u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9"});return}const l=await Wt(n,a,o,c);t.json({message:l})}catch(c){const u=c;m.error(`[Git] generate commit message failed: ${u.message}`),t.status(500).json({error:u.message||"generate commit message failed"})}}),S.post("/git/generate-commit-message-stream",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,stagedFiles:s,aiBaseUrl:n,aiApiKey:a,aiModel:o}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!Array.isArray(s)||s.length===0){t.status(400).json({error:"stagedFiles is required and must be a non-empty array"});return}if(!n||!String(n).trim()||!a||!String(a).trim()||!o||!String(o).trim()){t.status(400).json({error:"aiBaseUrl, aiApiKey and aiModel are required"});return}m.info(`[Git/SSE] \u6536\u5230 generate-commit-message-stream \u8BF7\u6C42: workspacePath=${String(e||"").substring(0,40)}, stagedFiles=${s?.length??0}, aiModel=${o}, aiBaseUrl=${n}`),t.status(200),t.setHeader("Content-Type","text/event-stream; charset=utf-8"),t.setHeader("Cache-Control","no-cache, no-transform"),t.setHeader("Connection","keep-alive"),t.setHeader("X-Accel-Buffering","no"),r.socket?.setNoDelay(!0),t.flushHeaders?.();let c=!1,u=null;const l={delta:0},f=`${r.ip}:${r.socket?.remotePort??"?"}`;r.on("close",()=>{c||(c=!0,m.info(`[Git/SSE] \u5BA2\u6237\u7AEF\u65AD\u5F00\u8FDE\u63A5: workspacePath=${String(e||"").substring(0,40)}, deltaEventsSent=${l.delta}, peer=${f}`))}),$(t,"init",{status:"generating"});try{const{truncatedDiff:d,error:p}=await W(String(i||""),String(e),s);if(p){m.warn(`[Git/SSE] collectStagedDiffForAi \u8FD4\u56DE\u9519\u8BEF: ${p.message}`),$(t,"error",{message:p.message}),t.end();return}if(!d){m.info("[Git/SSE] \u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9, \u76F4\u63A5\u8FD4\u56DE done"),$(t,"delta",{contentDelta:"",message:"\u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9"}),$(t,"done",{message:"\u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9"}),t.end();return}m.info(`[Git/SSE] \u6536\u96C6\u5230 diff: truncatedDiff.length=${d.length}`),c&&m.info("[Git/SSE] \u5BA2\u6237\u7AEF\u5728 collectDiff \u671F\u95F4\u65AD\u5F00, \u4F46\u4ECD\u5C1D\u8BD5 AI \u8C03\u7528"),m.info(`[Git/SSE] \u5F00\u59CB\u8C03\u7528 AI streamAiForCommitMessage: aiModel=${o}`),u=new AbortController;let y="";const g=await Lt(n,a,o,d,(w,P)=>{l.delta++,P!==y?(y=P,$(t,"delta",{contentDelta:w,message:P})):$(t,"delta",{contentDelta:w,message:P})},u.signal);m.info(`[Git/SSE] AI streamAiForCommitMessage \u5B8C\u6210: finalMessage=${JSON.stringify(g).substring(0,200)}, deltaEventsSent=${l.delta}`),c||$(t,"done",{message:g}),t.end()}catch(d){const p=d;m.error(`[Git/SSE] stream commit message failed: ${p.message}${p.stack?`
33
+ `+p.stack:""}`),c||$(t,"error",{message:p.message||"generate commit message failed"}),t.end()}}),S.post("/git/unstage-files",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePaths:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=(Array.isArray(s)?s:[]).map(o=>R(String(o||"").trim())).filter(o=>o.length>0);if(a.length===0){t.status(400).json({error:"filePaths is required and must be a non-empty array"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}let c="";for(const u of a){const l=G(u,o),f=await h(o.repositoryRootPath,["restore","--staged",...l]);f.exitCode!==0&&(c=f.stderr||`git restore --staged failed for ${u}`)}if(c){t.status(400).json({error:c});return}t.json({ok:!0,filePaths:a,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git unstage files failed"})}}),S.post("/git/restore-files",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePaths:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=(Array.isArray(s)?s:[]).map(o=>R(String(o||"").trim())).filter(o=>o.length>0);if(a.length===0){t.status(400).json({error:"filePaths is required and must be a non-empty array"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}let c="";for(const u of a){const l=E(u,o),f=G(u,o),p=(await h(o.repositoryRootPath,["ls-files","--error-unmatch","--",l])).exitCode===0,y=p?await h(o.repositoryRootPath,["restore",...f]):await h(o.repositoryRootPath,["clean","-f",...f]);y.exitCode!==0&&(c=y.stderr||`${p?"git restore":"git clean -f"} failed for ${u}`)}if(c){t.status(400).json({error:c});return}t.json({ok:!0,filePaths:a,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git restore files failed"})}});function _t(r){const t=r.split(`
34
+ `),e=[];for(let i=0;i<t.length;i++){const s=t[i],n=s.endsWith("\r")?"\r":"",o=(n?s.slice(0,-1):s).match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);if(!o){e.push(s);continue}let c=0,u=0;for(let l=i+1;l<t.length;l++){const f=t[l].replace(/\r$/,"");if(/^diff --git /.test(f)||/^@@ /.test(f))break;f.startsWith("+")?u++:f.startsWith("-")?c++:f.startsWith(" ")&&(c++,u++)}e.push(`@@ -${o[1]},${c} +${o[2]},${u} @@${n}`)}return e.join(`
35
+ `)}S.post("/git/revert-hunk",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePath:s,patch:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!s||!String(s).trim()){t.status(400).json({error:"filePath is required"});return}const a=String(n||"").replace(/^\uFEFF/,"").trim();if(!a){t.status(400).json({error:"patch is required"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=R(String(s).trim()),u=E(c,o);if(D(u),(a.match(/^diff --git /gm)||[]).length!==1){t.status(400).json({error:"patch must contain exactly one file diff"});return}const f=a.match(/^\+\+\+ b\/(.+)$/m),d=f?R(f[1].trim()):"";if(!d||d!==u){t.status(400).json({error:"patch target path does not match filePath"});return}let p=null;try{p=await b.mkdtemp(x.join(ot.tmpdir(),"agentswork-git-revert-"));const y=x.join(p,"revert.patch");await b.writeFile(y,_t(a)+`
36
+ `,"utf-8");const g=await h(o.repositoryRootPath,["apply","--reverse",y]);if(g.exitCode!==0){t.status(400).json({error:g.stderr||"git apply --reverse failed; \u8BE5\u5904\u5185\u5BB9\u53EF\u80FD\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u5DEE\u5F02\u540E\u91CD\u8BD5"});return}t.json({ok:!0,filePath:c,repositoryRelativeFilePath:u,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath})}finally{p&&await b.rm(p,{recursive:!0,force:!0}).catch(()=>{})}}catch(o){const c=o;t.status(500).json({error:c.message||"git revert hunk failed"})}}),S.post("/git/diff-content",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,stagedFiles:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!Array.isArray(s)||s.length===0){t.status(400).json({error:"stagedFiles is required and must be a non-empty array"});return}try{const{truncatedDiff:n,error:a}=await W(String(i||""),String(e),s);if(a){t.status(a.status).json({error:a.message});return}t.json({ok:!0,diff:n||""})}catch(n){const a=n;m.error(`[Git/diff-content] \u83B7\u53D6\u6682\u5B58\u533A\u5DEE\u5F02\u5931\u8D25: ${a.message}`),t.status(500).json({error:a.message||"get diff content failed"})}});export{ct as assertGitWorkspacePathAccessible,bt as buildCommitDiffBaseArgs,Wt as callAiForCommitMessage,St as collectUntrackedFiles,At as commitSelectedFiles,kt as countUntrackedFileAdditions,pt as createAgentsWorkStashSnapshotRef,vt as createGitStashSnapshot,ht as createMissingInitialCommitSnapshotError,G as createScopedFilePathspecArgs,jt as createScopedGitPathspecArgs,Nt as debugAiStreamChunk,et as extractOpenAiStreamDelta,S as gitRouter,ft as isGitStashMissingInitialCommitError,dt as isGitStashNoChangesOutput,J as listGitBranches,Rt as mergeGitDiffSummaryFiles,xt as normalizeGitCommitMessage,H as parseGitDiffSummary,_ as parseGitStashListLine,Pt as parseGitStatusStagedPaths,wt as parseGitStatusUntrackedPaths,I as parseLatestGitStash,z as persistAgentsWorkStashSnapshotRef,Tt as previewRepositoryReset,$t as pushBranchToRemote,Mt as resetRepositoryTo,N as resolveCurrentBranchName,nt as resolveGitCommandTimeoutMs,gt as resolveGitPushTimeoutMs,U as resolveRemoteCommitHashes,T as sanitizeCommitMessage,lt as shouldIncludeGitDiffSummaryFile,Lt as streamAiForCommitMessage};
37
37