@yuandc/aica 0.1.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/job-worker.js +2 -2
- package/dist/worker-cli.js +1 -1
- package/package.json +1 -1
package/dist/core/job-worker.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import V from"node:crypto";import $ from"node:fs";import j from"node:path";import{compactAgentContext as q,forkAgentSession as D,sendAgentPrompt as H}from"../acp/agent.js";import{acaServerDownload as G,acaServerRequest as I}from"./aca-server-client.js";import{loadAcaConfig as K}from"./aca-config.js";import{prepareLocalWorktree as X}from"./worktree.js";import{expandWorkerRuntimePath as Z}from"./worker-runtime-paths.js";import{collectAffectedFiles as O,extractAffectedFilePathsFromAcpRaw as Q}from"./affected-files.js";import{resolveJobPermissionPolicy as Y}from"./job-permission-policy.js";import{appendCoalescedAcpEvent as ee}from"./acp-event-coalescer.js";import{shouldUploadAcpEvent as te}from"./acp-event-upload-filter.js";import{userFacingAcpError as ne}from"../acp/error-display.js";const oe=1500,ae=5e3,U=1e3,ie=200,re=64,B=900,se=1e4,ce=0,le=12e3;function ue(e){return Z(e)}function pe(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 Ze(e,t={}){let n=!1,a=null,o=null,r=0,c=0;const u=new Set,i=new Set,m=new Map,C=async()=>{if(n||Date.now()<c)return;const y=de();if(F(u.size,y))try{const p=t.loadConfig?.()??K();if(!p.token)return;for(;!n&&F(u.size,y);){const _=t.claimNextJob?await t.claimNextJob(p):await I("POST","/api/client/jobs/claim",{machineId:p.machineId,workspaceId:p.defaultWorkspaceId});if(r=0,c=0,!_.item)break;const f=_.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(f,p):R(f.job_id,p.machineId,"cancelled")).catch(()=>{});break}const h=f.session_id;i.has(h)&&e.warn(`claimed job=${f.job_id} for already active session=${h}; running anyway to avoid leaving it stuck`),u.add(f.job_id),i.add(h);const k=new AbortController,P=(t.runJob??fe)(f,p,e,k).catch(d=>{e.warn(`job ${f.job_id} failed outside handler: ${d instanceof Error?d.message:String(d)}`)}).finally(()=>{u.delete(f.job_id),i.delete(h),m.delete(f.job_id)});m.set(f.job_id,{controller:k,promise:P})}}catch(p){if(!n){r+=1;const _=Ne(r);c=Date.now()+_,e.warn(`job worker failed, retry in ${_}ms: ${p instanceof Error?p.message:String(p)}`)}}},l=()=>{if(n||a)return;const y=C();a=y,y.finally(()=>{a===y&&(a=null)})};l();const T=setInterval(()=>{l()},t.claimIntervalMs??oe);return{timer:T,stop:()=>o||(o=(async()=>{if(n)return;n=!0,clearInterval(T);for(const p of m.values())p.controller.abort();await a?.catch(()=>{});for(const p of m.values())p.controller.abort();const y=[...m.values()].map(p=>p.promise);y.length!==0&&await Promise.race([Promise.allSettled(y).then(()=>{}),M(le)])})(),o)}}function de(){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?ce:t===0?0:Math.max(1,Math.min(t,8))}function F(e,t){return t===0||e<t}function me(){const e=Number.parseInt(process.env.ACA_ACP_EVENT_BATCH_INTERVAL_MS||"",10);return Number.isFinite(e)?Math.max(40,Math.min(e,2e3)):ie}async function fe(e,t,n,a){const o=e.job_id,r=e.session_id,c=e.workspace_id,u=e.project_id??null,i=e.input??{},m=String(i.control?.type||"").toLowerCase();if(!m&&!i.message?.trim()&&(i.attachments??[]).length===0){await R(o,t.machineId,"job input missing message");return}const C=t.projects.find(d=>d.projectId===u),l=ue(C?.rootPath||e.project_root_path||"");if(!l){await R(o,t.machineId,"job project root is unknown");return}pe(e)&&$.mkdirSync(l,{recursive:!0}),n.info(`claimed job=${o} session=${r} project=${C?.name??u??"unknown"}`);let T=!1,y=!1;const p=setInterval(()=>{T||(T=!0,I("POST",`/api/client/jobs/${encodeURIComponent(o)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}).finally(()=>{T=!1})),y||(y=!0,Me(o,a).catch(()=>{}).finally(()=>{y=!1}))},ae);let _=0;const f=_e(o,t.machineId),h=Te(o,t.machineId),k=new Set;let P=null;try{await h.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const d=X({projectId:u,sessionId:r,projectRoot:l,worktree:e});P=d,await Ee(o,t.machineId,d);const g=we(i.acpRun);if(m==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const s=await q({agentType:v(g.agentType,"codex"),cliType:v(g.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:d.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,timeoutMs:i.timeoutMs,allowRollover:i.control?.allowRollover===!0,signal:a.signal,onStatus:b=>{h.report({phase:b.phase,label:b.label,detail:b.detail,updateType:b.updateType,raw:b})}});await h.flush(),await I("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:s.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:i.control?.allowRollover===!0},acpSessionId:s.acpSessionId,durationMs:s.durationMs,usage:s.contextUsage,maintenance:s.maintenance,worktree:x(d)}}),n.info(`completed context compact job=${o}`);return}if(m==="fork"){const s=String(i.control?.sourceAcpSessionId||"").trim();if(!s)throw new Error("Source session does not have a Codex ACP context to fork");await h.sendNow({phase:"forking",label:"正在分叉 Codex 会话",detail:i.control?.sourceSessionId?`源会话 ${i.control.sourceSessionId}`:void 0}).catch(()=>{});const b=await D({agentType:v(g.agentType,"codex"),cliType:v(g.cliType,"builtin"),sourceProviderSessionId:s,cwd:d.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,timeoutMs:i.timeoutMs,signal:a.signal,onStatus:A=>{h.report({phase:A.phase,label:A.label,detail:A.detail,updateType:A.updateType,raw:A})}});await h.flush(),await I("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:b.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:i.control?.sourceSessionId||null},acpSessionId:b.acpSessionId,worktree:x(d)}}),n.info(`completed Codex session fork job=${o}`);return}const S=await ke({cwd:d.cwd,sessionId:r,jobId:o,attachments:i.attachments??[]});S.length>0&&(n.info(`materialized ${S.length} attachment(s) for job=${o}`),await h.sendNow({phase:"initializing",label:"附件已写入本机",detail:S.map(s=>s.relativePath).join(", "),raw:{attachments:J(S)}}).catch(()=>{}));const N=Ae(i,S),L=Ce(N,S),w=await H({agentType:v(g.agentType,"codex"),cliType:v(g.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:he(e.session_raw),prompt:N,promptBlocks:L,cwd:d.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,canExecuteTools:i.canExecuteTools,timeoutMs:i.timeoutMs,contextWindow:ye(g.contextWindow),signal:a.signal,onStatus:s=>{h.report({phase:s.phase,label:s.label,detail:s.detail,updateType:s.updateType,raw:s})},onUpdate:s=>{_+=1;for(const A of Q(s.raw))k.add(A);const b={turnId:e.user_turn_id??null,eventType:s.type,label:s.label,text:s.text,status:s.status,toolCallId:s.toolCallId,raw:Se(s.raw,_)};te(b)&&f.push(b)},onPermissionRequest:s=>{const b=Y(i,s);return b?Promise.resolve(b):je(o,t.machineId,s,a.signal)},onUserInputRequest:s=>$e(o,t.machineId,s,a.signal)});await f.flush(),await h.flush();const W=O({cwd:d.cwd,evidencePaths:k}),E=be(w.promptResponse);if(!(Ie(E)||!!w.content)){await R(o,t.machineId,`${w.agentType} stopped: ${E??"unknown"}`,{acpSessionId:w.acpSessionId,stopReason:E,exitCode:w.exitCode,stderr:Oe(w.stderr,4e3),worktree:x(d)});return}await I("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:w.acpSessionId,assistantText:w.content,result:{ok:!0,acpSessionId:w.acpSessionId,stopReason:E,durationMs:w.durationMs,acpRun:i.acpRun??null,effectiveConfig:w.effectiveConfig??null,performance:w.performance??null,usage:w.contextUsage??null,updateCount:w.updateCount,statusTimeline:w.statusTimeline,worktree:x(d),attachments:J(S),files:W}}),n.info(`completed job=${o}`)}catch(d){await f.flush(),await h.flush();const g=a.signal.aborted?"cancelled":d instanceof Error?d.message:String(d),S=P?O({cwd:P.cwd,evidencePaths:k}):[];await R(o,t.machineId,ne(g),{rawError:g,files:S})}finally{await f.flush(),await h.flush(),clearInterval(p)}}function he(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 we(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function ye(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function v(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function be(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 Ie(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function _e(e,t){let n=[],a=null,o=Promise.resolve();const r=async i=>{i.length!==0&&await I("POST",`/api/client/jobs/${encodeURIComponent(e)}/events/batch`,{machineId:t,events:i})},c=()=>{a&&(clearTimeout(a),a=null);const i=n;return n=[],o=o.then(()=>r(i)).catch(()=>{}),o},u=()=>{a||(a=setTimeout(()=>{c()},me()),a.unref())};return{push(i){if(ee(n,i),ge(i)||n.length>=re){c();return}u()},flush:c}}function ge(e){return e.eventType==="tool_call"?!0:e.eventType!=="tool_call_update"?!1:["completed","failed","cancelled"].includes(String(e.status||"").toLowerCase())}function Te(e,t){let n=null,a=0,o="",r=null,c=Promise.resolve();const u=l=>JSON.stringify({phase:l.phase??"",label:l.label??"",detail:l.detail??"",updateType:l.updateType??""}),i=async l=>{await I("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:l.phase,label:l.label,detail:l.detail,updateType:l.updateType,raw:l.raw}),a=Date.now(),o=u(l)},m=()=>{if(r&&(clearTimeout(r),r=null),!n)return c;const l=n;return n=null,c=c.then(()=>i(l)).catch(()=>{}),c},C=l=>{r||(r=setTimeout(()=>{m()},l),r.unref())};return{report(l){const T=Date.now(),p=u(l)!==o,_=T-a>=se;if(!p&&!_)return;n=l;const f=T-a;if(f>=B||a===0){m();return}C(B-f)},sendNow(l){return n=l,m()},flush:m}}function Se(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}function Ae(e,t=[]){const n=e?.message?.trim()??"";if(t.length===0)return n;const o=["用户随消息上传了以下附件,aca 已将附件写入本机工作区。需要读取附件时请直接使用下面的 localPath;图片附件也已作为 ACP image block 随本轮请求传入。",...t.map((r,c)=>`${c+1}. ${r.name} (${r.kind}, ${r.mimeType}, ${Re(r.size)})
|
|
1
|
+
import H from"node:crypto";import $ from"node:fs";import j from"node:path";import{compactAgentContext as G,forkAgentSession as K,sendAgentPrompt as X}from"../acp/agent.js";import{acaServerDownload as Z,acaServerRequest as y}from"./aca-server-client.js";import{loadAcaConfig as Q}from"./aca-config.js";import{prepareLocalWorktree as Y}from"./worktree.js";import{expandWorkerRuntimePath as ee}from"./worker-runtime-paths.js";import{collectAffectedFiles as O,extractAffectedFilePathsFromAcpRaw as te}from"./affected-files.js";import{resolveJobPermissionPolicy as ne}from"./job-permission-policy.js";import{appendCoalescedAcpEvent as oe}from"./acp-event-coalescer.js";import{shouldUploadAcpEvent as ae}from"./acp-event-upload-filter.js";import{userFacingAcpError as ie}from"../acp/error-display.js";const re=1500,F=5e3,U=1e3,se=200,ce=64,z=900,le=1e4,ue=0,de=12e3;function pe(e){return ee(e)}function me(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 nt(e,t={}){let n=!1,o=null,a=null,r=0,s=0;const u=new Set,i=new Map,d=new Map,_=async()=>{if(n||Date.now()<s)return;const w=we();if(B(u.size,w))try{const m=t.loadConfig?.()??Q();if(!m.token)return;for(;!n&&B(u.size,w);){const g=t.claimNextJob?await t.claimNextJob(m,[...i.keys()]):await y("POST","/api/client/jobs/claim",{machineId:m.machineId,workspaceId:m.defaultWorkspaceId,activeSessionIds:[...i.keys()]});if(r=0,s=0,!g.item)break;const h=g.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(h,m):x(h.job_id,m.machineId,"cancelled")).catch(()=>{});break}const f=h.session_id,P=i.get(f);u.add(h.job_id);const v=new AbortController;let p;p=(async()=>{P&&(e.warn(`claimed job=${h.job_id} for already active session=${f}; waiting for the previous run to release its ACP runtime`),await fe(P,h,m,v)),!v.signal.aborted&&await(t.runJob??ye)(h,m,e,v)})().catch(I=>{e.warn(`job ${h.job_id} failed outside handler: ${I instanceof Error?I.message:String(I)}`)}).finally(()=>{u.delete(h.job_id),i.get(f)===p&&i.delete(f),d.delete(h.job_id)}),i.set(f,p),d.set(h.job_id,{controller:v,promise:p})}}catch(m){if(!n){r+=1;const g=Be(r);s=Date.now()+g,e.warn(`job worker failed, retry in ${g}ms: ${m instanceof Error?m.message:String(m)}`)}}},A=()=>{if(n||o)return;const w=_();o=w,w.finally(()=>{o===w&&(o=null)})};A();const l=setInterval(()=>{A()},t.claimIntervalMs??re);return{timer:l,stop:()=>a||(a=(async()=>{if(n)return;n=!0,clearInterval(l);for(const m of d.values())m.controller.abort();await o?.catch(()=>{});for(const m of d.values())m.controller.abort();const w=[...d.values()].map(m=>m.promise);w.length!==0&&await Promise.race([Promise.allSettled(w).then(()=>{}),M(de)])})(),a)}}async function fe(e,t,n,o){let a=!1;const s=setInterval(()=>{a||o.signal.aborted||(a=!0,Promise.allSettled([y("POST",`/api/client/jobs/${encodeURIComponent(t.job_id)}/heartbeat`,{machineId:n.machineId}),W(t.job_id,o)]).finally(()=>{a=!1}))},F);try{await Promise.race([e,he(o.signal)])}finally{clearInterval(s)}}function he(e){return e.aborted?Promise.resolve():new Promise(t=>e.addEventListener("abort",()=>t(),{once:!0}))}function we(){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?ue:t===0?0:Math.max(1,Math.min(t,8))}function B(e,t){return t===0||e<t}function be(){const e=Number.parseInt(process.env.ACA_ACP_EVENT_BATCH_INTERVAL_MS||"",10);return Number.isFinite(e)?Math.max(40,Math.min(e,2e3)):se}async function ye(e,t,n,o){const a=e.job_id,r=e.session_id,s=e.workspace_id,u=e.project_id??null,i=e.input??{},d=String(i.control?.type||"").toLowerCase();if(!d&&!i.message?.trim()&&(i.attachments??[]).length===0){await x(a,t.machineId,"job input missing message");return}const _=t.projects.find(p=>p.projectId===u),A=pe(_?.rootPath||e.project_root_path||"");if(!A){await x(a,t.machineId,"job project root is unknown");return}me(e)&&$.mkdirSync(A,{recursive:!0}),n.info(`claimed job=${a} session=${r} project=${_?.name??u??"unknown"}`);let l=!1,w=!1;const m=setInterval(()=>{l||(l=!0,y("POST",`/api/client/jobs/${encodeURIComponent(a)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}).finally(()=>{l=!1})),w||(w=!0,W(a,o).catch(()=>{}).finally(()=>{w=!1}))},F);let g=0;const h=Ae(a,t.machineId),f=Ce(a,t.machineId),P=new Set;let v=null;try{await f.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const p=Y({projectId:u,sessionId:r,projectRoot:A,worktree:e});v=p,await Ne(a,t.machineId,p);const I=Se(i.acpRun);if(d==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const c=await G({agentType:k(I.agentType,"codex"),cliType:k(I.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:p.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,timeoutMs:i.timeoutMs,allowRollover:i.control?.allowRollover===!0,signal:o.signal,onStatus:S=>{f.report({phase:S.phase,label:S.label,detail:S.detail,updateType:S.updateType,raw:S})}});await f.flush(),await y("POST",`/api/client/jobs/${encodeURIComponent(a)}/complete`,{machineId:t.machineId,acpSessionId:c.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:i.control?.allowRollover===!0},acpSessionId:c.acpSessionId,durationMs:c.durationMs,usage:c.contextUsage,maintenance:c.maintenance,worktree:R(p)}}),n.info(`completed context compact job=${a}`);return}if(d==="fork"){const c=String(i.control?.sourceAcpSessionId||"").trim();if(!c)throw new Error("Source session does not have a Codex ACP context to fork");await f.sendNow({phase:"forking",label:"正在分叉 Codex 会话",detail:i.control?.sourceSessionId?`源会话 ${i.control.sourceSessionId}`:void 0}).catch(()=>{});const S=await K({agentType:k(I.agentType,"codex"),cliType:k(I.cliType,"builtin"),sourceProviderSessionId:c,cwd:p.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,timeoutMs:i.timeoutMs,signal:o.signal,onStatus:C=>{f.report({phase:C.phase,label:C.label,detail:C.detail,updateType:C.updateType,raw:C})}});await f.flush(),await y("POST",`/api/client/jobs/${encodeURIComponent(a)}/complete`,{machineId:t.machineId,acpSessionId:S.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:i.control?.sourceSessionId||null},acpSessionId:S.acpSessionId,worktree:R(p)}}),n.info(`completed Codex session fork job=${a}`);return}const T=await xe({cwd:p.cwd,sessionId:r,jobId:a,attachments:i.attachments??[]});T.length>0&&(n.info(`materialized ${T.length} attachment(s) for job=${a}`),await f.sendNow({phase:"initializing",label:"附件已写入本机",detail:T.map(c=>c.relativePath).join(", "),raw:{attachments:L(T)}}).catch(()=>{}));const N=ke(i,T),q=Re(N,T),b=await X({agentType:k(I.agentType,"codex"),cliType:k(I.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:Ie(e.session_raw),prompt:N,promptBlocks:q,cwd:p.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,canExecuteTools:i.canExecuteTools,timeoutMs:i.timeoutMs,contextWindow:ge(I.contextWindow),signal:o.signal,onStatus:c=>{f.report({phase:c.phase,label:c.label,detail:c.detail,updateType:c.updateType,raw:c})},onUpdate:c=>{g+=1;for(const C of te(c.raw))P.add(C);const S={turnId:e.user_turn_id??null,eventType:c.type,label:c.label,text:c.text,status:c.status,toolCallId:c.toolCallId,raw:Pe(c.raw,g)};ae(S)&&h.push(S)},onPermissionRequest:c=>{const S=ne(i,c);return S?Promise.resolve(S):Oe(a,t.machineId,c,o.signal)},onUserInputRequest:c=>Fe(a,t.machineId,c,o.signal)});await h.flush(),await f.flush();const D=O({cwd:p.cwd,evidencePaths:P}),E=_e(b.promptResponse);if(!(Te(E)||!!b.content)){await x(a,t.machineId,`${b.agentType} stopped: ${E??"unknown"}`,{acpSessionId:b.acpSessionId,stopReason:E,exitCode:b.exitCode,stderr:Je(b.stderr,4e3),worktree:R(p)});return}await y("POST",`/api/client/jobs/${encodeURIComponent(a)}/complete`,{machineId:t.machineId,acpSessionId:b.acpSessionId,assistantText:b.content,result:{ok:!0,acpSessionId:b.acpSessionId,stopReason:E,durationMs:b.durationMs,acpRun:i.acpRun??null,effectiveConfig:b.effectiveConfig??null,performance:b.performance??null,usage:b.contextUsage??null,updateCount:b.updateCount,statusTimeline:b.statusTimeline,worktree:R(p),attachments:L(T),files:D}}),n.info(`completed job=${a}`)}catch(p){if(ze(o.signal)){h.discard(),f.discard(),n.warn(`stopped local job=${a} because ACA Server already finalized it`);return}await h.flush(),await f.flush();const I=o.signal.aborted?"cancelled":p instanceof Error?p.message:String(p),T=v?O({cwd:v.cwd,evidencePaths:P}):[];await x(a,t.machineId,ie(I),{rawError:I,files:T})}finally{await h.flush(),await f.flush(),clearInterval(m)}}function Ie(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 Se(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function ge(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 _e(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 Te(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function Ae(e,t){let n=[],o=null,a=Promise.resolve(),r=!1;const s=async d=>{d.length!==0&&await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/events/batch`,{machineId:t,events:d})},u=()=>{if(o&&(clearTimeout(o),o=null),r)return n=[],a;const d=n;return n=[],a=a.then(()=>s(d)).catch(()=>{}),a},i=()=>{o||(o=setTimeout(()=>{u()},be()),o.unref())};return{push(d){if(!r){if(oe(n,d),ve(d)||n.length>=ce){u();return}i()}},flush:u,discard(){r=!0,n=[],o&&clearTimeout(o),o=null}}}function ve(e){return e.eventType==="tool_call"?!0:e.eventType!=="tool_call_update"?!1:["completed","failed","cancelled"].includes(String(e.status||"").toLowerCase())}function Ce(e,t){let n=null,o=0,a="",r=null,s=Promise.resolve(),u=!1;const i=l=>JSON.stringify({phase:l.phase??"",label:l.label??"",detail:l.detail??"",updateType:l.updateType??""}),d=async l=>{await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:l.phase,label:l.label,detail:l.detail,updateType:l.updateType,raw:l.raw}),o=Date.now(),a=i(l)},_=()=>{if(r&&(clearTimeout(r),r=null),u)return n=null,s;if(!n)return s;const l=n;return n=null,s=s.then(()=>d(l)).catch(()=>{}),s},A=l=>{r||(r=setTimeout(()=>{_()},l),r.unref())};return{report(l){if(u)return;const w=Date.now(),g=i(l)!==a,h=w-o>=le;if(!g&&!h)return;n=l;const f=w-o;if(f>=z||o===0){_();return}A(z-f)},sendNow(l){return u?s:(n=l,_())},flush:_,discard(){u=!0,n=null,r&&clearTimeout(r),r=null}}}function Pe(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}function ke(e,t=[]){const n=e?.message?.trim()??"";if(t.length===0)return n;const a=["用户随消息上传了以下附件,aca 已将附件写入本机工作区。需要读取附件时请直接使用下面的 localPath;图片附件也已作为 ACP image block 随本轮请求传入。",...t.map((r,s)=>`${s+1}. ${r.name} (${r.kind}, ${r.mimeType}, ${Me(r.size)})
|
|
2
2
|
localPath: ${r.path}
|
|
3
3
|
workspacePath: ${r.relativePath}`)].join(`
|
|
4
4
|
`);return n?`${n}
|
|
5
5
|
|
|
6
|
-
${
|
|
6
|
+
${a}`:a}function Re(e,t){const n=[{type:"text",text:e}];for(const o of t)o.kind!=="image"||!o.mimeType.startsWith("image/")||n.push({type:"image",data:o.base64,mimeType:o.mimeType,uri:o.path});return n}async function xe(e){if(e.attachments.length===0)return[];const t=j.join(e.cwd,".aca","attachments",J(e.sessionId),J(e.jobId));$.mkdirSync(t,{recursive:!0});const n=[];for(const[o,a]of e.attachments.entries()){const r=a.attachmentId?{mimeType:a.mimeType||"application/octet-stream",bytes:await Z(`/api/client/attachments/${encodeURIComponent(a.attachmentId)}`)}:Ee(a.dataUrl??""),s=a.mimeType||r.mimeType||"application/octet-stream",u=a.kind==="image"||s.startsWith("image/")?"image":"file",i=je(a.name||`attachment-${o+1}`,s),d=j.join(t,`${String(o+1).padStart(2,"0")}-${i}`);$.writeFileSync(d,r.bytes),n.push({name:a.name||i,mimeType:s,size:r.bytes.byteLength,kind:u,path:d,relativePath:j.relative(e.cwd,d),base64:r.bytes.toString("base64"),sha256:H.createHash("sha256").update(r.bytes).digest("hex")})}return n}function Ee(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),o=e.slice(t+1),a=n.split(";").filter(Boolean),r=a[0]?.includes("/")?a[0]:"application/octet-stream";if(!a.includes("base64"))throw new Error("attachment dataUrl is not base64 encoded");return{mimeType:r,bytes:Buffer.from(o,"base64")}}function je(e,t){const n=j.basename(e).replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").trim(),o=`attachment${$e(t)}`;return(!n||n==="."||n===".."?o:n).slice(0,180)}function $e(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 J(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"item"}function L(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 Me(e){if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KB","MB","GB"];let n=e,o=0;for(;n>=1024&&o<t.length-1;)n/=1024,o+=1;return`${n>=10||o===0?n.toFixed(0):n.toFixed(1)} ${t[o]}`}async function Ne(e,t,n){!n.usedWorktree||!n.worktreeId||await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/worktree`,{machineId:t,baseCommit:n.baseCommit??null,worktreePath:n.cwd,status:n.status??"active",raw:n.raw??R(n)})}function R(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 Oe(e,t,n,o){const r=(await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:n})).item.permission_id;for(;!o.aborted;){await M(U);const s=await y("GET",`/api/client/permissions/${encodeURIComponent(r)}`);if(s.item.status==="resolved"&&s.item.response){const u=s.item.response.outcome;return u.outcome==="selected"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function Fe(e,t,n,o){const r=(await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:{kind:"elicitation",...n}})).item.permission_id;for(;!o.aborted;){await M(U);const s=await y("GET",`/api/client/permissions/${encodeURIComponent(r)}`);if(s.item.status==="resolved"&&s.item.response){const u=s.item.response.outcome;return u.outcome==="submitted"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function W(e,t){if(t.signal.aborted)return;const n=await y("GET",`/api/client/jobs/${encodeURIComponent(e)}`);if(Number(n.item.cancel_requested??0)===1){t.abort();return}const o=Ue(n.item);o&&t.abort(new V(e,o))}function Ue(e){const t=String(e.status||"").toLowerCase();return t&&!["claimed","running"].includes(t)?t:null}class V extends Error{constructor(t,n){super(`ACA Server already finalized job=${t} status=${n}`),this.name="ServerFinalizedJobError"}}function ze(e){return e.aborted&&e.reason instanceof V}async function x(e,t,n,o){await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/fail`,{machineId:t,error:n,result:o})}function M(e){return new Promise(t=>setTimeout(t,e))}function Be(e){return Math.min(1e4,500*2**Math.min(5,Math.max(0,e-1)))+Math.floor(Math.random()*250)}function Je(e,t){return e.length<=t?e:`${e.slice(0,t)}...`}export{Ue as serverFinalizedJobStatus,nt as startJobWorkerLoop};
|
package/dist/worker-cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as a}from"commander";import m from"node:path";import{createStartCommand as n}from"./commands/start.js";import{createDaemonCommand as t}from"./commands/daemon.js";import{createWorkerAuthCommand as s}from"./commands/worker-auth.js";import{createWorkerCodexCommand as c}from"./commands/worker-codex.js";import{createWorkerProjectCommand as i}from"./commands/worker-project.js";import{DEFAULT_ACA_SERVER_URL as d}from"./core/aca-config.js";process.env.ACA_DEFAULT_SERVER_URL||=d;async function p(r=process.argv){const o=new a;o.name("aica").description("AICA remote Agent worker").version("0.1.
|
|
2
|
+
import{Command as a}from"commander";import m from"node:path";import{createStartCommand as n}from"./commands/start.js";import{createDaemonCommand as t}from"./commands/daemon.js";import{createWorkerAuthCommand as s}from"./commands/worker-auth.js";import{createWorkerCodexCommand as c}from"./commands/worker-codex.js";import{createWorkerProjectCommand as i}from"./commands/worker-project.js";import{DEFAULT_ACA_SERVER_URL as d}from"./core/aca-config.js";process.env.ACA_DEFAULT_SERVER_URL||=d;async function p(r=process.argv){const o=new a;o.name("aica").description("AICA remote Agent worker").version("0.1.24").enablePositionalOptions(),o.addCommand(n()),o.addCommand(t()),o.addCommand(s()),o.addCommand(i()),o.addCommand(c()),await o.parseAsync(r)}const C=process.argv[1]||"",e=m.basename(C).replace(/\.(?:js|ts|cjs|mjs)$/i,"");(e==="worker-cli"||e==="aica"||e==="aca")&&p().catch(r=>{const o=r instanceof Error?r.message:String(r);console.error(o),process.exitCode=1});export{p as runWorkerCli};
|