agentix-cli 0.9.0 → 0.13.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.
Files changed (51) hide show
  1. package/README.md +51 -1
  2. package/dist/agent-5KIMJI3F.js +2 -0
  3. package/dist/{chunk-SBUX74OU.js → chunk-D6EDMQRV.js} +5 -5
  4. package/dist/chunk-HI24KAEY.js +54 -0
  5. package/dist/chunk-HI24KAEY.js.map +1 -0
  6. package/dist/chunk-MCJUAE6V.js +6 -0
  7. package/dist/chunk-MCJUAE6V.js.map +1 -0
  8. package/dist/chunk-QOYAX2FT.js +2 -0
  9. package/dist/chunk-QOYAX2FT.js.map +1 -0
  10. package/dist/chunk-QZP65I2H.js +45 -0
  11. package/dist/chunk-QZP65I2H.js.map +1 -0
  12. package/dist/chunk-T25UEXVK.js +126 -0
  13. package/dist/chunk-T25UEXVK.js.map +1 -0
  14. package/dist/chunk-VH23CDHL.js +157 -0
  15. package/dist/chunk-VH23CDHL.js.map +1 -0
  16. package/dist/chunk-WJ6ZF6EZ.js +6 -0
  17. package/dist/chunk-WJ6ZF6EZ.js.map +1 -0
  18. package/dist/cli.js +26 -29
  19. package/dist/cli.js.map +1 -1
  20. package/dist/compaction-7I3E5QUO.js +28 -0
  21. package/dist/compaction-7I3E5QUO.js.map +1 -0
  22. package/dist/config-HAD5E4YX.js +2 -0
  23. package/dist/index.d.ts +1084 -76
  24. package/dist/index.js +39 -35
  25. package/dist/index.js.map +1 -1
  26. package/dist/loader-YQE6XJBT.js +2 -0
  27. package/dist/registry-CDWY7CLL.js +2 -0
  28. package/dist/workspace-setup-QQTCJY4B.js +2 -0
  29. package/dist/workspace-setup-QQTCJY4B.js.map +1 -0
  30. package/package.json +1 -1
  31. package/dist/agent-QYQVGKLM.js +0 -2
  32. package/dist/chunk-BAIQJHQF.js +0 -64
  33. package/dist/chunk-BAIQJHQF.js.map +0 -1
  34. package/dist/chunk-BHDLKX3G.js +0 -6
  35. package/dist/chunk-BHDLKX3G.js.map +0 -1
  36. package/dist/chunk-FRFR27IN.js +0 -3
  37. package/dist/chunk-FRFR27IN.js.map +0 -1
  38. package/dist/chunk-IVVVYXPH.js +0 -136
  39. package/dist/chunk-IVVVYXPH.js.map +0 -1
  40. package/dist/chunk-X32247GM.js +0 -2
  41. package/dist/chunk-X32247GM.js.map +0 -1
  42. package/dist/compaction-E3MQRLTL.js +0 -28
  43. package/dist/compaction-E3MQRLTL.js.map +0 -1
  44. package/dist/config-MSWKC466.js +0 -2
  45. package/dist/loader-6VSM6FSY.js +0 -2
  46. package/dist/registry-FP6FUOXF.js +0 -2
  47. /package/dist/{agent-QYQVGKLM.js.map → agent-5KIMJI3F.js.map} +0 -0
  48. /package/dist/{chunk-SBUX74OU.js.map → chunk-D6EDMQRV.js.map} +0 -0
  49. /package/dist/{config-MSWKC466.js.map → config-HAD5E4YX.js.map} +0 -0
  50. /package/dist/{loader-6VSM6FSY.js.map → loader-YQE6XJBT.js.map} +0 -0
  51. /package/dist/{registry-FP6FUOXF.js.map → registry-CDWY7CLL.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,16 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import{g as ge,h as $e}from"./chunk-BAIQJHQF.js";import{f as K}from"./chunk-IVVVYXPH.js";import"./chunk-CJ45Y2IR.js";import"./chunk-X32247GM.js";import{b as M,c as ne}from"./chunk-BHDLKX3G.js";import"./chunk-34UAFPK4.js";import"./chunk-M7HKBG3V.js";import"./chunk-DSYKYZMT.js";import"./chunk-ZRYBSI5G.js";import{a as ye}from"./chunk-4YCH6IZV.js";import{Command as Fe}from"commander";import{Command as Ee}from"commander";import m from"chalk";var L=new Ee().name("daemon").description("manage the agentx daemon \u2014 start, stop, status, logs");L.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:d}=await import("child_process"),h=["dist/cli.js","daemon","start"];e.config&&h.push("-c",e.config),e.port&&h.push("--port",e.port);let p=d("node",h,{detached:!0,stdio:["ignore","ignore","ignore"],cwd:process.cwd()});p.unref();let{writeFileSync:g}=await import("fs");g("/tmp/agentx-daemon.pid",String(p.pid)),console.log(m.green(`Daemon started in background (PID: ${p.pid})`)),console.log(m.dim(" Logs: tail -f /tmp/agentx-daemon.log")),console.log(m.dim(" Stop: agentx daemon stop"));return}let{readFileSync:t,writeFileSync:s,mkdirSync:o,existsSync:n}=await import("fs"),{resolve:i}=await import("path"),r=i(process.cwd(),".agentx");n(r)||o(r,{recursive:!0});let c=i(r,"daemon.pid");if(n(c)){let d=parseInt(t(c,"utf-8").trim(),10);if(d&&d!==process.pid)try{process.kill(d,0),console.error(`Another agentx daemon is already running (PID ${d}). Exiting.`),process.exit(0)}catch{}}s(c,String(process.pid)),await new ge(e.config).start()});L.command("stop").description("stop the running daemon").action(async()=>{let{readFileSync:e,existsSync:t,unlinkSync:s}=await import("fs"),{resolve:o}=await import("path"),n=[o(process.cwd(),".agentx/daemon.pid"),"/tmp/agentx-daemon.pid"];for(let r of n)if(t(r))try{let c=parseInt(e(r,"utf-8").trim(),10);process.kill(c,"SIGTERM"),await new Promise(a=>setTimeout(a,2e3));try{s(r)}catch{}console.log(m.green(`Daemon stopped (PID: ${c})`));return}catch{try{s(r)}catch{}}let{execSync:i}=await import("child_process");try{i("pkill -f 'node dist/cli.js daemon'",{stdio:"ignore"}),console.log(m.green("Daemon stopped"))}catch{console.log(m.yellow("No daemon process found"))}});L.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 t=M(e.config),[s,o]=t.node.bind.split(":"),n=`http://${s||"127.0.0.1"}:${o}/health`,r=await(await fetch(n,{signal:AbortSignal.timeout(3e3)})).json();if(e.json){console.log(JSON.stringify(r,null,2));return}console.log(),console.log(m.bold(` ${r.node.name}`)+m.dim(` (${r.node.id})`)),console.log(m.green(" Status: running")+m.dim(` \u2014 uptime ${je(r.uptime)}`)),console.log(),console.log(m.bold(" Agents:"));for(let a of r.agents){let d=a.active>0?m.yellow(` [${a.active} active]`):"",h=a.errors>0?m.red(` (${a.errors} errors)`):"";console.log(` ${m.cyan(a.name)} ${m.dim(`(${a.tier})`)}${d}${h}`),console.log(m.dim(` ${a.workspace}`))}let c=r.crons.filter(a=>a.enabled);if(r.crons.length){console.log(),console.log(m.bold(` Crons: ${c.length} enabled / ${r.crons.length} total`));for(let a of r.crons){let d=a.enabled?m.green("\u25CF"):m.dim("\u25CB"),h=a.nextRun?m.dim(` next: ${new Date(a.nextRun).toLocaleTimeString()}`):"";console.log(` ${d} ${a.id}${h}`)}}if(r.mesh?.length){console.log(),console.log(m.bold(" Mesh:"));for(let a of r.mesh){let d=a.healthy?m.green("\u25CF"):m.red("\u25CF"),h=a.skills?.length?m.dim(` (${a.skills.length} agents)`):"";console.log(` ${d} ${a.peer} ${m.dim(a.peerUrl)}${h}`)}}console.log()}catch(t){if(t.cause?.code==="ECONNREFUSED"||t.name==="TimeoutError")console.log(m.red(" Daemon is not running")),console.log(m.dim(" Start with: agentx daemon start"));else try{let s=M(e.config),o=ne(s);if(console.log(),console.log(m.bold(` ${s.node.name}`)+m.dim(` (${s.node.id})`)),console.log(m.yellow(" Status: stopped")),console.log(m.dim(` ${Object.keys(s.agents).length} agents, ${Object.keys(s.crons).length} crons configured`)),o.length){console.log();for(let n of o)console.log(m.yellow(` \u26A0 ${n}`))}console.log()}catch(s){console.log(m.red(` ${s.message}`))}}});L.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:t,spawn:s}=await import("child_process"),o="/tmp/agentx-daemon.log",{existsSync:n}=await import("fs");if(!n(o)){console.log(m.yellow("No log file found at /tmp/agentx-daemon.log"));return}if(e.follow){let i=s("tail",["-f","-n",e.lines,o],{stdio:"inherit"});process.on("SIGINT",()=>{i.kill(),process.exit(0)})}else t(`tail -n ${e.lines} ${o}`,{stdio:"inherit"})});L.command("watch").description("stream live agent activity (SSE)").option("-c, --config <path>","path to agentx.json").action(async e=>{let o=`http://${(M(e.config).node.bind||"127.0.0.1:18800").replace("0.0.0.0","127.0.0.1")}/events`;console.log(m.dim(` Connecting to ${o}...`)),console.log(m.dim(` Press Ctrl+C to stop
3
- `));try{let n=await fetch(o);if(!n.ok||!n.body){console.log(m.red(` Failed: ${n.status}`));return}let i=n.body.getReader(),r=new TextDecoder,c="";for(;;){let{done:a,value:d}=await i.read();if(a)break;c+=r.decode(d,{stream:!0});let h=c.split(`
4
- `);c=h.pop()||"";for(let p of h)if(p.startsWith("data: "))try{let g=JSON.parse(p.slice(6)),y=new Date(g.time).toLocaleTimeString(),f=g.message?.replace(/^\[agentx\]\s*/,"")||"";f.includes("executing")?console.log(`${m.dim(y)} ${m.cyan("\u25B6")} ${f}`):f.includes("completed")?console.log(`${m.dim(y)} ${m.green("\u2713")} ${f}`):f.includes("error")||f.includes("Error")?console.log(`${m.dim(y)} ${m.red("\u2717")} ${f}`):f.includes("Routing")?console.log(`${m.dim(y)} ${m.yellow("\u2192")} ${f}`):f.includes("mention")?console.log(`${m.dim(y)} ${m.magenta("@")} ${f}`):f.includes("Skipping")?console.log(`${m.dim(y)} ${m.dim("\u2298")} ${m.dim(f)}`):console.log(`${m.dim(y)} ${f}`)}catch{}else p.startsWith("event: status")}}catch(n){console.log(m.red(` Connection failed: ${n.message}`)),console.log(m.dim(" Is the daemon running? Try: agentx daemon status"))}});L.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,t,s)=>{let o=t.join(" ");try{let n=M(s.config),[i,r]=n.node.bind.split(":"),c=`http://${i||"127.0.0.1"}:${r}`,a,d;s.peer?(a=`${c}/mesh/task`,d={peer:s.peer,message:o,agent:e}):(a=`${c}/task`,d={agent:e,message:o}),console.log(m.dim(` Sending to ${s.peer?`${s.peer}/${e}`:e}...`));let p=await(await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d)})).json();s.json?console.log(JSON.stringify(p,null,2)):p.error?console.log(m.red(` Error: ${p.error}`)):(console.log(),console.log(p.content||p.response||"No response"),p.duration&&console.log(m.dim(`
5
- (${(p.duration/1e3).toFixed(1)}s)`)))}catch(n){console.log(m.red(` Failed: ${n.message}`)),console.log(m.dim(" Is the daemon running? Try: agentx daemon status"))}});L.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","root").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,t)=>{let{execSync:s}=await import("child_process"),o=t.identity?`-e "ssh -i ${t.identity}"`:"",n=`${t.user}@${e}:${t.path}/dist/`;if(!t.skipChecks){try{console.log(m.dim(" Running tests...")),s("npx vitest run --reporter=dot",{stdio:"pipe",cwd:process.cwd()}),console.log(m.green(" \u2713 Tests passed"))}catch{console.log(m.red(" \u2717 Tests failed \u2014 aborting deploy")),console.log(m.dim(" Use --skip-checks to deploy anyway"));return}let{existsSync:i,statSync:r}=await import("fs"),{resolve:c}=await import("path"),a=c(process.cwd(),"dist/cli.js");if(!i(a)){console.log(m.red(" \u2717 dist/ not found \u2014 run build first"));return}Date.now()-r(a).mtimeMs>3e5&&(console.log(m.yellow(" \u26A0 dist/ is older than 5 minutes \u2014 rebuilding...")),s("npx tsup --no-dts",{stdio:"pipe",cwd:process.cwd()}),console.log(m.green(" \u2713 Build complete")))}console.log(m.dim(` Deploying to ${t.user}@${e}:${t.path}...`));try{if(s(`rsync -avzL --delete ${o} dist/ ${n}`,{stdio:"inherit",cwd:process.cwd()}),console.log(m.green(" \u2713 Deploy complete")),t.restart){console.log(m.dim(" Restarting remote daemon..."));let i=t.identity?`ssh -i ${t.identity}`:"ssh";s(`${i} ${t.user}@${e} "pkill -f 'node dist/cli.js daemon' 2>/dev/null; sleep 2; cd ${t.path} && nohup node dist/cli.js daemon start > /tmp/agentx-daemon.log 2>&1 &"`,{stdio:"inherit"}),console.log(m.green(" Remote daemon restarted"))}}catch(i){console.log(m.red(` Deploy failed: ${i.message}`))}});L.action(async e=>{await new ge(e.config).start()});function je(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 Re}from"commander";import D from"chalk";import{existsSync as q,readFileSync as Te,writeFileSync as we,mkdirSync as ke}from"fs";import{resolve as J}from"path";var ve=new Re().name("init").description("initialize agentx configuration in the current directory").option("--force","overwrite existing config").action(async e=>{let t=process.cwd(),s=J(t,"agentx.json"),o=J(t,".env"),n=J(t,".agentx/sessions");if(console.log(),console.log(D.bold(" agentx init")),console.log(),q(s)&&!e.force){console.log(D.yellow(" agentx.json already exists. Use --force to overwrite.")),console.log();return}let i=[J(t,"agentx.example.json"),J(t,"node_modules/agentix-cli/agentx.example.json")],r;for(let a of i)if(q(a)){r=Te(a,"utf-8");break}r||(r=JSON.stringify({node:{id:ye("os").hostname().toLowerCase().replace(/[^a-z0-9-]/g,"-"),name:ye("os").hostname(),bind:"127.0.0.1:18800"},providers:{claude:{apiKey:"${ANTHROPIC_API_KEY}"}},agents:{assistant:{name:"Assistant",workspace:t,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)),we(s,r),console.log(D.green(" \u2713 Created agentx.json")),q(o)||(we(o,["# AgentX environment variables","# ANTHROPIC_API_KEY=sk-ant-...","# TG_BOT_TOKEN=123456:ABC...","# MESH_TOKEN=your-mesh-secret",""].join(`
6
- `)),console.log(D.green(" \u2713 Created .env template"))),q(n)||(ke(n,{recursive:!0}),console.log(D.green(" \u2713 Created .agentx/sessions/")));let c=J(t,".claude");q(c)||(ke(c,{recursive:!0}),console.log(D.green(" \u2713 Created .claude/"))),console.log(),console.log(D.bold(" Next steps:")),console.log(` 1. Edit ${D.cyan("agentx.json")} \u2014 configure agents, channels, crons`),console.log(` 2. Edit ${D.cyan(".env")} \u2014 add API keys and bot tokens`),console.log(` 3. Run ${D.cyan("agentx daemon start")} \u2014 start the daemon`),console.log(` 4. Run ${D.cyan("agentx daemon status")} \u2014 verify everything`),console.log()});import{Command as B}from"commander";import l from"chalk";import O from"prompts";import{readFileSync as se,writeFileSync as ae,existsSync as R,mkdirSync as _,cpSync as pe,readdirSync as xe}from"fs";import{resolve as I,join as oe}from"path";function j(){let e=I(process.cwd(),"agentx.json");if(!R(e))throw new Error("No agentx.json found. Run: agentx init");return JSON.parse(se(e,"utf-8"))}function N(e){ae(I(process.cwd(),"agentx.json"),JSON.stringify(e,null,2))}var X=new B().name("agent").description("manage agents \u2014 add, list, remove");X.command("list").alias("ls").description("list configured agents").action(()=>{let e=j();console.log();for(let[t,s]of Object.entries(e.agents||{})){let o=s.mentions?.join(", ")||"";console.log(` ${l.cyan(t)} \u2014 ${s.name} (${s.tier})`),console.log(l.dim(` workspace: ${s.workspace}`)),o&&console.log(l.dim(` mentions: ${o}`))}console.log()});X.command("add").description("add a new agent interactively").action(async()=>{let e=j(),t=await O([{type:"text",name:"id",message:"Agent ID (kebab-case)",validate:c=>/^[a-z0-9-]+$/.test(c)||"Use lowercase, numbers, hyphens"},{type:"text",name:"name",message:"Display name"},{type:"text",name:"workspace",message:"Workspace path",initial:I(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(!t.id)return;let s=t.workspace.endsWith(t.id)?t.workspace:oe(t.workspace,t.id);_(I(s,".claude/skills"),{recursive:!0});let o=`# ${t.name}
7
-
8
- ${t.systemPrompt}
9
- `;ae(I(s,"CLAUDE.md"),o),ae(I(s,".claude/settings.json"),JSON.stringify({permissions:{allow:["Bash(git *)","Bash(curl *)","Bash(node *)","Read","Write","Edit","Glob","Grep","WebFetch","WebSearch"]}},null,2));let n=I(process.cwd(),"src/wiki/SKILL.md"),i=I(__dirname,"../wiki/SKILL.md"),r=R(n)?n:i;R(r)&&(_(I(s,".claude/skills/wiki"),{recursive:!0}),pe(r,I(s,".claude/skills/wiki/SKILL.md"))),e.agents=e.agents||{},e.agents[t.id]={name:t.name,workspace:s,tier:t.tier,model:t.model,mentions:t.mentions?.map(c=>c.trim()).filter(Boolean)||[],maxConcurrent:t.maxConcurrent,systemPrompt:t.systemPrompt,permissionMode:"default"},N(e),console.log(l.green(`
10
- Agent "${t.id}" added`)),console.log(l.dim(` Workspace: ${s}`)),console.log(l.dim(" Wiki skill installed")),console.log(l.dim(" CLAUDE.md + settings.json created")),console.log()});X.command("remove <id>").alias("rm").description("remove an agent from config (keeps workspace)").action(e=>{let t=j();if(!t.agents?.[e]){console.log(l.red(` Agent "${e}" not found`));return}delete t.agents[e],N(t),console.log(l.green(` Agent "${e}" removed from config (workspace preserved)`))});var ie=new B().name("channel").description("manage channels \u2014 add telegram/whatsapp/discord, list");ie.command("list").alias("ls").description("list configured channels and bindings").action(()=>{let e=j();console.log();let t=e.channels?.telegram;if(t?.enabled){console.log(l.bold(" Telegram:"));for(let[i,r]of Object.entries(t.accounts||{}))console.log(` ${l.cyan(i)} -> agent: ${r.agentBinding}`)}else console.log(l.dim(" Telegram: disabled"));let s=e.channels?.whatsapp;if(s?.enabled){console.log(l.bold(" WhatsApp:")),console.log(` default agent: ${s.defaultAgent||"(none)"}`),console.log(` session: ${s.sessionDir||".agentx/whatsapp-sessions"}`);for(let i of s.routes||[]){let r=i.contact?`contact ${i.contact}`:`group "${i.group}"`;console.log(` ${r} -> agent: ${i.agent}`)}}else console.log(l.dim(" WhatsApp: disabled"));let o=e.channels?.discord;o?.enabled?(console.log(l.bold(" Discord:")),console.log(` agent: ${o.agentBinding||"(none)"}`)):console.log(l.dim(" Discord: disabled"));let n=e.channels?.gitlab;if(n?.enabled){console.log(l.bold(" GitLab:")),console.log(` host: ${n.host}`),console.log(` webhook: :${n.webhookPort}`);for(let i of n.routes||[])console.log(` ${i.project} -> agent: ${i.agent}`)}else console.log(l.dim(" GitLab: disabled"));console.log()});ie.command("add").description("add a channel (telegram, whatsapp, or discord)").action(async()=>{let e=j(),t=Object.keys(e.agents||{});if(!t.length){console.log(l.red(" No agents configured. Run: agentx agent add"));return}let s=t.map(n=>({title:`${n} (${e.agents[n].name})`,value:n})),{channelType:o}=await O({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(o){if(e.channels=e.channels||{},o==="telegram"){let n=await O([{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:s}]);if(!n.accountName||!n.token)return;try{let r=await(await fetch(`https://api.telegram.org/bot${n.token}/getMe`)).json();if(!r.ok)throw new Error(r.description);console.log(l.green(` Bot verified: @${r.result.username}`));let c=`@${r.result.username}`,a=e.agents[n.agentBinding].mentions||[];a.includes(c)||(a.push(c,r.result.username),e.agents[n.agentBinding].mentions=a)}catch(i){console.log(l.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[n.accountName]={token:n.token,agentBinding:n.agentBinding},N(e),console.log(l.green(` Telegram "${n.accountName}" added -> ${n.agentBinding}`))}if(o==="whatsapp"){let n=await O([{type:"select",name:"defaultAgent",message:"Default agent for WhatsApp messages",choices:s},{type:"text",name:"sessionDir",message:"Session directory",initial:".agentx/whatsapp-sessions"},{type:"confirm",name:"addRoute",message:"Add a contact/group route?",initial:!0}]);if(!n.defaultAgent)return;let i=[],r=n.addRoute;for(;r;){let c=await O([{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:a=>a==="contact"?"Phone number (e.g. +21624309128)":"Group name (partial match)"},{type:"select",name:"agent",message:"Route to agent",choices:s},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);c.value&&c.agent&&i.push({[c.type]:c.value,agent:c.agent}),r=c.more}e.channels.whatsapp={enabled:!0,sessionDir:n.sessionDir,defaultAgent:n.defaultAgent,routes:i},N(e),_(I(process.cwd(),n.sessionDir),{recursive:!0}),console.log(l.green(` WhatsApp enabled (${i.length} routes, default: ${n.defaultAgent})`)),console.log(l.dim(" Start daemon to scan QR code: agentx daemon start"))}if(o==="discord"){let n=await O([{type:"text",name:"token",message:"Discord bot token (from developer portal)"},{type:"select",name:"agentBinding",message:"Bind to agent",choices:s}]);if(!n.token)return;e.channels.discord={enabled:!0,token:n.token,agentBinding:n.agentBinding},N(e),console.log(l.green(` Discord added -> ${n.agentBinding}`))}if(o==="gitlab"){let n=await O([{type:"text",name:"host",message:"GitLab instance URL",initial:"https://gitlab.example.com"},{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(!n.token)return;let i=[],r=n.addRoute;for(;r;){let c=await O([{type:"text",name:"project",message:"GitLab project path (e.g. group/project, or * for default)"},{type:"select",name:"agent",message:"Route to agent",choices:s},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);c.project&&c.agent&&i.push({project:c.project,agent:c.agent}),r=c.more}e.channels.gitlab={enabled:!0,host:n.host,token:n.token,webhookPort:n.webhookPort,webhookSecret:n.webhookSecret||void 0,routes:i},N(e),console.log(l.green(` GitLab enabled (${i.length} routes, webhook :${n.webhookPort})`)),console.log(l.dim(` Add webhook in GitLab: ${n.host}/<project>/-/hooks`)),console.log(l.dim(` URL: http://your-server:${n.webhookPort}/`)),n.webhookSecret&&console.log(l.dim(` Secret token: ${n.webhookSecret}`)),console.log(l.dim(" Events: Comments, Issues, Merge requests, Pipeline"))}console.log(l.dim(" Restart daemon to apply: agentx daemon stop && agentx daemon start")),console.log()}});var Y=new B().name("cron").description("manage cron jobs \u2014 add, list, enable, disable");Y.command("list").alias("ls").description("list cron jobs").action(()=>{let e=j();console.log();for(let[t,s]of Object.entries(e.crons||{})){let o=s.enabled?l.green("\u25CF"):l.dim("\u25CB");console.log(` ${o} ${l.cyan(t)} \u2014 ${s.schedule} (${s.timezone})`),console.log(l.dim(` agent: ${s.agent} | timeout: ${s.timeout}s`))}Object.keys(e.crons||{}).length||console.log(l.dim(" No cron jobs configured")),console.log()});Y.command("add").description("add a cron job").action(async()=>{let e=j(),t=Object.keys(e.agents||{}),s=await O([{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:t.map(o=>({title:o,value:o}))},{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}]);!s.id||!s.schedule||(e.crons=e.crons||{},e.crons[s.id]={enabled:s.enabled,schedule:s.schedule,timezone:s.timezone,agent:s.agent,prompt:s.prompt,timeout:s.timeout,onError:"log"},N(e),console.log(l.green(` Cron "${s.id}" added (${s.enabled?"enabled":"disabled"})`)),console.log())});Y.command("enable <id>").description("enable a cron job").action(e=>{let t=j();if(!t.crons?.[e]){console.log(l.red(` Cron "${e}" not found`));return}t.crons[e].enabled=!0,N(t),console.log(l.green(` Cron "${e}" enabled`))});Y.command("disable <id>").description("disable a cron job").action(e=>{let t=j();if(!t.crons?.[e]){console.log(l.red(` Cron "${e}" not found`));return}t.crons[e].enabled=!1,N(t),console.log(l.green(` Cron "${e}" disabled`))});var V=new B().name("mesh").description("manage mesh peers \u2014 add, list, remove");V.command("list").alias("ls").description("list mesh peers").action(async()=>{let e=j();if(console.log(),!e.mesh?.enabled)console.log(l.dim(" Mesh: disabled"));else{console.log(l.bold(" Mesh peers:"));for(let t of e.mesh.peers||[]){let s=l.red("unreachable");try{let n=await(await fetch(`${t.url}/health`,{signal:AbortSignal.timeout(3e3)})).json();s=l.green(`healthy \u2014 ${n.agents?.length||0} agents`)}catch{}console.log(` ${l.cyan(t.name)} ${l.dim(t.url)} ${s}`)}}console.log()});V.command("add").description("add a mesh peer (another agentx server)").action(async()=>{let e=j(),t=await O([{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(!(!t.name||!t.url)){try{let o=await(await fetch(`${t.url}/.well-known/agent-card.json`,{signal:AbortSignal.timeout(5e3)})).json();console.log(l.green(` Connected: ${o.name} (${o.skills?.length||0} agents)`))}catch{console.log(l.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:t.url,name:t.name,...t.token?{token:t.token}:{}}),N(e),console.log(l.green(` Peer "${t.name}" added`)),console.log()}});V.command("remove <name>").alias("rm").description("remove a mesh peer").action(e=>{let t=j();t.mesh?.peers&&(t.mesh.peers=t.mesh.peers.filter(s=>s.name!==e),t.mesh.peers.length===0&&(t.mesh.enabled=!1),N(t),console.log(l.green(` Peer "${e}" removed`)))});var re=new B().name("skill").description("manage skills \u2014 add to agent(s), list");re.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,t)=>{let s=j(),o=I(e);if(!R(o)){console.log(l.red(` Skill not found: ${o}`));return}let n=R(oe(o,"SKILL.md"))?o.split("/").pop():o.replace(/\/SKILL\.md$/,"").split("/").pop(),i;if(t.all)i=Object.keys(s.agents||{});else if(t.agent)i=t.agent;else{let r=Object.keys(s.agents||{});i=(await O({type:"multiselect",name:"agents",message:"Install to which agents?",choices:r.map(a=>({title:`${a} (${s.agents[a].name})`,value:a}))})).agents||[]}for(let r of i){let c=s.agents[r];if(!c){console.log(l.yellow(` Agent "${r}" not found, skipping`));continue}let a=I(c.workspace,".claude/skills",n);_(a,{recursive:!0}),R(oe(o,"SKILL.md"))?pe(o,a,{recursive:!0}):o.endsWith(".md")&&pe(o,oe(a,"SKILL.md")),console.log(l.green(` ${n} -> ${r} (${c.workspace})`))}console.log()});re.command("list").alias("ls").description("list skills per agent").action(()=>{let e=j();console.log();for(let[t,s]of Object.entries(e.agents||{})){let o=I(s.workspace,".claude/skills"),n=R(o)?xe(o).filter(i=>!i.startsWith(".")):[];console.log(` ${l.cyan(t)}: ${n.length?n.join(", "):l.dim("(none)")}`)}console.log()});var me=new B().name("hook").description("manage hooks \u2014 add to agent workspace");me.command("add <agent>").description("add a hook to an agent's workspace settings").action(async e=>{let s=j().agents?.[e];if(!s){console.log(l.red(` Agent "${e}" not found`));return}let o=I(s.workspace,".claude/settings.json"),n=R(o)?JSON.parse(se(o,"utf-8")):{},i=await O([{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:a=>a==="command"?"Shell command":"URL"},{type:"text",name:"matcher",message:"Matcher regex (optional, e.g. 'Bash' to match tool)"}]);if(!i.event||!i.value)return;n.hooks=n.hooks||{},n.hooks[i.event]=n.hooks[i.event]||[];let c={hooks:[{type:i.type,...i.type==="command"?{command:i.value}:{url:i.value}}]};i.matcher&&(c.matcher=i.matcher),n.hooks[i.event].push(c),_(I(s.workspace,".claude"),{recursive:!0}),ae(o,JSON.stringify(n,null,2)),console.log(l.green(` Hook added to ${e}: ${i.event} -> ${i.type}`)),console.log()});var he=new B().name("migrate").description("import configuration from OpenClaw or other tools");he.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,t)=>{let s=[e,I(process.env.HOME||"",".openclaw/openclaw.json"),I(process.env.HOME||"",".openclaw/clawdbot.json")].filter(Boolean),o;for(let y of s)if(y&&R(y)){o=y;break}if(!o){console.log(l.red(" OpenClaw config not found. Pass the path: agentx migrate openclaw /path/to/config.json"));return}console.log(l.dim(` Reading: ${o}`));let n=JSON.parse(se(o,"utf-8")),i=R(I(process.cwd(),"agentx.json"))?j():{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=n.agents?.list||[],c=n.bindings||[],a={};for(let y of c)y.match?.channel==="telegram"&&(a[y.match.accountId]=y.agentId);r.find(y=>y.id==="main"||y.default)&&(a.default="main");let d=0;for(let y of r){if(y.workspace?.startsWith("/data"))continue;let f=y.id.replace(/-agent$/,"").replace(/^main$/,"main-agent"),b=y.groupChat?.mentionPatterns||[];b.some(w=>w===f)||b.push(f),i.agents[f]={name:y.name,workspace:y.workspace,tier:"claude-code",model:"claude-sonnet-4-6",mentions:b,maxConcurrent:y.id==="main"?2:1,systemPrompt:`You are ${y.name} on AgentX.`,permissionMode:"bypassPermissions"},d++,t.dryRun||_(I(y.workspace,".claude/skills"),{recursive:!0})}console.log(` ${l.green(d)} agents imported`);let h=n.channels?.telegram?.accounts||{},p=0;for(let[y,f]of Object.entries(h)){if(!f.botToken)continue;let w=a[y]?.replace(/-agent$/,"").replace(/^main$/,"main-agent")||"default";i.channels.telegram.accounts[y]={token:f.botToken,agentBinding:w},i.channels.telegram.enabled=!0,p++}console.log(` ${l.green(p)} Telegram accounts imported`);let g=I(o,"../cron/jobs.json");if(R(g)){let y=JSON.parse(se(g,"utf-8")),f=0;for(let b of y.jobs||[]){let w=b.name||b.id,k=b.agentId?.replace(/-agent$/,"").replace(/^main$/,"main-agent")||"default";i.crons[w]={enabled:b.enabled||!1,schedule:b.schedule?.expr||"0 9 * * *",timezone:b.schedule?.tz||"UTC",agent:k,prompt:b.payload?.message||"",timeout:b.payload?.timeoutSeconds||600,onError:"log"},f++}console.log(` ${l.green(f)} cron jobs imported`)}t.dryRun?(console.log(l.yellow(`
11
- Dry run \u2014 nothing written`)),console.log(JSON.stringify(i,null,2))):(N(i),console.log(l.green(`
12
- Migration complete! Review agentx.json and run: agentx daemon start`))),console.log()});var ce=new B().name("config").description("validate and inspect configuration");ce.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 t=M(e.config);console.log(l.green(" \u2713 Config valid")),console.log(l.dim(` Node: ${t.node.name} (${t.node.id})`)),console.log(l.dim(` Bind: ${t.node.bind}`)),console.log(l.dim(` Agents: ${Object.keys(t.agents).length}`)),console.log(l.dim(` Crons: ${Object.keys(t.crons).length}`)),console.log(l.dim(` Mesh peers: ${t.mesh.peers.length}`));let s=Object.keys(t.channels.telegram.accounts).length;console.log(l.dim(` Telegram: ${t.channels.telegram.enabled?`${s} accounts`:"disabled"}`)),console.log(l.dim(` WhatsApp: ${t.channels.whatsapp.enabled?`${t.channels.whatsapp.routes.length} routes`:"disabled"}`)),console.log(l.dim(` Discord: ${t.channels.discord?.enabled?"enabled":"disabled"}`));let o=ne(t);if(o.length){console.log(),console.log(l.yellow(" Warnings:"));for(let n of o)console.log(l.yellow(` \u26A0 ${n}`))}else console.log(l.green(" \u2713 All workspaces valid"));console.log();for(let[n,i]of Object.entries(t.agents)){let r=i.workspace,c=R(I(r,".claude")),a=R(I(r,"CLAUDE.md")),d=R(I(r,".claude/settings.json")),h=R(I(r,".claude/skills"))?xe(I(r,".claude/skills")).filter(g=>!g.startsWith(".")).length:0,p=[c?l.green("\u2713"):l.red("\u2717"),".claude",a?l.green("\u2713"):l.yellow("\u25CB"),"CLAUDE.md",d?l.green("\u2713"):l.yellow("\u25CB"),"settings",`${h} skills`];console.log(` ${l.cyan(n)}: ${p.join(" ")}`)}}catch(t){console.log(l.red(` \u2717 ${t.message}`))}console.log()});ce.command("show").description("print the resolved configuration").option("-c, --config <path>","path to agentx.json").action(e=>{try{let t=M(e.config);console.log(JSON.stringify(t,null,2))}catch(t){console.log(l.red(t.message))}});import{Command as We}from"commander";import U from"chalk";var Q=new We().name("usage").description("token usage analysis and reporting");Q.command("today",{isDefault:!0}).description("show today's token usage").action(async()=>{try{let e=M(),[t,s]=e.node.bind.split(":"),n=await(await fetch(`http://${t||"127.0.0.1"}:${s}/usage`,{signal:AbortSignal.timeout(3e3)})).json();if(console.log(),console.log(U.bold(" Token Usage (last 7 days)")),console.log(),n.totalTasks===0){console.log(U.dim(" No tasks recorded yet")),console.log();return}let i=c=>c.toLocaleString();console.log(` Total: ${U.bold(i(n.totalTokens))} tokens across ${n.totalTasks} tasks`),console.log(` Input: ${i(n.totalInput)}`),console.log(` Output: ${i(n.totalOutput)}`),console.log(` Cache read: ${i(n.totalCacheRead)}`),console.log(` Cache create: ${i(n.totalCacheCreate)}`),n.cacheHitRatio>0&&console.log(` Cache hit: ${(n.cacheHitRatio*100).toFixed(1)}%`),n.totalErrors>0&&console.log(` Errors: ${U.red(String(n.totalErrors))}`),console.log(),console.log(U.bold(" By Agent:"));let r=Object.entries(n.byAgent||{});r.sort((c,a)=>a[1].total-c[1].total);for(let[c,a]of r){let d=i(a.total||a.input+a.output+a.cacheRead+a.cacheCreate),h=a.avgDuration?`${(a.avgDuration/1e3).toFixed(1)}s avg`:"";console.log(` ${U.cyan(c)}: ${d} tokens (${a.tasks} tasks) ${U.dim(h)}`)}console.log()}catch(e){e.cause?.code==="ECONNREFUSED"?console.log(U.red(" Daemon not running. Start with: agentx daemon start")):console.log(U.red(` ${e.message}`))}});Q.command("serve").description("start a web dashboard for token usage visualization").option("--port <n>","port number","4201").action(async e=>{let{startUsageDashboard:t}=await import("./usage-dashboard-2TJQBFSH.js");t(parseInt(e.port))});Q.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:t}=await import("child_process"),{existsSync:s}=await import("fs"),{resolve:o}=await import("path"),n=o(process.cwd(),"scripts/token-report.py");if(!s(n)){console.log(U.red(` Script not found: ${n}`)),console.log(U.dim(" The token analyzer script should be at scripts/token-report.py"));return}console.log(U.dim(" Analyzing Claude Code sessions..."));try{t(`SINCE_DAYS=${e.days} python3 "${n}"`,{stdio:"inherit"})}catch{console.log(U.red(" Analysis failed. Is Python 3 installed?"))}});import{Command as Ge}from"commander";import u from"chalk";import{createServer as Pe}from"http";var le=class{peers;cache=new Map;ttl=3e4;constructor(t){this.peers=t.map(s=>({url:s.replace(/\/$/,"")}))}async getRemoteAgents(){let t=[];return await Promise.all(this.peers.map(async s=>{try{let o=await this.fetchJson(`${s.url}/wiki/agents`);s.nodeId=o.nodeId;for(let n of o.agents||[])(n.totalEntries>0||n.totalArticles>0)&&t.push({...n,nodeId:o.nodeId,peerUrl:s.url})}catch{}})),t}async getRemoteArticles(t,s){try{return(await this.fetchJson(`${t}/wiki/articles?agent=${encodeURIComponent(s)}`)).articles||[]}catch{return[]}}async getRemoteArticle(t,s,o){try{let n=await this.fetchJson(`${t}/wiki/article?agent=${encodeURIComponent(s)}&path=${encodeURIComponent(o)}`);return{meta:{title:n.title,tags:n.tags||[],owner:n.owner,access:"public",created:n.created,lastUpdated:n.lastUpdated,sources:n.sources||[]},content:n.content,path:n.path}}catch{return null}}async fetchJson(t){let s=this.cache.get(t);if(s&&Date.now()-s.ts<this.ttl)return s.data;let o=await fetch(t,{signal:AbortSignal.timeout(5e3)});if(!o.ok)throw new Error(`HTTP ${o.status}`);let n=await o.json();return this.cache.set(t,{data:n,ts:Date.now()}),n}};function $(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function de(e,t,s=""){let o=$(e);return o=o.replace(/```(\w*)\n([\s\S]*?)```/g,(n,i,r)=>`<pre><code class="lang-${i}">${r.trim()}</code></pre>`),o=o.replace(/`([^`]+)`/g,"<code>$1</code>"),o=o.replace(/^(\|.+\|)\n(\|[-| :]+\|)\n((?:\|.+\|\n?)*)/gm,(n,i,r,c)=>{let a=i.split("|").filter(h=>h.trim()).map(h=>`<th>${h.trim()}</th>`).join(""),d=c.trim().split(`
13
- `).map(h=>`<tr>${h.split("|").filter(g=>g.trim()).map(g=>`<td>${g.trim()}</td>`).join("")}</tr>`).join("");return`<table><thead><tr>${a}</tr></thead><tbody>${d}</tbody></table>`}),o=o.replace(/^#### (.+)$/gm,"<h4>$1</h4>"),o=o.replace(/^### (.+)$/gm,"<h3>$1</h3>"),o=o.replace(/^## (.+)$/gm,"<h2>$1</h2>"),o=o.replace(/^# (.+)$/gm,"<h1>$1</h1>"),o=o.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>"),o=o.replace(/\*(.+?)\*/g,"<em>$1</em>"),o=o.replace(/\[\[([^\]]+)\]\]/g,(n,i)=>{let r=t.get(i);return r?`<a href="${s}/article/${encodeURIComponent(r)}" class="wikilink">${i}</a>`:`<a href="${s}/search?q=${encodeURIComponent(i)}" class="wikilink broken">${i}</a>`}),o=o.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank">$1</a>'),o=o.replace(/^- (.+)$/gm,"<li>$1</li>"),o=o.replace(/(<li>.*<\/li>\n?)+/g,"<ul>$&</ul>"),o=o.replace(/^---$/gm,"<hr>"),o=o.replace(/\n\n+/g,"</p><p>"),o=`<p>${o}</p>`,o=o.replace(/<p>(<(?:h[1-4]|pre|table|ul|hr|div))/g,"$1"),o=o.replace(/(<\/(?:h[1-4]|pre|table|ul|hr|div)>)<\/p>/g,"$1"),o=o.replace(/<p>\s*<\/p>/g,""),o}var Ue=`
2
+ import{g as de,h as ye}from"./chunk-T25UEXVK.js";import{f as K}from"./chunk-VH23CDHL.js";import"./chunk-CJ45Y2IR.js";import{b as M,c as ne}from"./chunk-WJ6ZF6EZ.js";import"./chunk-QZP65I2H.js";import"./chunk-HI24KAEY.js";import"./chunk-34UAFPK4.js";import"./chunk-M7HKBG3V.js";import"./chunk-DSYKYZMT.js";import"./chunk-ZRYBSI5G.js";import"./chunk-QOYAX2FT.js";import{a as be}from"./chunk-4YCH6IZV.js";import{Command as Fe}from"commander";import{Command as Ee}from"commander";import m from"chalk";var L=new Ee().name("daemon").description("manage the agentx daemon \u2014 start, stop, status, logs");L.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:l}=await import("child_process"),h=["dist/cli.js","daemon","start"];e.config&&h.push("-c",e.config),e.port&&h.push("--port",e.port);let p=l("node",h,{detached:!0,stdio:["ignore","ignore","ignore"],cwd:process.cwd()});p.unref();let{writeFileSync:g}=await import("fs");g("/tmp/agentx-daemon.pid",String(p.pid)),console.log(m.green(`Daemon started in background (PID: ${p.pid})`)),console.log(m.dim(" Logs: tail -f /tmp/agentx-daemon.log")),console.log(m.dim(" Stop: agentx daemon stop"));return}let{readFileSync:t,writeFileSync:s,mkdirSync:o,existsSync:n}=await import("fs"),{resolve:i}=await import("path"),r=i(process.cwd(),".agentx");n(r)||o(r,{recursive:!0});let c=i(r,"daemon.pid");if(n(c)){let l=parseInt(t(c,"utf-8").trim(),10);if(l&&l!==process.pid)try{process.kill(l,0),console.error(`Another agentx daemon is already running (PID ${l}). Exiting.`),process.exit(0)}catch{}}s(c,String(process.pid)),await new de(e.config).start()});L.command("stop").description("stop the running daemon").action(async()=>{let{readFileSync:e,existsSync:t,unlinkSync:s}=await import("fs"),{resolve:o}=await import("path"),n=[o(process.cwd(),".agentx/daemon.pid"),"/tmp/agentx-daemon.pid"];for(let r of n)if(t(r))try{let c=parseInt(e(r,"utf-8").trim(),10);process.kill(c,"SIGTERM"),await new Promise(a=>setTimeout(a,2e3));try{s(r)}catch{}console.log(m.green(`Daemon stopped (PID: ${c})`));return}catch{try{s(r)}catch{}}let{execSync:i}=await import("child_process");try{i("pkill -f 'node dist/cli.js daemon'",{stdio:"ignore"}),console.log(m.green("Daemon stopped"))}catch{console.log(m.yellow("No daemon process found"))}});L.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 t=M(e.config),[s,o]=t.node.bind.split(":"),n=`http://${s||"127.0.0.1"}:${o}/health`,r=await(await fetch(n,{signal:AbortSignal.timeout(3e3)})).json();if(e.json){console.log(JSON.stringify(r,null,2));return}console.log(),console.log(m.bold(` ${r.node.name}`)+m.dim(` (${r.node.id})`)),console.log(m.green(" Status: running")+m.dim(` \u2014 uptime ${je(r.uptime)}`)),console.log(),console.log(m.bold(" Agents:"));for(let a of r.agents){let l=a.active>0?m.yellow(` [${a.active} active]`):"",h=a.errors>0?m.red(` (${a.errors} errors)`):"";console.log(` ${m.cyan(a.name)} ${m.dim(`(${a.tier})`)}${l}${h}`),console.log(m.dim(` ${a.workspace}`))}let c=r.crons.filter(a=>a.enabled);if(r.crons.length){console.log(),console.log(m.bold(` Crons: ${c.length} enabled / ${r.crons.length} total`));for(let a of r.crons){let l=a.enabled?m.green("\u25CF"):m.dim("\u25CB"),h=a.nextRun?m.dim(` next: ${new Date(a.nextRun).toLocaleTimeString()}`):"";console.log(` ${l} ${a.id}${h}`)}}if(r.mesh?.length){console.log(),console.log(m.bold(" Mesh:"));for(let a of r.mesh){let l=a.healthy?m.green("\u25CF"):m.red("\u25CF"),h=a.skills?.length?m.dim(` (${a.skills.length} agents)`):"";console.log(` ${l} ${a.peer} ${m.dim(a.peerUrl)}${h}`)}}console.log()}catch(t){if(t.cause?.code==="ECONNREFUSED"||t.name==="TimeoutError")console.log(m.red(" Daemon is not running")),console.log(m.dim(" Start with: agentx daemon start"));else try{let s=M(e.config),o=ne(s);if(console.log(),console.log(m.bold(` ${s.node.name}`)+m.dim(` (${s.node.id})`)),console.log(m.yellow(" Status: stopped")),console.log(m.dim(` ${Object.keys(s.agents).length} agents, ${Object.keys(s.crons).length} crons configured`)),o.length){console.log();for(let n of o)console.log(m.yellow(` \u26A0 ${n}`))}console.log()}catch(s){console.log(m.red(` ${s.message}`))}}});L.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:t,spawn:s}=await import("child_process"),o="/tmp/agentx-daemon.log",{existsSync:n}=await import("fs");if(!n(o)){console.log(m.yellow("No log file found at /tmp/agentx-daemon.log"));return}if(e.follow){let i=s("tail",["-f","-n",e.lines,o],{stdio:"inherit"});process.on("SIGINT",()=>{i.kill(),process.exit(0)})}else t(`tail -n ${e.lines} ${o}`,{stdio:"inherit"})});L.command("watch").description("stream live agent activity (SSE)").option("-c, --config <path>","path to agentx.json").action(async e=>{let o=`http://${(M(e.config).node.bind||"127.0.0.1:18800").replace("0.0.0.0","127.0.0.1")}/events`;console.log(m.dim(` Connecting to ${o}...`)),console.log(m.dim(` Press Ctrl+C to stop
3
+ `));try{let n=await fetch(o);if(!n.ok||!n.body){console.log(m.red(` Failed: ${n.status}`));return}let i=n.body.getReader(),r=new TextDecoder,c="";for(;;){let{done:a,value:l}=await i.read();if(a)break;c+=r.decode(l,{stream:!0});let h=c.split(`
4
+ `);c=h.pop()||"";for(let p of h)if(p.startsWith("data: "))try{let g=JSON.parse(p.slice(6)),y=new Date(g.time).toLocaleTimeString(),f=g.message?.replace(/^\[agentx\]\s*/,"")||"";f.includes("executing")?console.log(`${m.dim(y)} ${m.cyan("\u25B6")} ${f}`):f.includes("completed")?console.log(`${m.dim(y)} ${m.green("\u2713")} ${f}`):f.includes("error")||f.includes("Error")?console.log(`${m.dim(y)} ${m.red("\u2717")} ${f}`):f.includes("Routing")?console.log(`${m.dim(y)} ${m.yellow("\u2192")} ${f}`):f.includes("mention")?console.log(`${m.dim(y)} ${m.magenta("@")} ${f}`):f.includes("Skipping")?console.log(`${m.dim(y)} ${m.dim("\u2298")} ${m.dim(f)}`):console.log(`${m.dim(y)} ${f}`)}catch{}else p.startsWith("event: status")}}catch(n){console.log(m.red(` Connection failed: ${n.message}`)),console.log(m.dim(" Is the daemon running? Try: agentx daemon status"))}});L.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,t,s)=>{let o=t.join(" ");try{let n=M(s.config),[i,r]=n.node.bind.split(":"),c=`http://${i||"127.0.0.1"}:${r}`,a,l;s.peer?(a=`${c}/mesh/task`,l={peer:s.peer,message:o,agent:e}):(a=`${c}/task`,l={agent:e,message:o}),console.log(m.dim(` Sending to ${s.peer?`${s.peer}/${e}`:e}...`));let p=await(await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)})).json();s.json?console.log(JSON.stringify(p,null,2)):p.error?console.log(m.red(` Error: ${p.error}`)):(console.log(),console.log(p.content||p.response||"No response"),p.duration&&console.log(m.dim(`
5
+ (${(p.duration/1e3).toFixed(1)}s)`)))}catch(n){console.log(m.red(` Failed: ${n.message}`)),console.log(m.dim(" Is the daemon running? Try: agentx daemon status"))}});L.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","root").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,t)=>{let{execSync:s}=await import("child_process"),o=t.identity?`-e "ssh -i ${t.identity}"`:"",n=`${t.user}@${e}:${t.path}/dist/`;if(!t.skipChecks){try{console.log(m.dim(" Running tests...")),s("npx vitest run --reporter=dot",{stdio:"pipe",cwd:process.cwd()}),console.log(m.green(" \u2713 Tests passed"))}catch{console.log(m.red(" \u2717 Tests failed \u2014 aborting deploy")),console.log(m.dim(" Use --skip-checks to deploy anyway"));return}let{existsSync:i,statSync:r}=await import("fs"),{resolve:c}=await import("path"),a=c(process.cwd(),"dist/cli.js");if(!i(a)){console.log(m.red(" \u2717 dist/ not found \u2014 run build first"));return}Date.now()-r(a).mtimeMs>3e5&&(console.log(m.yellow(" \u26A0 dist/ is older than 5 minutes \u2014 rebuilding...")),s("npx tsup --no-dts",{stdio:"pipe",cwd:process.cwd()}),console.log(m.green(" \u2713 Build complete")))}console.log(m.dim(` Deploying to ${t.user}@${e}:${t.path}...`));try{if(s(`rsync -avzL --delete ${o} dist/ ${n}`,{stdio:"inherit",cwd:process.cwd()}),console.log(m.green(" \u2713 Deploy complete")),t.restart){console.log(m.dim(" Restarting remote daemon..."));let i=t.identity?`ssh -i ${t.identity}`:"ssh";s(`${i} ${t.user}@${e} "pkill -f 'node dist/cli.js daemon' 2>/dev/null; sleep 2; cd ${t.path} && nohup node dist/cli.js daemon start > /tmp/agentx-daemon.log 2>&1 &"`,{stdio:"inherit"}),console.log(m.green(" Remote daemon restarted"))}}catch(i){console.log(m.red(` Deploy failed: ${i.message}`))}});L.action(async e=>{await new de(e.config).start()});function je(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 Re}from"commander";import D from"chalk";import{existsSync as q,readFileSync as Te,writeFileSync as $e,mkdirSync as we}from"fs";import{resolve as J}from"path";var ke=new Re().name("init").description("initialize agentx configuration in the current directory").option("--force","overwrite existing config").action(async e=>{let t=process.cwd(),s=J(t,"agentx.json"),o=J(t,".env"),n=J(t,".agentx/sessions");if(console.log(),console.log(D.bold(" agentx init")),console.log(),q(s)&&!e.force){console.log(D.yellow(" agentx.json already exists. Use --force to overwrite.")),console.log();return}let i=[J(t,"agentx.example.json"),J(t,"node_modules/agentix-cli/agentx.example.json")],r;for(let a of i)if(q(a)){r=Te(a,"utf-8");break}r||(r=JSON.stringify({node:{id:be("os").hostname().toLowerCase().replace(/[^a-z0-9-]/g,"-"),name:be("os").hostname(),bind:"127.0.0.1:18800"},providers:{claude:{apiKey:"${ANTHROPIC_API_KEY}"}},agents:{assistant:{name:"Assistant",workspace:t,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)),$e(s,r),console.log(D.green(" \u2713 Created agentx.json")),q(o)||($e(o,["# AgentX environment variables","# ANTHROPIC_API_KEY=sk-ant-...","# TG_BOT_TOKEN=123456:ABC...","# MESH_TOKEN=your-mesh-secret",""].join(`
6
+ `)),console.log(D.green(" \u2713 Created .env template"))),q(n)||(we(n,{recursive:!0}),console.log(D.green(" \u2713 Created .agentx/sessions/")));let c=J(t,".claude");q(c)||(we(c,{recursive:!0}),console.log(D.green(" \u2713 Created .claude/"))),console.log(),console.log(D.bold(" Next steps:")),console.log(` 1. Edit ${D.cyan("agentx.json")} \u2014 configure agents, channels, crons`),console.log(` 2. Edit ${D.cyan(".env")} \u2014 add API keys and bot tokens`),console.log(` 3. Run ${D.cyan("agentx daemon start")} \u2014 start the daemon`),console.log(` 4. Run ${D.cyan("agentx daemon status")} \u2014 verify everything`),console.log()});import{Command as B}from"commander";import d from"chalk";import N from"prompts";import{readFileSync as se,writeFileSync as ve,existsSync as R,mkdirSync as _,cpSync as ge,readdirSync as xe}from"fs";import{resolve as I,join as oe}from"path";function j(){let e=I(process.cwd(),"agentx.json");if(!R(e))throw new Error("No agentx.json found. Run: agentx init");return JSON.parse(se(e,"utf-8"))}function O(e){ve(I(process.cwd(),"agentx.json"),JSON.stringify(e,null,2))}var X=new B().name("agent").description("manage agents \u2014 add, list, remove");X.command("list").alias("ls").description("list configured agents").action(()=>{let e=j();console.log();for(let[t,s]of Object.entries(e.agents||{})){let o=s.mentions?.join(", ")||"";console.log(` ${d.cyan(t)} \u2014 ${s.name} (${s.tier})`),console.log(d.dim(` workspace: ${s.workspace}`)),o&&console.log(d.dim(` mentions: ${o}`))}console.log()});X.command("add").description("add a new agent interactively").action(async()=>{let e=j(),t=await N([{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:I(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(!t.id)return;let s=t.workspace.endsWith(t.id)?t.workspace:oe(t.workspace,t.id);_(I(s,".claude/skills"),{recursive:!0});let o={name:t.name,workspace:s,tier:t.tier,model:t.model,mentions:t.mentions?.map(l=>l.trim()).filter(Boolean)||[],maxConcurrent:t.maxConcurrent,systemPrompt:t.systemPrompt,permissionMode:"default"},{setupWorkspace:n}=await import("./workspace-setup-QQTCJY4B.js"),i=n(t.id,o,"19900",(...l)=>{console.log(d.dim(` ${l.join(" ")}`))}),r=I(process.cwd(),"src/wiki/SKILL.md"),c=I(__dirname,"../wiki/SKILL.md"),a=R(r)?r:c;R(a)&&(_(I(s,".claude/skills/wiki"),{recursive:!0}),ge(a,I(s,".claude/skills/wiki/SKILL.md"))),e.agents=e.agents||{},e.agents[t.id]=o,O(e),console.log(d.green(`
7
+ Agent "${t.id}" added`)),console.log(d.dim(` Workspace: ${s}`)),console.log(d.dim(` Created: ${i.created.length} files (CLAUDE.md, AGENTS.md, settings, rules)`)),console.log()});X.command("remove <id>").alias("rm").description("remove an agent from config (keeps workspace)").action(e=>{let t=j();if(!t.agents?.[e]){console.log(d.red(` Agent "${e}" not found`));return}delete t.agents[e],O(t),console.log(d.green(` Agent "${e}" removed from config (workspace preserved)`))});var ae=new B().name("channel").description("manage channels \u2014 add telegram/whatsapp/discord, list");ae.command("list").alias("ls").description("list configured channels and bindings").action(()=>{let e=j();console.log();let t=e.channels?.telegram;if(t?.enabled){console.log(d.bold(" Telegram:"));for(let[i,r]of Object.entries(t.accounts||{}))console.log(` ${d.cyan(i)} -> agent: ${r.agentBinding}`)}else console.log(d.dim(" Telegram: disabled"));let s=e.channels?.whatsapp;if(s?.enabled){console.log(d.bold(" WhatsApp:")),console.log(` default agent: ${s.defaultAgent||"(none)"}`),console.log(` session: ${s.sessionDir||".agentx/whatsapp-sessions"}`);for(let i of s.routes||[]){let r=i.contact?`contact ${i.contact}`:`group "${i.group}"`;console.log(` ${r} -> agent: ${i.agent}`)}}else console.log(d.dim(" WhatsApp: disabled"));let o=e.channels?.discord;o?.enabled?(console.log(d.bold(" Discord:")),console.log(` agent: ${o.agentBinding||"(none)"}`)):console.log(d.dim(" Discord: disabled"));let n=e.channels?.gitlab;if(n?.enabled){console.log(d.bold(" GitLab:")),console.log(` host: ${n.host}`),console.log(` webhook: :${n.webhookPort}`);for(let i of n.routes||[])console.log(` ${i.project} -> agent: ${i.agent}`)}else console.log(d.dim(" GitLab: disabled"));console.log()});ae.command("add").description("add a channel (telegram, whatsapp, or discord)").action(async()=>{let e=j(),t=Object.keys(e.agents||{});if(!t.length){console.log(d.red(" No agents configured. Run: agentx agent add"));return}let s=t.map(n=>({title:`${n} (${e.agents[n].name})`,value:n})),{channelType:o}=await N({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(o){if(e.channels=e.channels||{},o==="telegram"){let n=await N([{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:s}]);if(!n.accountName||!n.token)return;try{let r=await(await fetch(`https://api.telegram.org/bot${n.token}/getMe`)).json();if(!r.ok)throw new Error(r.description);console.log(d.green(` Bot verified: @${r.result.username}`));let c=`@${r.result.username}`,a=e.agents[n.agentBinding].mentions||[];a.includes(c)||(a.push(c,r.result.username),e.agents[n.agentBinding].mentions=a)}catch(i){console.log(d.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[n.accountName]={token:n.token,agentBinding:n.agentBinding},O(e),console.log(d.green(` Telegram "${n.accountName}" added -> ${n.agentBinding}`))}if(o==="whatsapp"){let n=await N([{type:"select",name:"defaultAgent",message:"Default agent for WhatsApp messages",choices:s},{type:"text",name:"sessionDir",message:"Session directory",initial:".agentx/whatsapp-sessions"},{type:"confirm",name:"addRoute",message:"Add a contact/group route?",initial:!0}]);if(!n.defaultAgent)return;let i=[],r=n.addRoute;for(;r;){let c=await N([{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:a=>a==="contact"?"Phone number (e.g. +21624309128)":"Group name (partial match)"},{type:"select",name:"agent",message:"Route to agent",choices:s},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);c.value&&c.agent&&i.push({[c.type]:c.value,agent:c.agent}),r=c.more}e.channels.whatsapp={enabled:!0,sessionDir:n.sessionDir,defaultAgent:n.defaultAgent,routes:i},O(e),_(I(process.cwd(),n.sessionDir),{recursive:!0}),console.log(d.green(` WhatsApp enabled (${i.length} routes, default: ${n.defaultAgent})`)),console.log(d.dim(" Start daemon to scan QR code: agentx daemon start"))}if(o==="discord"){let n=await N([{type:"text",name:"token",message:"Discord bot token (from developer portal)"},{type:"select",name:"agentBinding",message:"Bind to agent",choices:s}]);if(!n.token)return;e.channels.discord={enabled:!0,token:n.token,agentBinding:n.agentBinding},O(e),console.log(d.green(` Discord added -> ${n.agentBinding}`))}if(o==="gitlab"){let n=await N([{type:"text",name:"host",message:"GitLab instance URL",initial:"https://gitlab.example.com"},{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(!n.token)return;let i=[],r=n.addRoute;for(;r;){let c=await N([{type:"text",name:"project",message:"GitLab project path (e.g. group/project, or * for default)"},{type:"select",name:"agent",message:"Route to agent",choices:s},{type:"confirm",name:"more",message:"Add another route?",initial:!1}]);c.project&&c.agent&&i.push({project:c.project,agent:c.agent}),r=c.more}e.channels.gitlab={enabled:!0,host:n.host,token:n.token,webhookPort:n.webhookPort,webhookSecret:n.webhookSecret||void 0,routes:i},O(e),console.log(d.green(` GitLab enabled (${i.length} routes, webhook :${n.webhookPort})`)),console.log(d.dim(` Add webhook in GitLab: ${n.host}/<project>/-/hooks`)),console.log(d.dim(` URL: http://your-server:${n.webhookPort}/`)),n.webhookSecret&&console.log(d.dim(` Secret token: ${n.webhookSecret}`)),console.log(d.dim(" Events: Comments, Issues, Merge requests, Pipeline"))}console.log(d.dim(" Restart daemon to apply: agentx daemon stop && agentx daemon start")),console.log()}});var Y=new B().name("cron").description("manage cron jobs \u2014 add, list, enable, disable");Y.command("list").alias("ls").description("list cron jobs").action(()=>{let e=j();console.log();for(let[t,s]of Object.entries(e.crons||{})){let o=s.enabled?d.green("\u25CF"):d.dim("\u25CB");console.log(` ${o} ${d.cyan(t)} \u2014 ${s.schedule} (${s.timezone})`),console.log(d.dim(` agent: ${s.agent} | timeout: ${s.timeout}s`))}Object.keys(e.crons||{}).length||console.log(d.dim(" No cron jobs configured")),console.log()});Y.command("add").description("add a cron job").action(async()=>{let e=j(),t=Object.keys(e.agents||{}),s=await N([{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:t.map(o=>({title:o,value:o}))},{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}]);!s.id||!s.schedule||(e.crons=e.crons||{},e.crons[s.id]={enabled:s.enabled,schedule:s.schedule,timezone:s.timezone,agent:s.agent,prompt:s.prompt,timeout:s.timeout,onError:["log"]},O(e),console.log(d.green(` Cron "${s.id}" added (${s.enabled?"enabled":"disabled"})`)),console.log())});Y.command("enable <id>").description("enable a cron job").action(e=>{let t=j();if(!t.crons?.[e]){console.log(d.red(` Cron "${e}" not found`));return}t.crons[e].enabled=!0,O(t),console.log(d.green(` Cron "${e}" enabled`))});Y.command("disable <id>").description("disable a cron job").action(e=>{let t=j();if(!t.crons?.[e]){console.log(d.red(` Cron "${e}" not found`));return}t.crons[e].enabled=!1,O(t),console.log(d.green(` Cron "${e}" disabled`))});var V=new B().name("mesh").description("manage mesh peers \u2014 add, list, remove");V.command("list").alias("ls").description("list mesh peers").action(async()=>{let e=j();if(console.log(),!e.mesh?.enabled)console.log(d.dim(" Mesh: disabled"));else{console.log(d.bold(" Mesh peers:"));for(let t of e.mesh.peers||[]){let s=d.red("unreachable");try{let n=await(await fetch(`${t.url}/health`,{signal:AbortSignal.timeout(3e3)})).json();s=d.green(`healthy \u2014 ${n.agents?.length||0} agents`)}catch{}console.log(` ${d.cyan(t.name)} ${d.dim(t.url)} ${s}`)}}console.log()});V.command("add").description("add a mesh peer (another agentx server)").action(async()=>{let e=j(),t=await N([{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(!(!t.name||!t.url)){try{let o=await(await fetch(`${t.url}/.well-known/agent-card.json`,{signal:AbortSignal.timeout(5e3)})).json();console.log(d.green(` Connected: ${o.name} (${o.skills?.length||0} agents)`))}catch{console.log(d.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:t.url,name:t.name,...t.token?{token:t.token}:{}}),O(e),console.log(d.green(` Peer "${t.name}" added`)),console.log()}});V.command("remove <name>").alias("rm").description("remove a mesh peer").action(e=>{let t=j();t.mesh?.peers&&(t.mesh.peers=t.mesh.peers.filter(s=>s.name!==e),t.mesh.peers.length===0&&(t.mesh.enabled=!1),O(t),console.log(d.green(` Peer "${e}" removed`)))});var ie=new B().name("skill").description("manage skills \u2014 add to agent(s), list");ie.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,t)=>{let s=j(),o=I(e);if(!R(o)){console.log(d.red(` Skill not found: ${o}`));return}let n=R(oe(o,"SKILL.md"))?o.split("/").pop():o.replace(/\/SKILL\.md$/,"").split("/").pop(),i;if(t.all)i=Object.keys(s.agents||{});else if(t.agent)i=t.agent;else{let r=Object.keys(s.agents||{});i=(await N({type:"multiselect",name:"agents",message:"Install to which agents?",choices:r.map(a=>({title:`${a} (${s.agents[a].name})`,value:a}))})).agents||[]}for(let r of i){let c=s.agents[r];if(!c){console.log(d.yellow(` Agent "${r}" not found, skipping`));continue}let a=I(c.workspace,".claude/skills",n);_(a,{recursive:!0}),R(oe(o,"SKILL.md"))?ge(o,a,{recursive:!0}):o.endsWith(".md")&&ge(o,oe(a,"SKILL.md")),console.log(d.green(` ${n} -> ${r} (${c.workspace})`))}console.log()});ie.command("list").alias("ls").description("list skills per agent").action(()=>{let e=j();console.log();for(let[t,s]of Object.entries(e.agents||{})){let o=I(s.workspace,".claude/skills"),n=R(o)?xe(o).filter(i=>!i.startsWith(".")):[];console.log(` ${d.cyan(t)}: ${n.length?n.join(", "):d.dim("(none)")}`)}console.log()});var pe=new B().name("hook").description("manage hooks \u2014 add to agent workspace");pe.command("add <agent>").description("add a hook to an agent's workspace settings").action(async e=>{let s=j().agents?.[e];if(!s){console.log(d.red(` Agent "${e}" not found`));return}let o=I(s.workspace,".claude/settings.json"),n=R(o)?JSON.parse(se(o,"utf-8")):{},i=await N([{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:a=>a==="command"?"Shell command":"URL"},{type:"text",name:"matcher",message:"Matcher regex (optional, e.g. 'Bash' to match tool)"}]);if(!i.event||!i.value)return;n.hooks=n.hooks||{},n.hooks[i.event]=n.hooks[i.event]||[];let c={hooks:[{type:i.type,...i.type==="command"?{command:i.value}:{url:i.value}}]};i.matcher&&(c.matcher=i.matcher),n.hooks[i.event].push(c),_(I(s.workspace,".claude"),{recursive:!0}),ve(o,JSON.stringify(n,null,2)),console.log(d.green(` Hook added to ${e}: ${i.event} -> ${i.type}`)),console.log()});var me=new B().name("migrate").description("import configuration from OpenClaw or other tools");me.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,t)=>{let s=[e,I(process.env.HOME||"",".openclaw/openclaw.json"),I(process.env.HOME||"",".openclaw/clawdbot.json")].filter(Boolean),o;for(let y of s)if(y&&R(y)){o=y;break}if(!o){console.log(d.red(" OpenClaw config not found. Pass the path: agentx migrate openclaw /path/to/config.json"));return}console.log(d.dim(` Reading: ${o}`));let n=JSON.parse(se(o,"utf-8")),i=R(I(process.cwd(),"agentx.json"))?j():{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=n.agents?.list||[],c=n.bindings||[],a={};for(let y of c)y.match?.channel==="telegram"&&(a[y.match.accountId]=y.agentId);r.find(y=>y.id==="main"||y.default)&&(a.default="main");let l=0;for(let y of r){if(y.workspace?.startsWith("/data"))continue;let f=y.id.replace(/-agent$/,"").replace(/^main$/,"main-agent"),b=y.groupChat?.mentionPatterns||[];if(b.some($=>$===f)||b.push(f),i.agents[f]={name:y.name,workspace:y.workspace,tier:"claude-code",model:"claude-sonnet-4-6",mentions:b,maxConcurrent:y.id==="main"?2:1,systemPrompt:`You are ${y.name} on AgentX.`,permissionMode:"bypassPermissions"},l++,!t.dryRun){_(I(y.workspace,".claude/skills"),{recursive:!0});let{setupWorkspace:$}=await import("./workspace-setup-QQTCJY4B.js");$(f,i.agents[f])}}console.log(` ${d.green(l)} agents imported (workspaces set up with Claude Code best practices)`);let h=n.channels?.telegram?.accounts||{},p=0;for(let[y,f]of Object.entries(h)){if(!f.botToken)continue;let $=a[y]?.replace(/-agent$/,"").replace(/^main$/,"main-agent")||"default";i.channels.telegram.accounts[y]={token:f.botToken,agentBinding:$},i.channels.telegram.enabled=!0,p++}console.log(` ${d.green(p)} Telegram accounts imported`);let g=I(o,"../cron/jobs.json");if(R(g)){let y=JSON.parse(se(g,"utf-8")),f=0;for(let b of y.jobs||[]){let $=b.name||b.id,k=b.agentId?.replace(/-agent$/,"").replace(/^main$/,"main-agent")||"default";i.crons[$]={enabled:b.enabled||!1,schedule:b.schedule?.expr||"0 9 * * *",timezone:b.schedule?.tz||"UTC",agent:k,prompt:b.payload?.message||"",timeout:b.payload?.timeoutSeconds||600,onError:["log"]},f++}console.log(` ${d.green(f)} cron jobs imported`)}t.dryRun?(console.log(d.yellow(`
8
+ Dry run \u2014 nothing written`)),console.log(JSON.stringify(i,null,2))):(O(i),console.log(d.green(`
9
+ Migration complete! Review agentx.json and run: agentx daemon start`))),console.log()});var re=new B().name("config").description("validate and inspect configuration");re.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 t=M(e.config);console.log(d.green(" \u2713 Config valid")),console.log(d.dim(` Node: ${t.node.name} (${t.node.id})`)),console.log(d.dim(` Bind: ${t.node.bind}`)),console.log(d.dim(` Agents: ${Object.keys(t.agents).length}`)),console.log(d.dim(` Crons: ${Object.keys(t.crons).length}`)),console.log(d.dim(` Mesh peers: ${t.mesh.peers.length}`));let s=Object.keys(t.channels.telegram.accounts).length;console.log(d.dim(` Telegram: ${t.channels.telegram.enabled?`${s} accounts`:"disabled"}`)),console.log(d.dim(` WhatsApp: ${t.channels.whatsapp.enabled?`${t.channels.whatsapp.routes.length} routes`:"disabled"}`)),console.log(d.dim(` Discord: ${t.channels.discord?.enabled?"enabled":"disabled"}`));let o=ne(t);if(o.length){console.log(),console.log(d.yellow(" Warnings:"));for(let n of o)console.log(d.yellow(` \u26A0 ${n}`))}else console.log(d.green(" \u2713 All workspaces valid"));console.log();for(let[n,i]of Object.entries(t.agents)){let r=i.workspace,c=R(I(r,".claude")),a=R(I(r,"CLAUDE.md")),l=R(I(r,".claude/settings.json")),h=R(I(r,".claude/skills"))?xe(I(r,".claude/skills")).filter(g=>!g.startsWith(".")).length:0,p=[c?d.green("\u2713"):d.red("\u2717"),".claude",a?d.green("\u2713"):d.yellow("\u25CB"),"CLAUDE.md",l?d.green("\u2713"):d.yellow("\u25CB"),"settings",`${h} skills`];console.log(` ${d.cyan(n)}: ${p.join(" ")}`)}}catch(t){console.log(d.red(` \u2717 ${t.message}`))}console.log()});re.command("show").description("print the resolved configuration").option("-c, --config <path>","path to agentx.json").action(e=>{try{let t=M(e.config);console.log(JSON.stringify(t,null,2))}catch(t){console.log(d.red(t.message))}});import{Command as We}from"commander";import U from"chalk";var Q=new We().name("usage").description("token usage analysis and reporting");Q.command("today",{isDefault:!0}).description("show today's token usage").action(async()=>{try{let e=M(),[t,s]=e.node.bind.split(":"),n=await(await fetch(`http://${t||"127.0.0.1"}:${s}/usage`,{signal:AbortSignal.timeout(3e3)})).json();if(console.log(),console.log(U.bold(" Token Usage (last 7 days)")),console.log(),n.totalTasks===0){console.log(U.dim(" No tasks recorded yet")),console.log();return}let i=c=>c.toLocaleString();console.log(` Total: ${U.bold(i(n.totalTokens))} tokens across ${n.totalTasks} tasks`),console.log(` Input: ${i(n.totalInput)}`),console.log(` Output: ${i(n.totalOutput)}`),console.log(` Cache read: ${i(n.totalCacheRead)}`),console.log(` Cache create: ${i(n.totalCacheCreate)}`),n.cacheHitRatio>0&&console.log(` Cache hit: ${(n.cacheHitRatio*100).toFixed(1)}%`),n.totalErrors>0&&console.log(` Errors: ${U.red(String(n.totalErrors))}`),console.log(),console.log(U.bold(" By Agent:"));let r=Object.entries(n.byAgent||{});r.sort((c,a)=>a[1].total-c[1].total);for(let[c,a]of r){let l=i(a.total||a.input+a.output+a.cacheRead+a.cacheCreate),h=a.avgDuration?`${(a.avgDuration/1e3).toFixed(1)}s avg`:"";console.log(` ${U.cyan(c)}: ${l} tokens (${a.tasks} tasks) ${U.dim(h)}`)}console.log()}catch(e){e.cause?.code==="ECONNREFUSED"?console.log(U.red(" Daemon not running. Start with: agentx daemon start")):console.log(U.red(` ${e.message}`))}});Q.command("serve").description("start a web dashboard for token usage visualization").option("--port <n>","port number","4201").action(async e=>{let{startUsageDashboard:t}=await import("./usage-dashboard-2TJQBFSH.js");t(parseInt(e.port))});Q.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:t}=await import("child_process"),{existsSync:s}=await import("fs"),{resolve:o}=await import("path"),n=o(process.cwd(),"scripts/token-report.py");if(!s(n)){console.log(U.red(` Script not found: ${n}`)),console.log(U.dim(" The token analyzer script should be at scripts/token-report.py"));return}console.log(U.dim(" Analyzing Claude Code sessions..."));try{t(`SINCE_DAYS=${e.days} python3 "${n}"`,{stdio:"inherit"})}catch{console.log(U.red(" Analysis failed. Is Python 3 installed?"))}});import{Command as Ge}from"commander";import u from"chalk";import{createServer as Pe}from"http";var ce=class{peers;cache=new Map;ttl=3e4;constructor(t){this.peers=t.map(s=>({url:s.replace(/\/$/,"")}))}async getRemoteAgents(){let t=[];return await Promise.all(this.peers.map(async s=>{try{let o=await this.fetchJson(`${s.url}/wiki/agents`);s.nodeId=o.nodeId;for(let n of o.agents||[])(n.totalEntries>0||n.totalArticles>0)&&t.push({...n,nodeId:o.nodeId,peerUrl:s.url})}catch{}})),t}async getRemoteArticles(t,s){try{return(await this.fetchJson(`${t}/wiki/articles?agent=${encodeURIComponent(s)}`)).articles||[]}catch{return[]}}async getRemoteArticle(t,s,o){try{let n=await this.fetchJson(`${t}/wiki/article?agent=${encodeURIComponent(s)}&path=${encodeURIComponent(o)}`);return{meta:{title:n.title,tags:n.tags||[],owner:n.owner,access:"public",created:n.created,lastUpdated:n.lastUpdated,sources:n.sources||[]},content:n.content,path:n.path}}catch{return null}}async fetchJson(t){let s=this.cache.get(t);if(s&&Date.now()-s.ts<this.ttl)return s.data;let o=await fetch(t,{signal:AbortSignal.timeout(5e3)});if(!o.ok)throw new Error(`HTTP ${o.status}`);let n=await o.json();return this.cache.set(t,{data:n,ts:Date.now()}),n}};function w(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function le(e,t,s=""){let o=w(e);return o=o.replace(/```(\w*)\n([\s\S]*?)```/g,(n,i,r)=>`<pre><code class="lang-${i}">${r.trim()}</code></pre>`),o=o.replace(/`([^`]+)`/g,"<code>$1</code>"),o=o.replace(/^(\|.+\|)\n(\|[-| :]+\|)\n((?:\|.+\|\n?)*)/gm,(n,i,r,c)=>{let a=i.split("|").filter(h=>h.trim()).map(h=>`<th>${h.trim()}</th>`).join(""),l=c.trim().split(`
10
+ `).map(h=>`<tr>${h.split("|").filter(g=>g.trim()).map(g=>`<td>${g.trim()}</td>`).join("")}</tr>`).join("");return`<table><thead><tr>${a}</tr></thead><tbody>${l}</tbody></table>`}),o=o.replace(/^#### (.+)$/gm,"<h4>$1</h4>"),o=o.replace(/^### (.+)$/gm,"<h3>$1</h3>"),o=o.replace(/^## (.+)$/gm,"<h2>$1</h2>"),o=o.replace(/^# (.+)$/gm,"<h1>$1</h1>"),o=o.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>"),o=o.replace(/\*(.+?)\*/g,"<em>$1</em>"),o=o.replace(/\[\[([^\]]+)\]\]/g,(n,i)=>{let r=t.get(i);return r?`<a href="${s}/article/${encodeURIComponent(r)}" class="wikilink">${i}</a>`:`<a href="${s}/search?q=${encodeURIComponent(i)}" class="wikilink broken">${i}</a>`}),o=o.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank">$1</a>'),o=o.replace(/^- (.+)$/gm,"<li>$1</li>"),o=o.replace(/(<li>.*<\/li>\n?)+/g,"<ul>$&</ul>"),o=o.replace(/^---$/gm,"<hr>"),o=o.replace(/\n\n+/g,"</p><p>"),o=`<p>${o}</p>`,o=o.replace(/<p>(<(?:h[1-4]|pre|table|ul|hr|div))/g,"$1"),o=o.replace(/(<\/(?:h[1-4]|pre|table|ul|hr|div)>)<\/p>/g,"$1"),o=o.replace(/<p>\s*<\/p>/g,""),o}var Ue=`
14
11
  :root {
15
12
  --bg: #f8f9fa; --card: #ffffff; --border: #a2a9b1;
16
13
  --link: #0645ad; --link-visited: #0b0080; --link-broken: #ba0000;
@@ -74,7 +71,7 @@ p code { background: #f5f5f5; padding: 1px 4px; border-radius: 2px; }
74
71
  <head>
75
72
  <meta charset="utf-8">
76
73
  <meta name="viewport" content="width=device-width, initial-scale=1">
77
- <title>${$(e)} \u2014 AgentX Wiki</title>
74
+ <title>${w(e)} \u2014 AgentX Wiki</title>
78
75
  <style>${Ue}</style>
79
76
  </head>
80
77
  <body>
@@ -83,27 +80,27 @@ p code { background: #f5f5f5; padding: 1px 4px; border-radius: 2px; }
83
80
  <main class="main">${s}</main>
84
81
  </div>
85
82
  </body>
86
- </html>`}function Se(e){let t=new Map;for(let s of e){let o=s.path.includes("/")?s.path.split("/").slice(0,-1).join("/"):"/",n=t.get(o)||[];n.push(s),t.set(o,n)}return new Map([...t].sort((s,o)=>s[0].localeCompare(o[0])))}function Ae(e){return e==="/"?"Root":e.split("/").pop().replace(/-/g," ")}function z(e,t){let s='<a href="/" class="logo">AgentX Wiki</a>';s+='<form action="/search" method="get"><input type="text" name="q" class="search-box" placeholder="Search all wikis..."></form>',s+="<h2>Hub</h2>",s+=`<a href="/"${t?"":' class="active"'}>Home</a>`,s+='<a href="/entries">All Entries</a>';for(let o of e){let n=`/agent/${encodeURIComponent(o.agentId)}`;s+=`<h2><a href="${n}" style="color:inherit;text-decoration:none">${$(o.agentId)}</a> <span class="agent-badge">${o.totalArticles}</span></h2>`;let i=Se(o.articles);for(let[r,c]of i){s+=`<div style="font-size:11px;text-transform:uppercase;letter-spacing:0.3px;color:#888;margin:8px 8px 2px;font-weight:600">${Ae(r)}</div>`;for(let a of c){let h=`${o.agentId}/${a.path}`===t;s+=`<a href="${n}/article/${encodeURIComponent(a.path)}"${h?' class="active"':""} style="padding-left:16px;font-size:13px">${$(a.title)}</a>`}}}return s}function Oe(e,t,s){let o=t||e.summary(),i=e.getSharedStore().listEntries().length,r=o.reduce((h,p)=>h+p.totalArticles,0),c=o.reduce((h,p)=>h+p.unabsorbed,0),a=new Set((s||[]).map(h=>h.agentId)),d="<h1>AgentX Wiki Hub</h1>";if(d+='<div class="stats">',d+=`<div class="stat-card"><div class="number">${o.length}</div><div class="label">Agents</div></div>`,d+=`<div class="stat-card"><div class="number">${r}</div><div class="label">Articles</div></div>`,d+=`<div class="stat-card"><div class="number">${i}</div><div class="label">Raw Entries</div></div>`,s&&s.length>0){let h=new Set(s.map(p=>p.nodeId));d+=`<div class="stat-card"><div class="number">${h.size}</div><div class="label">Mesh Nodes</div></div>`}d+="</div>",d+="<h2>Agent Wikis</h2>";for(let h of o){let p=h.unabsorbed>0?`<span style="color: #e6a700;">${h.unabsorbed} unabsorbed</span>`:'<span style="color: green;">up to date</span>',y=a.has(h.agentId)&&!e.listAgents().includes(h.agentId)?' <span class="tag" style="background:#fff3cd;color:#856404">remote</span>':"";if(d+='<div class="agent-card">',d+=`<h3><a href="/agent/${encodeURIComponent(h.agentId)}">${$(h.agentId)}</a>${y}</h3>`,d+=`<div class="agent-stats">
83
+ </html>`}function Se(e){let t=new Map;for(let s of e){let o=s.path.includes("/")?s.path.split("/").slice(0,-1).join("/"):"/",n=t.get(o)||[];n.push(s),t.set(o,n)}return new Map([...t].sort((s,o)=>s[0].localeCompare(o[0])))}function Ae(e){return e==="/"?"Root":e.split("/").pop().replace(/-/g," ")}function z(e,t){let s='<a href="/" class="logo">AgentX Wiki</a>';s+='<form action="/search" method="get"><input type="text" name="q" class="search-box" placeholder="Search all wikis..."></form>',s+="<h2>Hub</h2>",s+=`<a href="/"${t?"":' class="active"'}>Home</a>`,s+='<a href="/entries">All Entries</a>';for(let o of e){let n=`/agent/${encodeURIComponent(o.agentId)}`;s+=`<h2><a href="${n}" style="color:inherit;text-decoration:none">${w(o.agentId)}</a> <span class="agent-badge">${o.totalArticles}</span></h2>`;let i=Se(o.articles);for(let[r,c]of i){s+=`<div style="font-size:11px;text-transform:uppercase;letter-spacing:0.3px;color:#888;margin:8px 8px 2px;font-weight:600">${Ae(r)}</div>`;for(let a of c){let h=`${o.agentId}/${a.path}`===t;s+=`<a href="${n}/article/${encodeURIComponent(a.path)}"${h?' class="active"':""} style="padding-left:16px;font-size:13px">${w(a.title)}</a>`}}}return s}function Ne(e,t,s){let o=t||e.summary(),i=e.getSharedStore().listEntries().length,r=o.reduce((h,p)=>h+p.totalArticles,0),c=o.reduce((h,p)=>h+p.unabsorbed,0),a=new Set((s||[]).map(h=>h.agentId)),l="<h1>AgentX Wiki Hub</h1>";if(l+='<div class="stats">',l+=`<div class="stat-card"><div class="number">${o.length}</div><div class="label">Agents</div></div>`,l+=`<div class="stat-card"><div class="number">${r}</div><div class="label">Articles</div></div>`,l+=`<div class="stat-card"><div class="number">${i}</div><div class="label">Raw Entries</div></div>`,s&&s.length>0){let h=new Set(s.map(p=>p.nodeId));l+=`<div class="stat-card"><div class="number">${h.size}</div><div class="label">Mesh Nodes</div></div>`}l+="</div>",l+="<h2>Agent Wikis</h2>";for(let h of o){let p=h.unabsorbed>0?`<span style="color: #e6a700;">${h.unabsorbed} unabsorbed</span>`:'<span style="color: green;">up to date</span>',y=a.has(h.agentId)&&!e.listAgents().includes(h.agentId)?' <span class="tag" style="background:#fff3cd;color:#856404">remote</span>':"";if(l+='<div class="agent-card">',l+=`<h3><a href="/agent/${encodeURIComponent(h.agentId)}">${w(h.agentId)}</a>${y}</h3>`,l+=`<div class="agent-stats">
87
84
  <span><strong>${h.totalArticles}</strong> articles</span>
88
85
  <span><strong>${h.totalEntries}</strong> entries</span>
89
86
  <span>${p}</span>
90
- </div>`,h.articles.length>0){d+='<div class="article-list">';for(let f of h.articles)d+=`<a href="/agent/${encodeURIComponent(h.agentId)}/article/${encodeURIComponent(f.path)}"><span class="tag">${(f.tags||[]).slice(0,3).join(", ")}</span> ${$(f.title)}</a> `;d+="</div>"}d+="</div>"}return W("Hub",z(o),d)}function Ne(e,t,s){let o=s||e.summary(),n=o.find(g=>g.agentId===t);if(!n)return W("Not Found",z(o),"<h1>Agent not found</h1>");let i=n.peerUrl!==void 0,r=e.getAgentEntries(t),c=n.articles||[],a=`<div class="breadcrumb"><a href="/">Hub</a> / ${$(t)}</div>`;if(a+=`<h1>${$(t)}</h1>`,i&&(a+=`<p><span class="tag" style="background:#fff3cd;color:#856404">remote @ ${$(n.nodeId)}</span></p>`),a+='<div class="stats">',a+=`<div class="stat-card"><div class="number">${n.totalArticles}</div><div class="label">Articles</div></div>`,a+=`<div class="stat-card"><div class="number">${n.totalEntries}</div><div class="label">Entries</div></div>`,a+=`<div class="stat-card"><div class="number">${n.unabsorbed}</div><div class="label">Unabsorbed</div></div>`,a+="</div>",c.length>0){a+="<h2>Articles</h2><table>",a+="<tr><th>Title</th><th>Tags</th></tr>";for(let g of c)a+=`<tr>
91
- <td><a href="/agent/${encodeURIComponent(t)}/article/${encodeURIComponent(g.path)}">${$(g.title)}</a></td>
87
+ </div>`,h.articles.length>0){l+='<div class="article-list">';for(let f of h.articles)l+=`<a href="/agent/${encodeURIComponent(h.agentId)}/article/${encodeURIComponent(f.path)}"><span class="tag">${(f.tags||[]).slice(0,3).join(", ")}</span> ${w(f.title)}</a> `;l+="</div>"}l+="</div>"}return W("Hub",z(o),l)}function Oe(e,t,s){let o=s||e.summary(),n=o.find(g=>g.agentId===t);if(!n)return W("Not Found",z(o),"<h1>Agent not found</h1>");let i=n.peerUrl!==void 0,r=e.getAgentEntries(t),c=n.articles||[],a=`<div class="breadcrumb"><a href="/">Hub</a> / ${w(t)}</div>`;if(a+=`<h1>${w(t)}</h1>`,i&&(a+=`<p><span class="tag" style="background:#fff3cd;color:#856404">remote @ ${w(n.nodeId)}</span></p>`),a+='<div class="stats">',a+=`<div class="stat-card"><div class="number">${n.totalArticles}</div><div class="label">Articles</div></div>`,a+=`<div class="stat-card"><div class="number">${n.totalEntries}</div><div class="label">Entries</div></div>`,a+=`<div class="stat-card"><div class="number">${n.unabsorbed}</div><div class="label">Unabsorbed</div></div>`,a+="</div>",c.length>0){a+="<h2>Articles</h2><table>",a+="<tr><th>Title</th><th>Tags</th></tr>";for(let g of c)a+=`<tr>
88
+ <td><a href="/agent/${encodeURIComponent(t)}/article/${encodeURIComponent(g.path)}">${w(g.title)}</a></td>
92
89
  <td>${(g.tags||[]).slice(0,4).map(y=>`<span class="tag">${y}</span>`).join(" ")}</td>
93
- </tr>`;a+="</table>"}let d=r.slice(-5).reverse();if(d.length>0){a+="<h2>Recent Entries</h2>";for(let g of d)a+=`<div class="entry-card">
90
+ </tr>`;a+="</table>"}let l=r.slice(-5).reverse();if(l.length>0){a+="<h2>Recent Entries</h2>";for(let g of l)a+=`<div class="entry-card">
94
91
  <div class="meta-line">${g.date} via ${g.source} &middot; <code>${g.id}</code></div>
95
- <p>${$(g.content.slice(0,300))}${g.content.length>300?"...":""}</p>
96
- </div>`}let p=e.getAgentWiki(t).lint();if(p.length>0){a+=`<h2>Health Issues (${p.length})</h2><table>`,a+="<tr><th>Type</th><th>Article</th><th>Issue</th></tr>";for(let g of p)a+=`<tr><td><span class="tag">${g.type}</span></td><td>${$(g.article)}</td><td>${$(g.message)}</td></tr>`;a+="</table>"}return W(t,z(o,t),a)}function De(e,t,s,o){let n=e.getAgentWiki(t),i=n.readArticle(s);if(!i)return null;let r=o||e.summary(),c=n.rebuildIndex(),a=new Map;for(let y of c.articles)a.set(y.title,y.path);let d=`/agent/${encodeURIComponent(t)}`,p=n.buildBacklinks()[i.meta.title]||[],g=`<div class="breadcrumb"><a href="/">Hub</a> / <a href="${d}">${$(t)}</a> / ${$(i.meta.title)}</div>`;if(g+=`<h1>${$(i.meta.title)}</h1>`,g+='<dl class="meta">',g+=`<dt>Tags</dt><dd>${(i.meta.tags||[]).map(y=>`<span class="tag">${y}</span>`).join(" ")}</dd>`,g+=`<dt>Owner</dt><dd>${$(i.meta.owner)}</dd>`,g+=`<dt>Created</dt><dd>${i.meta.created}</dd>`,g+=`<dt>Updated</dt><dd>${i.meta.lastUpdated}</dd>`,i.meta.sources?.length&&(g+=`<dt>Sources</dt><dd>${i.meta.sources.length} entries</dd>`),g+="</dl>",g+=de(i.content,a,d),p.length>0){g+='<div class="backlinks"><h4>Pages that link here:</h4><ul>';for(let y of p){let f=n.readArticle(y);g+=`<li><a href="${d}/article/${encodeURIComponent(y)}">${$(f?.meta.title||y)}</a></li>`}g+="</ul></div>"}return W(i.meta.title,z(r,`${t}/${s}`),g)}function Me(e,t){let s=t||e.summary(),n=e.getSharedStore().listEntries(),i='<div class="breadcrumb"><a href="/">Hub</a> / Entries</div>';i+=`<h1>All Raw Entries (${n.length})</h1>`;let r=new Map;for(let c of n){let a=r.get(c.date)||[];a.push(c),r.set(c.date,a)}for(let[c,a]of[...r].reverse()){i+=`<h2>${c} (${a.length})</h2>`;for(let d of a)i+=`<div class="entry-card">
92
+ <p>${w(g.content.slice(0,300))}${g.content.length>300?"...":""}</p>
93
+ </div>`}let p=e.getAgentWiki(t).lint();if(p.length>0){a+=`<h2>Health Issues (${p.length})</h2><table>`,a+="<tr><th>Type</th><th>Article</th><th>Issue</th></tr>";for(let g of p)a+=`<tr><td><span class="tag">${g.type}</span></td><td>${w(g.article)}</td><td>${w(g.message)}</td></tr>`;a+="</table>"}return W(t,z(o,t),a)}function De(e,t,s,o){let n=e.getAgentWiki(t),i=n.readArticle(s);if(!i)return null;let r=o||e.summary(),c=n.rebuildIndex(),a=new Map;for(let y of c.articles)a.set(y.title,y.path);let l=`/agent/${encodeURIComponent(t)}`,p=n.buildBacklinks()[i.meta.title]||[],g=`<div class="breadcrumb"><a href="/">Hub</a> / <a href="${l}">${w(t)}</a> / ${w(i.meta.title)}</div>`;if(g+=`<h1>${w(i.meta.title)}</h1>`,g+='<dl class="meta">',g+=`<dt>Tags</dt><dd>${(i.meta.tags||[]).map(y=>`<span class="tag">${y}</span>`).join(" ")}</dd>`,g+=`<dt>Owner</dt><dd>${w(i.meta.owner)}</dd>`,g+=`<dt>Created</dt><dd>${i.meta.created}</dd>`,g+=`<dt>Updated</dt><dd>${i.meta.lastUpdated}</dd>`,i.meta.sources?.length&&(g+=`<dt>Sources</dt><dd>${i.meta.sources.length} entries</dd>`),g+="</dl>",g+=le(i.content,a,l),p.length>0){g+='<div class="backlinks"><h4>Pages that link here:</h4><ul>';for(let y of p){let f=n.readArticle(y);g+=`<li><a href="${l}/article/${encodeURIComponent(y)}">${w(f?.meta.title||y)}</a></li>`}g+="</ul></div>"}return W(i.meta.title,z(r,`${t}/${s}`),g)}function Me(e,t){let s=t||e.summary(),n=e.getSharedStore().listEntries(),i='<div class="breadcrumb"><a href="/">Hub</a> / Entries</div>';i+=`<h1>All Raw Entries (${n.length})</h1>`;let r=new Map;for(let c of n){let a=r.get(c.date)||[];a.push(c),r.set(c.date,a)}for(let[c,a]of[...r].reverse()){i+=`<h2>${c} (${a.length})</h2>`;for(let l of a)i+=`<div class="entry-card">
97
94
  <div class="meta-line">
98
- <a href="/agent/${encodeURIComponent(d.agentId)}">${$(d.agentId)}</a>
99
- via ${d.source} &middot; <code>${d.id}</code>
95
+ <a href="/agent/${encodeURIComponent(l.agentId)}">${w(l.agentId)}</a>
96
+ via ${l.source} &middot; <code>${l.id}</code>
100
97
  </div>
101
- <p>${$(d.content.slice(0,400))}${d.content.length>400?"...":""}</p>
102
- </div>`}return W("All Entries",z(s),i)}function Le(e,t,s){let o=s||e.summary(),n=`<h1>Search: "${$(t)}"</h1>`,i=0;for(let r of o){let a=e.getAgentWiki(r.agentId).findRelevant(t,void 0,10);if(a.length>0){n+=`<h2>${$(r.agentId)} (${a.length})</h2>`;for(let d of a){let h=`/agent/${encodeURIComponent(r.agentId)}`;n+=`<div class="entry-card">
103
- <div class="meta-line">${(d.meta.tags||[]).slice(0,3).map(p=>`<span class="tag">${p}</span>`).join(" ")}</div>
104
- <h3><a href="${h}/article/${encodeURIComponent(d.path)}">${$(d.meta.title)}</a></h3>
105
- <p>${$(d.content.slice(0,200))}...</p>
106
- </div>`}i+=a.length}}return i===0&&(n+="<p>No matching articles found.</p>"),W(`Search: ${t}`,z(o),n)}function Z(e,t,s){let o=e.rebuildIndex(),n=Se(o.articles.map(r=>({title:r.title,path:r.path,tags:r.tags}))),i=`<a href="/" class="logo">${$(t)}</a>`;i+='<form action="/search" method="get"><input type="text" name="q" class="search-box" placeholder="Search..."></form>',i+="<h2>Navigation</h2>",i+=`<a href="/"${s?"":' class="active"'}>Home</a>`,i+='<a href="/lint">Health Check</a>';for(let[r,c]of n){i+=`<h2>${Ae(r)}</h2>`;for(let a of c){let d=a.path===s;i+=`<a href="/article/${encodeURIComponent(a.path)}"${d?' class="active"':""}>${$(a.title)}</a>`}}return i}function Ie(e,t=4200,s,o=[],n="unified"){let i=new K(e,void 0,n),r=o.length>0?new le(o):null;Pe(async(a,d)=>{let h=new URL(a.url||"/",`http://localhost:${t}`),p=decodeURIComponent(h.pathname),g=null,y=200;try{if(p.startsWith("/remote/")&&r){let f=p.slice(8).split("/article/");if(f.length===2){let[b,w]=f,k=b.indexOf("/"),S=decodeURIComponent(b.slice(0,k)),v=b.slice(k+1),A=await r.getRemoteArticle(S,v,w);if(A){let P=await r.getRemoteAgents(),G=[...i.summary(),...P.map(C=>({...C,agentId:`${C.agentId} @${C.nodeId}`}))],E=`<div class="breadcrumb"><a href="/">Hub</a> / <span class="tag">remote</span> ${$(v)} @ ${$(S)}</div>`;E+=`<h1>${$(A.meta.title)}</h1>`,E+='<dl class="meta">',E+=`<dt>Tags</dt><dd>${(A.meta.tags||[]).map(C=>`<span class="tag">${C}</span>`).join(" ")}</dd>`,E+=`<dt>Owner</dt><dd>${$(A.meta.owner)}</dd>`,E+=`<dt>Source</dt><dd><span class="tag">remote</span> ${$(S)}</dd>`,E+=`<dt>Updated</dt><dd>${A.meta.lastUpdated}</dd>`,E+="</dl>";let T=new Map;E+=de(A.content,T),g=W(A.meta.title,z(G),E)}}}if(!g&&s){let f=i.getAgentWiki(s);if(p==="/"||p===""){let b=f.rebuildIndex(),w=i.getAgentEntries(s),k=`<h1>${$(s)}</h1>`;if(k+='<div class="stats">',k+=`<div class="stat-card"><div class="number">${b.articles.length}</div><div class="label">Articles</div></div>`,k+=`<div class="stat-card"><div class="number">${w.length}</div><div class="label">Entries</div></div>`,k+="</div>",b.articles.length>0){k+="<h2>Articles</h2><table><tr><th>Title</th><th>Type</th><th>Updated</th></tr>";for(let S of b.articles)k+=`<tr><td><a href="/article/${encodeURIComponent(S.path)}">${$(S.title)}</a></td><td><span class="tag">${(S.tags||[]).slice(0,3).join(", ")}</span></td><td>${S.lastUpdated||""}</td></tr>`;k+="</table>"}g=W(s,Z(f,s),k)}else if(p.startsWith("/article/")){let b=p.slice(9),w=f.readArticle(b);if(w){let k=f.rebuildIndex(),S=new Map;for(let A of k.articles)S.set(A.title,A.path);let v=`<h1>${$(w.meta.title)}</h1>`;v+='<dl class="meta">',v+=`<dt>Tags</dt><dd>${(w.meta.tags||[]).map(A=>`<span class="tag">${A}</span>`).join(" ")}</dd>`,v+=`<dt>Updated</dt><dd>${w.meta.lastUpdated}</dd>`,w.meta.sources?.length&&(v+=`<dt>Sources</dt><dd>${w.meta.sources.length} entries</dd>`),v+="</dl>",v+=de(w.content,S),g=W(w.meta.title,Z(f,s,b),v)}}else if(p==="/lint"){let b=f.lint(),w="<h1>Health Check</h1>";if(b.length===0)w+='<p style="color:green;">No issues.</p>';else{w+="<table><tr><th>Type</th><th>Article</th><th>Issue</th></tr>";for(let k of b)w+=`<tr><td><span class="tag">${k.type}</span></td><td>${$(k.article)}</td><td>${$(k.message)}</td></tr>`;w+="</table>"}g=W("Health Check",Z(f,s),w)}else if(p==="/search"){let b=h.searchParams.get("q")||"",w=f.findRelevant(b,void 0,20),k=`<h1>Search: "${$(b)}"</h1>`;for(let S of w)k+=`<div class="entry-card"><h3><a href="/article/${encodeURIComponent(S.path)}">${$(S.meta.title)}</a></h3><p>${$(S.content.slice(0,200))}...</p></div>`;w.length===0&&(k+="<p>No results.</p>"),g=W(`Search: ${b}`,Z(f,s),k)}}else{let f=r?await r.getRemoteAgents():[],b=i.summary(),w=new Map(b.map(v=>[v.agentId,v])),k=[],S=new Set;for(let v of b){let A=f.find(P=>P.agentId===v.agentId);A&&A.totalArticles>v.totalArticles?k.push({...A,totalEntries:Math.max(v.totalEntries,A.totalEntries),unabsorbed:Math.max(v.unabsorbed,A.unabsorbed)}):k.push(v),S.add(v.agentId)}for(let v of f)S.has(v.agentId)||(k.push(v),S.add(v.agentId));if(p==="/"||p==="")g=Oe(i,k,f);else if(p==="/entries")g=Me(i,k);else if(p==="/search")g=Le(i,h.searchParams.get("q")||"",k);else if(p.startsWith("/agent/")){let v=p.slice(7),A=v.indexOf("/article/");if(A===-1)g=Ne(i,v,k);else{let P=v.slice(0,A),G=v.slice(A+9);if(g=De(i,P,G,k),!g&&r){let E=f.find(T=>T.agentId===P);if(E){let T=await r.getRemoteArticle(E.peerUrl,P,G);if(T){let C=`<div class="breadcrumb"><a href="/">Hub</a> / <span class="tag">remote @ ${$(E.nodeId)}</span> / ${$(P)}</div>`;C+=`<h1>${$(T.meta.title)}</h1>`,C+='<dl class="meta">',C+=`<dt>Tags</dt><dd>${(T.meta.tags||[]).map(te=>`<span class="tag">${te}</span>`).join(" ")}</dd>`,C+=`<dt>Source</dt><dd><span class="tag">remote</span> ${$(E.nodeId)}</dd>`,C+=`<dt>Updated</dt><dd>${T.meta.lastUpdated}</dd>`,C+="</dl>",C+=de(T.content,new Map),g=W(T.meta.title,z(k),C)}}}}}}if(!g){y=404;let f=s?Z(i.getAgentWiki(s),s):z(i.summary());g=W("Not Found",f,"<h1>Page not found</h1>")}}catch(f){y=500,g=W("Error","",`<h1>Error</h1><pre>${$(f.message)}</pre>`)}d.writeHead(y,{"Content-Type":"text/html; charset=utf-8"}),d.end(g)}).listen(t)}function Ce(e,t,s,o,n,i){let r=o.length>0?`
98
+ <p>${w(l.content.slice(0,400))}${l.content.length>400?"...":""}</p>
99
+ </div>`}return W("All Entries",z(s),i)}function Le(e,t,s){let o=s||e.summary(),n=`<h1>Search: "${w(t)}"</h1>`,i=0;for(let r of o){let a=e.getAgentWiki(r.agentId).findRelevant(t,void 0,10);if(a.length>0){n+=`<h2>${w(r.agentId)} (${a.length})</h2>`;for(let l of a){let h=`/agent/${encodeURIComponent(r.agentId)}`;n+=`<div class="entry-card">
100
+ <div class="meta-line">${(l.meta.tags||[]).slice(0,3).map(p=>`<span class="tag">${p}</span>`).join(" ")}</div>
101
+ <h3><a href="${h}/article/${encodeURIComponent(l.path)}">${w(l.meta.title)}</a></h3>
102
+ <p>${w(l.content.slice(0,200))}...</p>
103
+ </div>`}i+=a.length}}return i===0&&(n+="<p>No matching articles found.</p>"),W(`Search: ${t}`,z(o),n)}function Z(e,t,s){let o=e.rebuildIndex(),n=Se(o.articles.map(r=>({title:r.title,path:r.path,tags:r.tags}))),i=`<a href="/" class="logo">${w(t)}</a>`;i+='<form action="/search" method="get"><input type="text" name="q" class="search-box" placeholder="Search..."></form>',i+="<h2>Navigation</h2>",i+=`<a href="/"${s?"":' class="active"'}>Home</a>`,i+='<a href="/lint">Health Check</a>';for(let[r,c]of n){i+=`<h2>${Ae(r)}</h2>`;for(let a of c){let l=a.path===s;i+=`<a href="/article/${encodeURIComponent(a.path)}"${l?' class="active"':""}>${w(a.title)}</a>`}}return i}function Ie(e,t=4200,s,o=[],n="unified"){let i=new K(e,void 0,n),r=o.length>0?new ce(o):null;Pe(async(a,l)=>{let h=new URL(a.url||"/",`http://localhost:${t}`),p=decodeURIComponent(h.pathname),g=null,y=200;try{if(p.startsWith("/remote/")&&r){let f=p.slice(8).split("/article/");if(f.length===2){let[b,$]=f,k=b.indexOf("/"),S=decodeURIComponent(b.slice(0,k)),v=b.slice(k+1),A=await r.getRemoteArticle(S,v,$);if(A){let P=await r.getRemoteAgents(),G=[...i.summary(),...P.map(C=>({...C,agentId:`${C.agentId} @${C.nodeId}`}))],E=`<div class="breadcrumb"><a href="/">Hub</a> / <span class="tag">remote</span> ${w(v)} @ ${w(S)}</div>`;E+=`<h1>${w(A.meta.title)}</h1>`,E+='<dl class="meta">',E+=`<dt>Tags</dt><dd>${(A.meta.tags||[]).map(C=>`<span class="tag">${C}</span>`).join(" ")}</dd>`,E+=`<dt>Owner</dt><dd>${w(A.meta.owner)}</dd>`,E+=`<dt>Source</dt><dd><span class="tag">remote</span> ${w(S)}</dd>`,E+=`<dt>Updated</dt><dd>${A.meta.lastUpdated}</dd>`,E+="</dl>";let T=new Map;E+=le(A.content,T),g=W(A.meta.title,z(G),E)}}}if(!g&&s){let f=i.getAgentWiki(s);if(p==="/"||p===""){let b=f.rebuildIndex(),$=i.getAgentEntries(s),k=`<h1>${w(s)}</h1>`;if(k+='<div class="stats">',k+=`<div class="stat-card"><div class="number">${b.articles.length}</div><div class="label">Articles</div></div>`,k+=`<div class="stat-card"><div class="number">${$.length}</div><div class="label">Entries</div></div>`,k+="</div>",b.articles.length>0){k+="<h2>Articles</h2><table><tr><th>Title</th><th>Type</th><th>Updated</th></tr>";for(let S of b.articles)k+=`<tr><td><a href="/article/${encodeURIComponent(S.path)}">${w(S.title)}</a></td><td><span class="tag">${(S.tags||[]).slice(0,3).join(", ")}</span></td><td>${S.lastUpdated||""}</td></tr>`;k+="</table>"}g=W(s,Z(f,s),k)}else if(p.startsWith("/article/")){let b=p.slice(9),$=f.readArticle(b);if($){let k=f.rebuildIndex(),S=new Map;for(let A of k.articles)S.set(A.title,A.path);let v=`<h1>${w($.meta.title)}</h1>`;v+='<dl class="meta">',v+=`<dt>Tags</dt><dd>${($.meta.tags||[]).map(A=>`<span class="tag">${A}</span>`).join(" ")}</dd>`,v+=`<dt>Updated</dt><dd>${$.meta.lastUpdated}</dd>`,$.meta.sources?.length&&(v+=`<dt>Sources</dt><dd>${$.meta.sources.length} entries</dd>`),v+="</dl>",v+=le($.content,S),g=W($.meta.title,Z(f,s,b),v)}}else if(p==="/lint"){let b=f.lint(),$="<h1>Health Check</h1>";if(b.length===0)$+='<p style="color:green;">No issues.</p>';else{$+="<table><tr><th>Type</th><th>Article</th><th>Issue</th></tr>";for(let k of b)$+=`<tr><td><span class="tag">${k.type}</span></td><td>${w(k.article)}</td><td>${w(k.message)}</td></tr>`;$+="</table>"}g=W("Health Check",Z(f,s),$)}else if(p==="/search"){let b=h.searchParams.get("q")||"",$=f.findRelevant(b,void 0,20),k=`<h1>Search: "${w(b)}"</h1>`;for(let S of $)k+=`<div class="entry-card"><h3><a href="/article/${encodeURIComponent(S.path)}">${w(S.meta.title)}</a></h3><p>${w(S.content.slice(0,200))}...</p></div>`;$.length===0&&(k+="<p>No results.</p>"),g=W(`Search: ${b}`,Z(f,s),k)}}else{let f=r?await r.getRemoteAgents():[],b=i.summary(),$=new Map(b.map(v=>[v.agentId,v])),k=[],S=new Set;for(let v of b){let A=f.find(P=>P.agentId===v.agentId);A&&A.totalArticles>v.totalArticles?k.push({...A,totalEntries:Math.max(v.totalEntries,A.totalEntries),unabsorbed:Math.max(v.unabsorbed,A.unabsorbed)}):k.push(v),S.add(v.agentId)}for(let v of f)S.has(v.agentId)||(k.push(v),S.add(v.agentId));if(p==="/"||p==="")g=Ne(i,k,f);else if(p==="/entries")g=Me(i,k);else if(p==="/search")g=Le(i,h.searchParams.get("q")||"",k);else if(p.startsWith("/agent/")){let v=p.slice(7),A=v.indexOf("/article/");if(A===-1)g=Oe(i,v,k);else{let P=v.slice(0,A),G=v.slice(A+9);if(g=De(i,P,G,k),!g&&r){let E=f.find(T=>T.agentId===P);if(E){let T=await r.getRemoteArticle(E.peerUrl,P,G);if(T){let C=`<div class="breadcrumb"><a href="/">Hub</a> / <span class="tag">remote @ ${w(E.nodeId)}</span> / ${w(P)}</div>`;C+=`<h1>${w(T.meta.title)}</h1>`,C+='<dl class="meta">',C+=`<dt>Tags</dt><dd>${(T.meta.tags||[]).map(te=>`<span class="tag">${te}</span>`).join(" ")}</dd>`,C+=`<dt>Source</dt><dd><span class="tag">remote</span> ${w(E.nodeId)}</dd>`,C+=`<dt>Updated</dt><dd>${T.meta.lastUpdated}</dd>`,C+="</dl>",C+=le(T.content,new Map),g=W(T.meta.title,z(k),C)}}}}}}if(!g){y=404;let f=s?Z(i.getAgentWiki(s),s):z(i.summary());g=W("Not Found",f,"<h1>Page not found</h1>")}}catch(f){y=500,g=W("Error","",`<h1>Error</h1><pre>${w(f.message)}</pre>`)}l.writeHead(y,{"Content-Type":"text/html; charset=utf-8"}),l.end(g)}).listen(t)}function Ce(e,t,s,o,n,i){let r=o.length>0?`
107
104
  Existing articles:
108
105
  ${o.map(a=>`- ${a.title} [${(a.tags||[]).join(", ")}] (${a.path})`).join(`
109
106
  `)}
@@ -283,9 +280,9 @@ Be specific. "X is undocumented" is useless. "Alice \u2014 stakeholder who issue
283
280
 
284
281
  ENTRIES (${n}):
285
282
 
286
- ${o}`}import{resolve as ee}from"path";import{execSync as Je}from"child_process";import{writeFileSync as _e,mkdirSync as Ye}from"fs";function F(e,t){return new K(e||ee(process.cwd(),".agentx/wiki"),void 0,t||"graph")}function ue(e){return e==="graph"?"Knowledge Graph":e==="flat"?"Karpathy Flat":"Unified"}var H=new Ge().name("wiki").description("wiki knowledge base management");H.command("status").description("show wiki status per agent").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").action(e=>{let t=e.mode,s=F(e.dir,t),o=s.summary(),i=s.getSharedStore().listEntries().length;console.log(),console.log(u.bold(` Wiki Hub Status [${ue(t)}]`)),console.log(),console.log(` Total raw entries: ${i}`),console.log(` Agents: ${o.length}`),console.log();for(let c of o){let a=c.unabsorbed>0?u.yellow(`${c.unabsorbed} unabsorbed`):u.green("up to date");if(console.log(` ${u.cyan(c.agentId)}`),console.log(` Entries: ${c.totalEntries} Articles: ${c.totalArticles} ${a}`),c.articles.length>0){for(let d of c.articles.slice(0,3))console.log(u.dim(` - ${d.title} [${(d.tags||[]).slice(0,3).join(", ")}]`));c.articles.length>3&&console.log(u.dim(` ... and ${c.articles.length-3} more`))}console.log()}o.reduce((c,a)=>c+a.unabsorbed,0)>0&&(console.log(u.dim(" Run 'agentx wiki absorb' to compile all, or 'agentx wiki absorb --agent <id>' for one agent")),console.log())});H.command("lint").description("check wiki for issues per agent").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","lint a specific agent's wiki").action(e=>{let t=F(e.dir,e.mode),s=e.agent?[e.agent]:t.listAgents(),o=0;console.log();for(let n of s){let r=t.getAgentWiki(n).lint();if(o+=r.length,r.length===0)console.log(` ${u.cyan(n)}: ${u.green("healthy")}`);else{console.log(` ${u.cyan(n)}: ${u.yellow(`${r.length} issues`)}`);for(let c of r){let a=c.type==="broken-link"?"x":c.type==="orphan"?"?":"!";console.log(` [${a}] ${u.dim(c.type)} ${c.article}: ${c.message}`)}}}console.log(),o===0&&console.log(u.green(" All agent wikis are healthy")),console.log()});H.command("absorb").description("compile unabsorbed entries into per-agent wiki articles").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","absorb only this agent").option("--dry-run","preview without running").option("--max <n>","max entries per agent","20").action(async e=>{let t=e.mode,s=F(e.dir,t),o=e.agent?[e.agent]:s.listAgents(),n=parseInt(e.max),i=0;for(let r of o){let c=s.getUnabsorbedEntries(r).slice(0,n);if(c.length===0){console.log(` ${u.cyan(r)}: ${u.green("all absorbed")}`);continue}console.log(),console.log(u.bold(` ${u.cyan(r)}: ${c.length} entries to absorb`));for(let b of c.slice(0,3))console.log(u.dim(` [${b.date} via ${b.source}] ${b.content.slice(0,80)}...`));if(c.length>3&&console.log(u.dim(` ... and ${c.length-3} more`)),e.dryRun)continue;let a=s.getAgentWiki(r),d=a.rebuildIndex(),h=a.getWorldview()||s.getSharedStore().getWorldview()||"",p=c.map(b=>`--- ENTRY ${b.id} [${b.date} ${b.agentId} via ${b.source}] ---
283
+ ${o}`}import{resolve as ee}from"path";import{execSync as Je}from"child_process";import{writeFileSync as _e,mkdirSync as Ye}from"fs";function F(e,t){return new K(e||ee(process.cwd(),".agentx/wiki"),void 0,t||"graph")}function he(e){return e==="graph"?"Knowledge Graph":e==="flat"?"Karpathy Flat":"Unified"}var H=new Ge().name("wiki").description("wiki knowledge base management");H.command("status").description("show wiki status per agent").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").action(e=>{let t=e.mode,s=F(e.dir,t),o=s.summary(),i=s.getSharedStore().listEntries().length;console.log(),console.log(u.bold(` Wiki Hub Status [${he(t)}]`)),console.log(),console.log(` Total raw entries: ${i}`),console.log(` Agents: ${o.length}`),console.log();for(let c of o){let a=c.unabsorbed>0?u.yellow(`${c.unabsorbed} unabsorbed`):u.green("up to date");if(console.log(` ${u.cyan(c.agentId)}`),console.log(` Entries: ${c.totalEntries} Articles: ${c.totalArticles} ${a}`),c.articles.length>0){for(let l of c.articles.slice(0,3))console.log(u.dim(` - ${l.title} [${(l.tags||[]).slice(0,3).join(", ")}]`));c.articles.length>3&&console.log(u.dim(` ... and ${c.articles.length-3} more`))}console.log()}o.reduce((c,a)=>c+a.unabsorbed,0)>0&&(console.log(u.dim(" Run 'agentx wiki absorb' to compile all, or 'agentx wiki absorb --agent <id>' for one agent")),console.log())});H.command("lint").description("check wiki for issues per agent").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","lint a specific agent's wiki").action(e=>{let t=F(e.dir,e.mode),s=e.agent?[e.agent]:t.listAgents(),o=0;console.log();for(let n of s){let r=t.getAgentWiki(n).lint();if(o+=r.length,r.length===0)console.log(` ${u.cyan(n)}: ${u.green("healthy")}`);else{console.log(` ${u.cyan(n)}: ${u.yellow(`${r.length} issues`)}`);for(let c of r){let a=c.type==="broken-link"?"x":c.type==="orphan"?"?":"!";console.log(` [${a}] ${u.dim(c.type)} ${c.article}: ${c.message}`)}}}console.log(),o===0&&console.log(u.green(" All agent wikis are healthy")),console.log()});H.command("absorb").description("compile unabsorbed entries into per-agent wiki articles").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","absorb only this agent").option("--dry-run","preview without running").option("--max <n>","max entries per agent","20").action(async e=>{let t=e.mode,s=F(e.dir,t),o=e.agent?[e.agent]:s.listAgents(),n=parseInt(e.max),i=0;for(let r of o){let c=s.getUnabsorbedEntries(r).slice(0,n);if(c.length===0){console.log(` ${u.cyan(r)}: ${u.green("all absorbed")}`);continue}console.log(),console.log(u.bold(` ${u.cyan(r)}: ${c.length} entries to absorb`));for(let b of c.slice(0,3))console.log(u.dim(` [${b.date} via ${b.source}] ${b.content.slice(0,80)}...`));if(c.length>3&&console.log(u.dim(` ... and ${c.length-3} more`)),e.dryRun)continue;let a=s.getAgentWiki(r),l=a.rebuildIndex(),h=a.getWorldview()||s.getSharedStore().getWorldview()||"",p=c.map(b=>`--- ENTRY ${b.id} [${b.date} ${b.agentId} via ${b.source}] ---
287
284
  ${b.content}
288
285
  --- END ENTRY ---`).join(`
289
286
 
290
- `),g=Ce(t,r,h,d.articles,p,c.length);console.log(u.dim(` Mode: ${ue(t)}`));let y=ee(a.baseDir,"_tmp");Ye(y,{recursive:!0});let f=ee(y,"absorb-prompt.txt");_e(f,g),console.log(u.dim(" Compiling with Claude..."));try{let b;try{b=Je(`cat '${f}' | claude -p - --output-format json --max-turns 3 --model sonnet --disallowedTools "Bash Read Write Edit Glob Grep Agent WebSearch WebFetch NotebookEdit"`,{encoding:"utf-8",timeout:3e5,maxBuffer:10*1024*1024})}catch(x){if(b=x.stdout||"",!b)throw x}let w=b;try{let x=JSON.parse(b);w=x.result||x.content||"",w||(console.log(u.dim(` Claude envelope keys: ${Object.keys(x).join(", ")}`)),console.log(u.dim(` is_error: ${x.is_error}, stop_reason: ${x.stop_reason}`)))}catch{}let k,S=[],v=w.indexOf("{"),A=w.indexOf("["),P=v>=0&&(A<0||v<A)?v:A;if(P===-1){console.log(u.red(" No JSON found in response")),console.log(u.dim(w.slice(0,500)));continue}let G=w[P],E=G==="{"?"}":"]",T=0,C=-1;for(let x=P;x<w.length;x++)if(w[x]===G)T++;else if(w[x]===E&&(T--,T===0)){C=x+1;break}if(C===-1){console.log(u.red(" Unbalanced JSON"));continue}let te=w.slice(P,C);try{let x=JSON.parse(te);Array.isArray(x)?k=x:(k=x.articles||[],S=x.gaps||[])}catch(x){console.log(u.red(` JSON parse error: ${x.message}`)),console.log(u.dim(` First 300 chars: ${te.slice(0,300)}`));continue}for(let x of k){let fe=new Date().toISOString().slice(0,10);a.writeArticle(x.path,{title:x.title,tags:x.tags||[],owner:r,access:"public",created:fe,lastUpdated:fe,sources:x.sources||[]},x.content,r);let be=(x.tags||[]).slice(0,5).join(", ");console.log(` ${u.green("+")} ${x.path}: ${x.title}`),be&&console.log(u.dim(` [${be}]`)),i++}if(S.length>0){console.log(),console.log(u.yellow(` Gaps detected (${S.length} missing pieces):`));for(let x of S)console.log(u.yellow(` ? ${x}`))}a.rebuildIndex()}catch(b){console.log(u.red(` Absorb failed: ${b.message?.slice(0,200)}`)),b.stderr&&console.log(u.dim(String(b.stderr).slice(0,300))),b.stdout&&console.log(u.dim("stdout: "+String(b.stdout).slice(0,300)))}}console.log(),e.dryRun?console.log(u.dim(" Dry run \u2014 no changes made")):i>0&&console.log(u.green(` ${i} articles compiled across ${o.length} agent(s)`)),console.log()});H.command("entries").description("list raw entries").option("--dir <path>","wiki directory").option("--agent <id>","filter by agent").action(e=>{let o=F(e.dir).getSharedStore().listEntries();e.agent&&(o=o.filter(n=>n.agentId===e.agent)),console.log(),console.log(u.bold(` ${o.length} raw entries`)),console.log();for(let n of o.slice(-20))console.log(` ${u.dim(n.date)} ${u.cyan(n.agentId)} via ${n.source}: ${n.content.slice(0,80)}...`);o.length>20&&console.log(u.dim(` ... showing last 20 of ${o.length}`)),console.log()});H.command("serve").description("start a local web server to browse agent wikis (local + mesh)").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","serve only this agent's wiki").option("--port <n>","port number","4200").option("--peer <urls...>","mesh peer URLs to federate").action(async e=>{let t=e.dir||ee(process.cwd(),".agentx/wiki"),s=parseInt(e.port),o=e.peer||[];if(o.length===0)try{let{loadDaemonConfig:n}=await import("./config-MSWKC466.js");o=(n().mesh?.peers||[]).map(r=>r.url)}catch{}console.log(),console.log(u.bold(" AgentX Wiki Server")),console.log(),console.log(` ${u.green(">")} http://localhost:${s}`),e.agent?console.log(` Agent: ${u.cyan(e.agent)}`):console.log(` Mode: ${u.cyan("Hub")} (all agents)`),o.length>0&&console.log(` Mesh: ${u.cyan(o.length+" peer(s)")} \u2014 ${o.join(", ")}`),console.log(u.dim(` Wiki: ${t}`)),console.log(u.dim(" Press Ctrl+C to stop")),console.log(),Ie(t,s,e.agent,o,e.mode)});H.command("search <query>").description("search wiki articles").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","search specific agent's wiki").action((e,t)=>{let s=F(t.dir,t.mode),o=t.agent?[t.agent]:s.listAgents();console.log();let n=0;for(let i of o){let c=s.getAgentWiki(i).findRelevant(e,void 0,10);if(c.length>0){console.log(u.bold(` ${u.cyan(i)}:`));for(let a of c)console.log(` ${a.meta.title} [${(a.meta.tags||[]).slice(0,3).join(", ")}]`),console.log(u.dim(` ${a.path} \u2014 ${a.content.slice(0,100)}...`));n+=c.length}}n===0&&console.log(u.dim(" No matching articles")),console.log()});H.command("sync").description("pull raw entries from mesh peers into local wiki").option("--dir <path>","wiki directory").option("--peer <url>","sync from a specific peer URL (e.g., http://100.67.108.119:19900)").option("--dry-run","show what would be synced without writing").action(async e=>{let s=F(e.dir).getSharedStore(),o=[];if(e.peer)o=[e.peer];else try{let{loadDaemonConfig:r}=await import("./config-MSWKC466.js");o=(r().mesh?.peers||[]).map(a=>a.url)}catch{console.log(u.red(" No --peer specified and no daemon config found")),console.log(u.dim(" Usage: agentx wiki sync --peer http://100.67.108.119:19900"));return}if(o.length===0){console.log(u.dim(" No mesh peers configured"));return}let n=new Set(s.listEntries().map(r=>r.id)),i=0;for(let r of o){console.log(),console.log(u.bold(` Syncing from ${r}...`));try{let c=await fetch(`${r}/wiki/entries`,{signal:AbortSignal.timeout(1e4)});if(!c.ok){console.log(u.red(` HTTP ${c.status}`));continue}let a=await c.json(),d=a.entries||[],h=a.nodeId||"unknown";console.log(u.dim(` Node: ${h} \u2014 ${d.length} entries`));let p=0;for(let g of d)n.has(g.id)||(e.dryRun||(s.addEntry({id:g.id,date:g.date,agentId:g.agentId,source:`${g.source}@${h}`,sourceContext:g.sourceContext,content:g.content}),n.add(g.id)),p++);p>0?console.log(u.green(` ${p} new entries${e.dryRun?" (dry run)":" synced"}`)):console.log(u.dim(" Already up to date")),i+=p;try{let g=await fetch(`${r}/wiki/agents`,{signal:AbortSignal.timeout(5e3)});if(g.ok){let y=await g.json();for(let f of y.agents||[])f.totalEntries>0&&console.log(u.dim(` ${f.agentId}: ${f.totalEntries} entries, ${f.totalArticles} articles`))}}catch{}}catch(c){c.cause?.code==="ECONNREFUSED"?console.log(u.red(" Connection refused")):console.log(u.red(` ${c.message}`))}}console.log(),i>0&&!e.dryRun?console.log(u.green(` ${i} entries synced. Run 'agentx wiki absorb' to compile.`)):i>0?console.log(u.dim(` ${i} entries would be synced (dry run)`)):console.log(u.dim(" All peers up to date")),console.log()});H.command("compare").description("compare all wiki compilation modes for an agent").option("--dir <path>","wiki directory").requiredOption("--agent <id>","agent to compare").action(e=>{let t=e.dir||ee(process.cwd(),".agentx/wiki"),s=e.agent,n=["flat","graph","unified"].map(a=>{let d=new K(t,void 0,a),p=d.getAgentWiki(s).rebuildIndex(),g=d.getAgentEntries(s),y=d.getUnabsorbedEntries(s),f=new Set,b=0,w=new Set;for(let S of p.articles){for(let v of S.tags||[])f.add(v);b+=S.tags?.length||0;for(let v of S.sources||[])w.add(v)}let k=new Set(p.articles.map(S=>S.path.includes("/")?S.path.split("/")[0]:"/"));return{mode:a,label:ue(a),articles:p.articles.length,entries:g.length,unabsorbed:y.length,uniqueTags:f.size,avgTags:p.articles.length>0?(b/p.articles.length).toFixed(1):"0",dirs:k.size,coverage:w.size,articleList:p.articles}});console.log(),console.log(u.bold(` Wiki Compare: ${s}`)),console.log(` Raw entries: ${n[0].entries}`),console.log();let i=22;console.log(` ${"".padEnd(20)} ${n.map(a=>a.label.padEnd(i)).join(" ")}`),console.log(` ${"\u2500".repeat(20)} ${n.map(()=>"\u2500".repeat(i)).join(" ")}`);let r=[["Articles",a=>String(a.articles)],["Unabsorbed",a=>String(a.unabsorbed)],["Unique tags",a=>String(a.uniqueTags)],["Avg tags/article",a=>a.avgTags],["Directories",a=>String(a.dirs)],["Entry coverage",a=>`${a.coverage}/${a.entries}`]];for(let[a,d]of r){let h=n.map(p=>d(p).padEnd(i));console.log(` ${a.padEnd(20)} ${h.join(" ")}`)}console.log();let c=(a,d)=>{let h=Math.max(...n.map(g=>g[a])),p=n.filter(g=>g[a]===h).map(g=>g.label);h>0&&console.log(u.dim(` Best ${d}: ${p.join(", ")} (${h})`))};c("uniqueTags","tag richness"),c("articles","article count"),c("coverage","entry coverage"),console.log()});process.on("SIGINT",()=>process.exit(0));process.on("SIGTERM",()=>process.exit(0));async function Ke(){let e=await $e(),t=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(t.addCommand(L).addCommand(ve).addCommand(X).addCommand(ie).addCommand(Y).addCommand(V).addCommand(re).addCommand(me).addCommand(he).addCommand(ce).addCommand(Q).addCommand(H),process.argv.slice(2).length===0){t.outputHelp();return}t.parse()}Ke();
287
+ `),g=Ce(t,r,h,l.articles,p,c.length);console.log(u.dim(` Mode: ${he(t)}`));let y=ee(a.baseDir,"_tmp");Ye(y,{recursive:!0});let f=ee(y,"absorb-prompt.txt");_e(f,g),console.log(u.dim(" Compiling with Claude..."));try{let b;try{b=Je(`cat '${f}' | claude -p - --output-format json --max-turns 3 --model sonnet --disallowedTools "Bash Read Write Edit Glob Grep Agent WebSearch WebFetch NotebookEdit"`,{encoding:"utf-8",timeout:3e5,maxBuffer:10*1024*1024})}catch(x){if(b=x.stdout||"",!b)throw x}let $=b;try{let x=JSON.parse(b);$=x.result||x.content||"",$||(console.log(u.dim(` Claude envelope keys: ${Object.keys(x).join(", ")}`)),console.log(u.dim(` is_error: ${x.is_error}, stop_reason: ${x.stop_reason}`)))}catch{}let k,S=[],v=$.indexOf("{"),A=$.indexOf("["),P=v>=0&&(A<0||v<A)?v:A;if(P===-1){console.log(u.red(" No JSON found in response")),console.log(u.dim($.slice(0,500)));continue}let G=$[P],E=G==="{"?"}":"]",T=0,C=-1;for(let x=P;x<$.length;x++)if($[x]===G)T++;else if($[x]===E&&(T--,T===0)){C=x+1;break}if(C===-1){console.log(u.red(" Unbalanced JSON"));continue}let te=$.slice(P,C);try{let x=JSON.parse(te);Array.isArray(x)?k=x:(k=x.articles||[],S=x.gaps||[])}catch(x){console.log(u.red(` JSON parse error: ${x.message}`)),console.log(u.dim(` First 300 chars: ${te.slice(0,300)}`));continue}for(let x of k){let ue=new Date().toISOString().slice(0,10);a.writeArticle(x.path,{title:x.title,tags:x.tags||[],owner:r,access:"public",created:ue,lastUpdated:ue,sources:x.sources||[]},x.content,r);let fe=(x.tags||[]).slice(0,5).join(", ");console.log(` ${u.green("+")} ${x.path}: ${x.title}`),fe&&console.log(u.dim(` [${fe}]`)),i++}if(S.length>0){console.log(),console.log(u.yellow(` Gaps detected (${S.length} missing pieces):`));for(let x of S)console.log(u.yellow(` ? ${x}`))}a.rebuildIndex()}catch(b){console.log(u.red(` Absorb failed: ${b.message?.slice(0,200)}`)),b.stderr&&console.log(u.dim(String(b.stderr).slice(0,300))),b.stdout&&console.log(u.dim("stdout: "+String(b.stdout).slice(0,300)))}}console.log(),e.dryRun?console.log(u.dim(" Dry run \u2014 no changes made")):i>0&&console.log(u.green(` ${i} articles compiled across ${o.length} agent(s)`)),console.log()});H.command("entries").description("list raw entries").option("--dir <path>","wiki directory").option("--agent <id>","filter by agent").action(e=>{let o=F(e.dir).getSharedStore().listEntries();e.agent&&(o=o.filter(n=>n.agentId===e.agent)),console.log(),console.log(u.bold(` ${o.length} raw entries`)),console.log();for(let n of o.slice(-20))console.log(` ${u.dim(n.date)} ${u.cyan(n.agentId)} via ${n.source}: ${n.content.slice(0,80)}...`);o.length>20&&console.log(u.dim(` ... showing last 20 of ${o.length}`)),console.log()});H.command("serve").description("start a local web server to browse agent wikis (local + mesh)").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","serve only this agent's wiki").option("--port <n>","port number","4200").option("--peer <urls...>","mesh peer URLs to federate").action(async e=>{let t=e.dir||ee(process.cwd(),".agentx/wiki"),s=parseInt(e.port),o=e.peer||[];if(o.length===0)try{let{loadDaemonConfig:n}=await import("./config-HAD5E4YX.js");o=(n().mesh?.peers||[]).map(r=>r.url)}catch{}console.log(),console.log(u.bold(" AgentX Wiki Server")),console.log(),console.log(` ${u.green(">")} http://localhost:${s}`),e.agent?console.log(` Agent: ${u.cyan(e.agent)}`):console.log(` Mode: ${u.cyan("Hub")} (all agents)`),o.length>0&&console.log(` Mesh: ${u.cyan(o.length+" peer(s)")} \u2014 ${o.join(", ")}`),console.log(u.dim(` Wiki: ${t}`)),console.log(u.dim(" Press Ctrl+C to stop")),console.log(),Ie(t,s,e.agent,o,e.mode)});H.command("search <query>").description("search wiki articles").option("--dir <path>","wiki directory").option("--mode <mode>","unified (default), graph, or flat","unified").option("--agent <id>","search specific agent's wiki").action((e,t)=>{let s=F(t.dir,t.mode),o=t.agent?[t.agent]:s.listAgents();console.log();let n=0;for(let i of o){let c=s.getAgentWiki(i).findRelevant(e,void 0,10);if(c.length>0){console.log(u.bold(` ${u.cyan(i)}:`));for(let a of c)console.log(` ${a.meta.title} [${(a.meta.tags||[]).slice(0,3).join(", ")}]`),console.log(u.dim(` ${a.path} \u2014 ${a.content.slice(0,100)}...`));n+=c.length}}n===0&&console.log(u.dim(" No matching articles")),console.log()});H.command("sync").description("pull raw entries from mesh peers into local wiki").option("--dir <path>","wiki directory").option("--peer <url>","sync from a specific peer URL (e.g., http://100.67.108.119:19900)").option("--dry-run","show what would be synced without writing").action(async e=>{let s=F(e.dir).getSharedStore(),o=[];if(e.peer)o=[e.peer];else try{let{loadDaemonConfig:r}=await import("./config-HAD5E4YX.js");o=(r().mesh?.peers||[]).map(a=>a.url)}catch{console.log(u.red(" No --peer specified and no daemon config found")),console.log(u.dim(" Usage: agentx wiki sync --peer http://100.67.108.119:19900"));return}if(o.length===0){console.log(u.dim(" No mesh peers configured"));return}let n=new Set(s.listEntries().map(r=>r.id)),i=0;for(let r of o){console.log(),console.log(u.bold(` Syncing from ${r}...`));try{let c=await fetch(`${r}/wiki/entries`,{signal:AbortSignal.timeout(1e4)});if(!c.ok){console.log(u.red(` HTTP ${c.status}`));continue}let a=await c.json(),l=a.entries||[],h=a.nodeId||"unknown";console.log(u.dim(` Node: ${h} \u2014 ${l.length} entries`));let p=0;for(let g of l)n.has(g.id)||(e.dryRun||(s.addEntry({id:g.id,date:g.date,agentId:g.agentId,source:`${g.source}@${h}`,sourceContext:g.sourceContext,content:g.content}),n.add(g.id)),p++);p>0?console.log(u.green(` ${p} new entries${e.dryRun?" (dry run)":" synced"}`)):console.log(u.dim(" Already up to date")),i+=p;try{let g=await fetch(`${r}/wiki/agents`,{signal:AbortSignal.timeout(5e3)});if(g.ok){let y=await g.json();for(let f of y.agents||[])f.totalEntries>0&&console.log(u.dim(` ${f.agentId}: ${f.totalEntries} entries, ${f.totalArticles} articles`))}}catch{}}catch(c){c.cause?.code==="ECONNREFUSED"?console.log(u.red(" Connection refused")):console.log(u.red(` ${c.message}`))}}console.log(),i>0&&!e.dryRun?console.log(u.green(` ${i} entries synced. Run 'agentx wiki absorb' to compile.`)):i>0?console.log(u.dim(` ${i} entries would be synced (dry run)`)):console.log(u.dim(" All peers up to date")),console.log()});H.command("compare").description("compare all wiki compilation modes for an agent").option("--dir <path>","wiki directory").requiredOption("--agent <id>","agent to compare").action(e=>{let t=e.dir||ee(process.cwd(),".agentx/wiki"),s=e.agent,n=["flat","graph","unified"].map(a=>{let l=new K(t,void 0,a),p=l.getAgentWiki(s).rebuildIndex(),g=l.getAgentEntries(s),y=l.getUnabsorbedEntries(s),f=new Set,b=0,$=new Set;for(let S of p.articles){for(let v of S.tags||[])f.add(v);b+=S.tags?.length||0;for(let v of S.sources||[])$.add(v)}let k=new Set(p.articles.map(S=>S.path.includes("/")?S.path.split("/")[0]:"/"));return{mode:a,label:he(a),articles:p.articles.length,entries:g.length,unabsorbed:y.length,uniqueTags:f.size,avgTags:p.articles.length>0?(b/p.articles.length).toFixed(1):"0",dirs:k.size,coverage:$.size,articleList:p.articles}});console.log(),console.log(u.bold(` Wiki Compare: ${s}`)),console.log(` Raw entries: ${n[0].entries}`),console.log();let i=22;console.log(` ${"".padEnd(20)} ${n.map(a=>a.label.padEnd(i)).join(" ")}`),console.log(` ${"\u2500".repeat(20)} ${n.map(()=>"\u2500".repeat(i)).join(" ")}`);let r=[["Articles",a=>String(a.articles)],["Unabsorbed",a=>String(a.unabsorbed)],["Unique tags",a=>String(a.uniqueTags)],["Avg tags/article",a=>a.avgTags],["Directories",a=>String(a.dirs)],["Entry coverage",a=>`${a.coverage}/${a.entries}`]];for(let[a,l]of r){let h=n.map(p=>l(p).padEnd(i));console.log(` ${a.padEnd(20)} ${h.join(" ")}`)}console.log();let c=(a,l)=>{let h=Math.max(...n.map(g=>g[a])),p=n.filter(g=>g[a]===h).map(g=>g.label);h>0&&console.log(u.dim(` Best ${l}: ${p.join(", ")} (${h})`))};c("uniqueTags","tag richness"),c("articles","article count"),c("coverage","entry coverage"),console.log()});process.on("SIGINT",()=>process.exit(0));process.on("SIGTERM",()=>process.exit(0));async function Ke(){let e=await ye(),t=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(t.addCommand(L).addCommand(ke).addCommand(X).addCommand(ae).addCommand(Y).addCommand(V).addCommand(ie).addCommand(pe).addCommand(me).addCommand(re).addCommand(Q).addCommand(H),process.argv.slice(2).length===0){t.outputHelp();return}t.parse()}Ke();
291
288
  //# sourceMappingURL=cli.js.map