@yuandc/aica 0.1.72 → 0.1.73
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/commands/daemon.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{assertManualStartAllowed as
|
|
2
|
-
`);console.log(
|
|
3
|
-
`))})).addCommand(
|
|
1
|
+
import{assertManualStartAllowed as P,prepareManualWorkerAction as f}from"../core/management/manual.js";import{Command as a}from"commander";import s from"chalk";import d from"node:fs";import m from"node:path";import{spawn as $}from"node:child_process";import{randomUUID as k}from"node:crypto";import{getLogDir as i,getRuntimeStatePath as v}from"../core/paths.js";import{ensureDir as g,readJsonFile as A}from"../core/fs.js";import{isProcessAlive as E,readPidFile as w,removePidFile as h}from"../core/process.js";import{ACA_WORKER_INSTANCE_ENV as R,readProcessStartTicks as _}from"../core/process-identity.js";import{assertNoWorkerInstances as I,findWorkerInstances as p,sameWorkerInstance as u}from"../core/worker-instances.js";import{applyRuntimePathOptions as c}from"../core/runtime-options.js";const S=3e4;function Q(){return new a("daemon").description("Run aca as a background daemon service").addCommand(l(new a("start").description("Start aca daemon in the background")).allowUnknownOption(!0).action(o=>{c(o),P(),I();const e=w();if(process.platform!=="linux"&&e&&E(e)){console.log(`Daemon is already running (PID ${e}).`),process.exitCode=1;return}const n=y(D("start"));n.unref(),console.log(`Daemon started (PID ${n.pid})`),C()})).addCommand(l(new a("stop").description("Stop the running aca daemon")).action(async o=>{c(o),await f();const e=p();if(!e.length){console.log("当前运行目录没有存活的 Worker。");return}await x(e)?console.log(`Worker 已停止:${e.map(n=>n.pid).join(", ")}`):process.exitCode=1})).addCommand(l(new a("status").description("Show daemon status")).action(o=>{c(o);const e=A(v()),n=p(),t=w();if(n.length){console.log(s.green("● Daemon is running")),console.log(` PID: ${n.map(r=>r.pid).join(", ")}`),n.length>1&&console.log(" 检测到重复 Worker;daemon restart 会先停止该运行目录下的全部已确认实例。"),n.some(r=>r.pid===t)||console.log(" PID 文件缺失或失效,已从存活进程恢复实例清单。"),n.some(r=>r.pid===e?.pid)&&(e?.phase&&console.log(` Phase: ${e.phase}`),e?.startupStage&&console.log(` Stage: ${e.startupStage}`));return}t?(console.log(s.red("● Daemon is not running (stale PID file)")),h(t)):console.log(s.red("● Daemon is not running")),process.exitCode=1})).addCommand(l(new a("logs").description("Show daemon logs")).option("-n, --lines <count>","number of lines to show","50").action(o=>{c(o);const e=Number.parseInt(o.lines,10)||50,n=O();if(!n){console.log(`No log files found in ${i()}`),process.exitCode=1;return}const t=d.readFileSync(n,"utf8").trimEnd().split(`
|
|
2
|
+
`);console.log(s.dim(`--- ${n} (last ${e} lines) ---`)),console.log(t.slice(-e).join(`
|
|
3
|
+
`))})).addCommand(l(new a("restart").description("Restart the aca daemon")).allowUnknownOption(!0).action(async o=>{if(c(o),await f(),!await x(p())){process.exitCode=1;return}I();const e=y(D("restart"));e.unref(),console.log(`Daemon started (PID ${e.pid})`),C()}))}function l(o){return o.option("--aica-home <path>","Runtime home directory (defaults to ~/.aica)").option("--aica-config <path>","Config file path").option("--aca-home <path>","Deprecated alias for --aica-home").option("--aca-config <path>","Deprecated alias for --aica-config")}function D(o){const e=process.argv.findIndex((t,r)=>r>=2&&t==="daemon"),n=process.argv.findIndex((t,r)=>r>e&&t===o);return n>=0?process.argv.slice(n+1):[]}function y(o){const e=W();g(i());const n=new Date().toISOString().slice(0,10),t=m.join(i(),`daemon-stderr-${n}.log`),r=d.openSync(t,"a");try{return $(e.command,[...e.prefixArgs,"start",...o],{detached:!0,stdio:["ignore","ignore",r],windowsHide:!0,env:{...process.env,[R]:k()}})}finally{d.closeSync(r)}}function W(){if(process.env.ACA_SINGLE_FILE_WORKER==="1")return{command:process.execPath,prefixArgs:[]};const o=process.argv[1];if(!o)throw new Error("Unable to resolve the current ACA CLI entrypoint");return{command:process.execPath,prefixArgs:[m.resolve(o)]}}function C(){const o=N();console.log(`Use ${s.yellow(`${o} daemon status`)} to check status`),console.log(`Use ${s.yellow(`${o} daemon stop`)} to stop`),console.log(`Use ${s.yellow(`${o} daemon logs`)} to view logs`)}function N(){if(process.env.ACA_SINGLE_FILE_WORKER==="1")return process.execPath;const o=m.basename(process.argv[1]||"");return/worker-cli|aica/i.test(o)?"aica":"aca"}function O(){return g(i()),d.readdirSync(i()).filter(e=>/^\d{4}-\d{2}-\d{2}\.log$/.test(e)).map(e=>m.join(i(),e)).sort().at(-1)??null}async function U(o,e){const n=Date.now();for(;Date.now()-n<e;){if(!u(o))return!0;await new Promise(t=>setTimeout(t,100))}return!u(o)}async function F(o,e=S,n){const t=n||{pid:o,startTicks:_(o)};if(!u(t))return!0;try{process.kill(o,"SIGTERM")}catch(r){if(r.code!=="ESRCH")throw r}return U(t,e)}async function x(o,e=S){return(await Promise.all(o.map(async t=>{const r=await F(t.pid,e,t);return r?h(t.pid):console.error(`Worker PID ${t.pid} 未在 ${e}ms 内退出,已中止重启,请检查该实例的任务和日志。`),r}))).every(Boolean)}export{Q as createDaemonCommand,C as printStartTips,y as spawnDaemon,F as stopDaemonProcess,x as stopWorkerInstances};
|
package/dist/commands/start.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{stopManagedClaims as M}from"../core/management/claims.js";import{assertManualStartAllowed as L}from"../core/management/manual.js";import{startManagementControl as U}from"../core/management/control-server.js";import{managementActivity as w}from"../core/management/activity.js";import{
|
|
1
|
+
import{stopManagedClaims as M}from"../core/management/claims.js";import{assertManualStartAllowed as L}from"../core/management/manual.js";import{startManagementControl as U}from"../core/management/control-server.js";import{managementActivity as w}from"../core/management/activity.js";import{acquireWorkerLease as F,assertNoWorkerInstances as H}from"../core/worker-instances.js";import{getAcaHome as K}from"../core/paths.js";import{Command as V}from"commander";import v from"node:fs";import T from"node:os";import f from"node:path";import r from"node:process";import{randomUUID as B}from"node:crypto";import{Logger as G}from"../core/logger.js";import{loadCredentials as J,saveApiKeyCredential as q}from"../core/auth.js";import{getRuntimeStatePath as z}from"../core/paths.js";import{writeJsonFile as Q}from"../core/fs.js";import{HEARTBEAT_INTERVAL_MS as X,startHeartbeatLoop as Y}from"../core/heartbeat.js";import{startJobWorkerLoop as Z}from"../core/job-worker.js";import{startFileTransferWorkerLoop as ee}from"../core/file-transfer-worker.js";import{startTerminalWorker as te}from"../core/terminal/worker.js";import{startMachineFilesystemWorkerLoop as re}from"../core/machine-filesystem-worker.js";import{cleanupOrphanedAcpProcesses as ae}from"../core/acp-orphan-cleanup.js";import{defaultAcpRuntimePool as $}from"../acp/client/acp-runtime-pool.js";import{isProcessAlive as u,readPidFile as m,writePidFile as ne,removePidFile as W}from"../core/process.js";import{applyRuntimePathOptions as x}from"../core/runtime-options.js";import{loadAcaConfig as D,updateAcaConfig as R}from"../core/aca-config.js";import{acaServerRequest as ie}from"../core/aca-server-client.js";import{restoreProjectsAfterLogin as oe}from"../core/project-recovery.js";import{syncCodexProviderRuntimeConfig as se}from"../core/codex-provider-runtime-config.js";import{ACA_WORKER_INSTANCE_ENV as _,ACA_WORKER_PID_ENV as ce,ACA_WORKER_START_TICKS_ENV as me,readProcessStartTicks as de}from"../core/process-identity.js";import{printStartTips as fe,spawnDaemon as le}from"./daemon.js";function Xe(){return new V("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 pe(e);return}await ge(e)})}async function pe(e){x(e),H();const n=m();if(r.platform!=="linux"&&n&&u(n))throw new Error(`Worker 已在后台运行,PID ${n}。`);n&&W(n);const o=le(ue());if(o.unref(),!o.pid)throw new Error("后台 Worker 启动失败:未获得进程 PID。");if(!await he(o.pid,3e3)&&!u(o.pid))throw new Error(`后台 Worker 启动失败,请检查 ${f.join(r.env.AICA_HOME||r.env.ACA_HOME||f.join(T.homedir(),".aica"),"logs")}。`);console.log(`Worker 已在后台启动,PID ${o.pid}。`),fe()}function ue(){const e=r.argv.findIndex((n,o)=>o>=2&&n==="start");return e<0?[]:r.argv.slice(e+1).filter(n=>n!=="--background"&&n!=="-b")}async function he(e,n){const o=Date.now();for(;Date.now()-o<n;){if(m()===e)return!0;if(!u(e))return!1;await new Promise(a=>setTimeout(a,50))}return m()===e}async function ge(e){x(e);const n=await F();try{await we(e)}finally{n()}}async function we(e){if(e.managedStart||L(),e.managedStart){if(r.platform!=="linux")throw new Error("当前平台尚未支持受管理启动");if(!r.env.AICA_MANAGER_ID)throw new Error("受管理启动缺少管理实例");r.env.AICA_UPDATE_OPERATION=e.managedStart}const n=r.env[_]||B(),o=de(r.pid)||"";r.env[_]=n,r.env[ce]=String(r.pid),r.env[me]=o;const a=new G("start",e.debug?"debug":"info"),N=D(),A={...e.server?.trim()?{serverUrl:e.server.trim()}:{},...e.machineId?{machineId:e.machineId.trim()}:{},...e.machineName?{machineName:e.machineName.trim()}:{}};let c=Object.keys(A).length>0?R(A):N;const d=e.machineName?.trim()||c.machineName||T.hostname();if(e.token?.trim()){const t=await ie("POST","/api/auth/machine-enroll",{token:e.token.trim(),machineId:c.machineId,machineName:d},{serverUrl:c.serverUrl});c=R({machineId:t.machineId,machineName:d,token:t.token,username:t.username}),a.info(`Machine enrollment completed for user ${t.username}`);try{const i=await oe();i.restored>0&&a.info(`restored ${i.restored} local project(s) after machine enrollment`),i.unavailable>0&&a.warn(`skipped ${i.unavailable} unavailable project path(s) during machine enrollment recovery`),c=D()}catch(i){a.warn(`project recovery after machine enrollment failed: ${i instanceof Error?i.message:String(i)}`)}}const O=e.auth?q(e.auth,d):J(),l=m();if(r.platform!=="linux"&&l&&l!==r.pid&&u(l))throw new Error(`Worker 已在运行,PID ${l}`);ne(r.pid),j({pid:r.pid,workerInstanceId:n,workerStartTicks:o,phase:"running",startupStage:"local-compatible",machineId:c.machineId||O?.machine?.machineId||"local-machine",machineName:d,cliTypes:e.cliTypes??ve(),startedAt:new Date().toISOString(),heartbeatIntervalMs:X,issues:[{severity:"info",message:"aca is running in aca-server compatibility mode; ACP execution happens on this machine."}]}),a.info(`Starting aca local-compatible service on machine ${d}`),a.info(`PID ${r.pid}`);const h=await ae();h.terminated>0&&a.warn(`terminated ${h.terminated} orphaned ACP process(es): ${h.pids.join(",")}`),se().then(t=>a.info(t?`Codex Provider runtime config loaded revision=${t.revision}`:"Codex Provider runtime config is not configured")).catch(t=>a.warn(`Codex Provider runtime config sync failed: ${t instanceof Error?t.message:String(t)}`));let I=!1,g,S,k,y,P;const p=async t=>{if(I)return;I=!0,M(),a.info("Shutting down"),g&&clearInterval(g),k?.stop(),P?.stop(),y?.stop(),await S?.stop().catch(s=>a.warn(`job worker shutdown failed: ${s instanceof Error?s.message:String(s)}`));const i=$.stats();await $.disposeAll().catch(s=>a.warn(`ACP runtime shutdown failed: ${s instanceof Error?s.message:String(s)}`)),a.info(`ACP runtimes stopped total=${i.total} inUse=${i.inUse} idle=${i.idle}`),m()===r.pid&&(j({pid:r.pid,phase:"stopped",stoppedAt:new Date().toISOString(),issues:[]}),W(r.pid)),r.exit(t)},C=t=>{if(!w.stopping&&m()===r.pid){const i=f.join(K(),"management");v.mkdirSync(i,{recursive:!0,mode:448}),v.writeFileSync(f.join(i,"manual-stop.json"),JSON.stringify({workerInstanceId:n,at:Date.now()}),{mode:384})}p(t)};r.once("SIGINT",()=>C(130)),r.once("SIGTERM",()=>C(0)),r.once("uncaughtException",t=>{a.error(`uncaught exception: ${t.stack||t.message}`),p(1)}),r.once("unhandledRejection",t=>{const i=t instanceof Error?t.stack||t.message:String(t);a.error(`unhandled rejection: ${i}`),p(1)}),e.heartbeatLog&&setInterval(()=>a.info(`heartbeat: ${new Date().toISOString()}`),5e3);let E=!1;const b=()=>{E||(E=!0,S=Z(a),k=ee(a),P=re(a),y=te(a))};try{await U(n,()=>p(0),b)}catch(t){if(e.managedStart||t instanceof Error&&t.message.includes("已有 Worker 管理实例"))throw t;w.paused=!1,a.warn(`可选管理通道不可用: ${t instanceof Error?t.message:String(t)}`)}g=Y(a),w.paused||b(),await new Promise(()=>{})}function ve(){return["codex","claude"].filter(e=>Ae(e))}function Ae(e){const n=r.env.PATH||"";for(const o of n.split(":"))try{if(o&&v.existsSync(f.join(o,e)))return!0}catch{}return!1}function j(e){Q(z(),e)}export{Xe as createStartCommand};
|
package/dist/core/process.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import i from"node:fs";import{getPidFile as t}from"./paths.js";import{ensureDir as
|
|
2
|
-
`,"utf8")}function a(){
|
|
1
|
+
import i from"node:fs";import{getPidFile as t}from"./paths.js";import{ensureDir as n,removeFileIfExists as o}from"./fs.js";import u from"node:path";function p(r){try{return process.kill(r,0),!0}catch{return!1}}function f(){try{const r=i.readFileSync(t(),"utf8").trim(),e=Number(r);return Number.isInteger(e)&&e>0?e:null}catch(r){if(r.code==="ENOENT")return null;throw r}}function d(r){const e=t();n(u.dirname(e)),i.writeFileSync(e,`${r}
|
|
2
|
+
`,"utf8")}function a(r){r!==void 0&&f()!==r||o(t())}export{p as isProcessAlive,f as readPidFile,a as removePidFile,d as writePidFile};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import a from"node:fs";import i from"node:path";import w from"node:os";import A from"node:net";import{createHash as x}from"node:crypto";import{getAcaHome as m}from"./paths.js";import{readPidFile as h,isProcessAlive as k}from"./process.js";import{readProcessStartTicks as O}from"./process-identity.js";function l(e){const r=i.resolve(e);try{return a.realpathSync(r)}catch{return i.dirname(r)===r?r:i.join(l(i.dirname(r)),i.basename(r))}}function y(e,r){const o=e.find(t=>t.startsWith(`${r}=`));if(o)return o.slice(r.length+1);const n=e.indexOf(r);return n>=0?e[n+1]:void 0}function $(e,r,o){const n=r.ACA_SINGLE_FILE_WORKER==="1"&&e[1]==="start",t=e.indexOf("start");if(t<(n?1:2)||t>8||e.slice(t+1).some(s=>s==="-b"||s==="--background"))return!1;if(n)return!0;const f=e[t-1];if(!/^(?:aca|aica|(?:worker-cli|cli)\.(?:js|mjs|cjs|ts))$/.test(i.basename(f)))return!1;if(r.ACA_WORKER_INSTANCE_ID)return!0;let c=i.dirname(l(i.resolve(o,f)));for(let s=0;s<4;s++){try{const u=JSON.parse(a.readFileSync(i.join(c,"package.json"),"utf8"));if(["aca","@yuandc/aica"].includes(u.name))return!0}catch{}c=i.dirname(c)}return!1}function I(e=m()){if(process.platform!=="linux"){const n=h();return n&&n!==process.pid&&k(n)?[{pid:n,startTicks:null}]:[]}const r=l(e),o=[];for(const n of a.readdirSync("/proc")){if(!/^\d+$/.test(n)||Number(n)===process.pid)continue;const t=Number(n);try{const f=a.readFileSync(`/proc/${t}/stat`,"utf8").split(") ").at(-1).split(" ");if(f[0]==="Z")continue;const c=a.readFileSync(`/proc/${t}/cmdline`,"utf8").split("\0").filter(Boolean);if(!c.includes("start"))continue;const s=Object.fromEntries(a.readFileSync(`/proc/${t}/environ`,"utf8").split("\0").filter(d=>d.includes("=")).map(d=>[d.slice(0,d.indexOf("=")),d.slice(d.indexOf("=")+1)])),u=a.readlinkSync(`/proc/${t}/cwd`);if(!$(c,s,u))continue;const S=y(c,"--aica-home")||y(c,"--aca-home")||s.AICA_HOME||s.ACA_HOME||i.join(s.HOME||w.homedir(),".aica");if(l(i.resolve(u,S))!==r)continue;const p=O(t);p===f[19]&&o.push({pid:t,startTicks:p})}catch(f){if(f.code==="EACCES"&&h()===t)throw new Error(`无法核对 Worker PID ${t} 的身份,请检查进程权限`)}}return o.sort((n,t)=>n.pid-t.pid)}function E(){const e=I();if(e.length)throw new Error(`当前运行目录已有 Worker:${e.map(r=>r.pid).join(", ")}。请使用 daemon restart 或 daemon stop 处理现有实例。`)}async function T(){if(process.platform!=="linux")return E(),()=>{};const e=x("sha256").update(l(m())).digest("hex"),r=A.createServer(o=>o.destroy());await new Promise((o,n)=>{r.once("error",t=>n(t.code==="EADDRINUSE"?new Error("当前运行目录已有 Worker 正在启动或运行,请勿重复启动"):t)),r.listen(`\0aica-worker-${e}`,o)}),r.unref();try{E()}catch(o){throw r.close(),o}return()=>{r.close()}}function g(e){if(e.startTicks!==null)try{const r=a.readFileSync(`/proc/${e.pid}/stat`,"utf8").split(") ").at(-1).split(" ");return r[0]!=="Z"&&r[19]===e.startTicks}catch{return!1}return k(e.pid)}export{T as acquireWorkerLease,E as assertNoWorkerInstances,I as findWorkerInstances,g as sameWorkerInstance};
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const o="0.1.
|
|
1
|
+
const o="0.1.73";export{o as AICA_VERSION};
|