agentix-cli 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-57MG4LBM.js → agent-Y4U4R3ZZ.js} +2 -2
- package/dist/{board-dashboard-HXDOYGIG.js → board-dashboard-2CPBWFOB.js} +339 -97
- package/dist/board-dashboard-2CPBWFOB.js.map +1 -0
- package/dist/chunk-2HXSJYAJ.js +15 -0
- package/dist/chunk-2HXSJYAJ.js.map +1 -0
- package/dist/{chunk-QW2OYR3N.js → chunk-ABVZHJUO.js} +2 -2
- package/dist/chunk-BHSPAP4U.js +44 -0
- package/dist/chunk-BHSPAP4U.js.map +1 -0
- package/dist/chunk-CO4YCIIA.js +13 -0
- package/dist/chunk-CO4YCIIA.js.map +1 -0
- package/dist/{chunk-4MREHZP5.js → chunk-GOLZ6NEL.js} +2 -2
- package/dist/chunk-GOLZ6NEL.js.map +1 -0
- package/dist/chunk-GSX4NK2N.js +2 -0
- package/dist/chunk-GSX4NK2N.js.map +1 -0
- package/dist/chunk-GV7S5BAX.js +2 -0
- package/dist/chunk-GV7S5BAX.js.map +1 -0
- package/dist/{chunk-WPPUOTY6.js → chunk-KX626FN4.js} +2 -2
- package/dist/chunk-OPKQNLGX.js +450 -0
- package/dist/chunk-OPKQNLGX.js.map +1 -0
- package/dist/chunk-OZOQKPJV.js +102 -0
- package/dist/chunk-OZOQKPJV.js.map +1 -0
- package/dist/{chunk-DLVOHCTA.js → chunk-P3TJHGO5.js} +2 -2
- package/dist/chunk-SZDACK6O.js +2 -0
- package/dist/chunk-SZDACK6O.js.map +1 -0
- package/dist/{chunk-TC3BSUP7.js → chunk-VYLZ7UYM.js} +2 -2
- package/dist/{chunk-WFHIIVLL.js → chunk-WHP4I44J.js} +75 -51
- package/dist/chunk-WHP4I44J.js.map +1 -0
- package/dist/chunk-X5D5AV5N.js +833 -0
- package/dist/chunk-X5D5AV5N.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/{config-QXWKEQON.js → config-BLOKGMER.js} +2 -2
- package/dist/config-mutator-RG7QY2EL.js +2 -0
- package/dist/doctor-ZTECTUHX.js +2 -0
- package/dist/index.d.ts +140 -3
- package/dist/index.js +1 -1
- package/dist/program-GZOJIWTB.js +2 -0
- package/dist/registry-QZIUSQ5J.js +2 -0
- package/dist/runner-BFYDWPUO.js +2 -0
- package/dist/scheduler-B2BQPV4M.js +2 -0
- package/dist/store-I3HEWVLQ.js +2 -0
- package/dist/store-I3HEWVLQ.js.map +1 -0
- package/dist/types-Y6ZCZ52Z.js +2 -0
- package/dist/types-Y6ZCZ52Z.js.map +1 -0
- package/dist/web/workflow-editor.global.js +12 -10
- package/dist/web/workflow-editor.global.js.map +1 -1
- package/dist/workspace-setup-DQCZX2NE.js +2 -0
- package/dist/workspace-setup-DQCZX2NE.js.map +1 -0
- package/package.json +1 -1
- package/dist/board-dashboard-HXDOYGIG.js.map +0 -1
- package/dist/chunk-23AUU4S2.js +0 -102
- package/dist/chunk-23AUU4S2.js.map +0 -1
- package/dist/chunk-4MREHZP5.js.map +0 -1
- package/dist/chunk-6B74TO2J.js +0 -743
- package/dist/chunk-6B74TO2J.js.map +0 -1
- package/dist/chunk-7GVXCOLO.js +0 -440
- package/dist/chunk-7GVXCOLO.js.map +0 -1
- package/dist/chunk-E4ZZILAK.js +0 -15
- package/dist/chunk-E4ZZILAK.js.map +0 -1
- package/dist/chunk-MBOLXKA2.js +0 -6
- package/dist/chunk-MBOLXKA2.js.map +0 -1
- package/dist/chunk-WFHIIVLL.js.map +0 -1
- package/dist/chunk-ZE2YFX4K.js +0 -45
- package/dist/chunk-ZE2YFX4K.js.map +0 -1
- package/dist/config-mutator-53XGR2NT.js +0 -2
- package/dist/doctor-SEOUFHSL.js +0 -2
- package/dist/program-WWCXMRU2.js +0 -2
- package/dist/registry-HM4ELSXS.js +0 -2
- package/dist/scheduler-XVFIK4XH.js +0 -2
- package/dist/workspace-setup-FPGN5VNY.js +0 -2
- /package/dist/{agent-57MG4LBM.js.map → agent-Y4U4R3ZZ.js.map} +0 -0
- /package/dist/{chunk-QW2OYR3N.js.map → chunk-ABVZHJUO.js.map} +0 -0
- /package/dist/{chunk-WPPUOTY6.js.map → chunk-KX626FN4.js.map} +0 -0
- /package/dist/{chunk-DLVOHCTA.js.map → chunk-P3TJHGO5.js.map} +0 -0
- /package/dist/{chunk-TC3BSUP7.js.map → chunk-VYLZ7UYM.js.map} +0 -0
- /package/dist/{config-QXWKEQON.js.map → config-BLOKGMER.js.map} +0 -0
- /package/dist/{config-mutator-53XGR2NT.js.map → config-mutator-RG7QY2EL.js.map} +0 -0
- /package/dist/{doctor-SEOUFHSL.js.map → doctor-ZTECTUHX.js.map} +0 -0
- /package/dist/{program-WWCXMRU2.js.map → program-GZOJIWTB.js.map} +0 -0
- /package/dist/{registry-HM4ELSXS.js.map → registry-QZIUSQ5J.js.map} +0 -0
- /package/dist/{scheduler-XVFIK4XH.js.map → runner-BFYDWPUO.js.map} +0 -0
- /package/dist/{workspace-setup-FPGN5VNY.js.map → scheduler-B2BQPV4M.js.map} +0 -0
package/dist/chunk-MBOLXKA2.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{z as e}from"zod";import{readFileSync as y,existsSync as u}from"fs";import{resolve as p}from"path";import{z as t}from"zod";var x=t.enum(["mon","tue","wed","thu","fri","sat","sun"]),k=t.object({days:t.array(x).default(["mon","tue","wed","thu","fri"]),start:t.string().regex(/^\d{2}:\d{2}$/),end:t.string().regex(/^\d{2}:\d{2}$/),lunch:t.object({start:t.string().regex(/^\d{2}:\d{2}$/),end:t.string().regex(/^\d{2}:\d{2}$/)}).optional()}),S=t.object({title:t.string(),responsibilities:t.array(t.string()).default([]),sopPath:t.string().optional(),kpis:t.array(t.string()).default([])}),j=t.object({role:t.string(),reportsTo:t.string().optional(),schedule:k,utilizationTarget:t.number().min(0).max(1).default(.8)}),w=t.object({channel:t.string(),chatId:t.string(),accountId:t.string().optional()}),C=t.discriminatedUnion("type",[t.object({type:t.literal("backlog"),path:t.string().default(".agentx/backlog.md")}),t.object({type:t.literal("gitlab"),projects:t.array(t.string()).default([])}),t.object({type:t.literal("wiki"),path:t.string(),glob:t.string().default("**/*.md")})]),v=t.object({id:t.string(),pm:t.string().optional(),client:t.string().optional()}),T=t.object({channel:t.string().optional(),chatId:t.string().optional(),username:t.string().optional(),senderId:t.string().optional(),client:t.string(),project:t.string().optional(),displayName:t.string().optional()}),b=t.object({enabled:t.boolean().default(!1),timezone:t.string().default("UTC"),mainChannel:w,workSource:C,roles:t.record(t.string(),S).default({}),orgChart:t.record(t.string(),j).default({}),projects:t.array(v).default([]),contactMap:t.array(T).default([]),workTickMinutes:t.number().int().min(1).max(60).default(15),idleQueueThreshold:t.number().int().min(0).default(0)});import{z as n}from"zod";var L=n.object({name:n.string(),color:n.string().regex(/^#[0-9a-f]{6}$/i).default("#6366f1"),description:n.string().optional()}),z=n.object({id:n.string().regex(/^[a-z0-9_-]+$/,"column id must be lowercase slug"),title:n.string(),kind:n.enum(["open-backlog","scoped-label","closed","label"]).default("label"),mapsToLabel:n.string().optional(),scopedLabel:n.string().optional(),scopedPrefix:n.string().default("Status"),accent:n.string().optional()}),I=n.object({enabled:n.boolean().default(!0),staleDoingMinutes:n.number().int().min(1).default(45),respectLunchBreak:n.boolean().default(!0),respectSchedule:n.boolean().default(!0),action:n.enum(["badge","notify"]).default("badge")}),D=n.discriminatedUnion("type",[n.object({type:n.literal("gitlab"),projects:n.array(n.string()).min(1)})]),B=[{id:"open",title:"Open",kind:"open-backlog",scopedPrefix:"Status",accent:"#6b7280"},{id:"todo",title:"To Do",kind:"scoped-label",scopedLabel:"Status::To Do",scopedPrefix:"Status",accent:"#fb7a35"},{id:"doing",title:"Doing",kind:"scoped-label",scopedLabel:"Status::Doing",scopedPrefix:"Status",accent:"#22c55e"},{id:"onhold",title:"On Hold",kind:"scoped-label",scopedLabel:"Status::On Hold",scopedPrefix:"Status",accent:"#f59e0b"},{id:"review",title:"Review",kind:"scoped-label",scopedLabel:"Status::Review",scopedPrefix:"Status",accent:"#3b82f6"},{id:"closed",title:"Closed",kind:"closed",scopedPrefix:"Status",accent:"#6b7280"}],$=n.object({id:n.string().regex(/^[a-z0-9_-]+$/,"board id must be lowercase slug"),name:n.string(),source:D,primaryToolLabel:n.string().optional(),labels:n.array(L).default([]),columns:n.array(z).min(1).default(B),timeRangeDays:n.number().int().min(1).max(365).default(30),closedWindowDays:n.number().int().min(1).max(365).default(30),reconciliation:I.default({})}),m=n.array($).default([]),P=n.object({name:n.string(),url:n.string(),dashboardUrl:n.string().optional(),token:n.string().optional(),dashboardToken:n.string().optional()}),h=n.object({enabled:n.boolean().default(!1),port:n.number().int().default(4202),bind:n.string().default("127.0.0.1"),token:n.string().optional(),daemonUrl:n.string().default("http://localhost:18800"),daemons:n.array(P).default([]),draftAgent:n.string().optional()}).default({});function V(a,r){let s=new Set(a.labels||[]);if(a.state==="closed"){let o=r.find(l=>l.kind==="closed");if(o)return o.id}for(let o of r)if(o.kind==="scoped-label"&&o.scopedLabel&&s.has(o.scopedLabel))return o.id;for(let o of r)if(o.kind==="label"&&o.mapsToLabel&&s.has(o.mapsToLabel))return o.id;let i=r.find(o=>o.kind==="open-backlog");if(i){let o=i.scopedPrefix+"::";return[...s].some(d=>d.startsWith(o))?null:i.id}return r[0]?.id??null}function X(a,r){let s=[],i=[],o=!1,l=!1;return r.kind==="scoped-label"&&r.scopedLabel?s.push(r.scopedLabel):r.kind==="label"&&r.mapsToLabel?s.push(r.mapsToLabel):r.kind==="closed"&&(o=!0),a&&(a.kind==="scoped-label"&&a.scopedLabel?i.push(a.scopedLabel):a.kind==="label"&&a.mapsToLabel?i.push(a.mapsToLabel):a.kind==="closed"&&r.kind!=="closed"&&(l=!0)),{addLabels:s.length?s:void 0,removeLabels:i.length?i:void 0,closeIssue:o||void 0,reopen:l||void 0}}function A(a){let r=p(a,".env");if(!u(r))return;let s=y(r,"utf-8");for(let i of s.split(`
|
|
2
|
-
`)){let o=i.trim();if(!o||o.startsWith("#"))continue;let l=o.indexOf("=");if(l===-1)continue;let d=o.slice(0,l).trim(),c=o.slice(l+1).trim();process.env[d]||(process.env[d]=c)}}var M=e.object({apiKey:e.string().optional(),defaultModel:e.string().optional(),baseUrl:e.string().optional()}),R=e.object({command:e.string(),args:e.array(e.string()).default([]),env:e.record(e.string(),e.string()).optional()}),E=e.object({name:e.string(),workspace:e.string(),tier:e.enum(["claude-code","sdk","orchestrator"]).default("claude-code"),provider:e.string().optional(),model:e.string().optional(),systemPrompt:e.string().optional(),mentions:e.array(e.string()).default([]),intents:e.array(e.string()).default([]),maxDelegationDepth:e.number().int().min(0).max(50).default(5),mcp:e.record(e.string(),R).optional(),contextStrategy:e.enum(["layered","planner"]).optional(),contextReferences:e.boolean().default(!1),maxConcurrent:e.number().default(1),maxExecutionMinutes:e.number().int().min(1).max(240).default(20),permissionMode:e.string().default("default"),queueMode:e.enum(["collect","followup","drop"]).default("collect"),heartbeat:e.object({enabled:e.boolean().default(!1),intervalMinutes:e.number().default(30),prompt:e.string().default("Check inbox, pending tasks, and system health. Report anything that needs attention."),channel:e.string().default("heartbeat")}).default({}),access:e.enum(["private","public"]).default("private")}),O=e.object({token:e.string(),agentBinding:e.string(),allowFrom:e.array(e.string()).optional(),pollInbound:e.boolean().default(!0)}),U=e.object({telegram:e.object({enabled:e.boolean().default(!1),accounts:e.record(e.string(),O).default({}),policy:e.object({dm:e.enum(["pair","block"]).default("pair"),group:e.enum(["mention-required","all"]).default("mention-required"),allowFrom:e.array(e.string()).optional()}).default({})}).default({}),whatsapp:e.object({enabled:e.boolean().default(!1),sessionDir:e.string().default(".agentx/whatsapp-sessions"),defaultAgent:e.string().optional(),allowFrom:e.array(e.string()).optional(),routes:e.array(e.object({contact:e.string().optional(),group:e.string().optional(),agent:e.string()})).default([]),ingest:e.object({enabled:e.boolean().default(!1),mode:e.enum(["metadata-only","messages"]).default("metadata-only"),allowContacts:e.array(e.string()).default([]),allowGroups:e.array(e.string()).default([]),denyContacts:e.array(e.string()).default([]),denyGroups:e.array(e.string()).default([]),messageCap:e.number().int().min(1).max(500).default(50),historyDays:e.number().int().min(1).max(365).default(30),contactRefreshDays:e.number().int().min(1).max(90).default(7),throttle:e.object({minMsBetweenCalls:e.number().int().min(100).default(1500),maxCallsPerMinute:e.number().int().min(1).default(20),maxChatsPerSweep:e.number().int().min(1).default(25)}).default({}),retentionDays:e.number().int().min(0).default(0)}).default({})}).default({}),discord:e.object({enabled:e.boolean().default(!1),token:e.string().optional(),agentBinding:e.string().optional()}).default({}),slack:e.object({enabled:e.boolean().default(!1),botToken:e.string().optional(),appToken:e.string().optional(),agentBinding:e.string().optional()}).default({}),gitlab:e.object({enabled:e.boolean().default(!1),webhookPort:e.number().default(18810),webhookSecret:e.string().optional(),host:e.string().default("https://gitlab.com"),token:e.string().optional(),routes:e.array(e.object({project:e.string(),agent:e.string()})).default([]),agentMappings:e.array(e.object({agentId:e.string(),gitlabUsernames:e.array(e.string()).default([]),keywords:e.array(e.string()).default([]),token:e.string().optional(),node:e.string().optional()})).default([])}).default({}),github:e.object({enabled:e.boolean().default(!1),token:e.string().optional(),tokenFile:e.string().optional(),appId:e.number().optional(),clientId:e.string().optional(),privateKeyFile:e.string().optional(),webhookSecret:e.string().optional(),routes:e.array(e.object({repo:e.string(),agent:e.string()})).default([]),agentMappings:e.array(e.object({agentId:e.string(),githubUsernames:e.array(e.string()).default([]),token:e.string().optional(),tokenFile:e.string().optional(),node:e.string().optional()})).default([])}).default({}),webrtc:e.object({enabled:e.boolean().default(!1),stunServers:e.array(e.string()).default(["stun:stun.l.google.com:19302"]),turnServers:e.array(e.object({urls:e.string(),username:e.string().optional(),credential:e.string().optional()})).default([]),allowedCallers:e.array(e.string()).default([]),ringNotify:e.array(e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()})).default([]),callUrlBase:e.string().optional(),bot:e.object({enabled:e.boolean().default(!1),defaultAgentId:e.string().optional(),whisperBackend:e.enum(["auto","mlx","openai"]).default("auto"),whisperModel:e.string().optional(),whisperLanguage:e.string().default("auto"),mlxBinary:e.string().optional(),transcriptChannel:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional(),maxCallMinutes:e.number().int().min(1).max(240).default(30)}).default({})}).default({})}),_=e.object({enabled:e.boolean().default(!0),schedule:e.string(),timezone:e.string().default("UTC"),agent:e.string(),prompt:e.string(),timeout:e.number().default(600),model:e.string().optional(),maxOutputTokens:e.number().int().min(50).max(8e3).optional(),onError:e.union([e.enum(["log","notify","disable"]),e.array(e.enum(["log","notify","disable"]))]).default("log").transform(a=>Array.isArray(a)?a:[a]),notify:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional()}),F=e.object({name:e.string(),triggers:e.array(e.object({pattern:e.string(),channel:e.string().optional()})),allowedContacts:e.array(e.string()).optional(),agent:e.string(),prompt:e.string(),schedule:e.string().optional(),timezone:e.string().default("UTC"),notify:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional()}),N=e.object({url:e.string(),name:e.string(),token:e.string().optional()}),W=e.object({enabled:e.boolean().default(!1),peers:e.array(N).default([]),discovery:e.enum(["static","mdns"]).default("static"),healthCheck:e.object({interval:e.number().default(60),timeout:e.number().default(10)}).default({})}),J=e.object({enabled:e.boolean().default(!1),baseDir:e.string().default(".agentx/graph"),draftAgent:e.string().optional(),reviewAgent:e.string().optional(),autoApproveStructure:e.enum(["strict","extend-leaves","any"]).default("extend-leaves"),autoApproveConfidence:e.number().min(0).max(1).default(1),classifierModel:e.string().default("claude-haiku-4-5-20251001"),retrievalWeights:e.object({graph:e.number().min(0).default(.6),bm25:e.number().min(0).default(.4)}).default({})}).default({}),q=e.object({longTaskThreshold:e.number().default(30),destination:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional(),on:e.object({taskComplete:e.boolean().default(!0),taskError:e.boolean().default(!0),taskQueued:e.boolean().default(!1)}).default({})}).default({}),K=e.object({node:e.object({id:e.string(),name:e.string(),bind:e.string().default("127.0.0.1:18800"),defaultAgent:e.string().optional()}),providers:e.record(e.string(),M).default({}),agents:e.record(e.string(),E).default({}),channels:U.default({}),crons:e.record(e.string(),_).default({}),services:e.record(e.string(),F).default({}),notifications:q,mesh:W.default({}),business:b.optional(),boards:m,dashboard:h,graph:J,workflows:e.object({enabled:e.boolean().default(!1),dir:e.string().default(".agentx/workflows"),editor:e.enum(["disabled","readonly","edit"]).default("edit")}).default({}),webhooks:e.array(e.object({id:e.string().regex(/^[a-z0-9][a-z0-9_-]*$/,"webhook id must be lowercase slug"),source:e.enum(["gitlab","github","sentry","stripe","discord","slack","custom"]),agentId:e.string(),secretEnv:e.string().optional(),description:e.string().optional(),enabled:e.boolean().default(!0),node:e.string().optional(),triggers:e.record(e.string(),e.string()).optional(),defaultWorkflow:e.string().optional()})).default([]),session:e.object({staleMinutes:e.number().int().min(1).max(1440).default(45),maxTurnsPerSession:e.number().int().min(2).max(200).default(15),tierTwoThresholdTokens:e.number().int().min(5e4).max(2e5).default(195e3),contextStrategy:e.enum(["layered","planner"]).default("layered"),maxClaudeCodeDispatchesPerHour:e.number().int().min(1).max(1e4).default(80),maxClaudeCodeDispatchesPer5h:e.number().int().min(1).max(5e4).default(180)}).default({}),plugins:e.array(e.string()).default([])});function f(a){if(typeof a=="string")return a.replace(/\$\{(\w+)\}/g,(r,s)=>process.env[s]||"");if(Array.isArray(a))return a.map(f);if(a!==null&&typeof a=="object"){let r={};for(let[s,i]of Object.entries(a))r[s]=f(i);return r}return a}function oe(a){let r=a?[a]:[p(process.cwd(),"agentx.json"),p(process.cwd(),".agentx/config.json")];A(process.cwd());let s,i;for(let c of r)if(u(c)){s=y(c,"utf-8"),i=c;break}if(!s||!i)throw new Error(`No config found. Create agentx.json or .agentx/config.json
|
|
3
|
-
Searched: ${r.join(", ")}`);let o;try{o=JSON.parse(s)}catch(c){throw new Error(`Invalid JSON in ${i}: ${c.message}`)}let l=f(o),d=K.safeParse(l);if(!d.success){let c=d.error.issues.map(g=>` ${g.path.join(".")}: ${g.message}`).join(`
|
|
4
|
-
`);throw new Error(`Config validation failed (${i}):
|
|
5
|
-
${c}`)}return d.data}function re(a){let r=[];for(let[s,i]of Object.entries(a.agents)){if(!u(i.workspace)){r.push(`Agent "${s}": workspace not found at ${i.workspace}`);continue}if(i.tier==="claude-code"){let d=p(i.workspace,".claude");u(d)||r.push(`Agent "${s}": no .claude/ directory in workspace ${i.workspace}. Claude Code native features (hooks, MCP, skills) won't be available.`)}let o=i.provider||"claude",l=a.providers[o];i.tier!=="claude-code"&&(!l||!l.apiKey)&&r.push(`Agent "${s}": provider "${o}" has no API key configured. Set providers.${o}.apiKey in config or use tier "claude-code" for subscription.`)}for(let[s,i]of Object.entries(a.crons))a.agents[i.agent]||r.push(`Cron "${s}": references unknown agent "${i.agent}"`);if(a.channels.telegram.enabled)for(let[s,i]of Object.entries(a.channels.telegram.accounts))a.agents[i.agentBinding]||r.push(`Telegram account "${s}": references unknown agent "${i.agentBinding}"`);return r}export{V as a,X as b,K as c,f as d,oe as e,re as f};
|
|
6
|
-
//# sourceMappingURL=chunk-MBOLXKA2.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/daemon/config.ts","../src/business/config.ts","../src/boards/config.ts"],"sourcesContent":["import { z } from \"zod\"\nimport { readFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { businessConfigSchema } from \"@/business/config\"\nimport { boardsConfigSchema, dashboardConfigSchema } from \"@/boards/config\"\n\n/**\n * Load .env file into process.env (simple, no dependency).\n */\nfunction loadDotEnv(dir: string): void {\n const envPath = resolve(dir, \".env\")\n if (!existsSync(envPath)) return\n\n const content = readFileSync(envPath, \"utf-8\")\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith(\"#\")) continue\n const eqIdx = trimmed.indexOf(\"=\")\n if (eqIdx === -1) continue\n const key = trimmed.slice(0, eqIdx).trim()\n const value = trimmed.slice(eqIdx + 1).trim()\n if (!process.env[key]) {\n process.env[key] = value\n }\n }\n}\n\n// --- Daemon configuration schema & loader ---\n\nconst providerConfigSchema = z.object({\n apiKey: z.string().optional(),\n defaultModel: z.string().optional(),\n baseUrl: z.string().optional(),\n})\n\nconst mcpServerSchema = z.object({\n command: z.string(),\n args: z.array(z.string()).default([]),\n env: z.record(z.string(), z.string()).optional(),\n})\n\nconst agentConfigSchema = z.object({\n name: z.string(),\n workspace: z.string(),\n tier: z.enum([\"claude-code\", \"sdk\", \"orchestrator\"]).default(\"claude-code\"),\n provider: z.string().optional(),\n model: z.string().optional(),\n systemPrompt: z.string().optional(),\n mentions: z.array(z.string()).default([]),\n /** Phase 5 — typed capabilities (drop-condition fallback). Free-form\n * list of intent strings this agent is allowed to handle. When set,\n * the org-chart `canHandle(agentId, project, intent)` check rejects\n * dispatches with intents not in this list. When empty/unset\n * (the default), the agent is permissive — handles any intent.\n * Intent matching is exact-string for now; a glob/prefix layer can\n * come later if it produces real rejections.\n * Example: [\"issue.opened\", \"issue.commented\", \"merge_request.opened\"]\n * Example: [\"cron.fired\", \"message.received\"] */\n intents: z.array(z.string()).default([]),\n /** Phase 8 — capability-bounded security. Max distinct upstream\n * agents in the delegation chain on the same (project, subject)\n * before a dispatch to THIS agent is refused. The ledger itself\n * provides the chain — `decideAndCommit` walks recent decisions on\n * the subject and counts distinct agents. Default 5; set to 0 to\n * disable for an agent that's always called as the bottom of a\n * chain. The check prevents cascade loops where A → B → A → B → ...\n * blows past sane chain depth. */\n maxDelegationDepth: z.number().int().min(0).max(50).default(5),\n /** MCP servers this agent's Claude Code session should load. Synced\n * to <workspace>/.mcp.json at daemon boot via agent-mcp.ts. Operator\n * edits to .mcp.json are respected (see SyncResult.skipped-operator-owned). */\n mcp: z.record(z.string(), mcpServerSchema).optional(),\n /** Per-agent override for the global `session.contextStrategy`. Lets\n * one agent run `planner` (smaller upfront context, more tool-driven\n * exploration) while siblings stay on `layered`. Used for agents that\n * consistently bloat their cache via large workspace reads. */\n contextStrategy: z.enum([\"layered\", \"planner\"]).optional(),\n /** When true, the registry resolves references-recipes for this agent's\n * workspace and renders a deterministic [Verified References] block at\n * priority 4.7. Off by default — flip on per agent (pm-ksi, devops-agent,\n * etc.) once a `references/` registry exists in the agent's workspace\n * or repo root. See src/agents/references/. */\n contextReferences: z.boolean().default(false),\n maxConcurrent: z.number().default(1),\n /** Hard wall-clock cap on a single Claude Code invocation. Exceeding the\n * cap sends SIGTERM (exit 143). Default 20 min — bump for devops/coder\n * agents that do long investigations or multi-file refactors. */\n maxExecutionMinutes: z.number().int().min(1).max(240).default(20),\n permissionMode: z.string().default(\"default\"),\n queueMode: z.enum([\"collect\", \"followup\", \"drop\"]).default(\"collect\"),\n heartbeat: z.object({\n enabled: z.boolean().default(false),\n intervalMinutes: z.number().default(30),\n prompt: z.string().default(\"Check inbox, pending tasks, and system health. Report anything that needs attention.\"),\n channel: z.string().default(\"heartbeat\"),\n }).default({}),\n /** Reachability via the external public API.\n * - \"private\" (default): this agent is daemon-internal + channel-bound only.\n * - \"public\": external apps can POST /api/public/agents/<id>/messages\n * with a scoped token (agent:<id> or agent:*). */\n access: z.enum([\"private\", \"public\"]).default(\"private\"),\n})\n\nconst telegramAccountSchema = z.object({\n token: z.string(),\n agentBinding: z.string(),\n /** Per-account sender allowlist. When set, OVERRIDES the global\n * `policy.allowFrom`. Entries accept:\n * - numeric user id (\"1816212449\") — matches sender (from.id)\n * - numeric chat id (\"-1003861455814\") — matches chat (chat.id)\n * - \"@username\" — matches sender username\n * A message is dispatched iff at least one entry matches. */\n allowFrom: z.array(z.string()).optional(),\n /** When false, the daemon keeps the bot token registered so outbound\n * `send()` / ring notifications work, but does NOT long-poll for inbound\n * updates. Use this on nodes where the bound agent lives on a different\n * daemon — otherwise two daemons race on the same `getUpdates` cursor and\n * Telegram responds 409 Conflict on every poll.\n * Defaults to true (poll) so existing single-node setups are unchanged. */\n pollInbound: z.boolean().default(true),\n})\n\nconst channelsConfigSchema = z.object({\n telegram: z.object({\n enabled: z.boolean().default(false),\n accounts: z.record(z.string(), telegramAccountSchema).default({}),\n policy: z.object({\n dm: z.enum([\"pair\", \"block\"]).default(\"pair\"),\n group: z.enum([\"mention-required\", \"all\"]).default(\"mention-required\"),\n /** Global sender allowlist applied to every account that doesn't set\n * its own `allowFrom`. When neither is configured, every incoming\n * message is dropped — closed by default. Same entry forms as the\n * per-account list (user id, chat id, @username). */\n allowFrom: z.array(z.string()).optional(),\n }).default({}),\n }).default({}),\n whatsapp: z.object({\n enabled: z.boolean().default(false),\n sessionDir: z.string().default(\".agentx/whatsapp-sessions\"),\n defaultAgent: z.string().optional(),\n allowFrom: z.array(z.string()).optional(),\n routes: z.array(z.object({\n contact: z.string().optional(),\n group: z.string().optional(),\n agent: z.string(),\n })).default([]),\n /** Data-source ingestion — turns WhatsApp from a messaging-only channel\n * into a source that seeds the wiki with contact/group metadata (and\n * optionally bounded message windows) per an explicit allowlist.\n * Default-deny: `enabled: false` and empty allowlists mean nothing is\n * ingested. See docs/reference/whatsapp-ingest.md for the full story. */\n ingest: z.object({\n enabled: z.boolean().default(false),\n /** `metadata-only` pulls contact/group info; `messages` additionally\n * pulls the last `messageCap` messages per allowlisted chat. */\n mode: z.enum([\"metadata-only\", \"messages\"]).default(\"metadata-only\"),\n /** Phone numbers or JIDs. Substring match, same semantics as allowFrom. */\n allowContacts: z.array(z.string()).default([]),\n allowGroups: z.array(z.string()).default([]),\n denyContacts: z.array(z.string()).default([]),\n denyGroups: z.array(z.string()).default([]),\n /** Per-chat cap when mode = \"messages\". Keeps the raw-entry size bounded. */\n messageCap: z.number().int().min(1).max(500).default(50),\n /** Max age (days) of messages to consider for the bounded window. */\n historyDays: z.number().int().min(1).max(365).default(30),\n /** Skip re-writing a contact entry unless this many days have passed\n * OR the profile hash differs. Prevents churn on unchanged profiles. */\n contactRefreshDays: z.number().int().min(1).max(90).default(7),\n /** Safety rails on live Baileys reads — personal-account accounts\n * can get throttled/banned under burst reads. */\n throttle: z.object({\n minMsBetweenCalls: z.number().int().min(100).default(1500),\n maxCallsPerMinute: z.number().int().min(1).default(20),\n maxChatsPerSweep: z.number().int().min(1).default(25),\n }).default({}),\n /** Purge absorbed raw entries older than this many days. `0` = never. */\n retentionDays: z.number().int().min(0).default(0),\n }).default({}),\n }).default({}),\n discord: z.object({\n enabled: z.boolean().default(false),\n token: z.string().optional(),\n agentBinding: z.string().optional(),\n }).default({}),\n slack: z.object({\n enabled: z.boolean().default(false),\n /** xoxb-... — bot user OAuth token from the Slack app's \"OAuth & Permissions\" tab. */\n botToken: z.string().optional(),\n /** xapp-... — app-level token with connections:write scope (for Socket Mode). */\n appToken: z.string().optional(),\n agentBinding: z.string().optional(),\n }).default({}),\n gitlab: z.object({\n enabled: z.boolean().default(false),\n webhookPort: z.number().default(18810),\n webhookSecret: z.string().optional(),\n host: z.string().default(\"https://gitlab.com\"),\n token: z.string().optional(),\n routes: z.array(z.object({\n project: z.string(),\n agent: z.string(),\n })).default([]),\n agentMappings: z.array(z.object({\n agentId: z.string(),\n gitlabUsernames: z.array(z.string()).default([]),\n keywords: z.array(z.string()).default([]),\n token: z.string().optional(),\n /** If set, the agent lives on a remote mesh peer (node id). Forces the\n * username→agent map to resolve to this mapping even when a local\n * agent's token resolves to the same GitLab user — prevents collisions\n * like two agents both claiming @devops-noqta. */\n node: z.string().optional(),\n })).default([]),\n }).default({}),\n github: z.object({\n enabled: z.boolean().default(false),\n /** GitHub PAT or env var (${GITHUB_TOKEN}) for posting comments back. */\n token: z.string().optional(),\n /** Path to file containing the token (first line read at startup). */\n tokenFile: z.string().optional(),\n /** GitHub App ID (legacy issuer). */\n appId: z.number().optional(),\n /** GitHub App Client ID (preferred JWT issuer per GitHub's updated docs). */\n clientId: z.string().optional(),\n /** Path to the GitHub App private key PEM file. */\n privateKeyFile: z.string().optional(),\n /** Webhook secret for validating X-Hub-Signature-256. */\n webhookSecret: z.string().optional(),\n /** Repo → agent routing. Use \"owner/repo\" or \"*\" for default. */\n routes: z.array(z.object({\n repo: z.string(),\n agent: z.string(),\n })).default([]),\n /** Per-agent identity mappings (GitHub usernames, tokens, mesh nodes). */\n agentMappings: z.array(z.object({\n agentId: z.string(),\n githubUsernames: z.array(z.string()).default([]),\n token: z.string().optional(),\n tokenFile: z.string().optional(),\n node: z.string().optional(),\n })).default([]),\n }).default({}),\n webrtc: z.object({\n enabled: z.boolean().default(false),\n /** ICE STUN servers for NAT discovery. Default is Google's public STUN. */\n stunServers: z.array(z.string()).default([\"stun:stun.l.google.com:19302\"]),\n /** Optional TURN relays (required when both peers are behind symmetric NAT). */\n turnServers: z.array(z.object({\n urls: z.string(),\n username: z.string().optional(),\n credential: z.string().optional(),\n })).default([]),\n /** Peer names permitted to *initiate* a call into this daemon. Empty = allow all mesh peers. */\n allowedCallers: z.array(z.string()).default([]),\n /** Where to send \"someone is calling\" notifications when an inbound ring\n * arrives. Each entry is delivered via the matching channel adapter —\n * same plumbing as any other outbound message. Empty disables notifications. */\n ringNotify: z.array(z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n })).default([]),\n /** Base URL used when building the tap-to-join link in ring notifications.\n * Defaults to `http://<node.bind>` — override when the daemon's public\n * hostname differs (e.g. HTTPS-terminated tunnel, Tailscale MagicDNS). */\n callUrlBase: z.string().optional(),\n /** AI participant (\"bot\") joins calls when a browser opens with `?bot=<id>`.\n * v1 is transcribe-only — bot consumes remote audio, transcribes via\n * Whisper, posts chunks to the configured channel. No TTS-back. */\n bot: z.object({\n enabled: z.boolean().default(false),\n /** Default agent id used to attribute the transcript when the URL\n * doesn't override via `?bot=<other-agent>`. */\n defaultAgentId: z.string().optional(),\n /** \"auto\" tries mlx-whisper, falls back to OpenAI; explicit forces. */\n whisperBackend: z.enum([\"auto\", \"mlx\", \"openai\"]).default(\"auto\"),\n whisperModel: z.string().optional(),\n whisperLanguage: z.string().default(\"auto\"),\n /** Absolute path to mlx_whisper if not on the daemon's PATH (common\n * on macOS launchd, where ~/.pyenv/shims is missing from the env). */\n mlxBinary: z.string().optional(),\n /** Where to send each transcribed chunk. Same shape as ringNotify[]. */\n transcriptChannel: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Hard cap so a forgotten bot doesn't run forever. */\n maxCallMinutes: z.number().int().min(1).max(240).default(30),\n }).default({}),\n }).default({}),\n})\n\nconst cronJobSchema = z.object({\n enabled: z.boolean().default(true),\n schedule: z.string(),\n timezone: z.string().default(\"UTC\"),\n agent: z.string(),\n prompt: z.string(),\n timeout: z.number().default(600),\n model: z.string().optional(),\n /** Soft cap on output length. Claude Code CLI has no hard flag for this,\n * but appending an instruction to the prompt is reliably honored. Use\n * 1500 for briefs, 500 for status pings, 300 for pure classifiers. */\n maxOutputTokens: z.number().int().min(50).max(8000).optional(),\n onError: z.union([\n z.enum([\"log\", \"notify\", \"disable\"]),\n z.array(z.enum([\"log\", \"notify\", \"disable\"])),\n ]).default(\"log\").transform(v => Array.isArray(v) ? v : [v]),\n notify: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n})\n\nconst serviceSchema = z.object({\n name: z.string(),\n triggers: z.array(z.object({\n pattern: z.string(),\n channel: z.string().optional(),\n })),\n allowedContacts: z.array(z.string()).optional(),\n agent: z.string(),\n prompt: z.string(),\n schedule: z.string().optional(),\n timezone: z.string().default(\"UTC\"),\n notify: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n})\n\nconst meshPeerSchema = z.object({\n url: z.string(),\n name: z.string(),\n token: z.string().optional(),\n})\n\nconst meshConfigSchema = z.object({\n enabled: z.boolean().default(false),\n peers: z.array(meshPeerSchema).default([]),\n discovery: z.enum([\"static\", \"mdns\"]).default(\"static\"),\n healthCheck: z.object({\n interval: z.number().default(60),\n timeout: z.number().default(10),\n }).default({}),\n})\n\n/** Intent Knowledge Graph — fixed-axis, LLM-proposed taxonomy used by the\n * Intent layer in context.ts and (eventually) wiki retrieval. Off by default\n * so existing installs see no change. */\nconst graphConfigSchema = z.object({\n enabled: z.boolean().default(false),\n /** Where the schema/nodes/classifications live. Relative to cwd. */\n baseDir: z.string().default(\".agentx/graph\"),\n /** Which agent makes LLM classification proposals. Falls back to\n * `dashboard.draftAgent` at call sites if unset. */\n draftAgent: z.string().optional(),\n /** Agent used by `agentx graph review` to triage pending classifications.\n * Should be an agent with the wiki skill so it can call `wiki query` for\n * context before deciding approve/reject. Falls back to draftAgent. */\n reviewAgent: z.string().optional(),\n /** Structural auto-approval policy. Evaluated against each classification\n * independently of `autoApproveConfidence`; either triggers approval.\n * - \"strict\" : never approve via structure — every classification\n * waits for human review\n * - \"extend-leaves\" : auto-approve when the proposed path (a) reuses only\n * existing nodes, or (b) adds exactly one NEW node at\n * the deepest level. Structural changes (new mid-path\n * node, new root) still queue for review. Default.\n * - \"any\" : auto-approve every classification regardless of\n * structural change\n */\n autoApproveStructure: z.enum([\"strict\", \"extend-leaves\", \"any\"]).default(\"extend-leaves\"),\n /** Minimum classifier confidence (0..1) to auto-approve. OR'd with the\n * structural policy — either hitting the threshold lets the classification\n * bypass the pending queue. 1.0 (default) disables this knob, so approval\n * is driven by `autoApproveStructure` alone. Lower it to e.g. 0.7 if you\n * also want high-confidence structural changes auto-approved. */\n autoApproveConfidence: z.number().min(0).max(1).default(1.0),\n /** Anthropic model id used for the direct classifier call (Phase 2 of\n * classifier-retire). Default haiku — classification is metadata, not\n * work, so we use the cheapest fast model. Override only if Haiku is\n * rate-limited or you want to A/B test. */\n classifierModel: z.string().default(\"claude-haiku-4-5-20251001\"),\n /** Weights for the wiki hybrid retrieval score. Path-ancestry match vs\n * BM25 over article text. Sum need not be 1. */\n retrievalWeights: z.object({\n graph: z.number().min(0).default(0.6),\n bm25: z.number().min(0).default(0.4),\n }).default({}),\n}).default({})\n\nconst notificationsSchema = z.object({\n /** Send notification when task takes longer than this (seconds). 0 = disabled. */\n longTaskThreshold: z.number().default(30),\n /** Where to send notifications */\n destination: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Notify on these events */\n on: z.object({\n taskComplete: z.boolean().default(true),\n taskError: z.boolean().default(true),\n taskQueued: z.boolean().default(false),\n }).default({}),\n}).default({})\n\nexport const daemonConfigSchema = z.object({\n node: z.object({\n id: z.string(),\n name: z.string(),\n bind: z.string().default(\"127.0.0.1:18800\"),\n /** Default agent for voice/API calls that don't specify one (e.g. Siri) */\n defaultAgent: z.string().optional(),\n }),\n providers: z.record(z.string(), providerConfigSchema).default({}),\n agents: z.record(z.string(), agentConfigSchema).default({}),\n channels: channelsConfigSchema.default({}),\n crons: z.record(z.string(), cronJobSchema).default({}),\n services: z.record(z.string(), serviceSchema).default({}),\n notifications: notificationsSchema,\n mesh: meshConfigSchema.default({}),\n business: businessConfigSchema.optional(),\n boards: boardsConfigSchema,\n dashboard: dashboardConfigSchema,\n graph: graphConfigSchema,\n /** Workflow engine — declarative state machines that bind channel events\n * to agents. Off by default; existing installs see no change until\n * flipped. Definitions live under `dir` (default .agentx/workflows/). */\n workflows: z.object({\n enabled: z.boolean().default(false),\n dir: z.string().default(\".agentx/workflows\"),\n /** Controls whether the dashboard exposes the visual editor. \"readonly\"\n * serves the list + run timelines but strips write controls from the\n * page. \"disabled\" hides the tab entirely. */\n editor: z.enum([\"disabled\", \"readonly\", \"edit\"]).default(\"edit\"),\n }).default({}),\n /** Registered inbound webhooks — an inventory the dashboard manages. Each\n * entry binds an (agent, source) pair to an optional signing secret. The\n * actual inbound URL is always POST /webhook/<agentId>/<source>. */\n webhooks: z.array(z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]*$/, \"webhook id must be lowercase slug\"),\n source: z.enum([\"gitlab\", \"github\", \"sentry\", \"stripe\", \"discord\", \"slack\", \"custom\"]),\n agentId: z.string(),\n secretEnv: z.string().optional(),\n description: z.string().optional(),\n enabled: z.boolean().default(true),\n /** Route to a mesh peer instead of local execution. */\n node: z.string().optional(),\n /** Per-event-type workflow routing. Keys are platform event-type\n * strings — the format the platform's HTTP header uses, optionally\n * joined with the action. Examples:\n * GitHub: \"issues.opened\", \"pull_request.synchronize\",\n * \"push\" (action-less events use the bare type)\n * GitLab: \"Note Hook\", \"Merge Request Hook\"\n * Values are workflow ids registered via `agentx workflow create`.\n * When the inbound event-type matches a key, the named workflow is\n * dispatched; if no key matches, the webhook falls through to\n * `defaultWorkflow` (or the agent itself). Closes the recurring\n * GitHub problem of multiple event types collapsing to a single\n * workflow. */\n triggers: z.record(z.string(), z.string()).optional(),\n /** Workflow id used when no `triggers` entry matches the event-type.\n * Backward-compatible: pre-existing webhooks without `triggers`\n * always hit this path. */\n defaultWorkflow: z.string().optional(),\n })).default([]),\n /** Session cache-reuse policy. Controls when we drop a Claude `--resume`\n * session and rebuild the prompt from scratch.\n * - `staleMinutes`: idle timeout before rotation. Longer is cheaper at\n * Opus rates but lets a session accumulate tool-result bloat across an\n * all-day chat.\n * - `maxTurnsPerSession`: hard cap on turns per Claude session. Prevents\n * unbounded `--resume` growth. When hit, next turn starts fresh with\n * the compacted summary + recent-messages context.\n * - `tierTwoThresholdTokens`: if the prior turn's (input + cacheRead +\n * cacheCreate) exceeded this, rotate proactively. Claude bills tier-2\n * at 1.5× above 200K total input, so rotating before that threshold\n * undercuts the multiplier. */\n session: z.object({\n staleMinutes: z.number().int().min(1).max(1440).default(45),\n maxTurnsPerSession: z.number().int().min(2).max(200).default(15),\n tierTwoThresholdTokens: z.number().int().min(50_000).max(200_000).default(195_000),\n /** Context assembly strategy:\n * - \"layered\" (default): the classic stacked layers — session history,\n * memory, cross-chat, wiki hint all appended every turn.\n * - \"planner\": a Haiku pre-call decides what to retrieve before the\n * main agent runs. Always-on core is kept (channel/scope/landscape/\n * identity/intent + last 3 turns verbatim); history blob, wiki hint,\n * and cross-chat are replaced by planner-selected bundles. Per-task\n * overrides via AgentTask.contextStrategy for A/B benchmarking. */\n contextStrategy: z.enum([\"layered\", \"planner\"]).default(\"layered\"),\n /** Soft dispatch budget for claude-code-tier agents (shared OAuth pools\n * one counter across the fleet). Warms at 80% of the cap; short-circuits\n * cold dispatches (no warm Claude session) when exceeded. Warm sessions\n * are always allowed through. Defaults sized for Max 5×:\n * - maxClaudeCodeDispatchesPerHour: 80 (headroom under typical hourly cap)\n * - maxClaudeCodeDispatchesPer5h: 180 (headroom under ~225/5h tier)\n * Raise for Max 20×, lower if your workload stays under the cap naturally. */\n maxClaudeCodeDispatchesPerHour: z.number().int().min(1).max(10_000).default(80),\n maxClaudeCodeDispatchesPer5h: z.number().int().min(1).max(50_000).default(180),\n }).default({}),\n /** Move B — JS/TS plugins. Each entry is an installed npm package name\n * (e.g. `agentx-plugin-mattermost` or `@noqta/plugin-mattermost`); the\n * loader does a dynamic `import(name)` at boot, validates the manifest,\n * and calls plugin.setup(ctx). Plugins can register channel adapters\n * via ctx.addChannel() and subscribe to bus events via ctx.on(). Empty\n * default — installs that don't list plugins are unaffected. Plugin\n * authoring guide: docs/architecture/plugins.md. */\n plugins: z.array(z.string()).default([]),\n})\n\nexport type DaemonConfig = z.infer<typeof daemonConfigSchema>\nexport type AgentDef = z.infer<typeof agentConfigSchema>\nexport type CronJobDef = z.infer<typeof cronJobSchema>\nexport type MeshPeer = z.infer<typeof meshPeerSchema>\n\n/**\n * Expand environment variables in strings: ${VAR_NAME} -> process.env.VAR_NAME\n */\nexport function expandEnvVars(obj: unknown): unknown {\n if (typeof obj === \"string\") {\n return obj.replace(/\\$\\{(\\w+)\\}/g, (_match, name) => process.env[name] || \"\")\n }\n if (Array.isArray(obj)) {\n return obj.map(expandEnvVars)\n }\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[key] = expandEnvVars(value)\n }\n return result\n }\n return obj\n}\n\n/**\n * Load daemon config from agentx.json, with env var expansion and validation.\n */\nexport function loadDaemonConfig(configPath?: string): DaemonConfig {\n const paths = configPath\n ? [configPath]\n : [\n resolve(process.cwd(), \"agentx.json\"),\n resolve(process.cwd(), \".agentx/config.json\"),\n ]\n\n // Load .env from same directory as config search\n loadDotEnv(process.cwd())\n\n let raw: string | undefined\n let foundPath: string | undefined\n\n for (const p of paths) {\n if (existsSync(p)) {\n raw = readFileSync(p, \"utf-8\")\n foundPath = p\n break\n }\n }\n\n if (!raw || !foundPath) {\n throw new Error(\n `No config found. Create agentx.json or .agentx/config.json\\n` +\n `Searched: ${paths.join(\", \")}`\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (e: any) {\n throw new Error(`Invalid JSON in ${foundPath}: ${e.message}`)\n }\n\n // Expand environment variables\n const expanded = expandEnvVars(parsed)\n\n // Validate\n const result = daemonConfigSchema.safeParse(expanded)\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .join(\"\\n\")\n throw new Error(`Config validation failed (${foundPath}):\\n${issues}`)\n }\n\n return result.data\n}\n\n/**\n * Validate agent workspace directories exist and have .claude/ setup.\n */\nexport function validateWorkspaces(config: DaemonConfig): string[] {\n const warnings: string[] = []\n\n for (const [id, agent] of Object.entries(config.agents)) {\n if (!existsSync(agent.workspace)) {\n warnings.push(`Agent \"${id}\": workspace not found at ${agent.workspace}`)\n continue\n }\n\n if (agent.tier === \"claude-code\") {\n const claudeDir = resolve(agent.workspace, \".claude\")\n if (!existsSync(claudeDir)) {\n warnings.push(\n `Agent \"${id}\": no .claude/ directory in workspace ${agent.workspace}. ` +\n `Claude Code native features (hooks, MCP, skills) won't be available.`\n )\n }\n }\n\n // Check provider availability\n const providerName = agent.provider || \"claude\"\n const providerConfig = config.providers[providerName]\n if (agent.tier !== \"claude-code\" && (!providerConfig || !providerConfig.apiKey)) {\n warnings.push(\n `Agent \"${id}\": provider \"${providerName}\" has no API key configured. ` +\n `Set providers.${providerName}.apiKey in config or use tier \"claude-code\" for subscription.`\n )\n }\n }\n\n // Validate cron agent bindings\n for (const [id, cron] of Object.entries(config.crons)) {\n if (!config.agents[cron.agent]) {\n warnings.push(`Cron \"${id}\": references unknown agent \"${cron.agent}\"`)\n }\n }\n\n // Validate channel agent bindings\n if (config.channels.telegram.enabled) {\n for (const [name, account] of Object.entries(config.channels.telegram.accounts)) {\n if (!config.agents[account.agentBinding]) {\n warnings.push(\n `Telegram account \"${name}\": references unknown agent \"${account.agentBinding}\"`\n )\n }\n }\n }\n\n return warnings\n}\n","import { z } from \"zod\"\n\n// --- Business layer configuration schema ---\n// Optional top-level `business` key in agentx.json.\n// When enabled, AgentX simulates a team with roles, schedules, and a work pool.\n\nconst dayEnum = z.enum([\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"])\n\nconst scheduleSchema = z.object({\n days: z.array(dayEnum).default([\"mon\", \"tue\", \"wed\", \"thu\", \"fri\"]),\n start: z.string().regex(/^\\d{2}:\\d{2}$/), // \"09:00\"\n end: z.string().regex(/^\\d{2}:\\d{2}$/), // \"17:00\"\n lunch: z.object({\n start: z.string().regex(/^\\d{2}:\\d{2}$/),\n end: z.string().regex(/^\\d{2}:\\d{2}$/),\n }).optional(),\n})\n\nconst roleSchema = z.object({\n title: z.string(),\n responsibilities: z.array(z.string()).default([]),\n sopPath: z.string().optional(),\n kpis: z.array(z.string()).default([]),\n})\n\nconst orgEntrySchema = z.object({\n role: z.string(),\n reportsTo: z.string().optional(),\n schedule: scheduleSchema,\n utilizationTarget: z.number().min(0).max(1).default(0.8),\n})\n\nconst channelRefSchema = z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n})\n\nconst workSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"backlog\"),\n path: z.string().default(\".agentx/backlog.md\"),\n }),\n z.object({\n type: z.literal(\"gitlab\"),\n projects: z.array(z.string()).default([]), // empty = all configured gitlab projects\n }),\n z.object({\n type: z.literal(\"wiki\"),\n path: z.string(),\n glob: z.string().default(\"**/*.md\"),\n }),\n])\n\n/** Per-project metadata. Phase 3 of the architectural rescue —\n * `pm` is the agentId responsible for gating dispatches on this\n * project. Optional; when null, dispatches don't go through a PM\n * gate. The wiring lives in `decideAndCommit` (gated by a flag,\n * separate commit). */\nconst projectSchema = z.object({\n /** Project identifier. For GitLab/GitHub: \"owner/repo\". For\n * noqta-internal projects: a stable string. */\n id: z.string(),\n /** AgentId of the PM for this project. Looked up by\n * `Organization.pmFor(project)` at dispatch time. */\n pm: z.string().optional(),\n /** Optional client this project belongs to. Used by the activity\n * graph to attribute non-projected events (Telegram chats, free-\n * text WhatsApp, cron jobs) to the right client. Falls back to\n * the leading segment of `id` (split on \"/\") when unset. */\n client: z.string().optional(),\n})\n\n/** Maps a chat or sender to a client/project so free-text channels\n * (telegram/whatsapp/slack/discord/email) get attributed correctly\n * in the activity graph. Without this, every chat lands in the\n * fallback \"internal\" bucket — the operator can't tell which client\n * the agent was helping.\n *\n * Match priority: chatId > username > channel-default. First match\n * wins. All fields except `client` are optional; `project` falls\n * back to `${client}/_chat` when unset. */\nconst contactMapSchema = z.object({\n /** Channel kind, e.g. \"telegram\", \"whatsapp\", \"slack\", \"discord\". */\n channel: z.string().optional(),\n /** Native chat id (\"-100...\" for tg groups, JID for WhatsApp). */\n chatId: z.string().optional(),\n /** Sender username/handle as exposed by the channel adapter. */\n username: z.string().optional(),\n /** Numeric sender id when username is unstable. */\n senderId: z.string().optional(),\n /** Client this contact belongs to. Required. */\n client: z.string(),\n /** Project this contact's traffic should attribute to. Defaults to\n * `${client}/_chat`. */\n project: z.string().optional(),\n /** Display name override for the initiator pill. */\n displayName: z.string().optional(),\n})\n\nexport const businessConfigSchema = z.object({\n enabled: z.boolean().default(false),\n timezone: z.string().default(\"UTC\"),\n mainChannel: channelRefSchema,\n workSource: workSourceSchema,\n roles: z.record(z.string(), roleSchema).default({}),\n orgChart: z.record(z.string(), orgEntrySchema).default({}),\n /** Per-project metadata. Phase 3 — defaults to empty array, so\n * existing agentx.json files validate without modification. */\n projects: z.array(projectSchema).default([]),\n /** Maps free-text chat senders to a client/project so the activity\n * graph can attribute Telegram/WhatsApp/etc. to the right client\n * instead of the catch-all \"internal\" bucket. */\n contactMap: z.array(contactMapSchema).default([]),\n /** Work-tick cadence in minutes during business hours. Default 15. */\n workTickMinutes: z.number().int().min(1).max(60).default(15),\n /** Max queue depth for an idle agent before skipping the work tick (avoids piling up). */\n idleQueueThreshold: z.number().int().min(0).default(0),\n})\n\nexport type BusinessConfig = z.infer<typeof businessConfigSchema>\nexport type BusinessRole = z.infer<typeof roleSchema>\nexport type BusinessOrgEntry = z.infer<typeof orgEntrySchema>\nexport type BusinessSchedule = z.infer<typeof scheduleSchema>\nexport type BusinessWorkSource = z.infer<typeof workSourceSchema>\nexport type BusinessChannelRef = z.infer<typeof channelRefSchema>\nexport type BusinessProject = z.infer<typeof projectSchema>\n","import { z } from \"zod\"\n\n// --- Kanban board configuration ---\n//\n// Top-level optional keys in agentx.json, independent of the business layer:\n//\n// boards: array of kanban boards, each backed by a WorkSource\n// dashboard: HTTP server controlling whether the board UI serves\n//\n// Both default to empty / disabled so existing installs see no change.\n\nexport const boardLabelSchema = z.object({\n /** Label name — used verbatim as the GitLab label string. */\n name: z.string(),\n color: z.string().regex(/^#[0-9a-f]{6}$/i).default(\"#6366f1\"),\n description: z.string().optional(),\n})\n\n/**\n * A board column. `kind` controls what issues land here and how drag-to-this-column\n * mutates the issue:\n *\n * - \"open-backlog\": opened issues that have NO scoped label matching `scopedPrefix`\n * (default \"Status\"). Dragging INTO this column removes any existing scoped label.\n * - \"scoped-label\": opened issues carrying `scopedLabel` (e.g. \"Status::Doing\").\n * Scoped labels are mutually exclusive by prefix, so adding the new one lets\n * GitLab auto-remove the old one (Premium), but we still explicitly strip the\n * previous column's label for compatibility.\n * - \"closed\": all closed issues. Dragging here closes the issue; dragging away\n * reopens it.\n * - \"label\" (default, legacy): opened issues with `mapsToLabel`; flat add/remove.\n */\nexport const boardColumnSchema = z.object({\n id: z.string().regex(/^[a-z0-9_-]+$/, \"column id must be lowercase slug\"),\n title: z.string(),\n kind: z.enum([\"open-backlog\", \"scoped-label\", \"closed\", \"label\"]).default(\"label\"),\n /** For kind=\"label\": label written on entry, removed on exit. */\n mapsToLabel: z.string().optional(),\n /** For kind=\"scoped-label\": the full scoped label (e.g. \"Status::Doing\"). */\n scopedLabel: z.string().optional(),\n /** For kind=\"open-backlog\": scoped prefix that marks \"claimed\" cards to exclude. */\n scopedPrefix: z.string().default(\"Status\"),\n /** CSS color or hex — shown as the column's top accent bar. */\n accent: z.string().optional(),\n})\nexport type BoardColumnId = string\n\nexport const boardReconcileSchema = z.object({\n enabled: z.boolean().default(true),\n staleDoingMinutes: z.number().int().min(1).default(45),\n respectLunchBreak: z.boolean().default(true),\n respectSchedule: z.boolean().default(true),\n /** Badge only for P1/P3; auto-notify + auto-demote deferred. */\n action: z.enum([\"badge\", \"notify\"]).default(\"badge\"),\n})\n\nexport const boardSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"gitlab\"),\n /** GitLab project paths (e.g. \"mtgl/mtgl-system-v2\"). At least one required. */\n projects: z.array(z.string()).min(1),\n }),\n // future: backlog, wiki\n])\n\n/**\n * GitLab-style default board — six columns:\n * Open (backlog) → To Do → Doing → On Hold → Review → Closed\n *\n * Drives the `Status::*` scoped label taxonomy. Moving a card adds/removes the\n * column's scoped label; moving INTO Closed closes the issue; moving OUT of\n * Closed reopens it; moving INTO Open strips any Status::* label.\n */\nexport const DEFAULT_COLUMNS: Array<z.infer<typeof boardColumnSchema>> = [\n { id: \"open\", title: \"Open\", kind: \"open-backlog\", scopedPrefix: \"Status\", accent: \"#6b7280\" },\n { id: \"todo\", title: \"To Do\", kind: \"scoped-label\", scopedLabel: \"Status::To Do\", scopedPrefix: \"Status\", accent: \"#fb7a35\" },\n { id: \"doing\", title: \"Doing\", kind: \"scoped-label\", scopedLabel: \"Status::Doing\", scopedPrefix: \"Status\", accent: \"#22c55e\" },\n { id: \"onhold\", title: \"On Hold\", kind: \"scoped-label\", scopedLabel: \"Status::On Hold\", scopedPrefix: \"Status\", accent: \"#f59e0b\" },\n { id: \"review\", title: \"Review\", kind: \"scoped-label\", scopedLabel: \"Status::Review\", scopedPrefix: \"Status\", accent: \"#3b82f6\" },\n { id: \"closed\", title: \"Closed\", kind: \"closed\", scopedPrefix: \"Status\", accent: \"#6b7280\" },\n]\n\nexport const boardSchema = z.object({\n id: z.string().regex(/^[a-z0-9_-]+$/, \"board id must be lowercase slug\"),\n name: z.string(),\n source: boardSourceSchema,\n /** If set, ANDed into every listAll query and shown as a baseline chip in the UI. */\n primaryToolLabel: z.string().optional(),\n labels: z.array(boardLabelSchema).default([]),\n columns: z.array(boardColumnSchema).min(1).default(DEFAULT_COLUMNS),\n timeRangeDays: z.number().int().min(1).max(365).default(30),\n /** Maximum closed-issue window (days) shown in the Closed column. */\n closedWindowDays: z.number().int().min(1).max(365).default(30),\n reconciliation: boardReconcileSchema.default({}),\n})\n\nexport const boardsConfigSchema = z.array(boardSchema).default([])\n\nexport const dashboardDaemonSchema = z.object({\n name: z.string(),\n /** Daemon HTTP URL (the one serving /agents, /health, /task). */\n url: z.string(),\n /** Optional board-dashboard URL if we want to proxy admin writes to this\n * peer. Defaults to `url` with port :18800/:19900 rewritten to :4202,\n * matching our convention that the dashboard runs alongside the daemon. */\n dashboardUrl: z.string().optional(),\n /** Bearer for the daemon's HTTP API (live snapshot, task stream). */\n token: z.string().optional(),\n /** Bearer for the peer's dashboard when proxying admin calls. Defaults to\n * `token` when unset. */\n dashboardToken: z.string().optional(),\n})\n\nexport const dashboardConfigSchema = z.object({\n enabled: z.boolean().default(false),\n port: z.number().int().default(4202),\n bind: z.string().default(\"127.0.0.1\"),\n /** Opt-in bearer token for writes. If unset, writes are unauthenticated. */\n token: z.string().optional(),\n /** Primary daemon the dashboard talks to for live view + A2A. */\n daemonUrl: z.string().default(\"http://localhost:18800\"),\n /** Additional daemons to poll (beyond peers discovered via primary's /mesh). */\n daemons: z.array(dashboardDaemonSchema).default([]),\n /** Default agent used for AI-assisted drafting in the create-issue flow. */\n draftAgent: z.string().optional(),\n}).default({})\n\nexport type BoardConfig = z.infer<typeof boardSchema>\nexport type BoardLabel = z.infer<typeof boardLabelSchema>\nexport type BoardColumn = z.infer<typeof boardColumnSchema>\nexport type BoardSource = z.infer<typeof boardSourceSchema>\nexport type BoardReconcile = z.infer<typeof boardReconcileSchema>\nexport type DashboardConfig = z.infer<typeof dashboardConfigSchema>\n\n/**\n * Decide which column an item lives in. Walks columns in order; the first\n * matching column wins. Items with `state: \"closed\"` always land in the\n * first `kind: \"closed\"` column; otherwise the first `scoped-label` whose\n * `scopedLabel` is present wins; otherwise the first `label` column whose\n * `mapsToLabel` is present; otherwise the first `open-backlog` column.\n *\n * Returns `null` when the item carries a scoped label matching an\n * `open-backlog` column's `scopedPrefix` but no `scoped-label` column claims\n * it — those are off-board workflow states (e.g. `Status::Done` when no\n * Done column exists) and must not leak into the backlog.\n */\nexport function deriveStage(\n item: { state?: \"opened\" | \"closed\"; labels?: string[] },\n columns: BoardColumn[],\n): BoardColumnId | null {\n const set = new Set(item.labels || [])\n if (item.state === \"closed\") {\n const closed = columns.find((c) => c.kind === \"closed\")\n if (closed) return closed.id\n }\n for (const c of columns) {\n if (c.kind === \"scoped-label\" && c.scopedLabel && set.has(c.scopedLabel)) return c.id\n }\n for (const c of columns) {\n if (c.kind === \"label\" && c.mapsToLabel && set.has(c.mapsToLabel)) return c.id\n }\n const backlog = columns.find((c) => c.kind === \"open-backlog\")\n if (backlog) {\n // If the item has any scoped-prefix label, it's not an open-backlog candidate.\n const prefix = backlog.scopedPrefix + \"::\"\n const hasScoped = [...set].some((l) => l.startsWith(prefix))\n return hasScoped ? null : backlog.id\n }\n return columns[0]?.id ?? null\n}\n\n/**\n * Describes how to mutate an issue to move it between two columns.\n * `closeIssue` / `reopen` take precedence over label mutations on the same action.\n */\nexport interface ColumnTransition {\n addLabels?: string[]\n removeLabels?: string[]\n closeIssue?: boolean\n reopen?: boolean\n}\n\nexport function transitionDiff(\n fromColumn: BoardColumn | undefined,\n toColumn: BoardColumn,\n): ColumnTransition {\n const add: string[] = []\n const remove: string[] = []\n let closeIssue = false\n let reopen = false\n\n // Handle target column.\n if (toColumn.kind === \"scoped-label\" && toColumn.scopedLabel) {\n add.push(toColumn.scopedLabel)\n } else if (toColumn.kind === \"label\" && toColumn.mapsToLabel) {\n add.push(toColumn.mapsToLabel)\n } else if (toColumn.kind === \"closed\") {\n closeIssue = true\n }\n\n // Handle source column — strip its label; if it was \"closed\", reopen.\n if (fromColumn) {\n if (fromColumn.kind === \"scoped-label\" && fromColumn.scopedLabel) {\n remove.push(fromColumn.scopedLabel)\n } else if (fromColumn.kind === \"label\" && fromColumn.mapsToLabel) {\n remove.push(fromColumn.mapsToLabel)\n } else if (fromColumn.kind === \"closed\" && toColumn.kind !== \"closed\") {\n reopen = true\n }\n }\n\n return {\n addLabels: add.length ? add : undefined,\n removeLabels: remove.length ? remove : undefined,\n closeIssue: closeIssue || undefined,\n reopen: reopen || undefined,\n }\n}\n"],"mappings":"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","names":["z","readFileSync","existsSync","resolve","z","dayEnum","scheduleSchema","roleSchema","orgEntrySchema","channelRefSchema","workSourceSchema","projectSchema","contactMapSchema","businessConfigSchema","z","boardLabelSchema","boardColumnSchema","boardReconcileSchema","boardSourceSchema","DEFAULT_COLUMNS","boardSchema","boardsConfigSchema","dashboardDaemonSchema","dashboardConfigSchema","deriveStage","item","columns","set","closed","c","backlog","prefix","l","transitionDiff","fromColumn","toColumn","add","remove","closeIssue","reopen","loadDotEnv","dir","envPath","resolve","existsSync","content","readFileSync","line","trimmed","eqIdx","key","value","providerConfigSchema","z","mcpServerSchema","agentConfigSchema","telegramAccountSchema","channelsConfigSchema","cronJobSchema","v","serviceSchema","meshPeerSchema","meshConfigSchema","graphConfigSchema","notificationsSchema","daemonConfigSchema","businessConfigSchema","boardsConfigSchema","dashboardConfigSchema","expandEnvVars","obj","_match","name","result","loadDaemonConfig","configPath","paths","raw","foundPath","p","parsed","e","expanded","issues","i","validateWorkspaces","config","warnings","id","agent","claudeDir","providerName","providerConfig","cron","account"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/daemon/setup-wizard.ts","../src/daemon/config-mutate.ts","../src/daemon/ui/pages/setup.ts","../src/daemon/token-store.ts","../src/wiki/serve.ts","../src/wiki/mesh.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { hostname } from \"os\"\nimport type { IncomingMessage, ServerResponse } from \"http\"\nimport { mutateAgentxConfig, writeAgentxConfig } from \"./config-mutate\"\nimport { renderSetupPage } from \"./ui/pages/setup\"\n\n/** Shape of the pre-render state snapshot — exported so the page file can\n * type its props without re-importing this whole module. */\nexport interface WizardState {\n configExists: boolean\n agentCount: number\n channelCount: number\n nodeName?: string\n}\n\n// --- /setup wizard: form-driven first-run experience for non-technical operators ---\n//\n// This is the web equivalent of `agentx init` + `agentx agent add` + `agentx\n// channel add`, collapsed into one page. It writes agentx.json and scaffolds\n// the first agent's workspace so a business operator can go from a blank\n// machine to a working AgentX install without editing JSON.\n//\n// The route lives on the board-dashboard server (port 4202) because that's the\n// surface operators actually open. It talks to the loopback daemon for the\n// post-install restart; the wizard itself never requires a running daemon.\n\nexport interface WizardPayload {\n nodeName: string\n agent: {\n id: string\n name: string\n triggerWords: string // comma/space separated; we normalise on the server\n tier: \"claude-code\" | \"sdk\"\n model?: string\n personality?: string // short free text → CLAUDE.md\n }\n telegram?: {\n accountId: string\n botToken: string\n botUsername?: string\n }\n anthropicApiKey?: string\n}\n\n/**\n * Return the current state of the working directory so the wizard can choose\n * between \"fresh install\" and \"add another\" flows.\n */\nexport function wizardState(baseDir: string = process.cwd()): WizardState {\n const file = resolve(baseDir, \"agentx.json\")\n if (!existsSync(file)) return { configExists: false, agentCount: 0, channelCount: 0 }\n try {\n const cfg = JSON.parse(readFileSync(file, \"utf-8\"))\n const agents = cfg.agents ? Object.keys(cfg.agents).length : 0\n const channels = cfg.channels\n ? Object.values(cfg.channels).filter((c: any) => c && c.enabled).length\n : 0\n return { configExists: true, agentCount: agents, channelCount: channels, nodeName: cfg.node?.name }\n } catch {\n return { configExists: false, agentCount: 0, channelCount: 0 }\n }\n}\n\n/**\n * Execute the wizard submission. Returns a short human-readable summary of\n * what was written. Throws on validation error — the caller surfaces the\n * message to the user.\n */\nexport function runWizard(payload: WizardPayload, baseDir: string = process.cwd()): {\n summary: string\n nextSteps: string[]\n backupPath?: string\n} {\n // --- validate ---\n if (!payload.nodeName?.trim()) throw new Error(\"Team name is required.\")\n if (!payload.agent?.id?.trim()) throw new Error(\"Agent id is required.\")\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(payload.agent.id)) {\n throw new Error(\"Agent id must be lowercase (letters, digits, -, _) and start with a letter or digit.\")\n }\n if (!payload.agent.name?.trim()) throw new Error(\"Agent name is required.\")\n const mentions = splitMentions(payload.agent.triggerWords)\n if (mentions.length === 0) throw new Error(\"At least one trigger word is required.\")\n if (payload.agent.tier !== \"claude-code\" && payload.agent.tier !== \"sdk\") {\n throw new Error(\"AI engine must be claude-code or sdk.\")\n }\n\n const configPath = resolve(baseDir, \"agentx.json\")\n const envPath = resolve(baseDir, \".env\")\n const workspaceDir = resolve(baseDir, \"agents\", payload.agent.id)\n const state = wizardState(baseDir)\n\n // --- scaffold workspace ---\n mkdirSync(workspaceDir, { recursive: true })\n const claudeMd = resolve(workspaceDir, \"CLAUDE.md\")\n if (!existsSync(claudeMd)) {\n const personality = payload.agent.personality?.trim() || `You are ${payload.agent.name}, an assistant on the ${payload.nodeName} team.`\n writeFileSync(claudeMd, `# ${payload.agent.name}\\n\\n${personality}\\n`)\n }\n\n // --- build / mutate agentx.json ---\n const telegramTokenRef = payload.telegram\n ? `TG_${payload.agent.id.toUpperCase()}_BOT_TOKEN`\n : undefined\n\n let backupPath: string | undefined\n if (!state.configExists) {\n const cfg = buildFreshConfig(payload, workspaceDir, mentions, telegramTokenRef)\n writeAgentxConfig(cfg, { configPath })\n } else {\n const result = mutateAgentxConfig((cfg) => {\n cfg.node = cfg.node || {}\n if (!cfg.node.name) cfg.node.name = payload.nodeName\n if (!cfg.node.id) cfg.node.id = slugify(payload.nodeName) || \"team\"\n if (!cfg.node.bind) cfg.node.bind = \"127.0.0.1:18800\"\n cfg.node.defaultAgent = cfg.node.defaultAgent || payload.agent.id\n\n cfg.providers = cfg.providers || {}\n if (!cfg.providers.claude) cfg.providers.claude = { apiKey: \"${ANTHROPIC_API_KEY}\" }\n\n cfg.agents = cfg.agents || {}\n if (cfg.agents[payload.agent.id]) throw new Error(`Agent \"${payload.agent.id}\" already exists.`)\n cfg.agents[payload.agent.id] = agentBlock(payload, workspaceDir, mentions)\n\n if (payload.telegram) {\n cfg.channels = cfg.channels || {}\n cfg.channels.telegram = cfg.channels.telegram || { enabled: true, accounts: {}, policy: { dm: \"pair\", group: \"mention-required\" } }\n cfg.channels.telegram.enabled = true\n cfg.channels.telegram.accounts = cfg.channels.telegram.accounts || {}\n cfg.channels.telegram.accounts[payload.telegram.accountId] = telegramAccountBlock(payload, telegramTokenRef!)\n }\n\n return { summary: `added agent \"${payload.agent.id}\"` + (payload.telegram ? \" + telegram account\" : \"\") }\n }, { configPath })\n backupPath = result.backupPath\n }\n\n // --- write / append .env ---\n ensureEnvEntries(envPath, payload, telegramTokenRef)\n\n const nextSteps: string[] = []\n if (payload.anthropicApiKey || !existsSync(resolve(baseDir, \".env\"))) {\n nextSteps.push(\"Double-check the API keys in .env\")\n }\n if (payload.agent.tier === \"claude-code\") {\n nextSteps.push(\"Run claude --version to confirm Claude Code is installed (needed for the claude-code engine)\")\n }\n nextSteps.push(\"Start the daemon: agentx daemon start\")\n nextSteps.push(\"Open the dashboard: http://127.0.0.1:4202\")\n\n return {\n summary: state.configExists\n ? `Added agent \"${payload.agent.id}\" to your team.`\n : `Initialized your team with first agent \"${payload.agent.id}\".`,\n nextSteps,\n backupPath,\n }\n}\n\nfunction splitMentions(s: string): string[] {\n return (s || \"\")\n .split(/[,\\s]+/)\n .map((m) => m.trim())\n .filter(Boolean)\n // Normalise: ensure @-prefix entries stay, plain ones are kept as-is.\n .filter((m, i, a) => a.indexOf(m) === i)\n}\n\nfunction slugify(s: string): string {\n return (s || \"\").toLowerCase().replace(/[^a-z0-9-]+/g, \"-\").replace(/^-+|-+$/g, \"\")\n}\n\nfunction buildFreshConfig(payload: WizardPayload, workspaceDir: string, mentions: string[], telegramTokenRef?: string) {\n const id = slugify(payload.nodeName) || \"team\"\n const cfg: any = {\n node: { id, name: payload.nodeName, bind: \"127.0.0.1:18800\", defaultAgent: payload.agent.id },\n providers: { claude: { apiKey: \"${ANTHROPIC_API_KEY}\" } },\n agents: { [payload.agent.id]: agentBlock(payload, workspaceDir, mentions) },\n channels: {},\n crons: {},\n mesh: { enabled: false, peers: [], discovery: \"static\", healthCheck: { interval: 60, timeout: 10 } },\n dashboard: { enabled: true, port: 4202, bind: \"127.0.0.1\", daemonUrl: \"http://localhost:18800\" },\n boards: [],\n }\n if (payload.telegram && telegramTokenRef) {\n cfg.channels.telegram = {\n enabled: true,\n accounts: { [payload.telegram.accountId]: telegramAccountBlock(payload, telegramTokenRef) },\n policy: { dm: \"pair\", group: \"mention-required\" },\n }\n } else {\n cfg.channels.telegram = { enabled: false, accounts: {}, policy: { dm: \"pair\", group: \"mention-required\" } }\n }\n return cfg\n}\n\nfunction agentBlock(payload: WizardPayload, workspaceDir: string, mentions: string[]) {\n const block: any = {\n name: payload.agent.name,\n workspace: workspaceDir,\n tier: payload.agent.tier,\n mentions,\n maxConcurrent: 2,\n permissionMode: \"default\",\n }\n if (payload.agent.model) block.model = payload.agent.model\n if (payload.agent.personality?.trim()) block.systemPrompt = payload.agent.personality.trim().slice(0, 400)\n return block\n}\n\nfunction telegramAccountBlock(payload: WizardPayload, tokenRef: string) {\n return {\n botToken: \"${\" + tokenRef + \"}\",\n botUsername: payload.telegram?.botUsername || \"\",\n agentBinding: payload.agent.id,\n }\n}\n\nfunction ensureEnvEntries(envPath: string, payload: WizardPayload, telegramTokenRef?: string): void {\n const existing = existsSync(envPath) ? readFileSync(envPath, \"utf-8\") : \"\"\n const lines: string[] = []\n\n if (payload.anthropicApiKey && !existing.includes(\"ANTHROPIC_API_KEY=\")) {\n lines.push(`ANTHROPIC_API_KEY=${payload.anthropicApiKey}`)\n }\n if (payload.telegram && telegramTokenRef && !existing.includes(`${telegramTokenRef}=`)) {\n lines.push(`${telegramTokenRef}=${payload.telegram.botToken}`)\n }\n\n if (!existsSync(envPath)) {\n const header = [\n \"# AgentX environment variables\",\n \"# (Never commit this file — it contains secrets.)\",\n \"\",\n ]\n writeFileSync(envPath, [...header, ...lines, \"\"].join(\"\\n\"))\n return\n }\n if (lines.length > 0) {\n const sep = existing.endsWith(\"\\n\") ? \"\" : \"\\n\"\n appendFileSync(envPath, sep + lines.join(\"\\n\") + \"\\n\")\n }\n}\n\n// --- HTTP handlers used by board-dashboard.ts ---\n\nexport function handleWizardGet(_req: IncomingMessage, res: ServerResponse): void {\n const state = wizardState()\n const defaultNode = state.nodeName || hostname().replace(/\\.local$/, \"\")\n const html = renderSetupPage(state, defaultNode)\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" })\n res.end(html)\n}\n\nexport async function handleWizardPost(req: IncomingMessage, res: ServerResponse): Promise<void> {\n let raw = \"\"\n await new Promise<void>((ok) => { req.on(\"data\", (c) => { raw += c }); req.on(\"end\", () => ok()) })\n let payload: WizardPayload\n try { payload = JSON.parse(raw) }\n catch { res.writeHead(400, { \"Content-Type\": \"application/json\" }); res.end(JSON.stringify({ error: \"Invalid JSON\" })); return }\n try {\n const result = runWizard(payload)\n res.writeHead(200, { \"Content-Type\": \"application/json\" })\n res.end(JSON.stringify(result))\n } catch (e: any) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" })\n res.end(JSON.stringify({ error: e.message || \"wizard failed\" }))\n }\n}\n\n","import { existsSync, readFileSync, writeFileSync, copyFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { loadDaemonConfig } from \"./config\"\n\n// --- Shared helpers for safe agentx.json mutation ---\n//\n// Multiple CLI commands and the dashboard admin UI all need to read → tweak →\n// write agentx.json without corrupting it on failure. This module owns the\n// atomic-write + backup + best-effort-reload pattern so every caller gets the\n// same guarantees.\n\nexport interface MutateOptions {\n /** Absolute path to the target agentx.json. Defaults to ./agentx.json. */\n configPath?: string\n /** When true, don't write a timestamped backup. Use for operations that are\n * intrinsically reversible (or when the caller already took a snapshot). */\n skipBackup?: boolean\n /** When true, don't POST /reload after writing. Handy in tests. */\n skipReload?: boolean\n}\n\nexport interface MutateResult<T = void> {\n summary: string\n payload?: T\n}\n\n/**\n * Read agentx.json, let the mutator tweak it in-place, then atomically write\n * the result back to disk. The mutator returns a short human-readable summary\n * of what changed and may optionally return a payload for the caller to relay.\n *\n * If the mutator throws, nothing is written. If writing succeeds but the file\n * doesn't round-trip as JSON, we restore from backup and rethrow.\n */\nexport function mutateAgentxConfig<T = void>(\n mutator: (cfg: any) => MutateResult<T> | string,\n opts: MutateOptions = {},\n): { summary: string; payload?: T; backupPath?: string } {\n const file = opts.configPath || resolve(process.cwd(), \"agentx.json\")\n if (!existsSync(file)) throw new Error(`agentx.json not found at ${file}`)\n\n const cfg = JSON.parse(readFileSync(file, \"utf-8\"))\n const raw = mutator(cfg)\n const result: MutateResult<T> = typeof raw === \"string\" ? { summary: raw } : raw\n\n const json = JSON.stringify(cfg, null, 2) + \"\\n\"\n const tmp = file + \".tmp\"\n writeFileSync(tmp, json, \"utf-8\")\n try {\n JSON.parse(readFileSync(tmp, \"utf-8\"))\n } catch (e: any) {\n throw new Error(`config did not round-trip cleanly: ${e.message}`)\n }\n\n let backupPath: string | undefined\n if (!opts.skipBackup) {\n backupPath = `${file}.bak.${Date.now()}`\n copyFileSync(file, backupPath)\n }\n writeFileSync(file, json, \"utf-8\")\n\n if (!opts.skipReload) triggerLocalReload().catch(() => { /* daemon may be off */ })\n\n return { summary: result.summary, payload: result.payload, backupPath }\n}\n\n/**\n * Write a fresh agentx.json from scratch. Used by the setup wizard on a truly\n * empty working directory. Fails if a file already exists (callers must pass\n * the overwrite flag explicitly — we don't want to silently clobber an\n * operator's hand-written config).\n */\nexport function writeAgentxConfig(\n cfg: unknown,\n opts: { configPath?: string; overwrite?: boolean } = {},\n): { file: string } {\n const file = opts.configPath || resolve(process.cwd(), \"agentx.json\")\n if (existsSync(file) && !opts.overwrite) {\n throw new Error(`agentx.json already exists at ${file}. Pass overwrite=true to replace it.`)\n }\n writeFileSync(file, JSON.stringify(cfg, null, 2) + \"\\n\", \"utf-8\")\n return { file }\n}\n\n/**\n * POST to the primary daemon's /reload endpoint so it picks up config changes\n * without a full restart. Silently no-ops if the daemon isn't running.\n */\nasync function triggerLocalReload(): Promise<void> {\n try {\n const cfg = loadDaemonConfig()\n const url = cfg.dashboard.daemonUrl?.replace(/\\/+$/, \"\") || \"http://127.0.0.1:18800\"\n await fetch(`${url}/reload`, { method: \"POST\" }).catch(() => null)\n } catch { /* config didn't load — leave it */ }\n}\n","// --- Setup wizard page ---\n//\n// First-run flow for non-technical operators: team name, first agent,\n// optional Telegram, optional Anthropic key. Writes agentx.json, scaffolds\n// the agent's workspace, writes .env, and points the operator at the next\n// step.\n//\n// The server-side state machine + form handler live in ../../setup-wizard.ts\n// — this file owns ONLY the HTML. That keeps the data flow inspectable\n// (one place) and the visual surface hot-reloadable (edit → rebuild → view).\n\nimport { renderShell, esc, stepCard, field, row, btn } from \"..\"\nimport type { WizardState } from \"../../setup-wizard\"\n\nexport function renderSetupPage(state: WizardState, defaultNode: string): string {\n const heading = state.configExists\n ? (state.agentCount === 0 ? \"Add your first agent\" : \"Add another agent\")\n : \"Set up your team\"\n\n // Custom topbar: /setup is pre-dashboard, we skip the tab nav and show\n // just brand + subtitle + theme switcher + \"Skip to dashboard\" pill.\n const customHeader = `<header class=\"ax-topbar\">\n <div class=\"ax-topbar__left\">\n <div class=\"ax-brand\">\n <span class=\"ax-brand__mark\">AX</span>\n <span class=\"ax-brand__name\">AgentX</span>\n <span class=\"ax-brand__subtitle\">Setup</span>\n </div>\n </div>\n <div class=\"ax-topbar__right\">\n <a href=\"/glossary\" class=\"ax-skip\">? Glossary</a>\n <a href=\"/live\" class=\"ax-skip\">Skip — open dashboard</a>\n <div class=\"ax-theme-switch\" role=\"tablist\" aria-label=\"Theme\">\n <button data-theme-opt=\"dark\">Dark</button>\n <button data-theme-opt=\"light\">Light</button>\n <button data-theme-opt=\"crt\">CRT</button>\n </div>\n </div>\n</header>`\n\n const step1 = stepCard(1, \"Team basics\",\n field({\n name: \"nodeName\",\n label: \"Team name\",\n hint: \"(what you'd call this AgentX install)\",\n value: defaultNode,\n placeholder: \"My Team\",\n required: true,\n })\n )\n\n const step2 = stepCard(2, \"First agent\",\n row([\n field({ name: \"agentName\", label: \"Agent name\", value: \"Assistant\", placeholder: \"Support Bot\", required: true }),\n field({ name: \"agentId\", label: \"Agent id\", hint: \"(lowercase, no spaces)\", value: \"assistant\", pattern: \"[a-z0-9][a-z0-9_-]*\", required: true }),\n ]) +\n field({\n name: \"triggerWords\",\n label: \"Trigger words\",\n hint: `(comma or space separated — e.g. <code>@support, support</code>)`,\n value: \"@assistant, assistant\",\n required: true,\n }) +\n row([\n field({\n name: \"tier\",\n label: \"AI engine\",\n type: \"select\",\n options: [\n { value: \"claude-code\", label: \"Claude Code (recommended)\", selected: true },\n { value: \"sdk\", label: \"Anthropic API (BYO key)\" },\n ],\n }),\n field({\n name: \"model\",\n label: \"Model\",\n hint: \"(optional)\",\n value: \"claude-sonnet-4-6\",\n placeholder: \"claude-sonnet-4-6\",\n }),\n ]) +\n field({\n name: \"personality\",\n label: \"Personality / instructions\",\n hint: \"(optional — what this agent does, in plain English)\",\n type: \"textarea\",\n placeholder: \"You are a support agent for Acme Co. Answer customer questions about our product, be friendly, keep replies short.\",\n })\n )\n\n const step3 = stepCard(3, \"First channel\", `\n <label class=\"ax-toggle\"><input type=\"checkbox\" id=\"enableTelegram\" /> Connect Telegram now</label>\n <div class=\"ax-optional\" id=\"telegramFields\">\n ${field({ name: \"telegramAccountId\", label: \"Account id\", hint: `(a label you choose — e.g. <code>support</code>)`, value: \"default\" })}\n ${field({ name: \"telegramBotToken\", label: \"Bot token\", hint: `(from <a href=\"https://t.me/BotFather\" target=\"_blank\">@BotFather</a>)`, placeholder: \"123456:ABCdef...\" })}\n ${field({ name: \"telegramBotUsername\", label: \"Bot username\", hint: `(optional — e.g. <code>my_support_bot</code>)`, placeholder: \"my_support_bot\" })}\n </div>\n `)\n\n const step4 = stepCard(4, \"Anthropic API key\",\n field({\n name: \"anthropicApiKey\",\n label: \"API key\",\n hint: \"(optional — skip if you're using Claude Code)\",\n type: \"password\",\n placeholder: \"sk-ant-…\",\n extraAttrs: `autocomplete=\"off\"`,\n }) +\n `<div class=\"ax-hint-block\">\n The key is written to <code>.env</code> as <code>ANTHROPIC_API_KEY</code>. AgentX never transmits it — everything stays on this machine.\n </div>`\n )\n\n const banner = state.configExists\n ? `<div class=\"ax-banner\">Existing install: <b>${state.agentCount}</b> agent(s), <b>${state.channelCount}</b> channel(s) already configured. This wizard will add to it.</div>`\n : \"\"\n\n const body = `<div class=\"ax-wizard\">\n <h1 class=\"ax-wizard__h1\">${esc(heading)}</h1>\n <p class=\"ax-wizard__lead\">Fill in the basics below. We'll write <code>agentx.json</code>, set up a folder for your first agent, and point you at the next step.</p>\n\n ${banner}\n\n <div id=\"msg\" class=\"ax-msg\"></div>\n\n <form id=\"wizard\" autocomplete=\"off\">\n ${step1}\n ${step2}\n ${step3}\n ${step4}\n <footer class=\"ax-actions\">\n ${btn(\"Save and continue\", { type: \"submit\", primary: true, id: \"submitBtn\" })}\n ${btn(\"Skip, I'll do it later\", { onclick: \"window.location.href='/live'\" })}\n </footer>\n </form>\n</div>`\n\n return renderShell({\n title: \"AgentX — Setup\",\n activeTab: \"custom\",\n subtitle: \"Setup\",\n customHeader,\n noMain: true,\n body,\n css: SETUP_PAGE_CSS,\n scripts: SETUP_SCRIPT,\n })\n}\n\n/** CSS that's specific to the setup page — wizard layout + optional-fields\n * toggle + result messaging. Everything reusable (form fields, step cards,\n * buttons) comes from AX_COMPONENTS_CSS. */\nconst SETUP_PAGE_CSS = `\n/* Pre-dashboard topbar variant — no tab nav, the pills are the only chrome. */\n.ax-topbar__right a.ax-skip {\n padding: 4px 10px; border: 1px solid var(--ax-border-2); border-radius: 4px;\n color: var(--ax-text-2); font-size: var(--ax-fs-xs); text-decoration: none;\n}\n.ax-topbar__right a.ax-skip:hover { color: var(--ax-accent); border-color: var(--ax-accent); }\n\n/* Wizard frame */\n.ax-wizard { max-width: 680px; margin: 0 auto; padding: 36px 22px 80px; }\n.ax-wizard__h1 { font-size: 22px; font-weight: 600; margin: 0 0 4px; letter-spacing: -0.015em; }\n.ax-wizard__lead { color: var(--ax-muted); margin: 0 0 24px; font-size: var(--ax-fs); line-height: 1.55; }\n.ax-wizard__lead code { background: var(--ax-surface-2); padding: 1px 6px; border-radius: 3px; color: var(--ax-text); font-size: var(--ax-fs-sm); }\n\n/* Info banner (\"You already have N agents…\") */\n.ax-banner {\n background: color-mix(in oklch, var(--ax-accent) 10%, var(--ax-bg-elev));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 30%, var(--ax-border-2));\n border-radius: var(--ax-radius);\n padding: 10px 14px; margin-bottom: 18px;\n color: var(--ax-text-2); font-size: var(--ax-fs-sm);\n}\n.ax-banner b { color: var(--ax-text); font-weight: 600; }\n\n/* \"Connect Telegram now\" toggle + collapsible fields */\n.ax-toggle { display: flex; align-items: center; gap: 8px; margin: 4px 0 6px; font-size: var(--ax-fs); color: var(--ax-text); cursor: pointer; user-select: none; }\n.ax-toggle input { width: auto; accent-color: var(--ax-accent); }\n.ax-optional { border-left: 2px solid var(--ax-border-2); padding-left: 14px; margin-top: 10px; display: none; }\n.ax-optional.is-show { display: block; }\n.ax-hint-block { font-size: var(--ax-fs-xs); color: var(--ax-muted); margin-top: 8px; line-height: 1.55; }\n.ax-hint-block code { background: var(--ax-surface-2); padding: 1px 6px; border-radius: 3px; color: var(--ax-text); font-family: var(--ax-mono); font-size: var(--ax-fs-xs); }\n\n/* Form footer */\n.ax-actions {\n display: flex; gap: 10px; padding: 20px 0 0; align-items: center;\n border-top: 1px solid var(--ax-border); margin-top: 20px;\n}\n\n/* Server-response messaging (success / error blocks) */\n.ax-msg { margin: 14px 0; padding: 12px 16px; border-radius: var(--ax-radius); font-size: var(--ax-fs); display: none; }\n.ax-msg.is-ok {\n display: block;\n background: color-mix(in oklch, var(--ax-accent) 12%, transparent);\n color: var(--ax-accent);\n border: 1px solid color-mix(in oklch, var(--ax-accent) 40%, transparent);\n}\n.ax-msg.is-err {\n display: block;\n background: color-mix(in oklch, var(--ax-err) 12%, transparent);\n color: var(--ax-err);\n border: 1px solid color-mix(in oklch, var(--ax-err) 40%, transparent);\n}\n.ax-next-steps { margin-top: 12px; padding-left: 20px; color: var(--ax-text-2); }\n.ax-next-steps li { margin: 4px 0; }\n.ax-next-steps code { background: var(--ax-surface-2); padding: 1px 6px; border-radius: 3px; color: var(--ax-text); font-family: var(--ax-mono); font-size: var(--ax-fs-sm); }`\n\nconst SETUP_SCRIPT = `<script>\ndocument.getElementById('enableTelegram').addEventListener('change', (e) => {\n document.getElementById('telegramFields').classList.toggle('is-show', e.target.checked);\n});\ndocument.getElementById('wizard').addEventListener('submit', async (e) => {\n e.preventDefault();\n const f = e.target;\n const msg = document.getElementById('msg');\n const btn = document.getElementById('submitBtn');\n msg.className = 'ax-msg'; msg.style.display = 'none';\n btn.disabled = true; btn.textContent = 'Saving…';\n const telegramEnabled = document.getElementById('enableTelegram').checked;\n const payload = {\n nodeName: f.nodeName.value.trim(),\n agent: {\n id: f.agentId.value.trim(),\n name: f.agentName.value.trim(),\n triggerWords: f.triggerWords.value.trim(),\n tier: f.tier.value,\n model: f.model.value.trim() || undefined,\n personality: f.personality.value.trim() || undefined,\n },\n anthropicApiKey: f.anthropicApiKey.value.trim() || undefined,\n };\n if (telegramEnabled) {\n payload.telegram = {\n accountId: f.telegramAccountId.value.trim() || 'default',\n botToken: f.telegramBotToken.value.trim(),\n botUsername: f.telegramBotUsername.value.trim() || undefined,\n };\n if (!payload.telegram.botToken) {\n msg.className = 'ax-msg is-err'; msg.textContent = 'Bot token is required when Telegram is enabled.';\n btn.disabled = false; btn.textContent = 'Save and continue'; return;\n }\n }\n try {\n const r = await fetch('/api/setup', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'agentx-wizard' },\n body: JSON.stringify(payload),\n });\n const data = await r.json();\n if (!r.ok) throw new Error(data.error || ('HTTP ' + r.status));\n const steps = data.nextSteps.map((s) => '<li>' + s.replace(/\\\\\\`([^\\\\\\`]+)\\\\\\`/g, '<code>$1</code>') + '</li>').join('');\n msg.className = 'ax-msg is-ok';\n msg.innerHTML = '<b>' + data.summary + '</b><ol class=\"ax-next-steps\">' + steps + '</ol>';\n btn.textContent = 'Done';\n btn.disabled = true;\n } catch (err) {\n msg.className = 'ax-msg is-err'; msg.textContent = err.message;\n btn.disabled = false; btn.textContent = 'Save and continue';\n }\n});\n</script>`\n","import { createHash, randomBytes } from \"crypto\"\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\"\nimport { resolve, dirname } from \"path\"\n\n// --- Scoped API tokens ---\n//\n// Tokens let external services (other agentx instances, webhook senders, custom\n// apps) talk to this daemon with explicit scopes instead of sharing a single\n// all-powerful dashboard token.\n//\n// We only ever store the sha256 of each token + its printable prefix. The full\n// token is shown exactly once, at creation time, and the operator copies it.\n// Format: agx_live_<hex(32)> — prefix lets secret-scanners catch leaks.\n//\n// Scopes (string equality; \"agent:*\" grants any agent):\n// dashboard:read — /api/live, /api/agents, /api/task/history\n// dashboard:write — /api/admin/*, config mutation endpoints\n// agent:<id> — /api/public/agents/<id>/messages\n// agent:* — any agent\n// mesh:peer — cross-node mesh (forward-compatible — not enforced yet)\n\nexport const TOKEN_PREFIX = \"agx_live_\"\nconst DEFAULT_FILE = \".agentx/tokens.json\"\n\nexport interface TokenRecord {\n id: string // short public id (shown in lists)\n name: string\n prefix: string // full printable prefix (TOKEN_PREFIX + first-4 chars of body)\n hash: string // sha256 of the full token\n scopes: string[]\n createdAt: string\n expiresAt?: string\n revokedAt?: string\n lastUsedAt?: string\n}\n\nexport class TokenStore {\n private file: string\n\n constructor(baseDir: string = process.cwd(), fileRel: string = DEFAULT_FILE) {\n this.file = resolve(baseDir, fileRel)\n }\n\n private load(): TokenRecord[] {\n if (!existsSync(this.file)) return []\n try { return JSON.parse(readFileSync(this.file, \"utf-8\")) as TokenRecord[] } catch { return [] }\n }\n\n private save(records: TokenRecord[]): void {\n mkdirSync(dirname(this.file), { recursive: true })\n writeFileSync(this.file, JSON.stringify(records, null, 2) + \"\\n\", \"utf-8\")\n }\n\n /**\n * Mint a new token. Returns the full token string AND the record.\n * The full token is the only place the secret is ever exposed — the caller\n * must surface it to the operator immediately; after this call the store only\n * knows the hash.\n */\n create(input: {\n name: string\n scopes: string[]\n expiresInDays?: number\n }): { token: string; record: TokenRecord } {\n if (!input.name?.trim()) throw new Error(\"Token name is required.\")\n if (!Array.isArray(input.scopes) || input.scopes.length === 0) {\n throw new Error(\"At least one scope is required.\")\n }\n for (const s of input.scopes) {\n if (!isValidScope(s)) throw new Error(`Invalid scope: ${s}`)\n }\n const body = randomBytes(32).toString(\"hex\")\n const token = TOKEN_PREFIX + body\n const record: TokenRecord = {\n id: \"tok_\" + randomBytes(4).toString(\"hex\"),\n name: input.name.trim(),\n prefix: TOKEN_PREFIX + body.slice(0, 4),\n hash: hashToken(token),\n scopes: [...input.scopes],\n createdAt: new Date().toISOString(),\n expiresAt: input.expiresInDays\n ? new Date(Date.now() + input.expiresInDays * 86400 * 1000).toISOString()\n : undefined,\n }\n const records = this.load()\n records.push(record)\n this.save(records)\n return { token, record }\n }\n\n list(): TokenRecord[] {\n return this.load()\n }\n\n revoke(id: string): TokenRecord | null {\n const records = this.load()\n const rec = records.find((r) => r.id === id)\n if (!rec) return null\n rec.revokedAt = new Date().toISOString()\n this.save(records)\n return rec\n }\n\n /**\n * Look up a token string. Returns the record on success, null on unknown /\n * revoked / expired. Updates lastUsedAt opportunistically (write-on-success).\n */\n verify(token: string): TokenRecord | null {\n if (!token || !token.startsWith(TOKEN_PREFIX)) return null\n const hash = hashToken(token)\n const records = this.load()\n const rec = records.find((r) => r.hash === hash)\n if (!rec) return null\n if (rec.revokedAt) return null\n if (rec.expiresAt && Date.parse(rec.expiresAt) < Date.now()) return null\n // Throttle lastUsedAt updates to at most once per minute to avoid disk churn.\n const now = Date.now()\n const last = rec.lastUsedAt ? Date.parse(rec.lastUsedAt) : 0\n if (now - last > 60_000) {\n rec.lastUsedAt = new Date(now).toISOString()\n try { this.save(records) } catch { /* best-effort */ }\n }\n return rec\n }\n}\n\n/**\n * Check whether a record has a given required scope. \"agent:<id>\" required\n * matches either itself or \"agent:*\". \"dashboard:write\" implicitly grants\n * \"dashboard:read\" for convenience.\n */\nexport function recordHasScope(record: TokenRecord, required: string): boolean {\n if (record.scopes.includes(required)) return true\n if (required.startsWith(\"agent:\") && record.scopes.includes(\"agent:*\")) return true\n if (required === \"dashboard:read\" && record.scopes.includes(\"dashboard:write\")) return true\n return false\n}\n\nfunction isValidScope(s: string): boolean {\n return (\n s === \"dashboard:read\" ||\n s === \"dashboard:write\" ||\n s === \"agent:*\" ||\n /^agent:[a-z0-9][a-z0-9_-]*$/.test(s) ||\n s === \"mesh:peer\"\n )\n}\n\nexport function hashToken(token: string): string {\n return \"sha256:\" + createHash(\"sha256\").update(token).digest(\"hex\")\n}\n\n/**\n * Extract a bearer token from the request headers. Falls back to the\n * `?token=` query param so EventSource (which can't set headers in most\n * browsers) can still authenticate.\n */\nexport function extractToken(req: { headers: Record<string, string | string[] | undefined>; url?: string }): string | null {\n const auth = req.headers[\"authorization\"]\n const authHeader = Array.isArray(auth) ? auth[0] : auth\n if (authHeader && authHeader.toLowerCase().startsWith(\"bearer \")) {\n return authHeader.slice(7).trim()\n }\n if (req.url) {\n try {\n const u = new URL(req.url, \"http://localhost\")\n const tok = u.searchParams.get(\"token\")\n if (tok) return tok\n } catch { /* */ }\n }\n return null\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from \"http\"\nimport { WikiStore } from \"./store\"\nimport { WikiHub } from \"./hub\"\nimport { MeshWikiClient } from \"./mesh\"\nimport type { AgentWikiSummary } from \"./hub\"\n\n// --- Lightweight Markdown → HTML (no deps) ---\n\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\")\n}\n\n/**\n * Strip legacy section-tag comments from the article body. The old absorb\n * prompt told the LLM to add `<!-- tags: runbook, staging -->` inside\n * sections; in the Farzapedia-aligned design, these are no longer\n * load-bearing and leak through as visible literal text once escaped.\n */\nfunction stripSectionTags(text: string): string {\n return text.replace(/<!--\\s*tags?:[^>]*-->\\s*\\n?/gi, \"\")\n}\n\n/** Render a single tag as a clickable link to the tag-filter route. */\nfunction tagLink(tag: string, agentId?: string): string {\n const href = agentId\n ? `/agent/${encodeURIComponent(agentId)}/tag/${encodeURIComponent(tag)}`\n : `/tag/${encodeURIComponent(tag)}`\n return `<a href=\"${href}\" class=\"tag\">${escapeHtml(tag)}</a>`\n}\n\n/** Render an array of tags as clickable links, space-separated. */\nfunction tagLinks(tags: string[] | undefined, agentId?: string): string {\n return (tags || []).map(t => tagLink(t, agentId)).join(\" \")\n}\n\n/**\n * Resolve the body of a `[[...]]` wikilink to a relative article path.\n *\n * Authors write wikilinks three ways: by title, by path (without `.md`), or\n * by full path (with `.md`). wikiArticles is a title→path map, so we build\n * a path-set on first call and try several normalisations:\n *\n * 1. Exact title lookup (handles [[My Article]])\n * 2. Exact path lookup (handles [[people/anis.md]])\n * 3. Path + \".md\" suffix (handles [[people/anis]])\n * 4. Case-insensitive title scan (forgives minor casing drift)\n *\n * Return the path if any matches, else undefined so the caller can render\n * a broken-link marker.\n */\nfunction resolveWikilink(target: string, wikiArticles: Map<string, string>): string | undefined {\n if (!target) return undefined\n // (1) exact title\n const byTitle = wikiArticles.get(target)\n if (byTitle) return byTitle\n\n // (2) + (3) path lookups\n const pathSet = new Set(wikiArticles.values())\n if (pathSet.has(target)) return target\n const withExt = target.endsWith('.md') ? target : `${target}.md`\n if (pathSet.has(withExt)) return withExt\n\n // (4) case-insensitive title fallback — common when the user writes\n // \"Anis Marrouchi\" but the canonical title is \"anis marrouchi\".\n const targetLower = target.toLowerCase()\n for (const [title, path] of wikiArticles) {\n if (title.toLowerCase() === targetLower) return path\n }\n return undefined\n}\n\nfunction md(text: string, wikiArticles: Map<string, string>, agentPrefix: string = \"\"): string {\n // Strip section-tag HTML comments before escaping so they don't render\n // as literal \"<!-- tags: ... -->\" in the output.\n let html = escapeHtml(stripSectionTags(text))\n\n // Code blocks (fenced)\n html = html.replace(/```(\\w*)\\n([\\s\\S]*?)```/g, (_m, lang, code) =>\n `<pre><code class=\"lang-${lang}\">${code.trim()}</code></pre>`)\n\n // Inline code\n html = html.replace(/`([^`]+)`/g, '<code>$1</code>')\n\n // Tables\n html = html.replace(/^(\\|.+\\|)\\n(\\|[-| :]+\\|)\\n((?:\\|.+\\|\\n?)*)/gm, (_m, header, _sep, body) => {\n const ths = header.split(\"|\").filter((c: string) => c.trim()).map((c: string) => `<th>${c.trim()}</th>`).join(\"\")\n const rows = body.trim().split(\"\\n\").map((row: string) => {\n const tds = row.split(\"|\").filter((c: string) => c.trim()).map((c: string) => `<td>${c.trim()}</td>`).join(\"\")\n return `<tr>${tds}</tr>`\n }).join(\"\")\n return `<table><thead><tr>${ths}</tr></thead><tbody>${rows}</tbody></table>`\n })\n\n // Headings\n html = html.replace(/^#### (.+)$/gm, '<h4>$1</h4>')\n html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>')\n html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>')\n html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>')\n\n // Bold and italic\n html = html.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n html = html.replace(/\\*(.+?)\\*/g, '<em>$1</em>')\n\n // Wikilinks — support three forms for the [[...]] body:\n // [[Title]] exact article title\n // [[path/to/article]] path without .md (e.g. \"people/anis\")\n // [[path/to/article.md]] full relative path\n // [[Title|custom display text]] optional display override (Obsidian-style)\n html = html.replace(/\\[\\[([^\\]]+)\\]\\]/g, (_m, body: string) => {\n const [rawTarget, rawDisplay] = body.split(\"|\", 2)\n const target = rawTarget.trim().replace(/^\\//, \"\")\n const display = (rawDisplay || rawTarget).trim()\n const path = resolveWikilink(target, wikiArticles)\n if (path) {\n return `<a href=\"${agentPrefix}/article/${encodeURIComponent(path)}\" class=\"wikilink\">${display}</a>`\n }\n return `<a href=\"${agentPrefix}/search?q=${encodeURIComponent(target)}\" class=\"wikilink broken\" title=\"Not found — click to search\">${display}</a>`\n })\n\n // External links\n html = html.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href=\"$2\" target=\"_blank\">$1</a>')\n\n // Blockquotes (consecutive `> ` lines → one <blockquote>)\n html = html.replace(/^(>\\s?.*(?:\\n>\\s?.*)*)/gm, (block: string) => {\n const inner = block.split(\"\\n\").map(l => l.replace(/^>\\s?/, \"\")).join(\"<br>\")\n return `<blockquote>${inner}</blockquote>`\n })\n\n // Task-list items (render as plain text with unicode checkbox)\n html = html.replace(/^[-*] \\[( |x|X)\\] (.+)$/gm, (_m, chk, rest) =>\n `<li class=\"task\">${chk.toLowerCase() === \"x\" ? \"☑\" : \"☐\"} ${rest}</li>`)\n\n // Unordered lists\n html = html.replace(/^[-*] (.+)$/gm, '<li>$1</li>')\n\n // Ordered lists — match lines like \"1. foo\" (number, dot, space)\n html = html.replace(/^\\d+\\. (.+)$/gm, '<li data-ordered=\"1\">$1</li>')\n\n // Wrap consecutive ordered <li>s into <ol>. Keep the data-ordered marker\n // on each <li> inside the <ol> so the subsequent <ul> regex below can\n // correctly skip them via negative lookahead. Marker is stripped once all\n // list-wrapping is done.\n html = html.replace(/(<li data-ordered=\"1\">.*?<\\/li>(?:\\n?<li data-ordered=\"1\">.*?<\\/li>)*)/g, '<ol>$1</ol>')\n\n // Wrap consecutive unordered <li>s — the negative lookahead `(?! data-ordered)`\n // ensures ordered <li>s inside the <ol> above are NOT re-wrapped in <ul>.\n html = html.replace(/((?:<li(?! data-ordered)[^>]*>.*?<\\/li>\\n?)+)/g, '<ul>$1</ul>')\n\n // Clean up the ordered marker now that wrapping is settled.\n html = html.replace(/ data-ordered=\"1\"/g, \"\")\n\n // Horizontal rules\n html = html.replace(/^---$/gm, '<hr>')\n\n // Paragraphs — only wrap non-block lines. A paragraph is a run of\n // text that isn't already wrapped in a block-level tag.\n html = html.replace(/\\n\\n+/g, '</p><p>')\n html = `<p>${html}</p>`\n // Unwrap <p>...</p> that now contain block-level elements.\n html = html.replace(/<p>\\s*(<(?:h[1-6]|ul|ol|blockquote|table|pre|hr)\\b[^>]*>[\\s\\S]*?<\\/(?:h[1-6]|ul|ol|blockquote|table|pre)>)\\s*<\\/p>/g, \"$1\")\n html = html.replace(/<p>\\s*<hr>\\s*<\\/p>/g, \"<hr>\")\n // Drop empty paragraphs that remain.\n html = html.replace(/<p>\\s*<\\/p>/g, \"\")\n\n html = html.replace(/<p>(<(?:h[1-4]|pre|table|ul|hr|div))/g, '$1')\n html = html.replace(/(<\\/(?:h[1-4]|pre|table|ul|hr|div)>)<\\/p>/g, '$1')\n html = html.replace(/<p>\\s*<\\/p>/g, '')\n\n return html\n}\n\n// --- CSS ---\n\nconst HEAD_FONTS = `<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link href=\"https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,300..600;1,6..72,300..600&family=Inter+Tight:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap\" rel=\"stylesheet\">`\n\n// Paper/ink editorial palette — warm neutrals, Newsreader display font,\n// Inter Tight for UI, JetBrains Mono for code/meta. Ported from the\n// /Users/macbookpro/Downloads/wiki mockup.\nconst CSS = `\n:root {\n --bg: #FAF8F3;\n --bg-raised: #FFFFFF;\n --bg-sunk: #F2EFE7;\n --bg-hover: #EFEBE0;\n --ink: #1A1915;\n --ink-2: #3D3A32;\n --muted: #6B6860;\n --muted-2: #96918A;\n --line: #E4DED0;\n --line-2: #D1CAB8;\n --accent: oklch(55% 0.14 45);\n --accent-soft: oklch(55% 0.14 45 / 0.10);\n --accent-hover: oklch(48% 0.14 45);\n --cool: oklch(55% 0.08 230);\n --cool-soft: oklch(55% 0.08 230 / 0.10);\n --ok: oklch(55% 0.10 145);\n --warn: oklch(60% 0.14 75);\n --err: oklch(55% 0.16 20);\n --font-display: 'Newsreader', Georgia, serif;\n --font-ui: 'Inter Tight', -apple-system, system-ui, sans-serif;\n --font-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;\n --sidebar-w: 260px;\n --topbar-h: 52px;\n --radius: 6px;\n --radius-sm: 4px;\n --radius-lg: 10px;\n --shadow-sm: 0 1px 2px rgba(26,25,21,0.04), 0 1px 1px rgba(26,25,21,0.03);\n --shadow-md: 0 4px 12px rgba(26,25,21,0.06), 0 2px 4px rgba(26,25,21,0.04);\n --shadow-lg: 0 12px 40px rgba(26,25,21,0.12), 0 4px 12px rgba(26,25,21,0.06);\n}\n[data-theme=\"dark\"] {\n --bg: #0F0E0C;\n --bg-raised: #1A1815;\n --bg-sunk: #07060A;\n --bg-hover: #22201C;\n --ink: #EBE7DD;\n --ink-2: #C9C4B7;\n --muted: #8B867B;\n --muted-2: #5E5A52;\n --line: #2A2822;\n --line-2: #3A3830;\n --accent: oklch(70% 0.16 45);\n --accent-soft: oklch(70% 0.16 45 / 0.14);\n --accent-hover: oklch(78% 0.16 45);\n --cool: oklch(70% 0.10 230);\n --cool-soft: oklch(70% 0.10 230 / 0.14);\n --ok: oklch(70% 0.14 145);\n --warn: oklch(75% 0.16 75);\n --err: oklch(68% 0.18 20);\n --shadow-sm: 0 1px 2px rgba(0,0,0,0.4);\n --shadow-md: 0 4px 12px rgba(0,0,0,0.5);\n --shadow-lg: 0 12px 40px rgba(0,0,0,0.7);\n}\n\n* { box-sizing: border-box; margin: 0; padding: 0; }\nhtml, body {\n background: var(--bg); color: var(--ink);\n font-family: var(--font-ui); font-size: 14px; line-height: 1.55;\n font-feature-settings: 'ss01','cv11';\n -webkit-font-smoothing: antialiased; min-height: 100vh;\n}\na { color: var(--accent); text-decoration: none; }\na:hover { color: var(--accent-hover); }\nbutton { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }\ninput, textarea, select { font: inherit; color: inherit; }\ncode, pre, .mono { font-family: var(--font-mono); font-size: 0.92em; }\n.display { font-family: var(--font-display); font-weight: 400; letter-spacing: -0.01em; }\n\n/* ——— App shell ——— */\n.app {\n display: grid;\n grid-template-columns: var(--sidebar-w) 1fr;\n grid-template-rows: var(--topbar-h) 1fr;\n min-height: 100vh;\n}\n.topbar {\n grid-column: 1 / -1;\n display: flex; align-items: center; gap: 16px; padding: 0 20px;\n background: var(--bg); border-bottom: 1px solid var(--line);\n position: sticky; top: 0; z-index: 20; backdrop-filter: blur(8px);\n}\n.topbar-logo {\n display: flex; align-items: center; gap: 10px;\n font-family: var(--font-display); font-size: 18px; color: var(--ink);\n width: calc(var(--sidebar-w) - 20px); padding-right: 12px;\n border-right: 1px solid var(--line); height: 100%;\n text-decoration: none;\n}\n.topbar-logo .mark {\n width: 22px; height: 22px; flex: 0 0 auto;\n display: inline-flex; align-items: center; justify-content: center;\n background: var(--accent); color: var(--bg-raised);\n border-radius: 5px; font-family: var(--font-mono);\n font-size: 11px; font-weight: 700;\n}\n.topbar-search {\n flex: 1; max-width: 520px;\n display: flex; align-items: center; gap: 8px; padding: 6px 12px;\n background: var(--bg-raised); border: 1px solid var(--line);\n border-radius: var(--radius); color: var(--muted);\n transition: border-color 0.15s, background 0.15s;\n}\n.topbar-search:focus-within { border-color: var(--line-2); background: var(--bg-hover); }\n.topbar-search input {\n flex: 1; border: 0; outline: 0; background: transparent;\n color: var(--ink); font: inherit;\n}\n.topbar-search input::placeholder { color: var(--muted); }\n.topbar-search kbd {\n font-family: var(--font-mono); font-size: 11px;\n padding: 2px 6px; border: 1px solid var(--line); border-radius: 3px;\n background: var(--bg); color: var(--muted);\n}\n.topbar-tools { margin-left: auto; display: flex; align-items: center; gap: 4px; }\n.icon-btn {\n width: 32px; height: 32px;\n display: inline-flex; align-items: center; justify-content: center;\n border-radius: var(--radius-sm); color: var(--muted);\n transition: background 0.15s, color 0.15s;\n}\n.icon-btn:hover { background: var(--bg-hover); color: var(--ink); }\n.icon-btn.active { color: var(--accent); background: var(--accent-soft); }\n.icon-btn svg { width: 16px; height: 16px; }\n\n/* ——— Sidebar ——— */\n.sidebar {\n grid-column: 1; grid-row: 2;\n background: var(--bg); border-right: 1px solid var(--line);\n padding: 16px 12px 40px; overflow-y: auto;\n position: sticky; top: var(--topbar-h);\n height: calc(100vh - var(--topbar-h));\n}\n.nav-section { margin-bottom: 20px; }\n.nav-section-title {\n display: flex; align-items: center; justify-content: space-between;\n padding: 4px 10px; font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: 0.06em;\n color: var(--muted); margin-bottom: 2px;\n}\n.nav-item {\n display: flex; align-items: center; gap: 8px;\n padding: 5px 10px; color: var(--ink-2);\n font-size: 13px; border-radius: var(--radius-sm);\n line-height: 1.35; cursor: pointer;\n transition: background 0.1s;\n text-decoration: none;\n}\n.nav-item:hover { background: var(--bg-hover); color: var(--ink); }\n.nav-item.active {\n background: var(--accent-soft); color: var(--accent); font-weight: 500;\n}\n.nav-item .count {\n margin-left: auto; font-family: var(--font-mono);\n font-size: 11px; color: var(--muted);\n}\n.nav-item.active .count { color: var(--accent); }\n.nav-agent-header {\n display: flex; align-items: center; gap: 8px;\n padding: 6px 10px; font-size: 13px; color: var(--ink);\n cursor: pointer; border-radius: var(--radius-sm);\n text-decoration: none;\n}\n.nav-agent-header:hover { color: var(--accent); background: var(--bg-hover); }\n.nav-agent-header .name { font-weight: 500; }\n.nav-agent-header .badge {\n margin-left: auto; font-family: var(--font-mono);\n font-size: 10.5px; color: var(--muted);\n}\n.nav-agent-type-label {\n padding: 6px 8px 2px; font-size: 10px;\n text-transform: uppercase; letter-spacing: 0.08em;\n color: var(--muted-2); font-weight: 600;\n}\n.nav-agent-children {\n padding-left: 14px; display: flex; flex-direction: column; gap: 1px;\n margin-top: 2px; border-left: 1px dashed var(--line); margin-left: 10px;\n}\n\n/* ——— Main ——— */\n.main {\n grid-column: 2; grid-row: 2;\n padding: 32px 48px 80px;\n max-width: 1180px; width: 100%;\n}\n.main.article { max-width: 1100px; }\n\n/* ——— Breadcrumb ——— */\n.breadcrumb {\n display: flex; align-items: center; gap: 6px;\n font-size: 12.5px; color: var(--muted);\n margin-bottom: 14px; font-family: var(--font-ui);\n}\n.breadcrumb a { color: var(--muted); }\n.breadcrumb a:hover { color: var(--accent); }\n.breadcrumb .sep { color: var(--muted-2); }\n.breadcrumb .current { color: var(--ink-2); }\n\n/* ——— Display heading ——— */\n.page-head {\n display: flex; align-items: flex-end; justify-content: space-between;\n gap: 24px; padding-bottom: 16px;\n border-bottom: 1px solid var(--line); margin-bottom: 28px;\n}\n.page-head h1 {\n font-family: var(--font-display); font-size: 38px;\n font-weight: 400; letter-spacing: -0.015em; line-height: 1.1;\n}\n.page-head .page-sub {\n font-size: 13.5px; color: var(--muted);\n margin-top: 6px; max-width: 60ch; line-height: 1.5;\n}\n.page-head .page-actions { display: flex; gap: 8px; flex: 0 0 auto; }\n\n/* ——— Buttons ——— */\n.btn {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 6px 12px; font-size: 13px;\n color: var(--ink-2); background: var(--bg-raised);\n border: 1px solid var(--line); border-radius: var(--radius-sm);\n transition: all 0.1s; text-decoration: none;\n}\n.btn:hover { background: var(--bg-hover); border-color: var(--line-2); color: var(--ink); }\n.btn.primary { background: var(--ink); color: var(--bg); border-color: var(--ink); }\n.btn.primary:hover { background: var(--ink-2); color: var(--bg); }\n.btn.ghost { background: transparent; border-color: transparent; }\n.btn.ghost:hover { background: var(--bg-hover); }\n.btn svg { width: 14px; height: 14px; }\n\n/* ——— Stat strip ——— */\n.stats, .stat-strip {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 0;\n background: var(--bg-raised); border: 1px solid var(--line);\n border-radius: var(--radius); overflow: hidden;\n margin-bottom: 28px;\n}\n.stat-card, .stat-cell {\n padding: 16px 20px;\n border-right: 1px solid var(--line);\n display: flex; flex-direction: column; gap: 4px;\n background: transparent; text-align: left;\n}\n.stat-card:last-child, .stat-cell:last-child { border-right: none; }\n.stat-card .label, .stat-cell .label {\n font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em;\n color: var(--muted); font-weight: 600;\n}\n.stat-card .number, .stat-cell .number {\n font-family: var(--font-display); font-size: 32px; font-weight: 400;\n line-height: 1; color: var(--ink);\n}\n\n/* ——— Section heading ——— */\n.section-head, h2.section {\n display: flex; align-items: baseline; justify-content: space-between;\n margin: 40px 0 14px;\n}\n.section-head h2 {\n font-family: var(--font-display); font-size: 22px;\n font-weight: 400; letter-spacing: -0.01em; color: var(--ink);\n border: none; padding: 0;\n}\n\n/* ——— Agent grid (hub home) ——— */\n.agent-grid {\n display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n gap: 14px;\n}\n.agent-card {\n background: var(--bg-raised); border: 1px solid var(--line);\n border-radius: var(--radius); padding: 18px 20px;\n display: flex; flex-direction: column; gap: 12px;\n transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;\n}\n.agent-card:hover {\n border-color: var(--line-2); box-shadow: var(--shadow-md);\n transform: translateY(-1px);\n}\n.agent-card h3 {\n font-family: var(--font-display); font-size: 20px; font-weight: 400;\n color: var(--ink); margin: 0;\n}\n.agent-card h3 a { color: inherit; text-decoration: none; }\n.agent-card h3 a:hover { color: var(--accent); }\n.agent-card .agent-stats {\n display: flex; gap: 20px; font-size: 13px; color: var(--muted);\n}\n.agent-card .agent-stats strong {\n color: var(--ink); font-weight: 600; font-family: var(--font-mono);\n}\n.agent-card .article-list {\n display: flex; flex-direction: column; gap: 4px; margin-top: 4px; font-size: 13px;\n}\n.agent-card .article-row {\n display: flex; flex-wrap: wrap; align-items: center; gap: 4px;\n padding: 2px 0;\n}\n.agent-card .article-row .article-title {\n color: var(--ink-2); text-decoration: none; margin-left: 4px; flex: 1;\n}\n.agent-card .article-row .article-title:hover { color: var(--accent); }\n\n/* ——— Tags ——— */\n.tag {\n display: inline-flex; align-items: center;\n font-family: var(--font-mono); font-size: 11.5px;\n padding: 1px 7px 2px; background: var(--bg-sunk);\n color: var(--ink-2); border-radius: 3px;\n line-height: 1.4; cursor: pointer;\n transition: background 0.1s, color 0.1s;\n text-decoration: none;\n}\n.tag:hover { background: var(--accent-soft); color: var(--accent); }\n.tag.lg { font-size: 12.5px; padding: 3px 10px; }\n\n/* ——— Article meta — inline dl below h1 ——— */\n.meta {\n background: var(--bg-raised); border: 1px solid var(--line);\n border-radius: var(--radius); padding: 14px 18px;\n margin-bottom: 24px; font-size: 13px;\n display: grid; grid-template-columns: auto 1fr;\n gap: 6px 18px;\n}\n.meta dt {\n font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.08em;\n color: var(--muted); font-weight: 600;\n align-self: center;\n}\n.meta dd { color: var(--ink-2); font-family: var(--font-mono); font-size: 12.5px; }\n\n/* ——— Prose body ——— */\n.prose {\n font-size: 15.5px; line-height: 1.65; color: var(--ink-2);\n max-width: 68ch;\n}\n.main h1, .prose h1 {\n font-family: var(--font-display); font-size: 40px;\n font-weight: 400; letter-spacing: -0.015em; line-height: 1.1;\n color: var(--ink); margin-bottom: 16px;\n border: none; padding: 0;\n}\n.main h2, .prose h2 {\n font-family: var(--font-display); font-size: 26px;\n font-weight: 400; letter-spacing: -0.01em;\n margin: 36px 0 12px; color: var(--ink);\n padding-bottom: 4px; border-bottom: 1px solid var(--line);\n}\n.main h3, .prose h3 {\n font-family: var(--font-display); font-size: 20px; font-weight: 500;\n margin: 28px 0 8px; color: var(--ink);\n}\n.main h4, .prose h4 {\n font-size: 15px; font-weight: 600; margin: 20px 0 6px; color: var(--ink);\n}\n.main p, .prose p { margin: 10px 0; }\n.main ul, .prose ul, .main ol, .prose ol { margin: 10px 0 10px 22px; }\n.main li, .prose li { margin: 4px 0; }\n.main code, .prose code {\n background: var(--bg-sunk); padding: 1px 5px; border-radius: 3px;\n font-family: var(--font-mono);\n}\n.main pre, .prose pre {\n background: var(--bg-sunk); border: 1px solid var(--line);\n border-radius: var(--radius-sm); padding: 14px 16px;\n overflow-x: auto; margin: 14px 0; font-size: 13px;\n}\n.main pre code, .prose pre code { background: none; padding: 0; }\n.main blockquote, .prose blockquote {\n border-left: 3px solid var(--accent);\n padding: 4px 0 4px 14px; color: var(--muted);\n font-style: italic; margin: 14px 0;\n}\n\n/* Wikilinks in prose */\n.prose a.wikilink, .main a.wikilink {\n color: var(--accent);\n text-decoration: underline;\n text-decoration-color: var(--accent-soft);\n text-underline-offset: 3px;\n text-decoration-thickness: 1.5px;\n}\n.prose a.wikilink:hover, .main a.wikilink:hover { text-decoration-color: var(--accent); }\n.prose a.wikilink.broken, .main a.wikilink.broken {\n color: var(--err); text-decoration-color: var(--err);\n text-decoration-style: dashed;\n}\n\n/* ——— Tables ——— */\ntable {\n width: 100%; border-collapse: collapse;\n margin: 14px 0; font-size: 14px;\n}\nth, td {\n border-bottom: 1px solid var(--line);\n padding: 8px 12px; text-align: left;\n}\nth {\n background: var(--bg-sunk); font-weight: 600;\n color: var(--ink); font-size: 12.5px;\n text-transform: uppercase; letter-spacing: 0.04em;\n}\ntr:hover td { background: var(--bg-hover); }\n\n/* ——— Entry cards ——— */\n.entry-card {\n background: var(--bg-raised); border: 1px solid var(--line);\n border-radius: var(--radius);\n padding: 14px 18px; margin: 10px 0;\n transition: border-color 0.15s, box-shadow 0.15s;\n}\n.entry-card:hover { border-color: var(--line-2); box-shadow: var(--shadow-sm); }\n.entry-card .meta-line {\n font-size: 11.5px; color: var(--muted);\n margin-bottom: 6px; font-family: var(--font-mono);\n}\n.entry-card h3 {\n font-family: var(--font-display); font-size: 19px; font-weight: 400;\n color: var(--ink); margin: 4px 0 6px;\n border: none; padding: 0;\n}\n.entry-card h3 a { color: inherit; }\n.entry-card h3 a:hover { color: var(--accent); }\n.entry-card p {\n color: var(--ink-2); font-size: 13.5px;\n line-height: 1.55; margin: 4px 0;\n}\n\n/* ——— Backlinks ——— */\n.backlinks {\n margin-top: 40px; padding: 16px 18px;\n background: var(--bg-raised); border: 1px solid var(--line);\n border-radius: var(--radius);\n}\n.backlinks h4 {\n font-size: 11px; text-transform: uppercase;\n letter-spacing: 0.08em; color: var(--muted);\n font-weight: 600; margin-bottom: 8px;\n}\n.backlinks ul { list-style: none; padding: 0; margin: 0; }\n.backlinks li { padding: 4px 0; font-size: 13.5px; }\n\n/* ——— Health issues list ——— */\n.health-list { display: flex; flex-direction: column; gap: 6px; }\n.health-row {\n display: grid; grid-template-columns: 96px 1fr 2fr;\n gap: 14px; align-items: center;\n padding: 10px 14px; background: var(--bg-raised);\n border: 1px solid var(--line); border-radius: var(--radius-sm);\n font-size: 13px;\n}\n.health-row .kind {\n font-family: var(--font-mono); font-size: 10.5px;\n text-transform: uppercase; letter-spacing: 0.06em;\n color: var(--muted);\n}\n.health-row .article { color: var(--ink); font-weight: 500; }\n.health-row .msg { color: var(--ink-2); }\n\n@media (max-width: 900px) {\n .app { grid-template-columns: 1fr; }\n .topbar-logo { width: auto; border-right: none; }\n .sidebar { display: none; }\n .main { padding: 20px; grid-column: 1; }\n}\n`\n\n// --- HTML Templates ---\n\nfunction pageLayout(title: string, sidebar: string, content: string, searchQuery: string = \"\"): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>${escapeHtml(title)} — AgentX Wiki</title>\n ${HEAD_FONTS}\n <style>${CSS}</style>\n <script>(function(){try{var t=localStorage.getItem('wiki-theme');if(t)document.documentElement.setAttribute('data-theme',t)}catch(e){}})();</script>\n</head>\n<body>\n <div class=\"app\">\n <header class=\"topbar\">\n <a href=\"/\" class=\"topbar-logo\">\n <span class=\"mark\">W</span>\n <span class=\"display\">AgentX Wiki</span>\n </a>\n <form class=\"topbar-search\" action=\"/search\" method=\"get\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"7\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/></svg>\n <input type=\"search\" name=\"q\" placeholder=\"Search every wiki…\" value=\"${escapeHtml(searchQuery)}\" autocomplete=\"off\" />\n <kbd>⌘K</kbd>\n </form>\n <div class=\"topbar-tools\">\n <button class=\"icon-btn\" id=\"wiki-theme-toggle\" title=\"Toggle theme\" aria-label=\"Toggle theme\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/></svg>\n </button>\n </div>\n </header>\n <nav class=\"sidebar\">${sidebar}</nav>\n <main class=\"main\">${content}</main>\n </div>\n <script>\n (function(){\n var btn = document.getElementById('wiki-theme-toggle');\n if (!btn) return;\n btn.addEventListener('click', function(){\n var cur = document.documentElement.getAttribute('data-theme') === 'dark' ? '' : 'dark';\n if (cur) document.documentElement.setAttribute('data-theme', 'dark');\n else document.documentElement.removeAttribute('data-theme');\n try { localStorage.setItem('wiki-theme', cur); } catch(e){}\n });\n // Cmd/Ctrl-K focuses search\n document.addEventListener('keydown', function(e){\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {\n e.preventDefault();\n var inp = document.querySelector('.topbar-search input');\n if (inp) inp.focus();\n }\n });\n })();\n </script>\n</body>\n</html>`\n}\n\n// --- Hub Mode (all agents) ---\n\nfunction groupByDir(articles: Array<{ title: string; path: string; tags?: string[] }>): Map<string, typeof articles> {\n const byDir = new Map<string, typeof articles>()\n for (const a of articles) {\n const dir = a.path.includes(\"/\") ? a.path.split(\"/\").slice(0, -1).join(\"/\") : \"/\"\n const list = byDir.get(dir) || []\n list.push(a)\n byDir.set(dir, list)\n }\n return new Map([...byDir].sort((a, b) => a[0].localeCompare(b[0])))\n}\n\nfunction dirLabel(dir: string): string {\n if (dir === \"/\") return \"Root\"\n return dir.split(\"/\").pop()!.replace(/-/g, \" \")\n}\n\nfunction hubSidebar(agents: AgentWikiSummary[], activePath?: string): string {\n let html = '<div class=\"nav-section\">'\n html += `<a class=\"nav-item${!activePath ? ' active' : ''}\" href=\"/\">Home</a>`\n html += `<a class=\"nav-item\" href=\"/entries\">All entries</a>`\n html += '</div>'\n\n for (const agent of agents) {\n const agentPrefix = `/agent/${encodeURIComponent(agent.agentId)}`\n html += '<div class=\"nav-section\">'\n html += `<a class=\"nav-agent-header\" href=\"${agentPrefix}\">`\n html += `<span class=\"name\">${escapeHtml(agent.agentId)}</span>`\n html += `<span class=\"badge\">${agent.totalArticles}</span>`\n html += `</a>`\n html += '<div class=\"nav-agent-children\">'\n\n const byType = groupByDir(agent.articles)\n for (const [type, articles] of byType) {\n html += `<div class=\"nav-agent-type-label\">${dirLabel(type)}</div>`\n for (const a of articles) {\n const fullPath = `${agent.agentId}/${a.path}`\n const isActive = fullPath === activePath\n html += `<a class=\"nav-item${isActive ? ' active' : ''}\" href=\"${agentPrefix}/article/${encodeURIComponent(a.path)}\">${escapeHtml(a.title)}</a>`\n }\n }\n html += '</div>'\n html += '</div>'\n }\n\n return html\n}\n\nfunction hubHome(hub: WikiHub, allAgents?: AgentWikiSummary[], remoteAgents?: Array<AgentWikiSummary & { nodeId: string }>): string {\n const agents = allAgents || hub.summary()\n const shared = hub.getSharedStore()\n const totalEntries = shared.listEntries().length\n const totalArticles = agents.reduce((s, a) => s + a.totalArticles, 0)\n const totalUnabsorbed = agents.reduce((s, a) => s + a.unabsorbed, 0)\n const remoteIds = new Set((remoteAgents || []).map(r => r.agentId))\n\n let content = `<div class=\"page-head\">\n <div>\n <h1>AgentX Wiki</h1>\n <p class=\"page-sub\">Agent-owned institutional knowledge, typed and cross-referenced. Everything here compiles from raw conversation entries through the nightly absorb pass.</p>\n </div>\n </div>`\n\n // Stat strip\n content += '<div class=\"stat-strip\">'\n content += `<div class=\"stat-cell\"><div class=\"label\">Agents</div><div class=\"number\">${agents.length}</div></div>`\n content += `<div class=\"stat-cell\"><div class=\"label\">Articles</div><div class=\"number\">${totalArticles}</div></div>`\n content += `<div class=\"stat-cell\"><div class=\"label\">Raw entries</div><div class=\"number\">${totalEntries}</div></div>`\n if (totalUnabsorbed > 0) {\n content += `<div class=\"stat-cell\"><div class=\"label\">Unabsorbed</div><div class=\"number\">${totalUnabsorbed}</div></div>`\n }\n if (remoteAgents && remoteAgents.length > 0) {\n const nodes = new Set(remoteAgents.map(r => r.nodeId))\n content += `<div class=\"stat-cell\"><div class=\"label\">Mesh nodes</div><div class=\"number\">${nodes.size}</div></div>`\n }\n content += '</div>'\n\n // Agent cards grid\n content += '<div class=\"section-head\"><h2>Agent wikis</h2></div>'\n content += '<div class=\"agent-grid\">'\n for (const agent of agents) {\n const statusChip = agent.unabsorbed > 0\n ? `<span class=\"tag\" style=\"color:var(--warn);background:color-mix(in oklch, var(--warn) 12%, transparent)\">${agent.unabsorbed} unabsorbed</span>`\n : `<span class=\"tag\" style=\"color:var(--ok);background:color-mix(in oklch, var(--ok) 12%, transparent)\">up to date</span>`\n\n const isRemote = remoteIds.has(agent.agentId) && !hub.listAgents().includes(agent.agentId)\n const remoteBadge = isRemote\n ? ` <span class=\"tag\" style=\"color:var(--cool);background:var(--cool-soft)\">remote</span>`\n : ''\n\n content += `<div class=\"agent-card\">`\n content += `<h3><a href=\"/agent/${encodeURIComponent(agent.agentId)}\">${escapeHtml(agent.agentId)}</a>${remoteBadge}</h3>`\n content += `<div class=\"agent-stats\">\n <span><strong>${agent.totalArticles}</strong> articles</span>\n <span><strong>${agent.totalEntries}</strong> entries</span>\n ${statusChip}\n </div>`\n\n if (agent.articles.length > 0) {\n content += '<div class=\"article-list\">'\n for (const a of agent.articles.slice(0, 6)) {\n const tagPart = tagLinks((a.tags || []).slice(0, 2), agent.agentId)\n content += `<div class=\"article-row\">${tagPart} <a class=\"article-title\" href=\"/agent/${encodeURIComponent(agent.agentId)}/article/${encodeURIComponent(a.path)}\">${escapeHtml(a.title)}</a></div>`\n }\n if (agent.articles.length > 6) {\n content += `<div class=\"article-row\"><a class=\"article-title\" style=\"color:var(--muted)\" href=\"/agent/${encodeURIComponent(agent.agentId)}\">+ ${agent.articles.length - 6} more →</a></div>`\n }\n content += '</div>'\n }\n content += '</div>'\n }\n content += '</div>'\n\n return pageLayout(\"Hub\", hubSidebar(agents), content)\n}\n\nfunction hubAgentOverview(hub: WikiHub, agentId: string, allAgents?: AgentWikiSummary[]): string {\n const agents = allAgents || hub.summary()\n const agentSummary = agents.find(a => a.agentId === agentId)\n if (!agentSummary) return pageLayout(\"Not Found\", hubSidebar(agents), \"<h1>Agent not found</h1>\")\n\n // Use summary articles (may come from remote peer if it has more)\n const isRemote = (agentSummary as any).peerUrl !== undefined\n const entries = hub.getAgentEntries(agentId)\n const articles = agentSummary.articles || []\n\n let content = `<nav class=\"breadcrumb\"><a href=\"/\">Hub</a> <span class=\"sep\">/</span> <span class=\"current\">${escapeHtml(agentId)}</span></nav>`\n content += `<div class=\"page-head\"><div><h1>${escapeHtml(agentId)}</h1>`\n if (isRemote) {\n content += `<p class=\"page-sub\"><span class=\"tag\" style=\"color:var(--cool);background:var(--cool-soft)\">remote @ ${escapeHtml((agentSummary as any).nodeId)}</span></p>`\n }\n content += `</div></div>`\n\n // Stats\n content += '<div class=\"stat-strip\">'\n content += `<div class=\"stat-cell\"><div class=\"label\">Articles</div><div class=\"number\">${agentSummary.totalArticles}</div></div>`\n content += `<div class=\"stat-cell\"><div class=\"label\">Entries</div><div class=\"number\">${agentSummary.totalEntries}</div></div>`\n content += `<div class=\"stat-cell\"><div class=\"label\">Unabsorbed</div><div class=\"number\">${agentSummary.unabsorbed}</div></div>`\n content += '</div>'\n\n // Articles grouped by type\n if (articles.length > 0) {\n content += '<div class=\"section-head\"><h2>Articles</h2><span class=\"mono\" style=\"font-size:12px;color:var(--muted)\">' + articles.length + ' total</span></div>'\n const byType = groupByDir(articles as Array<{ title: string; path: string; tags?: string[] }>)\n for (const [type, list] of byType) {\n content += `<h3 class=\"display\" style=\"font-size:17px;color:var(--muted);font-weight:500;margin:20px 0 8px\">${dirLabel(type)}</h3>`\n content += '<div class=\"agent-card\" style=\"padding:4px 14px\"><div class=\"article-list\">'\n for (const a of list) {\n content += `<div class=\"article-row\">${tagLinks(((a.tags || []) as string[]).slice(0, 3), agentId)} <a class=\"article-title\" href=\"/agent/${encodeURIComponent(agentId)}/article/${encodeURIComponent(a.path)}\">${escapeHtml(a.title)}</a></div>`\n }\n content += '</div></div>'\n }\n }\n\n // Recent entries\n const recent = entries.slice(-5).reverse()\n if (recent.length > 0) {\n content += '<div class=\"section-head\"><h2>Recent entries</h2><a class=\"btn ghost\" href=\"/entries\">See all →</a></div>'\n for (const e of recent) {\n content += `<div class=\"entry-card\">\n <div class=\"meta-line\">${e.date} · via ${escapeHtml(e.source)} · <code>${escapeHtml(e.id)}</code></div>\n <p>${escapeHtml(e.content.slice(0, 300))}${e.content.length > 300 ? \"…\" : \"\"}</p>\n </div>`\n }\n }\n\n // Lint\n const agentStore = hub.getAgentWiki(agentId)\n const issues = agentStore.lint()\n if (issues.length > 0) {\n content += `<div class=\"section-head\"><h2>Health issues</h2><span class=\"mono\" style=\"font-size:12px;color:var(--warn)\">${issues.length}</span></div>`\n content += '<div class=\"health-list\">'\n for (const issue of issues) {\n content += `<div class=\"health-row\">\n <span class=\"kind\">${escapeHtml(issue.type)}</span>\n <span class=\"article\">${escapeHtml(issue.article)}</span>\n <span class=\"msg\">${escapeHtml(issue.message)}</span>\n </div>`\n }\n content += '</div>'\n }\n\n return pageLayout(agentId, hubSidebar(agents, agentId), content)\n}\n\nfunction hubArticlePage(hub: WikiHub, agentId: string, articlePath: string, allAgents?: AgentWikiSummary[]): string | null {\n const store = hub.getAgentWiki(agentId)\n const article = store.readArticle(articlePath)\n if (!article) return null\n\n const agents = allAgents || hub.summary()\n const index = store.rebuildIndex()\n const titleToPath = new Map<string, string>()\n for (const a of index.articles) titleToPath.set(a.title, a.path)\n\n const prefix = `/agent/${encodeURIComponent(agentId)}`\n const backlinks = store.buildBacklinks()\n const inbound = backlinks[article.meta.title] || []\n\n let content = `<nav class=\"breadcrumb\"><a href=\"/\">Hub</a> <span class=\"sep\">/</span> <a href=\"${prefix}\">${escapeHtml(agentId)}</a> <span class=\"sep\">/</span> <span class=\"current\">${escapeHtml(article.meta.title)}</span></nav>`\n content += `<article class=\"prose\"><h1>${escapeHtml(article.meta.title)}</h1>`\n\n // Meta box\n content += '<dl class=\"meta\">'\n if (article.meta.type) {\n content += `<dt>Type</dt><dd>${tagLink(article.meta.type, agentId)}</dd>`\n }\n if (article.meta.tags?.length) {\n content += `<dt>Tags</dt><dd>${tagLinks(article.meta.tags, agentId)}</dd>`\n }\n if (article.meta.owner) content += `<dt>Owner</dt><dd>${escapeHtml(article.meta.owner)}</dd>`\n content += `<dt>Created</dt><dd>${escapeHtml(article.meta.created || '—')}</dd>`\n content += `<dt>Updated</dt><dd>${escapeHtml(article.meta.lastUpdated || '—')}</dd>`\n if (article.meta.sources?.length) {\n content += `<dt>Sources</dt><dd>${article.meta.sources.length} entries</dd>`\n }\n content += '</dl>'\n\n content += md(article.content, titleToPath, prefix)\n\n if (inbound.length > 0) {\n content += '<div class=\"backlinks\"><h4>Pages that link here</h4><ul>'\n for (const link of inbound) {\n const linked = store.readArticle(link)\n content += `<li><a href=\"${prefix}/article/${encodeURIComponent(link)}\">${escapeHtml(linked?.meta.title || link)}</a></li>`\n }\n content += '</ul></div>'\n }\n content += `</article>`\n\n return pageLayout(article.meta.title, hubSidebar(agents, `${agentId}/${articlePath}`), content)\n}\n\nfunction hubEntries(hub: WikiHub, allAgents?: AgentWikiSummary[]): string {\n const agents = allAgents || hub.summary()\n const shared = hub.getSharedStore()\n const entries = shared.listEntries()\n\n let content = '<nav class=\"breadcrumb\"><a href=\"/\">Hub</a> <span class=\"sep\">/</span> <span class=\"current\">Entries</span></nav>'\n content += `<div class=\"page-head\"><div><h1>All raw entries</h1><p class=\"page-sub\">Every conversation line that the ingest pipeline captured. Absorb compiles these into typed articles nightly.</p></div></div>`\n\n const byDate = new Map<string, typeof entries>()\n for (const e of entries) {\n const list = byDate.get(e.date) || []\n list.push(e)\n byDate.set(e.date, list)\n }\n\n for (const [date, dateEntries] of [...byDate].reverse()) {\n content += `<div class=\"section-head\"><h2>${date}</h2><span class=\"mono\" style=\"font-size:12px;color:var(--muted)\">${dateEntries.length} entries</span></div>`\n for (const e of dateEntries) {\n content += `<div class=\"entry-card\">\n <div class=\"meta-line\">\n <a href=\"/agent/${encodeURIComponent(e.agentId)}\">${escapeHtml(e.agentId)}</a>\n · via ${escapeHtml(e.source)} · <code>${escapeHtml(e.id)}</code>\n </div>\n <p>${escapeHtml(e.content.slice(0, 400))}${e.content.length > 400 ? \"…\" : \"\"}</p>\n </div>`\n }\n }\n\n return pageLayout(\"All Entries\", hubSidebar(agents), content)\n}\n\nfunction hubSearch(hub: WikiHub, query: string, allAgents?: AgentWikiSummary[]): string {\n const agents = allAgents || hub.summary()\n let content = `<nav class=\"breadcrumb\"><a href=\"/\">Hub</a> <span class=\"sep\">/</span> <span class=\"current\">Search</span></nav>`\n content += `<div class=\"page-head\"><div><h1>“${escapeHtml(query)}”</h1><p class=\"page-sub\">Full-text search across every agent's wiki.</p></div></div>`\n\n let totalResults = 0\n const blocks: string[] = []\n\n for (const agentSummary of agents) {\n const store = hub.getAgentWiki(agentSummary.agentId)\n const results = store.findRelevant(query, undefined, 10)\n\n if (results.length > 0) {\n blocks.push(`<div class=\"section-head\"><h2>${escapeHtml(agentSummary.agentId)}</h2><span class=\"mono\" style=\"font-size:12px;color:var(--muted)\">${results.length} match${results.length === 1 ? '' : 'es'}</span></div>`)\n for (const r of results) {\n const prefix = `/agent/${encodeURIComponent(agentSummary.agentId)}`\n blocks.push(`<div class=\"entry-card\">\n <div class=\"meta-line\">${tagLinks((r.meta.tags || []).slice(0, 3), agentSummary.agentId)}</div>\n <h3><a href=\"${prefix}/article/${encodeURIComponent(r.path)}\">${escapeHtml(r.meta.title)}</a></h3>\n <p>${escapeHtml(r.content.slice(0, 240))}…</p>\n </div>`)\n }\n totalResults += results.length\n }\n }\n content += blocks.join('')\n\n if (totalResults === 0) content += '<p style=\"color:var(--muted);margin-top:24px\">No matching articles found.</p>'\n\n return pageLayout(`Search: ${query}`, hubSidebar(agents), content, query)\n}\n\n/**\n * Hub-wide tag filter. Shows every article tagged with `tag` across all\n * local agents, grouped by agent. Exact-tag match (not BM25).\n */\nfunction hubTagPage(hub: WikiHub, tag: string, allAgents?: AgentWikiSummary[]): string {\n const agents = allAgents || hub.summary()\n let content = `<nav class=\"breadcrumb\"><a href=\"/\">Hub</a> <span class=\"sep\">/</span> <span class=\"current\">Tag</span></nav>`\n content += `<div class=\"page-head\"><div><h1>${escapeHtml(tag)}</h1><p class=\"page-sub\">All articles tagged <span class=\"tag\">${escapeHtml(tag)}</span>, across every agent.</p></div></div>`\n let total = 0\n for (const agentSummary of agents) {\n let store\n try { store = hub.getAgentWiki(agentSummary.agentId) } catch { continue }\n const matches = store.findByTags([tag], undefined, 100)\n if (matches.length === 0) continue\n content += `<div class=\"section-head\"><h2>${escapeHtml(agentSummary.agentId)}</h2><span class=\"mono\" style=\"font-size:12px;color:var(--muted)\">${matches.length}</span></div>`\n for (const a of matches) {\n const prefix = `/agent/${encodeURIComponent(agentSummary.agentId)}`\n content += `<div class=\"entry-card\">\n <div class=\"meta-line\">${tagLinks((a.meta.tags || []).slice(0, 6), agentSummary.agentId)}</div>\n <h3><a href=\"${prefix}/article/${encodeURIComponent(a.path)}\">${escapeHtml(a.meta.title)}</a></h3>\n </div>`\n }\n total += matches.length\n }\n if (total === 0) content += `<p style=\"color:var(--muted);margin-top:24px\">No articles tagged <code>${escapeHtml(tag)}</code>.</p>`\n return pageLayout(`Tag: ${tag}`, hubSidebar(agents), content)\n}\n\n/** Per-agent tag filter — narrows to one agent's corpus. */\nfunction hubAgentTagPage(hub: WikiHub, agentId: string, tag: string, allAgents?: AgentWikiSummary[]): string {\n const agents = allAgents || hub.summary()\n let store\n try { store = hub.getAgentWiki(agentId) } catch {\n return pageLayout(`Tag: ${tag}`, hubSidebar(agents), `<h1>Agent not found: ${escapeHtml(agentId)}</h1>`)\n }\n const matches = store.findByTags([tag], undefined, 100)\n const prefix = `/agent/${encodeURIComponent(agentId)}`\n let content = `<nav class=\"breadcrumb\"><a href=\"/\">Hub</a> <span class=\"sep\">/</span> <a href=\"${prefix}\">${escapeHtml(agentId)}</a> <span class=\"sep\">/</span> <span class=\"current\">${escapeHtml(tag)}</span></nav>`\n content += `<div class=\"page-head\"><div><h1>${escapeHtml(tag)}</h1><p class=\"page-sub\">${matches.length} article${matches.length === 1 ? \"\" : \"s\"} tagged <code>${escapeHtml(tag)}</code> in <b>${escapeHtml(agentId)}</b>.</p></div></div>`\n for (const a of matches) {\n content += `<div class=\"entry-card\">\n <div class=\"meta-line\">${tagLinks((a.meta.tags || []).slice(0, 6), agentId)}</div>\n <h3><a href=\"${prefix}/article/${encodeURIComponent(a.path)}\">${escapeHtml(a.meta.title)}</a></h3>\n </div>`\n }\n return pageLayout(`${agentId} · ${tag}`, hubSidebar(agents), content)\n}\n\n// --- Single Agent Mode ---\n\nfunction agentSidebar(store: WikiStore, agentId: string, activePath?: string): string {\n const index = store.rebuildIndex()\n const byType = groupByDir(index.articles.map(a => ({ title: a.title, path: a.path, tags: a.tags })))\n\n let html = '<div class=\"nav-section\">'\n html += `<div class=\"nav-section-title\">${escapeHtml(agentId)}</div>`\n html += `<a class=\"nav-item${!activePath ? ' active' : ''}\" href=\"/\">Home</a>`\n html += `<a class=\"nav-item\" href=\"/lint\">Health check</a>`\n html += '</div>'\n\n for (const [type, articles] of byType) {\n html += '<div class=\"nav-section\">'\n html += `<div class=\"nav-section-title\">${dirLabel(type)}</div>`\n for (const a of articles) {\n const isActive = a.path === activePath\n html += `<a class=\"nav-item${isActive ? ' active' : ''}\" href=\"/article/${encodeURIComponent(a.path)}\">${escapeHtml(a.title)}</a>`\n }\n html += '</div>'\n }\n\n return html\n}\n\n// --- Server ---\n\nexport interface WikiHandlerOpts {\n wikiDir: string\n agentFilter?: string\n peerUrls?: string[]\n mode?: \"flat\" | \"graph\" | \"unified\"\n /** Mount this handler under a URL prefix (e.g. \"/admin/wiki\" when\n * embedded in the dashboard). Routes are matched against the path\n * with the prefix stripped, and hrefs in the rendered HTML get the\n * prefix re-prepended so links round-trip back to the same mount. */\n pathPrefix?: string\n}\n\n/** Build the wiki HTTP request handler. Same logic as `startWikiServer`,\n * but factored out so the dashboard can mount it at `/admin/wiki/*` —\n * one HTTP listener instead of two. */\nexport function createWikiHandler(opts: WikiHandlerOpts): (req: IncomingMessage, res: ServerResponse) => Promise<void> {\n const { wikiDir, agentFilter, peerUrls = [], mode = \"graph\", pathPrefix = \"\" } = opts\n const prefix = pathPrefix.replace(/\\/+$/, \"\")\n const hub = new WikiHub(wikiDir, undefined, mode)\n const mesh = peerUrls.length > 0 ? new MeshWikiClient(peerUrls) : null\n\n return async (req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url || \"/\", `http://localhost`)\n const rawPath = decodeURIComponent(url.pathname)\n const path = prefix && rawPath.startsWith(prefix)\n ? (rawPath.slice(prefix.length) || \"/\")\n : rawPath\n\n let html: string | null = null\n let status = 200\n\n try {\n // Handle remote article fetch: /remote/:peerIdx/:agentId/article/:path\n if (path.startsWith(\"/remote/\") && mesh) {\n const parts = path.slice(\"/remote/\".length).split(\"/article/\")\n if (parts.length === 2) {\n const [peerAgent, articlePath] = parts\n const slashIdx = peerAgent.indexOf(\"/\")\n const peerUrl = decodeURIComponent(peerAgent.slice(0, slashIdx))\n const agentId = peerAgent.slice(slashIdx + 1)\n\n const article = await mesh.getRemoteArticle(peerUrl, agentId, articlePath)\n if (article) {\n const remoteAgents = await mesh.getRemoteAgents()\n const allAgents = [...hub.summary(), ...remoteAgents.map(r => ({ ...r, agentId: `${r.agentId} @${r.nodeId}` }))]\n\n let content = `<div class=\"breadcrumb\"><a href=\"/\">Hub</a> / <span class=\"tag\">remote</span> ${escapeHtml(agentId)} @ ${escapeHtml(peerUrl)}</div>`\n content += `<h1>${escapeHtml(article.meta.title)}</h1>`\n content += '<dl class=\"meta\">'\n content += `<dt>Tags</dt><dd>${(article.meta.tags||[]).map(t => `<span class=\"tag\">${t}</span>`).join(\" \")}</dd>`\n content += `<dt>Owner</dt><dd>${escapeHtml(article.meta.owner)}</dd>`\n content += `<dt>Source</dt><dd><span class=\"tag\">remote</span> ${escapeHtml(peerUrl)}</dd>`\n content += `<dt>Updated</dt><dd>${article.meta.lastUpdated}</dd>`\n content += '</dl>'\n\n const titleToPath = new Map<string, string>()\n content += md(article.content, titleToPath)\n\n html = pageLayout(article.meta.title, hubSidebar(allAgents), content)\n }\n }\n }\n\n if (!html && agentFilter) {\n // Single agent mode\n const store = hub.getAgentWiki(agentFilter)\n\n if (path === \"/\" || path === \"\") {\n const index = store.rebuildIndex()\n const entries = hub.getAgentEntries(agentFilter)\n let content = `<h1>${escapeHtml(agentFilter)}</h1>`\n content += '<div class=\"stats\">'\n content += `<div class=\"stat-card\"><div class=\"number\">${index.articles.length}</div><div class=\"label\">Articles</div></div>`\n content += `<div class=\"stat-card\"><div class=\"number\">${entries.length}</div><div class=\"label\">Entries</div></div>`\n content += '</div>'\n if (index.articles.length > 0) {\n content += '<h2>Articles</h2><table><tr><th>Title</th><th>Type</th><th>Updated</th></tr>'\n for (const a of index.articles) {\n content += `<tr><td><a href=\"/article/${encodeURIComponent(a.path)}\">${escapeHtml(a.title)}</a></td><td><span class=\"tag\">${(a.tags||[]).slice(0,3).join(\", \")}</span></td><td>${a.lastUpdated || \"\"}</td></tr>`\n }\n content += '</table>'\n }\n html = pageLayout(agentFilter, agentSidebar(store, agentFilter), content)\n } else if (path.startsWith(\"/article/\")) {\n const articlePath = path.slice(\"/article/\".length)\n const article = store.readArticle(articlePath)\n if (article) {\n const index = store.rebuildIndex()\n const titleToPath = new Map<string, string>()\n for (const a of index.articles) titleToPath.set(a.title, a.path)\n let content = `<h1>${escapeHtml(article.meta.title)}</h1>`\n content += '<dl class=\"meta\">'\n content += `<dt>Tags</dt><dd>${(article.meta.tags||[]).map(t => `<span class=\"tag\">${t}</span>`).join(\" \")}</dd>`\n content += `<dt>Updated</dt><dd>${article.meta.lastUpdated}</dd>`\n if (article.meta.sources?.length) content += `<dt>Sources</dt><dd>${article.meta.sources.length} entries</dd>`\n content += '</dl>'\n content += md(article.content, titleToPath)\n html = pageLayout(article.meta.title, agentSidebar(store, agentFilter, articlePath), content)\n }\n } else if (path === \"/lint\") {\n const issues = store.lint()\n let content = '<h1>Health Check</h1>'\n if (issues.length === 0) { content += '<p style=\"color:green;\">No issues.</p>' }\n else {\n content += `<table><tr><th>Type</th><th>Article</th><th>Issue</th></tr>`\n for (const i of issues) content += `<tr><td><span class=\"tag\">${i.type}</span></td><td>${escapeHtml(i.article)}</td><td>${escapeHtml(i.message)}</td></tr>`\n content += '</table>'\n }\n html = pageLayout(\"Health Check\", agentSidebar(store, agentFilter), content)\n } else if (path === \"/search\") {\n const q = url.searchParams.get(\"q\") || \"\"\n const results = store.findRelevant(q, undefined, 20)\n let content = `<h1>Search: \"${escapeHtml(q)}\"</h1>`\n for (const r of results) {\n content += `<div class=\"entry-card\"><h3><a href=\"/article/${encodeURIComponent(r.path)}\">${escapeHtml(r.meta.title)}</a></h3><p>${escapeHtml(r.content.slice(0, 200))}...</p></div>`\n }\n if (results.length === 0) content += '<p>No results.</p>'\n html = pageLayout(`Search: ${q}`, agentSidebar(store, agentFilter), content)\n } else if (path.startsWith(\"/tag/\")) {\n const tag = decodeURIComponent(path.slice(\"/tag/\".length))\n const matches = store.findByTags([tag], undefined, 100)\n let content = `<h1>Tag: <a class=\"tag\" href=\"#\">${escapeHtml(tag)}</a></h1>`\n content += `<p>${matches.length} article${matches.length === 1 ? \"\" : \"s\"} tagged <code>${escapeHtml(tag)}</code></p>`\n for (const a of matches) {\n content += `<div class=\"entry-card\"><h3><a href=\"/article/${encodeURIComponent(a.path)}\">${escapeHtml(a.meta.title)}</a></h3><div class=\"meta-line\">${tagLinks((a.meta.tags || []).slice(0, 6))}</div></div>`\n }\n if (matches.length === 0) content += '<p>No articles with this tag.</p>'\n html = pageLayout(`Tag: ${tag}`, agentSidebar(store, agentFilter), content)\n }\n } else {\n // Hub mode — merge local + remote agents\n const remoteAgents = mesh ? await mesh.getRemoteAgents() : []\n const localAgents = hub.summary()\n // Merge: prefer whichever has more articles (local or remote)\n const localMap = new Map(localAgents.map(a => [a.agentId, a]))\n const allAgents: AgentWikiSummary[] = []\n const seen = new Set<string>()\n\n for (const local of localAgents) {\n const remote = remoteAgents.find(r => r.agentId === local.agentId)\n if (remote && remote.totalArticles > local.totalArticles) {\n // Remote has more articles — merge: keep remote articles, combine entry counts\n allAgents.push({\n ...remote,\n totalEntries: Math.max(local.totalEntries, remote.totalEntries),\n unabsorbed: Math.max(local.unabsorbed, remote.unabsorbed),\n })\n } else {\n allAgents.push(local)\n }\n seen.add(local.agentId)\n }\n // Add remote-only agents\n for (const remote of remoteAgents) {\n if (!seen.has(remote.agentId)) {\n allAgents.push(remote)\n seen.add(remote.agentId)\n }\n }\n\n if (path === \"/\" || path === \"\") {\n html = hubHome(hub, allAgents, remoteAgents)\n } else if (path === \"/entries\") {\n html = hubEntries(hub, allAgents)\n } else if (path === \"/search\") {\n html = hubSearch(hub, url.searchParams.get(\"q\") || \"\", allAgents)\n } else if (path.startsWith(\"/tag/\")) {\n html = hubTagPage(hub, decodeURIComponent(path.slice(\"/tag/\".length)), allAgents)\n } else if (path.match(/^\\/agent\\/[^/]+\\/tag\\/.+/)) {\n const m = path.match(/^\\/agent\\/([^/]+)\\/tag\\/(.+)$/)!\n html = hubAgentTagPage(hub, decodeURIComponent(m[1]), decodeURIComponent(m[2]), allAgents)\n } else if (path.startsWith(\"/agent/\")) {\n const rest = path.slice(\"/agent/\".length)\n const slashIdx = rest.indexOf(\"/article/\")\n if (slashIdx === -1) {\n // Agent overview\n html = hubAgentOverview(hub, rest, allAgents)\n } else {\n // Agent article\n const agentId = rest.slice(0, slashIdx)\n const articlePath = rest.slice(slashIdx + \"/article/\".length)\n html = hubArticlePage(hub, agentId, articlePath, allAgents)\n\n // If not found locally, try remote peers\n if (!html && mesh) {\n const remote = remoteAgents.find(r => r.agentId === agentId)\n if (remote) {\n const article = await mesh.getRemoteArticle(remote.peerUrl, agentId, articlePath)\n if (article) {\n let content = `<div class=\"breadcrumb\"><a href=\"/\">Hub</a> / <span class=\"tag\">remote @ ${escapeHtml(remote.nodeId)}</span> / ${escapeHtml(agentId)}</div>`\n content += `<h1>${escapeHtml(article.meta.title)}</h1>`\n content += '<dl class=\"meta\">'\n content += `<dt>Tags</dt><dd>${(article.meta.tags||[]).map(t => `<span class=\"tag\">${t}</span>`).join(\" \")}</dd>`\n content += `<dt>Source</dt><dd><span class=\"tag\">remote</span> ${escapeHtml(remote.nodeId)}</dd>`\n content += `<dt>Updated</dt><dd>${article.meta.lastUpdated}</dd>`\n content += '</dl>'\n content += md(article.content, new Map())\n html = pageLayout(article.meta.title, hubSidebar(allAgents), content)\n }\n }\n }\n }\n }\n }\n\n if (!html) {\n status = 404\n const sidebar = agentFilter\n ? agentSidebar(hub.getAgentWiki(agentFilter), agentFilter)\n : hubSidebar(hub.summary())\n html = pageLayout(\"Not Found\", sidebar, \"<h1>Page not found</h1>\")\n }\n } catch (err: any) {\n status = 500\n html = pageLayout(\"Error\", \"\", `<h1>Error</h1><pre>${escapeHtml(err.message)}</pre>`)\n }\n\n res.writeHead(status, { \"Content-Type\": \"text/html; charset=utf-8\" })\n res.end(prefix ? prefixHrefs(html, prefix) : html)\n }\n}\n\n/** Re-prepend the mount prefix to every absolute internal href the wiki\n * generates (`/article/...`, `/agent/...`, `/tag/...`, `/`, `/search`,\n * `/lint`, `/entries`, `/remote/...`). Skips `//` (protocol-relative)\n * and external links. Cheap: a few regexes on the rendered HTML so we\n * don't need to thread `pathPrefix` through every layout helper.\n *\n * Also injects a small \"← Dashboard\" pill so embedded users have a way\n * back to the AgentX dashboard tabs (the wiki page renders its own\n * full-screen UI and replaces the dashboard chrome). */\nfunction prefixHrefs(html: string, prefix: string): string {\n // href and form action attributes\n html = html.replace(/(\\s(?:href|action)=\")\\/(?!\\/)/g, `$1${prefix}/`)\n // de-dupe if already prefixed\n html = html.replace(new RegExp(`(${prefix})${prefix}/`, \"g\"), `${prefix}/`)\n // back-pill — fixed top-right, neutral styling so it works in any wiki theme\n const pill = `<a href=\"/admin\" style=\"position:fixed;top:10px;right:14px;z-index:9999;padding:5px 11px;background:rgba(0,0,0,0.55);color:#fff;font:12px/1.2 'IBM Plex Sans',system-ui,sans-serif;text-decoration:none;border-radius:6px;border:1px solid rgba(255,255,255,0.18);backdrop-filter:blur(6px)\">← Dashboard</a>`\n html = html.replace(/<body([^>]*)>/, `<body$1>${pill}`)\n return html\n}\n\n/** Thin wrapper that runs the handler on its own port. Kept for the\n * `agentx wiki serve` CLI; the dashboard uses `createWikiHandler`\n * directly. */\nexport function startWikiServer(wikiDir: string, port: number = 4200, agentFilter?: string, peerUrls: string[] = [], mode: \"flat\" | \"graph\" | \"unified\" = \"graph\"): void {\n const handler = createWikiHandler({ wikiDir, agentFilter, peerUrls, mode })\n const server = createServer((req, res) => { void handler(req, res) })\n server.listen(port)\n}\n","import type { AgentWikiSummary } from \"./hub\"\nimport type { WikiArticle } from \"./types\"\n\n/**\n * Fetches wiki data from mesh peers over HTTP.\n * Each peer exposes /wiki/agents, /wiki/entries, /wiki/articles.\n */\nexport class MeshWikiClient {\n private peers: Array<{ url: string; nodeId?: string }>\n private cache: Map<string, { data: any; ts: number }> = new Map()\n private ttl = 30_000 // 30s cache\n\n constructor(peerUrls: string[]) {\n this.peers = peerUrls.map(url => ({ url: url.replace(/\\/$/, \"\") }))\n }\n\n /**\n * Get agent summaries from all peers.\n * Returns remote summaries with nodeId attached.\n */\n async getRemoteAgents(): Promise<Array<AgentWikiSummary & { nodeId: string; peerUrl: string }>> {\n const results: Array<AgentWikiSummary & { nodeId: string; peerUrl: string }> = []\n\n await Promise.all(this.peers.map(async (peer) => {\n try {\n const data = await this.fetchJson(`${peer.url}/wiki/agents`)\n peer.nodeId = data.nodeId\n for (const agent of (data.agents || [])) {\n if (agent.totalEntries > 0 || agent.totalArticles > 0) {\n results.push({ ...agent, nodeId: data.nodeId, peerUrl: peer.url })\n }\n }\n } catch { /* peer unreachable */ }\n }))\n\n return results\n }\n\n /**\n * Get articles for a specific agent from a specific peer.\n */\n async getRemoteArticles(peerUrl: string, agentId: string): Promise<Array<{\n path: string; title: string; tags: string[]; lastUpdated?: string; sources?: string[]\n }>> {\n try {\n const data = await this.fetchJson(`${peerUrl}/wiki/articles?agent=${encodeURIComponent(agentId)}`)\n return data.articles || []\n } catch {\n return []\n }\n }\n\n /**\n * Fetch a specific article's content from a peer.\n */\n async getRemoteArticle(peerUrl: string, agentId: string, articlePath: string): Promise<WikiArticle | null> {\n try {\n const data = await this.fetchJson(\n `${peerUrl}/wiki/article?agent=${encodeURIComponent(agentId)}&path=${encodeURIComponent(articlePath)}`\n )\n return {\n meta: {\n title: data.title,\n tags: data.tags || [],\n owner: data.owner,\n access: \"public\",\n created: data.created,\n lastUpdated: data.lastUpdated,\n sources: data.sources || [],\n },\n content: data.content,\n path: data.path,\n }\n } catch {\n return null\n }\n }\n\n private async fetchJson(url: string): Promise<any> {\n const cached = this.cache.get(url)\n if (cached && Date.now() - cached.ts < this.ttl) return cached.data\n\n const res = await fetch(url, { signal: AbortSignal.timeout(5000) })\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n const data = await res.json()\n this.cache.set(url, { data, ts: Date.now() })\n return data\n }\n}\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAudZ,SAASC,EAAWxB,EAAeyB,EAAiBC,EAAiBC,EAAsB,GAAY,CACrG,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKE5C,EAAWiB,CAAK;AAAA,IACvBsB;AAAA,WACOC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qFAYqExC,EAAW4C,CAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAS3EF;AAAA,yBACFC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAwBzB,CAIA,SAASE,EAAWC,EAAiG,CACnH,IAAMC,EAAQ,IAAI,IAClB,QAAWC,KAAKF,EAAU,CACxB,IAAMG,EAAMD,EAAE,KAAK,SAAS,GAAG,EAAIA,EAAE,KAAK,MAAM,GAAG,EAAE,MAAM,EAAG,EAAE,EAAE,KAAK,GAAG,EAAI,IACxEE,EAAOH,EAAM,IAAIE,CAAG,GAAK,CAAC,EAChCC,EAAK,KAAKF,CAAC,EACXD,EAAM,IAAIE,EAAKC,CAAI,EAErB,OAAO,IAAI,IAAI,CAAC,GAAGH,CAAK,EAAE,KAAK,CAACC,EAAGG,IAAMH,EAAE,CAAC,EAAE,cAAcG,EAAE,CAAC,CAAC,CAAC,CAAC,CACpE,CAEA,SAASC,EAASH,EAAqB,CACrC,OAAIA,IAAQ,IAAY,OACjBA,EAAI,MAAM,GAAG,EAAE,IAAI,EAAG,QAAQ,KAAM,GAAG,CAChD,CAEA,SAASI,EAAWC,EAA4BC,EAA6B,CAC3E,IAAIlC,EAAO,4BACXA,GAAQ,qBAAsBkC,EAAyB,GAAZ,+BAC3ClC,GAAQ,sDACRA,GAAQ,SAER,QAAWmC,KAASF,EAAQ,CAC1B,IAAMlC,EAAc,UAAU,mBAAmBoC,EAAM,OAAO,IAC9DnC,GAAQ,4BACRA,GAAQ,qCAAqCD,MAC7CC,GAAQ,sBAAsBrB,EAAWwD,EAAM,OAAO,WACtDnC,GAAQ,uBAAuBmC,EAAM,uBACrCnC,GAAQ,OACRA,GAAQ,mCAER,IAAMoC,EAASZ,EAAWW,EAAM,QAAQ,EACxC,OAAW,CAACE,EAAMZ,CAAQ,IAAKW,EAAQ,CACrCpC,GAAQ,qCAAqC+B,EAASM,CAAI,UAC1D,QAAWV,KAAKF,EAAU,CAExB,IAAMa,EADW,GAAGH,EAAM,WAAWR,EAAE,SACTO,EAC9BlC,GAAQ,qBAAqBsC,EAAW,UAAY,aAAavC,aAAuB,mBAAmB4B,EAAE,IAAI,MAAMhD,EAAWgD,EAAE,KAAK,SAG7I3B,GAAQ,SACRA,GAAQ,SAGV,OAAOA,CACT,CAEA,SAASuC,GAAQC,EAAcC,EAAgCC,EAAqE,CAClI,IAAMT,EAASQ,GAAaD,EAAI,QAAQ,EAElCG,EADSH,EAAI,eAAe,EACN,YAAY,EAAE,OACpCI,EAAgBX,EAAO,OAAO,CAACrD,EAAG+C,IAAM/C,EAAI+C,EAAE,cAAe,CAAC,EAC9DkB,EAAkBZ,EAAO,OAAO,CAACrD,EAAG+C,IAAM/C,EAAI+C,EAAE,WAAY,CAAC,EAC7DmB,EAAY,IAAI,KAAKJ,GAAgB,CAAC,GAAG,IAAIK,GAAKA,EAAE,OAAO,CAAC,EAE9DzB,EAAU;AAAA;AAAA;AAAA;AAAA;AAAA,UAed,GAPAA,GAAW,2BACXA,GAAW,6EAA6EW,EAAO,qBAC/FX,GAAW,+EAA+EsB,gBAC1FtB,GAAW,kFAAkFqB,gBACzFE,EAAkB,IACpBvB,GAAW,iFAAiFuB,iBAE1FH,GAAgBA,EAAa,OAAS,EAAG,CAC3C,IAAMM,EAAQ,IAAI,IAAIN,EAAa,IAAIK,GAAKA,EAAE,MAAM,CAAC,EACrDzB,GAAW,iFAAiF0B,EAAM,mBAEpG1B,GAAW,SAGXA,GAAW,uDACXA,GAAW,2BACX,QAAWa,KAASF,EAAQ,CAC1B,IAAMgB,EAAad,EAAM,WAAa,EAClC,4GAA4GA,EAAM,+BAClH,yHAGEe,EADWJ,EAAU,IAAIX,EAAM,OAAO,GAAK,CAACK,EAAI,WAAW,EAAE,SAASL,EAAM,OAAO,EAErF,yFACA,GAUJ,GARAb,GAAW,2BACXA,GAAW,uBAAuB,mBAAmBa,EAAM,OAAO,MAAMxD,EAAWwD,EAAM,OAAO,QAAQe,SACxG5B,GAAW;AAAA,sBACOa,EAAM;AAAA,sBACNA,EAAM;AAAA,QACpBc;AAAA,YAGAd,EAAM,SAAS,OAAS,EAAG,CAC7Bb,GAAW,6BACX,QAAWK,KAAKQ,EAAM,SAAS,MAAM,EAAG,CAAC,EAAG,CAC1C,IAAMgB,EAAUjE,GAAUyC,EAAE,MAAQ,CAAC,GAAG,MAAM,EAAG,CAAC,EAAGQ,EAAM,OAAO,EAClEb,GAAW,4BAA4B6B,2CAAiD,mBAAmBhB,EAAM,OAAO,aAAa,mBAAmBR,EAAE,IAAI,MAAMhD,EAAWgD,EAAE,KAAK,cAEpLQ,EAAM,SAAS,OAAS,IAC1Bb,GAAW,6FAA6F,mBAAmBa,EAAM,OAAO,QAAQA,EAAM,SAAS,OAAS,2BAE1Kb,GAAW,SAEbA,GAAW,SAEb,OAAAA,GAAW,SAEJF,EAAW,MAAOY,EAAWC,CAAM,EAAGX,CAAO,CACtD,CAEA,SAAS8B,GAAiBZ,EAAcvD,EAAiBwD,EAAwC,CAC/F,IAAMR,EAASQ,GAAaD,EAAI,QAAQ,EAClCa,EAAepB,EAAO,KAAKN,GAAKA,EAAE,UAAY1C,CAAO,EAC3D,GAAI,CAACoE,EAAc,OAAOjC,EAAW,YAAaY,EAAWC,CAAM,EAAG,0BAA0B,EAGhG,IAAMqB,EAAYD,EAAqB,UAAY,OAC7CE,EAAUf,EAAI,gBAAgBvD,CAAO,EACrCwC,EAAW4B,EAAa,UAAY,CAAC,EAEvC/B,EAAU,gGAAgG3C,EAAWM,CAAO,iBAehI,GAdAqC,GAAW,mCAAmC3C,EAAWM,CAAO,SAC5DqE,IACFhC,GAAW,wGAAwG3C,EAAY0E,EAAqB,MAAM,gBAE5J/B,GAAW,eAGXA,GAAW,2BACXA,GAAW,+EAA+E+B,EAAa,4BACvG/B,GAAW,8EAA8E+B,EAAa,2BACtG/B,GAAW,iFAAiF+B,EAAa,yBACzG/B,GAAW,SAGPG,EAAS,OAAS,EAAG,CACvBH,GAAW,2GAA6GG,EAAS,OAAS,sBAC1I,IAAMW,EAASZ,EAAWC,CAAmE,EAC7F,OAAW,CAACY,EAAMR,CAAI,IAAKO,EAAQ,CACjCd,GAAW,mGAAmGS,EAASM,CAAI,SAC3Hf,GAAW,8EACX,QAAWK,KAAKE,EACdP,GAAW,4BAA4BpC,GAAWyC,EAAE,MAAQ,CAAC,GAAgB,MAAM,EAAG,CAAC,EAAG1C,CAAO,2CAA2C,mBAAmBA,CAAO,aAAa,mBAAmB0C,EAAE,IAAI,MAAMhD,EAAWgD,EAAE,KAAK,cAEtOL,GAAW,gBAKf,IAAMkC,EAASD,EAAQ,MAAM,EAAE,EAAE,QAAQ,EACzC,GAAIC,EAAO,OAAS,EAAG,CACrBlC,GAAW,iHACX,QAAWmC,KAAKD,EACdlC,GAAW;AAAA,iCACgBmC,EAAE,iBAAc9E,EAAW8E,EAAE,MAAM,gBAAa9E,EAAW8E,EAAE,EAAE;AAAA,aACnF9E,EAAW8E,EAAE,QAAQ,MAAM,EAAG,GAAG,CAAC,IAAIA,EAAE,QAAQ,OAAS,IAAM,SAAM;AAAA,cAOhF,IAAMC,EADalB,EAAI,aAAavD,CAAO,EACjB,KAAK,EAC/B,GAAIyE,EAAO,OAAS,EAAG,CACrBpC,GAAW,+GAA+GoC,EAAO,sBACjIpC,GAAW,4BACX,QAAWqC,KAASD,EAClBpC,GAAW;AAAA,6BACY3C,EAAWgF,EAAM,IAAI;AAAA,gCAClBhF,EAAWgF,EAAM,OAAO;AAAA,4BAC5BhF,EAAWgF,EAAM,OAAO;AAAA,cAGhDrC,GAAW,SAGb,OAAOF,EAAWnC,EAAS+C,EAAWC,EAAQhD,CAAO,EAAGqC,CAAO,CACjE,CAEA,SAASsC,GAAepB,EAAcvD,EAAiB4E,EAAqBpB,EAA+C,CACzH,IAAMqB,EAAQtB,EAAI,aAAavD,CAAO,EAChC8E,EAAUD,EAAM,YAAYD,CAAW,EAC7C,GAAI,CAACE,EAAS,OAAO,KAErB,IAAM9B,EAASQ,GAAaD,EAAI,QAAQ,EAClCwB,EAAQF,EAAM,aAAa,EAC3BG,EAAc,IAAI,IACxB,QAAWtC,KAAKqC,EAAM,SAAUC,EAAY,IAAItC,EAAE,MAAOA,EAAE,IAAI,EAE/D,IAAMuC,EAAS,UAAU,mBAAmBjF,CAAO,IAE7CkF,EADYL,EAAM,eAAe,EACbC,EAAQ,KAAK,KAAK,GAAK,CAAC,EAE9CzC,EAAU,mFAAmF4C,MAAWvF,EAAWM,CAAO,0DAA0DN,EAAWoF,EAAQ,KAAK,KAAK,iBAqBrN,GApBAzC,GAAW,8BAA8B3C,EAAWoF,EAAQ,KAAK,KAAK,SAGtEzC,GAAW,oBACPyC,EAAQ,KAAK,OACfzC,GAAW,oBAAoBvC,GAAQgF,EAAQ,KAAK,KAAM9E,CAAO,UAE/D8E,EAAQ,KAAK,MAAM,SACrBzC,GAAW,oBAAoBpC,EAAS6E,EAAQ,KAAK,KAAM9E,CAAO,UAEhE8E,EAAQ,KAAK,QAAOzC,GAAW,qBAAqB3C,EAAWoF,EAAQ,KAAK,KAAK,UACrFzC,GAAW,uBAAuB3C,EAAWoF,EAAQ,KAAK,SAAW,QAAG,SACxEzC,GAAW,uBAAuB3C,EAAWoF,EAAQ,KAAK,aAAe,QAAG,SACxEA,EAAQ,KAAK,SAAS,SACxBzC,GAAW,uBAAuByC,EAAQ,KAAK,QAAQ,uBAEzDzC,GAAW,QAEXA,GAAWxB,EAAGiE,EAAQ,QAASE,EAAaC,CAAM,EAE9CC,EAAQ,OAAS,EAAG,CACtB7C,GAAW,2DACX,QAAW8C,KAAQD,EAAS,CAC1B,IAAME,EAASP,EAAM,YAAYM,CAAI,EACrC9C,GAAW,gBAAgB4C,aAAkB,mBAAmBE,CAAI,MAAMzF,EAAW0F,GAAQ,KAAK,OAASD,CAAI,aAEjH9C,GAAW,cAEb,OAAAA,GAAW,aAEJF,EAAW2C,EAAQ,KAAK,MAAO/B,EAAWC,EAAQ,GAAGhD,KAAW4E,GAAa,EAAGvC,CAAO,CAChG,CAEA,SAASgD,GAAW9B,EAAcC,EAAwC,CACxE,IAAMR,EAASQ,GAAaD,EAAI,QAAQ,EAElCe,EADSf,EAAI,eAAe,EACX,YAAY,EAE/BlB,EAAU,oHACdA,GAAW,wMAEX,IAAMiD,EAAS,IAAI,IACnB,QAAWd,KAAKF,EAAS,CACvB,IAAM1B,EAAO0C,EAAO,IAAId,EAAE,IAAI,GAAK,CAAC,EACpC5B,EAAK,KAAK4B,CAAC,EACXc,EAAO,IAAId,EAAE,KAAM5B,CAAI,EAGzB,OAAW,CAAC2C,EAAMC,CAAW,GAAK,CAAC,GAAGF,CAAM,EAAE,QAAQ,EAAG,CACvDjD,GAAW,iCAAiCkD,sEAAyEC,EAAY,8BACjI,QAAWhB,KAAKgB,EACdnD,GAAW;AAAA;AAAA,4BAEW,mBAAmBmC,EAAE,OAAO,MAAM9E,EAAW8E,EAAE,OAAO;AAAA,qBAChE9E,EAAW8E,EAAE,MAAM,gBAAa9E,EAAW8E,EAAE,EAAE;AAAA;AAAA,aAEpD9E,EAAW8E,EAAE,QAAQ,MAAM,EAAG,GAAG,CAAC,IAAIA,EAAE,QAAQ,OAAS,IAAM,SAAM;AAAA,cAKhF,OAAOrC,EAAW,cAAeY,EAAWC,CAAM,EAAGX,CAAO,CAC9D,CAEA,SAASoD,GAAUlC,EAAcmC,EAAelC,EAAwC,CACtF,IAAMR,EAASQ,GAAaD,EAAI,QAAQ,EACpClB,EAAU,mHACdA,GAAW,yCAAoC3C,EAAWgG,CAAK,8FAE/D,IAAIC,EAAe,EACbC,EAAmB,CAAC,EAE1B,QAAWxB,KAAgBpB,EAAQ,CAEjC,IAAM6C,EADQtC,EAAI,aAAaa,EAAa,OAAO,EAC7B,aAAasB,EAAO,OAAW,EAAE,EAEvD,GAAIG,EAAQ,OAAS,EAAG,CACtBD,EAAO,KAAK,iCAAiClG,EAAW0E,EAAa,OAAO,sEAAsEyB,EAAQ,eAAeA,EAAQ,SAAW,EAAI,GAAK,mBAAmB,EACxN,QAAW/B,KAAK+B,EAAS,CACvB,IAAMZ,EAAS,UAAU,mBAAmBb,EAAa,OAAO,IAChEwB,EAAO,KAAK;AAAA,mCACe3F,GAAU6D,EAAE,KAAK,MAAQ,CAAC,GAAG,MAAM,EAAG,CAAC,EAAGM,EAAa,OAAO;AAAA,yBACxEa,aAAkB,mBAAmBnB,EAAE,IAAI,MAAMpE,EAAWoE,EAAE,KAAK,KAAK;AAAA,eAClFpE,EAAWoE,EAAE,QAAQ,MAAM,EAAG,GAAG,CAAC;AAAA,eAClC,EAET6B,GAAgBE,EAAQ,QAG5B,OAAAxD,GAAWuD,EAAO,KAAK,EAAE,EAErBD,IAAiB,IAAGtD,GAAW,iFAE5BF,EAAW,WAAWuD,IAAS3C,EAAWC,CAAM,EAAGX,EAASqD,CAAK,CAC1E,CAMA,SAASI,GAAWvC,EAAcxD,EAAayD,EAAwC,CACrF,IAAMR,EAASQ,GAAaD,EAAI,QAAQ,EACpClB,EAAU,gHACdA,GAAW,mCAAmC3C,EAAWK,CAAG,mEAAmEL,EAAWK,CAAG,gDAC7I,IAAIgG,EAAQ,EACZ,QAAW3B,KAAgBpB,EAAQ,CACjC,IAAI6B,EACJ,GAAI,CAAEA,EAAQtB,EAAI,aAAaa,EAAa,OAAO,CAAE,MAAE,CAAQ,QAAS,CACxE,IAAM4B,EAAUnB,EAAM,WAAW,CAAC9E,CAAG,EAAG,OAAW,GAAG,EACtD,GAAIiG,EAAQ,SAAW,EACvB,CAAA3D,GAAW,iCAAiC3C,EAAW0E,EAAa,OAAO,sEAAsE4B,EAAQ,sBACzJ,QAAWtD,KAAKsD,EAAS,CACvB,IAAMf,EAAS,UAAU,mBAAmBb,EAAa,OAAO,IAChE/B,GAAW;AAAA,iCACgBpC,GAAUyC,EAAE,KAAK,MAAQ,CAAC,GAAG,MAAM,EAAG,CAAC,EAAG0B,EAAa,OAAO;AAAA,uBACxEa,aAAkB,mBAAmBvC,EAAE,IAAI,MAAMhD,EAAWgD,EAAE,KAAK,KAAK;AAAA,cAG3FqD,GAASC,EAAQ,QAEnB,OAAID,IAAU,IAAG1D,GAAW,0EAA0E3C,EAAWK,CAAG,iBAC7GoC,EAAW,QAAQpC,IAAOgD,EAAWC,CAAM,EAAGX,CAAO,CAC9D,CAGA,SAAS4D,GAAgB1C,EAAcvD,EAAiBD,EAAayD,EAAwC,CAC3G,IAAMR,EAASQ,GAAaD,EAAI,QAAQ,EACpCsB,EACJ,GAAI,CAAEA,EAAQtB,EAAI,aAAavD,CAAO,CAAE,MAAE,CACxC,OAAOmC,EAAW,QAAQpC,IAAOgD,EAAWC,CAAM,EAAG,wBAAwBtD,EAAWM,CAAO,QAAQ,CACzG,CACA,IAAMgG,EAAUnB,EAAM,WAAW,CAAC9E,CAAG,EAAG,OAAW,GAAG,EAChDkF,EAAS,UAAU,mBAAmBjF,CAAO,IAC/CqC,EAAU,mFAAmF4C,MAAWvF,EAAWM,CAAO,0DAA0DN,EAAWK,CAAG,iBACtMsC,GAAW,mCAAmC3C,EAAWK,CAAG,6BAA6BiG,EAAQ,iBAAiBA,EAAQ,SAAW,EAAI,GAAK,oBAAoBtG,EAAWK,CAAG,kBAAkBL,EAAWM,CAAO,yBACpN,QAAW0C,KAAKsD,EACd3D,GAAW;AAAA,+BACgBpC,GAAUyC,EAAE,KAAK,MAAQ,CAAC,GAAG,MAAM,EAAG,CAAC,EAAG1C,CAAO;AAAA,qBAC3DiF,aAAkB,mBAAmBvC,EAAE,IAAI,MAAMhD,EAAWgD,EAAE,KAAK,KAAK;AAAA,YAG3F,OAAOP,EAAW,GAAGnC,UAAaD,IAAOgD,EAAWC,CAAM,EAAGX,CAAO,CACtE,CAIA,SAAS6D,EAAarB,EAAkB7E,EAAiBiD,EAA6B,CACpF,IAAM8B,EAAQF,EAAM,aAAa,EAC3B1B,EAASZ,EAAWwC,EAAM,SAAS,IAAIrC,IAAM,CAAE,MAAOA,EAAE,MAAO,KAAMA,EAAE,KAAM,KAAMA,EAAE,IAAK,EAAE,CAAC,EAE/F3B,EAAO,4BACXA,GAAQ,kCAAkCrB,EAAWM,CAAO,UAC5De,GAAQ,qBAAsBkC,EAAyB,GAAZ,+BAC3ClC,GAAQ,oDACRA,GAAQ,SAER,OAAW,CAACqC,EAAMZ,CAAQ,IAAKW,EAAQ,CACrCpC,GAAQ,4BACRA,GAAQ,kCAAkC+B,EAASM,CAAI,UACvD,QAAWV,KAAKF,EAAU,CACxB,IAAMa,EAAWX,EAAE,OAASO,EAC5BlC,GAAQ,qBAAqBsC,EAAW,UAAY,sBAAsB,mBAAmBX,EAAE,IAAI,MAAMhD,EAAWgD,EAAE,KAAK,QAE7H3B,GAAQ,SAGV,OAAOA,CACT,CAmBO,SAASoF,GAAkBC,EAAqF,CACrH,GAAM,CAAE,QAAAC,EAAS,YAAAC,EAAa,SAAAC,EAAW,CAAC,EAAG,KAAAC,EAAO,QAAS,WAAAC,EAAa,EAAG,EAAIL,EAC3EnB,EAASwB,EAAW,QAAQ,OAAQ,EAAE,EACtClD,EAAM,IAAImD,EAAQL,EAAS,OAAWG,CAAI,EAC1CG,EAAOJ,EAAS,OAAS,EAAI,IAAIK,EAAeL,CAAQ,EAAI,KAElE,MAAO,OAAOM,EAAsBC,IAAwB,CAC1D,IAAMC,EAAM,IAAI,IAAIF,EAAI,KAAO,IAAK,kBAAkB,EAChDG,EAAU,mBAAmBD,EAAI,QAAQ,EACzCnG,EAAOqE,GAAU+B,EAAQ,WAAW/B,CAAM,EAC3C+B,EAAQ,MAAM/B,EAAO,MAAM,GAAK,IACjC+B,EAEAjG,EAAsB,KACtBkG,EAAS,IAEb,GAAI,CAEF,GAAIrG,EAAK,WAAW,UAAU,GAAK+F,EAAM,CACvC,IAAMO,EAAQtG,EAAK,MAAM,CAAiB,EAAE,MAAM,WAAW,EAC7D,GAAIsG,EAAM,SAAW,EAAG,CACtB,GAAM,CAACC,EAAWvC,CAAW,EAAIsC,EAC3BE,EAAWD,EAAU,QAAQ,GAAG,EAChCE,EAAU,mBAAmBF,EAAU,MAAM,EAAGC,CAAQ,CAAC,EACzDpH,EAAUmH,EAAU,MAAMC,EAAW,CAAC,EAEtCtC,EAAU,MAAM6B,EAAK,iBAAiBU,EAASrH,EAAS4E,CAAW,EACzE,GAAIE,EAAS,CACX,IAAMrB,EAAe,MAAMkD,EAAK,gBAAgB,EAC1CnD,EAAY,CAAC,GAAGD,EAAI,QAAQ,EAAG,GAAGE,EAAa,IAAIK,IAAM,CAAE,GAAGA,EAAG,QAAS,GAAGA,EAAE,YAAYA,EAAE,QAAS,EAAE,CAAC,EAE3GzB,EAAU,iFAAiF3C,EAAWM,CAAO,OAAON,EAAW2H,CAAO,UAC1IhF,GAAW,OAAO3C,EAAWoF,EAAQ,KAAK,KAAK,SAC/CzC,GAAW,oBACXA,GAAW,qBAAqByC,EAAQ,KAAK,MAAM,CAAC,GAAG,IAAI3E,GAAK,qBAAqBA,UAAU,EAAE,KAAK,GAAG,SACzGkC,GAAW,qBAAqB3C,EAAWoF,EAAQ,KAAK,KAAK,SAC7DzC,GAAW,sDAAsD3C,EAAW2H,CAAO,SACnFhF,GAAW,uBAAuByC,EAAQ,KAAK,mBAC/CzC,GAAW,QAEX,IAAM2C,EAAc,IAAI,IACxB3C,GAAWxB,EAAGiE,EAAQ,QAASE,CAAW,EAE1CjE,EAAOoB,EAAW2C,EAAQ,KAAK,MAAO/B,EAAWS,CAAS,EAAGnB,CAAO,IAK1E,GAAI,CAACtB,GAAQuF,EAAa,CAExB,IAAMzB,EAAQtB,EAAI,aAAa+C,CAAW,EAE1C,GAAI1F,IAAS,KAAOA,IAAS,GAAI,CAC/B,IAAMmE,EAAQF,EAAM,aAAa,EAC3BP,EAAUf,EAAI,gBAAgB+C,CAAW,EAC3CjE,EAAU,OAAO3C,EAAW4G,CAAW,SAK3C,GAJAjE,GAAW,sBACXA,GAAW,8CAA8C0C,EAAM,SAAS,sDACxE1C,GAAW,8CAA8CiC,EAAQ,qDACjEjC,GAAW,SACP0C,EAAM,SAAS,OAAS,EAAG,CAC7B1C,GAAW,+EACX,QAAWK,KAAKqC,EAAM,SACpB1C,GAAW,6BAA6B,mBAAmBK,EAAE,IAAI,MAAMhD,EAAWgD,EAAE,KAAK,oCAAoCA,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,KAAK,IAAI,oBAAoBA,EAAE,aAAe,eAEpML,GAAW,WAEbtB,EAAOoB,EAAWmE,EAAaJ,EAAarB,EAAOyB,CAAW,EAAGjE,CAAO,UAC/DzB,EAAK,WAAW,WAAW,EAAG,CACvC,IAAMgE,EAAchE,EAAK,MAAM,CAAkB,EAC3CkE,EAAUD,EAAM,YAAYD,CAAW,EAC7C,GAAIE,EAAS,CACX,IAAMC,EAAQF,EAAM,aAAa,EAC3BG,EAAc,IAAI,IACxB,QAAWtC,KAAKqC,EAAM,SAAUC,EAAY,IAAItC,EAAE,MAAOA,EAAE,IAAI,EAC/D,IAAIL,EAAU,OAAO3C,EAAWoF,EAAQ,KAAK,KAAK,SAClDzC,GAAW,oBACXA,GAAW,qBAAqByC,EAAQ,KAAK,MAAM,CAAC,GAAG,IAAI3E,GAAK,qBAAqBA,UAAU,EAAE,KAAK,GAAG,SACzGkC,GAAW,uBAAuByC,EAAQ,KAAK,mBAC3CA,EAAQ,KAAK,SAAS,SAAQzC,GAAW,uBAAuByC,EAAQ,KAAK,QAAQ,uBACzFzC,GAAW,QACXA,GAAWxB,EAAGiE,EAAQ,QAASE,CAAW,EAC1CjE,EAAOoB,EAAW2C,EAAQ,KAAK,MAAOoB,EAAarB,EAAOyB,EAAa1B,CAAW,EAAGvC,CAAO,WAErFzB,IAAS,QAAS,CAC3B,IAAM6D,EAASI,EAAM,KAAK,EACtBxC,EAAU,wBACd,GAAIoC,EAAO,SAAW,EAAKpC,GAAW,6CACjC,CACHA,GAAW,8DACX,QAAWiF,KAAK7C,EAAQpC,GAAW,6BAA6BiF,EAAE,uBAAuB5H,EAAW4H,EAAE,OAAO,aAAa5H,EAAW4H,EAAE,OAAO,cAC9IjF,GAAW,WAEbtB,EAAOoB,EAAW,eAAgB+D,EAAarB,EAAOyB,CAAW,EAAGjE,CAAO,UAClEzB,IAAS,UAAW,CAC7B,IAAM2G,EAAIR,EAAI,aAAa,IAAI,GAAG,GAAK,GACjClB,EAAUhB,EAAM,aAAa0C,EAAG,OAAW,EAAE,EAC/ClF,EAAU,gBAAgB3C,EAAW6H,CAAC,UAC1C,QAAWzD,KAAK+B,EACdxD,GAAW,iDAAiD,mBAAmByB,EAAE,IAAI,MAAMpE,EAAWoE,EAAE,KAAK,KAAK,gBAAgBpE,EAAWoE,EAAE,QAAQ,MAAM,EAAG,GAAG,CAAC,iBAElK+B,EAAQ,SAAW,IAAGxD,GAAW,sBACrCtB,EAAOoB,EAAW,WAAWoF,IAAKrB,EAAarB,EAAOyB,CAAW,EAAGjE,CAAO,UAClEzB,EAAK,WAAW,OAAO,EAAG,CACnC,IAAMb,EAAM,mBAAmBa,EAAK,MAAM,CAAc,CAAC,EACnDoF,EAAUnB,EAAM,WAAW,CAAC9E,CAAG,EAAG,OAAW,GAAG,EAClDsC,EAAU,oCAAoC3C,EAAWK,CAAG,aAChEsC,GAAW,MAAM2D,EAAQ,iBAAiBA,EAAQ,SAAW,EAAI,GAAK,oBAAoBtG,EAAWK,CAAG,eACxG,QAAW2C,KAAKsD,EACd3D,GAAW,iDAAiD,mBAAmBK,EAAE,IAAI,MAAMhD,EAAWgD,EAAE,KAAK,KAAK,oCAAoCzC,GAAUyC,EAAE,KAAK,MAAQ,CAAC,GAAG,MAAM,EAAG,CAAC,CAAC,gBAE5LsD,EAAQ,SAAW,IAAG3D,GAAW,qCACrCtB,EAAOoB,EAAW,QAAQpC,IAAOmG,EAAarB,EAAOyB,CAAW,EAAGjE,CAAO,OAEvE,CAEL,IAAMoB,EAAekD,EAAO,MAAMA,EAAK,gBAAgB,EAAI,CAAC,EACtDa,EAAcjE,EAAI,QAAQ,EAE1BkE,EAAW,IAAI,IAAID,EAAY,IAAI9E,GAAK,CAACA,EAAE,QAASA,CAAC,CAAC,CAAC,EACvDc,EAAgC,CAAC,EACjCkE,EAAO,IAAI,IAEjB,QAAWC,KAASH,EAAa,CAC/B,IAAMI,EAASnE,EAAa,KAAKK,GAAKA,EAAE,UAAY6D,EAAM,OAAO,EAC7DC,GAAUA,EAAO,cAAgBD,EAAM,cAEzCnE,EAAU,KAAK,CACb,GAAGoE,EACH,aAAc,KAAK,IAAID,EAAM,aAAcC,EAAO,YAAY,EAC9D,WAAY,KAAK,IAAID,EAAM,WAAYC,EAAO,UAAU,CAC1D,CAAC,EAEDpE,EAAU,KAAKmE,CAAK,EAEtBD,EAAK,IAAIC,EAAM,OAAO,EAGxB,QAAWC,KAAUnE,EACdiE,EAAK,IAAIE,EAAO,OAAO,IAC1BpE,EAAU,KAAKoE,CAAM,EACrBF,EAAK,IAAIE,EAAO,OAAO,GAI3B,GAAIhH,IAAS,KAAOA,IAAS,GAC3BG,EAAOuC,GAAQC,EAAKC,EAAWC,CAAY,UAClC7C,IAAS,WAClBG,EAAOsE,GAAW9B,EAAKC,CAAS,UACvB5C,IAAS,UAClBG,EAAO0E,GAAUlC,EAAKwD,EAAI,aAAa,IAAI,GAAG,GAAK,GAAIvD,CAAS,UACvD5C,EAAK,WAAW,OAAO,EAChCG,EAAO+E,GAAWvC,EAAK,mBAAmB3C,EAAK,MAAM,CAAc,CAAC,EAAG4C,CAAS,UACvE5C,EAAK,MAAM,0BAA0B,EAAG,CACjD,IAAMiH,EAAIjH,EAAK,MAAM,+BAA+B,EACpDG,EAAOkF,GAAgB1C,EAAK,mBAAmBsE,EAAE,CAAC,CAAC,EAAG,mBAAmBA,EAAE,CAAC,CAAC,EAAGrE,CAAS,UAChF5C,EAAK,WAAW,SAAS,EAAG,CACrC,IAAMoB,EAAOpB,EAAK,MAAM,CAAgB,EAClCwG,EAAWpF,EAAK,QAAQ,WAAW,EACzC,GAAIoF,IAAa,GAEfrG,EAAOoD,GAAiBZ,EAAKvB,EAAMwB,CAAS,MACvC,CAEL,IAAMxD,EAAUgC,EAAK,MAAM,EAAGoF,CAAQ,EAChCxC,EAAc5C,EAAK,MAAMoF,EAAW,CAAkB,EAI5D,GAHArG,EAAO4D,GAAepB,EAAKvD,EAAS4E,EAAapB,CAAS,EAGtD,CAACzC,GAAQ4F,EAAM,CACjB,IAAMiB,EAASnE,EAAa,KAAKK,GAAKA,EAAE,UAAY9D,CAAO,EAC3D,GAAI4H,EAAQ,CACV,IAAM9C,EAAU,MAAM6B,EAAK,iBAAiBiB,EAAO,QAAS5H,EAAS4E,CAAW,EAChF,GAAIE,EAAS,CACX,IAAIzC,EAAU,4EAA4E3C,EAAWkI,EAAO,MAAM,cAAclI,EAAWM,CAAO,UAClJqC,GAAW,OAAO3C,EAAWoF,EAAQ,KAAK,KAAK,SAC/CzC,GAAW,oBACXA,GAAW,qBAAqByC,EAAQ,KAAK,MAAM,CAAC,GAAG,IAAI3E,IAAK,qBAAqBA,WAAU,EAAE,KAAK,GAAG,SACzGkC,GAAW,sDAAsD3C,EAAWkI,EAAO,MAAM,SACzFvF,GAAW,uBAAuByC,EAAQ,KAAK,mBAC/CzC,GAAW,QACXA,GAAWxB,EAAGiE,EAAQ,QAAS,IAAI,GAAK,EACxC/D,EAAOoB,EAAW2C,EAAQ,KAAK,MAAO/B,EAAWS,CAAS,EAAGnB,CAAO,OAQhF,GAAI,CAACtB,EAAM,CACTkG,EAAS,IACT,IAAM7E,EAAUkE,EACZJ,EAAa3C,EAAI,aAAa+C,CAAW,EAAGA,CAAW,EACvDvD,EAAWQ,EAAI,QAAQ,CAAC,EAC5BxC,EAAOoB,EAAW,YAAaC,EAAS,yBAAyB,EAErE,OAAS0F,EAAP,CACAb,EAAS,IACTlG,EAAOoB,EAAW,QAAS,GAAI,sBAAsBzC,EAAWoI,EAAI,OAAO,SAAS,CACtF,CAEAhB,EAAI,UAAUG,EAAQ,CAAE,eAAgB,0BAA2B,CAAC,EACpEH,EAAI,IAAI7B,EAAS8C,GAAYhH,EAAMkE,CAAM,EAAIlE,CAAI,CACnD,CACF,CAWA,SAASgH,GAAYhH,EAAckE,EAAwB,CAEzDlE,EAAOA,EAAK,QAAQ,iCAAkC,KAAKkE,IAAS,EAEpElE,EAAOA,EAAK,QAAQ,IAAI,OAAO,IAAIkE,KAAUA,KAAW,GAAG,EAAG,GAAGA,IAAS,EAE1E,IAAM+C,EAAO,oTACb,OAAAjH,EAAOA,EAAK,QAAQ,gBAAiB,WAAWiH,GAAM,EAC/CjH,CACT,CAKO,SAASkH,GAAgB5B,EAAiB6B,EAAe,KAAM5B,EAAsBC,EAAqB,CAAC,EAAGC,EAAqC,QAAe,CACvK,IAAM2B,EAAUhC,GAAkB,CAAE,QAAAE,EAAS,YAAAC,EAAa,SAAAC,EAAU,KAAAC,CAAK,CAAC,EAC3D4B,GAAa,CAACvB,EAAKC,IAAQ,CAAOqB,EAAQtB,EAAKC,CAAG,CAAE,CAAC,EAC7D,OAAOoB,CAAI,CACpB","names":["existsSync","readFileSync","writeFileSync","appendFileSync","mkdirSync","resolve","hostname","existsSync","readFileSync","writeFileSync","copyFileSync","resolve","mutateAgentxConfig","mutator","opts","file","resolve","existsSync","cfg","readFileSync","raw","result","json","tmp","writeFileSync","e","backupPath","copyFileSync","triggerLocalReload","writeAgentxConfig","url","loadDaemonConfig","renderSetupPage","state","defaultNode","heading","customHeader","step1","stepCard","field","step2","row","step3","step4","banner","body","esc","btn","renderShell","SETUP_PAGE_CSS","SETUP_SCRIPT","wizardState","baseDir","file","resolve","existsSync","cfg","readFileSync","agents","channels","c","runWizard","payload","mentions","splitMentions","configPath","envPath","workspaceDir","state","mkdirSync","claudeMd","personality","writeFileSync","telegramTokenRef","backupPath","mutateAgentxConfig","slugify","agentBlock","telegramAccountBlock","buildFreshConfig","writeAgentxConfig","ensureEnvEntries","nextSteps","s","m","i","block","tokenRef","existing","lines","sep","appendFileSync","handleWizardGet","_req","res","defaultNode","hostname","html","renderSetupPage","handleWizardPost","req","raw","ok","result","e","createHash","randomBytes","existsSync","mkdirSync","readFileSync","writeFileSync","resolve","dirname","TOKEN_PREFIX","DEFAULT_FILE","TokenStore","baseDir","fileRel","records","input","s","isValidScope","body","token","record","hashToken","id","rec","hash","r","now","last","recordHasScope","required","extractToken","req","auth","authHeader","tok","createServer","MeshWikiClient","peerUrls","url","results","peer","data","agent","peerUrl","agentId","articlePath","cached","res","escapeHtml","s","stripSectionTags","text","tagLink","tag","agentId","tagLinks","tags","t","resolveWikilink","target","wikiArticles","byTitle","pathSet","withExt","targetLower","title","path","md","agentPrefix","html","_m","lang","code","header","_sep","body","ths","c","rows","row","rawTarget","rawDisplay","display","block","l","chk","rest","HEAD_FONTS","CSS","pageLayout","sidebar","content","searchQuery","groupByDir","articles","byDir","a","dir","list","b","dirLabel","hubSidebar","agents","activePath","agent","byType","type","isActive","hubHome","hub","allAgents","remoteAgents","totalEntries","totalArticles","totalUnabsorbed","remoteIds","r","nodes","statusChip","remoteBadge","tagPart","hubAgentOverview","agentSummary","isRemote","entries","recent","e","issues","issue","hubArticlePage","articlePath","store","article","index","titleToPath","prefix","inbound","link","linked","hubEntries","byDate","date","dateEntries","hubSearch","query","totalResults","blocks","results","hubTagPage","total","matches","hubAgentTagPage","agentSidebar","createWikiHandler","opts","wikiDir","agentFilter","peerUrls","mode","pathPrefix","WikiHub","mesh","MeshWikiClient","req","res","url","rawPath","status","parts","peerAgent","slashIdx","peerUrl","i","q","localAgents","localMap","seen","local","remote","m","err","prefixHrefs","pill","startWikiServer","port","handler","createServer"]}
|