agentix-cli 0.5.0 → 0.8.0
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/README.md +14 -0
- package/dist/chunk-NGKOM4ZZ.js +137 -0
- package/dist/chunk-NGKOM4ZZ.js.map +1 -0
- package/dist/cli.js +43 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +73 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-WT2UJMBC.js +0 -68
- package/dist/chunk-WT2UJMBC.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,47 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{d as
|
|
3
|
-
(${(
|
|
4
|
-
`)),console.log(
|
|
2
|
+
import{d as C,e as U,j as Z,p as _,q as ee}from"./chunk-NGKOM4ZZ.js";import"./chunk-Z4GC5D6D.js";import"./chunk-M7HKBG3V.js";import{a as V}from"./chunk-4YCH6IZV.js";import{Command as fe}from"commander";import{Command as re}from"commander";import d from"chalk";var A=new re().name("daemon").description("manage the agentx daemon \u2014 start, stop, status, logs");A.command("start").description("start the daemon (default if no subcommand)").option("-c, --config <path>","path to agentx.json").option("-d, --detach","run in background (detached)").option("--port <port>","override bind port").action(async e=>{if(e.detach){let{spawn:n}=await import("child_process"),s=["dist/cli.js","daemon","start"];e.config&&s.push("-c",e.config),e.port&&s.push("--port",e.port);let t=n("node",s,{detached:!0,stdio:["ignore","ignore","ignore"],cwd:process.cwd()});t.unref();let{writeFileSync:i}=await import("fs");i("/tmp/agentx-daemon.pid",String(t.pid)),console.log(d.green(`Daemon started in background (PID: ${t.pid})`)),console.log(d.dim(" Logs: tail -f /tmp/agentx-daemon.log")),console.log(d.dim(" Stop: agentx daemon stop"));return}await new _(e.config).start()});A.command("stop").description("stop the running daemon").action(async()=>{let{readFileSync:e,existsSync:o,unlinkSync:n}=await import("fs"),{resolve:s}=await import("path"),t=[s(process.cwd(),".agentx/daemon.pid"),"/tmp/agentx-daemon.pid"];for(let r of t)if(o(r))try{let l=parseInt(e(r,"utf-8").trim(),10);process.kill(l,"SIGTERM"),await new Promise(c=>setTimeout(c,2e3));try{n(r)}catch{}console.log(d.green(`Daemon stopped (PID: ${l})`));return}catch{try{n(r)}catch{}}let{execSync:i}=await import("child_process");try{i("pkill -f 'node dist/cli.js daemon'",{stdio:"ignore"}),console.log(d.green("Daemon stopped"))}catch{console.log(d.yellow("No daemon process found"))}});A.command("status").alias("st").description("show daemon status, agents, crons, and mesh").option("-c, --config <path>","path to agentx.json").option("--json","output as JSON").action(async e=>{try{let o=C(e.config),[n,s]=o.node.bind.split(":"),t=`http://${n||"127.0.0.1"}:${s}/health`,r=await(await fetch(t,{signal:AbortSignal.timeout(3e3)})).json();if(e.json){console.log(JSON.stringify(r,null,2));return}console.log(),console.log(d.bold(` ${r.node.name}`)+d.dim(` (${r.node.id})`)),console.log(d.green(" Status: running")+d.dim(` \u2014 uptime ${ce(r.uptime)}`)),console.log(),console.log(d.bold(" Agents:"));for(let c of r.agents){let y=c.active>0?d.yellow(` [${c.active} active]`):"",g=c.errors>0?d.red(` (${c.errors} errors)`):"";console.log(` ${d.cyan(c.name)} ${d.dim(`(${c.tier})`)}${y}${g}`),console.log(d.dim(` ${c.workspace}`))}let l=r.crons.filter(c=>c.enabled);if(r.crons.length){console.log(),console.log(d.bold(` Crons: ${l.length} enabled / ${r.crons.length} total`));for(let c of r.crons){let y=c.enabled?d.green("\u25CF"):d.dim("\u25CB"),g=c.nextRun?d.dim(` next: ${new Date(c.nextRun).toLocaleTimeString()}`):"";console.log(` ${y} ${c.id}${g}`)}}if(r.mesh?.length){console.log(),console.log(d.bold(" Mesh:"));for(let c of r.mesh){let y=c.healthy?d.green("\u25CF"):d.red("\u25CF"),g=c.skills?.length?d.dim(` (${c.skills.length} agents)`):"";console.log(` ${y} ${c.peer} ${d.dim(c.peerUrl)}${g}`)}}console.log()}catch(o){if(o.cause?.code==="ECONNREFUSED"||o.name==="TimeoutError")console.log(d.red(" Daemon is not running")),console.log(d.dim(" Start with: agentx daemon start"));else try{let n=C(e.config),s=U(n);if(console.log(),console.log(d.bold(` ${n.node.name}`)+d.dim(` (${n.node.id})`)),console.log(d.yellow(" Status: stopped")),console.log(d.dim(` ${Object.keys(n.agents).length} agents, ${Object.keys(n.crons).length} crons configured`)),s.length){console.log();for(let t of s)console.log(d.yellow(` \u26A0 ${t}`))}console.log()}catch(n){console.log(d.red(` ${n.message}`))}}});A.command("logs").description("tail daemon logs").option("-n, --lines <n>","number of lines","50").option("-f, --follow","follow log output",!1).action(async e=>{let{execSync:o,spawn:n}=await import("child_process"),s="/tmp/agentx-daemon.log",{existsSync:t}=await import("fs");if(!t(s)){console.log(d.yellow("No log file found at /tmp/agentx-daemon.log"));return}if(e.follow){let i=n("tail",["-f","-n",e.lines,s],{stdio:"inherit"});process.on("SIGINT",()=>{i.kill(),process.exit(0)})}else o(`tail -n ${e.lines} ${s}`,{stdio:"inherit"})});A.command("send <agent> <message...>").description("send a task to an agent via the daemon API").option("-c, --config <path>","path to agentx.json").option("--peer <peer>","send to a mesh peer's agent").option("--json","output raw JSON response").action(async(e,o,n)=>{let s=o.join(" ");try{let t=C(n.config),[i,r]=t.node.bind.split(":"),l=`http://${i||"127.0.0.1"}:${r}`,c,y;n.peer?(c=`${l}/mesh/task`,y={peer:n.peer,message:s,agent:e}):(c=`${l}/task`,y={agent:e,message:s}),console.log(d.dim(` Sending to ${n.peer?`${n.peer}/${e}`:e}...`));let h=await(await fetch(c,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(y)})).json();n.json?console.log(JSON.stringify(h,null,2)):h.error?console.log(d.red(` Error: ${h.error}`)):(console.log(),console.log(h.content||h.response||"No response"),h.duration&&console.log(d.dim(`
|
|
3
|
+
(${(h.duration/1e3).toFixed(1)}s)`)))}catch(t){console.log(d.red(` Failed: ${t.message}`)),console.log(d.dim(" Is the daemon running? Try: agentx daemon status"))}});A.command("deploy <host>").description("deploy agentx to a remote server via rsync").option("-i, --identity <key>","SSH identity file").option("-u, --user <user>","SSH user","clawd").option("-p, --path <path>","remote agentx path","~/agentx").option("--restart","restart remote daemon after deploy",!1).option("--skip-checks","skip build and test before deploy",!1).action(async(e,o)=>{let{execSync:n}=await import("child_process"),s=o.identity?`-e "ssh -i ${o.identity}"`:"",t=`${o.user}@${e}:${o.path}/dist/`;if(!o.skipChecks){try{console.log(d.dim(" Running tests...")),n("npx vitest run --reporter=dot",{stdio:"pipe",cwd:process.cwd()}),console.log(d.green(" \u2713 Tests passed"))}catch{console.log(d.red(" \u2717 Tests failed \u2014 aborting deploy")),console.log(d.dim(" Use --skip-checks to deploy anyway"));return}let{existsSync:i,statSync:r}=await import("fs"),{resolve:l}=await import("path"),c=l(process.cwd(),"dist/cli.js");if(!i(c)){console.log(d.red(" \u2717 dist/ not found \u2014 run build first"));return}Date.now()-r(c).mtimeMs>3e5&&(console.log(d.yellow(" \u26A0 dist/ is older than 5 minutes \u2014 rebuilding...")),n("npx tsup --no-dts",{stdio:"pipe",cwd:process.cwd()}),console.log(d.green(" \u2713 Build complete")))}console.log(d.dim(` Deploying to ${o.user}@${e}:${o.path}...`));try{if(n(`rsync -avzL --delete ${s} dist/ ${t}`,{stdio:"inherit",cwd:process.cwd()}),console.log(d.green(" \u2713 Deploy complete")),o.restart){console.log(d.dim(" Restarting remote daemon..."));let i=o.identity?`ssh -i ${o.identity}`:"ssh";n(`${i} ${o.user}@${e} "pkill -f 'node dist/cli.js daemon' 2>/dev/null; sleep 2; cd ${o.path} && nohup node dist/cli.js daemon start > /tmp/agentx-daemon.log 2>&1 &"`,{stdio:"inherit"}),console.log(d.green(" Remote daemon restarted"))}}catch(i){console.log(d.red(` Deploy failed: ${i.message}`))}});A.action(async e=>{await new _(e.config).start()});function ce(e){return e<60?`${Math.round(e)}s`:e<3600?`${Math.round(e/60)}m`:e<86400?`${Math.round(e/3600)}h ${Math.round(e%3600/60)}m`:`${Math.round(e/86400)}d ${Math.round(e%86400/3600)}h`}import{Command as le}from"commander";import j from"chalk";import{existsSync as R,readFileSync as de,writeFileSync as oe,mkdirSync as ne}from"fs";import{resolve as T}from"path";var te=new le().name("init").description("initialize agentx configuration in the current directory").option("--force","overwrite existing config").action(async e=>{let o=process.cwd(),n=T(o,"agentx.json"),s=T(o,".env"),t=T(o,".agentx/sessions");if(console.log(),console.log(j.bold(" agentx init")),console.log(),R(n)&&!e.force){console.log(j.yellow(" agentx.json already exists. Use --force to overwrite.")),console.log();return}let i=[T(o,"agentx.example.json"),T(o,"node_modules/agentix-cli/agentx.example.json")],r;for(let c of i)if(R(c)){r=de(c,"utf-8");break}r||(r=JSON.stringify({node:{id:V("os").hostname().toLowerCase().replace(/[^a-z0-9-]/g,"-"),name:V("os").hostname(),bind:"127.0.0.1:18800"},providers:{claude:{apiKey:"${ANTHROPIC_API_KEY}"}},agents:{assistant:{name:"Assistant",workspace:o,tier:"claude-code",model:"claude-sonnet-4-6",mentions:["@assistant","assistant"],maxConcurrent:2,systemPrompt:"You are a helpful assistant.",permissionMode:"default"}},channels:{telegram:{enabled:!1,accounts:{},policy:{dm:"pair",group:"mention-required"}},whatsapp:{enabled:!1}},crons:{},mesh:{enabled:!1,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}}},null,2)),oe(n,r),console.log(j.green(" \u2713 Created agentx.json")),R(s)||(oe(s,["# AgentX environment variables","# ANTHROPIC_API_KEY=sk-ant-...","# TG_BOT_TOKEN=123456:ABC...","# MESH_TOKEN=your-mesh-secret",""].join(`
|
|
4
|
+
`)),console.log(j.green(" \u2713 Created .env template"))),R(t)||(ne(t,{recursive:!0}),console.log(j.green(" \u2713 Created .agentx/sessions/")));let l=T(o,".claude");R(l)||(ne(l,{recursive:!0}),console.log(j.green(" \u2713 Created .claude/"))),console.log(),console.log(j.bold(" Next steps:")),console.log(` 1. Edit ${j.cyan("agentx.json")} \u2014 configure agents, channels, crons`),console.log(` 2. Edit ${j.cyan(".env")} \u2014 add API keys and bot tokens`),console.log(` 3. Run ${j.cyan("agentx daemon start")} \u2014 start the daemon`),console.log(` 4. Run ${j.cyan("agentx daemon status")} \u2014 verify everything`),console.log()});import{Command as I}from"commander";import a from"chalk";import x from"prompts";import{readFileSync as G,writeFileSync as J,existsSync as k,mkdirSync as N,cpSync as Y,readdirSync as se}from"fs";import{resolve as u,join as W}from"path";function b(){let e=u(process.cwd(),"agentx.json");if(!k(e))throw new Error("No agentx.json found. Run: agentx init");return JSON.parse(G(e,"utf-8"))}function v(e){J(u(process.cwd(),"agentx.json"),JSON.stringify(e,null,2))}var E=new I().name("agent").description("manage agents \u2014 add, list, remove");E.command("list").alias("ls").description("list configured agents").action(()=>{let e=b();console.log();for(let[o,n]of Object.entries(e.agents||{})){let s=n.mentions?.join(", ")||"";console.log(` ${a.cyan(o)} \u2014 ${n.name} (${n.tier})`),console.log(a.dim(` workspace: ${n.workspace}`)),s&&console.log(a.dim(` mentions: ${s}`))}console.log()});E.command("add").description("add a new agent interactively").action(async()=>{let e=b(),o=await x([{type:"text",name:"id",message:"Agent ID (kebab-case)",validate:l=>/^[a-z0-9-]+$/.test(l)||"Use lowercase, numbers, hyphens"},{type:"text",name:"name",message:"Display name"},{type:"text",name:"workspace",message:"Workspace path",initial:u(process.cwd(),"agents/")},{type:"select",name:"tier",message:"Execution tier",choices:[{title:"claude-code (subscription, full features)",value:"claude-code"},{title:"sdk (API key, programmatic)",value:"sdk"},{title:"orchestrator (any provider)",value:"orchestrator"}]},{type:"text",name:"model",message:"Model",initial:"claude-sonnet-4-6"},{type:"list",name:"mentions",message:"Mention patterns (comma-separated)",separator:","},{type:"number",name:"maxConcurrent",message:"Max concurrent tasks",initial:2},{type:"text",name:"systemPrompt",message:"System prompt"}]);if(!o.id)return;let n=o.workspace.endsWith(o.id)?o.workspace:W(o.workspace,o.id);N(u(n,".claude/skills"),{recursive:!0});let s=`# ${o.name}
|
|
5
5
|
|
|
6
6
|
${o.systemPrompt}
|
|
7
|
-
`;
|
|
8
|
-
Agent "${o.id}" added`)),console.log(s.dim(` Workspace: ${n}`)),console.log(s.dim(" Wiki skill installed")),console.log(s.dim(" CLAUDE.md + settings.json created")),console.log()});I.command("remove <id>").alias("rm").description("remove an agent from config (keeps workspace)").action(e=>{let o=p();if(!o.agents?.[e]){console.log(s.red(` Agent "${e}" not found`));return}delete o.agents[e],b(o),console.log(s.green(` Agent "${e}" removed from config (workspace preserved)`))});var B=new S().name("channel").description("manage channels \u2014 add telegram/whatsapp/discord, list");B.command("list").alias("ls").description("list configured channels and bindings").action(()=>{let e=p();console.log();let o=e.channels?.telegram;if(o?.enabled){console.log(s.bold(" Telegram:"));for(let[i,c]of Object.entries(o.accounts||{}))console.log(` ${s.cyan(i)} -> agent: ${c.agentBinding}`)}else console.log(s.dim(" Telegram: disabled"));let n=e.channels?.whatsapp;if(n?.enabled){console.log(s.bold(" WhatsApp:")),console.log(` default agent: ${n.defaultAgent||"(none)"}`),console.log(` session: ${n.sessionDir||".agentx/whatsapp-sessions"}`);for(let i of n.routes||[]){let c=i.contact?`contact ${i.contact}`:`group "${i.group}"`;console.log(` ${c} -> agent: ${i.agent}`)}}else console.log(s.dim(" WhatsApp: disabled"));let a=e.channels?.discord;a?.enabled?(console.log(s.bold(" Discord:")),console.log(` agent: ${a.agentBinding||"(none)"}`)):console.log(s.dim(" Discord: disabled"));let t=e.channels?.gitlab;if(t?.enabled){console.log(s.bold(" GitLab:")),console.log(` host: ${t.host}`),console.log(` webhook: :${t.webhookPort}`);for(let i of t.routes||[])console.log(` ${i.project} -> agent: ${i.agent}`)}else console.log(s.dim(" GitLab: disabled"));console.log()});B.command("add").description("add a channel (telegram, whatsapp, or discord)").action(async()=>{let e=p(),o=Object.keys(e.agents||{});if(!o.length){console.log(s.red(" No agents configured. Run: agentx agent add"));return}let n=o.map(t=>({title:`${t} (${e.agents[t].name})`,value:t})),{channelType:a}=await w({type:"select",name:"channelType",message:"Channel type",choices:[{title:"Telegram \u2014 bot via BotFather token",value:"telegram"},{title:"WhatsApp \u2014 link via QR code (self-chat or contacts)",value:"whatsapp"},{title:"Discord \u2014 bot via Discord developer portal",value:"discord"},{title:"GitLab \u2014 webhook for issues, MRs, comments, pipelines",value:"gitlab"}]});if(a){if(e.channels=e.channels||{},a==="telegram"){let t=await w([{type:"text",name:"accountName",message:"Account name (e.g. 'default', 'devops')"},{type:"text",name:"token",message:"Bot token (from @BotFather)"},{type:"select",name:"agentBinding",message:"Bind to agent",choices:n}]);if(!t.accountName||!t.token)return;try{let c=await(await fetch(`https://api.telegram.org/bot${t.token}/getMe`)).json();if(!c.ok)throw new Error(c.description);console.log(s.green(` Bot verified: @${c.result.username}`));let d=`@${c.result.username}`,l=e.agents[t.agentBinding].mentions||[];l.includes(d)||(l.push(d,c.result.username),e.agents[t.agentBinding].mentions=l)}catch(i){console.log(s.red(` Invalid token: ${i.message}`));return}e.channels.telegram=e.channels.telegram||{enabled:!1,accounts:{},policy:{dm:"pair",group:"mention-required"}},e.channels.telegram.enabled=!0,e.channels.telegram.accounts[t.accountName]={token:t.token,agentBinding:t.agentBinding},b(e),console.log(s.green(` Telegram "${t.accountName}" added -> ${t.agentBinding}`))}if(a==="whatsapp"){let t=await w([{type:"select",name:"defaultAgent",message:"Default agent for WhatsApp messages",choices:n},{type:"text",name:"sessionDir",message:"Session directory",initial:".agentx/whatsapp-sessions"},{type:"confirm",name:"addRoute",message:"Add a contact/group route?",initial:!0}]);if(!t.defaultAgent)return;let i=[],c=t.addRoute;for(;c;){let d=await w([{type:"select",name:"type",message:"Route type",choices:[{title:"Contact (phone number)",value:"contact"},{title:"Group (name match)",value:"group"}]},{type:"text",name:"value",message:l=>l==="contact"?"Phone number (e.g. +21624309128)":"Group name (partial match)"},{type:"select",name:"agent",message:"Route to agent",choices:n},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);d.value&&d.agent&&i.push({[d.type]:d.value,agent:d.agent}),c=d.more}e.channels.whatsapp={enabled:!0,sessionDir:t.sessionDir,defaultAgent:t.defaultAgent,routes:i},b(e),A(g(process.cwd(),t.sessionDir),{recursive:!0}),console.log(s.green(` WhatsApp enabled (${i.length} routes, default: ${t.defaultAgent})`)),console.log(s.dim(" Start daemon to scan QR code: agentx daemon start"))}if(a==="discord"){let t=await w([{type:"text",name:"token",message:"Discord bot token (from developer portal)"},{type:"select",name:"agentBinding",message:"Bind to agent",choices:n}]);if(!t.token)return;e.channels.discord={enabled:!0,token:t.token,agentBinding:t.agentBinding},b(e),console.log(s.green(` Discord added -> ${t.agentBinding}`))}if(a==="gitlab"){let t=await w([{type:"text",name:"host",message:"GitLab instance URL",initial:"https://gitlab.noqta.tn"},{type:"text",name:"token",message:"GitLab API token (PRIVATE-TOKEN)"},{type:"number",name:"webhookPort",message:"Webhook listen port",initial:18810},{type:"text",name:"webhookSecret",message:"Webhook secret (X-Gitlab-Token, optional)"},{type:"confirm",name:"addRoute",message:"Add a project route?",initial:!0}]);if(!t.token)return;let i=[],c=t.addRoute;for(;c;){let d=await w([{type:"text",name:"project",message:"GitLab project path (e.g. noqta/mtgl-v2, or * for default)"},{type:"select",name:"agent",message:"Route to agent",choices:n},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);d.project&&d.agent&&i.push({project:d.project,agent:d.agent}),c=d.more}e.channels.gitlab={enabled:!0,host:t.host,token:t.token,webhookPort:t.webhookPort,webhookSecret:t.webhookSecret||void 0,routes:i},b(e),console.log(s.green(` GitLab enabled (${i.length} routes, webhook :${t.webhookPort})`)),console.log(s.dim(` Add webhook in GitLab: ${t.host}/<project>/-/hooks`)),console.log(s.dim(` URL: http://your-server:${t.webhookPort}/`)),t.webhookSecret&&console.log(s.dim(` Secret token: ${t.webhookSecret}`)),console.log(s.dim(" Events: Comments, Issues, Merge requests, Pipeline"))}console.log(s.dim(" Restart daemon to apply: agentx daemon stop && agentx daemon start")),console.log()}});var P=new S().name("cron").description("manage cron jobs \u2014 add, list, enable, disable");P.command("list").alias("ls").description("list cron jobs").action(()=>{let e=p();console.log();for(let[o,n]of Object.entries(e.crons||{})){let a=n.enabled?s.green("\u25CF"):s.dim("\u25CB");console.log(` ${a} ${s.cyan(o)} \u2014 ${n.schedule} (${n.timezone})`),console.log(s.dim(` agent: ${n.agent} | timeout: ${n.timeout}s`))}Object.keys(e.crons||{}).length||console.log(s.dim(" No cron jobs configured")),console.log()});P.command("add").description("add a cron job").action(async()=>{let e=p(),o=Object.keys(e.agents||{}),n=await w([{type:"text",name:"id",message:"Job ID (kebab-case)"},{type:"text",name:"schedule",message:"Cron expression (e.g. '0 9 * * *')"},{type:"text",name:"timezone",message:"Timezone",initial:"UTC"},{type:"select",name:"agent",message:"Agent",choices:o.map(a=>({title:a,value:a}))},{type:"text",name:"prompt",message:"Task prompt"},{type:"number",name:"timeout",message:"Timeout (seconds)",initial:600},{type:"confirm",name:"enabled",message:"Enable now?",initial:!1}]);!n.id||!n.schedule||(e.crons=e.crons||{},e.crons[n.id]={enabled:n.enabled,schedule:n.schedule,timezone:n.timezone,agent:n.agent,prompt:n.prompt,timeout:n.timeout,onError:"log"},b(e),console.log(s.green(` Cron "${n.id}" added (${n.enabled?"enabled":"disabled"})`)),console.log())});P.command("enable <id>").description("enable a cron job").action(e=>{let o=p();if(!o.crons?.[e]){console.log(s.red(` Cron "${e}" not found`));return}o.crons[e].enabled=!0,b(o),console.log(s.green(` Cron "${e}" enabled`))});P.command("disable <id>").description("disable a cron job").action(e=>{let o=p();if(!o.crons?.[e]){console.log(s.red(` Cron "${e}" not found`));return}o.crons[e].enabled=!1,b(o),console.log(s.green(` Cron "${e}" disabled`))});var T=new S().name("mesh").description("manage mesh peers \u2014 add, list, remove");T.command("list").alias("ls").description("list mesh peers").action(async()=>{let e=p();if(console.log(),!e.mesh?.enabled)console.log(s.dim(" Mesh: disabled"));else{console.log(s.bold(" Mesh peers:"));for(let o of e.mesh.peers||[]){let n=s.red("unreachable");try{let t=await(await fetch(`${o.url}/health`,{signal:AbortSignal.timeout(3e3)})).json();n=s.green(`healthy \u2014 ${t.agents?.length||0} agents`)}catch{}console.log(` ${s.cyan(o.name)} ${s.dim(o.url)} ${n}`)}}console.log()});T.command("add").description("add a mesh peer (another agentx server)").action(async()=>{let e=p(),o=await w([{type:"text",name:"name",message:"Peer name (e.g. 'server-2')"},{type:"text",name:"url",message:"Peer URL (e.g. 'http://100.67.108.119:18800')"},{type:"text",name:"token",message:"Auth token (optional, press enter to skip)"}]);if(!(!o.name||!o.url)){try{let a=await(await fetch(`${o.url}/.well-known/agent-card.json`,{signal:AbortSignal.timeout(5e3)})).json();console.log(s.green(` Connected: ${a.name} (${a.skills?.length||0} agents)`))}catch{console.log(s.yellow(" Could not reach peer (adding anyway)"))}e.mesh=e.mesh||{enabled:!1,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}},e.mesh.enabled=!0,e.mesh.peers=e.mesh.peers||[],e.mesh.peers.push({url:o.url,name:o.name,...o.token?{token:o.token}:{}}),b(e),console.log(s.green(` Peer "${o.name}" added`)),console.log()}});T.command("remove <name>").alias("rm").description("remove a mesh peer").action(e=>{let o=p();o.mesh?.peers&&(o.mesh.peers=o.mesh.peers.filter(n=>n.name!==e),o.mesh.peers.length===0&&(o.mesh.enabled=!1),b(o),console.log(s.green(` Peer "${e}" removed`)))});var W=new S().name("skill").description("manage skills \u2014 add to agent(s), list");W.command("add <skillPath>").description("add a skill to agent(s)").option("-a, --agent <agents...>","target agent ID(s)").option("--all","add to all agents").action(async(e,o)=>{let n=p(),a=g(e);if(!h(a)){console.log(s.red(` Skill not found: ${a}`));return}let t=h(L(a,"SKILL.md"))?a.split("/").pop():a.replace(/\/SKILL\.md$/,"").split("/").pop(),i;if(o.all)i=Object.keys(n.agents||{});else if(o.agent)i=o.agent;else{let c=Object.keys(n.agents||{});i=(await w({type:"multiselect",name:"agents",message:"Install to which agents?",choices:c.map(l=>({title:`${l} (${n.agents[l].name})`,value:l}))})).agents||[]}for(let c of i){let d=n.agents[c];if(!d){console.log(s.yellow(` Agent "${c}" not found, skipping`));continue}let l=g(d.workspace,".claude/skills",t);A(l,{recursive:!0}),h(L(a,"SKILL.md"))?F(a,l,{recursive:!0}):a.endsWith(".md")&&F(a,L(l,"SKILL.md")),console.log(s.green(` ${t} -> ${c} (${d.workspace})`))}console.log()});W.command("list").alias("ls").description("list skills per agent").action(()=>{let e=p();console.log();for(let[o,n]of Object.entries(e.agents||{})){let a=g(n.workspace,".claude/skills"),t=h(a)?Y(a).filter(i=>!i.startsWith(".")):[];console.log(` ${s.cyan(o)}: ${t.length?t.join(", "):s.dim("(none)")}`)}console.log()});var J=new S().name("hook").description("manage hooks \u2014 add to agent workspace");J.command("add <agent>").description("add a hook to an agent's workspace settings").action(async e=>{let n=p().agents?.[e];if(!n){console.log(s.red(` Agent "${e}" not found`));return}let a=g(n.workspace,".claude/settings.json"),t=h(a)?JSON.parse(M(a,"utf-8")):{},i=await w([{type:"select",name:"event",message:"Hook event",choices:[{title:"PreToolUse \u2014 before any tool runs",value:"PreToolUse"},{title:"PostToolUse \u2014 after any tool runs",value:"PostToolUse"},{title:"SessionStart \u2014 when session begins",value:"SessionStart"},{title:"Notification \u2014 on notifications",value:"Notification"},{title:"Stop \u2014 before session stops",value:"Stop"}]},{type:"select",name:"type",message:"Hook type",choices:[{title:"command \u2014 run a shell command",value:"command"},{title:"http \u2014 POST to a URL",value:"http"}]},{type:"text",name:"value",message:l=>l==="command"?"Shell command":"URL"},{type:"text",name:"matcher",message:"Matcher regex (optional, e.g. 'Bash' to match tool)"}]);if(!i.event||!i.value)return;t.hooks=t.hooks||{},t.hooks[i.event]=t.hooks[i.event]||[];let d={hooks:[{type:i.type,...i.type==="command"?{command:i.value}:{url:i.value}}]};i.matcher&&(d.matcher=i.matcher),t.hooks[i.event].push(d),A(g(n.workspace,".claude"),{recursive:!0}),E(a,JSON.stringify(t,null,2)),console.log(s.green(` Hook added to ${e}: ${i.event} -> ${i.type}`)),console.log()});var K=new S().name("migrate").description("import configuration from OpenClaw or other tools");K.command("openclaw [configPath]").description("import agents, channels, and crons from OpenClaw config").option("--dry-run","show what would be imported without writing").action(async(e,o)=>{let n=[e,g(process.env.HOME||"",".openclaw/openclaw.json"),g(process.env.HOME||"",".openclaw/clawdbot.json")].filter(Boolean),a;for(let m of n)if(m&&h(m)){a=m;break}if(!a){console.log(s.red(" OpenClaw config not found. Pass the path: agentx migrate openclaw /path/to/config.json"));return}console.log(s.dim(` Reading: ${a}`));let t=JSON.parse(M(a,"utf-8")),i=h(g(process.cwd(),"agentx.json"))?p():{node:{id:"imported",name:"Imported",bind:"127.0.0.1:18800"},providers:{},agents:{},channels:{telegram:{enabled:!1,accounts:{},policy:{dm:"pair",group:"mention-required"}},whatsapp:{enabled:!1}},crons:{},mesh:{enabled:!1,peers:[]}},c=t.agents?.list||[],d=t.bindings||[],l={};for(let m of d)m.match?.channel==="telegram"&&(l[m.match.accountId]=m.agentId);c.find(m=>m.id==="main"||m.default)&&(l.default="main");let u=0;for(let m of c){if(m.workspace?.startsWith("/data"))continue;let x=m.id.replace(/-agent$/,"").replace(/^main$/,"atlas"),f=m.groupChat?.mentionPatterns||[];f.some(D=>D===x)||f.push(x),i.agents[x]={name:m.name,workspace:m.workspace,tier:"claude-code",model:"claude-sonnet-4-6",mentions:f,maxConcurrent:m.id==="main"?2:1,systemPrompt:`You are ${m.name} on AgentX.`,permissionMode:"bypassPermissions"},u++,o.dryRun||A(g(m.workspace,".claude/skills"),{recursive:!0})}console.log(` ${s.green(u)} agents imported`);let k=t.channels?.telegram?.accounts||{},y=0;for(let[m,x]of Object.entries(k)){if(!x.botToken)continue;let D=l[m]?.replace(/-agent$/,"").replace(/^main$/,"atlas")||"atlas";i.channels.telegram.accounts[m]={token:x.botToken,agentBinding:D},i.channels.telegram.enabled=!0,y++}console.log(` ${s.green(y)} Telegram accounts imported`);let N=g(a,"../cron/jobs.json");if(h(N)){let m=JSON.parse(M(N,"utf-8")),x=0;for(let f of m.jobs||[]){let D=f.name||f.id,Q=f.agentId?.replace(/-agent$/,"").replace(/^main$/,"atlas")||"atlas";i.crons[D]={enabled:f.enabled||!1,schedule:f.schedule?.expr||"0 9 * * *",timezone:f.schedule?.tz||"UTC",agent:Q,prompt:f.payload?.message||"",timeout:f.payload?.timeoutSeconds||600,onError:"log"},x++}console.log(` ${s.green(x)} cron jobs imported`)}o.dryRun?(console.log(s.yellow(`
|
|
9
|
-
Dry run \u2014 nothing written`)),console.log(JSON.stringify(i,null,2))):(
|
|
10
|
-
Migration complete! Review agentx.json and run: agentx daemon start`))),console.log()});var
|
|
7
|
+
`;J(u(n,"CLAUDE.md"),s),J(u(n,".claude/settings.json"),JSON.stringify({permissions:{allow:["Bash(git *)","Bash(curl *)","Bash(node *)","Read","Write","Edit","Glob","Grep","WebFetch","WebSearch"]}},null,2));let t=u(process.cwd(),"src/wiki/SKILL.md"),i=u(__dirname,"../wiki/SKILL.md"),r=k(t)?t:i;k(r)&&(N(u(n,".claude/skills/wiki"),{recursive:!0}),Y(r,u(n,".claude/skills/wiki/SKILL.md"))),e.agents=e.agents||{},e.agents[o.id]={name:o.name,workspace:n,tier:o.tier,model:o.model,mentions:o.mentions?.map(l=>l.trim()).filter(Boolean)||[],maxConcurrent:o.maxConcurrent,systemPrompt:o.systemPrompt,permissionMode:"default"},v(e),console.log(a.green(`
|
|
8
|
+
Agent "${o.id}" added`)),console.log(a.dim(` Workspace: ${n}`)),console.log(a.dim(" Wiki skill installed")),console.log(a.dim(" CLAUDE.md + settings.json created")),console.log()});E.command("remove <id>").alias("rm").description("remove an agent from config (keeps workspace)").action(e=>{let o=b();if(!o.agents?.[e]){console.log(a.red(` Agent "${e}" not found`));return}delete o.agents[e],v(o),console.log(a.green(` Agent "${e}" removed from config (workspace preserved)`))});var F=new I().name("channel").description("manage channels \u2014 add telegram/whatsapp/discord, list");F.command("list").alias("ls").description("list configured channels and bindings").action(()=>{let e=b();console.log();let o=e.channels?.telegram;if(o?.enabled){console.log(a.bold(" Telegram:"));for(let[i,r]of Object.entries(o.accounts||{}))console.log(` ${a.cyan(i)} -> agent: ${r.agentBinding}`)}else console.log(a.dim(" Telegram: disabled"));let n=e.channels?.whatsapp;if(n?.enabled){console.log(a.bold(" WhatsApp:")),console.log(` default agent: ${n.defaultAgent||"(none)"}`),console.log(` session: ${n.sessionDir||".agentx/whatsapp-sessions"}`);for(let i of n.routes||[]){let r=i.contact?`contact ${i.contact}`:`group "${i.group}"`;console.log(` ${r} -> agent: ${i.agent}`)}}else console.log(a.dim(" WhatsApp: disabled"));let s=e.channels?.discord;s?.enabled?(console.log(a.bold(" Discord:")),console.log(` agent: ${s.agentBinding||"(none)"}`)):console.log(a.dim(" Discord: disabled"));let t=e.channels?.gitlab;if(t?.enabled){console.log(a.bold(" GitLab:")),console.log(` host: ${t.host}`),console.log(` webhook: :${t.webhookPort}`);for(let i of t.routes||[])console.log(` ${i.project} -> agent: ${i.agent}`)}else console.log(a.dim(" GitLab: disabled"));console.log()});F.command("add").description("add a channel (telegram, whatsapp, or discord)").action(async()=>{let e=b(),o=Object.keys(e.agents||{});if(!o.length){console.log(a.red(" No agents configured. Run: agentx agent add"));return}let n=o.map(t=>({title:`${t} (${e.agents[t].name})`,value:t})),{channelType:s}=await x({type:"select",name:"channelType",message:"Channel type",choices:[{title:"Telegram \u2014 bot via BotFather token",value:"telegram"},{title:"WhatsApp \u2014 link via QR code (self-chat or contacts)",value:"whatsapp"},{title:"Discord \u2014 bot via Discord developer portal",value:"discord"},{title:"GitLab \u2014 webhook for issues, MRs, comments, pipelines",value:"gitlab"}]});if(s){if(e.channels=e.channels||{},s==="telegram"){let t=await x([{type:"text",name:"accountName",message:"Account name (e.g. 'default', 'devops')"},{type:"text",name:"token",message:"Bot token (from @BotFather)"},{type:"select",name:"agentBinding",message:"Bind to agent",choices:n}]);if(!t.accountName||!t.token)return;try{let r=await(await fetch(`https://api.telegram.org/bot${t.token}/getMe`)).json();if(!r.ok)throw new Error(r.description);console.log(a.green(` Bot verified: @${r.result.username}`));let l=`@${r.result.username}`,c=e.agents[t.agentBinding].mentions||[];c.includes(l)||(c.push(l,r.result.username),e.agents[t.agentBinding].mentions=c)}catch(i){console.log(a.red(` Invalid token: ${i.message}`));return}e.channels.telegram=e.channels.telegram||{enabled:!1,accounts:{},policy:{dm:"pair",group:"mention-required"}},e.channels.telegram.enabled=!0,e.channels.telegram.accounts[t.accountName]={token:t.token,agentBinding:t.agentBinding},v(e),console.log(a.green(` Telegram "${t.accountName}" added -> ${t.agentBinding}`))}if(s==="whatsapp"){let t=await x([{type:"select",name:"defaultAgent",message:"Default agent for WhatsApp messages",choices:n},{type:"text",name:"sessionDir",message:"Session directory",initial:".agentx/whatsapp-sessions"},{type:"confirm",name:"addRoute",message:"Add a contact/group route?",initial:!0}]);if(!t.defaultAgent)return;let i=[],r=t.addRoute;for(;r;){let l=await x([{type:"select",name:"type",message:"Route type",choices:[{title:"Contact (phone number)",value:"contact"},{title:"Group (name match)",value:"group"}]},{type:"text",name:"value",message:c=>c==="contact"?"Phone number (e.g. +21624309128)":"Group name (partial match)"},{type:"select",name:"agent",message:"Route to agent",choices:n},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);l.value&&l.agent&&i.push({[l.type]:l.value,agent:l.agent}),r=l.more}e.channels.whatsapp={enabled:!0,sessionDir:t.sessionDir,defaultAgent:t.defaultAgent,routes:i},v(e),N(u(process.cwd(),t.sessionDir),{recursive:!0}),console.log(a.green(` WhatsApp enabled (${i.length} routes, default: ${t.defaultAgent})`)),console.log(a.dim(" Start daemon to scan QR code: agentx daemon start"))}if(s==="discord"){let t=await x([{type:"text",name:"token",message:"Discord bot token (from developer portal)"},{type:"select",name:"agentBinding",message:"Bind to agent",choices:n}]);if(!t.token)return;e.channels.discord={enabled:!0,token:t.token,agentBinding:t.agentBinding},v(e),console.log(a.green(` Discord added -> ${t.agentBinding}`))}if(s==="gitlab"){let t=await x([{type:"text",name:"host",message:"GitLab instance URL",initial:"https://gitlab.noqta.tn"},{type:"text",name:"token",message:"GitLab API token (PRIVATE-TOKEN)"},{type:"number",name:"webhookPort",message:"Webhook listen port",initial:18810},{type:"text",name:"webhookSecret",message:"Webhook secret (X-Gitlab-Token, optional)"},{type:"confirm",name:"addRoute",message:"Add a project route?",initial:!0}]);if(!t.token)return;let i=[],r=t.addRoute;for(;r;){let l=await x([{type:"text",name:"project",message:"GitLab project path (e.g. noqta/mtgl-v2, or * for default)"},{type:"select",name:"agent",message:"Route to agent",choices:n},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);l.project&&l.agent&&i.push({project:l.project,agent:l.agent}),r=l.more}e.channels.gitlab={enabled:!0,host:t.host,token:t.token,webhookPort:t.webhookPort,webhookSecret:t.webhookSecret||void 0,routes:i},v(e),console.log(a.green(` GitLab enabled (${i.length} routes, webhook :${t.webhookPort})`)),console.log(a.dim(` Add webhook in GitLab: ${t.host}/<project>/-/hooks`)),console.log(a.dim(` URL: http://your-server:${t.webhookPort}/`)),t.webhookSecret&&console.log(a.dim(` Secret token: ${t.webhookSecret}`)),console.log(a.dim(" Events: Comments, Issues, Merge requests, Pipeline"))}console.log(a.dim(" Restart daemon to apply: agentx daemon stop && agentx daemon start")),console.log()}});var P=new I().name("cron").description("manage cron jobs \u2014 add, list, enable, disable");P.command("list").alias("ls").description("list cron jobs").action(()=>{let e=b();console.log();for(let[o,n]of Object.entries(e.crons||{})){let s=n.enabled?a.green("\u25CF"):a.dim("\u25CB");console.log(` ${s} ${a.cyan(o)} \u2014 ${n.schedule} (${n.timezone})`),console.log(a.dim(` agent: ${n.agent} | timeout: ${n.timeout}s`))}Object.keys(e.crons||{}).length||console.log(a.dim(" No cron jobs configured")),console.log()});P.command("add").description("add a cron job").action(async()=>{let e=b(),o=Object.keys(e.agents||{}),n=await x([{type:"text",name:"id",message:"Job ID (kebab-case)"},{type:"text",name:"schedule",message:"Cron expression (e.g. '0 9 * * *')"},{type:"text",name:"timezone",message:"Timezone",initial:"UTC"},{type:"select",name:"agent",message:"Agent",choices:o.map(s=>({title:s,value:s}))},{type:"text",name:"prompt",message:"Task prompt"},{type:"number",name:"timeout",message:"Timeout (seconds)",initial:600},{type:"confirm",name:"enabled",message:"Enable now?",initial:!1}]);!n.id||!n.schedule||(e.crons=e.crons||{},e.crons[n.id]={enabled:n.enabled,schedule:n.schedule,timezone:n.timezone,agent:n.agent,prompt:n.prompt,timeout:n.timeout,onError:"log"},v(e),console.log(a.green(` Cron "${n.id}" added (${n.enabled?"enabled":"disabled"})`)),console.log())});P.command("enable <id>").description("enable a cron job").action(e=>{let o=b();if(!o.crons?.[e]){console.log(a.red(` Cron "${e}" not found`));return}o.crons[e].enabled=!0,v(o),console.log(a.green(` Cron "${e}" enabled`))});P.command("disable <id>").description("disable a cron job").action(e=>{let o=b();if(!o.crons?.[e]){console.log(a.red(` Cron "${e}" not found`));return}o.crons[e].enabled=!1,v(o),console.log(a.green(` Cron "${e}" disabled`))});var L=new I().name("mesh").description("manage mesh peers \u2014 add, list, remove");L.command("list").alias("ls").description("list mesh peers").action(async()=>{let e=b();if(console.log(),!e.mesh?.enabled)console.log(a.dim(" Mesh: disabled"));else{console.log(a.bold(" Mesh peers:"));for(let o of e.mesh.peers||[]){let n=a.red("unreachable");try{let t=await(await fetch(`${o.url}/health`,{signal:AbortSignal.timeout(3e3)})).json();n=a.green(`healthy \u2014 ${t.agents?.length||0} agents`)}catch{}console.log(` ${a.cyan(o.name)} ${a.dim(o.url)} ${n}`)}}console.log()});L.command("add").description("add a mesh peer (another agentx server)").action(async()=>{let e=b(),o=await x([{type:"text",name:"name",message:"Peer name (e.g. 'server-2')"},{type:"text",name:"url",message:"Peer URL (e.g. 'http://100.67.108.119:18800')"},{type:"text",name:"token",message:"Auth token (optional, press enter to skip)"}]);if(!(!o.name||!o.url)){try{let s=await(await fetch(`${o.url}/.well-known/agent-card.json`,{signal:AbortSignal.timeout(5e3)})).json();console.log(a.green(` Connected: ${s.name} (${s.skills?.length||0} agents)`))}catch{console.log(a.yellow(" Could not reach peer (adding anyway)"))}e.mesh=e.mesh||{enabled:!1,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}},e.mesh.enabled=!0,e.mesh.peers=e.mesh.peers||[],e.mesh.peers.push({url:o.url,name:o.name,...o.token?{token:o.token}:{}}),v(e),console.log(a.green(` Peer "${o.name}" added`)),console.log()}});L.command("remove <name>").alias("rm").description("remove a mesh peer").action(e=>{let o=b();o.mesh?.peers&&(o.mesh.peers=o.mesh.peers.filter(n=>n.name!==e),o.mesh.peers.length===0&&(o.mesh.enabled=!1),v(o),console.log(a.green(` Peer "${e}" removed`)))});var z=new I().name("skill").description("manage skills \u2014 add to agent(s), list");z.command("add <skillPath>").description("add a skill to agent(s)").option("-a, --agent <agents...>","target agent ID(s)").option("--all","add to all agents").action(async(e,o)=>{let n=b(),s=u(e);if(!k(s)){console.log(a.red(` Skill not found: ${s}`));return}let t=k(W(s,"SKILL.md"))?s.split("/").pop():s.replace(/\/SKILL\.md$/,"").split("/").pop(),i;if(o.all)i=Object.keys(n.agents||{});else if(o.agent)i=o.agent;else{let r=Object.keys(n.agents||{});i=(await x({type:"multiselect",name:"agents",message:"Install to which agents?",choices:r.map(c=>({title:`${c} (${n.agents[c].name})`,value:c}))})).agents||[]}for(let r of i){let l=n.agents[r];if(!l){console.log(a.yellow(` Agent "${r}" not found, skipping`));continue}let c=u(l.workspace,".claude/skills",t);N(c,{recursive:!0}),k(W(s,"SKILL.md"))?Y(s,c,{recursive:!0}):s.endsWith(".md")&&Y(s,W(c,"SKILL.md")),console.log(a.green(` ${t} -> ${r} (${l.workspace})`))}console.log()});z.command("list").alias("ls").description("list skills per agent").action(()=>{let e=b();console.log();for(let[o,n]of Object.entries(e.agents||{})){let s=u(n.workspace,".claude/skills"),t=k(s)?se(s).filter(i=>!i.startsWith(".")):[];console.log(` ${a.cyan(o)}: ${t.length?t.join(", "):a.dim("(none)")}`)}console.log()});var q=new I().name("hook").description("manage hooks \u2014 add to agent workspace");q.command("add <agent>").description("add a hook to an agent's workspace settings").action(async e=>{let n=b().agents?.[e];if(!n){console.log(a.red(` Agent "${e}" not found`));return}let s=u(n.workspace,".claude/settings.json"),t=k(s)?JSON.parse(G(s,"utf-8")):{},i=await x([{type:"select",name:"event",message:"Hook event",choices:[{title:"PreToolUse \u2014 before any tool runs",value:"PreToolUse"},{title:"PostToolUse \u2014 after any tool runs",value:"PostToolUse"},{title:"SessionStart \u2014 when session begins",value:"SessionStart"},{title:"Notification \u2014 on notifications",value:"Notification"},{title:"Stop \u2014 before session stops",value:"Stop"}]},{type:"select",name:"type",message:"Hook type",choices:[{title:"command \u2014 run a shell command",value:"command"},{title:"http \u2014 POST to a URL",value:"http"}]},{type:"text",name:"value",message:c=>c==="command"?"Shell command":"URL"},{type:"text",name:"matcher",message:"Matcher regex (optional, e.g. 'Bash' to match tool)"}]);if(!i.event||!i.value)return;t.hooks=t.hooks||{},t.hooks[i.event]=t.hooks[i.event]||[];let l={hooks:[{type:i.type,...i.type==="command"?{command:i.value}:{url:i.value}}]};i.matcher&&(l.matcher=i.matcher),t.hooks[i.event].push(l),N(u(n.workspace,".claude"),{recursive:!0}),J(s,JSON.stringify(t,null,2)),console.log(a.green(` Hook added to ${e}: ${i.event} -> ${i.type}`)),console.log()});var X=new I().name("migrate").description("import configuration from OpenClaw or other tools");X.command("openclaw [configPath]").description("import agents, channels, and crons from OpenClaw config").option("--dry-run","show what would be imported without writing").action(async(e,o)=>{let n=[e,u(process.env.HOME||"",".openclaw/openclaw.json"),u(process.env.HOME||"",".openclaw/clawdbot.json")].filter(Boolean),s;for(let p of n)if(p&&k(p)){s=p;break}if(!s){console.log(a.red(" OpenClaw config not found. Pass the path: agentx migrate openclaw /path/to/config.json"));return}console.log(a.dim(` Reading: ${s}`));let t=JSON.parse(G(s,"utf-8")),i=k(u(process.cwd(),"agentx.json"))?b():{node:{id:"imported",name:"Imported",bind:"127.0.0.1:18800"},providers:{},agents:{},channels:{telegram:{enabled:!1,accounts:{},policy:{dm:"pair",group:"mention-required"}},whatsapp:{enabled:!1}},crons:{},mesh:{enabled:!1,peers:[]}},r=t.agents?.list||[],l=t.bindings||[],c={};for(let p of l)p.match?.channel==="telegram"&&(c[p.match.accountId]=p.agentId);r.find(p=>p.id==="main"||p.default)&&(c.default="main");let y=0;for(let p of r){if(p.workspace?.startsWith("/data"))continue;let f=p.id.replace(/-agent$/,"").replace(/^main$/,"atlas"),w=p.groupChat?.mentionPatterns||[];w.some(O=>O===f)||w.push(f),i.agents[f]={name:p.name,workspace:p.workspace,tier:"claude-code",model:"claude-sonnet-4-6",mentions:w,maxConcurrent:p.id==="main"?2:1,systemPrompt:`You are ${p.name} on AgentX.`,permissionMode:"bypassPermissions"},y++,o.dryRun||N(u(p.workspace,".claude/skills"),{recursive:!0})}console.log(` ${a.green(y)} agents imported`);let g=t.channels?.telegram?.accounts||{},h=0;for(let[p,f]of Object.entries(g)){if(!f.botToken)continue;let O=c[p]?.replace(/-agent$/,"").replace(/^main$/,"atlas")||"atlas";i.channels.telegram.accounts[p]={token:f.botToken,agentBinding:O},i.channels.telegram.enabled=!0,h++}console.log(` ${a.green(h)} Telegram accounts imported`);let S=u(s,"../cron/jobs.json");if(k(S)){let p=JSON.parse(G(S,"utf-8")),f=0;for(let w of p.jobs||[]){let O=w.name||w.id,B=w.agentId?.replace(/-agent$/,"").replace(/^main$/,"atlas")||"atlas";i.crons[O]={enabled:w.enabled||!1,schedule:w.schedule?.expr||"0 9 * * *",timezone:w.schedule?.tz||"UTC",agent:B,prompt:w.payload?.message||"",timeout:w.payload?.timeoutSeconds||600,onError:"log"},f++}console.log(` ${a.green(f)} cron jobs imported`)}o.dryRun?(console.log(a.yellow(`
|
|
9
|
+
Dry run \u2014 nothing written`)),console.log(JSON.stringify(i,null,2))):(v(i),console.log(a.green(`
|
|
10
|
+
Migration complete! Review agentx.json and run: agentx daemon start`))),console.log()});var H=new I().name("config").description("validate and inspect configuration");H.command("check").description("validate agentx.json and check all workspaces").option("-c, --config <path>","path to agentx.json").action(async e=>{console.log();try{let o=C(e.config);console.log(a.green(" \u2713 Config valid")),console.log(a.dim(` Node: ${o.node.name} (${o.node.id})`)),console.log(a.dim(` Bind: ${o.node.bind}`)),console.log(a.dim(` Agents: ${Object.keys(o.agents).length}`)),console.log(a.dim(` Crons: ${Object.keys(o.crons).length}`)),console.log(a.dim(` Mesh peers: ${o.mesh.peers.length}`));let n=Object.keys(o.channels.telegram.accounts).length;console.log(a.dim(` Telegram: ${o.channels.telegram.enabled?`${n} accounts`:"disabled"}`)),console.log(a.dim(` WhatsApp: ${o.channels.whatsapp.enabled?`${o.channels.whatsapp.routes.length} routes`:"disabled"}`)),console.log(a.dim(` Discord: ${o.channels.discord?.enabled?"enabled":"disabled"}`));let s=U(o);if(s.length){console.log(),console.log(a.yellow(" Warnings:"));for(let t of s)console.log(a.yellow(` \u26A0 ${t}`))}else console.log(a.green(" \u2713 All workspaces valid"));console.log();for(let[t,i]of Object.entries(o.agents)){let r=i.workspace,l=k(u(r,".claude")),c=k(u(r,"CLAUDE.md")),y=k(u(r,".claude/settings.json")),g=k(u(r,".claude/skills"))?se(u(r,".claude/skills")).filter(S=>!S.startsWith(".")).length:0,h=[l?a.green("\u2713"):a.red("\u2717"),".claude",c?a.green("\u2713"):a.yellow("\u25CB"),"CLAUDE.md",y?a.green("\u2713"):a.yellow("\u25CB"),"settings",`${g} skills`];console.log(` ${a.cyan(t)}: ${h.join(" ")}`)}}catch(o){console.log(a.red(` \u2717 ${o.message}`))}console.log()});H.command("show").description("print the resolved configuration").option("-c, --config <path>","path to agentx.json").action(e=>{try{let o=C(e.config);console.log(JSON.stringify(o,null,2))}catch(o){console.log(a.red(o.message))}});import{Command as ge}from"commander";import $ from"chalk";var K=new ge().name("usage").description("token usage analysis and reporting");K.command("today",{isDefault:!0}).description("show today's token usage").action(async()=>{try{let e=C(),[o,n]=e.node.bind.split(":"),t=await(await fetch(`http://${o||"127.0.0.1"}:${n}/usage`,{signal:AbortSignal.timeout(3e3)})).json();if(console.log(),console.log($.bold(" Token Usage (last 7 days)")),console.log(),t.totalTasks===0){console.log($.dim(" No tasks recorded yet")),console.log();return}let i=l=>l.toLocaleString();console.log(` Total: ${$.bold(i(t.totalTokens))} tokens across ${t.totalTasks} tasks`),console.log(` Input: ${i(t.totalInput)}`),console.log(` Output: ${i(t.totalOutput)}`),console.log(` Cache read: ${i(t.totalCacheRead)}`),console.log(` Cache create: ${i(t.totalCacheCreate)}`),t.cacheHitRatio>0&&console.log(` Cache hit: ${(t.cacheHitRatio*100).toFixed(1)}%`),t.totalErrors>0&&console.log(` Errors: ${$.red(String(t.totalErrors))}`),console.log(),console.log($.bold(" By Agent:"));let r=Object.entries(t.byAgent||{});r.sort((l,c)=>c[1].total-l[1].total);for(let[l,c]of r){let y=i(c.total||c.input+c.output+c.cacheRead+c.cacheCreate),g=c.avgDuration?`${(c.avgDuration/1e3).toFixed(1)}s avg`:"";console.log(` ${$.cyan(l)}: ${y} tokens (${c.tasks} tasks) ${$.dim(g)}`)}console.log()}catch(e){e.cause?.code==="ECONNREFUSED"?console.log($.red(" Daemon not running. Start with: agentx daemon start")):console.log($.red(` ${e.message}`))}});K.command("report").description("run full session analysis (parses Claude Code JSONL files)").option("--days <n>","analyze last N days","7").action(async e=>{let{execSync:o}=await import("child_process"),{existsSync:n}=await import("fs"),{resolve:s}=await import("path"),t=s(process.cwd(),"scripts/token-report.py");if(!n(t)){console.log($.red(` Script not found: ${t}`)),console.log($.dim(" The token analyzer script should be at scripts/token-report.py"));return}console.log($.dim(" Analyzing Claude Code sessions..."));try{o(`SINCE_DAYS=${e.days} python3 "${t}"`,{stdio:"inherit"})}catch{console.log($.red(" Analysis failed. Is Python 3 installed?"))}});import{Command as me}from"commander";import m from"chalk";import{resolve as Q}from"path";import{execSync as pe}from"child_process";import{writeFileSync as ue,mkdirSync as he}from"fs";function M(e){let o=e||Q(process.cwd(),".agentx/wiki");return new Z(o)}var D=new me().name("wiki").description("wiki knowledge base management");D.command("status").description("show wiki status: entries, articles, unabsorbed count").option("--dir <path>","wiki directory").action(e=>{let o=M(e.dir),n=o.listEntries(),s=o.getUnabsorbedEntries(),t=o.rebuildIndex();if(console.log(),console.log(m.bold(" Wiki Status")),console.log(),console.log(` Raw entries: ${n.length}`),console.log(` Articles: ${t.articles.length}`),console.log(` Unabsorbed: ${m.yellow(String(s.length))}`),console.log(),s.length>0){console.log(m.dim(" Run 'agentx wiki absorb' to compile entries into articles")),console.log();let i=new Map;for(let r of s)i.set(r.agentId,(i.get(r.agentId)||0)+1);for(let[r,l]of i)console.log(` ${m.cyan(r)}: ${l} unabsorbed entries`);console.log()}});D.command("lint").description("check wiki for issues: broken links, orphans, stubs, unsourced").option("--dir <path>","wiki directory").action(e=>{let n=M(e.dir).lint();if(console.log(),n.length===0)console.log(m.green(" No issues found"));else{console.log(m.bold(` ${n.length} issues found:`)),console.log();for(let s of n){let t=s.type==="broken-link"?"x":s.type==="orphan"?"?":"!";console.log(` [${t}] ${m.dim(s.type)} ${s.article}: ${s.message}`)}}console.log()});D.command("absorb").description("compile unabsorbed raw entries into wiki articles using Claude").option("--dir <path>","wiki directory").option("--dry-run","show what would be absorbed without running").option("--max <n>","max entries to process","20").action(async e=>{let o=M(e.dir),n=o.getUnabsorbedEntries().slice(0,parseInt(e.max));if(n.length===0){console.log(m.green(" All entries are absorbed. Nothing to do."));return}console.log(m.bold(` ${n.length} entries to absorb`)),console.log();let s=new Map;for(let g of n){let h=s.get(g.agentId)||[];h.push(g),s.set(g.agentId,h)}for(let[g,h]of s){console.log(` ${m.cyan(g)}: ${h.length} entries`);for(let S of h.slice(0,3))console.log(m.dim(` [${S.date} via ${S.source}] ${S.content.slice(0,80)}...`));h.length>3&&console.log(m.dim(` ... and ${h.length-3} more`))}if(console.log(),e.dryRun){console.log(m.dim(" Dry run \u2014 no changes made"));return}let t=o.rebuildIndex(),i=t.articles.length>0?`
|
|
11
|
+
Existing articles:
|
|
12
|
+
${t.articles.map(g=>`- ${g.title} (${g.path})`).join(`
|
|
13
|
+
`)}
|
|
14
|
+
`:"",r=n.map(g=>`--- ENTRY ${g.id} [${g.date} ${g.agentId} via ${g.source}] ---
|
|
15
|
+
${g.content}
|
|
16
|
+
--- END ENTRY ---`).join(`
|
|
17
|
+
|
|
18
|
+
`),l=`You are a wiki editor for an agent system. Your job is to compile raw conversation entries into structured wiki articles.
|
|
19
|
+
|
|
20
|
+
Read these ${n.length} raw entries and produce wiki articles in JSON format.
|
|
21
|
+
${i}
|
|
22
|
+
Rules:
|
|
23
|
+
1. Group related entries into articles by topic (e.g., "Weekly Marketing Brief", "MTGL Deploy Process", "GitLab Token Issues")
|
|
24
|
+
2. Each article should synthesize information, not just copy-paste
|
|
25
|
+
3. Use wikilinks [[Article Title]] to cross-reference related articles
|
|
26
|
+
4. If an existing article covers the topic, produce an UPDATE with merged content
|
|
27
|
+
5. Output ONLY valid JSON \u2014 no markdown fencing, no explanation
|
|
28
|
+
|
|
29
|
+
Output format (JSON array):
|
|
30
|
+
[
|
|
31
|
+
{
|
|
32
|
+
"path": "projects/mtgl-deploy.md",
|
|
33
|
+
"title": "MTGL Deploy Process",
|
|
34
|
+
"type": "process",
|
|
35
|
+
"content": "How we deploy MTGL to staging...
|
|
36
|
+
|
|
37
|
+
See also [[GitLab Token Issues]]",
|
|
38
|
+
"sources": ["entry-id-1", "entry-id-2"]
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
Valid types: concept, process, project, decision, person, pattern, incident, report
|
|
43
|
+
|
|
44
|
+
ENTRIES:
|
|
45
|
+
|
|
46
|
+
${r}`,c=Q(o.baseDir,"_tmp");he(c,{recursive:!0});let y=Q(c,"absorb-prompt.txt");ue(y,l),console.log(m.dim(" Running Claude to compile articles..."));try{let g;try{g=pe(`cat '${y}' | claude -p - --output-format json --max-turns 5`,{encoding:"utf-8",timeout:18e4,maxBuffer:10*1024*1024})}catch(f){if(g=f.stdout||"",!g)throw f}let h;try{let f=JSON.parse(g);h=f.result||f.content||g}catch{h=g}let S=h.match(/\[[\s\S]*\]/);if(!S){console.log(m.red(" Claude didn't return valid JSON. Raw output:")),console.log(h.slice(0,500));return}let p=JSON.parse(S[0]);console.log(m.green(` Claude produced ${p.length} articles:`)),console.log();for(let f of p){let w=new Date().toISOString().slice(0,10),B=f.sources.map(ae=>n.find(ie=>ie.id===ae)?.agentId).filter(Boolean)[0]||n[0]?.agentId||"system";o.writeArticle(f.path,{title:f.title,type:f.type,owner:B,access:"internal",created:w,lastUpdated:w,related:[],sources:f.sources},f.content,B),console.log(` ${m.green("+")} ${f.path}: ${f.title}`),console.log(m.dim(` sources: ${f.sources.join(", ")}`))}o.rebuildIndex(),console.log(),console.log(m.green(" Index rebuilt. Done."))}catch(g){console.log(m.red(` Absorb failed: ${g.message?.slice(0,200)}`)),g.stderr&&console.log(m.dim(String(g.stderr).slice(0,500))),g.stdout&&console.log(m.dim("stdout: "+String(g.stdout).slice(0,500)))}});D.command("entries").description("list raw entries").option("--dir <path>","wiki directory").option("--agent <id>","filter by agent").action(e=>{let n=M(e.dir).listEntries();e.agent&&(n=n.filter(s=>s.agentId===e.agent)),console.log(),console.log(m.bold(` ${n.length} raw entries`)),console.log();for(let s of n.slice(-20))console.log(` ${m.dim(s.date)} ${m.cyan(s.agentId)} via ${s.source}: ${s.content.slice(0,80)}...`);n.length>20&&console.log(m.dim(` ... showing last 20 of ${n.length}`)),console.log()});D.command("search <query>").description("search wiki articles").option("--dir <path>","wiki directory").action((e,o)=>{let s=M(o.dir).findRelevant(e,void 0,10);if(console.log(),s.length===0)console.log(m.dim(" No matching articles"));else{console.log(m.bold(` ${s.length} results for "${e}":`)),console.log();for(let t of s)console.log(` ${m.cyan(t.meta.title)} (${t.meta.type})`),console.log(m.dim(` ${t.path} \u2014 ${t.content.slice(0,100)}...`))}console.log()});process.on("SIGINT",()=>process.exit(0));process.on("SIGTERM",()=>process.exit(0));async function ye(){let e=await ee(),o=new fe().name("agentx").description("self-hosted multi-agent orchestrator \u2014 Telegram, WhatsApp, crons, A2A mesh, wiki knowledge").version(e.version||"1.0.0","-v, --version","display the version number");if(o.addCommand(A).addCommand(te).addCommand(E).addCommand(F).addCommand(P).addCommand(L).addCommand(z).addCommand(q).addCommand(X).addCommand(H).addCommand(K).addCommand(D),process.argv.slice(2).length===0){o.outputHelp();return}o.parse()}ye();
|
|
11
47
|
//# sourceMappingURL=cli.js.map
|