@yuandc/aica 0.1.7 → 0.1.8

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.
@@ -0,0 +1,3 @@
1
+ import{Command as a}from"commander";import s from"chalk";import f from"node:fs";import u from"node:path";import{spawn as P}from"node:child_process";import{randomUUID as C}from"node:crypto";import{getLogDir as l,getRuntimeStatePath as $}from"../core/paths.js";import{ensureDir as x,readJsonFile as S}from"../core/fs.js";import{isProcessAlive as r,readPidFile as m,removePidFile as d}from"../core/process.js";import{ACA_WORKER_INSTANCE_ENV as y}from"../core/process-identity.js";import{applyRuntimePathOptions as i}from"../core/runtime-options.js";const g=3e4;function G(){return new a("daemon").description("Run aca as a background daemon service").addCommand(c(new a("start").description("Start aca daemon in the background")).allowUnknownOption(!0).action(e=>{i(e);const o=m();if(o&&r(o)){console.log(`Daemon is already running (PID ${o}).`),process.exitCode=1;return}const n=w(h("start"));n.unref(),console.log(`Daemon started (PID ${n.pid})`),D()})).addCommand(c(new a("stop").description("Stop the running aca daemon")).action(async e=>{i(e);const o=m();if(!o){console.log("No daemon PID file found. Daemon is not running.");return}if(!r(o)){console.log(`Daemon process (PID ${o}) is not running. Cleaning up PID file.`),d();return}await I(o)?(d(),console.log(`Daemon stopped successfully (PID ${o}).`)):(console.log(`Daemon (PID ${o}) is still running. You may need to kill it manually: ${s.yellow(`kill -9 ${o}`)}`),process.exitCode=1)})).addCommand(c(new a("status").description("Show daemon status")).action(e=>{i(e);const o=S($()),n=m();if(n&&r(n)){console.log(s.green("● Daemon is running")),console.log(` PID: ${n}`),o?.phase&&console.log(` Phase: ${o.phase}`),o?.startupStage&&console.log(` Stage: ${o.startupStage}`);return}n?(console.log(s.red("● Daemon is not running (stale PID file)")),d()):console.log(s.red("● Daemon is not running")),process.exitCode=1})).addCommand(c(new a("logs").description("Show daemon logs")).option("-n, --lines <count>","number of lines to show","50").action(e=>{i(e);const o=Number.parseInt(e.lines,10)||50,n=E();if(!n){console.log(`No log files found in ${l()}`),process.exitCode=1;return}const t=f.readFileSync(n,"utf8").trimEnd().split(`
2
+ `);console.log(s.dim(`--- ${n} (last ${o} lines) ---`)),console.log(t.slice(-o).join(`
3
+ `))})).addCommand(c(new a("restart").description("Restart the aca daemon")).allowUnknownOption(!0).action(async e=>{i(e);const o=m();if(o&&r(o)&&!await I(o)){console.error(`Daemon restart aborted because PID ${o} did not stop within ${g}ms.`),process.exitCode=1;return}d();const n=w(h("restart"));n.unref(),console.log(`Daemon started (PID ${n.pid})`),D()}))}function c(e){return e.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 h(e){const o=process.argv.findIndex((t,p)=>p>=2&&t==="daemon"),n=process.argv.findIndex((t,p)=>p>o&&t===e);return n>=0?process.argv.slice(n+1):[]}function w(e){const o=v();return P(o.command,[...o.prefixArgs,"start",...e],{detached:!0,stdio:"ignore",env:{...process.env,[y]:C()}})}function v(){if(process.env.ACA_SINGLE_FILE_WORKER==="1")return{command:process.execPath,prefixArgs:[]};const e=process.argv[1];if(!e)throw new Error("Unable to resolve the current ACA CLI entrypoint");return{command:process.execPath,prefixArgs:[u.resolve(e)]}}function D(){const e=A();console.log(`Use ${s.yellow(`${e} daemon status`)} to check status`),console.log(`Use ${s.yellow(`${e} daemon stop`)} to stop`),console.log(`Use ${s.yellow(`${e} daemon logs`)} to view logs`)}function A(){if(process.env.ACA_SINGLE_FILE_WORKER==="1")return process.execPath;const e=u.basename(process.argv[1]||"");return/worker-cli|aica/i.test(e)?"aica":"aca"}function E(){return x(l()),f.readdirSync(l()).filter(o=>o.endsWith(".log")).map(o=>u.join(l(),o)).sort().at(-1)??null}async function R(e,o){const n=Date.now();for(;Date.now()-n<o;){if(!r(e))return!0;await new Promise(t=>setTimeout(t,100))}return!r(e)}async function I(e,o=g){return r(e)?(process.kill(e,"SIGTERM"),R(e,o)):!0}export{G as createDaemonCommand,D as printStartTips,w as spawnDaemon,I as stopDaemonProcess};
@@ -1 +1 @@
1
- import{Command as k}from"commander";import b from"node:fs";import T from"node:os";import P from"node:path";import t from"node:process";import{randomUUID as R}from"node:crypto";import{Logger as E}from"../core/logger.js";import{loadCredentials as _,saveApiKeyCredential as N}from"../core/auth.js";import{getRuntimeStatePath as D}from"../core/paths.js";import{writeJsonFile as O}from"../core/fs.js";import{HEARTBEAT_INTERVAL_MS as $,startHeartbeatLoop as L}from"../core/heartbeat.js";import{startJobWorkerLoop as U}from"../core/job-worker.js";import{startFileTransferWorkerLoop as W}from"../core/file-transfer-worker.js";import{startMachineFilesystemWorkerLoop as M}from"../core/machine-filesystem-worker.js";import{cleanupOrphanedAcpProcesses as x}from"../core/acp-orphan-cleanup.js";import{defaultAcpRuntimePool as S}from"../acp/client/acp-runtime-pool.js";import{writePidFile as j,removePidFile as F}from"../core/process.js";import{applyRuntimePathOptions as K}from"../core/runtime-options.js";import{loadAcaConfig as V,updateAcaConfig as v}from"../core/aca-config.js";import{acaServerRequest as H}from"../core/aca-server-client.js";import{ACA_WORKER_INSTANCE_ENV as A,ACA_WORKER_PID_ENV as G,ACA_WORKER_START_TICKS_ENV as J,readProcessStartTicks as q}from"../core/process-identity.js";function Se(){return new k("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("--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").action(async e=>{await B(e)})}async function B(e){K(e);const s=t.env[A]||R(),o=q(t.pid)||"";t.env[A]=s,t.env[G]=String(t.pid),t.env[J]=o;const a=new E("start",e.debug?"debug":"info"),C=V(),l={...e.server?.trim()?{serverUrl:e.server.trim()}:{},...e.machineId?{machineId:e.machineId.trim()}:{},...e.machineName?{machineName:e.machineName.trim()}:{}};let n=Object.keys(l).length>0?v(l):C;const m=e.machineName?.trim()||n.machineName||T.hostname();if(e.token?.trim()){const r=await H("POST","/api/auth/machine-enroll",{token:e.token.trim(),machineId:n.machineId,machineName:m},{serverUrl:n.serverUrl});n=v({machineId:r.machineId,machineName:m,token:r.token,username:r.username}),a.info(`Machine enrollment completed for user ${r.username}`)}const y=e.auth?N(e.auth,m):_();j(t.pid),w({pid:t.pid,workerInstanceId:s,workerStartTicks:o,phase:"running",startupStage:"local-compatible",machineId:n.machineId||y?.machine?.machineId||"local-machine",machineName:m,cliTypes:e.cliTypes??z(),startedAt:new Date().toISOString(),heartbeatIntervalMs:$,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 ${m}`),a.info(`PID ${t.pid}`);const c=await x();c.terminated>0&&a.warn(`terminated ${c.terminated} orphaned ACP process(es): ${c.pids.join(",")}`);let f=!1,p,h,u,g;const I=async r=>{if(f)return;f=!0,a.info("Shutting down"),p&&clearInterval(p),u?.stop(),g?.stop(),await h?.stop().catch(i=>a.warn(`job worker shutdown failed: ${i instanceof Error?i.message:String(i)}`));const d=S.stats();await S.disposeAll().catch(i=>a.warn(`ACP runtime shutdown failed: ${i instanceof Error?i.message:String(i)}`)),a.info(`ACP runtimes stopped total=${d.total} inUse=${d.inUse} idle=${d.idle}`),w({pid:t.pid,phase:"stopped",stoppedAt:new Date().toISOString(),issues:[]}),F(),t.exit(r)};t.once("SIGINT",()=>{I(130)}),t.once("SIGTERM",()=>{I(0)}),e.heartbeatLog&&setInterval(()=>a.info(`heartbeat: ${new Date().toISOString()}`),5e3),p=L(a),h=U(a),u=W(a),g=M(a),await new Promise(()=>{})}function z(){return["codex","claude"].filter(e=>Q(e))}function Q(e){const s=t.env.PATH||"";for(const o of s.split(":"))try{if(o&&b.existsSync(P.join(o,e)))return!0}catch{}return!1}function w(e){O(D(),e)}export{Se as createStartCommand};
1
+ import{Command as R}from"commander";import _ from"node:fs";import v from"node:os";import f from"node:path";import t from"node:process";import{randomUUID as W}from"node:crypto";import{Logger as $}from"../core/logger.js";import{loadCredentials as N,saveApiKeyCredential as O}from"../core/auth.js";import{getRuntimeStatePath as x}from"../core/paths.js";import{writeJsonFile as L}from"../core/fs.js";import{HEARTBEAT_INTERVAL_MS as M,startHeartbeatLoop as U}from"../core/heartbeat.js";import{startJobWorkerLoop as j}from"../core/job-worker.js";import{startFileTransferWorkerLoop as F}from"../core/file-transfer-worker.js";import{startMachineFilesystemWorkerLoop as H}from"../core/machine-filesystem-worker.js";import{cleanupOrphanedAcpProcesses as K}from"../core/acp-orphan-cleanup.js";import{defaultAcpRuntimePool as S}from"../acp/client/acp-runtime-pool.js";import{isProcessAlive as l,readPidFile as h,writePidFile as V,removePidFile as y}from"../core/process.js";import{applyRuntimePathOptions as C}from"../core/runtime-options.js";import{loadAcaConfig as B,updateAcaConfig as b}from"../core/aca-config.js";import{acaServerRequest as G}from"../core/aca-server-client.js";import{ACA_WORKER_INSTANCE_ENV as P,ACA_WORKER_PID_ENV as J,ACA_WORKER_START_TICKS_ENV as q,readProcessStartTicks as z}from"../core/process-identity.js";import{printStartTips as Q,spawnDaemon as X}from"./daemon.js";function Pe(){return new R("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("--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 Y(e);return}await te(e)})}async function Y(e){C(e);const r=h();if(r&&l(r))throw new Error(`Worker 已在后台运行,PID ${r}。`);r&&y();const a=X(Z());if(a.unref(),!a.pid)throw new Error("后台 Worker 启动失败:未获得进程 PID。");if(!await ee(a.pid,3e3)&&!l(a.pid))throw new Error(`后台 Worker 启动失败,请检查 ${f.join(t.env.AICA_HOME||t.env.ACA_HOME||f.join(v.homedir(),".aica"),"logs")}。`);console.log(`Worker 已在后台启动,PID ${a.pid}。`),Q()}function Z(){const e=t.argv.findIndex((r,a)=>a>=2&&r==="start");return e<0?[]:t.argv.slice(e+1).filter(r=>r!=="--background"&&r!=="-b")}async function ee(e,r){const a=Date.now();for(;Date.now()-a<r;){if(h()===e)return!0;if(!l(e))return!1;await new Promise(i=>setTimeout(i,50))}return h()===e}async function te(e){C(e);const r=t.env[P]||W(),a=z(t.pid)||"";t.env[P]=r,t.env[J]=String(t.pid),t.env[q]=a;const i=new $("start",e.debug?"debug":"info"),E=B(),u={...e.server?.trim()?{serverUrl:e.server.trim()}:{},...e.machineId?{machineId:e.machineId.trim()}:{},...e.machineName?{machineName:e.machineName.trim()}:{}};let s=Object.keys(u).length>0?b(u):E;const c=e.machineName?.trim()||s.machineName||v.hostname();if(e.token?.trim()){const n=await G("POST","/api/auth/machine-enroll",{token:e.token.trim(),machineId:s.machineId,machineName:c},{serverUrl:s.serverUrl});s=b({machineId:n.machineId,machineName:c,token:n.token,username:n.username}),i.info(`Machine enrollment completed for user ${n.username}`)}const D=e.auth?O(e.auth,c):N();V(t.pid),T({pid:t.pid,workerInstanceId:r,workerStartTicks:a,phase:"running",startupStage:"local-compatible",machineId:s.machineId||D?.machine?.machineId||"local-machine",machineName:c,cliTypes:e.cliTypes??re(),startedAt:new Date().toISOString(),heartbeatIntervalMs:M,issues:[{severity:"info",message:"aca is running in aca-server compatibility mode; ACP execution happens on this machine."}]}),i.info(`Starting aca local-compatible service on machine ${c}`),i.info(`PID ${t.pid}`);const m=await K();m.terminated>0&&i.warn(`terminated ${m.terminated} orphaned ACP process(es): ${m.pids.join(",")}`);let g=!1,d,w,I,A;const k=async n=>{if(g)return;g=!0,i.info("Shutting down"),d&&clearInterval(d),I?.stop(),A?.stop(),await w?.stop().catch(o=>i.warn(`job worker shutdown failed: ${o instanceof Error?o.message:String(o)}`));const p=S.stats();await S.disposeAll().catch(o=>i.warn(`ACP runtime shutdown failed: ${o instanceof Error?o.message:String(o)}`)),i.info(`ACP runtimes stopped total=${p.total} inUse=${p.inUse} idle=${p.idle}`),T({pid:t.pid,phase:"stopped",stoppedAt:new Date().toISOString(),issues:[]}),y(),t.exit(n)};t.once("SIGINT",()=>{k(130)}),t.once("SIGTERM",()=>{k(0)}),e.heartbeatLog&&setInterval(()=>i.info(`heartbeat: ${new Date().toISOString()}`),5e3),d=U(i),w=j(i),I=F(i),A=H(i),await new Promise(()=>{})}function re(){return["codex","claude"].filter(e=>ie(e))}function ie(e){const r=t.env.PATH||"";for(const a of r.split(":"))try{if(a&&_.existsSync(f.join(a,e)))return!0}catch{}return!1}function T(e){L(x(),e)}export{Pe as createStartCommand};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Command as a}from"commander";import m from"node:path";import{createStartCommand as n}from"./commands/start.js";import{createWorkerAuthCommand as t}from"./commands/worker-auth.js";import{createWorkerCodexCommand as s}from"./commands/worker-codex.js";import{createWorkerProjectCommand as c}from"./commands/worker-project.js";import{DEFAULT_ACA_SERVER_URL as i}from"./core/aca-config.js";process.env.ACA_DEFAULT_SERVER_URL||=i;async function d(r=process.argv){const o=new a;o.name("aica").description("AICA remote Agent worker").version("0.1.7").enablePositionalOptions(),o.addCommand(n()),o.addCommand(t()),o.addCommand(c()),o.addCommand(s()),await o.parseAsync(r)}const p=process.argv[1]||"",e=m.basename(p).replace(/\.(?:js|ts|cjs|mjs)$/i,"");(e==="worker-cli"||e==="aica"||e==="aca")&&d().catch(r=>{const o=r instanceof Error?r.message:String(r);console.error(o),process.exitCode=1});export{d as runWorkerCli};
2
+ import{Command as a}from"commander";import m from"node:path";import{createStartCommand as n}from"./commands/start.js";import{createDaemonCommand as t}from"./commands/daemon.js";import{createWorkerAuthCommand as s}from"./commands/worker-auth.js";import{createWorkerCodexCommand as c}from"./commands/worker-codex.js";import{createWorkerProjectCommand as i}from"./commands/worker-project.js";import{DEFAULT_ACA_SERVER_URL as d}from"./core/aca-config.js";process.env.ACA_DEFAULT_SERVER_URL||=d;async function p(r=process.argv){const o=new a;o.name("aica").description("AICA remote Agent worker").version("0.1.8").enablePositionalOptions(),o.addCommand(n()),o.addCommand(t()),o.addCommand(s()),o.addCommand(i()),o.addCommand(c()),await o.parseAsync(r)}const C=process.argv[1]||"",e=m.basename(C).replace(/\.(?:js|ts|cjs|mjs)$/i,"");(e==="worker-cli"||e==="aica"||e==="aca")&&p().catch(r=>{const o=r instanceof Error?r.message:String(r);console.error(o),process.exitCode=1});export{p as runWorkerCli};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuandc/aica",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "AICA remote Agent worker",
5
5
  "author": "yuandc",
6
6
  "type": "module",