@yuandc/aica 0.1.88 → 0.1.90
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/acp/client/json-rpc.js +1 -1
- package/dist/acp/providers/workbuddy/auth-transport.js +3 -3
- package/dist/acp/providers/workbuddy/commands.js +1 -0
- package/dist/acp/providers/workbuddy/config.js +1 -1
- package/dist/acp/providers/workbuddy/errors.js +1 -0
- package/dist/acp/providers/workbuddy/fork.js +1 -0
- package/dist/acp/providers/workbuddy/provider.js +1 -1
- package/dist/acp/providers/workbuddy/session-lock.js +1 -0
- package/dist/acp/providers/workbuddy/usage.js +1 -0
- package/dist/commands/start.js +1 -1
- package/dist/core/heartbeat.js +3 -3
- package/dist/core/job-execution-config.js +1 -0
- package/dist/core/job-worker.js +1 -1
- package/dist/core/machine-filesystem-worker.js +4 -4
- package/dist/core/workbuddy-auth-worker.js +1 -1
- package/dist/core/workbuddy-credits/query.js +1 -0
- package/dist/core/workbuddy-credits/scheduler.js +1 -0
- package/dist/core/workbuddy-credits/types.js +1 -0
- package/dist/core/workbuddy-credits/worker.js +1 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import{appendBoundedText as u}from"./bounded-buffer.js";class s{child;handlers;static MAX_STDOUT_BYTES=1024*1024;static MAX_STDERR_BYTES=1024*1024;nextId=1;lineBuffer="";pending=new Map;peerName;includeJsonRpc;stdoutBuffer="";stderrBuffer="";notificationCount=0;exitCode=null;signal=null;constructor(e,t={}){this.child=e,this.handlers=t,this.peerName=t.peerName||"JSON-RPC peer",this.includeJsonRpc=t.includeJsonRpc===!0,e.stdout.on("data",r=>this.handleStdout(String(r))),e.stderr.on("data",r=>{this.stderr=this.stderr+String(r)}),e.on("close",(r,i)=>{this.exitCode=r,this.signal=i;for(const[n,o]of this.pending)clearTimeout(o.timer),o.reject(new Error(`${this.peerName} exited before ${o.method} completed (code=${r}, signal=${i})`)),this.pending.delete(n)}),e.on("error",r=>{for(const[i,n]of this.pending)clearTimeout(n.timer),n.reject(r),this.pending.delete(i)})}get stdout(){return this.stdoutBuffer}get stderr(){return this.stderrBuffer}set stderr(e){this.stderrBuffer=u("",e,s.MAX_STDERR_BYTES)}request(e,t,r=12e4){const i=this.nextId++;return new Promise((n,o)=>{const d=this.createRequestTimeout(i,e,r,o);d.unref(),this.pending.set(i,{method:e,resolve:n,reject:o,timer:d,timeoutMs:r}),this.write({...this.includeJsonRpc?{jsonrpc:"2.0"}:{},id:i,method:e,...t===void 0?{}:{params:t}})})}notify(e,t){this.write({...this.includeJsonRpc?{jsonrpc:"2.0"}:{},method:e,...t===void 0?{}:{params:t}})}refreshPendingRequestTimeout(e){for(const[t,r]of this.pending)r.method===e&&(clearTimeout(r.timer),r.timer=this.createRequestTimeout(t,r.method,r.timeoutMs,r.reject),r.timer.unref())}respond(e,t){this.write({...this.includeJsonRpc?{jsonrpc:"2.0"}:{},id:e,result:t})}respondError(e,t){this.write({...this.includeJsonRpc?{jsonrpc:"2.0"}:{},id:e,error:{code:-32603,message:t.message}})}createRequestTimeout(e,t,r,i){return setTimeout(()=>{this.pending.delete(e),i(new Error(`${this.peerName} request timed out while waiting for ${t}`))},r)}handleStdout(e){this.stdoutBuffer=u(this.stdoutBuffer,e,s.MAX_STDOUT_BYTES),this.lineBuffer+=e;const t=this.lineBuffer.split(`
|
|
2
2
|
`);this.lineBuffer=t.pop()??"";for(const r of t){const i=r.trim();if(!i)continue;let n;try{n=JSON.parse(i)}catch{this.stderr+=`
|
|
3
|
-
[non-json stdout] ${i}`;continue}this.handleMessage(n)}}async handleMessage(e){if("id"in e&&("result"in e||"error"in e)&&typeof e.method!="string"){const t=Number(e.id),r=this.pending.get(t);if(!r)return;if(this.pending.delete(t),clearTimeout(r.timer),"error"in e){const i=e.error;r.reject(Object.assign(new Error(i?.message||`${this.peerName} error ${i?.code??"unknown"}`),{rpcResponse:!0,rpcCode:i?.code}))}else r.resolve(e.result);return}if(typeof e.method=="string"){if("id"in e){try{const t=await this.handlers.onRequest?.(e.method,e.params,e);this.respond(e.id,t??null)}catch(t){this.respondError(e.id,t instanceof Error?t:new Error(String(t)))}return}this.notificationCount+=1,await this.handlers.onNotification?.(e.method,e.params,e)}}write(e){this.child.stdin.write(`${JSON.stringify(e)}
|
|
3
|
+
[non-json stdout] ${i}`;continue}this.handleMessage(n)}}async handleMessage(e){if("id"in e&&("result"in e||"error"in e)&&typeof e.method!="string"){const t=Number(e.id),r=this.pending.get(t);if(!r)return;if(this.pending.delete(t),clearTimeout(r.timer),"error"in e){const i=e.error;r.reject(Object.assign(new Error(i?.message||`${this.peerName} error ${i?.code??"unknown"}`),{rpcResponse:!0,rpcCode:i?.code,rpcData:i?.data}))}else r.resolve(e.result);return}if(typeof e.method=="string"){if("id"in e){try{const t=await this.handlers.onRequest?.(e.method,e.params,e);this.respond(e.id,t??null)}catch(t){this.respondError(e.id,t instanceof Error?t:new Error(String(t)))}return}this.notificationCount+=1,await this.handlers.onNotification?.(e.method,e.params,e)}}write(e){this.child.stdin.write(`${JSON.stringify(e)}
|
|
4
4
|
`)}}export{s as JsonLineRpcClient};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
`))>=0;){const
|
|
3
|
-
`)})}}}export{M as createWorkBuddyAuthTransport,
|
|
1
|
+
import _ from"node:fs";import U from"node:os";import v from"node:path";import{randomUUID as W}from"node:crypto";import{startAcpProcess as B,closeAcpProcess as T}from"../../client/acp-process.js";import{JsonLineRpcClient as R}from"../../client/json-rpc.js";import{resolveWorkBuddyLaunch as A}from"./launch.js";import{record as r}from"./config.js";import{workBuddyRegionEnv as N}from"./auth-environment.js";function b(c){const s=r(c),e=typeof s.userId=="string"?s.userId.slice(0,200):"",n=[s.userNickname,s.userName].find(i=>typeof i=="string"&&i.trim());return e||n?{id:e,name:typeof n=="string"?n.slice(0,200):e}:void 0}function $(c,s){if(typeof c!="string"||c.length>8192)throw new Error("invalid_auth_url");const e=new URL(c),n=["tencent.com","codebuddy.ai","codebuddy.cn"];if(e.protocol!=="https:"||e.username||e.password||e.port&&e.port!=="443"||!n.some(o=>e.hostname===o||e.hostname.endsWith(`.${o}`)))throw new Error("invalid_auth_url");const i=e.hostname==="codebuddy.ai"||e.hostname.endsWith(".codebuddy.ai"),m=e.hostname==="codebuddy.cn"||e.hostname.endsWith(".codebuddy.cn")||e.hostname==="copilot.tencent.com";if(s==="internal"&&i||s==="external"&&m)throw new Error("auth_region_mismatch");return e.href}function M(c,s,e=!1){const n=A(!1),i=_.mkdtempSync(v.join(U.tmpdir(),"aca-workbuddy-auth-")),m=e?n.args.flatMap(t=>t==="--acp"?["--print","--input-format","stream-json","--output-format","stream-json","--verbose"]:[t]):n.args;let o;try{o=B(n.command,[...m,"--no-session-persistence"],i,{...n.env,...N(c),BROWSER:v.join(i,"no-local-browser")})}catch(t){throw _.rmSync(i,{recursive:!0,force:!0}),t}let E;const q=()=>E??=T(o.child).finally(()=>_.rmSync(i,{recursive:!0,force:!0})),f=e?null:new R(o.child,{includeJsonRpc:!0,onNotification(t,d){t==="_codebuddy.ai/authUrl"&&s?.(String(r(d).authUrl||""))}});return{close:q,async initialize(){return r(await f.request("initialize",{protocolVersion:1,clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1},terminal:!1},clientInfo:{name:"aca-auth",version:"1"}},12e3))},async account(){return b(r(await f.request("_codebuddy.ai/getUserInfo",{},12e3)).userInfo)},async billingIdentity(){const t=r(r(await f.request("_codebuddy.ai/getUserInfo",{},12e3)).userInfo);return{account:b(t),token:typeof t.accessToken=="string"?t.accessToken:typeof t.token=="string"?t.token:"",enterpriseId:typeof t.enterpriseId=="string"?t.enterpriseId:""}},async authenticate(t,d){const p=r(await f.request("authenticate",{methodId:t},d));return b(r(p._meta)["codebuddy.ai/userinfo"])},async logout(){const t=W();await new Promise((d,p)=>{let a="";const x=setTimeout(()=>l(new Error("logout_timeout")),15e3),l=u=>{clearTimeout(x),o.child.stdout.off("data",I),o.child.off("error",h),o.child.off("close",k),u?p(u):d()},h=u=>l(u),k=()=>l(new Error("logout_process_exited")),I=u=>{if(a+=u.toString(),Buffer.byteLength(a)>1024*1024)return l(new Error("invalid_auth_response"));let y;for(;(y=a.indexOf(`
|
|
2
|
+
`))>=0;){const S=a.slice(0,y);a=a.slice(y+1);let w;try{w=r(JSON.parse(S))}catch{continue}const g=r(w.response);if(!(w.type!=="control_response"||g.request_id!==t))return l(g.subtype==="success"&&r(g.response).success===!0?void 0:new Error("logout_failed"))}};o.child.stdout.on("data",I),o.child.stderr.resume(),o.child.on("error",h),o.child.on("close",k),o.child.stdin.on("error",h),o.child.stdin.write(`${JSON.stringify({type:"control_request",request_id:t,request:{subtype:"logout"}})}
|
|
3
|
+
`)})}}}export{M as createWorkBuddyAuthTransport,b as publicWorkBuddyAccount,$ as safeWorkBuddyAuthUrl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{record as n}from"./config.js";function a(o){if(!Array.isArray(o))return[];const i=new Set,c=[];for(const p of o.slice(0,500)){const e=n(p),t=String(e.name||"").replace(/^\/+/,"");if(!/^[\p{L}\p{N}_][\p{L}\p{N}_.:/-]{0,127}$/u.test(t)||i.has(t.toLowerCase()))continue;i.add(t.toLowerCase());const r=n(e.input).hint,s=n(e._meta);c.push({name:t,description:String(e.description||"").slice(0,500),...typeof r=="string"&&r?{input:{hint:r.slice(0,200)}}:{},...s.type==="skill"?{_meta:{type:"skill",...typeof s.source=="string"?{source:s.source.slice(0,100)}:{}}}:{}})}return c}export{a as workBuddyCommands};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveWorkBuddyLaunch as _}from"./launch.js";function s(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}const m=[{id:"default",name:"询问授权"},{id:"acceptEdits",name:"自动接受文件修改"},{id:"plan",name:"规划"},{id:"bypassPermissions",name:"完全访问"}];let
|
|
1
|
+
import{resolveWorkBuddyLaunch as _}from"./launch.js";function s(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}const m=[{id:"default",name:"询问授权"},{id:"acceptEdits",name:"自动接受文件修改"},{id:"plan",name:"规划"},{id:"bypassPermissions",name:"完全访问"}];let d=null;function O(){d=null}function b(){return(d?.identity===g()&&d.expiresAtMs>Date.now()?d.value:null)??{source:"aca-static",contextUsage:!0,contextCompaction:!0,liveSupplement:!0,sessionResume:!0,sessionFork:!0,sessionForkScope:"same-directory",modelCatalogAuthoritative:!0,models:[{modelId:process.env.ACA_WORKBUDDY_DEFAULT_MODEL||"default",name:"默认模型"}],modes:m,configOptions:[{id:"model",category:"model",type:"select",currentValue:process.env.ACA_WORKBUDDY_DEFAULT_MODEL||"default",options:[{value:process.env.ACA_WORKBUDDY_DEFAULT_MODEL||"default",name:"默认模型"}]},{id:"mode",category:"mode",type:"select",currentValue:"default",options:m.map(r=>({value:r.id,name:r.name}))},{id:"thought_level",category:"thought_level",type:"select",currentValue:"enabled",options:[{value:"enabled",name:"模型默认"}]}]}}function v(e){const r=p(e).filter(o=>["model","mode","thought_level","context_window"].includes(String(o.id)));if(!r.length)return;const a=s(e.models).availableModels,i=r.find(o=>o.category==="model"||o.id==="model");d={identity:g(),expiresAtMs:Date.now()+360*60*1e3,value:{source:"codebuddy-acp",fetchedAt:Date.now(),modelCatalogAuthoritative:!0,contextUsage:!0,contextCompaction:!0,liveSupplement:!0,sessionResume:!0,sessionFork:!0,sessionForkScope:"same-directory",models:Array.isArray(a)?a:Array.isArray(i?.options)?i.options:[],modes:s(e.modes).availableModes||m,configOptions:r}}}function g(){try{return _().runtimeIdentity}catch{return"unavailable"}}function p(e){return Array.isArray(e.configOptions)?e.configOptions.map(s):[]}function D(e){return!e||e==="agent"?"default":e==="read-only"?"plan":["agent-full-access","full-access","danger-full-access"].includes(e)?"bypassPermissions":e}async function w(e,r,a,i){let o=a;const t={...i.configOptionValues},A=i.canExecuteTools===!1?"default":D(i.mode||String(t.mode||"default"));await e.setSessionMode({sessionId:r,modeId:A});const u=i.model||String(t.model||process.env.ACA_WORKBUDDY_DEFAULT_MODEL||"");u?t.model=u==="default"?"default-model":u:delete t.model,delete t.mode;const y=t.thought_level??t.reasoning_effort??t.effort;if(delete t.reasoning_effort,delete t.effort,y!==void 0&&(t.thought_level=y),t.multitask===!0||t.delegate===!0)throw new Error("WorkBuddy 当前不支持后台多任务或委派模式。");p(o).some(n=>n.id==="multitask"&&n.currentValue===!0)&&(t.multitask=!1);for(const[n,l]of Object.entries(t)){const c=p(o).find(f=>f.id===n);if(!c&&n!=="model")throw new Error(`WorkBuddy 不支持配置项:${n}`);if(!(n==="model"&&l==="default-model")&&Array.isArray(c?.options)&&!c.options.some(f=>s(f).value===l))throw new Error(`WorkBuddy 配置值不可用:${n}=${l}`);const h=s(await e.setSessionConfigOption({sessionId:r,configId:n,value:l}));o={...o,...h}}return v(o),o}export{w as applyWorkBuddyConfig,O as clearWorkBuddyCapabilities,p as configOptions,s as record,v as rememberWorkBuddyCapabilities,b as workBuddyHeartbeatCapabilities,D as workBuddyMode,m as workBuddyModes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{record as u}from"./config.js";class s extends Error{failure;constructor(e,o,t){super(o),this.name="WorkBuddyRunError",this.failure={provider:"workbuddy",category:e,...t!==void 0?{code:t}:{}}}}function a(n){if(typeof n=="string")try{return u(JSON.parse(n))}catch{return{message:n}}return u(n)}function C(n){const e=u(n),o=u(e._meta),t=o["codebuddy.ai/error"]??o["codebuddy.ai/errorMessage"];if(t!=null&&t!=="")throw m(a(t));if(e.stopReason!=="end_turn")throw e.stopReason==="cancelled"?new s("cancelled","腾讯 Code Buddy 任务已取消。"):e.stopReason==="refusal"?new s("refusal","腾讯 Code Buddy 模型拒绝了本次请求,请调整请求内容后重试。"):["max_tokens","max_turn_requests"].includes(String(e.stopReason))?new s("incomplete","腾讯 Code Buddy 已达到本轮输出或请求次数上限,任务尚未完成;请检查已有结果后继续。"):new s("incomplete","腾讯 Code Buddy 未返回有效的完成状态,请检查已有结果后再继续。")}function m(n){if(n instanceof s)return n;const e=u(n),o=a(e.rpcData??e.data),t=a(o["codebuddy.ai/error"]??e["codebuddy.ai/error"]??(typeof e.code=="string"?e:void 0)),i=t.code??e.rpcCode??e.code,r=[t.code,t.message,t.additionalDetails,e.code,e.message,e.additionalDetails,o.category,o.details,o.code,u(o.quotaExceed).subject,u(t.quotaExceed).subject].filter(c=>typeof c=="string"||typeof c=="number").join(" "),f=t.httpStatusCode??e.httpStatusCode??o.statusCode,d=(c,y)=>new s(c,y,typeof i=="string"||typeof i=="number"?i:void 0);if(i==="ENOENT")return d("internal","未找到 CodeBuddy CLI,请安装 @tencent-ai/codebuddy-code,或设置 ACA_WORKBUDDY_COMMAND。");if(/session not found|resource not found/i.test(r))return d("internal","腾讯 Code Buddy 历史会话不存在或已失效,无法恢复;请新建会话。");if(/invalid mode|config rejected|unknown configuration/i.test(r))return d("internal","腾讯 Code Buddy 配置被拒绝,请重新选择可用模型、思考深度或权限配置。");if(/provider_authentication_failed|custom_model_authentication_failed/.test(r))return d("model","腾讯 Code Buddy 的模型凭据无效,请检查模型接口的认证配置。");if(i===-32e3||/authentication.required|not.authenticated|unauthorized|尚未认证|authentication_required/.test(r.toLowerCase()))return d("auth","腾讯 Code Buddy 尚未认证:请到设置 → 我的设备 → 腾讯 Code Buddy 登录;也可在 Worker 的系统账号下执行 codebuddy 登录,或配置 CODEBUDDY_API_KEY。");const l=u(t.quotaExceed??o.quotaExceed).subject;return/(insufficient|not enough).{0,24}(credit|balance)|credit.{0,24}(exhaust|insufficient|not enough)|积分不足|余额不足|credit_balance/i.test(r)||(i===-32003||i==="usage_limit_exceeded")&&/^(credit|credits|balance|积分)$/i.test(String(l||""))?d("credits","腾讯 Code Buddy 积分不足,请查看积分详情并补充积分或切换免费模型后重试。"):/server_overloaded|overload|at capacity|model.{0,20}busy|排队|模型繁忙/i.test(r)?d("model","腾讯 Code Buddy 模型服务繁忙,请稍后重试或切换模型。"):/rate.?limit|too many requests|请求过于频繁/i.test(r)?d("rate_limit","腾讯 Code Buddy 请求过于频繁,请稍后重试。"):i===-32003||f===429||/usage_limit_exceeded|quota|额度|用量上限/i.test(r)?d("quota","腾讯 Code Buddy 可用额度或用量配额已耗尽,请查看积分详情与账号配额。"):/context_window_exceeded|maximum context|context.{0,24}(length|window)|上下文.*上限/i.test(r)?d("context","腾讯 Code Buddy 上下文已超过模型限制,请压缩上下文或新建会话后重试。"):o.category==="cancelled"||/\bcancelled\b|已取消|\baborted\b/i.test(r)?d("cancelled","腾讯 Code Buddy 任务已取消。"):i===-32001||[408,502,503,504].includes(Number(f))||/network|connection|econn|enet|etimedout|fetch failed|timed? ?out|socket|disconnected/i.test(r)?d("network","腾讯 Code Buddy 网络连接异常或响应超时,请检查 Worker 网络后重试;已有执行结果可能仍然有效。"):i===-32004||/model_service|empty_model_response|empty stream|invalid response|malformed tool|custom_model_config/i.test(r)?d("model","腾讯 Code Buddy 模型服务返回异常,请检查模型配置或切换模型后重试。"):/^(WorkBuddy|当前 CodeBuddy|腾讯 Code Buddy|未找到 CodeBuddy)/.test(String(e.message||""))&&e.rpcResponse!==!0?d("internal",String(e.message)):d("internal","腾讯 Code Buddy 执行失败,请检查已有结果、Worker 状态及模型配置后重试。")}export{s as WorkBuddyRunError,C as assertWorkBuddyPromptCompleted,m as workBuddyError};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import A from"node:fs";import{createHash as R,randomUUID as W}from"node:crypto";import{AcpClient as M}from"../../client/acp-client.js";import{closeAcpProcess as P,startAcpProcess as U}from"../../client/acp-process.js";import{defaultAcpRuntimePool as x}from"../../client/acp-runtime-pool.js";import{runAbortableRequest as z}from"../../client/abortable-request.js";import{resolveWorkBuddyLaunch as E}from"./launch.js";import{applyWorkBuddyConfig as q,record as r}from"./config.js";import{workBuddyCommands as Z}from"./commands.js";import{assertWorkBuddyPromptCompleted as $,WorkBuddyRunError as g,workBuddyError as H}from"./errors.js";import{enterWorkBuddySession as J}from"./session-lock.js";function v(e=1/0){const o=R("sha256");let l=0;return{observe(a){const d=r(r(a).update);l>=e||!["user_message_chunk","agent_message_chunk"].includes(String(d.sessionUpdate))||(o.update(JSON.stringify([d.sessionUpdate,d.content])),l++)},get count(){return l},finish:()=>o.digest("hex")}}async function Y(e){if(!A.statSync(e.cwd).isDirectory())throw new Error("CodeBuddy 项目目录不可用。");const o=e.sourceProviderSessionId.trim();if(!o)throw new Error("CodeBuddy 分叉缺少源会话 ID。");const l=J(o);let a;const d=async()=>{a&&await P(a.child)},D=Date.now()+(e.timeoutMs??12e4),c=t=>new g("internal",t),n=()=>{if(e.signal?.aborted)throw new g("cancelled","CodeBuddy 会话分叉已取消。");const t=D-Date.now();if(t<=0)throw new g("network","CodeBuddy 会话分叉响应超时。");return t};try{return await z({signal:e.signal,cancelledMessage:"CodeBuddy 会话分叉已取消。",onAbort:d,startRequest:async()=>{const t=E(!1);n(),await x.retireSessionRuntimes({providerId:"workbuddy",cwd:e.cwd,providerSessionId:o,keepRuntimeIdentity:`fork:${W()}`});const k=async s=>{n(),a=U(t.command,t.args,e.cwd,t.env);const m=new M(a.child,s),u=r(await m.initialize(n()));if(u.protocolVersion!==1||r(u.agentCapabilities).loadSession!==!0)throw c("当前 CodeBuddy 版本不支持恢复分叉会话。");return m},f=v();let B=[],i=null,y=!1;const C=await k(s=>{const m=r(s),u=r(m.update),S=r(u._meta);if(m.sessionId===o&&(y||f.observe(s),u.sessionUpdate==="available_commands_update"&&(B=Z(u.availableCommands)),y&&S["codebuddy.ai/sessionReset"]===!0)){const w=S["codebuddy.ai/newSessionId"];typeof w=="string"&&/^[a-zA-Z0-9][a-zA-Z0-9_:-]{0,255}$/.test(w)&&w!==o&&(i=w)}});e.onStatus?.({phase:"forking",label:"正在恢复 CodeBuddy 源会话",atMs:Date.now()});const I=r(await C.loadSession({sessionId:o,cwd:e.cwd},n()));if(I.sessionId&&I.sessionId!==o)throw c("CodeBuddy 恢复返回了不同的源会话 ID。");if(!f.count)throw c("CodeBuddy 源会话没有可恢复的历史,请确认仍在原工作目录中。");if(!B.some(s=>s.name==="fork"&&s._meta?.type!=="skill"))throw c("当前 CodeBuddy 未公布原生 /fork 命令,请更新 CodeBuddy 后重试。");const _=f.finish();if(y=!0,e.onStatus?.({phase:"forking",label:"正在分叉 CodeBuddy 会话",atMs:Date.now()}),$(await C.prompt({sessionId:o,prompt:[{type:"text",text:"/fork"}]},n())),!i)throw c("CodeBuddy 未确认生成独立分叉会话,无法完成分叉。");await d();const p=v(f.count),b=await k(s=>{r(s).sessionId===i&&p.observe(s)});e.onStatus?.({phase:"forking",label:"正在验证 CodeBuddy 分叉历史",atMs:Date.now()});const h=r(await b.loadSession({sessionId:i,cwd:e.cwd},n()));if(h.sessionId&&h.sessionId!==i||p.count!==f.count||p.finish()!==_)throw c("CodeBuddy 分叉历史校验失败,未将此会话标记为成功。");return await q(b,i,h,{...e,prompt:""}),n(),{providerSessionId:i}}})}catch(t){throw H(t)}finally{try{await d()}finally{l.release()}}}export{Y as forkWorkBuddySession};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import p from"node:fs";import{enterWorkBuddyTask as k}from"./auth-gate.js";import{runAbortableRequest as A}from"../../client/abortable-request.js";import{defaultAcpRuntimePool as m}from"../../client/acp-runtime-pool.js";import{BoundedItemBuffer as w}from"../../client/bounded-buffer.js";import{contextUsageFromAcpSessionUpdate as B}from"../../client/context-usage.js";import{providerEventFromAcpSessionUpdate as E,providerStatusFromAcpSessionUpdate as b}from"../standard-acp-events.js";import{resolveWorkBuddyLaunch as C,workBuddyTimeout as W}from"./launch.js";import{applyWorkBuddyConfig as M,record as o,rememberWorkBuddyCapabilities as f}from"./config.js";import{handleWorkBuddyRequest as R}from"./interactions.js";import{WorkBuddyEventRouter as y}from"./events.js";import{WorkBuddySupplements as D,workBuddyPromptContent as q}from"./supplements.js";const h=new WeakMap,u=new Set,_={sessionResume:!0,liveSupplement:!0,imageInput:!0,fileAttachment:!0,filesystem:!0,terminal:!0,permissionRequest:!0,configOptions:!0,usage:!0,contextUsage:!0,contextCompaction:!0,plan:!0,diff:!0};class G{id="workbuddy";name="腾讯 Code Buddy";capabilities=_;async createSession(s){return new O(s.cwd,s.providerSessionId??null,s.timeoutMs)}}class O{cwd;providerSessionId;timeoutMs;lease=null;input=null;acceptingOutput=!1;busy=!1;content="";usage=null;response={};commands=new Set;router=new y;supplements=null;updates=new w(16);statuses=new w(64);firstEventAtMs=null;lastEventAtMs=null;constructor(s,e,t=W()){this.cwd=s,this.providerSessionId=e,this.timeoutMs=t}async sendPrompt(s){return this.run(s,!1)}async compactContext(s){if(!this.providerSessionId)throw new Error("WorkBuddy 当前没有可压缩的会话。");const e=await this.run({...s,cwd:s.cwd,prompt:"/compact",requireSessionResume:!0},!0);return{providerSessionId:e.providerSessionId,contextUsage:e.contextUsage??null,maintenance:{action:"compact",provider:"workbuddy",protocol:"session/prompt",stopReason:o(e.promptResponse).stopReason},durationMs:e.durationMs}}async run(s,e){if(this.busy)throw new Error("WorkBuddy 会话正在执行另一轮任务。");if(p.realpathSync(s.cwd)!==p.realpathSync(this.cwd))throw new Error("WorkBuddy 会话不能切换工作目录。");if(s.requireSessionResume&&!this.providerSessionId)throw new Error("WorkBuddy 历史会话缺少 Session ID,禁止新建会话。");let t=this.providerSessionId?`${p.realpathSync(this.cwd)}\0${this.providerSessionId}`:null;if(t&&u.has(t))throw new Error("WorkBuddy 会话正在执行另一轮任务。");const a=k();t&&u.add(t),this.busy=!0,this.input=s,this.content="",this.usage=null,this.response={},this.commands.clear(),this.router=new y,this.updates.clear(),this.statuses.clear(),this.firstEventAtMs=this.lastEventAtMs=null;const n=Date.now();try{return await A({signal:s.signal,cancelledMessage:"WorkBuddy 任务已取消",onAbort:()=>this.cancelAndDispose(),startRequest:async()=>{this.status("initializing","WorkBuddy ACP 启动中");const r=C(s.canExecuteTools!==!1);if(this.providerSessionId&&await m.retireSessionRuntimes({providerId:"workbuddy",cwd:this.cwd,providerSessionId:this.providerSessionId,keepRuntimeIdentity:r.runtimeIdentity}),s.signal?.aborted)throw new Error("WorkBuddy 任务已取消");const i=await m.acquire({providerId:"workbuddy",cwd:this.cwd,providerSessionId:this.providerSessionId,...r,onSessionUpdate:d=>this.handleUpdate(d),onClientRequest:(d,g,I)=>this.handleRequest(d,g,I.id)});if(this.lease=i,s.signal?.aborted)throw await i.dispose(),new Error("WorkBuddy 任务已取消");if(await this.initialize(i),await this.establishSession(i),!t){if(t=`${p.realpathSync(this.cwd)}\0${this.providerSessionId}`,u.has(t))throw t=null,new Error("WorkBuddy 会话正在执行另一轮任务。");u.add(t)}const S=o(o(h.get(i.client)?.agentCapabilities).promptCapabilities);if(s.promptBlocks?.some(d=>d.type==="image")&&S.image!==!0)throw new Error("当前 CodeBuddy ACP 版本未声明图片输入支持。");if(e||(this.response=await M(i.client,this.providerSessionId,this.response,s)),i.setSessionResponse(this.response),e&&!this.commands.has("compact"))throw new Error("当前 WorkBuddy 会话未提供 /compact 命令,无法压缩上下文。");this.acceptingOutput=!0,this.status("thinking",e?"正在压缩上下文":"思考中");const l=await i.client.prompt({sessionId:this.providerSessionId,prompt:q(s.prompt,s.promptBlocks)},s.timeoutMs??this.timeoutMs);if(s.signal?.aborted)throw new Error("WorkBuddy 任务已取消");const c=o(l).stopReason;if(c==="cancelled")throw new Error("WorkBuddy 任务已取消");if(e&&c!=="end_turn")throw new Error(`WorkBuddy 压缩未完成:${String(c||"未知状态")}`);return e&&this.usage&&(this.usage={...this.usage,state:"compacted",maintenanceAction:"compact"}),this.status("completed",e?"上下文压缩完成":"已完成"),{providerSessionId:this.providerSessionId,cliType:"builtin",agentType:"workbuddy",effectiveConfig:{model:s.model||null,mode:s.mode||"default",configOptions:this.response.configOptions,provider:"workbuddy",protocol:"acp",command:i.started.command,processPid:i.started.child.pid},command:i.started.command,args:i.started.args,cwd:this.cwd,exitCode:i.started.child.exitCode,signal:i.started.child.signalCode,durationMs:Date.now()-n,stdout:i.client.rpc.stdout,stderr:i.client.rpc.stderr,content:this.content.trim(),updateCount:this.updates.count(),updates:this.updates.snapshot(),statusTimeline:this.statuses.snapshot(),contextUsage:this.usage,promptResponse:l,performance:{startedAtMs:n,firstEventAtMs:this.firstEventAtMs,lastEventAtMs:this.lastEventAtMs,eventCount:this.updates.count(),firstEventLatencyMs:this.firstEventAtMs===null?null:this.firstEventAtMs-n,silentForMs:Date.now()-(this.lastEventAtMs??n),durationMs:Date.now()-n}}}})}catch(r){await this.lease?.dispose();const i=r instanceof Error?r.message:String(r);throw this.status("failed","WorkBuddy 执行失败",i),/authentication required|not authenticated|unauthorized/i.test(i)?new Error("腾讯 Code Buddy 尚未认证:请到设置 → 我的设备 → 腾讯 Code Buddy 登录;也可在运行 ACA Worker 的系统账号下执行 codebuddy 登录,或配置 CODEBUDDY_API_KEY。"):o(r).code==="ENOENT"?new Error("未找到 CodeBuddy CLI,请安装 @tencent-ai/codebuddy-code,或设置 ACA_WORKBUDDY_COMMAND。"):r}finally{this.acceptingOutput=!1,s.onSupplementReady?.(null);try{await this.supplements?.close()}finally{a()}this.supplements=null,this.lease?.release(),this.lease=null,this.input=null,this.busy=!1,t&&u.delete(t)}}async initialize(s){if(s.initialized)return;const e=o(await s.client.rpc.request("initialize",{protocolVersion:1,clientInfo:{name:"aca",version:"0.1.0"},clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1},terminal:!1,elicitation:{form:{}},_meta:{"codebuddy.ai":{question:!0}}}},6e4));if(e.protocolVersion!==1)throw new Error("WorkBuddy ACP 协议版本不兼容。");h.set(s.client,e),s.markInitialized()}async establishSession(s){if(s.sessionEstablished&&s.providerSessionId){this.providerSessionId=s.providerSessionId,this.response=s.sessionResponse??{};const e=this.response.acaAvailableCommands;Array.isArray(e)&&e.forEach(t=>this.commands.add(String(t)));return}if(this.providerSessionId){if(o(h.get(s.client)?.agentCapabilities).loadSession!==!0)throw new Error("当前 WorkBuddy 版本不支持恢复会话,禁止静默新建。");if(this.status("resuming","WorkBuddy 会话恢复中"),this.response=o(await s.client.loadSession({sessionId:this.providerSessionId,cwd:this.cwd},12e4)),this.response.sessionId&&this.response.sessionId!==this.providerSessionId)throw new Error("WorkBuddy 恢复返回了不同的会话 ID。")}else{if(this.status("acp","WorkBuddy 会话创建中"),this.response=o(await s.client.newSession({cwd:this.cwd},12e4)),typeof this.response.sessionId!="string"||!this.response.sessionId.trim())throw new Error("WorkBuddy session/new 未返回会话 ID。");this.providerSessionId=this.response.sessionId}this.response.acaAvailableCommands=[...this.commands],f(this.response),s.setProviderSession({providerSessionId:this.providerSessionId,sessionResponse:this.response})}handleUpdate(s){const e=o(s),t=o(e.update);if(this.providerSessionId&&e.sessionId!==this.providerSessionId||(t.sessionUpdate==="available_commands_update"&&Array.isArray(t.availableCommands)&&(this.commands=new Set(t.availableCommands.map(i=>String(o(i).name||"").replace(/^\//,""))),this.response.acaAvailableCommands=[...this.commands],this.lease?.setSessionResponse(this.response)),t.sessionUpdate==="config_option_update"&&Array.isArray(t.configOptions)&&(this.response={...this.response,configOptions:t.configOptions},this.lease?.setSessionResponse(this.response),f(this.response)),!this.acceptingOutput||!this.input))return;const a=this.router.route(s);if(this.updates.push({method:"session/update",params:a.params}),this.firstEventAtMs??=Date.now(),this.lastEventAtMs=Date.now(),!a.child){this.usage=B(s,this.input.contextWindow)??this.usage;const i=o(t._meta)["codebuddy.ai/requestId"]||o(e._meta)["codebuddy.ai/requestId"];!this.supplements&&typeof i=="string"&&i&&this.lease&&this.providerSessionId&&(this.supplements=new D(this.lease.client,this.providerSessionId,i),this.input.onSupplementReady?.(this.supplements.channel))}const n=E("workbuddy",a.params);n&&(!a.child&&n.type==="agent_message_chunk"&&(this.content+=n.text??""),this.input.onUpdate?.(n));const r=b("workbuddy",a.params);r&&this.status(r.phase,r.label,r.detail,r.updateType)}async handleRequest(s,e,t){if(!this.input||!this.acceptingOutput)throw new Error("WorkBuddy 当前没有可交互的执行轮次。");const a=o(e).sessionId;if((s!=="elicitation/create"||a!==void 0)&&a!==this.providerSessionId)throw new Error("WorkBuddy 交互请求会话不匹配。");this.status("requestPermission",s==="session/request_permission"?"等待授权":"等待回答",void 0,s);try{return await R(s,e,t,this.input)}finally{this.acceptingOutput&&this.status("thinking","处理中")}}status(s,e,t,a){const n={phase:s,label:e,detail:t,updateType:a,atMs:Date.now()};this.statuses.push(n),this.input?.onStatus?.(n)}async cancel(){await this.cancelAndDispose()}async cancelAndDispose(){this.acceptingOutput=!1,this.input?.onSupplementReady?.(null),this.providerSessionId&&this.lease?.client.cancel(this.providerSessionId),await this.lease?.dispose()}async close(){this.busy?await this.cancelAndDispose():this.lease?.release()}}export{G as WorkBuddyProvider,_ as workBuddyCapabilities};
|
|
1
|
+
import{workBuddyCredits as C}from"../../../core/workbuddy-credits/worker.js";import c from"node:fs";import{enterWorkBuddySession as B}from"./session-lock.js";import{forkWorkBuddySession as A}from"./fork.js";import{runAbortableRequest as E}from"../../client/abortable-request.js";import{defaultAcpRuntimePool as h}from"../../client/acp-runtime-pool.js";import{BoundedItemBuffer as m}from"../../client/bounded-buffer.js";import{contextUsageFromAcpSessionUpdate as W}from"../../client/context-usage.js";import{providerEventFromAcpSessionUpdate as f,providerStatusFromAcpSessionUpdate as M}from"../standard-acp-events.js";import{resolveWorkBuddyLaunch as R,workBuddyTimeout as U}from"./launch.js";import{applyWorkBuddyConfig as _,record as r,rememberWorkBuddyCapabilities as w}from"./config.js";import{handleWorkBuddyRequest as q}from"./interactions.js";import{WorkBuddyEventRouter as y}from"./events.js";import{WorkBuddySupplements as x,workBuddyPromptContent as D}from"./supplements.js";import{assertWorkBuddyPromptCompleted as O,workBuddyError as g}from"./errors.js";import{WorkBuddyTurnCredits as v,workBuddyContextUsage as P}from"./usage.js";import{workBuddyCommands as S}from"./commands.js";const l=new WeakMap,T={sessionResume:!0,liveSupplement:!0,imageInput:!0,fileAttachment:!0,filesystem:!0,terminal:!0,permissionRequest:!0,configOptions:!0,usage:!0,contextUsage:!0,contextCompaction:!0,plan:!0,diff:!0};class is{id="workbuddy";name="腾讯 Code Buddy";capabilities=T;async createSession(s){return new z(s.cwd,s.providerSessionId??null,s.timeoutMs)}async forkSession(s){return A(s)}}class z{cwd;providerSessionId;timeoutMs;lease=null;input=null;acceptingOutput=!1;busy=!1;content="";usage=null;response={};commands=new Set;commandCatalog=null;credits=new v;technicalError=null;router=new y;supplements=null;updates=new m(16);statuses=new m(64);firstEventAtMs=null;lastEventAtMs=null;constructor(s,t,i=U()){this.cwd=s,this.providerSessionId=t,this.timeoutMs=i}async sendPrompt(s){return this.run(s,!1)}async compactContext(s){if(!this.providerSessionId)throw new Error("WorkBuddy 当前没有可压缩的会话。");const t=await this.run({...s,cwd:s.cwd,prompt:"/compact",requireSessionResume:!0},!0);return{providerSessionId:t.providerSessionId,contextUsage:t.contextUsage??null,maintenance:{action:"compact",provider:"workbuddy",protocol:"session/prompt",stopReason:r(t.promptResponse).stopReason},durationMs:t.durationMs}}async run(s,t){if(this.busy)throw new Error("WorkBuddy 会话正在执行另一轮任务。");if(c.realpathSync(s.cwd)!==c.realpathSync(this.cwd))throw new Error("WorkBuddy 会话不能切换工作目录。");if(s.requireSessionResume&&!this.providerSessionId)throw new Error("WorkBuddy 历史会话缺少 Session ID,禁止新建会话。");if(/^\s*\/(fork|branch|fork-bg)(?:\s|$)/i.test(s.prompt))throw new Error("请使用会话菜单中的“从当前会话分叉”,以便 ACA 正确保存新会话。");const i=B(this.providerSessionId);this.busy=!0,this.input=s,this.content="",this.usage=null,this.response={},this.commands.clear(),this.commandCatalog=null,this.credits=new v,this.technicalError=null,this.router=new y,this.updates.clear(),this.statuses.clear(),this.firstEventAtMs=this.lastEventAtMs=null;const o=Date.now();let d;try{return await E({signal:s.signal,cancelledMessage:"WorkBuddy 任务已取消",onAbort:()=>this.cancelAndDispose(),startRequest:async()=>{this.status("initializing","WorkBuddy ACP 启动中");const n=R(s.canExecuteTools!==!1);if(this.providerSessionId&&await h.retireSessionRuntimes({providerId:"workbuddy",cwd:this.cwd,providerSessionId:this.providerSessionId,keepRuntimeIdentity:n.runtimeIdentity}),s.signal?.aborted)throw new Error("WorkBuddy 任务已取消");const e=await h.acquire({providerId:"workbuddy",cwd:this.cwd,providerSessionId:this.providerSessionId,...n,onSessionUpdate:a=>this.handleUpdate(a),onClientRequest:(a,k,b)=>this.handleRequest(a,k,b.id)});if(this.lease=e,s.signal?.aborted)throw await e.dispose(),new Error("WorkBuddy 任务已取消");await this.initialize(e),await this.establishSession(e),i.bind(this.providerSessionId);const p=r(r(l.get(e.client)?.agentCapabilities).promptCapabilities);if(s.promptBlocks?.some(a=>a.type==="image")&&p.image!==!0)throw new Error("当前 CodeBuddy ACP 版本未声明图片输入支持。");if(t||(this.response=await _(e.client,this.providerSessionId,this.response,s)),e.setSessionResponse(this.response),t&&!this.commands.has("compact"))throw new Error("当前 WorkBuddy 会话未提供 /compact 命令,无法压缩上下文。");this.acceptingOutput=!0,this.publishCommands(),this.status("thinking",t?"正在压缩上下文":"思考中"),d=C.beginUsage();const u=await e.client.prompt({sessionId:this.providerSessionId,prompt:D(s.prompt,s.promptBlocks)},s.timeoutMs??this.timeoutMs);if(s.signal?.aborted)throw new Error("WorkBuddy 任务已取消");if(this.credits.identifyResponse(u),this.technicalError)throw g(this.technicalError);return O(u),t&&this.usage&&(this.usage={...this.usage,state:"compacted",maintenanceAction:"compact"}),this.status("completed",t?"上下文压缩完成":"已完成"),{providerSessionId:this.providerSessionId,cliType:"builtin",agentType:"workbuddy",effectiveConfig:{model:s.model||null,mode:s.mode||"default",configOptions:this.response.configOptions,provider:"workbuddy",protocol:"acp",command:e.started.command,processPid:e.started.child.pid},command:e.started.command,args:e.started.args,cwd:this.cwd,exitCode:e.started.child.exitCode,signal:e.started.child.signalCode,durationMs:Date.now()-o,stdout:e.client.rpc.stdout,stderr:e.client.rpc.stderr,content:this.content.trim(),updateCount:this.updates.count(),updates:this.updates.snapshot(),statusTimeline:this.statuses.snapshot(),contextUsage:this.usage,promptResponse:u,providerDiagnostics:this.diagnostics(),performance:{startedAtMs:o,firstEventAtMs:this.firstEventAtMs,lastEventAtMs:this.lastEventAtMs,eventCount:this.updates.count(),firstEventLatencyMs:this.firstEventAtMs===null?null:this.firstEventAtMs-o,silentForMs:Date.now()-(this.lastEventAtMs??o),durationMs:Date.now()-o}}}})}catch(n){await this.lease?.dispose();const e=g(n);throw this.status(e.failure.category==="cancelled"?"cancelled":"failed",e.message),Object.assign(e,{providerDiagnostics:this.diagnostics(),statusTimeline:this.statuses.snapshot()})}finally{d?.(),this.acceptingOutput=!1,s.onSupplementReady?.(null);try{await this.supplements?.close()}finally{i.release()}this.supplements=null,this.lease?.release(),this.lease=null,this.input=null,this.busy=!1}}async initialize(s){if(s.initialized)return;const t=r(await s.client.rpc.request("initialize",{protocolVersion:1,clientInfo:{name:"aca",version:"0.1.0"},clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1},terminal:!1,elicitation:{form:{}},_meta:{"codebuddy.ai":{question:!0}}}},6e4));if(t.protocolVersion!==1)throw new Error("WorkBuddy ACP 协议版本不兼容。");l.set(s.client,t),s.markInitialized()}async establishSession(s){if(s.sessionEstablished&&s.providerSessionId){this.providerSessionId=s.providerSessionId,this.response=s.sessionResponse??{};const t=this.response.acaAvailableCommands;Array.isArray(t)&&(this.commandCatalog=S(t),this.commands=new Set(this.commandCatalog.map(i=>i.name)));return}if(this.providerSessionId){if(r(l.get(s.client)?.agentCapabilities).loadSession!==!0)throw new Error("当前 WorkBuddy 版本不支持恢复会话,禁止静默新建。");if(this.status("resuming","WorkBuddy 会话恢复中"),this.response=r(await s.client.loadSession({sessionId:this.providerSessionId,cwd:this.cwd},12e4)),this.response.sessionId&&this.response.sessionId!==this.providerSessionId)throw new Error("WorkBuddy 恢复返回了不同的会话 ID。")}else{if(this.status("acp","WorkBuddy 会话创建中"),this.response=r(await s.client.newSession({cwd:this.cwd},12e4)),typeof this.response.sessionId!="string"||!this.response.sessionId.trim())throw new Error("WorkBuddy session/new 未返回会话 ID。");this.providerSessionId=this.response.sessionId}this.commandCatalog&&(this.response.acaAvailableCommands=this.commandCatalog),w(this.response),s.setProviderSession({providerSessionId:this.providerSessionId,sessionResponse:this.response})}handleUpdate(s){const t=r(s),i=r(t.update);if(this.providerSessionId&&t.sessionId!==this.providerSessionId)return;const o=this.router.route(s);if(!o.child&&i.sessionUpdate==="available_commands_update"&&Array.isArray(i.availableCommands)&&(this.commandCatalog=S(i.availableCommands),this.commands=new Set(this.commandCatalog.map(p=>p.name)),this.response.acaAvailableCommands=this.commandCatalog,this.lease?.setSessionResponse(this.response)),!o.child&&i.sessionUpdate==="config_option_update"&&Array.isArray(i.configOptions)&&(this.response={...this.response,configOptions:i.configOptions},this.lease?.setSessionResponse(this.response),w(this.response)),!this.acceptingOutput||!this.input)return;if(this.updates.push({method:"session/update",params:o.params}),this.firstEventAtMs??=Date.now(),this.lastEventAtMs=Date.now(),!o.child){this.credits.observe(s);const p=W(s,this.input.contextWindow);this.usage=P(s,p)??this.usage;const u=r(i._meta)["codebuddy.ai/error"];u&&(this.technicalError=u);const a=r(i._meta)["codebuddy.ai/requestId"]||r(t._meta)["codebuddy.ai/requestId"];!this.supplements&&typeof a=="string"&&a&&this.lease&&this.providerSessionId&&(this.supplements=new x(this.lease.client,this.providerSessionId,a),this.input.onSupplementReady?.(this.supplements.channel))}const d=!o.child&&i.sessionUpdate==="available_commands_update"?{...o.params,update:{...i,availableCommands:this.commandCatalog??[]}}:o.params,n=f("workbuddy",d);n&&(!o.child&&n.type==="agent_message_chunk"&&(this.content+=n.text??""),this.input.onUpdate?.(n));const e=M("workbuddy",o.params);e&&this.status(e.phase,e.label,e.detail,e.updateType)}diagnostics(){return{workbuddy:{creditUsage:this.credits.snapshot(),...this.commandCatalog!==null?{availableCommands:this.commandCatalog}:{}}}}publishCommands(){if(this.commandCatalog===null)return;const s=f("workbuddy",{sessionId:this.providerSessionId,update:{sessionUpdate:"available_commands_update",availableCommands:this.commandCatalog}});s&&this.input?.onUpdate?.(s)}async handleRequest(s,t,i){if(!this.input||!this.acceptingOutput)throw new Error("WorkBuddy 当前没有可交互的执行轮次。");const o=r(t).sessionId;if((s!=="elicitation/create"||o!==void 0)&&o!==this.providerSessionId)throw new Error("WorkBuddy 交互请求会话不匹配。");this.status("requestPermission",s==="session/request_permission"?"等待授权":"等待回答",void 0,s);try{return await q(s,t,i,this.input)}finally{this.acceptingOutput&&this.status("thinking","处理中")}}status(s,t,i,o){const d={phase:s,label:t,detail:i,updateType:o,atMs:Date.now()};this.statuses.push(d),this.input?.onStatus?.(d)}async cancel(){await this.cancelAndDispose()}async cancelAndDispose(){this.acceptingOutput=!1,this.input?.onSupplementReady?.(null),this.providerSessionId&&this.lease?.client.cancel(this.providerSessionId),await this.lease?.dispose()}async close(){this.busy?await this.cancelAndDispose():this.lease?.release()}}export{is as WorkBuddyProvider,T as workBuddyCapabilities};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{enterWorkBuddyTask as a}from"./auth-gate.js";const t=new Set;function c(n){const o=a();let r=null,u=!1;const s=e=>{if(r!==e){if(r||t.has(e))throw new Error("CodeBuddy 会话正在执行另一轮任务,请完成后重试。");t.add(e),r=e}};try{n&&s(n)}catch(e){throw o(),e}return{bind:s,release(){u||(u=!0,r&&t.delete(r),o())}}}export{c as enterWorkBuddySession};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{record as s}from"./config.js";const a=["systemPrompt","conversation","tools","mcp","skills"];function m(o,t){if(!t)return null;const n=s(s(s(o).update)._meta)["codebuddy.ai/usageByCategory"],e=s(n);if(e.version!==1||!a.every(d=>c(e[d])))return t;const i=a.reduce((d,r)=>d+Number(e[r]),0);return Math.abs(i-t.usedTokens)>1?t:{...t,workbuddyUsageByCategory:{version:1,systemPrompt:Number(e.systemPrompt),conversation:Number(e.conversation),tools:Number(e.tools),mcp:Number(e.mcp),skills:Number(e.skills)}}}class h{totals=new Map;requestIds=new Set;observe(t){const n=s(t),e=s(n.update),i=s(e._meta),d=i["codebuddy.ai/requestId"]??s(n._meta)["codebuddy.ai/requestId"];if(this.identify(d),e.sessionUpdate==="usage_update")for(const[r,u]of Object.entries(s(i["codebuddy.ai/requestCredits"])))!r||r.length>256||!c(u)||this.totals.size>=4096&&!this.totals.has(r)||this.totals.set(r,Math.max(this.totals.get(r)??0,u))}identifyResponse(t){this.identify(s(s(t)._meta)["codebuddy.ai/requestId"])}identify(t){typeof t=="string"&&t&&t.length<=256&&this.requestIds.size<64&&this.requestIds.add(t)}snapshot(){const t=[...this.requestIds].filter(e=>this.totals.has(e));return t.length?{credits:Math.round(t.reduce((e,i)=>e+this.totals.get(i),0)*100)/100,requestCount:t.length,partial:t.length<this.requestIds.size}:null}}function c(o){return typeof o=="number"&&Number.isFinite(o)&&o>=0}export{h as WorkBuddyTurnCredits,m as workBuddyContextUsage};
|
package/dist/commands/start.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{stopProviderInstaller as M}from"../core/provider-installer.js";import{
|
|
1
|
+
import{stopProviderInstaller as M}from"../core/provider-installer.js";import{activateWorkBuddyCredits as L,stopWorkBuddyCredits as U}from"../core/workbuddy-credits/worker.js";import{stopManagedClaims as F}from"../core/management/claims.js";import{workBuddyAuthManager as H}from"../core/workbuddy-auth-worker.js";import{assertManualStartAllowed as B}from"../core/management/manual.js";import{startManagementControl as K}from"../core/management/control-server.js";import{managementActivity as u}from"../core/management/activity.js";import{acquireWorkerLease as V,assertNoWorkerInstances as G}from"../core/worker-instances.js";import{getAcaHome as J}from"../core/paths.js";import{Command as q}from"commander";import I from"node:fs";import D from"node:os";import m from"node:path";import t from"node:process";import{randomUUID as z}from"node:crypto";import{Logger as Q}from"../core/logger.js";import{loadCredentials as X,saveApiKeyCredential as Y}from"../core/auth.js";import{getRuntimeStatePath as Z}from"../core/paths.js";import{writeJsonFile as ee}from"../core/fs.js";import{HEARTBEAT_INTERVAL_MS as te,startHeartbeatLoop as re}from"../core/heartbeat.js";import{startJobWorkerLoop as oe}from"../core/job-worker.js";import{startFileTransferWorkerLoop as ae}from"../core/file-transfer-worker.js";import{startTerminalWorker as ie}from"../core/terminal/worker.js";import{startMachineFilesystemWorkerLoop as ne}from"../core/machine-filesystem-worker.js";import{cleanupOrphanedAcpProcesses as se}from"../core/acp-orphan-cleanup.js";import{defaultAcpRuntimePool as _}from"../acp/client/acp-runtime-pool.js";import{isProcessAlive as h,readPidFile as d,writePidFile as ce,removePidFile as W}from"../core/process.js";import{applyRuntimePathOptions as x}from"../core/runtime-options.js";import{loadAcaConfig as $,updateAcaConfig as R}from"../core/aca-config.js";import{acaServerRequest as me}from"../core/aca-server-client.js";import{restoreProjectsAfterLogin as de}from"../core/project-recovery.js";import{syncCodexProviderRuntimeConfig as fe}from"../core/codex-provider-runtime-config.js";import{ACA_WORKER_INSTANCE_ENV as O,ACA_WORKER_PID_ENV as le,ACA_WORKER_START_TICKS_ENV as pe,readProcessStartTicks as ue}from"../core/process-identity.js";import{printStartTips as he,spawnDaemon as ge}from"./daemon.js";import{startDesktopControl as we}from"../core/desktop-control.js";function nt(){return new q("start").description("Start agent service in native mode").option("--aica-home <path>","Runtime home directory (defaults to ~/.aica or AICA_HOME)").option("--aica-config <path>","Config file path (defaults to <aica-home>/config.json)").option("--aca-home <path>","Deprecated alias for --aica-home").option("--aca-config <path>","Deprecated alias for --aica-config").option("--server <url>","ACA Server URL override").option("--machine-id <id>","Machine ID override for this runtime").option("--machine-name <name>","Machine name to register (defaults to hostname)").option("--cli-types <types...>","Specify CLI types to register, `claude` or `codex`").option("--managed-start <operationId>","受管理启动:保持业务入口关闭,等待 Proxy 激活").option("--debug","enable debug output").option("--heartbeat-log","output current timestamp every 5 seconds").option("--auth <api-key>","Use a CLI API key for non-interactive authentication").option("--token <enrollment-token>","Use a one-time Web-generated token to register this machine").option("-b, --background","在后台启动 Worker").action(async e=>{if(e.background){await ve(e);return}await Se(e)})}async function ve(e){x(e),G();const a=d();if(t.platform!=="linux"&&a&&h(a))throw new Error(`Worker 已在后台运行,PID ${a}。`);a&&W(a);const n=ge(Ae());if(n.unref(),!n.pid)throw new Error("后台 Worker 启动失败:未获得进程 PID。");if(!await Ie(n.pid,3e3)&&!h(n.pid))throw new Error(`后台 Worker 启动失败,请检查 ${m.join(t.env.AICA_HOME||t.env.ACA_HOME||m.join(D.homedir(),".aica"),"logs")}。`);console.log(`Worker 已在后台启动,PID ${n.pid}。`),he()}function Ae(){const e=t.argv.findIndex((a,n)=>n>=2&&a==="start");return e<0?[]:t.argv.slice(e+1).filter(a=>a!=="--background"&&a!=="-b")}async function Ie(e,a){const n=Date.now();for(;Date.now()-n<a;){if(d()===e)return!0;if(!h(e))return!1;await new Promise(o=>setTimeout(o,50))}return d()===e}async function Se(e){x(e);const a=await V();try{await ke(e)}finally{a()}}async function ke(e){if(e.managedStart||B(),e.managedStart){if(t.platform!=="linux")throw new Error("当前平台尚未支持受管理启动");if(!t.env.AICA_MANAGER_ID)throw new Error("受管理启动缺少管理实例");t.env.AICA_UPDATE_OPERATION=e.managedStart}const a=t.env[O]||z(),n=ue(t.pid)||"";t.env[O]=a,t.env[le]=String(t.pid),t.env[pe]=n;const o=new Q("start",e.debug?"debug":"info"),g=$(),S={...e.server?.trim()?{serverUrl:e.server.trim()}:{},...e.machineId?{machineId:e.machineId.trim()}:{},...e.machineName?{machineName:e.machineName.trim()}:{}};let c=Object.keys(S).length>0?R(S):g;const f=e.machineName?.trim()||c.machineName||D.hostname();if(e.token?.trim()){const r=await me("POST","/api/auth/machine-enroll",{token:e.token.trim(),machineId:c.machineId,machineName:f},{serverUrl:c.serverUrl});c=R({machineId:r.machineId,machineName:f,token:r.token,username:r.username}),o.info(`Machine enrollment completed for user ${r.username}`);try{const i=await de();i.restored>0&&o.info(`restored ${i.restored} local project(s) after machine enrollment`),i.unavailable>0&&o.warn(`skipped ${i.unavailable} unavailable project path(s) during machine enrollment recovery`),c=$()}catch(i){o.warn(`project recovery after machine enrollment failed: ${i instanceof Error?i.message:String(i)}`)}}const N=e.auth?Y(e.auth,f):X(),p=d();if(t.platform!=="linux"&&p&&p!==t.pid&&h(p))throw new Error(`Worker 已在运行,PID ${p}`);ce(t.pid),j({pid:t.pid,workerInstanceId:a,workerStartTicks:n,phase:"running",startupStage:"local-compatible",machineId:c.machineId||N?.machine?.machineId||"local-machine",machineName:f,cliTypes:e.cliTypes??ye(),startedAt:new Date().toISOString(),heartbeatIntervalMs:te,issues:[{severity:"info",message:"aca is running in aca-server compatibility mode; ACP execution happens on this machine."}]}),o.info(`Starting aca local-compatible service on machine ${f}`),o.info(`PID ${t.pid}`);const w=await se();w.terminated>0&&o.warn(`terminated ${w.terminated} orphaned ACP process(es): ${w.pids.join(",")}`),fe().then(r=>o.info(r?`Codex Provider runtime config loaded revision=${r.revision}`:"Codex Provider runtime config is not configured")).catch(r=>o.warn(`Codex Provider runtime config sync failed: ${r instanceof Error?r.message:String(r)}`));let k=!1,v,y,C,P,E;const l=async r=>{if(k)return;k=!0,F(),o.info("Shutting down"),v&&clearInterval(v),C?.stop(),E?.stop(),P?.stop(),await M(),await U(),await H.stop(),await y?.stop().catch(s=>o.warn(`job worker shutdown failed: ${s instanceof Error?s.message:String(s)}`));const i=_.stats();await _.disposeAll().catch(s=>o.warn(`ACP runtime shutdown failed: ${s instanceof Error?s.message:String(s)}`)),o.info(`ACP runtimes stopped total=${i.total} inUse=${i.inUse} idle=${i.idle}`),d()===t.pid&&(j({pid:t.pid,phase:"stopped",stoppedAt:new Date().toISOString(),issues:[]}),W(t.pid)),t.exit(r)},b=r=>{if(!u.stopping&&d()===t.pid){const i=m.join(J(),"management");I.mkdirSync(i,{recursive:!0,mode:448}),I.writeFileSync(m.join(i,"manual-stop.json"),JSON.stringify({workerInstanceId:a,at:Date.now()}),{mode:384})}l(r)};t.once("SIGINT",()=>b(130)),t.once("SIGTERM",()=>b(0)),t.once("uncaughtException",r=>{o.error(`uncaught exception: ${r.stack||r.message}`),l(1)}),t.once("unhandledRejection",r=>{const i=r instanceof Error?r.stack||r.message:String(r);o.error(`unhandled rejection: ${i}`),l(1)}),e.heartbeatLog&&setInterval(()=>o.info(`heartbeat: ${new Date().toISOString()}`),5e3);let T=!1;const A=()=>{T||(T=!0,L(),y=oe(o),C=ae(o),E=ne(o),P=ie(o))};try{await K(a,()=>l(0),A)}catch(r){if(e.managedStart||r instanceof Error&&r.message.includes("已有 Worker 管理实例"))throw r;u.paused=!1,o.warn(`可选管理通道不可用: ${r instanceof Error?r.message:String(r)}`)}v=re(o),t.env.ACA_DESKTOP_CHILD==="1"&&t.env.ACA_DESKTOP_STAGED==="1"&&(u.paused=!0),u.paused||A(),we(()=>l(0),A),await new Promise(()=>{})}function ye(){return["codex","claude"].filter(e=>Ce(e))}function Ce(e){const a=t.env.PATH||"";for(const n of a.split(m.delimiter))try{const o=t.platform==="win32"?["",".exe",".cmd",".bat"]:[""];if(n&&o.some(g=>I.existsSync(m.join(n,e+g))))return!0}catch{}return!1}function j(e){ee(Z(),e)}export{nt as createStartCommand};
|
package/dist/core/heartbeat.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{resolveInstalledLaunch as g}from"./provider-install-paths.js";import{execFileSync as y}from"node:child_process";import m from"node:os";import{readJsonFile as v,writeJsonFile as x}from"./fs.js";import{getRuntimeStatePath as b}from"./paths.js";import{acaServerRequest as k}from"./aca-server-client.js";import{loadAcaConfig as C}from"./aca-config.js";import{providerHealthSnapshot as O}from"./provider-health.js";import{readCodexCapabilities as _}from"./codex-capabilities.js";import{AICA_VERSION as E}from"../version.js";import{applyProjectSync as S}from"./project-sync.js";import{workBuddyHeartbeatCapabilities as I}from"../acp/providers/workbuddy/config.js";import{readWorkBuddyCapabilities as D}from"../acp/providers/workbuddy/discovery.js";const A=3e4,
|
|
2
|
-
`));if(!r)continue;const l=M(r.name,r.id,a)??a;o.push({modelId:a,displayName:
|
|
3
|
-
`));if(!r)continue;const l=M(r.name,r.id,a)??a;o.push({modelId:a,displayName:
|
|
1
|
+
import{resolveInstalledLaunch as g}from"./provider-install-paths.js";import{execFileSync as y}from"node:child_process";import m from"node:os";import{readJsonFile as v,writeJsonFile as x}from"./fs.js";import{getRuntimeStatePath as b}from"./paths.js";import{acaServerRequest as k}from"./aca-server-client.js";import{loadAcaConfig as C}from"./aca-config.js";import{providerHealthSnapshot as O}from"./provider-health.js";import{readCodexCapabilities as _}from"./codex-capabilities.js";import{AICA_VERSION as E}from"../version.js";import{applyProjectSync as S}from"./project-sync.js";import{workBuddyHeartbeatCapabilities as I}from"../acp/providers/workbuddy/config.js";import{readWorkBuddyCapabilities as D}from"../acp/providers/workbuddy/discovery.js";import{workBuddyCreditsConnected as N}from"./workbuddy-credits/worker.js";const A=3e4,V=360*60*1e3,H=360*60*1e3;let p=null,u=null;async function R(e=C(),t={}){if(!e.token)return{ok:!1,skipped:!0,reason:"not_logged_in"};const o=await k("POST","/api/heartbeat",{machineId:e.machineId,machineName:e.machineName,machineInfo:{platform:m.platform(),architecture:m.arch(),osRelease:m.release(),acaVersion:T(),hostname:m.hostname()},workspaceId:e.defaultWorkspaceId,agentConfigs:t.includeProviderDetails===!1?[]:await j(e),projects:e.projects.map(n=>({projectId:n.projectId,workspaceId:n.workspaceId,name:n.name,rootPath:n.rootPath}))});return S(e,o),N(),o}async function j(e){const t=O(),o=await _(),n=o?.capabilities??L(),a=t.workbuddy.available?await D().catch(()=>I()):I();return[{agentId:`${e.machineId}:builtin:codex`,workspaceId:e.defaultWorkspaceId,name:"Codex",agentType:"codex",cliType:"builtin",machineId:e.machineId,model:o?.defaultModel||"gpt-5.5",mode:"agent-full-access",raw:{providerStatus:t.codex,capabilities:n}},{agentId:`${e.machineId}:builtin:mimo`,workspaceId:e.defaultWorkspaceId,name:"MimoCode",agentType:"mimo",cliType:"builtin",machineId:e.machineId,model:"mimo/mimo-auto",mode:"agent-full-access",raw:{providerStatus:t.mimo,capabilities:F()}},{agentId:`${e.machineId}:builtin:opencode`,workspaceId:e.defaultWorkspaceId,name:"OpenCode",agentType:"opencode",cliType:"builtin",machineId:e.machineId,model:process.env.ACA_OPENCODE_DEFAULT_MODEL||null,mode:"agent-full-access",raw:{providerStatus:t.opencode,capabilities:U()}},{agentId:`${e.machineId}:builtin:workbuddy`,workspaceId:e.defaultWorkspaceId,name:"腾讯 Code Buddy",agentType:"workbuddy",cliType:"builtin",machineId:e.machineId,model:process.env.ACA_WORKBUDDY_DEFAULT_MODEL||null,mode:"default",raw:{providerStatus:t.workbuddy,capabilities:a}}]}function T(e=process.env){return e.ACA_VERSION?.trim()||E}function L(){const e=[{id:"read-only",name:"Read-only",description:"Requires approval to edit files and run commands."},{id:"agent",name:"Agent",description:"Read and edit files, and run commands."},{id:"agent-full-access",name:"Agent (full access)",description:"Edit files and run commands with full access."}],t=[{modelId:"gpt-5.5",name:"gpt-5.5",description:"Latest frontier Codex model"},{modelId:"gpt-5.4",name:"gpt-5.4",description:"Frontier Codex model"},{modelId:"gpt-5.4-mini",name:"gpt-5.4-mini",description:"Smaller, faster Codex model"}],o=[{value:"low",name:"Low",description:"Fastest responses"},{value:"medium",name:"Medium",description:"Balanced reasoning"},{value:"high",name:"High",description:"More reasoning for difficult tasks"},{value:"xhigh",name:"X High",description:"Extra reasoning for complex tasks"}];return{source:"aca-static",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!0,modes:e,models:t,configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:e.map(n=>({value:n.id,name:n.name,description:n.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:"gpt-5.5",options:t.map(n=>({value:n.modelId,name:n.name,description:n.description}))},{id:"reasoning_effort",name:"Reasoning effort",category:"thought_level",type:"select",currentValue:"medium",options:o}]}}function $(){const e=[{id:"agent",name:"Agent",description:"Run MimoCode with normal workspace permissions."},{id:"agent-full-access",name:"Agent (full access)",description:"Run MimoCode and auto-approve supported local permissions."}],t=[{modelId:"mimo/mimo-auto",name:"MiMo Auto (free)",description:"MiMo Auto free model"},{modelId:"xiaomi/mimo-v2.5",name:"mimo-v2.5",description:"MimoCode v2.5"},{modelId:"xiaomi/mimo-v2.5-pro",name:"mimo-v2.5-pro",description:"MimoCode v2.5 Pro"},{modelId:"xiaomi/mimo-v2.5-pro-ultraspeed",name:"mimo-v2.5-pro-ultraspeed",description:"MimoCode v2.5 Pro UltraSpeed"}],o=[{value:"minimal",name:"Minimal",description:"Fastest responses"},{value:"high",name:"High",description:"More reasoning for difficult tasks"},{value:"max",name:"Max",description:"Maximum reasoning when supported"}];return{source:"aca-static",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:e,models:t,configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:e.map(n=>({value:n.id,name:n.name,description:n.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:"mimo/mimo-auto",options:t.map(n=>({value:n.modelId,name:n.name,description:n.description}))},{id:"variant",name:"Variant",category:"thought_level",type:"select",currentValue:"high",options:o}]}}function F(){const e=Date.now();if(p&&p.expiresAtMs>e)return p.capabilities;const t=X()??$();return p={expiresAtMs:e+V,capabilities:t},t}function P(){const e=[{id:"plan",name:"Plan",description:"Inspect the project and propose changes."},{id:"build",name:"Build",description:"Read and edit files, and run commands when approved."},{id:"agent-full-access",name:"Agent (full access)",description:"Run OpenCode with ACA full-access mode mapping."}],t=[{modelId:"",name:"Default",description:"Use the model selected by OpenCode configuration."}];return{source:"aca-static",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:e,models:t,configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:e.map(o=>({value:o.id,name:o.name,description:o.description}))},{id:"model",name:"Model",category:"model",type:"text",currentValue:process.env.ACA_OPENCODE_DEFAULT_MODEL||"",options:t.map(o=>({value:o.modelId,name:o.name,description:o.description}))}]}}function U(){const e=Date.now();if(u&&u.expiresAtMs>e)return u.capabilities;const t=B()??P();return u={expiresAtMs:e+H,capabilities:t},t}function B(){try{const{command:e,args:t}=g("opencode"),o=y(e,[...t,"models","--verbose"],{windowsHide:!0,encoding:"utf8",timeout:15e3,maxBuffer:8*1024*1024,stdio:["ignore","pipe","pipe"]}),n=W(o);if(!n.length)return null;const a=[{id:"plan",name:"Plan",description:"Inspect the project and propose changes."},{id:"build",name:"Build",description:"Read and edit files, and run commands when approved."},{id:"agent-full-access",name:"Agent (full access)",description:"Run OpenCode with ACA full-access mode mapping."}],s=z(n);return{source:"opencode-cli",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:a,models:n.map(i=>({modelId:i.modelId,name:i.displayName,description:i.description,metadata:i.metadata})),configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:a.map(i=>({value:i.id,name:i.name,description:i.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:s,options:n.map(i=>({value:i.modelId,name:i.displayName,description:i.description}))}]}}catch{return null}}function W(e){const t=e.split(/\r?\n/),o=[];let n=0;for(;n<t.length;){const a=t[n]?.trim()??"";if(n+=1,!/^[a-z0-9_.-]+\/[a-z0-9_.-]+$/i.test(a))continue;const s=[];let i=0,c=!1;for(;n<t.length;){const d=t[n]??"";if(n+=1,!(!c&&!d.trim().startsWith("{"))&&(c=!0,s.push(d),i+=f(d,"{")-f(d,"}"),c&&i<=0))break}const r=w(s.join(`
|
|
2
|
+
`));if(!r)continue;const l=M(r.name,r.id,a)??a;o.push({modelId:a,displayName:J(l,r),description:K(r),metadata:r})}return o}function z(e){const t=process.env.ACA_OPENCODE_DEFAULT_MODEL?.trim();return t&&e.some(o=>o.modelId===t)?t:e.find(o=>o.modelId==="opencode/big-pickle")?.modelId||e.find(o=>/free$/i.test(o.modelId))?.modelId||e[0]?.modelId||""}function J(e,t){return q(t)&&!/\bfree\b/i.test(e)?`${e} (free)`:e}function q(e){const t=e.cost;if(!t||typeof t!="object"||Array.isArray(t))return!1;const o=t,n=o.cache&&typeof o.cache=="object"&&!Array.isArray(o.cache)?o.cache:{};return[o.input,o.output,n.read,n.write].every(a=>Number(a??0)===0)}function K(e){const t=e.limit&&typeof e.limit=="object"&&!Array.isArray(e.limit)?e.limit:{},o=Number(t.context??0),n=Number(t.output??0);return o>0&&n>0?`Context ${o}, output ${n}`:o>0?`Context ${o}`:"OpenCode model"}function X(){try{const{command:e,args:t}=g("mimo"),o=y(e,[...t,"models","--verbose"],{windowsHide:!0,encoding:"utf8",timeout:15e3,maxBuffer:2*1024*1024,stdio:["ignore","pipe","pipe"]}),n=Y(o);if(!n.length)return null;const a=[{id:"agent",name:"Agent",description:"Run MimoCode with normal workspace permissions."},{id:"agent-full-access",name:"Agent (full access)",description:"Run MimoCode and auto-approve supported local permissions."}],s=G(n);return{source:"mimo-cli",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:a,models:n.map(i=>({modelId:i.modelId,name:i.displayName,description:i.description,metadata:i.metadata})),configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:a.map(i=>({value:i.id,name:i.name,description:i.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:n.some(i=>i.modelId==="mimo/mimo-auto")?"mimo/mimo-auto":n[0]?.modelId,options:n.map(i=>({value:i.modelId,name:i.displayName,description:i.description}))},{id:"variant",name:"Variant",category:"thought_level",type:"select",currentValue:s.some(i=>i.value==="high")?"high":s[0]?.value,options:s}]}}catch{return null}}function Y(e){const t=e.split(/\r?\n/),o=[];let n=0;for(;n<t.length;){const a=t[n]?.trim()??"";if(n+=1,!/^[a-z0-9_.-]+\/[a-z0-9_.-]+$/i.test(a))continue;const s=[];let i=0,c=!1;for(;n<t.length;){const d=t[n]??"";if(n+=1,!(!c&&!d.trim().startsWith("{"))&&(c=!0,s.push(d),i+=f(d,"{")-f(d,"}"),c&&i<=0))break}const r=w(s.join(`
|
|
3
|
+
`));if(!r)continue;const l=M(r.name,r.id,a)??a;o.push({modelId:a,displayName:Q(l,r),description:ee(r),metadata:r})}return o}function G(e){const t=new Set;for(const a of e){const s=a.metadata.variants;if(!(!s||typeof s!="object"||Array.isArray(s)))for(const i of Object.keys(s))t.add(i)}const o=["minimal","low","medium","high","max"].filter(a=>t.has(a));return(o.length?o:["minimal","high","max"]).map(a=>({value:a,name:te(a),description:a==="high"?"More reasoning for difficult tasks":a==="max"?"Maximum reasoning when supported":"Fastest responses"}))}function Q(e,t){return Z(t)&&!/\bfree\b/i.test(e)?`${e} (free)`:e}function Z(e){const t=e.cost;if(!t||typeof t!="object"||Array.isArray(t))return!1;const o=t,n=o.cache&&typeof o.cache=="object"&&!Array.isArray(o.cache)?o.cache:{};return[o.input,o.output,n.read,n.write].every(a=>Number(a??0)===0)}function ee(e){const t=e.limit&&typeof e.limit=="object"&&!Array.isArray(e.limit)?e.limit:{},o=Number(t.context??0);return o>0?`Context ${o}`:"MimoCode model"}function w(e){try{const t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:null}catch{return null}}function M(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim()}function te(e){return e.slice(0,1).toUpperCase()+e.slice(1)}function f(e,t){let o=0;for(const n of e)n===t&&(o+=1);return o}function ge(e){let t=!1;const o=async(s,i="heartbeat")=>{const c=new Date().toISOString();try{const r=await R(C(),{includeProviderDetails:s});if(r.skipped){h({lastHeartbeatAt:c,lastHeartbeatStatus:"skipped",lastHeartbeatReason:r.reason??"unknown"}),e.debug(`${i} skipped: ${r.reason??"unknown"}`);return}h({lastHeartbeatAt:c,lastHeartbeatStatus:"ok",lastHeartbeatProjectCount:r.projectCount??null,presenceExpiresAtMs:r.presenceExpiresAtMs??null}),e.debug(`${i} ok projects=${r.projectCount??0}`)}catch(r){const l=r instanceof Error?r.message:String(r);h({lastHeartbeatAt:c,lastHeartbeatStatus:"error",lastHeartbeatError:l}),e.warn(`${i} failed: ${l}`)}},n=async()=>{if(!t){t=!0;try{await o(!0)}finally{t=!1}}};return(async()=>{if(!t){t=!0;try{await o(!1,"initial presence heartbeat"),await o(!0)}finally{t=!1}}})(),setInterval(()=>{n()},A)}function h(e){const t=v(b())??{};x(b(),{...t,heartbeatIntervalMs:A,...e})}export{A as HEARTBEAT_INTERVAL_MS,T as reportedAicaVersion,R as sendHeartbeat,ge as startHeartbeatLoop};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function m(o){const e=f(o.acpRun),t=s(e.configOptionValues),i=s(o.configOptionValues),n={...t,...i},r=d(o.model,i.model,e.model,t.model),c=d(o.mode,i.mode,e.mode,t.mode);return r&&"model"in n&&(n.model=r),c&&"mode"in n&&(n.mode=c),{model:r,mode:c,configOptionValues:o.configOptionValues!==void 0||Object.keys(t).length?n:void 0}}function f(o){return o&&typeof o=="object"&&!Array.isArray(o)?o:{}}function s(o){return Object.fromEntries(Object.entries(f(o)).filter(e=>typeof e[1]=="string"||typeof e[1]=="boolean"))}function d(...o){return o.find(e=>typeof e=="string"&&!!e.trim())?.trim()}export{m as resolveJobExecutionConfig};
|
package/dist/core/job-worker.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createJobInteractions as Y}from"../interactions/job-interactions.js";import{createJobSupplementReceiver as Z}from"./job-supplements.js";import{buildPromptWithAttachments as ee,buildPromptBlocks as te,materializeJobAttachments as ne,summarizeMaterializedAttachments as L}from"./job-attachments.js";import{managementActivity as g}from"./management/activity.js";import{managedClaim as oe}from"./management/claims.js";import{startJobArtifactMonitor as ie,mergeArtifactFiles as D}from"./artifacts/job-artifact-monitor.js";import ae from"node:fs";import{compactAgentContext as re,forkAgentSession as se,sendAgentPrompt as ce}from"../acp/agent.js";import{acaServerRequest as A}from"./aca-server-client.js";import{loadAcaConfig as le}from"./aca-config.js";import{prepareLocalWorktree as de}from"./worktree.js";import{expandWorkerRuntimePath as ue}from"./worker-runtime-paths.js";import{collectAffectedFiles as V,extractAffectedFilePathsFromAcpRaw as pe}from"./affected-files.js";import{resolveJobPermissionPolicy as me}from"./job-permission-policy.js";import{createAcpEventBatcher as fe}from"./acp-event-batcher.js";import{createToolLifecycleTrace as he}from"../acp/tool-lifecycle-trace.js";import{shouldBufferAcpEvent as we}from"./acp-event-upload-filter.js";import{userFacingAcpError as Ie}from"../acp/error-display.js";import{ensureCodexProviderRuntimeRevision as Se}from"./codex-provider-runtime-config.js";import{acpSessionInvalidationFromError as be}from"../acp/session-errors.js";const ye=1500,q=5e3,K=900,ve=1e4,Ae=0,_e=12e3;function Ce(e){return ue(e)}function Te(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 ut(e,t={}){let n=!1,s=null,i=null,l=0,w=0;const I=new Set,a=new Map,y=new Map,_=async()=>{if(n||g.paused||g.stopping||Date.now()<w)return;const m=Pe();if(!X(I.size,m))return;const S=g.enter("poll");if(S)try{const u=t.loadConfig?.()??le();if(!u.token)return;for(;!n&&!g.paused&&!g.stopping&&X(I.size,m);){const v=t.claimNextJob?await t.claimNextJob(u,[...a.keys()]):await oe("job",()=>A("POST","/api/client/jobs/claim",{machineId:u.machineId,workspaceId:u.defaultWorkspaceId,activeSessionIds:[...a.keys()]}),{workspaceId:u.defaultWorkspaceId,activeSessionIds:[...a.keys()]});if(l=0,w=0,!v.item)break;const p=v.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(p,u):J(p.job_id,u.machineId,"cancelled")).catch(()=>{});break}const b=p.session_id,f=a.get(b),$=g.track("job");I.add(p.job_id);const k=new AbortController;let j;j=(async()=>{f&&(e.warn(`claimed job=${p.job_id} for already active session=${b}; waiting for the previous run to release its ACP runtime`),await ge(f,p,u,k)),!k.signal.aborted&&await(t.runJob??ke)(p,u,e,k)})().catch(R=>{g.uncertain.add("job-finalize"),e.warn(`job ${p.job_id} failed outside handler: ${R instanceof Error?R.message:String(R)}`)}).finally(()=>{$(),I.delete(p.job_id),a.get(b)===j&&a.delete(b),y.delete(p.job_id)}),a.set(b,j),y.set(p.job_id,{controller:k,promise:j})}}catch(u){if(!n){l+=1;const v=Be(l);w=Date.now()+v,e.warn(`job worker failed, retry in ${v}ms: ${u instanceof Error?u.message:String(u)}`)}}finally{S()}},M=()=>{if(n||s)return;const m=_();s=m,m.finally(()=>{s===m&&(s=null)})};M();const c=setInterval(()=>{M()},t.claimIntervalMs??ye);return{timer:c,stop:()=>i||(i=(async()=>{if(n)return;n=!0,clearInterval(c);for(const S of y.values())S.controller.abort();await s?.catch(()=>{});for(const S of y.values())S.controller.abort();const m=[...y.values()].map(S=>S.promise);m.length!==0&&await Promise.race([Promise.allSettled(m).then(()=>{}),We(_e)])})(),i)}}async function ge(e,t,n,s){let i=!1;const w=setInterval(()=>{i||s.signal.aborted||(i=!0,Promise.allSettled([A("POST",`/api/client/jobs/${encodeURIComponent(t.job_id)}/heartbeat`,{machineId:n.machineId}),H(t.job_id,s)]).finally(()=>{i=!1}))},q);try{await Promise.race([e,Re(s.signal)])}finally{clearInterval(w)}}function Re(e){return e.aborted?Promise.resolve():new Promise(t=>e.addEventListener("abort",()=>t(),{once:!0}))}function Pe(){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?Ae:t===0?0:Math.max(1,Math.min(t,8))}function X(e,t){return t===0||e<t}async function ke(e,t,n,s){const i=e.job_id,l=e.session_id,w=e.workspace_id,I=e.project_id??null,a=e.input??{},y=Y(i,t.machineId,String(a.acpRun?.agentType||"codex"),s.signal),_=String(a.control?.type||"").toLowerCase();if(!_&&!a.message?.trim()&&(a.attachments??[]).length===0&&!a.sessionReferences?.length){await J(i,t.machineId,"job input missing message");return}const M=t.projects.find(r=>r.projectId===I),c=Ce(M?.rootPath||e.project_root_path||"");if(!c){await J(i,t.machineId,"job project root is unknown");return}Te(e)&&ae.mkdirSync(c,{recursive:!0}),n.info(`claimed job=${i} session=${l} project=${M?.name??I??"unknown"}`);let m=!1,S=!1;const u=setInterval(()=>{m||(m=!0,A("POST",`/api/client/jobs/${encodeURIComponent(i)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}).finally(()=>{m=!1})),S||(S=!0,H(i,s).catch(()=>{}).finally(()=>{S=!1}))},q);let v=0;const p=he(r=>n.info(r)),b=fe(i,t.machineId,{warn:r=>n.warn(r),trace:p}),f=Ne(i,t.machineId),$=new Set;let k=null,j,R;const W=async()=>j?.finish().catch(r=>(g.uncertain.add("job-artifacts"),n.warn(`artifact collection failed job=${i}: ${String(r)}`),[]))??[];try{await f.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const r=de({projectId:I,sessionId:l,projectRoot:c,worktree:e});k=r,await Fe(i,t.machineId,r);const C=Me(a.acpRun);if(await y.initialize(),xe(C.agentType)&&await Se(e.codex_provider_revision),_==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const o=await re({agentType:E(C.agentType,"codex"),cliType:E(C.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,timeoutMs:a.timeoutMs,allowRollover:a.control?.allowRollover===!0,signal:s.signal,onStatus:h=>{f.report({phase:h.phase,label:h.label,detail:h.detail,updateType:h.updateType,raw:h})}});await f.flush(),await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:o.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:a.control?.allowRollover===!0},acpSessionId:o.acpSessionId,durationMs:o.durationMs,usage:o.contextUsage,maintenance:o.maintenance,worktree:N(r)}}),n.info(`completed context compact job=${i}`);return}if(_==="fork"){const o=String(a.control?.sourceAcpSessionId||"").trim();if(!o)throw new Error("Source session does not have a Codex ACP context to fork");await f.sendNow({phase:"forking",label:"正在分叉 Codex 会话",detail:a.control?.sourceSessionId?`源会话 ${a.control.sourceSessionId}`:void 0}).catch(()=>{});const h=await se({agentType:E(C.agentType,"codex"),cliType:E(C.cliType,"builtin"),sourceProviderSessionId:o,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,timeoutMs:a.timeoutMs,signal:s.signal,onStatus:x=>{f.report({phase:x.phase,label:x.label,detail:x.detail,updateType:x.updateType,raw:x})}});await f.flush(),await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:h.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:a.control?.sourceSessionId||null},acpSessionId:h.acpSessionId,worktree:N(r)}}),n.info(`completed Codex session fork job=${i}`);return}const T=await ne({cwd:r.cwd,sessionId:l,jobId:i,attachments:a.attachments??[]});T.length>0&&(n.info(`materialized ${T.length} attachment(s) for job=${i}`),await f.sendNow({phase:"initializing",label:"附件已写入本机",detail:T.map(o=>o.relativePath).join(", "),raw:{attachments:L(T)}}).catch(()=>{}));const O=ee(a,T),P=te(O,T);j=await ie(r.cwd,o=>n.warn(o)).catch(o=>{n.warn(`artifact monitor unavailable job=${i}: ${String(o)}`)}),a.automation||(R=Z({jobId:i,sessionId:l,cwd:r.cwd,logger:n,signal:s.signal}));const d=await ce({agentType:E(C.agentType,"codex"),cliType:E(C.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:je(e.session_raw),prompt:O,promptBlocks:P,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,canExecuteTools:a.canExecuteTools,timeoutMs:a.timeoutMs,contextWindow:Ee(C.contextWindow),signal:s.signal,onSupplementReady:o=>R?.setChannel(o),onStatus:o=>{f.report({phase:o.phase,label:o.label,detail:o.detail,updateType:o.updateType,raw:o})},onUpdate:o=>{v+=1,(o.type==="tool_call"||o.type==="tool_call_update"&&["completed","failed","cancelled"].includes(o.status||""))&&p("worker_event",{jobId:i,uploadRunId:b.uploadRunId,toolCallId:o.toolCallId,status:o.status,sequence:v});for(const x of pe(o.raw))$.add(x);const h={turnId:e.user_turn_id??null,eventType:o.type,label:o.label,text:o.text,status:o.status,toolCallId:o.toolCallId,raw:Je(o.raw,v)};we(h)&&b.push(h)},onPermissionRequest:o=>{const h=me(a,o);return h?Promise.resolve(h):y.permission(o)},onUserInputRequest:o=>y.input(o),onProviderSessionMigration:async o=>{const h=await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/acp-session`,{machineId:t.machineId,previousAcpSessionId:o.previousProviderSessionId,acpSessionId:o.providerSessionId,reason:o.reason,migratedAtMs:o.migratedAtMs});e.acp_session_id=o.providerSessionId,n.info(`migrated ACP session for job=${i} ${o.previousProviderSessionId} -> ${o.providerSessionId}`),await b.flush().catch(()=>{}),await f.sendNow({phase:"resuming",label:"Codex 会话已迁移",detail:o.providerSessionId,updateType:"aca.performance.provider_session_migrated",raw:{migration:o,server:{migrated:h.migrated===!0}}}).catch(()=>{})}}),Q=await W(),U=await b.finish();U.complete||g.uncertain.add("job-event-upload"),await f.flush(),await R?.stop();const B=D(V({cwd:r.cwd,evidencePaths:$}),Q),F=$e(d.promptResponse);if(!(Oe(F)||!!d.content)){await J(i,t.machineId,`${d.agentType} stopped: ${F??"unknown"}`,{acpSessionId:d.acpSessionId,stopReason:F,eventUpload:U,files:B,exitCode:d.exitCode,stderr:Le(d.stderr,4e3),worktree:N(r)});return}await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:d.acpSessionId,assistantText:d.content,result:{ok:!0,eventUpload:U,acpSessionId:d.acpSessionId,stopReason:F,durationMs:d.durationMs,acpRun:a.acpRun??null,effectiveConfig:d.effectiveConfig??null,performance:d.performance??null,usage:d.contextUsage??null,providerDiagnostics:d.providerDiagnostics??null,updateCount:d.updateCount,statusTimeline:d.statusTimeline,worktree:N(r),attachments:L(T),files:B}}),n.info(`completed job=${i}`)}catch(r){if(await R?.stop(),ze(s.signal)){b.discard(),f.discard(),n.warn(`stopped local job=${i} because ACA Server already finalized it`);return}const C=await W(),T=await b.finish();T.complete||g.uncertain.add("job-event-upload"),await f.flush();const O=s.signal.aborted?"cancelled":r instanceof Error?r.message:String(r),P=be(r),d=D(k?V({cwd:k.cwd,evidencePaths:$}):[],C);await J(i,t.machineId,Ie(O),{rawError:O,eventUpload:T,files:d,providerDiagnostics:z(r,"providerDiagnostics"),statusTimeline:z(r,"statusTimeline"),performance:z(r,"performance"),...P?{acpSessionInvalidated:!0,invalidatedAcpSessionId:P.providerSessionId,failure:{category:P.category,actions:P.actions,details:P.details}}:{}},P?{invalidateAcpSession:!0,invalidatedAcpSessionId:P.providerSessionId}:void 0)}finally{await y.close(),await R?.stop(),j?.dispose(),b.discard(),clearInterval(u),await f.flush()}}function je(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 Me(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function xe(e){const t=String(e||"codex").toLowerCase().replace(/[^a-z0-9]+/g,"");return t==="codex"||t==="codexacp"||t==="codexstandard"}function Ee(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function E(...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 Oe(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function Ne(e,t){let n=null,s=0,i="",l=null,w=Promise.resolve(),I=!1;const a=c=>JSON.stringify({phase:c.phase??"",label:c.label??"",detail:c.detail??"",updateType:c.updateType??""}),y=async c=>{await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:c.phase,label:c.label,detail:c.detail,updateType:c.updateType,raw:c.raw}),s=Date.now(),i=a(c)},_=()=>{if(l&&(clearTimeout(l),l=null),I)return n=null,w;if(!n)return w;const c=n;return n=null,w=w.then(()=>y(c)).catch(()=>{}),w},M=c=>{l||(l=setTimeout(()=>{_()},c),l.unref())};return{report(c){if(I)return;const m=Date.now(),u=a(c)!==i,v=m-s>=ve;if(!u&&!v)return;n=c;const p=m-s;if(p>=K||s===0){_();return}M(K-p)},sendNow(c){return I?w:(n=c,_())},flush:_,discard(){I=!0,n=null,l&&clearTimeout(l),l=null}}}function Je(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}async function Fe(e,t,n){!n.usedWorktree||!n.worktreeId||await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/worktree`,{machineId:t,baseCommit:n.baseCommit??null,worktreePath:n.cwd,status:n.status??"active",raw:n.raw??N(n)})}function N(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 H(e,t){if(t.signal.aborted)return;const n=await A("GET",`/api/client/jobs/${encodeURIComponent(e)}`);if(Number(n.item.cancel_requested??0)===1){t.abort();return}const s=Ue(n.item);s&&t.abort(new G(e,s))}function Ue(e){const t=String(e.status||"").toLowerCase();return t&&!["claimed","running"].includes(t)?t:null}class G 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 G}async function J(e,t,n,s,i){await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/fail`,{machineId:t,error:n,result:s,...i?.invalidateAcpSession?{invalidateAcpSession:!0,invalidatedAcpSessionId:i.invalidatedAcpSessionId}:{}})}function We(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 Le(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{Ue as serverFinalizedJobStatus,ut as startJobWorkerLoop};
|
|
1
|
+
import{createJobInteractions as Y}from"../interactions/job-interactions.js";import{createJobSupplementReceiver as Z}from"./job-supplements.js";import{buildPromptWithAttachments as ee,buildPromptBlocks as te,materializeJobAttachments as ne,summarizeMaterializedAttachments as L}from"./job-attachments.js";import{managementActivity as g}from"./management/activity.js";import{managedClaim as oe}from"./management/claims.js";import{startJobArtifactMonitor as ie,mergeArtifactFiles as D}from"./artifacts/job-artifact-monitor.js";import ae from"node:fs";import{compactAgentContext as re,forkAgentSession as se,sendAgentPrompt as ce}from"../acp/agent.js";import{acaServerRequest as A}from"./aca-server-client.js";import{loadAcaConfig as le}from"./aca-config.js";import{prepareLocalWorktree as de}from"./worktree.js";import{expandWorkerRuntimePath as ue}from"./worker-runtime-paths.js";import{collectAffectedFiles as V,extractAffectedFilePathsFromAcpRaw as pe}from"./affected-files.js";import{resolveJobPermissionPolicy as me}from"./job-permission-policy.js";import{resolveJobExecutionConfig as fe}from"./job-execution-config.js";import{createAcpEventBatcher as he}from"./acp-event-batcher.js";import{createToolLifecycleTrace as we}from"../acp/tool-lifecycle-trace.js";import{shouldBufferAcpEvent as Ie}from"./acp-event-upload-filter.js";import{userFacingAcpError as Se}from"../acp/error-display.js";import{ensureCodexProviderRuntimeRevision as be}from"./codex-provider-runtime-config.js";import{acpSessionInvalidationFromError as ye}from"../acp/session-errors.js";const ve=1500,q=5e3,K=900,Ae=1e4,_e=0,Te=12e3;function Ce(e){return ue(e)}function ge(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 mt(e,t={}){let n=!1,s=null,i=null,l=0,w=0;const I=new Set,a=new Map,y=new Map,_=async()=>{if(n||g.paused||g.stopping||Date.now()<w)return;const m=ke();if(!X(I.size,m))return;const S=g.enter("poll");if(S)try{const u=t.loadConfig?.()??le();if(!u.token)return;for(;!n&&!g.paused&&!g.stopping&&X(I.size,m);){const v=t.claimNextJob?await t.claimNextJob(u,[...a.keys()]):await oe("job",()=>A("POST","/api/client/jobs/claim",{machineId:u.machineId,workspaceId:u.defaultWorkspaceId,activeSessionIds:[...a.keys()]}),{workspaceId:u.defaultWorkspaceId,activeSessionIds:[...a.keys()]});if(l=0,w=0,!v.item)break;const p=v.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(p,u):N(p.job_id,u.machineId,"cancelled")).catch(()=>{});break}const b=p.session_id,f=a.get(b),$=g.track("job");I.add(p.job_id);const k=new AbortController;let j;j=(async()=>{f&&(e.warn(`claimed job=${p.job_id} for already active session=${b}; waiting for the previous run to release its ACP runtime`),await Re(f,p,u,k)),!k.signal.aborted&&await(t.runJob??je)(p,u,e,k)})().catch(R=>{g.uncertain.add("job-finalize"),e.warn(`job ${p.job_id} failed outside handler: ${R instanceof Error?R.message:String(R)}`)}).finally(()=>{$(),I.delete(p.job_id),a.get(b)===j&&a.delete(b),y.delete(p.job_id)}),a.set(b,j),y.set(p.job_id,{controller:k,promise:j})}}catch(u){if(!n){l+=1;const v=Le(l);w=Date.now()+v,e.warn(`job worker failed, retry in ${v}ms: ${u instanceof Error?u.message:String(u)}`)}}finally{S()}},E=()=>{if(n||s)return;const m=_();s=m,m.finally(()=>{s===m&&(s=null)})};E();const c=setInterval(()=>{E()},t.claimIntervalMs??ve);return{timer:c,stop:()=>i||(i=(async()=>{if(n)return;n=!0,clearInterval(c);for(const S of y.values())S.controller.abort();await s?.catch(()=>{});for(const S of y.values())S.controller.abort();const m=[...y.values()].map(S=>S.promise);m.length!==0&&await Promise.race([Promise.allSettled(m).then(()=>{}),Be(Te)])})(),i)}}async function Re(e,t,n,s){let i=!1;const w=setInterval(()=>{i||s.signal.aborted||(i=!0,Promise.allSettled([A("POST",`/api/client/jobs/${encodeURIComponent(t.job_id)}/heartbeat`,{machineId:n.machineId}),H(t.job_id,s)]).finally(()=>{i=!1}))},q);try{await Promise.race([e,Pe(s.signal)])}finally{clearInterval(w)}}function Pe(e){return e.aborted?Promise.resolve():new Promise(t=>e.addEventListener("abort",()=>t(),{once:!0}))}function ke(){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?_e:t===0?0:Math.max(1,Math.min(t,8))}function X(e,t){return t===0||e<t}async function je(e,t,n,s){const i=e.job_id,l=e.session_id,w=e.workspace_id,I=e.project_id??null,a={...e.input,...fe(e.input??{})},y=Y(i,t.machineId,String(a.acpRun?.agentType||"codex"),s.signal),_=String(a.control?.type||"").toLowerCase();if(!_&&!a.message?.trim()&&(a.attachments??[]).length===0&&!a.sessionReferences?.length){await N(i,t.machineId,"job input missing message");return}const E=t.projects.find(r=>r.projectId===I),c=Ce(E?.rootPath||e.project_root_path||"");if(!c){await N(i,t.machineId,"job project root is unknown");return}ge(e)&&ae.mkdirSync(c,{recursive:!0}),n.info(`claimed job=${i} session=${l} project=${E?.name??I??"unknown"}`);let m=!1,S=!1;const u=setInterval(()=>{m||(m=!0,A("POST",`/api/client/jobs/${encodeURIComponent(i)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}).finally(()=>{m=!1})),S||(S=!0,H(i,s).catch(()=>{}).finally(()=>{S=!1}))},q);let v=0;const p=we(r=>n.info(r)),b=he(i,t.machineId,{warn:r=>n.warn(r),trace:p}),f=Ne(i,t.machineId),$=new Set;let k=null,j,R;const W=async()=>j?.finish().catch(r=>(g.uncertain.add("job-artifacts"),n.warn(`artifact collection failed job=${i}: ${String(r)}`),[]))??[];try{await f.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const r=de({projectId:I,sessionId:l,projectRoot:c,worktree:e});k=r,await Ue(i,t.machineId,r);const T=Me(a.acpRun);if(await y.initialize(),xe(T.agentType)&&await be(e.codex_provider_revision),_==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const o=await re({agentType:x(T.agentType,"codex"),cliType:x(T.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,timeoutMs:a.timeoutMs,allowRollover:a.control?.allowRollover===!0,signal:s.signal,onStatus:h=>{f.report({phase:h.phase,label:h.label,detail:h.detail,updateType:h.updateType,raw:h})}});await f.flush(),await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:o.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:a.control?.allowRollover===!0},acpSessionId:o.acpSessionId,durationMs:o.durationMs,usage:o.contextUsage,maintenance:o.maintenance,worktree:J(r)}}),n.info(`completed context compact job=${i}`);return}if(_==="fork"){const o=String(a.control?.sourceAcpSessionId||"").trim();if(!o)throw new Error("Source session does not have an ACP context to fork");await f.sendNow({phase:"forking",label:"正在分叉会话",detail:a.control?.sourceSessionId?`源会话 ${a.control.sourceSessionId}`:void 0}).catch(()=>{});const h=await se({agentType:x(T.agentType,"codex"),cliType:x(T.cliType,"builtin"),sourceProviderSessionId:o,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,timeoutMs:a.timeoutMs,signal:s.signal,onStatus:M=>{f.report({phase:M.phase,label:M.label,detail:M.detail,updateType:M.updateType,raw:M})}});await f.flush(),await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:h.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:a.control?.sourceSessionId||null},acpSessionId:h.acpSessionId,worktree:J(r)}}),n.info(`completed session fork job=${i}`);return}const C=await ne({cwd:r.cwd,sessionId:l,jobId:i,attachments:a.attachments??[]});C.length>0&&(n.info(`materialized ${C.length} attachment(s) for job=${i}`),await f.sendNow({phase:"initializing",label:"附件已写入本机",detail:C.map(o=>o.relativePath).join(", "),raw:{attachments:L(C)}}).catch(()=>{}));const O=ee(a,C),P=te(O,C);j=await ie(r.cwd,o=>n.warn(o)).catch(o=>{n.warn(`artifact monitor unavailable job=${i}: ${String(o)}`)}),a.automation||(R=Z({jobId:i,sessionId:l,cwd:r.cwd,logger:n,signal:s.signal}));const d=await ce({agentType:x(T.agentType,"codex"),cliType:x(T.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:Ee(e.session_raw),prompt:O,promptBlocks:P,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,canExecuteTools:a.canExecuteTools,timeoutMs:a.timeoutMs,contextWindow:$e(T.contextWindow),signal:s.signal,onSupplementReady:o=>R?.setChannel(o),onStatus:o=>{f.report({phase:o.phase,label:o.label,detail:o.detail,updateType:o.updateType,raw:o})},onUpdate:o=>{v+=1,(o.type==="tool_call"||o.type==="tool_call_update"&&["completed","failed","cancelled"].includes(o.status||""))&&p("worker_event",{jobId:i,uploadRunId:b.uploadRunId,toolCallId:o.toolCallId,status:o.status,sequence:v});for(const M of pe(o.raw))$.add(M);const h={turnId:e.user_turn_id??null,eventType:o.type,label:o.label,text:o.text,status:o.status,toolCallId:o.toolCallId,raw:Fe(o.raw,v)};Ie(h)&&b.push(h)},onPermissionRequest:o=>{const h=me(a,o);return h?Promise.resolve(h):y.permission(o)},onUserInputRequest:o=>y.input(o),onProviderSessionMigration:async o=>{const h=await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/acp-session`,{machineId:t.machineId,previousAcpSessionId:o.previousProviderSessionId,acpSessionId:o.providerSessionId,reason:o.reason,migratedAtMs:o.migratedAtMs});e.acp_session_id=o.providerSessionId,n.info(`migrated ACP session for job=${i} ${o.previousProviderSessionId} -> ${o.providerSessionId}`),await b.flush().catch(()=>{}),await f.sendNow({phase:"resuming",label:"Codex 会话已迁移",detail:o.providerSessionId,updateType:"aca.performance.provider_session_migrated",raw:{migration:o,server:{migrated:h.migrated===!0}}}).catch(()=>{})}}),Q=await W(),z=await b.finish();z.complete||g.uncertain.add("job-event-upload"),await f.flush(),await R?.stop();const B=D(V({cwd:r.cwd,evidencePaths:$}),Q),F=Oe(d.promptResponse);if(!(Je(F)||!!d.content)){await N(i,t.machineId,`${d.agentType} stopped: ${F??"unknown"}`,{acpSessionId:d.acpSessionId,stopReason:F,eventUpload:z,files:B,exitCode:d.exitCode,stderr:De(d.stderr,4e3),worktree:J(r)});return}await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:d.acpSessionId,assistantText:d.content,result:{ok:!0,eventUpload:z,acpSessionId:d.acpSessionId,stopReason:F,durationMs:d.durationMs,acpRun:a.acpRun??null,effectiveConfig:d.effectiveConfig??null,performance:d.performance??null,usage:d.contextUsage??null,providerDiagnostics:d.providerDiagnostics??null,updateCount:d.updateCount,statusTimeline:d.statusTimeline,worktree:J(r),attachments:L(C),files:B}}),n.info(`completed job=${i}`)}catch(r){if(await R?.stop(),We(s.signal)){b.discard(),f.discard(),n.warn(`stopped local job=${i} because ACA Server already finalized it`);return}const T=await W(),C=await b.finish();C.complete||g.uncertain.add("job-event-upload"),await f.flush();const O=s.signal.aborted?"cancelled":r instanceof Error?r.message:String(r),P=ye(r),d=D(k?V({cwd:k.cwd,evidencePaths:$}):[],T);await N(i,t.machineId,Se(O),{rawError:O,eventUpload:C,files:d,providerDiagnostics:U(r,"providerDiagnostics"),failure:U(r,"failure"),statusTimeline:U(r,"statusTimeline"),performance:U(r,"performance"),...P?{acpSessionInvalidated:!0,invalidatedAcpSessionId:P.providerSessionId,failure:{category:P.category,actions:P.actions,details:P.details}}:{}},P?{invalidateAcpSession:!0,invalidatedAcpSessionId:P.providerSessionId}:void 0)}finally{await y.close(),await R?.stop(),j?.dispose(),b.discard(),clearInterval(u),await f.flush()}}function Ee(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 Me(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function xe(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 x(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function Oe(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 Je(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function Ne(e,t){let n=null,s=0,i="",l=null,w=Promise.resolve(),I=!1;const a=c=>JSON.stringify({phase:c.phase??"",label:c.label??"",detail:c.detail??"",updateType:c.updateType??""}),y=async c=>{await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:c.phase,label:c.label,detail:c.detail,updateType:c.updateType,raw:c.raw}),s=Date.now(),i=a(c)},_=()=>{if(l&&(clearTimeout(l),l=null),I)return n=null,w;if(!n)return w;const c=n;return n=null,w=w.then(()=>y(c)).catch(()=>{}),w},E=c=>{l||(l=setTimeout(()=>{_()},c),l.unref())};return{report(c){if(I)return;const m=Date.now(),u=a(c)!==i,v=m-s>=Ae;if(!u&&!v)return;n=c;const p=m-s;if(p>=K||s===0){_();return}E(K-p)},sendNow(c){return I?w:(n=c,_())},flush:_,discard(){I=!0,n=null,l&&clearTimeout(l),l=null}}}function Fe(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}async function Ue(e,t,n){!n.usedWorktree||!n.worktreeId||await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/worktree`,{machineId:t,baseCommit:n.baseCommit??null,worktreePath:n.cwd,status:n.status??"active",raw:n.raw??J(n)})}function J(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 H(e,t){if(t.signal.aborted)return;const n=await A("GET",`/api/client/jobs/${encodeURIComponent(e)}`);if(Number(n.item.cancel_requested??0)===1){t.abort();return}const s=ze(n.item);s&&t.abort(new G(e,s))}function ze(e){const t=String(e.status||"").toLowerCase();return t&&!["claimed","running"].includes(t)?t:null}class G extends Error{constructor(t,n){super(`ACA Server already finalized job=${t} status=${n}`),this.name="ServerFinalizedJobError"}}function We(e){return e.aborted&&e.reason instanceof G}async function N(e,t,n,s,i){await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/fail`,{machineId:t,error:n,result:s,...i?.invalidateAcpSession?{invalidateAcpSession:!0,invalidatedAcpSessionId:i.invalidatedAcpSessionId}:{}})}function Be(e){return new Promise(t=>setTimeout(t,e))}function Le(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 U(e,t){return!e||typeof e!="object"||Array.isArray(e)?null:e[t]??null}export{ze as serverFinalizedJobStatus,mt as startJobWorkerLoop};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{providerInstallOperation as q}from"./provider-installer.js";import{executeExternalCodexOperation as tt}from"./external-codex-worker.js";import{workBuddyAuthManager as et}from"./workbuddy-auth-worker.js";import{managementActivity as T}from"./management/activity.js";import{managedClaim as rt}from"./management/claims.js";import{activeTerminalService as ot}from"./terminal/service.js";import{execFileSync as _}from"node:child_process";import{randomUUID as I}from"node:crypto";import l from"node:fs";import F from"node:os";import u from"node:path";import{acaServerDownloadToFile as it,acaServerRequest as E}from"./aca-server-client.js";import{loadAcaConfig as M,upsertLocalProject as nt}from"./aca-config.js";import{startLongPollWorker as st}from"./long-poll-worker.js";import{expandWorkerRuntimePath as at}from"./worker-runtime-paths.js";import{syncCodexProviderRuntimeConfig as ct}from"./codex-provider-runtime-config.js";import{defaultAcpRuntimePool as lt}from"../acp/client/acp-runtime-pool.js";import{execPowerShell as L}from"./machine-filesystem/powershell.js";const O=25e3,ut=O+1e4,x=300,dt=3e4;let b=null;function fe(t){return st({run:async()=>{const e=M();if(!e.token)throw new Error("Worker 尚未配置认证令牌");const r=await rt("machine",()=>E("GET",`/api/client/machine-operations/claim?machineId=${encodeURIComponent(e.machineId)}&waitMs=${O}`,void 0,{timeoutMs:ut}));if(!r.item)return;const o=T.track("machine");try{const i=await ft(r.item);await E("POST",`/api/client/machine-operations/${encodeURIComponent(r.item.requestId)}/complete`,{result:i})}catch(i){const s=i instanceof Error?i.message:String(i);t.warn(`machine filesystem request failed request=${r.item.requestId}: ${s}`),await E("POST",`/api/client/machine-operations/${encodeURIComponent(r.item.requestId)}/fail`,{message:s}).catch(()=>{T.uncertain.add("machine-finalize")})}finally{o()}},onError:(e,r)=>t.warn(`machine filesystem worker failed, retry in ${r}ms: ${e instanceof Error?e.message:String(e)}`)})}async function ft(t){if(t.operation==="provider_install")return q(t.payload);if(t.operation==="workbuddy_auth")return et.execute(String(t.payload.action||""),t.payload);if(t.operation.startsWith("external_codex_"))return tt(t.operation,t.payload);if(t.operation==="sync_codex_provider_config"){const e=await ct(),r=await lt.retireProviderRuntimes("codex-acp");return{configured:!!e,revision:e?.revision??null,retiredRuntimes:r}}if(t.operation==="filesystem_roots")return It();if(t.operation==="list_directory")return Ft({directoryPath:String(t.payload.path||""),includeHidden:t.payload.includeHidden===!0});if(t.operation==="register_project")return Wt(t.payload);if(t.operation==="list_project_directory")return ht(t.payload);if(t.operation==="upload_project_file")return yt(t.payload);if(t.operation==="mutate_project_files")return gt(t.payload);if(t.operation==="get_project_git_status")return B(t.payload);if(t.operation==="get_project_git_diff")return bt(t.payload);if(t.operation==="get_project_git_log")return jt(t.payload);if(t.operation==="get_project_git_commit")return vt(t.payload);if(t.operation==="get_worktree_status")return k(t.payload);if(t.operation==="commit_worktree")return mt(t.payload);if(t.operation==="cleanup_worktree")return pt(t.payload);throw new Error("不支持的 Machine 文件系统操作。 ")}function k(t){const e=$(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:Rt(r),changedFiles:null,ahead:0};const o=B({rootPath:r}),i=String(t.baseCommit||"").trim(),s=i&&/^[0-9a-f]{7,64}$/i.test(i)?Dt(r,i):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 mt(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 仓库,无法提交。");_("git",["-C",e,"add","-A"],{windowsHide:!0,stdio:"pipe",timeout:3e4});try{return _("git",["-C",e,"diff","--cached","--quiet"],{windowsHide:!0,stdio:"pipe",timeout:1e4}),{committed:!1,reason:"no_changes",...k(t)}}catch(o){if(Number(o.status)!==1)throw o}return _("git",["-C",e,"commit","-m",r],{windowsHide:!0,stdio:"pipe",timeout:6e4}),{committed:!0,commit:m(e,["rev-parse","HEAD"],8*1024).trim(),...k(t)}}function pt(t){const e=$(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(ot?.hasRunningInDirectory(o,String(t.sessionId||"")))throw new Error("请先结束此目录中的会话终端,再清理 Worktree。");const i=String(t.branchName||"").trim(),s=w(o)&&w(r);if(s){if(_("git",["-C",r,"worktree","remove","--force",o],{windowsHide:!0,stdio:"pipe",timeout:6e4}),t.deleteBranch===!0&&i)try{_("git",["-C",r,"branch","-D",i],{windowsHide:!0,stdio:"pipe",timeout:3e4})}catch(n){const c=String(n.stderr||n);if(!/not found|not exist/i.test(c))throw n}}else l.rmSync(o,{recursive:!0,force:!0,maxRetries:2,retryDelay:200});return{removed:!0,branchDeleted:!!(s&&t.deleteBranch===!0&&i)}}function ht(t){const e=p(String(t.rootPath||"")),r=g(String(t.relativePath||"")),o=v(e,r,!0),i=Math.max(0,Number(t.offset||0)),s=Math.min(300,Math.max(20,Number(t.limit||200))),n=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:J(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:n==="name"?void 0:wt(u.join(o,a.name))})).filter(a=>a.type!=="other").sort((a,f)=>{if(a.type!==f.type)return a.type==="directory"?-1:1;if(n!=="name"){if(a.modifiedAt==null&&f.modifiedAt!=null)return 1;if(f.modifiedAt==null&&a.modifiedAt!=null)return-1;const P=(a.modifiedAt??0)-(f.modifiedAt??0);if(P)return n==="modified-desc"?-P:P}return a.name.localeCompare(f.name,void 0,{numeric:!0,sensitivity:"base"})}),d=c.slice(i,i+s);return{path:r,rootKind:t.rootKind==="worktree"?"worktree":"project",items:d,total:c.length,nextOffset:i+d.length<c.length?i+d.length:null}}function wt(t){try{const e=l.lstatSync(t);return e.isSymbolicLink()?null:e.mtimeMs}catch{return null}}async function yt(t){const e=String(t.uploadId||"").trim();if(!e)throw new Error("项目上传任务缺少 uploadId。");const r=p(String(t.rootPath||"")),o=g(String(t.relativeDirectory||"")),i=v(r,o,!0),s=H(String(t.fileName||"")),n=Number(t.sizeBytes),c=String(t.sha256||"").trim().toLowerCase();if(!Number.isSafeInteger(n)||n<0)throw new Error("项目上传文件大小无效。");if(!/^[0-9a-f]{64}$/.test(c))throw new Error("项目上传文件摘要无效。");const d=u.join(i,`.aca-upload-${I()}.tmp`);try{const a=await it(`/api/client/project-uploads/${encodeURIComponent(e)}`,d,{maxBytes:n,timeoutMs:3e5});if(a.sizeBytes!==n)throw new Error("项目上传文件大小校验失败。");if(a.sha256.toLowerCase()!==c)throw new Error("项目上传文件完整性校验失败。");const f=Pt(r,o,s,d);return{name:u.basename(f),path:J(r,f),directory:o,size:a.sizeBytes,sha256:a.sha256,renamed:u.basename(f)!==s,rootKind:t.rootKind==="worktree"?"worktree":"project"}}finally{l.rmSync(d,{force:!0})}}function gt(t){const e=p(String(t.rootPath||"")),r=t.action&&typeof t.action=="object"?t.action:{};if(r.type==="delete")return St(e,String(r.path||""),r.recursive===!0,t.rootKind);throw new Error("project_file_operation_unsupported")}function St(t,e,r,o){const i=g(e);if(!i)throw new Error("project_file_root_protected");_t(i);const s=i.split("/"),n=s.slice(0,-1).join("/"),c=v(t,n,!0),d=u.join(c,s.at(-1)||"");let a;try{a=l.lstatSync(d)}catch(f){throw f.code==="ENOENT"?new Error("project_file_not_found"):f}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(d).length>0)throw new Error("project_directory_not_empty");return a.isDirectory()&&r?l.rmSync(d,{recursive:!0,force:!1,maxRetries:2,retryDelay:150}):a.isDirectory()?l.rmdirSync(d):l.unlinkSync(d),{operation:"delete",path:i,entryType:a.isDirectory()?"directory":"file",parentPath:n,affectedDirectories:[n],rootKind:o==="worktree"?"worktree":"project"}}function _t(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 i=p(t),s=g(e),n=v(i,s,!0),c=H(r);if(l.realpathSync.native(u.dirname(o))!==n)throw new Error("项目上传临时文件不在目标目录中。");const a=l.lstatSync(o);if(!a.isFile()||a.isSymbolicLink())throw new Error("项目上传临时文件无效。");for(let f=1;f<=1e4;f+=1){const P=f===1?c:Et(c,f),S=u.join(n,P);try{return l.linkSync(o,S),l.chmodSync(S,420),l.rmSync(o,{force:!0}),S}catch(A){if(G(A))continue;if(!xt(A))throw A;try{return l.copyFileSync(o,S,l.constants.COPYFILE_EXCL),l.chmodSync(S,420),l.rmSync(o,{force:!0}),S}catch(N){if(G(N))continue;throw N}}}throw new Error("同名文件过多,无法生成可用文件名。")}function H(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 Et(t,e){const r=u.extname(t);return`${r?t.slice(0,-r.length):t} (${e})${r}`}function G(t){return!!(t&&typeof t=="object"&&t.code==="EEXIST")}function xt(t){const e=t&&typeof t=="object"?t.code:"";return["EPERM","EACCES","EXDEV","ENOTSUP","EOPNOTSUPP"].includes(String(e||""))}const W=500,j=2*1024*1024,y=2*1024*1024;function B(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,W),i=o.map(c=>kt(c)).filter(c=>!!c),s=D(m(e,["diff","--no-ext-diff","--numstat","-z","--no-renames","HEAD","--","."],j));for(const c of i){const d=s.get(c.path);if(d)c.additions=d.additions,c.deletions=d.deletions;else if(c.status==="untracked"){const a=U(e,c.path);c.additions=a.additions,c.deletions=a.deletions}}const n=Tt(e);return{isRepository:!0,branch:Nt(e),files:i,ahead:n.ahead,behind:n.behind,truncated:o.length>=W}}function bt(t){const e=p(String(t.rootPath||"")),r=g(String(t.relativePath||""));if(!r)throw new Error("Diff 文件路径不能为空。");if(!w(e))return{isRepository:!1,path:r,diff:"",additions:0,deletions:0};const o=K(e,r),i=Ct(e,r),s=i?m(e,["diff","--no-ext-diff","--unified=80","HEAD","--",r],y):X(e,["diff","--no-ext-diff","--no-index","--unified=80",process.platform==="win32"?"NUL":"/dev/null",o],y),n=i?D(m(e,["diff","--no-ext-diff","--numstat","-z","--no-renames","HEAD","--",r],32*1024)).get(r):U(e,r);return{isRepository:!0,path:r,diff:V(s),additions:n?.additions??0,deletions:n?.deletions??0,truncated:s.length>y}}function jt(t){const e=p(String(t.rootPath||""));if(!w(e))return{isRepository:!1,items:[]};if(!$t(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(n=>At(n)).filter(n=>!!n),limit:r}}function vt(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],y),i=[...D(m(e,["show","--no-ext-diff","--numstat","-z","--no-renames","--format=",r],j)).values()];return{isRepository:!0,commit:r,diff:V(o),additions:i.reduce((s,n)=>s+(n.additions||0),0),deletions:i.reduce((s,n)=>s+(n.deletions||0),0),changedFiles:i.length,truncated:o.length>y}}function At(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 kt(t){if(t.length<4)return null;const e=t[0]||" ",r=t[1]||" ",o=g(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=z(r);if(!o||!o.path)continue;const i=g(o.path);i&&e.set(i,{additions:o.additions,deletions:o.deletions})}return e}function U(t,e){const r=K(t,e),i=X(t,["diff","--no-ext-diff","--no-index","--numstat","-z","--",process.platform==="win32"?"NUL":"/dev/null",r],32*1024).split("\0",1)[0]||"",s=z(i);return s?{additions:s.additions,deletions:s.deletions}:{additions:null,deletions:null}}function z(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),i=t.slice(e+1,r);return{additions:/^\d+$/.test(o)?Number(o):null,deletions:/^\d+$/.test(i)?Number(i):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 Dt(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 Rt(t){try{return l.readdirSync(t).length>0}catch{return!1}}function Ct(t,e){try{return m(t,["ls-files","--error-unmatch","--",e],8*1024),!0}catch{return!1}}function $t(t){try{return m(t,["rev-parse","--verify","HEAD"],8*1024),!0}catch{return!1}}function Nt(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 Tt(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 K(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 _("git",["-C",t,...e],{windowsHide:!0,encoding:"utf8",timeout:2e4,maxBuffer:r,stdio:["ignore","pipe","pipe"]})}function X(t,e,r){try{return m(t,e,r)}catch(o){const i=String(o.stdout||"");if(i)return i;if(Number(o.status)===1)return"";throw o}}function V(t){return t.length<=y?t:`${t.slice(0,y)}
|
|
1
|
+
import{providerInstallOperation as q}from"./provider-installer.js";import{executeExternalCodexOperation as tt}from"./external-codex-worker.js";import{workBuddyAuthManager as et}from"./workbuddy-auth-worker.js";import{workBuddyCredits as rt}from"./workbuddy-credits/worker.js";import{managementActivity as T}from"./management/activity.js";import{managedClaim as ot}from"./management/claims.js";import{activeTerminalService as it}from"./terminal/service.js";import{execFileSync as _}from"node:child_process";import{randomUUID as F}from"node:crypto";import l from"node:fs";import M from"node:os";import u from"node:path";import{acaServerDownloadToFile as nt,acaServerRequest as E}from"./aca-server-client.js";import{loadAcaConfig as k,upsertLocalProject as st}from"./aca-config.js";import{startLongPollWorker as at}from"./long-poll-worker.js";import{expandWorkerRuntimePath as ct}from"./worker-runtime-paths.js";import{syncCodexProviderRuntimeConfig as lt}from"./codex-provider-runtime-config.js";import{defaultAcpRuntimePool as ut}from"../acp/client/acp-runtime-pool.js";import{execPowerShell as L}from"./machine-filesystem/powershell.js";const O=25e3,dt=O+1e4,x=300,ft=3e4;let b=null;function pe(t){return at({run:async()=>{const e=k();if(!e.token)throw new Error("Worker 尚未配置认证令牌");const r=await ot("machine",()=>E("GET",`/api/client/machine-operations/claim?machineId=${encodeURIComponent(e.machineId)}&waitMs=${O}`,void 0,{timeoutMs:dt}));if(!r.item)return;const o=T.track("machine");try{const i=await mt(r.item);await E("POST",`/api/client/machine-operations/${encodeURIComponent(r.item.requestId)}/complete`,{result:i})}catch(i){const s=i instanceof Error?i.message:String(i);t.warn(`machine filesystem request failed request=${r.item.requestId}: ${s}`),await E("POST",`/api/client/machine-operations/${encodeURIComponent(r.item.requestId)}/fail`,{message:s}).catch(()=>{T.uncertain.add("machine-finalize")})}finally{o()}},onError:(e,r)=>t.warn(`machine filesystem worker failed, retry in ${r}ms: ${e instanceof Error?e.message:String(e)}`)})}async function mt(t){if(t.operation==="provider_install")return q(t.payload);if(t.operation==="workbuddy_auth")return et.execute(String(t.payload.action||""),t.payload);if(t.operation==="workbuddy_credits")return{machineId:k().machineId,instanceId:process.env.ACA_WORKER_INSTANCE_ID||String(process.pid),snapshot:await rt.refresh(!0)};if(t.operation.startsWith("external_codex_"))return tt(t.operation,t.payload);if(t.operation==="sync_codex_provider_config"){const e=await lt(),r=await ut.retireProviderRuntimes("codex-acp");return{configured:!!e,revision:e?.revision??null,retiredRuntimes:r}}if(t.operation==="filesystem_roots")return Ft();if(t.operation==="list_directory")return Mt({directoryPath:String(t.payload.path||""),includeHidden:t.payload.includeHidden===!0});if(t.operation==="register_project")return Bt(t.payload);if(t.operation==="list_project_directory")return wt(t.payload);if(t.operation==="upload_project_file")return gt(t.payload);if(t.operation==="mutate_project_files")return St(t.payload);if(t.operation==="get_project_git_status")return B(t.payload);if(t.operation==="get_project_git_diff")return jt(t.payload);if(t.operation==="get_project_git_log")return vt(t.payload);if(t.operation==="get_project_git_commit")return At(t.payload);if(t.operation==="get_worktree_status")return D(t.payload);if(t.operation==="commit_worktree")return pt(t.payload);if(t.operation==="cleanup_worktree")return ht(t.payload);throw new Error("不支持的 Machine 文件系统操作。 ")}function D(t){const e=$(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:Rt(r),changedFiles:null,ahead:0};const o=B({rootPath:r}),i=String(t.baseCommit||"").trim(),s=i&&/^[0-9a-f]{7,64}$/i.test(i)?Ct(r,i):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 pt(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 仓库,无法提交。");_("git",["-C",e,"add","-A"],{windowsHide:!0,stdio:"pipe",timeout:3e4});try{return _("git",["-C",e,"diff","--cached","--quiet"],{windowsHide:!0,stdio:"pipe",timeout:1e4}),{committed:!1,reason:"no_changes",...D(t)}}catch(o){if(Number(o.status)!==1)throw o}return _("git",["-C",e,"commit","-m",r],{windowsHide:!0,stdio:"pipe",timeout:6e4}),{committed:!0,commit:m(e,["rev-parse","HEAD"],8*1024).trim(),...D(t)}}function ht(t){const e=$(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(it?.hasRunningInDirectory(o,String(t.sessionId||"")))throw new Error("请先结束此目录中的会话终端,再清理 Worktree。");const i=String(t.branchName||"").trim(),s=w(o)&&w(r);if(s){if(_("git",["-C",r,"worktree","remove","--force",o],{windowsHide:!0,stdio:"pipe",timeout:6e4}),t.deleteBranch===!0&&i)try{_("git",["-C",r,"branch","-D",i],{windowsHide:!0,stdio:"pipe",timeout:3e4})}catch(n){const c=String(n.stderr||n);if(!/not found|not exist/i.test(c))throw n}}else l.rmSync(o,{recursive:!0,force:!0,maxRetries:2,retryDelay:200});return{removed:!0,branchDeleted:!!(s&&t.deleteBranch===!0&&i)}}function wt(t){const e=p(String(t.rootPath||"")),r=g(String(t.relativePath||"")),o=v(e,r,!0),i=Math.max(0,Number(t.offset||0)),s=Math.min(300,Math.max(20,Number(t.limit||200))),n=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:J(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:n==="name"?void 0:yt(u.join(o,a.name))})).filter(a=>a.type!=="other").sort((a,f)=>{if(a.type!==f.type)return a.type==="directory"?-1:1;if(n!=="name"){if(a.modifiedAt==null&&f.modifiedAt!=null)return 1;if(f.modifiedAt==null&&a.modifiedAt!=null)return-1;const P=(a.modifiedAt??0)-(f.modifiedAt??0);if(P)return n==="modified-desc"?-P:P}return a.name.localeCompare(f.name,void 0,{numeric:!0,sensitivity:"base"})}),d=c.slice(i,i+s);return{path:r,rootKind:t.rootKind==="worktree"?"worktree":"project",items:d,total:c.length,nextOffset:i+d.length<c.length?i+d.length:null}}function yt(t){try{const e=l.lstatSync(t);return e.isSymbolicLink()?null:e.mtimeMs}catch{return null}}async function gt(t){const e=String(t.uploadId||"").trim();if(!e)throw new Error("项目上传任务缺少 uploadId。");const r=p(String(t.rootPath||"")),o=g(String(t.relativeDirectory||"")),i=v(r,o,!0),s=H(String(t.fileName||"")),n=Number(t.sizeBytes),c=String(t.sha256||"").trim().toLowerCase();if(!Number.isSafeInteger(n)||n<0)throw new Error("项目上传文件大小无效。");if(!/^[0-9a-f]{64}$/.test(c))throw new Error("项目上传文件摘要无效。");const d=u.join(i,`.aca-upload-${F()}.tmp`);try{const a=await nt(`/api/client/project-uploads/${encodeURIComponent(e)}`,d,{maxBytes:n,timeoutMs:3e5});if(a.sizeBytes!==n)throw new Error("项目上传文件大小校验失败。");if(a.sha256.toLowerCase()!==c)throw new Error("项目上传文件完整性校验失败。");const f=Et(r,o,s,d);return{name:u.basename(f),path:J(r,f),directory:o,size:a.sizeBytes,sha256:a.sha256,renamed:u.basename(f)!==s,rootKind:t.rootKind==="worktree"?"worktree":"project"}}finally{l.rmSync(d,{force:!0})}}function St(t){const e=p(String(t.rootPath||"")),r=t.action&&typeof t.action=="object"?t.action:{};if(r.type==="delete")return _t(e,String(r.path||""),r.recursive===!0,t.rootKind);throw new Error("project_file_operation_unsupported")}function _t(t,e,r,o){const i=g(e);if(!i)throw new Error("project_file_root_protected");Pt(i);const s=i.split("/"),n=s.slice(0,-1).join("/"),c=v(t,n,!0),d=u.join(c,s.at(-1)||"");let a;try{a=l.lstatSync(d)}catch(f){throw f.code==="ENOENT"?new Error("project_file_not_found"):f}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(d).length>0)throw new Error("project_directory_not_empty");return a.isDirectory()&&r?l.rmSync(d,{recursive:!0,force:!1,maxRetries:2,retryDelay:150}):a.isDirectory()?l.rmdirSync(d):l.unlinkSync(d),{operation:"delete",path:i,entryType:a.isDirectory()?"directory":"file",parentPath:n,affectedDirectories:[n],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 Et(t,e,r,o){const i=p(t),s=g(e),n=v(i,s,!0),c=H(r);if(l.realpathSync.native(u.dirname(o))!==n)throw new Error("项目上传临时文件不在目标目录中。");const a=l.lstatSync(o);if(!a.isFile()||a.isSymbolicLink())throw new Error("项目上传临时文件无效。");for(let f=1;f<=1e4;f+=1){const P=f===1?c:xt(c,f),S=u.join(n,P);try{return l.linkSync(o,S),l.chmodSync(S,420),l.rmSync(o,{force:!0}),S}catch(A){if(G(A))continue;if(!bt(A))throw A;try{return l.copyFileSync(o,S,l.constants.COPYFILE_EXCL),l.chmodSync(S,420),l.rmSync(o,{force:!0}),S}catch(N){if(G(N))continue;throw N}}}throw new Error("同名文件过多,无法生成可用文件名。")}function H(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 xt(t,e){const r=u.extname(t);return`${r?t.slice(0,-r.length):t} (${e})${r}`}function G(t){return!!(t&&typeof t=="object"&&t.code==="EEXIST")}function bt(t){const e=t&&typeof t=="object"?t.code:"";return["EPERM","EACCES","EXDEV","ENOTSUP","EOPNOTSUPP"].includes(String(e||""))}const W=500,j=2*1024*1024,y=2*1024*1024;function B(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,W),i=o.map(c=>Dt(c)).filter(c=>!!c),s=C(m(e,["diff","--no-ext-diff","--numstat","-z","--no-renames","HEAD","--","."],j));for(const c of i){const d=s.get(c.path);if(d)c.additions=d.additions,c.deletions=d.deletions;else if(c.status==="untracked"){const a=U(e,c.path);c.additions=a.additions,c.deletions=a.deletions}}const n=Tt(e);return{isRepository:!0,branch:Nt(e),files:i,ahead:n.ahead,behind:n.behind,truncated:o.length>=W}}function jt(t){const e=p(String(t.rootPath||"")),r=g(String(t.relativePath||""));if(!r)throw new Error("Diff 文件路径不能为空。");if(!w(e))return{isRepository:!1,path:r,diff:"",additions:0,deletions:0};const o=K(e,r),i=It(e,r),s=i?m(e,["diff","--no-ext-diff","--unified=80","HEAD","--",r],y):X(e,["diff","--no-ext-diff","--no-index","--unified=80",process.platform==="win32"?"NUL":"/dev/null",o],y),n=i?C(m(e,["diff","--no-ext-diff","--numstat","-z","--no-renames","HEAD","--",r],32*1024)).get(r):U(e,r);return{isRepository:!0,path:r,diff:V(s),additions:n?.additions??0,deletions:n?.deletions??0,truncated:s.length>y}}function vt(t){const e=p(String(t.rootPath||""));if(!w(e))return{isRepository:!1,items:[]};if(!$t(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(n=>kt(n)).filter(n=>!!n),limit:r}}function At(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],y),i=[...C(m(e,["show","--no-ext-diff","--numstat","-z","--no-renames","--format=",r],j)).values()];return{isRepository:!0,commit:r,diff:V(o),additions:i.reduce((s,n)=>s+(n.additions||0),0),deletions:i.reduce((s,n)=>s+(n.deletions||0),0),changedFiles:i.length,truncated:o.length>y}}function kt(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 Dt(t){if(t.length<4)return null;const e=t[0]||" ",r=t[1]||" ",o=g(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 C(t){const e=new Map;for(const r of t.split("\0")){if(!r)continue;const o=z(r);if(!o||!o.path)continue;const i=g(o.path);i&&e.set(i,{additions:o.additions,deletions:o.deletions})}return e}function U(t,e){const r=K(t,e),i=X(t,["diff","--no-ext-diff","--no-index","--numstat","-z","--",process.platform==="win32"?"NUL":"/dev/null",r],32*1024).split("\0",1)[0]||"",s=z(i);return s?{additions:s.additions,deletions:s.deletions}:{additions:null,deletions:null}}function z(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),i=t.slice(e+1,r);return{additions:/^\d+$/.test(o)?Number(o):null,deletions:/^\d+$/.test(i)?Number(i):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 Ct(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 Rt(t){try{return l.readdirSync(t).length>0}catch{return!1}}function It(t,e){try{return m(t,["ls-files","--error-unmatch","--",e],8*1024),!0}catch{return!1}}function $t(t){try{return m(t,["rev-parse","--verify","HEAD"],8*1024),!0}catch{return!1}}function Nt(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 Tt(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 K(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 _("git",["-C",t,...e],{windowsHide:!0,encoding:"utf8",timeout:2e4,maxBuffer:r,stdio:["ignore","pipe","pipe"]})}function X(t,e,r){try{return m(t,e,r)}catch(o){const i=String(o.stdout||"");if(i)return i;if(Number(o.status)===1)return"";throw o}}function V(t){return t.length<=y?t:`${t.slice(0,y)}
|
|
2
2
|
|
|
3
|
-
[Diff 内容过大,已截断]`}function
|
|
4
|
-
`),o=Z(L(r));for(const i of o){const s=String(i.Root||"");if(!s)continue;const n=Number(i.DriveType||0),c=String(i.Name||s.replace(/[\\/]+$/,"")),d=String(i.VolumeName||"").trim();e.set(h(s,"win32"),{id:`drive-${c.replace(/[^a-z0-9]+/gi,"").toLowerCase()}`,label:d?`${d} (${c})`:
|
|
5
|
-
`),i=Z(L(o,{ACA_MACHINE_FS_PATH:r})),s=i.map(n=>{const c=String(n.Attributes||"");return{name:String(n.Name||""),path:String(n.FullName||""),kind:"directory",hidden:/Hidden|System/i.test(c),hasChildren:!0,reparsePoint:/ReparsePoint/i.test(c)}}).filter(n=>n.name&&n.path&&!n.reparsePoint&&(e||!n.hidden)).slice(0,x).sort((n,c)=>n.name.localeCompare(c.name,void 0,{numeric:!0,sensitivity:"base"}));return Y(r,s,i.length>x,"win32")}function Y(t,e,r,o){const i=o==="win32"?u.win32:u.posix,s=i.parse(t).root,n=h(t,o)===h(s,o)?null:i.dirname(t);return{platform:o,path:t,parentPath:n,breadcrumbs:
|
|
3
|
+
[Diff 内容过大,已截断]`}function Ft(){const t=process.platform,e=R(t);return{platform:t,roots:e}}function Mt(t){if(!t.directoryPath.trim())throw new Error("目录路径不能为空。");return process.platform==="win32"?Gt(t.directoryPath,!!t.includeHidden):Ht(t.directoryPath,!!t.includeHidden)}function h(t,e=process.platform){const r=e==="win32"?u.win32:u.posix,o=r.normalize(t),i=r.parse(o).root,s=o.length>i.length?o.replace(/[\\/]+$/,""):o;return e==="win32"?s.toLocaleLowerCase("en-US"):s}function Lt(){const t=l.realpathSync(M.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(b?.platform===t&&b.expiresAtMs>Date.now())return b.roots;const e=t==="win32"?Ot():Lt();return b={platform:t,roots:e,expiresAtMs:Date.now()+ft},e}function Ot(){const t=l.realpathSync.native(M.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=Z(L(r));for(const i of o){const s=String(i.Root||"");if(!s)continue;const n=Number(i.DriveType||0),c=String(i.Name||s.replace(/[\\/]+$/,"")),d=String(i.VolumeName||"").trim();e.set(h(s,"win32"),{id:`drive-${c.replace(/[^a-z0-9]+/gi,"").toLowerCase()}`,label:d?`${d} (${c})`:Vt(c,n),path:s,kind:Xt(n),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 Ht(t,e){const r=I(t),i=l.readdirSync(r,{withFileTypes:!0}).filter(n=>n.isDirectory()&&!n.isSymbolicLink()&&(e||!n.name.startsWith("."))),s=i.slice(0,x).map(n=>({name:n.name,path:u.join(r,n.name),kind:"directory",hidden:n.name.startsWith("."),hasChildren:!0}));return s.sort((n,c)=>n.name.localeCompare(c.name,void 0,{numeric:!0,sensitivity:"base"})),Y(r,s,i.length>x,"linux")}function Gt(t,e){const r=I(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
|
+
`),i=Z(L(o,{ACA_MACHINE_FS_PATH:r})),s=i.map(n=>{const c=String(n.Attributes||"");return{name:String(n.Name||""),path:String(n.FullName||""),kind:"directory",hidden:/Hidden|System/i.test(c),hasChildren:!0,reparsePoint:/ReparsePoint/i.test(c)}}).filter(n=>n.name&&n.path&&!n.reparsePoint&&(e||!n.hidden)).slice(0,x).sort((n,c)=>n.name.localeCompare(c.name,void 0,{numeric:!0,sensitivity:"base"}));return Y(r,s,i.length>x,"win32")}function Y(t,e,r,o){const i=o==="win32"?u.win32:u.posix,s=i.parse(t).root,n=h(t,o)===h(s,o)?null:i.dirname(t);return{platform:o,path:t,parentPath:n,breadcrumbs:Wt(t,o),readable:Q(t,l.constants.R_OK),writable:Q(t,l.constants.W_OK),items:e,truncated:r}}function Wt(t,e){const r=e==="win32"?u.win32:u.posix,o=r.parse(t),i=t.slice(o.root.length).split(/[\\/]+/).filter(Boolean),s=[{label:o.root.replace(/[\\/]+$/,"")||o.root,path:o.root}];let n=o.root;for(const c of i)n=r.join(n,c),s.push({label:c,path:n});return s}async function Bt(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 i=I(String(t.path||""));l.accessSync(i,l.constants.R_OK|l.constants.W_OK);const s=k().machineId,c=(await E("GET",`/api/projects?workspaceId=${encodeURIComponent(e)}`)).items.find(a=>(!a.machine_id||a.machine_id===s)&&h(a.root_path)===h(i)),d=c||(await E("POST","/api/projects",{projectId:F(),workspaceId:e,name:r,rootPath:i,source:"aica",projectType:o})).item;return st(Ut(d)),{project:d,duplicate:!!c}}function Ut(t){return{projectId:t.project_id,workspaceId:t.workspace_id,name:t.name,rootPath:t.root_path}}function I(t){if(!t.trim()||!u.isAbsolute(t))throw new Error("必须选择绝对目录路径。");if(zt(t),!l.statSync(t).isDirectory())throw new Error("选择的路径不是目录。");const r=l.realpathSync.native(t);return Kt(r),r}function p(t){const e=$(t),r=l.realpathSync.native(e);if(!l.statSync(r).isDirectory())throw new Error("项目根目录不存在。");return r}function $(t){const e=ct(t);if(!e||!u.isAbsolute(e))throw new Error("项目根目录无效。");return u.resolve(e)}function g(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,i=l.realpathSync.native(o),s=u.relative(t,i);if(s.startsWith("..")||u.isAbsolute(s))throw new Error("目录超出项目范围。");const n=l.statSync(i);if(r&&!n.isDirectory())throw new Error("请求路径不是目录。");return i}function J(t,e){return u.relative(t,e).split(u.sep).join("/")}function zt(t){const e=process.platform==="win32"?u.win32:u.posix;if(!R(process.platform).some(o=>{const i=String(o.path||"");if(!i)return!1;const s=e.relative(i,t);return s===""||!s.startsWith("..")&&!e.isAbsolute(s)}))throw new Error("目录不在允许浏览的位置内。")}function Kt(t){const e=process.platform==="win32"?u.win32:u.posix;if(!R(process.platform).some(o=>{const i=String(o.path||"");if(!i)return!1;const s=e.relative(i,t);return s===""||!s.startsWith("..")&&!e.isAbsolute(s)}))throw new Error("目录不在允许浏览的位置内。")}function Q(t,e){try{return l.accessSync(t,e),!0}catch{return!1}}function Z(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 Xt(t){return t===2?"removable":t===3?"fixed":t===4?"network":t===5?"optical":"drive"}function Vt(t,e){return e===2?`移动磁盘 (${t})`:e===4?`网络驱动器 (${t})`:e===5?`光驱 (${t})`:`本地磁盘 (${t})`}export{ht as cleanupWorktree,Et as commitProjectUploadTempFile,pt as commitWorktree,h as comparableMachinePath,At as getProjectGitCommit,jt as getProjectGitDiff,vt as getProjectGitLog,B as getProjectGitStatus,D as getWorktreeStatus,Mt as listMachineDirectory,Ft as listMachineFilesystemRoots,wt as listProjectDirectory,St as mutateProjectFiles,pe as startMachineFilesystemWorkerLoop,gt as uploadProjectFile};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{WorkBuddyAuthManager as r}from"../acp/providers/workbuddy/auth-manager.js";import{sendHeartbeat as
|
|
1
|
+
import{WorkBuddyAuthManager as r}from"../acp/providers/workbuddy/auth-manager.js";import{sendHeartbeat as e}from"./heartbeat.js";import{workBuddyCredits as t}from"./workbuddy-credits/worker.js";const n=new r(void 0,async()=>{t.reset(),await e()});export{n as workBuddyAuthManager};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createWorkBuddyAuthTransport as _}from"../../acp/providers/workbuddy/auth-transport.js";import{enterWorkBuddyTask as k}from"../../acp/providers/workbuddy/auth-gate.js";import{workBuddyRegion as R,workBuddyEnvironmentCredentials as S}from"../../acp/providers/workbuddy/auth-environment.js";import{record as a}from"../../acp/providers/workbuddy/config.js";function m(n,t=null){return{state:n,account:t,remaining:null,total:null,unlimited:!1,resources:[]}}function C(n){if(typeof n!="number"&&(typeof n!="string"||!/^-?\d+(?:\.\d+)?$/.test(n)))return;const t=Number(n);return Number.isFinite(t)?t:void 0}function P(n){const t=[];for(const r of n){const e=a(r);if(e.Status!==void 0&&![0,3].includes(Number(e.Status)))continue;const s=C(e.CycleCapacityRemainPrecise??e.CycleCapacityRemain??e.CapacityRemainPrecise??e.CapacityRemain),o=C(e.CycleCapacitySizePrecise??e.CycleCapacitySize??e.CapacitySizePrecise??e.CapacitySize),c=e.Unlimited===!0||e.Unlimited===1||o===-1;if(s===void 0||o===void 0||!c&&(o<0||s<0))throw new Error("invalid_credits");const g=typeof e.CycleEndTime=="string"&&e.CycleEndTime.length<=100?e.CycleEndTime:null;t.push({name:typeof e.PackageName=="string"?e.PackageName.slice(0,200):"积分套餐",remaining:s,total:o,unlimited:c,cycleEnd:g})}const i=t.some(r=>r.unlimited),l=r=>Math.round(t.reduce((e,s)=>e+s[r],0)*100)/100;return{remaining:i?null:l("remaining"),total:i?null:l("total"),unlimited:i,resources:t}}async function B(n={}){let t=null,i,l;try{l=k();const r=R();i=(n.transport??_)(),await i.initialize();const e=await i.billingIdentity();if(!e.account)return m(S()?"unsupported":"signed_out");if(t={...e.account,region:r,scope:e.enterpriseId.slice(0,200)},r==="ioa"||!e.token)return m("unsupported",t);const s=r==="internal"?"https://www.codebuddy.cn":"https://www.codebuddy.ai",o={Authorization:`Bearer ${e.token}`,"Content-Type":"application/json",Accept:"application/json","X-User-Id":t.id};e.enterpriseId&&(o["X-Enterprise-Id"]=e.enterpriseId,o["X-Tenant-Id"]=e.enterpriseId);const c=[],g=AbortSignal.timeout(25e3),h=new Date,w=new Date(h);w.setUTCFullYear(w.getUTCFullYear()+101);const b=p=>p.toISOString().slice(0,19).replace("T"," ");for(let p=1;p<=10;p++){const d=await(n.fetch??fetch)(`${s}/v2/billing/meter/get-user-resource`,{method:"POST",headers:o,redirect:"error",signal:g,body:JSON.stringify({PageNumber:p,PageSize:100,ProductCode:"p_tcaca",Status:[0,3],PackageEndTimeRangeBegin:b(h),PackageEndTimeRangeEnd:b(w)})});if(d.status===429){const f=d.headers.get("Retry-After"),T=f&&/^\d+$/.test(f)?Number(f)*1e3:Date.parse(f||"")-Date.now();return{...m("error",t),retryAfterMs:Number.isFinite(T)?Math.max(0,T):3e5}}if(d.status===401||d.status===403)return m("signed_out");if(!d.ok)throw new Error("credits_http_error");const u=a(await d.json());if(u.code!==void 0&&![0,"0"].includes(u.code))throw new Error("credits_business_error");const y=a(a(a(u.data).Response??u.Response).Data);if(a(a(u.data).Response??u.Response).Error||!Array.isArray(y.Accounts))throw new Error("invalid_credits_response");c.push(...y.Accounts);const E=C(y.TotalCount);if(y.Accounts.length<100||E!==void 0&&c.length>=E)return{state:"ready",account:t,...P(c)}}throw new Error("credits_page_limit")}catch{return m("error",t)}finally{try{await i?.close()}finally{l?.()}}}export{m as emptyCredits,P as parseCreditsResources,B as queryWorkBuddyCredits};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{emptyCredits as r}from"./query.js";import{ACTIVE_CREDITS_INTERVAL_MS as n,IDLE_CREDITS_INTERVAL_MS as u}from"./types.js";class y{query;changed;now;value={...r("error"),fetchedAtMs:null,checkedAtMs:null,revision:0};lastAttempt=-1/0;retryAt=0;limitedUntil=0;failures=0;dirty=!1;active=0;epoch=0;timer;pending;running=!1;constructor(t,s,e=Date.now){this.query=t,this.changed=s,this.now=e}snapshot(){return this.value}start(){this.running||(this.running=!0,this.tick())}async stop(){this.running=!1,clearTimeout(this.timer),await this.pending}beginUsage(){this.active++,this.dirty=!0,this.running&&this.tick();let t=!1;return()=>{t||(t=!0,this.active--,this.dirty=!0,this.running&&this.tick())}}reset(){this.epoch++,this.lastAttempt=-1/0,this.retryAt=0,this.limitedUntil=0,this.failures=0,this.dirty=!1,this.value={...r("error"),fetchedAtMs:null,checkedAtMs:null,revision:this.value.revision+1},this.changed(this.value),this.running&&(this.pending??Promise.resolve()).then(()=>{if(this.running)return this.tick()})}nextDue(){const t=this.dirty||this.active||this.failures?n:u;return Math.max(this.lastAttempt+t,this.retryAt,this.limitedUntil)}async tick(){return this.pending?this.pending:this.now()>=this.nextDue()?this.refresh():(this.arm(),this.value)}refresh(t=!1){if(this.pending)return this.pending;if(t&&this.now()<this.limitedUntil)return Promise.resolve(this.value);const s=this.epoch,e=this.now();this.lastAttempt=e;const l=this.dirty;return this.dirty=!1,this.pending=Promise.resolve().then(this.query).catch(()=>r("error")).then(i=>{if(s!==this.epoch)return this.value;const o=i.state==="error"&&!i.account||JSON.stringify(i.account)===JSON.stringify(this.value.account),h=i.state==="error";h?(this.failures++,this.dirty||=l,this.retryAt=this.now()+Math.max(n,Math.min(u,n*2**Math.min(4,this.failures-1)))):(this.failures=0,this.retryAt=0,this.limitedUntil=0),i.retryAfterMs&&(this.limitedUntil=this.now()+i.retryAfterMs);const{retryAfterMs:f,...c}=i,a=h&&o&&this.value.fetchedAtMs!==null;return this.value={...c,...a?{account:this.value.account,remaining:this.value.remaining,total:this.value.total,unlimited:this.value.unlimited,resources:this.value.resources}:{},fetchedAtMs:i.state==="ready"?this.now():a?this.value.fetchedAtMs:null,checkedAtMs:e,revision:this.value.revision+1},this.changed(this.value),this.value}).finally(()=>{this.pending=void 0,this.arm()}),this.pending}arm(){clearTimeout(this.timer),!(!this.running||this.pending)&&(this.timer=setTimeout(()=>{this.tick()},Math.min(2147483647,Math.max(1,this.nextDue()-this.now()))),this.timer.unref())}}export{y as WorkBuddyCreditsScheduler};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const _=3e5,E=36e5;export{_ as ACTIVE_CREDITS_INTERVAL_MS,E as IDLE_CREDITS_INTERVAL_MS};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{acaServerRequest as u}from"../aca-server-client.js";import{loadAcaConfig as a}from"../aca-config.js";import{managementActivity as i}from"../management/activity.js";import{emptyCredits as f,queryWorkBuddyCredits as p}from"./query.js";import{WorkBuddyCreditsScheduler as l}from"./scheduler.js";let r=!1,s=!1,d="",o=Promise.resolve();const n=new l(async()=>{const e=i.enter("workbuddy-credits");if(!e)return f("error");try{return await p()}finally{e()}},()=>{c()});function c(){const e=a();if(!e.token||!r)return Promise.resolve();const t=n.snapshot();return o=o.catch(()=>{}).then(()=>u("POST","/api/client/workbuddy-credits",{machineId:e.machineId,instanceId:process.env.ACA_WORKER_INSTANCE_ID||String(process.pid),snapshot:t},{serverUrl:e.serverUrl,token:e.token})).catch(()=>{}),o}function w(){r=!0,s&&m()}function m(){if(s=!0,!r||i.paused||i.stopping)return;const e=a(),t=`${e.serverUrl}\0${e.machineId}\0${e.token}`;d&&d!==t&&n.reset(),d=t,n.start(),c()}async function g(){r=!1,s=!1,await n.stop(),await o}export{w as activateWorkBuddyCredits,g as stopWorkBuddyCredits,n as workBuddyCredits,m as workBuddyCreditsConnected};
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const o="0.1.
|
|
1
|
+
const o="0.1.90";export{o as AICA_VERSION};
|