@yuandc/aica 0.1.39 → 0.1.41
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,5 +1,5 @@
|
|
|
1
|
-
import{execFileSync as g}from"node:child_process";import{randomUUID as N}from"node:crypto";import u from"node:fs";import T from"node:os";import c from"node:path";import{acaServerDownloadToFile as Q,acaServerRequest as x}from"./aca-server-client.js";import{loadAcaConfig as Z,upsertLocalProject as q}from"./aca-config.js";import{startLongPollWorker as tt}from"./long-poll-worker.js";import{expandWorkerRuntimePath as et}from"./worker-runtime-paths.js";import{syncCodexProviderRuntimeConfig as rt}from"./codex-provider-runtime-config.js";import{defaultAcpRuntimePool as ot}from"../acp/client/acp-runtime-pool.js";const F=25e3,nt=F+1e4,b=300,it=3e4;let _=null;function Yt(t){return tt({run:async()=>{const e=Z();if(!e.token)throw new Error("Worker 尚未配置认证令牌");const r=await x("GET",`/api/client/machine-operations/claim?machineId=${encodeURIComponent(e.machineId)}&waitMs=${F}`,void 0,{timeoutMs:nt});if(r.item)try{const o=await st(r.item);await x("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 x("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 st(t){if(t.operation==="sync_codex_provider_config"){const e=await rt(),r=await ot.retireProviderRuntimes("codex-acp");return{configured:!!e,revision:e?.revision??null,retiredRuntimes:r}}if(t.operation==="filesystem_roots")return vt();if(t.operation==="list_directory")return jt({directoryPath:String(t.payload.path||""),includeHidden:t.payload.includeHidden===!0});if(t.operation==="register_project")return Dt(t.payload);if(t.operation==="list_project_directory")return ut(t.payload);if(t.operation==="upload_project_file")return lt(t.payload);if(t.operation==="get_project_git_status")return O(t.payload);if(t.operation==="get_project_git_diff")return pt(t.payload);if(t.operation==="get_project_git_log")return ht(t.payload);if(t.operation==="get_project_git_commit")return wt(t.payload);if(t.operation==="get_worktree_status")return j(t.payload);if(t.operation==="commit_worktree")return at(t.payload);if(t.operation==="cleanup_worktree")return ct(t.payload);throw new Error("不支持的 Machine 文件系统操作。 ")}function j(t){const e=C(String(t.worktreePath||""));if(!u.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:Pt(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)?St(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=m(String(t.worktreePath||"")),r=String(t.message||"").trim();if(!r)throw new Error("提交说明不能为空。");if(!w(e))throw new Error("当前 Worktree 不是 Git 仓库,无法提交。");g("git",["-C",e,"add","-A"],{stdio:"pipe",timeout:3e4});try{return g("git",["-C",e,"diff","--cached","--quiet"],{stdio:"pipe",timeout:1e4}),{committed:!1,reason:"no_changes",...j(t)}}catch(o){if(Number(o.status)!==1)throw o}return g("git",["-C",e,"commit","-m",r],{stdio:"pipe",timeout:6e4}),{committed:!0,commit:f(e,["rev-parse","HEAD"],8*1024).trim(),...j(t)}}function ct(t){const e=C(String(t.worktreePath||"")),r=m(String(t.projectRoot||""));if(p(e)===p(r))throw new Error("不能清理项目主目录。");if(!u.existsSync(e))return{removed:!1,reason:"not_found"};const o=m(e),n=String(t.branchName||"").trim(),s=w(o)&&w(r);if(s){if(g("git",["-C",r,"worktree","remove","--force",o],{stdio:"pipe",timeout:6e4}),t.deleteBranch===!0&&n)try{g("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 u.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=P(String(t.relativePath||"")),o=R(e,r,!0),n=Math.max(0,Number(t.offset||0)),s=Math.min(300,Math.max(20,Number(t.limit||200))),i=u.readdirSync(o,{withFileTypes:!0}).filter(l=>!l.isSymbolicLink()).map(l=>({name:l.name,path:K(e,c.join(o,l.name)),type:l.isDirectory()?"directory":l.isFile()?"file":"other",hidden:l.name.startsWith("."),extension:l.isFile()?c.extname(l.name).slice(1).toLowerCase():""})).filter(l=>l.type!=="other").sort((l,d)=>l.type!==d.type?l.type==="directory"?-1:1:l.name.localeCompare(d.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 lt(t){const e=String(t.uploadId||"").trim();if(!e)throw new Error("项目上传任务缺少 uploadId。");const r=m(String(t.rootPath||"")),o=P(String(t.relativeDirectory||"")),n=R(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 l=c.join(n,`.aca-upload-${N()}.tmp`);try{const d=await Q(`/api/client/project-uploads/${encodeURIComponent(e)}`,l,{maxBytes:i,timeoutMs:3e5});if(d.sizeBytes!==i)throw new Error("项目上传文件大小校验失败。");if(d.sha256.toLowerCase()!==a)throw new Error("项目上传文件完整性校验失败。");const h=ft(r,o,s,l);return{name:c.basename(h),path:K(r,h),directory:o,size:d.sizeBytes,sha256:d.sha256,renamed:c.basename(h)!==s,rootKind:t.rootKind==="worktree"?"worktree":"project"}}finally{u.rmSync(l,{force:!0})}}function ft(t,e,r,o){const n=m(t),s=P(e),i=R(n,s,!0),a=I(r);if(u.realpathSync.native(c.dirname(o))!==i)throw new Error("项目上传临时文件不在目标目录中。");const d=u.lstatSync(o);if(!d.isFile()||d.isSymbolicLink())throw new Error("项目上传临时文件无效。");for(let h=1;h<=1e4;h+=1){const J=h===1?a:dt(a,h),S=c.join(i,J);try{return u.linkSync(o,S),u.chmodSync(S,420),u.rmSync(o,{force:!0}),S}catch(v){if(L(v))continue;if(!mt(v))throw v;try{return u.copyFileSync(o,S,u.constants.COPYFILE_EXCL),u.chmodSync(S,420),u.rmSync(o,{force:!0}),S}catch(D){if(L(D))continue;throw D}}}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 dt(t,e){const r=c.extname(t);return`${r?t.slice(0,-r.length):t} (${e})${r}`}function L(t){return!!(t&&typeof t=="object"&&t.code==="EEXIST")}function mt(t){const e=t&&typeof t=="object"?t.code:"";return["EPERM","EACCES","EXDEV","ENOTSUP","EOPNOTSUPP"].includes(String(e||""))}const M=500,E=2*1024*1024,y=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=f(e,["status","--porcelain=v1","-z","--untracked-files=all","--no-renames"],E).split("\0").filter(Boolean).slice(0,M),n=o.map(a=>yt(a)).filter(a=>!!a),s=A(f(e,["diff","--no-ext-diff","--numstat","-z","--no-renames","HEAD","--","."],E));for(const a of n){const l=s.get(a.path);if(l)a.additions=l.additions,a.deletions=l.deletions;else if(a.status==="untracked"){const d=G(e,a.path);a.additions=d.additions,a.deletions=d.deletions}}const i=Et(e);return{isRepository:!0,branch:_t(e),files:n,ahead:i.ahead,behind:i.behind,truncated:o.length>=M}}function pt(t){const e=m(String(t.rootPath||"")),r=P(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=xt(e,r),s=n?f(e,["diff","--no-ext-diff","--unified=80","HEAD","--",r],y):W(e,["diff","--no-ext-diff","--no-index","--unified=80",process.platform==="win32"?"NUL":"/dev/null",o],y),i=n?A(f(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>y}}function ht(t){const e=m(String(t.rootPath||""));if(!w(e))return{isRepository:!1,items:[]};if(!bt(e))return{isRepository:!0,items:[]};const r=Math.min(200,Math.max(20,Number(t.limit||100)));return{isRepository:!0,items:f(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"],E).split("").map(i=>gt(i)).filter(i=>!!i),limit:r}}function wt(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};f(e,["cat-file","-e",`${r}^{commit}`],8*1024);const o=f(e,["show","--no-ext-diff","--no-color","--format=","--stat","--patch","--unified=40",r],y),n=[...A(f(e,["show","--no-ext-diff","--numstat","-z","--no-renames","--format=",r],E)).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>y}}function gt(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 yt(t){if(t.length<4)return null;const e=t[0]||" ",r=t[1]||" ",o=P(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=P(o.path);n&&e.set(n,{additions:o.additions,deletions:o.deletions})}return e}function G(t,e){const r=B(t,e),n=W(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 f(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 St(t,e){try{const r=f(t,["rev-list","--count",`${e}..HEAD`],8192).trim(),o=Number(r);return Number.isSafeInteger(o)&&o>0?o:0}catch{return 0}}function Pt(t){try{return u.readdirSync(t).length>0}catch{return!1}}function xt(t,e){try{return f(t,["ls-files","--error-unmatch","--",e],8*1024),!0}catch{return!1}}function bt(t){try{return f(t,["rev-parse","--verify","HEAD"],8*1024),!0}catch{return!1}}function _t(t){try{return f(t,["symbolic-ref","--quiet","--short","HEAD"],8*1024).trim()||null}catch{try{return`HEAD (${f(t,["rev-parse","--short","HEAD"],8*1024).trim()})`}catch{return null}}}function Et(t){try{const e=f(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=c.resolve(t,...e.split("/")),o=c.relative(t,r);if(o.startsWith("..")||c.isAbsolute(o))throw new Error("Diff 路径超出项目范围。");return r}function f(t,e,r){return g("git",["-C",t,...e],{encoding:"utf8",timeout:2e4,maxBuffer:r,stdio:["ignore","pipe","pipe"]})}function W(t,e,r){try{return f(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<=y?t:`${t.slice(0,y)}
|
|
1
|
+
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 Q,acaServerRequest as P}from"./aca-server-client.js";import{loadAcaConfig as Z,upsertLocalProject as q}from"./aca-config.js";import{startLongPollWorker as tt}from"./long-poll-worker.js";import{expandWorkerRuntimePath as et}from"./worker-runtime-paths.js";import{syncCodexProviderRuntimeConfig as rt}from"./codex-provider-runtime-config.js";import{defaultAcpRuntimePool as ot}from"../acp/client/acp-runtime-pool.js";const F=25e3,nt=F+1e4,E=300,it=3e4;let b=null;function Zt(t){return tt({run:async()=>{const e=Z();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:nt});if(r.item)try{const o=await st(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 st(t){if(t.operation==="sync_codex_provider_config"){const e=await rt(),r=await ot.retireProviderRuntimes("codex-acp");return{configured:!!e,revision:e?.revision??null,retiredRuntimes:r}}if(t.operation==="filesystem_roots")return kt();if(t.operation==="list_directory")return Dt({directoryPath:String(t.payload.path||""),includeHidden:t.payload.includeHidden===!0});if(t.operation==="register_project")return Ft(t.payload);if(t.operation==="list_project_directory")return lt(t.payload);if(t.operation==="upload_project_file")return ut(t.payload);if(t.operation==="mutate_project_files")return ft(t.payload);if(t.operation==="get_project_git_status")return O(t.payload);if(t.operation==="get_project_git_diff")return yt(t.payload);if(t.operation==="get_project_git_log")return gt(t.payload);if(t.operation==="get_project_git_commit")return St(t.payload);if(t.operation==="get_worktree_status")return A(t.payload);if(t.operation==="commit_worktree")return at(t.payload);if(t.operation==="cleanup_worktree")return ct(t.payload);throw new Error("不支持的 Machine 文件系统操作。 ")}function A(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:bt(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)?Et(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=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",...A(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(),...A(t)}}function ct(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),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 lt(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 ut(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 Q(`/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=pt(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 ft(t){const e=m(String(t.rootPath||"")),r=t.action&&typeof t.action=="object"?t.action:{};if(r.type==="delete")return dt(e,String(r.path||""),r.recursive===!0,t.rootKind);throw new Error("project_file_operation_unsupported")}function dt(t,e,r,o){const n=S(e);if(!n)throw new Error("project_file_root_protected");mt(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 mt(t){const e=t.split("/")[0].toLocaleLowerCase("en-US");if([".git",".aca",".aica"].includes(e))throw new Error("project_file_protected")}function pt(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:ht(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(!wt(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 ht(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 wt(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=>Pt(a)).filter(a=>!!a),s=k(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:vt(e),files:n,ahead:i.ahead,behind:i.behind,truncated:o.length>=M}}function yt(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=B(e,r),n=xt(e,r),s=n?d(e,["diff","--no-ext-diff","--unified=80","HEAD","--",r],g):W(e,["diff","--no-ext-diff","--no-index","--unified=80",process.platform==="win32"?"NUL":"/dev/null",o],g),i=n?k(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 gt(t){const e=m(String(t.rootPath||""));if(!w(e))return{isRepository:!1,items:[]};if(!jt(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=>_t(i)).filter(i=>!!i),limit:r}}function St(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=[...k(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 _t(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 Pt(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 k(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=B(t,e),n=W(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 Et(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 bt(t){try{return l.readdirSync(t).length>0}catch{return!1}}function xt(t,e){try{return d(t,["ls-files","--error-unmatch","--",e],8*1024),!0}catch{return!1}}function jt(t){try{return d(t,["rev-parse","--verify","HEAD"],8*1024),!0}catch{return!1}}function vt(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 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 d(t,e,r){return y("git",["-C",t,...e],{encoding:"utf8",timeout:2e4,maxBuffer:r,stdio:["ignore","pipe","pipe"]})}function W(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)}
|
|
2
2
|
|
|
3
|
-
[Diff 内容过大,已截断]`}function
|
|
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(/[\\/]+$/,"")),
|
|
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,
|
|
3
|
+
[Diff 内容过大,已截断]`}function kt(){const t=process.platform,e=D(t);return{platform:t,roots:e}}function Dt(t){if(!t.directoryPath.trim())throw new Error("目录路径不能为空。");return process.platform==="win32"?$t(t.directoryPath,!!t.includeHidden):Nt(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 Rt(){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"?Ct():Rt();return b={platform:t,roots:e,expiresAtMs:Date.now()+it},e}function Ct(){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})`:Gt(a,i),path:s,kind:Ot(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 Nt(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 $t(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:Tt(t,o),readable:X(t,l.constants.R_OK),writable:X(t,l.constants.W_OK),items:e,truncated:r}}function Tt(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 Ft(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 q(It(a)),{project:a,duplicate:!!i}}function It(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(Lt(t),!l.statSync(t).isDirectory())throw new Error("选择的路径不是目录。");const r=l.realpathSync.native(t);return Mt(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=et(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 Lt(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 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 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 Ot(t){return t===2?"removable":t===3?"fixed":t===4?"network":t===5?"optical":"drive"}function Gt(t,e){return e===2?`移动磁盘 (${t})`:e===4?`网络驱动器 (${t})`:e===5?`光驱 (${t})`:`本地磁盘 (${t})`}export{ct as cleanupWorktree,pt as commitProjectUploadTempFile,at as commitWorktree,h as comparableMachinePath,St as getProjectGitCommit,yt as getProjectGitDiff,gt as getProjectGitLog,O as getProjectGitStatus,A as getWorktreeStatus,Dt as listMachineDirectory,kt as listMachineFilesystemRoots,lt as listProjectDirectory,ft as mutateProjectFiles,Zt as startMachineFilesystemWorkerLoop,ut as uploadProjectFile};
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const o="0.1.
|
|
1
|
+
const o="0.1.41";export{o as AICA_VERSION};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuandc/aica",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.41",
|
|
4
4
|
"description": "AICA remote Agent worker",
|
|
5
5
|
"author": "yuandc",
|
|
6
6
|
"type": "module",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"worker"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@openai/codex": "0.
|
|
29
|
+
"@openai/codex": "0.144.0",
|
|
30
30
|
"chalk": "^5.4.1",
|
|
31
31
|
"commander": "^12.1.0",
|
|
32
32
|
"diff": "^9.0.0",
|