@yuandc/aica 0.1.63 → 0.1.65

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.
@@ -1,2 +1,2 @@
1
- import{execFileSync as p}from"node:child_process";import h from"node:fs";import c from"node:path";import d from"node:crypto";const g=50*1024*1024,m=["path","file","filePath","file_path","filename","targetPath","target_path","move_path"];function T(t){if(!x(t))return null;const e=w(t);if(!e)return null;const r=new Map;for(const n of e)r.set(n.path,{status:n.status,objectHash:n.status.includes("D")?null:A(t,n.path)});return r}function R(t){const e=new Map;for(const r of t.evidencePaths)P(e,t.cwd,r,"unknown","acp_tool");return[...e.values()].filter(r=>r.relativePath&&!r.relativePath.startsWith(".git/")).sort((r,n)=>r.relativePath.localeCompare(n.relativePath))}function G(t){const e=new Set;return u(t,(r,n)=>{if(typeof r=="string"&&(S(n)&&l(e,r),n==="command"))for(const s of F(r))l(e,s)}),v(t,e),j(t,e),[...e]}function P(t,e,r,n,s){const i=b(e,r);if(!i)return;const a=t.get(i.relativePath),o=C(i.absolutePath),f={path:i.absolutePath,absolutePath:i.absolutePath,relativePath:i.relativePath,changeType:y(a?.changeType,n),source:a?.source==="acp_tool"||a?.source==="acp_diff"?a.source:s,exists:!!o?.isFile(),downloadable:!!o?.isFile(),...o?.isFile()?{size:o.size,mime:z(i.absolutePath),sha256:o.size<=g?_(i.absolutePath):void 0}:{}};t.set(i.relativePath,f)}function y(t,e){return!t||t==="unknown"?e:e==="unknown"||t===e?t:t==="created"&&e==="modified"?"created":e}function b(t,e){if(!e||e.includes("\0"))return null;const r=c.resolve(t),n=c.isAbsolute(e)?c.resolve(e):c.resolve(r,e),s=c.relative(r,n).replace(/\\/g,"/");return!s||s.startsWith("..")||c.isAbsolute(s)?null:{absolutePath:n,relativePath:s}}function x(t){try{return p("git",["-C",t,"rev-parse","--is-inside-work-tree"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}),!0}catch{return!1}}function w(t){try{const r=p("git",["-C",t,"status","--porcelain=v1","-z","--untracked-files=all"],{encoding:"buffer",maxBuffer:20971520}).toString("utf8").split("\0").filter(Boolean),n=[];for(let s=0;s<r.length;s+=1){const i=r[s],a=i.slice(0,2),o=i.slice(3);if(o)if(a.startsWith("R")||a.startsWith("C")){const f=r[s+1];f&&(n.push({status:a,path:f}),s+=1)}else n.push({status:a,path:o})}return n}catch{return null}}function A(t,e){try{const r=p("git",["-C",t,"hash-object","--",e],{encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:1048576}).trim();return/^[0-9a-f]{40,64}$/i.test(r)?r:null}catch{return null}}function u(t,e,r,n=0){if(!(n>8)&&(e(t,r),!(!t||typeof t!="object"))){if(Array.isArray(t)){for(const s of t)u(s,e,void 0,n+1);return}for(const[s,i]of Object.entries(t))u(i,e,s,n+1)}}function v(t,e){u(t,r=>{if(!r||typeof r!="object"||Array.isArray(r))return;const n=r;n.type==="diff"&&l(e,n.path)})}function j(t,e){u(t,(r,n)=>{if(!(n!=="changes"||!r||typeof r!="object"||Array.isArray(r)))for(const s of Object.keys(r))l(e,s)})}function S(t){return t?m.includes(t):!1}function l(t,e){if(typeof e!="string")return;const r=k(e);!r||r.length>500||r.includes(`
2
- `)||r==="."||r===".."||t.add(r)}function k(t){let e=t.trim();e=e.replace(/\s+\+\d+\s*\/\s*-\d+(?:\s+.*)?$/,""),e=e.replace(/\s+\(\s*\+\d+\s*[,/ ]\s*-\d+\s*\)(?:\s+.*)?$/,""),e=e.replace(/\s+(?:source|event|workspace|session|last)=\S+.*$/,"");const r=e.split(/\s+/)[0]||"";return r&&/\.[A-Za-z][A-Za-z0-9+-]{0,12}$/.test(r)&&(e=r),e.replace(/^["']|["']$/g,"").trim()}function F(t){const e=[],r=/(?:^|\s)(?:\d?>|>>)\s*(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;let n;for(;n=r.exec(t);){const i=n[1]||n[2]||n[3];i&&e.push(i)}const s=/(?:^|\s)touch\s+(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;for(;n=s.exec(t);){const i=n[1]||n[2]||n[3];i&&!i.startsWith("-")&&e.push(i)}return e}function C(t){try{return h.statSync(t)}catch{return null}}function _(t){try{return d.createHash("sha256").update(h.readFileSync(t)).digest("hex")}catch{return}}function z(t){switch(c.extname(t).toLowerCase()){case".pdf":return"application/pdf";case".md":case".markdown":return"text/markdown; charset=utf-8";case".txt":case".log":return"text/plain; charset=utf-8";case".json":return"application/json; charset=utf-8";case".csv":return"text/csv; charset=utf-8";case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".gif":return"image/gif";case".webp":return"image/webp";case".zip":return"application/zip";case".docx":return"application/vnd.openxmlformats-officedocument.wordprocessingml.document";case".xlsx":return"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";default:return"application/octet-stream"}}export{T as captureGitSnapshot,R as collectAffectedFiles,G as extractAffectedFilePathsFromAcpRaw};
1
+ import{execFileSync as l}from"node:child_process";import d from"node:fs";import c from"node:path";import h from"node:crypto";const m=50*1024*1024,g=["path","file","filePath","file_path","filename","targetPath","target_path","move_path"];function T(t){if(!b(t))return null;const e=v(t);if(!e)return null;const n=new Map;for(const r of e)n.set(r.path,{status:r.status,objectHash:r.status.includes("D")?null:w(t,r.path)});return n}function R(t){const e=new Map;for(const n of t.evidencePaths)x(e,t.cwd,n,"unknown","acp_tool");return[...e.values()].filter(n=>n.relativePath&&!n.relativePath.startsWith(".git/")).sort((n,r)=>n.relativePath.localeCompare(r.relativePath))}function E(t){const e=new Set;return u(t,(n,r)=>{if(typeof n=="string"&&(S(r)&&p(e,n),r==="command"))for(const s of F(n))p(e,s)}),A(t,e),j(t,e),[...e]}function x(t,e,n,r,s){const a=y(e,n);if(!a)return;const i=t.get(a.relativePath),o=C(a.absolutePath),f={path:a.absolutePath,absolutePath:a.absolutePath,relativePath:a.relativePath,changeType:P(i?.changeType,r),source:i?.source==="acp_tool"||i?.source==="acp_diff"?i.source:s,exists:!!o?.isFile(),downloadable:!!o?.isFile(),...o?.isFile()?{size:o.size,mime:z(a.absolutePath),sha256:o.size<=m?_(a.absolutePath):void 0}:{}};t.set(a.relativePath,f)}function P(t,e){return!t||t==="unknown"?e:e==="unknown"||t===e?t:t==="created"&&e==="modified"?"created":e}function y(t,e){if(!e||e.includes("\0"))return null;const n=c.resolve(t),r=c.isAbsolute(e)?c.resolve(e):c.resolve(n,e),s=c.relative(n,r).replace(/\\/g,"/");return!s||s.startsWith("..")||c.isAbsolute(s)?null:{absolutePath:r,relativePath:s}}function b(t){try{return l("git",["-C",t,"rev-parse","--is-inside-work-tree"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}),!0}catch{return!1}}function v(t){try{const n=l("git",["-C",t,"status","--porcelain=v1","-z","--untracked-files=all"],{encoding:"buffer",maxBuffer:20971520}).toString("utf8").split("\0").filter(Boolean),r=[];for(let s=0;s<n.length;s+=1){const a=n[s],i=a.slice(0,2),o=a.slice(3);if(o)if(i.startsWith("R")||i.startsWith("C")){const f=n[s+1];f&&(r.push({status:i,path:f}),s+=1)}else r.push({status:i,path:o})}return r}catch{return null}}function w(t,e){try{const n=l("git",["-C",t,"hash-object","--",e],{encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:1048576}).trim();return/^[0-9a-f]{40,64}$/i.test(n)?n:null}catch{return null}}function u(t,e,n,r=0){if(!(r>8)&&(e(t,n),!(!t||typeof t!="object"))){if(Array.isArray(t)){for(const s of t)u(s,e,void 0,r+1);return}for(const[s,a]of Object.entries(t))u(a,e,s,r+1)}}function A(t,e){u(t,n=>{if(!n||typeof n!="object"||Array.isArray(n))return;const r=n;r.type==="diff"&&p(e,r.path)})}function j(t,e){u(t,(n,r)=>{if(!(r!=="changes"||!n||typeof n!="object"||Array.isArray(n)))for(const s of Object.keys(n))p(e,s)})}function S(t){return t?g.includes(t):!1}function p(t,e){if(typeof e!="string")return;const n=k(e);!n||n.length>500||n.includes(`
2
+ `)||n==="."||n===".."||t.add(n)}function k(t){let e=t.trim();e=e.replace(/\s+\+\d+\s*\/\s*-\d+(?:\s+.*)?$/,""),e=e.replace(/\s+\(\s*\+\d+\s*[,/ ]\s*-\d+\s*\)(?:\s+.*)?$/,""),e=e.replace(/\s+(?:source|event|workspace|session|last)=\S+.*$/,"");const n=e.split(/\s+/)[0]||"";return n&&/\.[A-Za-z][A-Za-z0-9+-]{0,12}$/.test(n)&&(e=n),e.replace(/^["']|["']$/g,"").trim()}function F(t){const e=[],n=/(?:^|\s)(?:\d?>|>>)\s*(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;let r;for(;r=n.exec(t);){const a=r[1]||r[2]||r[3];a&&e.push(a)}const s=/(?:^|\s)touch\s+(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;for(;r=s.exec(t);){const a=r[1]||r[2]||r[3];a&&!a.startsWith("-")&&e.push(a)}return e}function C(t){try{return d.statSync(t)}catch{return null}}function _(t){try{return h.createHash("sha256").update(d.readFileSync(t)).digest("hex")}catch{return}}function z(t){switch(c.extname(t).toLowerCase()){case".doc":return"application/msword";case".xls":return"application/vnd.ms-excel";case".xlsm":return"application/vnd.ms-excel.sheet.macroEnabled.12";case".ppt":return"application/vnd.ms-powerpoint";case".pptx":return"application/vnd.openxmlformats-officedocument.presentationml.presentation";case".odt":return"application/vnd.oasis.opendocument.text";case".ods":return"application/vnd.oasis.opendocument.spreadsheet";case".odp":return"application/vnd.oasis.opendocument.presentation";case".rtf":return"application/rtf";case".svg":return"image/svg+xml";case".bmp":return"image/bmp";case".tif":case".tiff":return"image/tiff";case".avif":return"image/avif";case".pdf":return"application/pdf";case".md":case".markdown":return"text/markdown; charset=utf-8";case".txt":case".log":return"text/plain; charset=utf-8";case".json":return"application/json; charset=utf-8";case".csv":return"text/csv; charset=utf-8";case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".gif":return"image/gif";case".webp":return"image/webp";case".zip":return"application/zip";case".docx":return"application/vnd.openxmlformats-officedocument.wordprocessingml.document";case".xlsx":return"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";default:return"application/octet-stream"}}export{T as captureGitSnapshot,R as collectAffectedFiles,E as extractAffectedFilePathsFromAcpRaw,z as mimeForPath};
@@ -0,0 +1 @@
1
+ import l from"node:fs";import n from"node:path";import{mimeForPath as x}from"../affected-files.js";const A=new Set([".pdf",".doc",".docx",".odt",".rtf",".xls",".xlsx",".xlsm",".ods",".csv",".ppt",".pptx",".odp",".png",".jpg",".jpeg",".gif",".webp",".bmp",".tif",".tiff",".svg",".avif"]),_=new Set([".git","node_modules",".venv","venv","__pycache__"]),z=[".aca/attachments",".aica/attachments",".aca/artifacts",".aica/artifacts",".aca/logs",".aica/logs",".aca/server-data",".aica/server-data"],M=8192,S=1e4,u=new Map;function m(o){const t=n.basename(o);return!f(o)&&!t.startsWith("~$")&&A.has(n.extname(t).toLowerCase())}function f(o){const t=o.split(n.sep).join("/");return t.split("/").some(e=>_.has(e))||z.some(e=>t===e||t.startsWith(`${e}/`))}class N{root;warn;watches=new Map;subscribers=new Set;pending=new Set;timer;closed=!1;warned=!1;draining=!1;constructor(t,e){this.root=t,this.warn=e}degraded(t){this.warned||this.closed||(this.warned=!0,this.warn(`文档监听部分不可用,将以结束扫描补偿(仍可能遗漏瞬时变化):${String(t)}`))}watchDirectory(t){if(!(this.closed||this.watches.has(t))){if(this.watches.size>=M){this.degraded("目录监听数量达到上限");return}try{const e=l.watch(t,{persistent:!1},(i,s)=>{if(this.closed||!s)return;const a=n.join(t,s.toString()),r=n.relative(this.root,a);if(!f(r)){if(m(r))for(const c of this.subscribers)c.size<S?c.add(r):this.degraded("文件事件候选数量达到上限");i==="rename"&&this.schedule(a)}});e.on("error",i=>{e.close(),this.watches.get(t)===e&&this.watches.delete(t),this.degraded(i)}),this.watches.set(t,e)}catch(e){this.degraded(e)}}}schedule(t){if(this.pending.size>=S){this.degraded("目录事件积压");return}this.pending.add(t),!(this.timer||this.draining)&&(this.timer=setTimeout(()=>{this.timer=void 0,this.drain()},50),this.timer.unref())}async drain(){this.draining=!0;try{for(;!this.closed&&this.pending.size>0;){const t=this.pending.values().next().value;if(this.pending.delete(t),this.watches.has(t))for(const[e,i]of this.watches)(e===t||e.startsWith(`${t}${n.sep}`))&&(i.close(),this.watches.delete(e));await this.walk(t,new Map)}}finally{this.draining=!1}}async snapshot(){const t=new Map;return await this.walk(this.root,t),t}async walk(t,e){if(this.closed)return;const i=n.relative(this.root,t);if(!f(i))try{const s=await l.promises.lstat(t);if(s.isSymbolicLink())return;if(s.isDirectory()){this.watchDirectory(t);const a=await l.promises.opendir(t);for await(const r of a){if(this.closed)break;if(r.isSymbolicLink())continue;const c=n.join(t,r.name),d=n.relative(this.root,c);f(d)||(r.isDirectory()||m(d))&&await this.walk(c,e)}}else if(s.isFile()&&m(i)){const a=await l.promises.realpath(t),r=n.relative(this.root,a);if(r.startsWith(`..${n.sep}`)||n.isAbsolute(r)||f(r))return;e.set(i,{size:s.size,signature:`${s.dev}:${s.ino}:${s.size}:${s.mtimeMs}:${s.ctimeMs}`})}}catch(s){const a=s.code;a!=="ENOENT"&&a!=="ENOTDIR"&&this.degraded(s)}}close(){this.closed=!0,this.timer&&clearTimeout(this.timer);for(const t of this.watches.values())t.close();this.watches.clear(),this.pending.clear()}}async function j(o,t){const e=await l.promises.realpath(o);let i=u.get(e);i||(i=new N(e,t),u.set(e,i));const s=i,a=new Set;s.subscribers.add(a);let r=!1;const c=()=>{r||(r=!0,s.subscribers.delete(a),s.subscribers.size===0&&(s.close(),u.delete(e)))};let d;try{d=await s.snapshot(),a.clear()}catch(w){throw c(),w}let b;return{dispose:c,finish(){return b??=(async()=>{if(r)return[];try{const w=await s.snapshot(),g=[];for(const[h,p]of w){const v=d.get(h);if(!a.has(h)&&v?.signature===p.signature)continue;const y=n.join(e,h);g.push({path:y,absolutePath:y,relativePath:h.split(n.sep).join("/"),changeType:v?"modified":"created",source:"job_time_window",exists:!0,downloadable:!0,size:p.size,mime:x(h)})}return g.sort((h,p)=>h.relativePath.localeCompare(p.relativePath))}finally{c()}})()}}}function k(o,t){const e=new Map(o.map(i=>[i.relativePath,i]));for(const i of t)e.set(i.relativePath,i);return[...e.values()]}export{m as isArtifactPath,f as isIgnoredArtifactPath,k as mergeArtifactFiles,j as startJobArtifactMonitor};
@@ -1,6 +1,6 @@
1
- import X from"node:crypto";import{appendSessionReferenceContext as Z}from"./session-reference-prompt.js";import N from"node:fs";import M from"node:path";import{compactAgentContext as Q,forkAgentSession as Y,sendAgentPrompt as ee}from"../acp/agent.js";import{acaServerDownload as te,acaServerRequest as S}from"./aca-server-client.js";import{loadAcaConfig as ne}from"./aca-config.js";import{prepareLocalWorktree as oe}from"./worktree.js";import{expandWorkerRuntimePath as ie}from"./worker-runtime-paths.js";import{collectAffectedFiles as J,extractAffectedFilePathsFromAcpRaw as ae}from"./affected-files.js";import{resolveJobPermissionPolicy as re}from"./job-permission-policy.js";import{createAcpEventBatcher as se}from"./acp-event-batcher.js";import{createToolLifecycleTrace as ce}from"../acp/tool-lifecycle-trace.js";import{shouldBufferAcpEvent as le}from"./acp-event-upload-filter.js";import{userFacingAcpError as de}from"../acp/error-display.js";import{ensureCodexProviderRuntimeRevision as ue}from"./codex-provider-runtime-config.js";import{acpSessionInvalidationFromError as pe}from"../acp/session-errors.js";const me=1500,B=5e3,L=1e3,W=900,fe=1e4,he=0,we=12e3;function Ie(e){return ie(e)}function ye(e){const t=e.project_metadata;return t&&typeof t=="object"&&!Array.isArray(t)&&t.kind==="chat"?!0:typeof e.project_root_path=="string"&&(e.project_root_path.startsWith("~/.aica/chats/")||e.project_root_path.startsWith("~/.aca/chats/")||/(^|\/)\.aica\/chats\//.test(e.project_root_path)||/(^|\/)\.aca\/chats\//.test(e.project_root_path))}function lt(e,t={}){let n=!1,i=null,o=null,s=0,l=0;const u=new Set,r=new Map,b=new Map,A=async()=>{if(n||Date.now()<l)return;const m=ve();if(D(u.size,m))try{const p=t.loadConfig?.()??ne();if(!p.token)return;for(;!n&&D(u.size,m);){const v=t.claimNextJob?await t.claimNextJob(p,[...r.keys()]):await S("POST","/api/client/jobs/claim",{machineId:p.machineId,workspaceId:p.defaultWorkspaceId,activeSessionIds:[...r.keys()]});if(s=0,l=0,!v.item)break;const f=v.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(f,p):j(f.job_id,p.machineId,"cancelled")).catch(()=>{});break}const h=f.session_id,w=r.get(h);u.add(f.job_id);const P=new AbortController;let x;x=(async()=>{w&&(e.warn(`claimed job=${f.job_id} for already active session=${h}; waiting for the previous run to release its ACP runtime`),await be(w,f,p,P)),!P.signal.aborted&&await(t.runJob??ge)(f,p,e,P)})().catch(c=>{e.warn(`job ${f.job_id} failed outside handler: ${c instanceof Error?c.message:String(c)}`)}).finally(()=>{u.delete(f.job_id),r.get(h)===x&&r.delete(h),b.delete(f.job_id)}),r.set(h,x),b.set(f.job_id,{controller:P,promise:x})}}catch(p){if(!n){s+=1;const v=We(s);l=Date.now()+v,e.warn(`job worker failed, retry in ${v}ms: ${p instanceof Error?p.message:String(p)}`)}}},C=()=>{if(n||i)return;const m=A();i=m,m.finally(()=>{i===m&&(i=null)})};C();const d=setInterval(()=>{C()},t.claimIntervalMs??me);return{timer:d,stop:()=>o||(o=(async()=>{if(n)return;n=!0,clearInterval(d);for(const p of b.values())p.controller.abort();await i?.catch(()=>{});for(const p of b.values())p.controller.abort();const m=[...b.values()].map(p=>p.promise);m.length!==0&&await Promise.race([Promise.allSettled(m).then(()=>{}),F(we)])})(),o)}}async function be(e,t,n,i){let o=!1;const l=setInterval(()=>{o||i.signal.aborted||(o=!0,Promise.allSettled([S("POST",`/api/client/jobs/${encodeURIComponent(t.job_id)}/heartbeat`,{machineId:n.machineId}),G(t.job_id,i)]).finally(()=>{o=!1}))},B);try{await Promise.race([e,Se(i.signal)])}finally{clearInterval(l)}}function Se(e){return e.aborted?Promise.resolve():new Promise(t=>e.addEventListener("abort",()=>t(),{once:!0}))}function ve(){const e=process.env.ACA_MAX_CONCURRENT_JOBS||process.env.ACA_MAX_CONCURRENT_PROJECT_JOBS||"",t=Number.parseInt(e,10);return!e.trim()||!Number.isFinite(t)||t<0?he:t===0?0:Math.max(1,Math.min(t,8))}function D(e,t){return t===0||e<t}async function ge(e,t,n,i){const o=e.job_id,s=e.session_id,l=e.workspace_id,u=e.project_id??null,r=e.input??{},b=String(r.control?.type||"").toLowerCase();if(!b&&!r.message?.trim()&&(r.attachments??[]).length===0&&!r.sessionReferences?.length){await j(o,t.machineId,"job input missing message");return}const A=t.projects.find(c=>c.projectId===u),C=Ie(A?.rootPath||e.project_root_path||"");if(!C){await j(o,t.machineId,"job project root is unknown");return}ye(e)&&N.mkdirSync(C,{recursive:!0}),n.info(`claimed job=${o} session=${s} project=${A?.name??u??"unknown"}`);let d=!1,m=!1;const p=setInterval(()=>{d||(d=!0,S("POST",`/api/client/jobs/${encodeURIComponent(o)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}).finally(()=>{d=!1})),m||(m=!0,G(o,i).catch(()=>{}).finally(()=>{m=!1}))},B);let v=0;const f=ce(c=>n.info(c)),h=se(o,t.machineId,{warn:c=>n.warn(c),trace:f}),w=Re(o,t.machineId),P=new Set;let x=null;try{await w.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const c=oe({projectId:u,sessionId:s,projectRoot:C,worktree:e});x=c,await ze(o,t.machineId,c);const g=Te(r.acpRun);if(Ae(g.agentType)&&await ue(e.codex_provider_revision),b==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const a=await Q({agentType:k(g.agentType,"codex"),cliType:k(g.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,timeoutMs:r.timeoutMs,allowRollover:r.control?.allowRollover===!0,signal:i.signal,onStatus:y=>{w.report({phase:y.phase,label:y.label,detail:y.detail,updateType:y.updateType,raw:y})}});await w.flush(),await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:a.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:r.control?.allowRollover===!0},acpSessionId:a.acpSessionId,durationMs:a.durationMs,usage:a.contextUsage,maintenance:a.maintenance,worktree:$(c)}}),n.info(`completed context compact job=${o}`);return}if(b==="fork"){const a=String(r.control?.sourceAcpSessionId||"").trim();if(!a)throw new Error("Source session does not have a Codex ACP context to fork");await w.sendNow({phase:"forking",label:"正在分叉 Codex 会话",detail:r.control?.sourceSessionId?`源会话 ${r.control.sourceSessionId}`:void 0}).catch(()=>{});const y=await Y({agentType:k(g.agentType,"codex"),cliType:k(g.cliType,"builtin"),sourceProviderSessionId:a,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,timeoutMs:r.timeoutMs,signal:i.signal,onStatus:R=>{w.report({phase:R.phase,label:R.label,detail:R.detail,updateType:R.updateType,raw:R})}});await w.flush(),await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:y.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:r.control?.sourceSessionId||null},acpSessionId:y.acpSessionId,worktree:$(c)}}),n.info(`completed Codex session fork job=${o}`);return}const _=await Ee({cwd:c.cwd,sessionId:s,jobId:o,attachments:r.attachments??[]});_.length>0&&(n.info(`materialized ${_.length} attachment(s) for job=${o}`),await w.sendNow({phase:"initializing",label:"附件已写入本机",detail:_.map(a=>a.relativePath).join(", "),raw:{attachments:q(_)}}).catch(()=>{}));const T=$e(r,_),O=je(T,_),I=await ee({agentType:k(g.agentType,"codex"),cliType:k(g.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:_e(e.session_raw),prompt:T,promptBlocks:O,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,canExecuteTools:r.canExecuteTools,timeoutMs:r.timeoutMs,contextWindow:Ce(g.contextWindow),signal:i.signal,onStatus:a=>{w.report({phase:a.phase,label:a.label,detail:a.detail,updateType:a.updateType,raw:a})},onUpdate:a=>{v+=1,(a.type==="tool_call"||a.type==="tool_call_update"&&["completed","failed","cancelled"].includes(a.status||""))&&f("worker_event",{jobId:o,uploadRunId:h.uploadRunId,toolCallId:a.toolCallId,status:a.status,sequence:v});for(const R of ae(a.raw))P.add(R);const y={turnId:e.user_turn_id??null,eventType:a.type,label:a.label,text:a.text,status:a.status,toolCallId:a.toolCallId,raw:ke(a.raw,v)};le(y)&&h.push(y)},onPermissionRequest:a=>{const y=re(r,a);return y?Promise.resolve(y):Ue(o,t.machineId,a,i.signal)},onUserInputRequest:a=>Je(o,t.machineId,a,i.signal),onProviderSessionMigration:async a=>{const y=await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/acp-session`,{machineId:t.machineId,previousAcpSessionId:a.previousProviderSessionId,acpSessionId:a.providerSessionId,reason:a.reason,migratedAtMs:a.migratedAtMs});e.acp_session_id=a.providerSessionId,n.info(`migrated ACP session for job=${o} ${a.previousProviderSessionId} -> ${a.providerSessionId}`),await h.flush().catch(()=>{}),await w.sendNow({phase:"resuming",label:"Codex 会话已迁移",detail:a.providerSessionId,updateType:"aca.performance.provider_session_migrated",raw:{migration:a,server:{migrated:y.migrated===!0}}}).catch(()=>{})}}),U=await h.finish();await w.flush();const H=J({cwd:c.cwd,evidencePaths:P}),E=Pe(I.promptResponse);if(!(xe(E)||!!I.content)){await j(o,t.machineId,`${I.agentType} stopped: ${E??"unknown"}`,{acpSessionId:I.acpSessionId,stopReason:E,eventUpload:U,exitCode:I.exitCode,stderr:De(I.stderr,4e3),worktree:$(c)});return}await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:I.acpSessionId,assistantText:I.content,result:{ok:!0,eventUpload:U,acpSessionId:I.acpSessionId,stopReason:E,durationMs:I.durationMs,acpRun:r.acpRun??null,effectiveConfig:I.effectiveConfig??null,performance:I.performance??null,usage:I.contextUsage??null,providerDiagnostics:I.providerDiagnostics??null,updateCount:I.updateCount,statusTimeline:I.statusTimeline,worktree:$(c),attachments:q(_),files:H}}),n.info(`completed job=${o}`)}catch(c){if(Le(i.signal)){h.discard(),w.discard(),n.warn(`stopped local job=${o} because ACA Server already finalized it`);return}const g=await h.finish();await w.flush();const _=i.signal.aborted?"cancelled":c instanceof Error?c.message:String(c),T=pe(c),O=x?J({cwd:x.cwd,evidencePaths:P}):[];await j(o,t.machineId,de(_),{rawError:_,eventUpload:g,files:O,providerDiagnostics:z(c,"providerDiagnostics"),statusTimeline:z(c,"statusTimeline"),performance:z(c,"performance"),...T?{acpSessionInvalidated:!0,invalidatedAcpSessionId:T.providerSessionId,failure:{category:T.category,actions:T.actions,details:T.details}}:{}},T?{invalidateAcpSession:!0,invalidatedAcpSessionId:T.providerSessionId}:void 0)}finally{h.discard(),clearInterval(p),await w.flush()}}function _e(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e.importedProviderSession;return!!(t&&typeof t=="object"&&!Array.isArray(t)&&t.strictResume===!0)}function Te(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function Ae(e){const t=String(e||"codex").toLowerCase().replace(/[^a-z0-9]+/g,"");return t==="codex"||t==="codexacp"||t==="codexstandard"}function Ce(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function k(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function Pe(e){if(!e||typeof e!="object"||Array.isArray(e))return;const t=e;if(typeof t.stopReason=="string")return t.stopReason;if(typeof t.reason=="string")return t.reason;const n=t.part;if(n&&typeof n=="object"&&!Array.isArray(n)&&typeof n.reason=="string")return n.reason}function xe(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function Re(e,t){let n=null,i=0,o="",s=null,l=Promise.resolve(),u=!1;const r=d=>JSON.stringify({phase:d.phase??"",label:d.label??"",detail:d.detail??"",updateType:d.updateType??""}),b=async d=>{await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:d.phase,label:d.label,detail:d.detail,updateType:d.updateType,raw:d.raw}),i=Date.now(),o=r(d)},A=()=>{if(s&&(clearTimeout(s),s=null),u)return n=null,l;if(!n)return l;const d=n;return n=null,l=l.then(()=>b(d)).catch(()=>{}),l},C=d=>{s||(s=setTimeout(()=>{A()},d),s.unref())};return{report(d){if(u)return;const m=Date.now(),v=r(d)!==o,f=m-i>=fe;if(!v&&!f)return;n=d;const h=m-i;if(h>=W||i===0){A();return}C(W-h)},sendNow(d){return u?l:(n=d,A())},flush:A,discard(){u=!0,n=null,s&&clearTimeout(s),s=null}}}function ke(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}function $e(e,t=[]){const n=Z(e?.message?.trim()??"",e?.sessionReferences);if(t.length===0)return n;const o=["用户随消息上传了以下附件,aca 已将附件写入本机工作区。需要读取附件时请直接使用下面的 localPath;图片附件也已作为 ACP image block 随本轮请求传入。",...t.map((s,l)=>`${l+1}. ${s.name} (${s.kind}, ${s.mimeType}, ${Fe(s.size)})
1
+ import ee from"node:crypto";import{startJobArtifactMonitor as te,mergeArtifactFiles as W}from"./artifacts/job-artifact-monitor.js";import{appendSessionReferenceContext as ne}from"./session-reference-prompt.js";import F from"node:fs";import N from"node:path";import{compactAgentContext as oe,forkAgentSession as ie,sendAgentPrompt as ae}from"../acp/agent.js";import{acaServerDownload as re,acaServerRequest as S}from"./aca-server-client.js";import{loadAcaConfig as se}from"./aca-config.js";import{prepareLocalWorktree as ce}from"./worktree.js";import{expandWorkerRuntimePath as le}from"./worker-runtime-paths.js";import{collectAffectedFiles as D,extractAffectedFilePathsFromAcpRaw as de}from"./affected-files.js";import{resolveJobPermissionPolicy as ue}from"./job-permission-policy.js";import{createAcpEventBatcher as pe}from"./acp-event-batcher.js";import{createToolLifecycleTrace as me}from"../acp/tool-lifecycle-trace.js";import{shouldBufferAcpEvent as fe}from"./acp-event-upload-filter.js";import{userFacingAcpError as he}from"../acp/error-display.js";import{ensureCodexProviderRuntimeRevision as we}from"./codex-provider-runtime-config.js";import{acpSessionInvalidationFromError as be}from"../acp/session-errors.js";const ye=1500,V=5e3,q=1e3,G=900,Ie=1e4,Se=0,ve=12e3;function ge(e){return le(e)}function _e(e){const t=e.project_metadata;return t&&typeof t=="object"&&!Array.isArray(t)&&t.kind==="chat"?!0:typeof e.project_root_path=="string"&&(e.project_root_path.startsWith("~/.aica/chats/")||e.project_root_path.startsWith("~/.aca/chats/")||/(^|\/)\.aica\/chats\//.test(e.project_root_path)||/(^|\/)\.aca\/chats\//.test(e.project_root_path))}function ht(e,t={}){let n=!1,i=null,o=null,s=0,l=0;const u=new Set,r=new Map,I=new Map,_=async()=>{if(n||Date.now()<l)return;const f=Ce();if(K(u.size,f))try{const p=t.loadConfig?.()??se();if(!p.token)return;for(;!n&&K(u.size,f);){const v=t.claimNextJob?await t.claimNextJob(p,[...r.keys()]):await S("POST","/api/client/jobs/claim",{machineId:p.machineId,workspaceId:p.defaultWorkspaceId,activeSessionIds:[...r.keys()]});if(s=0,l=0,!v.item)break;const h=v.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(h,p):M(h.job_id,p.machineId,"cancelled")).catch(()=>{});break}const w=h.session_id,b=r.get(w);u.add(h.job_id);const P=new AbortController;let x;x=(async()=>{b&&(e.warn(`claimed job=${h.job_id} for already active session=${w}; waiting for the previous run to release its ACP runtime`),await Te(b,h,p,P)),!P.signal.aborted&&await(t.runJob??Pe)(h,p,e,P)})().catch(R=>{e.warn(`job ${h.job_id} failed outside handler: ${R instanceof Error?R.message:String(R)}`)}).finally(()=>{u.delete(h.job_id),r.get(w)===x&&r.delete(w),I.delete(h.job_id)}),r.set(w,x),I.set(h.job_id,{controller:P,promise:x})}}catch(p){if(!n){s+=1;const v=Ke(s);l=Date.now()+v,e.warn(`job worker failed, retry in ${v}ms: ${p instanceof Error?p.message:String(p)}`)}}},C=()=>{if(n||i)return;const f=_();i=f,f.finally(()=>{i===f&&(i=null)})};C();const d=setInterval(()=>{C()},t.claimIntervalMs??ye);return{timer:d,stop:()=>o||(o=(async()=>{if(n)return;n=!0,clearInterval(d);for(const p of I.values())p.controller.abort();await i?.catch(()=>{});for(const p of I.values())p.controller.abort();const f=[...I.values()].map(p=>p.promise);f.length!==0&&await Promise.race([Promise.allSettled(f).then(()=>{}),z(ve)])})(),o)}}async function Te(e,t,n,i){let o=!1;const l=setInterval(()=>{o||i.signal.aborted||(o=!0,Promise.allSettled([S("POST",`/api/client/jobs/${encodeURIComponent(t.job_id)}/heartbeat`,{machineId:n.machineId}),Z(t.job_id,i)]).finally(()=>{o=!1}))},V);try{await Promise.race([e,Ae(i.signal)])}finally{clearInterval(l)}}function Ae(e){return e.aborted?Promise.resolve():new Promise(t=>e.addEventListener("abort",()=>t(),{once:!0}))}function Ce(){const e=process.env.ACA_MAX_CONCURRENT_JOBS||process.env.ACA_MAX_CONCURRENT_PROJECT_JOBS||"",t=Number.parseInt(e,10);return!e.trim()||!Number.isFinite(t)||t<0?Se:t===0?0:Math.max(1,Math.min(t,8))}function K(e,t){return t===0||e<t}async function Pe(e,t,n,i){const o=e.job_id,s=e.session_id,l=e.workspace_id,u=e.project_id??null,r=e.input??{},I=String(r.control?.type||"").toLowerCase();if(!I&&!r.message?.trim()&&(r.attachments??[]).length===0&&!r.sessionReferences?.length){await M(o,t.machineId,"job input missing message");return}const _=t.projects.find(c=>c.projectId===u),C=ge(_?.rootPath||e.project_root_path||"");if(!C){await M(o,t.machineId,"job project root is unknown");return}_e(e)&&F.mkdirSync(C,{recursive:!0}),n.info(`claimed job=${o} session=${s} project=${_?.name??u??"unknown"}`);let d=!1,f=!1;const p=setInterval(()=>{d||(d=!0,S("POST",`/api/client/jobs/${encodeURIComponent(o)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}).finally(()=>{d=!1})),f||(f=!0,Z(o,i).catch(()=>{}).finally(()=>{f=!1}))},V);let v=0;const h=me(c=>n.info(c)),w=pe(o,t.machineId,{warn:c=>n.warn(c),trace:h}),b=Me(o,t.machineId),P=new Set;let x=null,R;const J=async()=>R?.finish().catch(c=>(n.warn(`artifact collection failed job=${o}: ${String(c)}`),[]))??[];try{await b.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const c=ce({projectId:u,sessionId:s,projectRoot:C,worktree:e});x=c,await We(o,t.machineId,c);const g=Re(r.acpRun);if(ke(g.agentType)&&await we(e.codex_provider_revision),I==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const a=await oe({agentType:$(g.agentType,"codex"),cliType:$(g.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,timeoutMs:r.timeoutMs,allowRollover:r.control?.allowRollover===!0,signal:i.signal,onStatus:y=>{b.report({phase:y.phase,label:y.label,detail:y.detail,updateType:y.updateType,raw:y})}});await b.flush(),await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:a.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:r.control?.allowRollover===!0},acpSessionId:a.acpSessionId,durationMs:a.durationMs,usage:a.contextUsage,maintenance:a.maintenance,worktree:E(c)}}),n.info(`completed context compact job=${o}`);return}if(I==="fork"){const a=String(r.control?.sourceAcpSessionId||"").trim();if(!a)throw new Error("Source session does not have a Codex ACP context to fork");await b.sendNow({phase:"forking",label:"正在分叉 Codex 会话",detail:r.control?.sourceSessionId?`源会话 ${r.control.sourceSessionId}`:void 0}).catch(()=>{});const y=await ie({agentType:$(g.agentType,"codex"),cliType:$(g.cliType,"builtin"),sourceProviderSessionId:a,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,timeoutMs:r.timeoutMs,signal:i.signal,onStatus:k=>{b.report({phase:k.phase,label:k.label,detail:k.detail,updateType:k.updateType,raw:k})}});await b.flush(),await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:y.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:r.control?.sourceSessionId||null},acpSessionId:y.acpSessionId,worktree:E(c)}}),n.info(`completed Codex session fork job=${o}`);return}const T=await ze({cwd:c.cwd,sessionId:s,jobId:o,attachments:r.attachments??[]});T.length>0&&(n.info(`materialized ${T.length} attachment(s) for job=${o}`),await b.sendNow({phase:"initializing",label:"附件已写入本机",detail:T.map(a=>a.relativePath).join(", "),raw:{attachments:X(T)}}).catch(()=>{}));const j=Ne(r,T),A=Fe(j,T);R=await te(c.cwd,a=>n.warn(a)).catch(a=>{n.warn(`artifact monitor unavailable job=${o}: ${String(a)}`)});const m=await ae({agentType:$(g.agentType,"codex"),cliType:$(g.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:xe(e.session_raw),prompt:j,promptBlocks:A,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,canExecuteTools:r.canExecuteTools,timeoutMs:r.timeoutMs,contextWindow:$e(g.contextWindow),signal:i.signal,onStatus:a=>{b.report({phase:a.phase,label:a.label,detail:a.detail,updateType:a.updateType,raw:a})},onUpdate:a=>{v+=1,(a.type==="tool_call"||a.type==="tool_call_update"&&["completed","failed","cancelled"].includes(a.status||""))&&h("worker_event",{jobId:o,uploadRunId:w.uploadRunId,toolCallId:a.toolCallId,status:a.status,sequence:v});for(const k of de(a.raw))P.add(k);const y={turnId:e.user_turn_id??null,eventType:a.type,label:a.label,text:a.text,status:a.status,toolCallId:a.toolCallId,raw:Oe(a.raw,v)};fe(y)&&w.push(y)},onPermissionRequest:a=>{const y=ue(r,a);return y?Promise.resolve(y):De(o,t.machineId,a,i.signal)},onUserInputRequest:a=>Ve(o,t.machineId,a,i.signal),onProviderSessionMigration:async a=>{const y=await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/acp-session`,{machineId:t.machineId,previousAcpSessionId:a.previousProviderSessionId,acpSessionId:a.providerSessionId,reason:a.reason,migratedAtMs:a.migratedAtMs});e.acp_session_id=a.providerSessionId,n.info(`migrated ACP session for job=${o} ${a.previousProviderSessionId} -> ${a.providerSessionId}`),await w.flush().catch(()=>{}),await b.sendNow({phase:"resuming",label:"Codex 会话已迁移",detail:a.providerSessionId,updateType:"aca.performance.provider_session_migrated",raw:{migration:a,server:{migrated:y.migrated===!0}}}).catch(()=>{})}}),Y=await J(),B=await w.finish();await b.flush();const L=W(D({cwd:c.cwd,evidencePaths:P}),Y),O=je(m.promptResponse);if(!(Ee(O)||!!m.content)){await M(o,t.machineId,`${m.agentType} stopped: ${O??"unknown"}`,{acpSessionId:m.acpSessionId,stopReason:O,eventUpload:B,files:L,exitCode:m.exitCode,stderr:He(m.stderr,4e3),worktree:E(c)});return}await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:m.acpSessionId,assistantText:m.content,result:{ok:!0,eventUpload:B,acpSessionId:m.acpSessionId,stopReason:O,durationMs:m.durationMs,acpRun:r.acpRun??null,effectiveConfig:m.effectiveConfig??null,performance:m.performance??null,usage:m.contextUsage??null,providerDiagnostics:m.providerDiagnostics??null,updateCount:m.updateCount,statusTimeline:m.statusTimeline,worktree:E(c),attachments:X(T),files:L}}),n.info(`completed job=${o}`)}catch(c){if(Ge(i.signal)){w.discard(),b.discard(),n.warn(`stopped local job=${o} because ACA Server already finalized it`);return}const g=await J(),T=await w.finish();await b.flush();const j=i.signal.aborted?"cancelled":c instanceof Error?c.message:String(c),A=be(c),m=W(x?D({cwd:x.cwd,evidencePaths:P}):[],g);await M(o,t.machineId,he(j),{rawError:j,eventUpload:T,files:m,providerDiagnostics:U(c,"providerDiagnostics"),statusTimeline:U(c,"statusTimeline"),performance:U(c,"performance"),...A?{acpSessionInvalidated:!0,invalidatedAcpSessionId:A.providerSessionId,failure:{category:A.category,actions:A.actions,details:A.details}}:{}},A?{invalidateAcpSession:!0,invalidatedAcpSessionId:A.providerSessionId}:void 0)}finally{R?.dispose(),w.discard(),clearInterval(p),await b.flush()}}function xe(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e.importedProviderSession;return!!(t&&typeof t=="object"&&!Array.isArray(t)&&t.strictResume===!0)}function Re(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function ke(e){const t=String(e||"codex").toLowerCase().replace(/[^a-z0-9]+/g,"");return t==="codex"||t==="codexacp"||t==="codexstandard"}function $e(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function $(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function je(e){if(!e||typeof e!="object"||Array.isArray(e))return;const t=e;if(typeof t.stopReason=="string")return t.stopReason;if(typeof t.reason=="string")return t.reason;const n=t.part;if(n&&typeof n=="object"&&!Array.isArray(n)&&typeof n.reason=="string")return n.reason}function Ee(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function Me(e,t){let n=null,i=0,o="",s=null,l=Promise.resolve(),u=!1;const r=d=>JSON.stringify({phase:d.phase??"",label:d.label??"",detail:d.detail??"",updateType:d.updateType??""}),I=async d=>{await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:d.phase,label:d.label,detail:d.detail,updateType:d.updateType,raw:d.raw}),i=Date.now(),o=r(d)},_=()=>{if(s&&(clearTimeout(s),s=null),u)return n=null,l;if(!n)return l;const d=n;return n=null,l=l.then(()=>I(d)).catch(()=>{}),l},C=d=>{s||(s=setTimeout(()=>{_()},d),s.unref())};return{report(d){if(u)return;const f=Date.now(),v=r(d)!==o,h=f-i>=Ie;if(!v&&!h)return;n=d;const w=f-i;if(w>=G||i===0){_();return}C(G-w)},sendNow(d){return u?l:(n=d,_())},flush:_,discard(){u=!0,n=null,s&&clearTimeout(s),s=null}}}function Oe(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}function Ne(e,t=[]){const n=ne(e?.message?.trim()??"",e?.sessionReferences);if(t.length===0)return n;const o=["用户随消息上传了以下附件,aca 已将附件写入本机工作区。需要读取附件时请直接使用下面的 localPath;图片附件也已作为 ACP image block 随本轮请求传入。",...t.map((s,l)=>`${l+1}. ${s.name} (${s.kind}, ${s.mimeType}, ${Le(s.size)})
2
2
  localPath: ${s.path}
3
3
  workspacePath: ${s.relativePath}`)].join(`
4
4
  `);return n?`${n}
5
5
 
6
- ${o}`:o}function je(e,t){const n=[{type:"text",text:e}];for(const i of t)i.kind!=="image"||!i.mimeType.startsWith("image/")||n.push({type:"image",data:i.base64,mimeType:i.mimeType,uri:i.path});return n}async function Ee(e){if(e.attachments.length===0)return[];const t=M.join(e.cwd,".aca","attachments",V(e.sessionId),V(e.jobId));N.mkdirSync(t,{recursive:!0});const n=[];for(const[i,o]of e.attachments.entries()){const s=o.attachmentId?{mimeType:o.mimeType||"application/octet-stream",bytes:await te(`/api/client/attachments/${encodeURIComponent(o.attachmentId)}`)}:Me(o.dataUrl??""),l=o.mimeType||s.mimeType||"application/octet-stream",u=o.kind==="image"||l.startsWith("image/")?"image":"file",r=Oe(o.name||`attachment-${i+1}`,l),b=M.join(t,`${String(i+1).padStart(2,"0")}-${r}`);N.writeFileSync(b,s.bytes),n.push({name:o.name||r,mimeType:l,size:s.bytes.byteLength,kind:u,path:b,relativePath:M.relative(e.cwd,b),base64:s.bytes.toString("base64"),sha256:X.createHash("sha256").update(s.bytes).digest("hex")})}return n}function Me(e){const t=e.indexOf(",");if(!e.startsWith("data:")||t<0)throw new Error("attachment dataUrl is not a data URL");const n=e.slice(5,t),i=e.slice(t+1),o=n.split(";").filter(Boolean),s=o[0]?.includes("/")?o[0]:"application/octet-stream";if(!o.includes("base64"))throw new Error("attachment dataUrl is not base64 encoded");return{mimeType:s,bytes:Buffer.from(i,"base64")}}function Oe(e,t){const n=M.basename(e).replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").trim(),i=`attachment${Ne(t)}`;return(!n||n==="."||n===".."?i:n).slice(0,180)}function Ne(e){switch(e.toLowerCase()){case"image/png":return".png";case"image/jpeg":return".jpg";case"image/webp":return".webp";case"image/gif":return".gif";case"text/plain":return".txt";case"application/json":return".json";case"text/markdown":case"text/x-markdown":return".md";case"application/pdf":return".pdf";case"application/msword":return".doc";case"application/vnd.openxmlformats-officedocument.wordprocessingml.document":return".docx";case"application/vnd.ms-excel":return".xls";case"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":return".xlsx";case"application/vnd.ms-powerpoint":return".ppt";case"application/vnd.openxmlformats-officedocument.presentationml.presentation":return".pptx";case"application/zip":return".zip";case"application/x-7z-compressed":return".7z";case"application/x-rar-compressed":case"application/vnd.rar":return".rar";case"application/gzip":return".gz";case"application/x-tar":return".tar";default:return""}}function V(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"item"}function q(e){return e.map(t=>({name:t.name,kind:t.kind,mimeType:t.mimeType,size:t.size,path:t.path,relativePath:t.relativePath,sha256:t.sha256}))}function Fe(e){if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KB","MB","GB"];let n=e,i=0;for(;n>=1024&&i<t.length-1;)n/=1024,i+=1;return`${n>=10||i===0?n.toFixed(0):n.toFixed(1)} ${t[i]}`}async function ze(e,t,n){!n.usedWorktree||!n.worktreeId||await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/worktree`,{machineId:t,baseCommit:n.baseCommit??null,worktreePath:n.cwd,status:n.status??"active",raw:n.raw??$(n)})}function $(e){return{usedWorktree:e.usedWorktree,worktreeId:e.worktreeId??null,cwd:e.cwd,status:e.status??null,baseCommit:e.baseCommit??null,branchName:e.branchName??null,baseBranch:e.baseBranch??null}}async function Ue(e,t,n,i){const s=(await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:n})).item.permission_id;for(;!i.aborted;){await F(L);const l=await S("GET",`/api/client/permissions/${encodeURIComponent(s)}`);if(l.item.status==="resolved"&&l.item.response){const u=l.item.response.outcome;return u.outcome==="selected"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function Je(e,t,n,i){const s=(await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:{kind:"elicitation",...n}})).item.permission_id;for(;!i.aborted;){await F(L);const l=await S("GET",`/api/client/permissions/${encodeURIComponent(s)}`);if(l.item.status==="resolved"&&l.item.response){const u=l.item.response.outcome;return u.outcome==="submitted"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function G(e,t){if(t.signal.aborted)return;const n=await S("GET",`/api/client/jobs/${encodeURIComponent(e)}`);if(Number(n.item.cancel_requested??0)===1){t.abort();return}const i=Be(n.item);i&&t.abort(new K(e,i))}function Be(e){const t=String(e.status||"").toLowerCase();return t&&!["claimed","running"].includes(t)?t:null}class K extends Error{constructor(t,n){super(`ACA Server already finalized job=${t} status=${n}`),this.name="ServerFinalizedJobError"}}function Le(e){return e.aborted&&e.reason instanceof K}async function j(e,t,n,i,o){await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/fail`,{machineId:t,error:n,result:i,...o?.invalidateAcpSession?{invalidateAcpSession:!0,invalidatedAcpSessionId:o.invalidatedAcpSessionId}:{}})}function F(e){return new Promise(t=>setTimeout(t,e))}function We(e){return Math.min(1e4,500*2**Math.min(5,Math.max(0,e-1)))+Math.floor(Math.random()*250)}function De(e,t){return e.length<=t?e:`${e.slice(0,t)}...`}function z(e,t){return!e||typeof e!="object"||Array.isArray(e)?null:e[t]??null}export{Be as serverFinalizedJobStatus,lt as startJobWorkerLoop};
6
+ ${o}`:o}function Fe(e,t){const n=[{type:"text",text:e}];for(const i of t)i.kind!=="image"||!i.mimeType.startsWith("image/")||n.push({type:"image",data:i.base64,mimeType:i.mimeType,uri:i.path});return n}async function ze(e){if(e.attachments.length===0)return[];const t=N.join(e.cwd,".aca","attachments",H(e.sessionId),H(e.jobId));F.mkdirSync(t,{recursive:!0});const n=[];for(const[i,o]of e.attachments.entries()){const s=o.attachmentId?{mimeType:o.mimeType||"application/octet-stream",bytes:await re(`/api/client/attachments/${encodeURIComponent(o.attachmentId)}`)}:Ue(o.dataUrl??""),l=o.mimeType||s.mimeType||"application/octet-stream",u=o.kind==="image"||l.startsWith("image/")?"image":"file",r=Je(o.name||`attachment-${i+1}`,l),I=N.join(t,`${String(i+1).padStart(2,"0")}-${r}`);F.writeFileSync(I,s.bytes),n.push({name:o.name||r,mimeType:l,size:s.bytes.byteLength,kind:u,path:I,relativePath:N.relative(e.cwd,I),base64:s.bytes.toString("base64"),sha256:ee.createHash("sha256").update(s.bytes).digest("hex")})}return n}function Ue(e){const t=e.indexOf(",");if(!e.startsWith("data:")||t<0)throw new Error("attachment dataUrl is not a data URL");const n=e.slice(5,t),i=e.slice(t+1),o=n.split(";").filter(Boolean),s=o[0]?.includes("/")?o[0]:"application/octet-stream";if(!o.includes("base64"))throw new Error("attachment dataUrl is not base64 encoded");return{mimeType:s,bytes:Buffer.from(i,"base64")}}function Je(e,t){const n=N.basename(e).replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").trim(),i=`attachment${Be(t)}`;return(!n||n==="."||n===".."?i:n).slice(0,180)}function Be(e){switch(e.toLowerCase()){case"image/png":return".png";case"image/jpeg":return".jpg";case"image/webp":return".webp";case"image/gif":return".gif";case"text/plain":return".txt";case"application/json":return".json";case"text/markdown":case"text/x-markdown":return".md";case"application/pdf":return".pdf";case"application/msword":return".doc";case"application/vnd.openxmlformats-officedocument.wordprocessingml.document":return".docx";case"application/vnd.ms-excel":return".xls";case"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":return".xlsx";case"application/vnd.ms-powerpoint":return".ppt";case"application/vnd.openxmlformats-officedocument.presentationml.presentation":return".pptx";case"application/zip":return".zip";case"application/x-7z-compressed":return".7z";case"application/x-rar-compressed":case"application/vnd.rar":return".rar";case"application/gzip":return".gz";case"application/x-tar":return".tar";default:return""}}function H(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"item"}function X(e){return e.map(t=>({name:t.name,kind:t.kind,mimeType:t.mimeType,size:t.size,path:t.path,relativePath:t.relativePath,sha256:t.sha256}))}function Le(e){if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KB","MB","GB"];let n=e,i=0;for(;n>=1024&&i<t.length-1;)n/=1024,i+=1;return`${n>=10||i===0?n.toFixed(0):n.toFixed(1)} ${t[i]}`}async function We(e,t,n){!n.usedWorktree||!n.worktreeId||await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/worktree`,{machineId:t,baseCommit:n.baseCommit??null,worktreePath:n.cwd,status:n.status??"active",raw:n.raw??E(n)})}function E(e){return{usedWorktree:e.usedWorktree,worktreeId:e.worktreeId??null,cwd:e.cwd,status:e.status??null,baseCommit:e.baseCommit??null,branchName:e.branchName??null,baseBranch:e.baseBranch??null}}async function De(e,t,n,i){const s=(await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:n})).item.permission_id;for(;!i.aborted;){await z(q);const l=await S("GET",`/api/client/permissions/${encodeURIComponent(s)}`);if(l.item.status==="resolved"&&l.item.response){const u=l.item.response.outcome;return u.outcome==="selected"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function Ve(e,t,n,i){const s=(await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:{kind:"elicitation",...n}})).item.permission_id;for(;!i.aborted;){await z(q);const l=await S("GET",`/api/client/permissions/${encodeURIComponent(s)}`);if(l.item.status==="resolved"&&l.item.response){const u=l.item.response.outcome;return u.outcome==="submitted"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function Z(e,t){if(t.signal.aborted)return;const n=await S("GET",`/api/client/jobs/${encodeURIComponent(e)}`);if(Number(n.item.cancel_requested??0)===1){t.abort();return}const i=qe(n.item);i&&t.abort(new Q(e,i))}function qe(e){const t=String(e.status||"").toLowerCase();return t&&!["claimed","running"].includes(t)?t:null}class Q extends Error{constructor(t,n){super(`ACA Server already finalized job=${t} status=${n}`),this.name="ServerFinalizedJobError"}}function Ge(e){return e.aborted&&e.reason instanceof Q}async function M(e,t,n,i,o){await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/fail`,{machineId:t,error:n,result:i,...o?.invalidateAcpSession?{invalidateAcpSession:!0,invalidatedAcpSessionId:o.invalidatedAcpSessionId}:{}})}function z(e){return new Promise(t=>setTimeout(t,e))}function Ke(e){return Math.min(1e4,500*2**Math.min(5,Math.max(0,e-1)))+Math.floor(Math.random()*250)}function He(e,t){return e.length<=t?e:`${e.slice(0,t)}...`}function U(e,t){return!e||typeof e!="object"||Array.isArray(e)?null:e[t]??null}export{qe as serverFinalizedJobStatus,ht as startJobWorkerLoop};
@@ -1,5 +1,5 @@
1
- import{activeTerminalService as Q}from"./terminal/service.js";import{execFileSync as y}from"node:child_process";import{randomUUID as $}from"node:crypto";import l from"node:fs";import T from"node:os";import u from"node:path";import{acaServerDownloadToFile as Z,acaServerRequest as P}from"./aca-server-client.js";import{loadAcaConfig as q,upsertLocalProject as tt}from"./aca-config.js";import{startLongPollWorker as et}from"./long-poll-worker.js";import{expandWorkerRuntimePath as rt}from"./worker-runtime-paths.js";import{syncCodexProviderRuntimeConfig as ot}from"./codex-provider-runtime-config.js";import{defaultAcpRuntimePool as nt}from"../acp/client/acp-runtime-pool.js";const F=25e3,it=F+1e4,E=300,st=3e4;let b=null;function te(t){return et({run:async()=>{const e=q();if(!e.token)throw new Error("Worker 尚未配置认证令牌");const r=await P("GET",`/api/client/machine-operations/claim?machineId=${encodeURIComponent(e.machineId)}&waitMs=${F}`,void 0,{timeoutMs:it});if(r.item)try{const o=await at(r.item);await P("POST",`/api/client/machine-operations/${encodeURIComponent(r.item.requestId)}/complete`,{result:o})}catch(o){const n=o instanceof Error?o.message:String(o);t.warn(`machine filesystem request failed request=${r.item.requestId}: ${n}`),await P("POST",`/api/client/machine-operations/${encodeURIComponent(r.item.requestId)}/fail`,{message:n}).catch(()=>{})}},onError:(e,r)=>t.warn(`machine filesystem worker failed, retry in ${r}ms: ${e instanceof Error?e.message:String(e)}`)})}async function at(t){if(t.operation==="sync_codex_provider_config"){const e=await ot(),r=await nt.retireProviderRuntimes("codex-acp");return{configured:!!e,revision:e?.revision??null,retiredRuntimes:r}}if(t.operation==="filesystem_roots")return Dt();if(t.operation==="list_directory")return Rt({directoryPath:String(t.payload.path||""),includeHidden:t.payload.includeHidden===!0});if(t.operation==="register_project")return It(t.payload);if(t.operation==="list_project_directory")return ut(t.payload);if(t.operation==="upload_project_file")return ft(t.payload);if(t.operation==="mutate_project_files")return dt(t.payload);if(t.operation==="get_project_git_status")return O(t.payload);if(t.operation==="get_project_git_diff")return gt(t.payload);if(t.operation==="get_project_git_log")return St(t.payload);if(t.operation==="get_project_git_commit")return _t(t.payload);if(t.operation==="get_worktree_status")return k(t.payload);if(t.operation==="commit_worktree")return ct(t.payload);if(t.operation==="cleanup_worktree")return lt(t.payload);throw new Error("不支持的 Machine 文件系统操作。 ")}function k(t){const e=C(String(t.worktreePath||""));if(!l.existsSync(e))return{exists:!1,isRepository:!1,dirty:!1,changedFiles:0,ahead:0};const r=m(e);if(!w(r))return{exists:!0,isRepository:!1,dirty:xt(r),changedFiles:null,ahead:0};const o=O({rootPath:r}),n=String(t.baseCommit||"").trim(),s=n&&/^[0-9a-f]{7,64}$/i.test(n)?bt(r,n):0;return{exists:!0,isRepository:!0,dirty:Array.isArray(o.files)&&o.files.length>0,changedFiles:Array.isArray(o.files)?o.files.length:0,branch:o.branch,ahead:s,files:Array.isArray(o.files)?o.files.slice(0,50):[],truncated:o.truncated===!0}}function ct(t){const e=m(String(t.worktreePath||"")),r=String(t.message||"").trim();if(!r)throw new Error("提交说明不能为空。");if(!w(e))throw new Error("当前 Worktree 不是 Git 仓库,无法提交。");y("git",["-C",e,"add","-A"],{stdio:"pipe",timeout:3e4});try{return y("git",["-C",e,"diff","--cached","--quiet"],{stdio:"pipe",timeout:1e4}),{committed:!1,reason:"no_changes",...k(t)}}catch(o){if(Number(o.status)!==1)throw o}return y("git",["-C",e,"commit","-m",r],{stdio:"pipe",timeout:6e4}),{committed:!0,commit:d(e,["rev-parse","HEAD"],8*1024).trim(),...k(t)}}function lt(t){const e=C(String(t.worktreePath||"")),r=m(String(t.projectRoot||""));if(h(e)===h(r))throw new Error("不能清理项目主目录。");if(!l.existsSync(e))return{removed:!1,reason:"not_found"};const o=m(e);if(Q?.hasRunningInDirectory(o,String(t.sessionId||"")))throw new Error("请先结束此目录中的会话终端,再清理 Worktree。");const n=String(t.branchName||"").trim(),s=w(o)&&w(r);if(s){if(y("git",["-C",r,"worktree","remove","--force",o],{stdio:"pipe",timeout:6e4}),t.deleteBranch===!0&&n)try{y("git",["-C",r,"branch","-D",n],{stdio:"pipe",timeout:3e4})}catch(i){const a=String(i.stderr||i);if(!/not found|not exist/i.test(a))throw i}}else l.rmSync(o,{recursive:!0,force:!0,maxRetries:2,retryDelay:200});return{removed:!0,branchDeleted:!!(s&&t.deleteBranch===!0&&n)}}function ut(t){const e=m(String(t.rootPath||"")),r=S(String(t.relativePath||"")),o=j(e,r,!0),n=Math.max(0,Number(t.offset||0)),s=Math.min(300,Math.max(20,Number(t.limit||200))),i=l.readdirSync(o,{withFileTypes:!0}).filter(c=>!c.isSymbolicLink()).map(c=>({name:c.name,path:K(e,u.join(o,c.name)),type:c.isDirectory()?"directory":c.isFile()?"file":"other",hidden:c.name.startsWith("."),extension:c.isFile()?u.extname(c.name).slice(1).toLowerCase():""})).filter(c=>c.type!=="other").sort((c,f)=>c.type!==f.type?c.type==="directory"?-1:1:c.name.localeCompare(f.name,void 0,{numeric:!0,sensitivity:"base"})),a=i.slice(n,n+s);return{path:r,rootKind:t.rootKind==="worktree"?"worktree":"project",items:a,total:i.length,nextOffset:n+a.length<i.length?n+a.length:null}}async function ft(t){const e=String(t.uploadId||"").trim();if(!e)throw new Error("项目上传任务缺少 uploadId。");const r=m(String(t.rootPath||"")),o=S(String(t.relativeDirectory||"")),n=j(r,o,!0),s=I(String(t.fileName||"")),i=Number(t.sizeBytes),a=String(t.sha256||"").trim().toLowerCase();if(!Number.isSafeInteger(i)||i<0)throw new Error("项目上传文件大小无效。");if(!/^[0-9a-f]{64}$/.test(a))throw new Error("项目上传文件摘要无效。");const c=u.join(n,`.aca-upload-${$()}.tmp`);try{const f=await Z(`/api/client/project-uploads/${encodeURIComponent(e)}`,c,{maxBytes:i,timeoutMs:3e5});if(f.sizeBytes!==i)throw new Error("项目上传文件大小校验失败。");if(f.sha256.toLowerCase()!==a)throw new Error("项目上传文件完整性校验失败。");const p=ht(r,o,s,c);return{name:u.basename(p),path:K(r,p),directory:o,size:f.sizeBytes,sha256:f.sha256,renamed:u.basename(p)!==s,rootKind:t.rootKind==="worktree"?"worktree":"project"}}finally{l.rmSync(c,{force:!0})}}function dt(t){const e=m(String(t.rootPath||"")),r=t.action&&typeof t.action=="object"?t.action:{};if(r.type==="delete")return mt(e,String(r.path||""),r.recursive===!0,t.rootKind);throw new Error("project_file_operation_unsupported")}function mt(t,e,r,o){const n=S(e);if(!n)throw new Error("project_file_root_protected");pt(n);const s=n.split("/"),i=s.slice(0,-1).join("/"),a=j(t,i,!0),c=u.join(a,s.at(-1)||"");let f;try{f=l.lstatSync(c)}catch(p){throw p.code==="ENOENT"?new Error("project_file_not_found"):p}if(f.isSymbolicLink())throw new Error("project_file_symbolic_link");if(!f.isFile()&&!f.isDirectory())throw new Error("project_file_type_unsupported");if(f.isDirectory()&&!r&&l.readdirSync(c).length>0)throw new Error("project_directory_not_empty");return f.isDirectory()&&r?l.rmSync(c,{recursive:!0,force:!1,maxRetries:2,retryDelay:150}):f.isDirectory()?l.rmdirSync(c):l.unlinkSync(c),{operation:"delete",path:n,entryType:f.isDirectory()?"directory":"file",parentPath:i,affectedDirectories:[i],rootKind:o==="worktree"?"worktree":"project"}}function pt(t){const e=t.split("/")[0].toLocaleLowerCase("en-US");if([".git",".aca",".aica"].includes(e))throw new Error("project_file_protected")}function ht(t,e,r,o){const n=m(t),s=S(e),i=j(n,s,!0),a=I(r);if(l.realpathSync.native(u.dirname(o))!==i)throw new Error("项目上传临时文件不在目标目录中。");const f=l.lstatSync(o);if(!f.isFile()||f.isSymbolicLink())throw new Error("项目上传临时文件无效。");for(let p=1;p<=1e4;p+=1){const J=p===1?a:wt(a,p),_=u.join(i,J);try{return l.linkSync(o,_),l.chmodSync(_,420),l.rmSync(o,{force:!0}),_}catch(v){if(L(v))continue;if(!yt(v))throw v;try{return l.copyFileSync(o,_,l.constants.COPYFILE_EXCL),l.chmodSync(_,420),l.rmSync(o,{force:!0}),_}catch(N){if(L(N))continue;throw N}}}throw new Error("同名文件过多,无法生成可用文件名。")}function I(t){const e=t.trim();if(!e||e==="."||e===".."||/[\\/\x00-\x1F]/.test(e))throw new Error("项目上传文件名无效。");if(process.platform==="win32"&&(/[<>:"|?*]/.test(e)||/[. ]$/.test(e)))throw new Error("项目上传文件名在 Windows 上无效。");return e.slice(0,180)}function wt(t,e){const r=u.extname(t);return`${r?t.slice(0,-r.length):t} (${e})${r}`}function L(t){return!!(t&&typeof t=="object"&&t.code==="EEXIST")}function yt(t){const e=t&&typeof t=="object"?t.code:"";return["EPERM","EACCES","EXDEV","ENOTSUP","EOPNOTSUPP"].includes(String(e||""))}const M=500,x=2*1024*1024,g=2*1024*1024;function O(t){const e=m(String(t.rootPath||""));if(!w(e))return{isRepository:!1,branch:null,files:[],ahead:0,behind:0};const o=d(e,["status","--porcelain=v1","-z","--untracked-files=all","--no-renames"],x).split("\0").filter(Boolean).slice(0,M),n=o.map(a=>Et(a)).filter(a=>!!a),s=A(d(e,["diff","--no-ext-diff","--numstat","-z","--no-renames","HEAD","--","."],x));for(const a of n){const c=s.get(a.path);if(c)a.additions=c.additions,a.deletions=c.deletions;else if(a.status==="untracked"){const f=G(e,a.path);a.additions=f.additions,a.deletions=f.deletions}}const i=At(e);return{isRepository:!0,branch:kt(e),files:n,ahead:i.ahead,behind:i.behind,truncated:o.length>=M}}function gt(t){const e=m(String(t.rootPath||"")),r=S(String(t.relativePath||""));if(!r)throw new Error("Diff 文件路径不能为空。");if(!w(e))return{isRepository:!1,path:r,diff:"",additions:0,deletions:0};const o=W(e,r),n=jt(e,r),s=n?d(e,["diff","--no-ext-diff","--unified=80","HEAD","--",r],g):B(e,["diff","--no-ext-diff","--no-index","--unified=80",process.platform==="win32"?"NUL":"/dev/null",o],g),i=n?A(d(e,["diff","--no-ext-diff","--numstat","-z","--no-renames","HEAD","--",r],32*1024)).get(r):G(e,r);return{isRepository:!0,path:r,diff:U(s),additions:i?.additions??0,deletions:i?.deletions??0,truncated:s.length>g}}function St(t){const e=m(String(t.rootPath||""));if(!w(e))return{isRepository:!1,items:[]};if(!vt(e))return{isRepository:!0,items:[]};const r=Math.min(200,Math.max(20,Number(t.limit||100)));return{isRepository:!0,items:d(e,["log",`--max-count=${r}`,"--date=iso-strict","--pretty=format:%H%x1f%h%x1f%an%x1f%ae%x1f%aI%x1f%D%x1f%s%x1f%b%x1e"],x).split("").map(i=>Pt(i)).filter(i=>!!i),limit:r}}function _t(t){const e=m(String(t.rootPath||"")),r=String(t.commit||"").trim();if(!/^[0-9a-f]{7,64}$/i.test(r))throw new Error("Git 提交标识无效。");if(!w(e))return{isRepository:!1,commit:r,diff:"",additions:0,deletions:0};d(e,["cat-file","-e",`${r}^{commit}`],8*1024);const o=d(e,["show","--no-ext-diff","--no-color","--format=","--stat","--patch","--unified=40",r],g),n=[...A(d(e,["show","--no-ext-diff","--numstat","-z","--no-renames","--format=",r],x)).values()];return{isRepository:!0,commit:r,diff:U(o),additions:n.reduce((s,i)=>s+(i.additions||0),0),deletions:n.reduce((s,i)=>s+(i.deletions||0),0),changedFiles:n.length,truncated:o.length>g}}function Pt(t){const e=t.replace(/^\s+|\s+$/g,"");if(!e)return null;const r=e.split(""),o=String(r[0]||"").trim();return o?{hash:o,shortHash:String(r[1]||o.slice(0,8)).trim(),authorName:String(r[2]||"").trim(),authorEmail:String(r[3]||"").trim(),authoredAt:String(r[4]||"").trim(),refs:String(r[5]||"").trim(),subject:String(r[6]||"").trim(),body:r.slice(7).join("").trim()}:null}function Et(t){if(t.length<4)return null;const e=t[0]||" ",r=t[1]||" ",o=S(t.slice(3));return o?{path:o,status:e==="?"&&r==="?"?"untracked":e==="U"||r==="U"?"conflicted":e==="D"||r==="D"?"deleted":e==="A"||r==="A"?"added":e==="R"||r==="R"?"renamed":"modified",indexStatus:e,worktreeStatus:r,staged:e!==" "&&e!=="?",additions:null,deletions:null}:null}function A(t){const e=new Map;for(const r of t.split("\0")){if(!r)continue;const o=H(r);if(!o||!o.path)continue;const n=S(o.path);n&&e.set(n,{additions:o.additions,deletions:o.deletions})}return e}function G(t,e){const r=W(t,e),n=B(t,["diff","--no-ext-diff","--no-index","--numstat","-z","--",process.platform==="win32"?"NUL":"/dev/null",r],32*1024).split("\0",1)[0]||"",s=H(n);return s?{additions:s.additions,deletions:s.deletions}:{additions:null,deletions:null}}function H(t){const e=t.indexOf(" "),r=e<0?-1:t.indexOf(" ",e+1);if(e<0||r<0)return null;const o=t.slice(0,e),n=t.slice(e+1,r);return{additions:/^\d+$/.test(o)?Number(o):null,deletions:/^\d+$/.test(n)?Number(n):null,path:t.slice(r+1)}}function w(t){try{return d(t,["rev-parse","--is-inside-work-tree"],8*1024).trim()==="true"}catch(e){if(String(e.code||"")==="ENOENT")throw new Error("设备未安装 Git。");return!1}}function bt(t,e){try{const r=d(t,["rev-list","--count",`${e}..HEAD`],8192).trim(),o=Number(r);return Number.isSafeInteger(o)&&o>0?o:0}catch{return 0}}function xt(t){try{return l.readdirSync(t).length>0}catch{return!1}}function jt(t,e){try{return d(t,["ls-files","--error-unmatch","--",e],8*1024),!0}catch{return!1}}function vt(t){try{return d(t,["rev-parse","--verify","HEAD"],8*1024),!0}catch{return!1}}function kt(t){try{return d(t,["symbolic-ref","--quiet","--short","HEAD"],8*1024).trim()||null}catch{try{return`HEAD (${d(t,["rev-parse","--short","HEAD"],8*1024).trim()})`}catch{return null}}}function At(t){try{const e=d(t,["rev-list","--left-right","--count","HEAD...@{upstream}"],8192).trim().split(/\s+/).map(Number);return{ahead:Number.isFinite(e[0])?e[0]:0,behind:Number.isFinite(e[1])?e[1]:0}}catch{return{ahead:0,behind:0}}}function W(t,e){const r=u.resolve(t,...e.split("/")),o=u.relative(t,r);if(o.startsWith("..")||u.isAbsolute(o))throw new Error("Diff 路径超出项目范围。");return r}function d(t,e,r){return y("git",["-C",t,...e],{encoding:"utf8",timeout:2e4,maxBuffer:r,stdio:["ignore","pipe","pipe"]})}function B(t,e,r){try{return d(t,e,r)}catch(o){const n=String(o.stdout||"");if(n)return n;if(Number(o.status)===1)return"";throw o}}function U(t){return t.length<=g?t:`${t.slice(0,g)}
1
+ import{activeTerminalService as Q}from"./terminal/service.js";import{execFileSync as y}from"node:child_process";import{randomUUID as T}from"node:crypto";import l from"node:fs";import F from"node:os";import u from"node:path";import{acaServerDownloadToFile as Z,acaServerRequest as E}from"./aca-server-client.js";import{loadAcaConfig as q,upsertLocalProject as tt}from"./aca-config.js";import{startLongPollWorker as et}from"./long-poll-worker.js";import{expandWorkerRuntimePath as rt}from"./worker-runtime-paths.js";import{syncCodexProviderRuntimeConfig as ot}from"./codex-provider-runtime-config.js";import{defaultAcpRuntimePool as nt}from"../acp/client/acp-runtime-pool.js";const I=25e3,it=I+1e4,b=300,st=3e4;let x=null;function ee(t){return et({run:async()=>{const e=q();if(!e.token)throw new Error("Worker 尚未配置认证令牌");const r=await E("GET",`/api/client/machine-operations/claim?machineId=${encodeURIComponent(e.machineId)}&waitMs=${I}`,void 0,{timeoutMs:it});if(r.item)try{const o=await ct(r.item);await E("POST",`/api/client/machine-operations/${encodeURIComponent(r.item.requestId)}/complete`,{result:o})}catch(o){const n=o instanceof Error?o.message:String(o);t.warn(`machine filesystem request failed request=${r.item.requestId}: ${n}`),await E("POST",`/api/client/machine-operations/${encodeURIComponent(r.item.requestId)}/fail`,{message:n}).catch(()=>{})}},onError:(e,r)=>t.warn(`machine filesystem worker failed, retry in ${r}ms: ${e instanceof Error?e.message:String(e)}`)})}async function ct(t){if(t.operation==="sync_codex_provider_config"){const e=await ot(),r=await nt.retireProviderRuntimes("codex-acp");return{configured:!!e,revision:e?.revision??null,retiredRuntimes:r}}if(t.operation==="filesystem_roots")return Rt();if(t.operation==="list_directory")return Ct({directoryPath:String(t.payload.path||""),includeHidden:t.payload.includeHidden===!0});if(t.operation==="register_project")return Lt(t.payload);if(t.operation==="list_project_directory")return ut(t.payload);if(t.operation==="upload_project_file")return dt(t.payload);if(t.operation==="mutate_project_files")return mt(t.payload);if(t.operation==="get_project_git_status")return G(t.payload);if(t.operation==="get_project_git_diff")return St(t.payload);if(t.operation==="get_project_git_log")return _t(t.payload);if(t.operation==="get_project_git_commit")return Pt(t.payload);if(t.operation==="get_worktree_status")return k(t.payload);if(t.operation==="commit_worktree")return at(t.payload);if(t.operation==="cleanup_worktree")return lt(t.payload);throw new Error("不支持的 Machine 文件系统操作。 ")}function k(t){const e=N(String(t.worktreePath||""));if(!l.existsSync(e))return{exists:!1,isRepository:!1,dirty:!1,changedFiles:0,ahead:0};const r=p(e);if(!w(r))return{exists:!0,isRepository:!1,dirty:jt(r),changedFiles:null,ahead:0};const o=G({rootPath:r}),n=String(t.baseCommit||"").trim(),s=n&&/^[0-9a-f]{7,64}$/i.test(n)?xt(r,n):0;return{exists:!0,isRepository:!0,dirty:Array.isArray(o.files)&&o.files.length>0,changedFiles:Array.isArray(o.files)?o.files.length:0,branch:o.branch,ahead:s,files:Array.isArray(o.files)?o.files.slice(0,50):[],truncated:o.truncated===!0}}function at(t){const e=p(String(t.worktreePath||"")),r=String(t.message||"").trim();if(!r)throw new Error("提交说明不能为空。");if(!w(e))throw new Error("当前 Worktree 不是 Git 仓库,无法提交。");y("git",["-C",e,"add","-A"],{stdio:"pipe",timeout:3e4});try{return y("git",["-C",e,"diff","--cached","--quiet"],{stdio:"pipe",timeout:1e4}),{committed:!1,reason:"no_changes",...k(t)}}catch(o){if(Number(o.status)!==1)throw o}return y("git",["-C",e,"commit","-m",r],{stdio:"pipe",timeout:6e4}),{committed:!0,commit:m(e,["rev-parse","HEAD"],8*1024).trim(),...k(t)}}function lt(t){const e=N(String(t.worktreePath||"")),r=p(String(t.projectRoot||""));if(h(e)===h(r))throw new Error("不能清理项目主目录。");if(!l.existsSync(e))return{removed:!1,reason:"not_found"};const o=p(e);if(Q?.hasRunningInDirectory(o,String(t.sessionId||"")))throw new Error("请先结束此目录中的会话终端,再清理 Worktree。");const n=String(t.branchName||"").trim(),s=w(o)&&w(r);if(s){if(y("git",["-C",r,"worktree","remove","--force",o],{stdio:"pipe",timeout:6e4}),t.deleteBranch===!0&&n)try{y("git",["-C",r,"branch","-D",n],{stdio:"pipe",timeout:3e4})}catch(i){const c=String(i.stderr||i);if(!/not found|not exist/i.test(c))throw i}}else l.rmSync(o,{recursive:!0,force:!0,maxRetries:2,retryDelay:200});return{removed:!0,branchDeleted:!!(s&&t.deleteBranch===!0&&n)}}function ut(t){const e=p(String(t.rootPath||"")),r=S(String(t.relativePath||"")),o=v(e,r,!0),n=Math.max(0,Number(t.offset||0)),s=Math.min(300,Math.max(20,Number(t.limit||200))),i=t.sort==="modified-desc"||t.sort==="modified-asc"?t.sort:"name",c=l.readdirSync(o,{withFileTypes:!0}).filter(a=>!a.isSymbolicLink()).map(a=>({name:a.name,path:X(e,u.join(o,a.name)),type:a.isDirectory()?"directory":a.isFile()?"file":"other",hidden:a.name.startsWith("."),extension:a.isFile()?u.extname(a.name).slice(1).toLowerCase():"",modifiedAt:i==="name"?void 0:ft(u.join(o,a.name))})).filter(a=>a.type!=="other").sort((a,d)=>{if(a.type!==d.type)return a.type==="directory"?-1:1;if(i!=="name"){if(a.modifiedAt==null&&d.modifiedAt!=null)return 1;if(d.modifiedAt==null&&a.modifiedAt!=null)return-1;const P=(a.modifiedAt??0)-(d.modifiedAt??0);if(P)return i==="modified-desc"?-P:P}return a.name.localeCompare(d.name,void 0,{numeric:!0,sensitivity:"base"})}),f=c.slice(n,n+s);return{path:r,rootKind:t.rootKind==="worktree"?"worktree":"project",items:f,total:c.length,nextOffset:n+f.length<c.length?n+f.length:null}}function ft(t){try{const e=l.lstatSync(t);return e.isSymbolicLink()?null:e.mtimeMs}catch{return null}}async function dt(t){const e=String(t.uploadId||"").trim();if(!e)throw new Error("项目上传任务缺少 uploadId。");const r=p(String(t.rootPath||"")),o=S(String(t.relativeDirectory||"")),n=v(r,o,!0),s=L(String(t.fileName||"")),i=Number(t.sizeBytes),c=String(t.sha256||"").trim().toLowerCase();if(!Number.isSafeInteger(i)||i<0)throw new Error("项目上传文件大小无效。");if(!/^[0-9a-f]{64}$/.test(c))throw new Error("项目上传文件摘要无效。");const f=u.join(n,`.aca-upload-${T()}.tmp`);try{const a=await Z(`/api/client/project-uploads/${encodeURIComponent(e)}`,f,{maxBytes:i,timeoutMs:3e5});if(a.sizeBytes!==i)throw new Error("项目上传文件大小校验失败。");if(a.sha256.toLowerCase()!==c)throw new Error("项目上传文件完整性校验失败。");const d=wt(r,o,s,f);return{name:u.basename(d),path:X(r,d),directory:o,size:a.sizeBytes,sha256:a.sha256,renamed:u.basename(d)!==s,rootKind:t.rootKind==="worktree"?"worktree":"project"}}finally{l.rmSync(f,{force:!0})}}function mt(t){const e=p(String(t.rootPath||"")),r=t.action&&typeof t.action=="object"?t.action:{};if(r.type==="delete")return pt(e,String(r.path||""),r.recursive===!0,t.rootKind);throw new Error("project_file_operation_unsupported")}function pt(t,e,r,o){const n=S(e);if(!n)throw new Error("project_file_root_protected");ht(n);const s=n.split("/"),i=s.slice(0,-1).join("/"),c=v(t,i,!0),f=u.join(c,s.at(-1)||"");let a;try{a=l.lstatSync(f)}catch(d){throw d.code==="ENOENT"?new Error("project_file_not_found"):d}if(a.isSymbolicLink())throw new Error("project_file_symbolic_link");if(!a.isFile()&&!a.isDirectory())throw new Error("project_file_type_unsupported");if(a.isDirectory()&&!r&&l.readdirSync(f).length>0)throw new Error("project_directory_not_empty");return a.isDirectory()&&r?l.rmSync(f,{recursive:!0,force:!1,maxRetries:2,retryDelay:150}):a.isDirectory()?l.rmdirSync(f):l.unlinkSync(f),{operation:"delete",path:n,entryType:a.isDirectory()?"directory":"file",parentPath:i,affectedDirectories:[i],rootKind:o==="worktree"?"worktree":"project"}}function ht(t){const e=t.split("/")[0].toLocaleLowerCase("en-US");if([".git",".aca",".aica"].includes(e))throw new Error("project_file_protected")}function wt(t,e,r,o){const n=p(t),s=S(e),i=v(n,s,!0),c=L(r);if(l.realpathSync.native(u.dirname(o))!==i)throw new Error("项目上传临时文件不在目标目录中。");const a=l.lstatSync(o);if(!a.isFile()||a.isSymbolicLink())throw new Error("项目上传临时文件无效。");for(let d=1;d<=1e4;d+=1){const P=d===1?c:yt(c,d),_=u.join(i,P);try{return l.linkSync(o,_),l.chmodSync(_,420),l.rmSync(o,{force:!0}),_}catch(A){if(M(A))continue;if(!gt(A))throw A;try{return l.copyFileSync(o,_,l.constants.COPYFILE_EXCL),l.chmodSync(_,420),l.rmSync(o,{force:!0}),_}catch($){if(M($))continue;throw $}}}throw new Error("同名文件过多,无法生成可用文件名。")}function L(t){const e=t.trim();if(!e||e==="."||e===".."||/[\\/\x00-\x1F]/.test(e))throw new Error("项目上传文件名无效。");if(process.platform==="win32"&&(/[<>:"|?*]/.test(e)||/[. ]$/.test(e)))throw new Error("项目上传文件名在 Windows 上无效。");return e.slice(0,180)}function yt(t,e){const r=u.extname(t);return`${r?t.slice(0,-r.length):t} (${e})${r}`}function M(t){return!!(t&&typeof t=="object"&&t.code==="EEXIST")}function gt(t){const e=t&&typeof t=="object"?t.code:"";return["EPERM","EACCES","EXDEV","ENOTSUP","EOPNOTSUPP"].includes(String(e||""))}const O=500,j=2*1024*1024,g=2*1024*1024;function G(t){const e=p(String(t.rootPath||""));if(!w(e))return{isRepository:!1,branch:null,files:[],ahead:0,behind:0};const o=m(e,["status","--porcelain=v1","-z","--untracked-files=all","--no-renames"],j).split("\0").filter(Boolean).slice(0,O),n=o.map(c=>bt(c)).filter(c=>!!c),s=D(m(e,["diff","--no-ext-diff","--numstat","-z","--no-renames","HEAD","--","."],j));for(const c of n){const f=s.get(c.path);if(f)c.additions=f.additions,c.deletions=f.deletions;else if(c.status==="untracked"){const a=H(e,c.path);c.additions=a.additions,c.deletions=a.deletions}}const i=Dt(e);return{isRepository:!0,branch:kt(e),files:n,ahead:i.ahead,behind:i.behind,truncated:o.length>=O}}function St(t){const e=p(String(t.rootPath||"")),r=S(String(t.relativePath||""));if(!r)throw new Error("Diff 文件路径不能为空。");if(!w(e))return{isRepository:!1,path:r,diff:"",additions:0,deletions:0};const o=B(e,r),n=vt(e,r),s=n?m(e,["diff","--no-ext-diff","--unified=80","HEAD","--",r],g):U(e,["diff","--no-ext-diff","--no-index","--unified=80",process.platform==="win32"?"NUL":"/dev/null",o],g),i=n?D(m(e,["diff","--no-ext-diff","--numstat","-z","--no-renames","HEAD","--",r],32*1024)).get(r):H(e,r);return{isRepository:!0,path:r,diff:z(s),additions:i?.additions??0,deletions:i?.deletions??0,truncated:s.length>g}}function _t(t){const e=p(String(t.rootPath||""));if(!w(e))return{isRepository:!1,items:[]};if(!At(e))return{isRepository:!0,items:[]};const r=Math.min(200,Math.max(20,Number(t.limit||100)));return{isRepository:!0,items:m(e,["log",`--max-count=${r}`,"--date=iso-strict","--pretty=format:%H%x1f%h%x1f%an%x1f%ae%x1f%aI%x1f%D%x1f%s%x1f%b%x1e"],j).split("").map(i=>Et(i)).filter(i=>!!i),limit:r}}function Pt(t){const e=p(String(t.rootPath||"")),r=String(t.commit||"").trim();if(!/^[0-9a-f]{7,64}$/i.test(r))throw new Error("Git 提交标识无效。");if(!w(e))return{isRepository:!1,commit:r,diff:"",additions:0,deletions:0};m(e,["cat-file","-e",`${r}^{commit}`],8*1024);const o=m(e,["show","--no-ext-diff","--no-color","--format=","--stat","--patch","--unified=40",r],g),n=[...D(m(e,["show","--no-ext-diff","--numstat","-z","--no-renames","--format=",r],j)).values()];return{isRepository:!0,commit:r,diff:z(o),additions:n.reduce((s,i)=>s+(i.additions||0),0),deletions:n.reduce((s,i)=>s+(i.deletions||0),0),changedFiles:n.length,truncated:o.length>g}}function Et(t){const e=t.replace(/^\s+|\s+$/g,"");if(!e)return null;const r=e.split(""),o=String(r[0]||"").trim();return o?{hash:o,shortHash:String(r[1]||o.slice(0,8)).trim(),authorName:String(r[2]||"").trim(),authorEmail:String(r[3]||"").trim(),authoredAt:String(r[4]||"").trim(),refs:String(r[5]||"").trim(),subject:String(r[6]||"").trim(),body:r.slice(7).join("").trim()}:null}function bt(t){if(t.length<4)return null;const e=t[0]||" ",r=t[1]||" ",o=S(t.slice(3));return o?{path:o,status:e==="?"&&r==="?"?"untracked":e==="U"||r==="U"?"conflicted":e==="D"||r==="D"?"deleted":e==="A"||r==="A"?"added":e==="R"||r==="R"?"renamed":"modified",indexStatus:e,worktreeStatus:r,staged:e!==" "&&e!=="?",additions:null,deletions:null}:null}function D(t){const e=new Map;for(const r of t.split("\0")){if(!r)continue;const o=W(r);if(!o||!o.path)continue;const n=S(o.path);n&&e.set(n,{additions:o.additions,deletions:o.deletions})}return e}function H(t,e){const r=B(t,e),n=U(t,["diff","--no-ext-diff","--no-index","--numstat","-z","--",process.platform==="win32"?"NUL":"/dev/null",r],32*1024).split("\0",1)[0]||"",s=W(n);return s?{additions:s.additions,deletions:s.deletions}:{additions:null,deletions:null}}function W(t){const e=t.indexOf(" "),r=e<0?-1:t.indexOf(" ",e+1);if(e<0||r<0)return null;const o=t.slice(0,e),n=t.slice(e+1,r);return{additions:/^\d+$/.test(o)?Number(o):null,deletions:/^\d+$/.test(n)?Number(n):null,path:t.slice(r+1)}}function w(t){try{return m(t,["rev-parse","--is-inside-work-tree"],8*1024).trim()==="true"}catch(e){if(String(e.code||"")==="ENOENT")throw new Error("设备未安装 Git。");return!1}}function xt(t,e){try{const r=m(t,["rev-list","--count",`${e}..HEAD`],8192).trim(),o=Number(r);return Number.isSafeInteger(o)&&o>0?o:0}catch{return 0}}function jt(t){try{return l.readdirSync(t).length>0}catch{return!1}}function vt(t,e){try{return m(t,["ls-files","--error-unmatch","--",e],8*1024),!0}catch{return!1}}function At(t){try{return m(t,["rev-parse","--verify","HEAD"],8*1024),!0}catch{return!1}}function kt(t){try{return m(t,["symbolic-ref","--quiet","--short","HEAD"],8*1024).trim()||null}catch{try{return`HEAD (${m(t,["rev-parse","--short","HEAD"],8*1024).trim()})`}catch{return null}}}function Dt(t){try{const e=m(t,["rev-list","--left-right","--count","HEAD...@{upstream}"],8192).trim().split(/\s+/).map(Number);return{ahead:Number.isFinite(e[0])?e[0]:0,behind:Number.isFinite(e[1])?e[1]:0}}catch{return{ahead:0,behind:0}}}function B(t,e){const r=u.resolve(t,...e.split("/")),o=u.relative(t,r);if(o.startsWith("..")||u.isAbsolute(o))throw new Error("Diff 路径超出项目范围。");return r}function m(t,e,r){return y("git",["-C",t,...e],{encoding:"utf8",timeout:2e4,maxBuffer:r,stdio:["ignore","pipe","pipe"]})}function U(t,e,r){try{return m(t,e,r)}catch(o){const n=String(o.stdout||"");if(n)return n;if(Number(o.status)===1)return"";throw o}}function z(t){return t.length<=g?t:`${t.slice(0,g)}
2
2
 
3
- [Diff 内容过大,已截断]`}function Dt(){const t=process.platform,e=D(t);return{platform:t,roots:e}}function Rt(t){if(!t.directoryPath.trim())throw new Error("目录路径不能为空。");return process.platform==="win32"?Tt(t.directoryPath,!!t.includeHidden):$t(t.directoryPath,!!t.includeHidden)}function h(t,e=process.platform){const r=e==="win32"?u.win32:u.posix,o=r.normalize(t),n=r.parse(o).root,s=o.length>n.length?o.replace(/[\\/]+$/,""):o;return e==="win32"?s.toLocaleLowerCase("en-US"):s}function Ct(){const t=l.realpathSync(T.homedir()),e=[{id:"home",label:"用户目录",path:t,kind:"home",available:!0}];return t!==u.parse(t).root&&e.push({id:"root",label:"文件系统",path:"/",kind:"root",available:!0}),e}function D(t){if(b?.platform===t&&b.expiresAtMs>Date.now())return b.roots;const e=t==="win32"?Nt():Ct();return b={platform:t,roots:e,expiresAtMs:Date.now()+st},e}function Nt(){const t=l.realpathSync.native(T.homedir()),e=new Map;e.set(h(t,"win32"),{id:"home",label:"用户目录",path:t,kind:"home",available:!0});try{const r=["$items = Get-CimInstance Win32_LogicalDisk | ForEach-Object {"," [PSCustomObject]@{ Name = $_.DeviceID; Root = ($_.DeviceID + '\\\\'); DriveType = $_.DriveType; VolumeName = $_.VolumeName }","}","@($items) | ConvertTo-Json -Compress"].join(`
4
- `),o=Y(V(r));for(const n of o){const s=String(n.Root||"");if(!s)continue;const i=Number(n.DriveType||0),a=String(n.Name||s.replace(/[\\/]+$/,"")),c=String(n.VolumeName||"").trim();e.set(h(s,"win32"),{id:`drive-${a.replace(/[^a-z0-9]+/gi,"").toLowerCase()}`,label:c?`${c} (${a})`:Ht(a,i),path:s,kind:Gt(i),available:!0})}}catch{const r=u.win32.parse(t).root;e.set(h(r,"win32"),{id:"drive-system",label:`系统盘 (${r.replace(/\\$/,"")})`,path:r,kind:"fixed",available:!0})}return[...e.values()]}function $t(t,e){const r=R(t),n=l.readdirSync(r,{withFileTypes:!0}).filter(i=>i.isDirectory()&&!i.isSymbolicLink()&&(e||!i.name.startsWith("."))),s=n.slice(0,E).map(i=>({name:i.name,path:u.join(r,i.name),kind:"directory",hidden:i.name.startsWith("."),hasChildren:!0}));return s.sort((i,a)=>i.name.localeCompare(a.name,void 0,{numeric:!0,sensitivity:"base"})),z(r,s,n.length>E,"linux")}function Tt(t,e){const r=R(t),o=["$items = Get-ChildItem -LiteralPath $env:ACA_MACHINE_FS_PATH -Directory -Force -ErrorAction Stop | ForEach-Object {"," [PSCustomObject]@{ Name = $_.Name; FullName = $_.FullName; Attributes = $_.Attributes.ToString() }","}","@($items) | ConvertTo-Json -Compress"].join(`
5
- `),n=Y(V(o,{ACA_MACHINE_FS_PATH:r})),s=n.map(i=>{const a=String(i.Attributes||"");return{name:String(i.Name||""),path:String(i.FullName||""),kind:"directory",hidden:/Hidden|System/i.test(a),hasChildren:!0,reparsePoint:/ReparsePoint/i.test(a)}}).filter(i=>i.name&&i.path&&!i.reparsePoint&&(e||!i.hidden)).slice(0,E).sort((i,a)=>i.name.localeCompare(a.name,void 0,{numeric:!0,sensitivity:"base"}));return z(r,s,n.length>E,"win32")}function z(t,e,r,o){const n=o==="win32"?u.win32:u.posix,s=n.parse(t).root,i=h(t,o)===h(s,o)?null:n.dirname(t);return{platform:o,path:t,parentPath:i,breadcrumbs:Ft(t,o),readable:X(t,l.constants.R_OK),writable:X(t,l.constants.W_OK),items:e,truncated:r}}function Ft(t,e){const r=e==="win32"?u.win32:u.posix,o=r.parse(t),n=t.slice(o.root.length).split(/[\\/]+/).filter(Boolean),s=[{label:o.root.replace(/[\\/]+$/,"")||o.root,path:o.root}];let i=o.root;for(const a of n)i=r.join(i,a),s.push({label:a,path:i});return s}async function It(t){const e=String(t.workspaceId||"").trim(),r=String(t.name||"").trim(),o=t.projectType==="chat_room"?"chat_room":"project";if(!e||!r)throw new Error("项目名称和工作区不能为空。");const n=R(String(t.path||""));l.accessSync(n,l.constants.R_OK|l.constants.W_OK);const i=(await P("GET",`/api/projects?workspaceId=${encodeURIComponent(e)}`)).items.find(c=>h(c.root_path)===h(n)),a=i||(await P("POST","/api/projects",{projectId:$(),workspaceId:e,name:r,rootPath:n,source:"aica",projectType:o})).item;return tt(Lt(a)),{project:a,duplicate:!!i}}function Lt(t){return{projectId:t.project_id,workspaceId:t.workspace_id,name:t.name,rootPath:t.root_path}}function R(t){if(!t.trim()||!u.isAbsolute(t))throw new Error("必须选择绝对目录路径。");if(Mt(t),!l.statSync(t).isDirectory())throw new Error("选择的路径不是目录。");const r=l.realpathSync.native(t);return Ot(r),r}function m(t){const e=C(t),r=l.realpathSync.native(e);if(!l.statSync(r).isDirectory())throw new Error("项目根目录不存在。");return r}function C(t){const e=rt(t);if(!e||!u.isAbsolute(e))throw new Error("项目根目录无效。");return u.resolve(e)}function S(t){const e=t.replace(/\\/g,"/").replace(/^\/+|\/+$/g,"");if(!e)return"";if(e.includes("\0")||e.split("/").some(r=>!r||r==="."||r===".."))throw new Error("项目相对路径无效。");return e}function j(t,e,r){const o=e?u.join(t,...e.split("/")):t,n=l.realpathSync.native(o),s=u.relative(t,n);if(s.startsWith("..")||u.isAbsolute(s))throw new Error("目录超出项目范围。");const i=l.statSync(n);if(r&&!i.isDirectory())throw new Error("请求路径不是目录。");return n}function K(t,e){return u.relative(t,e).split(u.sep).join("/")}function Mt(t){const e=process.platform==="win32"?u.win32:u.posix;if(!D(process.platform).some(o=>{const n=String(o.path||"");if(!n)return!1;const s=e.relative(n,t);return s===""||!s.startsWith("..")&&!e.isAbsolute(s)}))throw new Error("目录不在允许浏览的位置内。")}function Ot(t){const e=process.platform==="win32"?u.win32:u.posix;if(!D(process.platform).some(o=>{const n=String(o.path||"");if(!n)return!1;const s=e.relative(n,t);return s===""||!s.startsWith("..")&&!e.isAbsolute(s)}))throw new Error("目录不在允许浏览的位置内。")}function X(t,e){try{return l.accessSync(t,e),!0}catch{return!1}}function V(t,e={}){return y("powershell.exe",["-NoLogo","-NoProfile","-NonInteractive","-Command",t],{encoding:"utf8",windowsHide:!0,timeout:8e3,maxBuffer:4*1024*1024,env:{...process.env,...e}}).replace(/^\uFEFF/,"").trim()}function Y(t){if(!t)return[];const e=JSON.parse(t);return Array.isArray(e)?e.filter(r=>!!(r&&typeof r=="object")):e&&typeof e=="object"?[e]:[]}function Gt(t){return t===2?"removable":t===3?"fixed":t===4?"network":t===5?"optical":"drive"}function Ht(t,e){return e===2?`移动磁盘 (${t})`:e===4?`网络驱动器 (${t})`:e===5?`光驱 (${t})`:`本地磁盘 (${t})`}export{lt as cleanupWorktree,ht as commitProjectUploadTempFile,ct as commitWorktree,h as comparableMachinePath,_t as getProjectGitCommit,gt as getProjectGitDiff,St as getProjectGitLog,O as getProjectGitStatus,k as getWorktreeStatus,Rt as listMachineDirectory,Dt as listMachineFilesystemRoots,ut as listProjectDirectory,dt as mutateProjectFiles,te as startMachineFilesystemWorkerLoop,ft as uploadProjectFile};
3
+ [Diff 内容过大,已截断]`}function Rt(){const t=process.platform,e=R(t);return{platform:t,roots:e}}function Ct(t){if(!t.directoryPath.trim())throw new Error("目录路径不能为空。");return process.platform==="win32"?Ft(t.directoryPath,!!t.includeHidden):Tt(t.directoryPath,!!t.includeHidden)}function h(t,e=process.platform){const r=e==="win32"?u.win32:u.posix,o=r.normalize(t),n=r.parse(o).root,s=o.length>n.length?o.replace(/[\\/]+$/,""):o;return e==="win32"?s.toLocaleLowerCase("en-US"):s}function Nt(){const t=l.realpathSync(F.homedir()),e=[{id:"home",label:"用户目录",path:t,kind:"home",available:!0}];return t!==u.parse(t).root&&e.push({id:"root",label:"文件系统",path:"/",kind:"root",available:!0}),e}function R(t){if(x?.platform===t&&x.expiresAtMs>Date.now())return x.roots;const e=t==="win32"?$t():Nt();return x={platform:t,roots:e,expiresAtMs:Date.now()+st},e}function $t(){const t=l.realpathSync.native(F.homedir()),e=new Map;e.set(h(t,"win32"),{id:"home",label:"用户目录",path:t,kind:"home",available:!0});try{const r=["$items = Get-CimInstance Win32_LogicalDisk | ForEach-Object {"," [PSCustomObject]@{ Name = $_.DeviceID; Root = ($_.DeviceID + '\\\\'); DriveType = $_.DriveType; VolumeName = $_.VolumeName }","}","@($items) | ConvertTo-Json -Compress"].join(`
4
+ `),o=J(Y(r));for(const n of o){const s=String(n.Root||"");if(!s)continue;const i=Number(n.DriveType||0),c=String(n.Name||s.replace(/[\\/]+$/,"")),f=String(n.VolumeName||"").trim();e.set(h(s,"win32"),{id:`drive-${c.replace(/[^a-z0-9]+/gi,"").toLowerCase()}`,label:f?`${f} (${c})`:Wt(c,i),path:s,kind:Ht(i),available:!0})}}catch{const r=u.win32.parse(t).root;e.set(h(r,"win32"),{id:"drive-system",label:`系统盘 (${r.replace(/\\$/,"")})`,path:r,kind:"fixed",available:!0})}return[...e.values()]}function Tt(t,e){const r=C(t),n=l.readdirSync(r,{withFileTypes:!0}).filter(i=>i.isDirectory()&&!i.isSymbolicLink()&&(e||!i.name.startsWith("."))),s=n.slice(0,b).map(i=>({name:i.name,path:u.join(r,i.name),kind:"directory",hidden:i.name.startsWith("."),hasChildren:!0}));return s.sort((i,c)=>i.name.localeCompare(c.name,void 0,{numeric:!0,sensitivity:"base"})),K(r,s,n.length>b,"linux")}function Ft(t,e){const r=C(t),o=["$items = Get-ChildItem -LiteralPath $env:ACA_MACHINE_FS_PATH -Directory -Force -ErrorAction Stop | ForEach-Object {"," [PSCustomObject]@{ Name = $_.Name; FullName = $_.FullName; Attributes = $_.Attributes.ToString() }","}","@($items) | ConvertTo-Json -Compress"].join(`
5
+ `),n=J(Y(o,{ACA_MACHINE_FS_PATH:r})),s=n.map(i=>{const c=String(i.Attributes||"");return{name:String(i.Name||""),path:String(i.FullName||""),kind:"directory",hidden:/Hidden|System/i.test(c),hasChildren:!0,reparsePoint:/ReparsePoint/i.test(c)}}).filter(i=>i.name&&i.path&&!i.reparsePoint&&(e||!i.hidden)).slice(0,b).sort((i,c)=>i.name.localeCompare(c.name,void 0,{numeric:!0,sensitivity:"base"}));return K(r,s,n.length>b,"win32")}function K(t,e,r,o){const n=o==="win32"?u.win32:u.posix,s=n.parse(t).root,i=h(t,o)===h(s,o)?null:n.dirname(t);return{platform:o,path:t,parentPath:i,breadcrumbs:It(t,o),readable:V(t,l.constants.R_OK),writable:V(t,l.constants.W_OK),items:e,truncated:r}}function It(t,e){const r=e==="win32"?u.win32:u.posix,o=r.parse(t),n=t.slice(o.root.length).split(/[\\/]+/).filter(Boolean),s=[{label:o.root.replace(/[\\/]+$/,"")||o.root,path:o.root}];let i=o.root;for(const c of n)i=r.join(i,c),s.push({label:c,path:i});return s}async function Lt(t){const e=String(t.workspaceId||"").trim(),r=String(t.name||"").trim(),o=t.projectType==="chat_room"?"chat_room":"project";if(!e||!r)throw new Error("项目名称和工作区不能为空。");const n=C(String(t.path||""));l.accessSync(n,l.constants.R_OK|l.constants.W_OK);const i=(await E("GET",`/api/projects?workspaceId=${encodeURIComponent(e)}`)).items.find(f=>h(f.root_path)===h(n)),c=i||(await E("POST","/api/projects",{projectId:T(),workspaceId:e,name:r,rootPath:n,source:"aica",projectType:o})).item;return tt(Mt(c)),{project:c,duplicate:!!i}}function Mt(t){return{projectId:t.project_id,workspaceId:t.workspace_id,name:t.name,rootPath:t.root_path}}function C(t){if(!t.trim()||!u.isAbsolute(t))throw new Error("必须选择绝对目录路径。");if(Ot(t),!l.statSync(t).isDirectory())throw new Error("选择的路径不是目录。");const r=l.realpathSync.native(t);return Gt(r),r}function p(t){const e=N(t),r=l.realpathSync.native(e);if(!l.statSync(r).isDirectory())throw new Error("项目根目录不存在。");return r}function N(t){const e=rt(t);if(!e||!u.isAbsolute(e))throw new Error("项目根目录无效。");return u.resolve(e)}function S(t){const e=t.replace(/\\/g,"/").replace(/^\/+|\/+$/g,"");if(!e)return"";if(e.includes("\0")||e.split("/").some(r=>!r||r==="."||r===".."))throw new Error("项目相对路径无效。");return e}function v(t,e,r){const o=e?u.join(t,...e.split("/")):t,n=l.realpathSync.native(o),s=u.relative(t,n);if(s.startsWith("..")||u.isAbsolute(s))throw new Error("目录超出项目范围。");const i=l.statSync(n);if(r&&!i.isDirectory())throw new Error("请求路径不是目录。");return n}function X(t,e){return u.relative(t,e).split(u.sep).join("/")}function Ot(t){const e=process.platform==="win32"?u.win32:u.posix;if(!R(process.platform).some(o=>{const n=String(o.path||"");if(!n)return!1;const s=e.relative(n,t);return s===""||!s.startsWith("..")&&!e.isAbsolute(s)}))throw new Error("目录不在允许浏览的位置内。")}function Gt(t){const e=process.platform==="win32"?u.win32:u.posix;if(!R(process.platform).some(o=>{const n=String(o.path||"");if(!n)return!1;const s=e.relative(n,t);return s===""||!s.startsWith("..")&&!e.isAbsolute(s)}))throw new Error("目录不在允许浏览的位置内。")}function V(t,e){try{return l.accessSync(t,e),!0}catch{return!1}}function Y(t,e={}){return y("powershell.exe",["-NoLogo","-NoProfile","-NonInteractive","-Command",t],{encoding:"utf8",windowsHide:!0,timeout:8e3,maxBuffer:4*1024*1024,env:{...process.env,...e}}).replace(/^\uFEFF/,"").trim()}function J(t){if(!t)return[];const e=JSON.parse(t);return Array.isArray(e)?e.filter(r=>!!(r&&typeof r=="object")):e&&typeof e=="object"?[e]:[]}function Ht(t){return t===2?"removable":t===3?"fixed":t===4?"network":t===5?"optical":"drive"}function Wt(t,e){return e===2?`移动磁盘 (${t})`:e===4?`网络驱动器 (${t})`:e===5?`光驱 (${t})`:`本地磁盘 (${t})`}export{lt as cleanupWorktree,wt as commitProjectUploadTempFile,at as commitWorktree,h as comparableMachinePath,Pt as getProjectGitCommit,St as getProjectGitDiff,_t as getProjectGitLog,G as getProjectGitStatus,k as getWorktreeStatus,Ct as listMachineDirectory,Rt as listMachineFilesystemRoots,ut as listProjectDirectory,mt as mutateProjectFiles,ee as startMachineFilesystemWorkerLoop,dt as uploadProjectFile};
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- const o="0.1.63";export{o as AICA_VERSION};
1
+ const o="0.1.65";export{o as AICA_VERSION};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuandc/aica",
3
- "version": "0.1.63",
3
+ "version": "0.1.65",
4
4
  "description": "AICA remote Agent worker",
5
5
  "author": "yuandc",
6
6
  "type": "module",