tg-agent 1.2.0 → 1.2.1

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/index.js CHANGED
@@ -1,17 +1,17 @@
1
- import D from"node:path";import q from"node:fs/promises";import ge from"node-telegram-bot-api";import{discoverAuthStorage as pe,discoverModels as we}from"@mariozechner/pi-coding-agent";import{getOAuthProviders as he}from"@mariozechner/pi-ai";import{config as m,assertConfig as $e}from"./config.js";import{resolveApiKeyForProvider as ye,resolveProxyInfo as ve}from"./auth.js";import{applyFetchProxy as Me}from"./proxy.js";import{runPiAgentPrompt as Se}from"./piAgentRunner.js";import{readCodexOAuth as ke}from"./codexAuth.js";import{formatMcpTarget as _e,freezeMcpCatalog as G,getMcpCatalog as Q,loadMcpServers as Ae,loadMcpServersSync as Pe,probeMcpServer as xe,refreshMcpCatalog as Ce}from"./mcp.js";import{appendMessage as V,closeSession as be,createSession as U,deleteSessionFile as z,getActiveSession as T,listSessions as H,loadChatState as X,pruneExpiredSessions as J,resetSession as Te,saveChatState as h,setActiveSession as Ee,setWorkspaceDir as Re}from"./sessionStore.js";import{chunkText as De,createQueueMap as ze,createSemaphore as Le,ensureDir as j,expandHome as Ne,nowMs as _,shortId as Z}from"./utils.js";$e();const p=new ge(m.telegramToken,{polling:!0}),Y=ze(),Ue=Le(m.maxConcurrent),W=new Map;function je(e,t){W.set(e,t)}function We(e){W.delete(e)}function Oe(e){const t=e.trim();if(t){if(t==="Markdown"||t==="MarkdownV2"||t==="HTML")return t;console.warn(`[tg-agent] invalid TELEGRAM_PARSE_MODE=${t}, ignoring`)}}const Be="image/jpeg";function Fe(){return D.join(m.agentDir,"uploads")}function Ke(e){return e.length===0?null:e.reduce((t,n)=>{const o=t.file_size??t.width*t.height;return(n.file_size??n.width*n.height)>o?n:t})}function qe(e){return!!e?.startsWith("image/")}function Ge(e){switch(D.extname(e).toLowerCase()){case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".webp":return"image/webp";case".gif":return"image/gif";default:return}}function Qe(e){if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KB","MB","GB"];let n=e,o=0;for(;n>=1024&&o<t.length-1;)n/=1024,o+=1;return`${n.toFixed(n>=10||o===0?0:1)} ${t[o]}`}function Ve(e){return e.length===0?"":e.map(t=>{const n=[];t.mimeType&&n.push(t.mimeType),t.bytes>0&&n.push(Qe(t.bytes));const o=n.length>0?` (${n.join(", ")})`:"";return`Attachment saved: ${t.path}${o}`}).join(`
2
- `)}function He(e){const t=[];if(e.photo&&e.photo.length>0){const n=Ke(e.photo);n&&t.push({kind:"photo",fileId:n.file_id,fileSize:n.file_size})}if(e.document&&t.push({kind:"document",fileId:e.document.file_id,fileName:e.document.file_name,mimeType:e.document.mime_type,fileSize:e.document.file_size}),e.audio){const n=e.audio.mime_type?.split("/")[1]??"mp3",o=e.audio.title??e.audio.performer??"audio";t.push({kind:"document",fileId:e.audio.file_id,fileName:`${o}.${n}`,mimeType:e.audio.mime_type,fileSize:e.audio.file_size})}if(e.voice&&t.push({kind:"document",fileId:e.voice.file_id,fileName:`voice.${e.voice.mime_type?.split("/")[1]??"ogg"}`,mimeType:e.voice.mime_type,fileSize:e.voice.file_size}),e.video){const n=e.video.mime_type?.split("/")[1]??"mp4";t.push({kind:"document",fileId:e.video.file_id,fileName:`video.${n}`,mimeType:e.video.mime_type,fileSize:e.video.file_size})}return e.video_note&&t.push({kind:"document",fileId:e.video_note.file_id,fileName:"video_note.mp4",mimeType:"video/mp4",fileSize:e.video_note.file_size}),t}async function Xe(e){if(e.length===0)return{resolved:[],images:[]};const t=Fe();await j(t);const n=[],o=[];for(const s of e)try{const i=await p.downloadFile(s.fileId,t),d=await q.stat(i),r=s.fileName??D.basename(i),c=s.mimeType??Ge(i),a=s.kind==="photo"||qe(c),u={path:i,name:r,mimeType:c,bytes:d.size,isImage:a};if(n.push(u),a){const f=await q.readFile(i);o.push({type:"image",data:f.toString("base64"),mimeType:c??Be})}}catch(i){console.warn("[tg-agent] attachment download failed",i)}return{resolved:n,images:o}}const Je=new Set(["_","*","[","]","(",")","~","`",">","#","+","-","=","|","{","}",".","!"]);function E(e,t){let n=0;for(let o=0;o<e.length;o+=1){const s=e[o];if(s==="\\"){o+=1;continue}s===t&&(n+=1)}return n}function Ze(e){if(E(e,"*")%2!==0||E(e,"_")%2!==0||E(e,"`")%2!==0)return!1;const s=E(e,"["),i=E(e,"]");return s===i}function Ye(e){for(let t=0;t<e.length;t+=1){const n=e[t];if(n==="\\"){t+=1;continue}if(Je.has(n))return!1}return!0}function I(e){const t=Oe(m.telegramParseMode);return t?[t]:Ye(e)?["MarkdownV2","Markdown"]:Ze(e)?["Markdown"]:[]}const Ie=600*1e3,k=new Map,O=new Set,et=new Map([["codex","openai-codex"],["antigravity","google-antigravity"],["gemini","google-gemini-cli"],["gemini-cli","google-gemini-cli"]]),tt={"codex-mini-latest":"gpt-5.1-codex-mini","codex-max-latest":"gpt-5.1-codex-max","codex-latest":"gpt-5.2-codex"};function P(e){const t=e.trim().toLowerCase();return et.get(t)??t}function nt(e,t){return e!=="openai-codex"?t:tt[t]??t}async function x(){await j(m.agentDir);const e=pe(m.agentDir),t=ke();t&&e.setRuntimeApiKey("openai-codex",t.accessToken);const n=we(e,m.agentDir);return{authStorage:e,modelRegistry:n}}function ee(e){const t=k.get(e);t&&(clearTimeout(t.timeoutId),k.delete(e))}function ot(e,t){const n=k.get(e);n&&(clearTimeout(n.timeoutId),k.delete(e),n.reject(new Error(t)))}async function te(e,t,n){if(k.has(e))throw new Error("Login is already awaiting input.");const o=n.placeholder?`${n.message} (${n.placeholder})`:n.message;return await l(t,o),await new Promise((s,i)=>{const d=setTimeout(()=>{k.delete(e),i(new Error("Login prompt timed out."))},Ie);k.set(e,{resolve:s,reject:i,timeoutId:d,chatId:t})})}async function ne(e){const t=String(e),n=W.get(t);if(!n){await l(e,"No active request to stop.");return}n.cancelRequested=!0,n.abortRequested=!0,n.status&&n.status.update("Cancelled by user.",!0),n.abort&&n.abort(),(!n.status||n.chatId!==e)&&await l(e,"Stopping current request...")}function rt(){console.log(`[tg-agent] modelProvider=${m.modelProvider} modelRef=${m.modelRef} sessionDir=${m.sessionDir} maxConcurrent=${m.maxConcurrent}`),console.log(`[tg-agent] agentDir=${m.agentDir} workspaceDir=${m.workspaceDir}`);const e=Pe(m.agentDir),t=e.length>0?`on (${e.length})`:"off";console.log(`[tg-agent] tools fetchMaxBytes=${m.fetchMaxBytes} fetchTimeoutMs=${m.fetchTimeoutMs} mcp=${t}`),console.log(`[tg-agent] proxy url=${m.proxyUrl||"(none)"} fetchProxy=${m.fetchProxyUrl||"(none)"}`),console.log(`[tg-agent] tls extra_ca=${m.tlsExtraCaCerts||"(empty)"} reject_unauthorized=${m.tlsRejectUnauthorized===null?"(unset)":String(m.tlsRejectUnauthorized)}`);try{const{source:o}=ye(m.modelProvider);console.log(`[tg-agent] authSource=${o}`)}catch(o){const s=o instanceof Error?o.message:String(o);console.warn(`[tg-agent] authSource=missing (${s})`)}const n=ve();console.log(n?`[tg-agent] proxyUrl=${n.url} kind=${n.kind} source=${n.source}`:"[tg-agent] proxyUrl=(none)")}rt(),j(m.agentDir).catch(e=>{console.warn(`[tg-agent] ensure agentDir failed: ${e instanceof Error?e.message:String(e)}`)}),(async()=>{try{await Q(m.agentDir,{timeoutMs:3e3,maxBytes:m.fetchMaxBytes,maxChars:2e3}),G()}catch(e){const t=e instanceof Error?e.message:String(e);console.warn(`[tg-agent] mcp catalog warmup failed: ${t}`)}})();const L=Me();console.log(L?`[tg-agent] fetchProxy=${L.url} kind=${L.kind} source=${L.source}`:"[tg-agent] fetchProxy=(none)");function B(e){if(!e.startsWith("/"))return null;const t=e.trim(),[n,...o]=t.split(" ");return{command:n.split("@")[0].slice(1).toLowerCase(),args:o.join(" ").trim()}}async function oe(e,t){let n=T(e);return n||(n=U(e,""),await h(e),await l(t,`Created session ${n.id}.`)),n}function it(e,t){const n=e.getAll(),o=new Map;for(const s of n){const i=o.get(s.provider)??{count:0,auth:t.hasAuth(s.provider)};i.count+=1,o.set(s.provider,i)}return Array.from(o.entries()).sort((s,i)=>s[0].localeCompare(i[0])).map(([s,i])=>({label:`${s} (${i.count}, ${i.auth?"ok":"no auth"})`,command:`/provider ${s}`}))}function st(e,t,n=[]){const o=e.getAll().filter(r=>r.provider===t).sort((r,c)=>r.id.localeCompare(c.id));if(n.length===0)return o.map(r=>({label:r.id,command:`/model ${t}/${r.id}`}));const s=new Map(o.map(r=>[r.id,r])),i=new Set,d=[];for(const r of n){const c=s.get(r);!c||i.has(c.id)||(i.add(c.id),d.push(c))}for(const r of o)i.has(r.id)||d.push(r);return d.map(r=>({label:r.id,command:`/model ${t}/${r.id}`}))}function at(e,t,n=3){const o=new Set,s=[],i=H(e);for(const d of i){if(d.modelProvider!==t)continue;const r=d.modelId;if(!(!r||o.has(r))&&(o.add(r),s.push(r),s.length>=n))break}return s}function ct(e){const t=e.trim();if(!t)return null;if(t.includes("/")){const[n,o]=t.split("/",2);return!n||!o?null:{provider:P(n),modelId:o.trim()}}return{modelId:t}}const lt=6e4,re=new Map;function ie(e,t){const n=Date.now(),o=re.get(e)??0;n-o<lt||(re.set(e,n),console.warn(`[tg-agent] unauthorized user=${e} chat=${t}`))}function se(e){const t=m.telegramAllowedUsers;return!t||t.size===0?!0:t.has(e)}function dt(e){return e.length===0?"No sessions found.":["Sessions:",...e.map(n=>{const o=new Date(n.updatedAt).toISOString();return`- ${n.id} | ${n.title} | updated ${o}`})].join(`
3
- `)}function F(e,t){if(t.workspaceDir)return t.workspaceDir;const n=m.workspaceMappings.get(e);return n||m.workspaceDir}async function ae(e,t,n){return n?await p.sendMessage(e,t,{parse_mode:n}):await p.sendMessage(e,t)}async function ce(e,t,n,o){if(o){await p.editMessageText(n,{chat_id:e,message_id:t,parse_mode:o});return}await p.editMessageText(n,{chat_id:e,message_id:t})}async function le(e,t){let n;for(const o of t)try{return await e(o)}catch(s){n=s;const i=s instanceof Error?s.message:String(s);console.warn(`[tg-agent] parse_mode ${o} failed, trying fallback: ${i}`)}try{return await e(void 0)}catch(o){if(n){const s=o instanceof Error?o.message:String(o);console.warn(`[tg-agent] parse_mode fallback to plain failed: ${s}`)}throw o}}async function ut(e,t){return await le(n=>ae(e,t,n),I(t))}async function ft(e,t,n){await le(o=>ce(e,t,n,o),I(n))}async function l(e,t){const n=De(t,3900);for(const o of n)await ut(e,o)}const de=1800*1e3,ue=2e3,mt=60,v=new Map;function gt(){const e=_()-de;for(const[o,s]of v)s.createdAt<e&&v.delete(o);if(v.size<=ue)return;const t=Array.from(v.entries()).sort((o,s)=>o[1].createdAt-s[1].createdAt),n=v.size-ue;for(let o=0;o<n;o+=1)v.delete(t[o][0])}function pt(e){gt();let t=Z();for(;v.has(t);)t=Z();return v.set(t,{command:e,createdAt:_()}),t}function wt(e){if(!e.startsWith("cmd:"))return null;const t=e.slice(4),n=v.get(t);return n?_()-n.createdAt>de?(v.delete(t),null):n.command:null}function ht(e,t){if(e.length<=t)return[e];const n=[];for(let o=0;o<e.length;o+=t)n.push(e.slice(o,o+t));return n}function $t(e,t){const n=[];let o=[];for(const s of e){const i=pt(s.command);o.push({text:s.label,callback_data:`cmd:${i}`}),o.length>=t&&(n.push(o),o=[])}return o.length>0&&n.push(o),n}async function yt(e,t,n){return await p.sendMessage(e,t,{reply_markup:{inline_keyboard:n}})}async function fe(e,t,n,o){const s=o?.perRow??1,i=o?.pageSize??mt,d=o?.footer,r=ht(n,i);for(let c=0;c<r.length;c+=1){const a=r[c],u=r.length>1?` (page ${c+1}/${r.length})`:"",f=[`${t}${u}`];d&&f.push(d);const g=$t(a,s);await yt(e,f.join(`
4
- `),g)}}const vt=3900,Mt=1200;function K(e,t=vt){return e.length<=t?{text:e,truncated:!1}:{text:`${e.slice(0,Math.max(0,t-15))}...
1
+ import U from"node:path";import X from"node:fs/promises";import $e from"node-telegram-bot-api";import{discoverAuthStorage as ve,discoverModels as he}from"@mariozechner/pi-coding-agent";import{getOAuthProviders as Me}from"@mariozechner/pi-ai";import{config as m,assertConfig as Se}from"./config.js";import{resolveApiKeyForProvider as _e,resolveProxyInfo as ke}from"./auth.js";import{applyFetchProxy as Ae}from"./proxy.js";import{runPiAgentPrompt as xe}from"./piAgentRunner.js";import{readCodexOAuth as Pe}from"./codexAuth.js";import{formatMcpTarget as Ce,freezeMcpCatalog as J,getMcpCatalog as Z,loadMcpServers as be,loadMcpServersSync as Ee,probeMcpServer as Re,refreshMcpCatalog as De}from"./mcp.js";import{appendMessage as Y,closeSession as ze,createSession as F,deleteSessionFile as j,getActiveSession as z,listSessions as T,loadChatState as I,pruneExpiredSessions as ee,resetSession as Le,saveChatState as $,setActiveSession as Ne,setWorkspaceDir as Ue}from"./sessionStore.js";import{chunkText as je,createQueueMap as We,createSemaphore as Oe,ensureDir as K,expandHome as Be,nowMs as C,shortId as te}from"./utils.js";Se();const v=new $e(m.telegramToken,{polling:!0}),ne=We(),Fe=Oe(m.maxConcurrent),G=new Map;function Ke(e,t){G.set(e,t)}function Ge(e){G.delete(e)}function Qe(e){const t=e.trim();if(t){if(t==="Markdown"||t==="MarkdownV2"||t==="HTML")return t;console.warn(`[tg-agent] invalid TELEGRAM_PARSE_MODE=${t}, ignoring`)}}const Ve="image/jpeg";function qe(){return U.join(m.agentDir,"uploads")}function He(e){return e.length===0?null:e.reduce((t,n)=>{const o=t.file_size??t.width*t.height;return(n.file_size??n.width*n.height)>o?n:t})}function Xe(e){return!!e?.startsWith("image/")}function Je(e){switch(U.extname(e).toLowerCase()){case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".webp":return"image/webp";case".gif":return"image/gif";default:return}}function Ze(e){if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KB","MB","GB"];let n=e,o=0;for(;n>=1024&&o<t.length-1;)n/=1024,o+=1;return`${n.toFixed(n>=10||o===0?0:1)} ${t[o]}`}function Ye(e){return e.length===0?"":e.map(t=>{const n=[];t.mimeType&&n.push(t.mimeType),t.bytes>0&&n.push(Ze(t.bytes));const o=n.length>0?` (${n.join(", ")})`:"";return`Attachment saved: ${t.path}${o}`}).join(`
2
+ `)}function Te(e){const t=[];if(e.photo&&e.photo.length>0){const n=He(e.photo);n&&t.push({kind:"photo",fileId:n.file_id,fileSize:n.file_size})}if(e.document&&t.push({kind:"document",fileId:e.document.file_id,fileName:e.document.file_name,mimeType:e.document.mime_type,fileSize:e.document.file_size}),e.audio){const n=e.audio.mime_type?.split("/")[1]??"mp3",o=e.audio.title??e.audio.performer??"audio";t.push({kind:"document",fileId:e.audio.file_id,fileName:`${o}.${n}`,mimeType:e.audio.mime_type,fileSize:e.audio.file_size})}if(e.voice&&t.push({kind:"document",fileId:e.voice.file_id,fileName:`voice.${e.voice.mime_type?.split("/")[1]??"ogg"}`,mimeType:e.voice.mime_type,fileSize:e.voice.file_size}),e.video){const n=e.video.mime_type?.split("/")[1]??"mp4";t.push({kind:"document",fileId:e.video.file_id,fileName:`video.${n}`,mimeType:e.video.mime_type,fileSize:e.video.file_size})}return e.video_note&&t.push({kind:"document",fileId:e.video_note.file_id,fileName:"video_note.mp4",mimeType:"video/mp4",fileSize:e.video_note.file_size}),t}async function Ie(e){if(e.length===0)return{resolved:[],images:[]};const t=qe();await K(t);const n=[],o=[];for(const i of e)try{const c=await v.downloadFile(i.fileId,t),p=await X.stat(c),a=i.fileName??U.basename(c),s=i.mimeType??Je(c),d=i.kind==="photo"||Xe(s),r={path:c,name:a,mimeType:s,bytes:p.size,isImage:d};if(n.push(r),d){const u=await X.readFile(c);o.push({type:"image",data:u.toString("base64"),mimeType:s??Ve})}}catch(c){console.warn("[tg-agent] attachment download failed",c)}return{resolved:n,images:o}}const et=new Set(["_","*","[","]","(",")","~","`",">","#","+","-","=","|","{","}",".","!"]);function L(e,t){let n=0;for(let o=0;o<e.length;o+=1){const i=e[o];if(i==="\\"){o+=1;continue}i===t&&(n+=1)}return n}function tt(e){if(L(e,"*")%2!==0||L(e,"_")%2!==0||L(e,"`")%2!==0)return!1;const i=L(e,"["),c=L(e,"]");return i===c}function nt(e){for(let t=0;t<e.length;t+=1){const n=e[t];if(n==="\\"){t+=1;continue}if(et.has(n))return!1}return!0}function oe(e){const t=Qe(m.telegramParseMode);return t?[t]:nt(e)?["MarkdownV2","Markdown"]:tt(e)?["Markdown"]:[]}const ot=600*1e3,A=new Map,Q=new Set,it=new Map([["codex","openai-codex"],["antigravity","google-antigravity"],["gemini","google-gemini-cli"],["gemini-cli","google-gemini-cli"]]),rt={"codex-mini-latest":"gpt-5.1-codex-mini","codex-max-latest":"gpt-5.1-codex-max","codex-latest":"gpt-5.2-codex"};function E(e){const t=e.trim().toLowerCase();return it.get(t)??t}function st(e,t){return e!=="openai-codex"?t:rt[t]??t}async function R(){await K(m.agentDir);const e=ve(m.agentDir),t=Pe();t&&e.setRuntimeApiKey("openai-codex",t.accessToken);const n=he(e,m.agentDir);return{authStorage:e,modelRegistry:n}}function ie(e){const t=A.get(e);t&&(clearTimeout(t.timeoutId),A.delete(e))}function at(e,t){const n=A.get(e);n&&(clearTimeout(n.timeoutId),A.delete(e),n.reject(new Error(t)))}async function re(e,t,n,o){if(A.has(e))throw new Error("Login is already awaiting input.");const i=n.placeholder?`${n.message} (${n.placeholder})`:n.message;return await l(t,i,o),await new Promise((c,p)=>{const a=setTimeout(()=>{A.delete(e),p(new Error("Login prompt timed out."))},ot);A.set(e,{resolve:c,reject:p,timeoutId:a,chatId:t})})}async function se(e){const t=String(e),n=G.get(t);if(!n){await l(e,"No active request to stop.");return}n.cancelRequested=!0,n.abortRequested=!0,n.status&&n.status.update("Cancelled by user.",!0),n.abort&&n.abort(),(!n.status||n.chatId!==e)&&await l(e,"Stopping current request...")}function ct(){console.log(`[tg-agent] modelProvider=${m.modelProvider} modelRef=${m.modelRef} sessionDir=${m.sessionDir} maxConcurrent=${m.maxConcurrent}`),console.log(`[tg-agent] agentDir=${m.agentDir} workspaceDir=${m.workspaceDir}`);const e=Ee(m.agentDir),t=e.length>0?`on (${e.length})`:"off";console.log(`[tg-agent] tools fetchMaxBytes=${m.fetchMaxBytes} fetchTimeoutMs=${m.fetchTimeoutMs} mcp=${t}`),console.log(`[tg-agent] proxy url=${m.proxyUrl||"(none)"} fetchProxy=${m.fetchProxyUrl||"(none)"}`),console.log(`[tg-agent] tls extra_ca=${m.tlsExtraCaCerts||"(empty)"} reject_unauthorized=${m.tlsRejectUnauthorized===null?"(unset)":String(m.tlsRejectUnauthorized)}`);try{const{source:o}=_e(m.modelProvider);console.log(`[tg-agent] authSource=${o}`)}catch(o){const i=o instanceof Error?o.message:String(o);console.warn(`[tg-agent] authSource=missing (${i})`)}const n=ke();console.log(n?`[tg-agent] proxyUrl=${n.url} kind=${n.kind} source=${n.source}`:"[tg-agent] proxyUrl=(none)")}ct(),K(m.agentDir).catch(e=>{console.warn(`[tg-agent] ensure agentDir failed: ${e instanceof Error?e.message:String(e)}`)}),(async()=>{try{await Z(m.agentDir,{timeoutMs:3e3,maxBytes:m.fetchMaxBytes,maxChars:2e3}),J()}catch(e){const t=e instanceof Error?e.message:String(e);console.warn(`[tg-agent] mcp catalog warmup failed: ${t}`)}})();const W=Ae();console.log(W?`[tg-agent] fetchProxy=${W.url} kind=${W.kind} source=${W.source}`:"[tg-agent] fetchProxy=(none)");function V(e){if(!e.startsWith("/"))return null;const t=e.trim(),[n,...o]=t.split(" ");return{command:n.split("@")[0].slice(1).toLowerCase(),args:o.join(" ").trim()}}async function ae(e,t,n){let o=z(e);return o||(o=F(e,""),await $(e),await l(t,`Created session ${o.id}.`,n)),o}function lt(e,t){const n=e.getAll(),o=new Map;for(const i of n){const c=o.get(i.provider)??{count:0,auth:t.hasAuth(i.provider)};c.count+=1,o.set(i.provider,c)}return Array.from(o.entries()).sort((i,c)=>i[0].localeCompare(c[0])).map(([i,c])=>({label:`${i} (${c.count}, ${c.auth?"ok":"no auth"})`,command:`/provider ${i}`}))}function ut(e,t,n=[]){const o=e.getAll().filter(a=>a.provider===t).sort((a,s)=>a.id.localeCompare(s.id));if(n.length===0)return o.map(a=>({label:a.id,command:`/model ${t}/${a.id}`}));const i=new Map(o.map(a=>[a.id,a])),c=new Set,p=[];for(const a of n){const s=i.get(a);!s||c.has(s.id)||(c.add(s.id),p.push(s))}for(const a of o)c.has(a.id)||p.push(a);return p.map(a=>({label:a.id,command:`/model ${t}/${a.id}`}))}function ft(e,t,n=3){const o=new Set,i=[],c=T(e);for(const p of c){if(p.modelProvider!==t)continue;const a=p.modelId;if(!(!a||o.has(a))&&(o.add(a),i.push(a),i.length>=n))break}return i}function dt(e){const t=e.trim();if(!t)return null;if(t.includes("/")){const[n,o]=t.split("/",2);return!n||!o?null:{provider:E(n),modelId:o.trim()}}return{modelId:t}}const mt=6e4,ce=new Map;function le(e,t){const n=Date.now(),o=ce.get(e)??0;n-o<mt||(ce.set(e,n),console.warn(`[tg-agent] unauthorized user=${e} chat=${t}`))}function ue(e){const t=m.telegramAllowedUsers;return!t||t.size===0?!0:t.has(e)}function pt(e){return e.length===0?"No sessions found.":["Sessions:",...e.map(n=>{const o=new Date(n.updatedAt).toISOString();return`- ${n.id} | ${n.title} | updated ${o}`})].join(`
3
+ `)}function O(e,t,n){const o=String(e);return t!==void 0&&!n?null:t!==void 0&&n?`${o}_${t}`:o}function q(e,t){if(t.workspaceDir)return t.workspaceDir;const n=m.workspaceMappings.get(e);return n||m.workspaceDir}async function fe(e,t,n,o){const i={};return n&&(i.parse_mode=n),o!==void 0&&(i.message_thread_id=o),await v.sendMessage(e,t,i)}async function de(e,t,n,o,i){const c={chat_id:e,message_id:t};o&&(c.parse_mode=o),i!==void 0&&(c.message_thread_id=i),await v.editMessageText(n,c)}async function me(e,t){let n;for(const o of t)try{return await e(o)}catch(i){n=i;const c=i instanceof Error?i.message:String(i);console.warn(`[tg-agent] parse_mode ${o} failed, trying fallback: ${c}`)}try{return await e(void 0)}catch(o){if(n){const i=o instanceof Error?o.message:String(o);console.warn(`[tg-agent] parse_mode fallback to plain failed: ${i}`)}throw o}}async function gt(e,t,n){return await me(o=>fe(e,t,o,n),oe(t))}async function wt(e,t,n,o){await me(i=>de(e,t,n,i,o),oe(n))}async function l(e,t,n){const o=je(t,3900);for(const i of o)await gt(e,i,n)}const pe=1800*1e3,ge=2e3,yt=60,S=new Map;function $t(){const e=C()-pe;for(const[o,i]of S)i.createdAt<e&&S.delete(o);if(S.size<=ge)return;const t=Array.from(S.entries()).sort((o,i)=>o[1].createdAt-i[1].createdAt),n=S.size-ge;for(let o=0;o<n;o+=1)S.delete(t[o][0])}function vt(e){$t();let t=te();for(;S.has(t);)t=te();return S.set(t,{command:e,createdAt:C()}),t}function ht(e){if(!e.startsWith("cmd:"))return null;const t=e.slice(4),n=S.get(t);return n?C()-n.createdAt>pe?(S.delete(t),null):n.command:null}function Mt(e,t){if(e.length<=t)return[e];const n=[];for(let o=0;o<e.length;o+=t)n.push(e.slice(o,o+t));return n}function St(e,t){const n=[];let o=[];for(const i of e){const c=vt(i.command);o.push({text:i.label,callback_data:`cmd:${c}`}),o.length>=t&&(n.push(o),o=[])}return o.length>0&&n.push(o),n}async function _t(e,t,n,o){const i={reply_markup:{inline_keyboard:n}};return o!==void 0&&(i.message_thread_id=o),await v.sendMessage(e,t,i)}async function we(e,t,n,o){const i=o?.perRow??1,c=o?.pageSize??yt,p=o?.footer,a=o?.messageThreadId,s=Mt(n,c);for(let d=0;d<s.length;d+=1){const r=s[d],u=s.length>1?` (page ${d+1}/${s.length})`:"",f=[`${t}${u}`];p&&f.push(p);const g=St(r,i);await _t(e,f.join(`
4
+ `),g,a)}}const kt=3900,At=1200;function H(e,t=kt){return e.length<=t?{text:e,truncated:!1}:{text:`${e.slice(0,Math.max(0,t-15))}...
5
5
 
6
- [truncated]`,truncated:!0}}async function St(e){const t="Working...",o=(await ae(e,t)).message_id;let s=t,i=0,d=Promise.resolve();const r=(a,u=!1)=>{const f=Date.now();!u&&f-i<Mt||a!==s&&(s=a,i=f,d=d.then(async()=>{await ce(e,o,a)}).catch(g=>{console.warn("[tg-agent] status edit failed",g)}))},c=a=>{s=a,i=Date.now(),d=d.then(async()=>{await ft(e,o,a)}).catch(u=>{console.warn("[tg-agent] status edit failed",u)})};return{update:(a,u=!1)=>{const f=K(a).text;r(f,u)},finalize:async a=>{const u=K(a).text;c(u),await d},fail:async a=>{const u=K(a).text;c(u),await d}}}function kt(e){switch(e.type){case"agent_start":return"Working...";case"tool_start":return`Running tool: ${e.name}
7
- args: ${_t(e.args)}`;case"tool_end":return e.ok?`Tool finished: ${e.name} (${e.durationMs}ms)`:`Tool failed: ${e.name} (${e.durationMs}ms)`;case"message_start":return e.role==="assistant"?"Generating reply...":null;case"message_end":return e.role==="assistant"?"Finalizing reply...":null;case"heartbeat":return`Working... ${Math.max(1,Math.round(e.elapsedMs/1e3))}s`;default:return null}}function _t(e){try{const t=new WeakSet,n=200,o=12,s=12,i=3,d=(a,u)=>{if(a==null)return a;if(typeof a=="string")return a.length>n?`${a.slice(0,n)}...`:a;if(typeof a=="number"||typeof a=="boolean")return a;if(typeof a=="bigint")return a.toString();if(typeof a=="function")return"[function]";if(typeof a!="object")return String(a);if(t.has(a))return"[circular]";if(u>=i)return"[truncated]";if(t.add(a),Array.isArray(a)){const w=a.slice(0,s).map(C=>d(C,u+1));return a.length>s&&w.push("[truncated]"),w}const f=Object.entries(a),g={};for(const[w,C]of f.slice(0,o))g[w]=d(C,u+1);return f.length>o&&(g._truncated=!0),g},r=d(e,0),c=JSON.stringify(r);return c?c.length>700?`${c.slice(0,700)}...`:c:"null"}catch{return"[unavailable]"}}function N(e){const t=e instanceof Error?e.message:String(e),n=e?.cause;return n instanceof Error?`${t} (${n.message})`:n?`${t} (${String(n)})`:t}function At(e,t=140){return e.length<=t?e:`${e.slice(0,t-3)}...`}async function Pt(){const e=await Ae(m.agentDir);if(e.length===0)return"No MCP servers configured. Add [mcp_servers.*] to ~/.tg-agent/config.toml.";const t=await Promise.all(e.map(o=>xe(o))),n=["MCP servers:"];for(let o=0;o<e.length;o+=1){const s=e[o],i=t[o],d=i.ok?"ok":`error: ${At(i.error??"unknown")}`;n.push(`- ${s.name} (${s.type}) ${_e(s)} status=${d} (${i.durationMs}ms)`)}return n.join(`
8
- `)}async function me(e,t,n,o){const s=String(e),i=await X(s),d=J(i);switch(d.length>0&&(await h(i),await Promise.all(d.map(r=>z(s,r).catch(c=>{console.warn(`[tg-agent] cleanup session file failed id=${r}`,c)})))),n){case"start":case"help":{const r=["Commands:","/new [title] - create a new session","/list - list sessions","/use <id> - switch active session","/close [id] - close a session (default: active)","/reset - clear active session history","/workspace [path] - view or set workspace directory","/providers - list available providers","/models [provider] - list models for provider","/provider <name> - set provider for current session","/model <provider>/<model> - set model for current session","/mcp - list configured MCP servers","/mcp refresh - reload MCP catalog","/status - show session and workspace settings","/login <provider> - login to OAuth provider","/logout <provider> - logout from provider","/stop - stop the current running request","","Tips:","Send images or files to attach them to the prompt."].join(`
9
- `);await l(e,r);return}case"new":{try{const c=U(i,o||"");await h(i),await l(e,`Created session ${c.id} (${c.title}).`)}catch(r){await l(e,r.message)}return}case"list":{await h(i),await l(e,dt(H(i)));return}case"use":{if(!o){await l(e,"Usage: /use <id>");return}if(!Ee(i,o)){await l(e,`Session not found: ${o}`);return}await h(i),await l(e,`Active session set to ${o}.`);return}case"close":{const r=o||i.activeSessionId;if(!r){await l(e,"No active session to close.");return}if(!be(i,r)){await l(e,`Session not found: ${r}`);return}await h(i),await z(s,r).catch(a=>{console.warn(`[tg-agent] delete session file failed id=${r}`,a)}),await l(e,`Closed session ${r}.`);return}case"reset":{const r=T(i);if(!r){await l(e,"No active session.");return}Te(r),await h(i),await z(s,r.id).catch(c=>{console.warn(`[tg-agent] reset session file failed id=${r.id}`,c)}),await l(e,`Reset session ${r.id}.`);return}case"workspace":{if(!o){const c=F(s,i),a=i.workspaceDir?"chat":m.workspaceMappings.has(s)?"config":"default";await l(e,`Workspace: ${c} (source: ${a})`);return}const r=D.resolve(Ne(o.trim()));Re(i,r),await h(i),await l(e,`Workspace set to: ${r}`);return}case"providers":{const{authStorage:r,modelRegistry:c}=await x(),a=c.getError(),u=it(c,r);if(u.length===0){const g=a?`Warning: ${a}
6
+ [truncated]`,truncated:!0}}async function xt(e,t){const n="Working...",i=(await fe(e,n,void 0,t)).message_id;let c=n,p=0,a=Promise.resolve();const s=(r,u=!1)=>{const f=Date.now();!u&&f-p<At||r!==c&&(c=r,p=f,a=a.then(async()=>{await de(e,i,r,void 0,t)}).catch(g=>{console.warn("[tg-agent] status edit failed",g)}))},d=r=>{c=r,p=Date.now(),a=a.then(async()=>{await wt(e,i,r,t)}).catch(u=>{console.warn("[tg-agent] status edit failed",u)})};return{update:(r,u=!1)=>{const f=H(r).text;s(f,u)},finalize:async r=>{const u=H(r).text;d(u),await a},fail:async r=>{const u=H(r).text;d(u),await a}}}function Pt(e){switch(e.type){case"agent_start":return"Working...";case"tool_start":return`Running tool: ${e.name}
7
+ args: ${Ct(e.args)}`;case"tool_end":return e.ok?`Tool finished: ${e.name} (${e.durationMs}ms)`:`Tool failed: ${e.name} (${e.durationMs}ms)`;case"message_start":return e.role==="assistant"?"Generating reply...":null;case"message_end":return e.role==="assistant"?"Finalizing reply...":null;case"heartbeat":return`Working... ${Math.max(1,Math.round(e.elapsedMs/1e3))}s`;default:return null}}function Ct(e){try{const t=new WeakSet,n=200,o=12,i=12,c=3,p=(d,r)=>{if(d==null)return d;if(typeof d=="string")return d.length>n?`${d.slice(0,n)}...`:d;if(typeof d=="number"||typeof d=="boolean")return d;if(typeof d=="bigint")return d.toString();if(typeof d=="function")return"[function]";if(typeof d!="object")return String(d);if(t.has(d))return"[circular]";if(r>=c)return"[truncated]";if(t.add(d),Array.isArray(d)){const g=d.slice(0,i).map(w=>p(w,r+1));return d.length>i&&g.push("[truncated]"),g}const u=Object.entries(d),f={};for(const[g,w]of u.slice(0,o))f[g]=p(w,r+1);return u.length>o&&(f._truncated=!0),f},a=p(e,0),s=JSON.stringify(a);return s?s.length>700?`${s.slice(0,700)}...`:s:"null"}catch{return"[unavailable]"}}function B(e){const t=e instanceof Error?e.message:String(e),n=e?.cause;return n instanceof Error?`${t} (${n.message})`:n?`${t} (${String(n)})`:t}function bt(e,t=140){return e.length<=t?e:`${e.slice(0,t-3)}...`}async function Et(){const e=await be(m.agentDir);if(e.length===0)return"No MCP servers configured. Add [mcp_servers.*] to ~/.tg-agent/config.toml.";const t=await Promise.all(e.map(o=>Re(o))),n=["MCP servers:"];for(let o=0;o<e.length;o+=1){const i=e[o],c=t[o],p=c.ok?"ok":`error: ${bt(c.error??"unknown")}`;n.push(`- ${i.name} (${i.type}) ${Ce(i)} status=${p} (${c.durationMs}ms)`)}return n.join(`
8
+ `)}async function ye(e,t,n,o,i){const c=i,a=O(e,c,c!==void 0);if(!a)return;const s=await I(a),d=ee(s);switch(d.length>0&&(await $(s),await Promise.all(d.map(r=>j(a,r).catch(u=>{console.warn(`[tg-agent] cleanup session file failed id=${r}`,u)})))),n){case"start":case"help":{const r=["Commands:","/new [title] - create a new session","/list - list sessions","/use <id> - switch active session","/close [id] - close a session (default: active)","/reset - clear active session history","/workspace [path] - view or set workspace directory","/providers - list available providers","/models [provider] - list models for provider","/provider <name> - set provider for current session","/model <provider>/<model> - set model for current session","/mcp - list configured MCP servers","/mcp refresh - reload MCP catalog","/status - show session and workspace settings","/login <provider> - login to OAuth provider","/logout <provider> - logout from provider","/stop - stop the current running request","","Tips:","Send images or files to attach them to the prompt."].join(`
9
+ `);await l(e,r,i);return}case"new":{try{const u=F(s,o||"");await $(s),await l(e,`Created session ${u.id} (${u.title}).`,i)}catch(r){await l(e,r.message,i)}return}case"list":{await $(s),await l(e,pt(T(s)),i);return}case"use":{if(!o){await l(e,"Usage: /use <id>",i);return}if(!Ne(s,o)){await l(e,`Session not found: ${o}`,i);return}await $(s),await l(e,`Active session set to ${o}.`,i);return}case"close":{const r=o||s.activeSessionId;if(!r){await l(e,"No active session to close.",i);return}if(!ze(s,r)){await l(e,`Session not found: ${r}`,i);return}await $(s),await j(a,r).catch(f=>{console.warn(`[tg-agent] delete session file failed id=${r}`,f)}),await l(e,`Closed session ${r}.`,i);return}case"reset":{const r=z(s);if(!r){await l(e,"No active session.",i);return}Le(r),await $(s),await j(a,r.id).catch(u=>{console.warn(`[tg-agent] reset session file failed id=${r.id}`,u)}),await l(e,`Reset session ${r.id}.`,i);return}case"workspace":{if(!o){const u=q(a,s),f=s.workspaceDir?"chat":m.workspaceMappings.has(a)?"config":"default";await l(e,`Workspace: ${u} (source: ${f})`,i);return}const r=U.resolve(Be(o.trim()));Ue(s,r),await $(s),await l(e,`Workspace set to: ${r}`,i);return}case"providers":{const{authStorage:r,modelRegistry:u}=await R(),f=u.getError(),g=lt(u,r);if(g.length===0){const y=f?`Warning: ${f}
10
10
 
11
- No providers found.`:"No providers found.";await l(e,g);return}const f=[];a&&f.push(`Warning: ${a}`),f.push("Providers:"),await fe(e,f.join(`
12
- `),u,{perRow:1,footer:"Tap a provider to set it. Use /models <provider> to list models."});return}case"models":{const{authStorage:r,modelRegistry:c}=await x();let a=o?P(o):"";if(!a){const w=T(i);if(!w?.modelProvider){await l(e,"Usage: /models <provider>");return}a=w.modelProvider}const u=r.hasAuth(a),f=at(i,a),g=st(c,a,f);if(g.length===0){await l(e,`No models found for provider ${a}.`);return}await fe(e,`Models for ${a} (auth: ${u?"ok":"missing"}):`,g,{perRow:1,footer:"Tap a model to set it."});return}case"provider":{if(!o){await l(e,"Usage: /provider <name>");return}const r=P(o),{modelRegistry:c}=await x();if(!c.getAll().some(f=>f.provider===r)){await l(e,`Unknown provider: ${r}`);return}const u=await oe(i,e);u.modelProvider=r,u.modelId=void 0,u.updatedAt=_(),await h(i),await l(e,`Session ${u.id} provider set to ${r}.`);return}case"model":{const r=ct(o);if(!r){await l(e,"Usage: /model <provider>/<model>");return}const c=await oe(i,e),a=P(r.provider??c.modelProvider??"");if(!a){await l(e,"Usage: /model <provider>/<model>");return}const{modelRegistry:u}=await x(),f=nt(a,r.modelId);if(!u.find(a,f)){await l(e,`Model not found: ${a}/${f}`);return}c.modelProvider=a,c.modelId=f,c.updatedAt=_(),await h(i),await l(e,`Session ${c.id} model set to ${a}/${f}.`);return}case"status":{const r=T(i);if(!r){await l(e,"No active session.");return}const c=m.modelRef.includes("/")?m.modelRef:`${m.modelProvider}/${m.modelRef}`,a=r.modelProvider||r.modelId?`Model: ${r.modelProvider??"?"}/${r.modelId??"?"}`:`Model: ${c} (default)`,u=F(s,i),f=i.workspaceDir?"chat":m.workspaceMappings.has(s)?"config":"default",g=[`Session: ${r.id} (${r.title})`,a,`Workspace: ${u} (${f})`].join(`
13
- `);await l(e,g);return}case"mcp":{if(o.trim().toLowerCase()==="refresh"){Ce(),await Q(m.agentDir,{timeoutMs:5e3,maxBytes:m.fetchMaxBytes,maxChars:3e3}),G(),await l(e,"MCP catalog refreshed.");return}const r=await Pt();await l(e,r);return}case"login":{const r=he();if(!o){const u=r.map(f=>`- ${f.id} (${f.name})`);await l(e,["OAuth providers:",...u].join(`
14
- `));return}const c=P(o);if(O.has(t)){await l(e,"Login already in progress.");return}const a=r.find(u=>u.id===c);if(!a){await l(e,`Unknown OAuth provider: ${c}`);return}O.add(t);try{const{authStorage:u}=await x();await l(e,`Starting login for ${a.id}...`),await u.login(a.id,{onAuth:({url:f,instructions:g})=>{const w=["Open this URL in your browser:",f];g&&w.push("",g),l(e,w.join(`
15
- `))},onPrompt:f=>te(t,e,f),onProgress:f=>{l(e,f)},onManualCodeInput:()=>te(t,e,{message:"Paste the authorization code:"})}),await l(e,`Login completed for ${a.id}.`)}catch(u){const f=N(u);await l(e,`Login failed: ${f}`)}finally{O.delete(t),ee(t)}return}case"logout":{if(!o){await l(e,"Usage: /logout <provider>");return}const r=P(o),{authStorage:c}=await x();c.logout(r),await l(e,`Logged out from ${r}.`);return}default:await l(e,`Unknown command: ${n}`)}}async function xt(e,t,n,o=[]){const s=B(n);if(s){await me(e,t,s.command,s.args);return}const i=String(e),d=await X(i),r=J(d);r.length>0&&(await h(d),await Promise.all(r.map($=>z(i,$).catch(A=>{console.warn(`[tg-agent] cleanup session file failed id=${$}`,A)}))));let c=T(d);if(!c)try{c=U(d,""),await h(d),await l(e,`Created session ${c.id}.`)}catch($){await l(e,$.message);return}const{resolved:a,images:u}=await Xe(o);if(o.length>0&&a.length===0&&!n.trim()){await l(e,"Failed to download attachments.");return}const f=Ve(a),g=[];n.trim()&&g.push(n.trim()),f&&g.push(f);const w=g.join(`
11
+ No providers found.`:"No providers found.";await l(e,y,i);return}const w=[];f&&w.push(`Warning: ${f}`),w.push("Providers:"),await we(e,w.join(`
12
+ `),g,{perRow:1,footer:"Tap a provider to set it. Use /models <provider> to list models.",messageThreadId:i});return}case"models":{const{authStorage:r,modelRegistry:u}=await R();let f=o?E(o):"";if(!f){const x=z(s);if(!x?.modelProvider){await l(e,"Usage: /models <provider>",i);return}f=x.modelProvider}const g=r.hasAuth(f),w=ft(s,f),y=ut(u,f,w);if(y.length===0){await l(e,`No models found for provider ${f}.`,i);return}await we(e,`Models for ${f} (auth: ${g?"ok":"missing"}):`,y,{perRow:1,footer:"Tap a model to set it.",messageThreadId:i});return}case"provider":{if(!o){await l(e,"Usage: /provider <name>",i);return}const r=E(o),{modelRegistry:u}=await R();if(!u.getAll().some(w=>w.provider===r)){await l(e,`Unknown provider: ${r}`,i);return}const g=await ae(s,e,i);g.modelProvider=r,g.modelId=void 0,g.updatedAt=C(),await $(s),await l(e,`Session ${g.id} provider set to ${r}.`,i);return}case"model":{const r=dt(o);if(!r){await l(e,"Usage: /model <provider>/<model>",i);return}const u=await ae(s,e,i),f=E(r.provider??u.modelProvider??"");if(!f){await l(e,"Usage: /model <provider>/<model>",i);return}const{modelRegistry:g}=await R(),w=st(f,r.modelId);if(!g.find(f,w)){await l(e,`Model not found: ${f}/${w}`,i);return}u.modelProvider=f,u.modelId=w,u.updatedAt=C(),await $(s),await l(e,`Session ${u.id} model set to ${f}/${w}.`,i);return}case"status":{const r=z(s);if(!r){await l(e,"No active session.",i);return}const u=m.modelRef.includes("/")?m.modelRef:`${m.modelProvider}/${m.modelRef}`,f=r.modelProvider||r.modelId?`Model: ${r.modelProvider??"?"}/${r.modelId??"?"}`:`Model: ${u} (default)`,g=q(a,s),w=s.workspaceDir?"chat":m.workspaceMappings.has(a)?"config":"default",y=[`Session: ${r.id} (${r.title})`,f,`Workspace: ${g} (${w})`].join(`
13
+ `);await l(e,y,i);return}case"mcp":{if(o.trim().toLowerCase()==="refresh"){De(),await Z(m.agentDir,{timeoutMs:5e3,maxBytes:m.fetchMaxBytes,maxChars:3e3}),J(),await l(e,"MCP catalog refreshed.",i);return}const r=await Et();await l(e,r,i);return}case"login":{const r=Me();if(!o){const g=r.map(w=>`- ${w.id} (${w.name})`);await l(e,["OAuth providers:",...g].join(`
14
+ `),i);return}const u=E(o);if(Q.has(t)){await l(e,"Login already in progress.",i);return}const f=r.find(g=>g.id===u);if(!f){await l(e,`Unknown OAuth provider: ${u}`,i);return}Q.add(t);try{const{authStorage:g}=await R();await l(e,`Starting login for ${f.id}...`,i),await g.login(f.id,{onAuth:({url:w,instructions:y})=>{const x=["Open this URL in your browser:",w];y&&x.push("",y),l(e,x.join(`
15
+ `),i)},onPrompt:w=>re(t,e,w,i),onProgress:w=>{l(e,w,i)},onManualCodeInput:()=>re(t,e,{message:"Paste the authorization code:"},i)}),await l(e,`Login completed for ${f.id}.`,i)}catch(g){const w=B(g);await l(e,`Login failed: ${w}`,i)}finally{Q.delete(t),ie(t)}return}case"logout":{if(!o){await l(e,"Usage: /logout <provider>",i);return}const r=E(o),{authStorage:u}=await R();u.logout(r),await l(e,`Logged out from ${r}.`,i);return}default:await l(e,`Unknown command: ${n}`,i)}}async function Rt(e,t,n,o=[],i){const c=V(n);if(c){await ye(e,t,c.command,c.args,i);return}const a=O(e,i,i!==void 0);if(!a)return;const s=await I(a),d=ee(s);d.length>0&&(await $(s),await Promise.all(d.map(h=>j(a,h).catch(b=>{console.warn(`[tg-agent] cleanup session file failed id=${h}`,b)}))));let r=z(s);if(!r)try{r=F(s,""),await $(s),await l(e,`Created session ${r.id}.`,i)}catch(h){await l(e,h.message,i);return}const{resolved:u,images:f}=await Ie(o);if(o.length>0&&u.length===0&&!n.trim()){await l(e,"Failed to download attachments.",i);return}const g=Ye(u),w=[];n.trim()&&w.push(n.trim()),g&&w.push(g);const y=w.join(`
16
16
 
17
- `)||"Attachment received.",C={role:"user",content:w,ts:_()};V(c,C,m.maxHistoryMessages),await h(d),console.log(`[tg-agent] request user=${t} chat=${i} session=${c.id} messages=${c.messages.length} textLen=${w.length} attachments=${a.length} provider=${c.modelProvider??"-"} model=${c.modelId??"-"}`);let y=null;try{y=await St(e)}catch($){console.warn("[tg-agent] status message failed",$)}const b={sessionId:c.id,chatId:e,status:y,abortRequested:!1,cancelRequested:!1};je(i,b);try{const $=await Ue(async()=>{const R=await Se({chatId:i,sessionId:c.id,prompt:w,images:u,systemPrompt:m.systemPrompt,modelProvider:c.modelProvider,modelId:c.modelId,workspaceDir:F(i,d),telegram:{chatId:e,sendPhoto:async(M,S)=>{await p.sendPhoto(e,M,S?{caption:S}:void 0)},sendDocument:async(M,S)=>{await p.sendDocument(e,M,S?{caption:S}:void 0)}},onAbortReady:M=>{b.abort=M,b.abortRequested&&M()},onStatus:y?M=>{const S=kt(M);S&&y?.update(S)}:void 0});return console.log(`[tg-agent] response session=${c.id} model=${R.modelProvider}/${R.modelId} file=${R.sessionFile}`),R.text});if(b.cancelRequested){y?await y.finalize("Cancelled by user."):await l(e,"Cancelled by user.");return}const A={role:"assistant",content:$,ts:_()};V(c,A,m.maxHistoryMessages),await h(d),y?await y.finalize($):await l(e,$)}catch($){if(console.error("[tg-agent] runModel error",$),b.cancelRequested){y?await y.fail("Cancelled by user."):await l(e,"Cancelled by user.");return}const A=N($);y?await y.fail(`Error: ${A}`):await l(e,`Error: ${A}`)}finally{We(i)}}p.on("callback_query",e=>{const t=e.data??"",n=e.message?.chat.id;if(!e.from?.id||!n){p.answerCallbackQuery(e.id);return}const o=String(e.from.id);if(!se(o)){ie(o,n),p.answerCallbackQuery(e.id);return}if(!t.startsWith("cmd:")){p.answerCallbackQuery(e.id);return}const s=wt(t);if(!s){p.answerCallbackQuery(e.id,{text:"Command expired."});return}p.answerCallbackQuery(e.id);const i=String(n);Y(i,async()=>{const d=B(s);if(!d){await l(n,"Invalid command.");return}if(d.command==="stop"){await ne(n);return}await me(n,o,d.command,d.args)}).catch(async d=>{console.error("[tg-agent] runModel error",d);const r=N(d);await l(n,`Error: ${r}`)})}),p.on("message",e=>{if(!e.from?.id)return;const t=String(e.from.id),n=e.chat.id,o=String(n);if(!se(t)){ie(t,n);return}const s=(e.text??e.caption??"").trim(),i=He(e);if(!s&&i.length===0)return;const d=k.get(t);if(d){if(s==="/stop"||s==="/cancel"){ot(t,"Login cancelled by user."),l(n,"Login cancelled.");return}if(!s){l(n,"Login expects a text response.");return}ee(t),d.resolve(s);return}if(B(s)?.command==="stop"){ne(n);return}Y(o,()=>xt(n,t,s,i)).catch(async c=>{console.error("[tg-agent] runModel error",c);const a=N(c);await l(n,`Error: ${a}`)})}),p.on("polling_error",e=>{console.error("Polling error",e)}),console.log("tg-agent started");
17
+ `)||"Attachment received.",x={role:"user",content:y,ts:C()};Y(r,x,m.maxHistoryMessages),await $(s),console.log(`[tg-agent] request user=${t} chat=${a} session=${r.id} messages=${r.messages.length} textLen=${y.length} attachments=${u.length} provider=${r.modelProvider??"-"} model=${r.modelId??"-"}`);let M=null;try{M=await xt(e,i)}catch(h){console.warn("[tg-agent] status message failed",h)}const D={sessionId:r.id,chatId:e,messageThreadId:i,status:M,abortRequested:!1,cancelRequested:!1};Ke(a,D);try{const h=await Fe(async()=>{const N=await xe({chatId:a,sessionId:r.id,prompt:y,images:f,systemPrompt:m.systemPrompt,modelProvider:r.modelProvider,modelId:r.modelId,workspaceDir:q(a,s),telegram:{chatId:e,sendPhoto:async(_,k)=>{const P={};k&&(P.caption=k),i!==void 0&&(P.message_thread_id=i),await v.sendPhoto(e,_,P)},sendDocument:async(_,k)=>{const P={};k&&(P.caption=k),i!==void 0&&(P.message_thread_id=i),await v.sendDocument(e,_,P)}},onAbortReady:_=>{D.abort=_,D.abortRequested&&_()},onStatus:M?_=>{const k=Pt(_);k&&M?.update(k)}:void 0});return console.log(`[tg-agent] response session=${r.id} model=${N.modelProvider}/${N.modelId} file=${N.sessionFile}`),N.text});if(D.cancelRequested){M?await M.finalize("Cancelled by user."):await l(e,"Cancelled by user.",i);return}const b={role:"assistant",content:h,ts:C()};Y(r,b,m.maxHistoryMessages),await $(s),M?await M.finalize(h):await l(e,h,i)}catch(h){if(console.error("[tg-agent] runModel error",h),D.cancelRequested){M?await M.fail("Cancelled by user."):await l(e,"Cancelled by user.",i);return}const b=B(h);M?await M.fail(`Error: ${b}`):await l(e,`Error: ${b}`,i)}finally{Ge(a)}}v.on("callback_query",e=>{const t=e.data??"",n=e.message?.chat.id;if(!e.from?.id||!n){v.answerCallbackQuery(e.id);return}const o=String(e.from.id);if(!ue(o)){le(o,n),v.answerCallbackQuery(e.id);return}if(!t.startsWith("cmd:")){v.answerCallbackQuery(e.id);return}const i=ht(t);if(!i){v.answerCallbackQuery(e.id,{text:"Command expired."});return}v.answerCallbackQuery(e.id);const c=e.message?.message_thread_id,p=e.message?.is_topic_message??!1,a=O(n,c,p);a&&ne(a,async()=>{const s=V(i);if(!s){await l(n,"Invalid command.",c);return}if(s.command==="stop"){await se(n);return}await ye(n,o,s.command,s.args,c)}).catch(async s=>{console.error("[tg-agent] runModel error",s);const d=B(s);await l(n,`Error: ${d}`,c)})}),v.on("message",e=>{if(!e.from?.id)return;const t=String(e.from.id),n=e.chat.id;if(!ue(t)){le(t,n);return}const o=e.message_thread_id,i=e.is_topic_message??!1,c=O(n,o,i);if(!c)return;const p=(e.text??e.caption??"").trim(),a=Te(e);if(!p&&a.length===0)return;const s=A.get(t);if(s){if(p==="/stop"||p==="/cancel"){at(t,"Login cancelled by user."),l(n,"Login cancelled.",o);return}if(!p){l(n,"Login expects a text response.",o);return}ie(t),s.resolve(p);return}if(V(p)?.command==="stop"){se(n);return}ne(c,()=>Rt(n,t,p,a,o)).catch(async r=>{console.error("[tg-agent] runModel error",r);const u=B(r);await l(n,`Error: ${u}`,o)})}),v.on("polling_error",e=>{console.error("Polling error",e)}),console.log("tg-agent started");
package/dist/mcp.js CHANGED
@@ -1,5 +1,5 @@
1
- import R from"node:fs";import H from"node:fs/promises";import q from"node:path";import J from"node:readline";import{spawn as U}from"node:child_process";const G=6e4,A=2e5,Q=40,X=3500;let w=null,P=!1;const h=new Map,V=1500;function z(t){return q.join(t,"config.toml")}function dt(t){const e=z(t);try{const r=R.readFileSync(e,"utf8");return O(r)}catch{return[]}}async function Y(t){const e=z(t);try{const r=await H.readFile(e,"utf8");return O(r)}catch{return[]}}async function pt(t,e={}){if(w)return w;const r=await Y(t);if(r.length===0)return w="","";const n=e.maxTools??Q,s=e.maxChars??X,i=e.timeoutMs??4e3,o=e.maxBytes??A,a=["MCP catalog (auto-discovered):","Use tool 'mcp' with server=<name> method=<tool> params=<object>."];for(const u of r){const l=await K(u,{timeoutMs:i,maxBytes:o});if(a.push(`${u.name} (${u.type})`),l.length===0){a.push(" - tools unavailable");continue}const d=l.slice(0,n);for(const p of d){const y=p.description?`: ${p.description}`:"";a.push(` - ${p.name}${y}`)}l.length>d.length&&a.push(` - ...and ${l.length-d.length} more`)}let c=a.join(`
2
- `);return c.length>s&&(c=`${c.slice(0,s-20)}...
3
- (truncated)`),w=c,c}function mt(){w&&(P=!0)}function ht(){w=null,P=!1,h.clear()}function gt(t){if(t.type==="http")return t.url?`url=${t.url}`:"url=(missing)";const e=t.args&&t.args.length>0?` ${t.args.join(" ")}`:"";return t.command?`command=${t.command}${e}`:"command=(missing)"}async function yt(t,e=5e3){const r=Date.now();try{return await $(t,"tools/list",{},{timeoutMs:e}),{ok:!0,durationMs:Date.now()-r}}catch(n){const s=n instanceof Error?n.message:String(n);return{ok:!1,durationMs:Date.now()-r,error:s}}}async function $(t,e,r,n={}){const s=j(n.timeoutMs??G,1e3,12e4),i=j(n.maxBytes??A,1024,5e6),o=new AbortController,a=()=>o.abort();n.signal?.addEventListener("abort",a,{once:!0});const c=setTimeout(()=>o.abort(),s);try{return t.type==="http"?(e!=="initialize"&&e!=="notifications/initialized"&&await v(t,i,o.signal),await B(t,e,r,i,o.signal)):await nt(t,e,r,i,o.signal)}finally{clearTimeout(c),n.signal?.removeEventListener("abort",a)}}function j(t,e,r){return Number.isNaN(t)?e:Math.min(r,Math.max(e,t))}async function K(t,e){try{const r=await $(t,"tools/list",{},e);return Z(r.output)}catch(r){const n=r instanceof Error?r.message:String(r);return console.warn(`[tg-agent] mcp tools/list failed server=${t.name} error=${n}`),[]}}function Z(t){let e;try{e=JSON.parse(t)}catch{return[]}if(!e||typeof e!="object")return[];const r=e,s=(r.result??r).tools??r.tools;if(!Array.isArray(s))return[];const i=[];for(const o of s){if(!o||typeof o!="object")continue;const a=o,c=typeof a.name=="string"?a.name:"";if(!c)continue;const u=typeof a.description=="string"?a.description:void 0,l=a.inputSchema;i.push({name:c,description:u,inputSchema:l})}return i}async function B(t,e,r,n,s){if(!t.url)throw new Error("MCP server url is missing.");const i=Date.now(),o=await k(t,e,r,n,s),a=I(o.parsed);if(a)throw new Error(a);let c=o.bodyText;return o.parsed&&(c=JSON.stringify(o.parsed,null,2)),{ok:o.statusCode>=200&&o.statusCode<300,output:c,durationMs:Date.now()-i,bytes:o.bytes,truncated:o.truncated,statusCode:o.statusCode,statusText:o.statusText,contentType:o.contentType}}async function k(t,e,r,n,s){if(!t.url)throw new Error("MCP server url is missing.");const i=h.get(t.name),o={jsonrpc:"2.0",id:`tg-agent-${Date.now()}`,method:e,params:r??{}},a={"content-type":"application/json"};t.auth&&(a.authorization=t.auth.startsWith("Bearer ")?t.auth:`Bearer ${t.auth}`),i?.cookie&&(a.cookie=i.cookie),i?.sessionId&&(a["mcp-session-id"]=i.sessionId);const c=await fetch(t.url,{method:"POST",headers:a,body:JSON.stringify(o),signal:s});tt(t.name,c);const u=await ot(c,n,s);let l;try{l=JSON.parse(u.text)}catch{l=void 0}return et(t.name,l),{statusCode:c.status,statusText:c.statusText,contentType:c.headers.get("content-type"),bodyText:u.text,parsed:l,bytes:u.bytes,truncated:u.truncated}}function I(t){if(!t||typeof t!="object")return null;const e=t;if(e.error&&typeof e.error=="object"){const r=e.error;if(typeof r.message=="string")return r.message}return typeof e.error_description=="string"?e.error_description:typeof e.error=="string"?e.error:null}async function v(t,e,r){const n=h.get(t.name);if(n?.initializedAt)return;if(n?.inFlight){await n.inFlight;return}const s=(async()=>{const i=await k(t,"initialize",{protocolVersion:"2024-11-05",clientInfo:{name:"tg-agent",version:process.env.npm_package_version??"dev"},capabilities:{}},e,r),o=I(i.parsed);if(o){const u=o.toLowerCase();if(u.includes("method")&&u.includes("not"))return;throw new Error(o)}const a=new AbortController,c=setTimeout(()=>a.abort(),V);try{await k(t,"notifications/initialized",{},e,a.signal)}catch{}finally{clearTimeout(c)}})();h.set(t.name,{initializedAt:Date.now(),inFlight:s});try{await s;const i=h.get(t.name);h.set(t.name,{initializedAt:Date.now(),cookie:i?.cookie,sessionId:i?.sessionId})}catch(i){throw h.delete(t.name),i}}function tt(t,e){const n=h.get(t)??{initializedAt:0},s=e.headers.get("set-cookie"),i=e.headers.getSetCookie?.(),o=i&&i.length>0?i.join("; "):s;o&&h.set(t,{...n,cookie:o});const a=e.headers.get("mcp-session-id")??e.headers.get("x-mcp-session-id");a&&h.set(t,{...n,sessionId:a})}function et(t,e){if(!e||typeof e!="object")return;const n=e.result;if(!n)return;const s=typeof n.sessionId=="string"&&n.sessionId||typeof n.session_id=="string"&&n.session_id;if(!s)return;const i=h.get(t)??{initializedAt:0};h.set(t,{...i,sessionId:s})}async function nt(t,e,r,n,s){if(!t.command)throw new Error("MCP server command is missing.");const i=Date.now(),o=U(t.command,t.args??[],{stdio:["pipe","pipe","pipe"]}),a=()=>{o.killed||o.kill("SIGTERM")};if(s.aborted)throw a(),new Error("MCP request aborted.");const c=new Map;let u=!1,l="";const d=new Error("MCP request aborted."),p=J.createInterface({input:o.stdout,terminal:!1});p.on("line",f=>{if(f.length>n){u=!0;return}let m=null;try{m=JSON.parse(f)}catch{return}const g=typeof m.id=="string"?m.id:typeof m.id=="number"?String(m.id):"";if(!g)return;const x=c.get(g);x&&(c.delete(g),x.resolve(m))}),o.stderr?.on("data",f=>{const m=f.toString();if(l.length+m.length>n){u=!0;return}l+=m});const y=()=>{for(const f of c.values())f.reject(d);c.clear(),a()};s.addEventListener("abort",y,{once:!0});const b=f=>{const m=f===0?"MCP stdio exited.":l.trim()||"MCP stdio exited with error.";for(const g of c.values())g.reject(new Error(m));c.clear()};o.once("exit",f=>b(f));const _=async(f,m)=>{const g=`tg-agent-${Date.now()}-${Math.random().toString(16).slice(2,8)}`,x={jsonrpc:"2.0",id:g,method:f,params:m??{}};return await new Promise((W,N)=>{c.set(g,{resolve:W,reject:N}),o.stdin?.write(`${JSON.stringify(x)}
4
- `)})},S=await _("initialize",{protocolVersion:"2024-11-05",clientInfo:{name:"tg-agent",version:process.env.npm_package_version??"dev"},capabilities:{}});if(S.error&&typeof S.error=="object"){const f=S.error.message??"MCP initialize failed.";throw a(),new Error(String(f))}const L={jsonrpc:"2.0",method:"notifications/initialized"};o.stdin?.write(`${JSON.stringify(L)}
5
- `);const T=await _(e,r);s.removeEventListener("abort",y),p.close(),o.stdin?.end(),a();const D=new Promise(f=>{o.once("exit",()=>f())});if(await Promise.race([D,new Promise(f=>setTimeout(f,500))]),T.error&&typeof T.error=="object"){const f=T.error.message??"MCP request failed.";throw new Error(String(f))}const M=JSON.stringify(T,null,2),F=M.length>n?M.slice(0,n):M;return M.length>n&&(u=!0),{ok:!0,output:F,durationMs:Date.now()-i,bytes:Math.min(M.length,n),truncated:u}}async function ot(t,e,r){const n=t.body?.getReader?.();if(!n){const d=await t.text(),p=Math.min(d.length,e);return{text:d.slice(0,e),bytes:p,truncated:d.length>e}}const s=new TextDecoder("utf-8"),i=[];let o=0,a=!1;for(;;){if(r.aborted){try{await n.cancel()}catch{}throw new Error("MCP request aborted.")}const{done:d,value:p}=await n.read();if(d)break;if(!p)continue;const y=o+p.byteLength;if(y>e){const b=Math.max(0,e-o);b>0&&(i.push(p.slice(0,b)),o+=b),a=!0;try{await n.cancel()}catch{}break}i.push(p),o=y}const c=new Uint8Array(o);let u=0;for(const d of i)c.set(d,u),u+=d.byteLength;return{text:s.decode(c),bytes:o,truncated:a}}function O(t){const e=[];let r=null,n={};const s=()=>{if(!r)return;const i=n.type??(n.url?"http":n.command?"stdio":void 0);if(!i)return;const o=i==="http"||i==="stdio"?i:void 0;o&&(o==="http"&&!n.url||o==="stdio"&&!n.command||e.push({name:r,type:o,url:n.url,command:n.command,args:n.args,auth:n.auth}))};for(const i of t.split(/\r?\n/)){const o=it(i).trim();if(!o)continue;if(o.startsWith("[")&&o.endsWith("]")){s();const l=o.slice(1,-1).trim();if(!l.startsWith("mcp_servers.")){r=null,n={};continue}let d=l.slice(12).trim();if(d=E(d),!d){r=null,n={};continue}r=d,n={};continue}if(!r)continue;const a=o.indexOf("=");if(a===-1)continue;const c=o.slice(0,a).trim(),u=o.slice(a+1).trim();if(c)switch(c){case"type":n.type=C(u).toLowerCase();break;case"url":n.url=C(u);break;case"command":n.command=C(u);break;case"args":n.args=rt(u);break;case"auth":case"authorization":case"token":n.auth=C(u);break;default:break}}return s(),e}function E(t){return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function C(t){const e=t.trim();return e?e.startsWith('"')&&e.endsWith('"')?e.slice(1,-1).replace(/\\\\/g,"\\").replace(/\\"/g,'"'):e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e:""}function rt(t){const e=t.trim();if(!e.startsWith("[")||!e.endsWith("]"))return[];const r=e.slice(1,-1).trim();if(!r)return[];const n=[];let s="",i=!1,o="";for(let c=0;c<r.length;c+=1){const u=r[c];if(!i&&(u==='"'||u==="'")){i=!0,o=u;continue}if(i&&u===o){i=!1;continue}if(!i&&u===","){const l=s.trim();l&&n.push(E(l)),s="";continue}s+=u}const a=s.trim();return a&&n.push(E(a)),n}function it(t){let e=!1,r="";for(let n=0;n<t.length;n+=1){const s=t[n];if(!e&&(s==='"'||s==="'")){e=!0,r=s;continue}if(e&&s===r){e=!1;continue}if(!e&&(s==="#"||s===";"))return t.slice(0,n)}return t}export{$ as callMcpServer,gt as formatMcpTarget,mt as freezeMcpCatalog,pt as getMcpCatalog,z as getMcpConfigPath,Y as loadMcpServers,dt as loadMcpServersSync,yt as probeMcpServer,ht as refreshMcpCatalog};
1
+ import J from"node:fs";import R from"node:fs/promises";import W from"node:path";import q from"node:readline";import{spawn as H}from"node:child_process";import*as U from"@iarna/toml";const G=6e4,A=2e5,Q=40,X=3500;let w=null,_=!1;const m=new Map,V=1500;function I(t){return W.join(t,"config.toml")}function lt(t){const e=I(t);try{const r=J.readFileSync(e,"utf8");return O(r)}catch{return[]}}async function Y(t){const e=I(t);try{const r=await R.readFile(e,"utf8");return O(r)}catch{return[]}}async function ft(t,e={}){if(w)return w;const r=await Y(t);if(r.length===0)return w="","";const o=e.maxTools??Q,a=e.maxChars??X,i=e.timeoutMs??4e3,n=e.maxBytes??A,s=["MCP catalog (auto-discovered):","Use tool 'mcp' with server=<name> method=<tool> params=<object>."];for(const u of r){const l=await B(u,{timeoutMs:i,maxBytes:n});if(s.push(`${u.name} (${u.type})`),l.length===0){s.push(" - tools unavailable");continue}const d=l.slice(0,o);for(const p of d){const g=p.description?`: ${p.description}`:"";s.push(` - ${p.name}${g}`)}l.length>d.length&&s.push(` - ...and ${l.length-d.length} more`)}let c=s.join(`
2
+ `);return c.length>a&&(c=`${c.slice(0,a-20)}...
3
+ (truncated)`),w=c,c}function dt(){w&&(_=!0)}function pt(){w=null,_=!1,m.clear()}function ht(t){if(t.type==="http")return t.url?`url=${t.url}`:"url=(missing)";const e=t.args&&t.args.length>0?` ${t.args.join(" ")}`:"";return t.command?`command=${t.command}${e}`:"command=(missing)"}async function mt(t,e=5e3){const r=Date.now();try{return await P(t,"tools/list",{},{timeoutMs:e}),{ok:!0,durationMs:Date.now()-r}}catch(o){const a=o instanceof Error?o.message:String(o);return{ok:!1,durationMs:Date.now()-r,error:a}}}async function P(t,e,r,o={}){const a=k(o.timeoutMs??G,1e3,12e4),i=k(o.maxBytes??A,1024,5e6),n=new AbortController,s=()=>n.abort();o.signal?.addEventListener("abort",s,{once:!0});const c=setTimeout(()=>n.abort(),a);try{return t.type==="http"?(e!=="initialize"&&e!=="notifications/initialized"&&await v(t,i,n.signal),await Z(t,e,r,i,n.signal)):await nt(t,e,r,i,n.signal)}finally{clearTimeout(c),o.signal?.removeEventListener("abort",s)}}function k(t,e,r){return Number.isNaN(t)?e:Math.min(r,Math.max(e,t))}async function B(t,e){try{const r=await P(t,"tools/list",{},e);return K(r.output)}catch(r){const o=r instanceof Error?r.message:String(r);return console.warn(`[tg-agent] mcp tools/list failed server=${t.name} error=${o}`),[]}}function K(t){let e;try{e=JSON.parse(t)}catch{return[]}if(!e||typeof e!="object")return[];const r=e,a=(r.result??r).tools??r.tools;if(!Array.isArray(a))return[];const i=[];for(const n of a){if(!n||typeof n!="object")continue;const s=n,c=typeof s.name=="string"?s.name:"";if(!c)continue;const u=typeof s.description=="string"?s.description:void 0,l=s.inputSchema;i.push({name:c,description:u,inputSchema:l})}return i}async function Z(t,e,r,o,a){if(!t.url)throw new Error("MCP server url is missing.");const i=Date.now(),n=await x(t,e,r,o,a),s=j(n.parsed);if(s)throw new Error(s);let c=n.bodyText;return n.parsed&&(c=JSON.stringify(n.parsed,null,2)),{ok:n.statusCode>=200&&n.statusCode<300,output:c,durationMs:Date.now()-i,bytes:n.bytes,truncated:n.truncated,statusCode:n.statusCode,statusText:n.statusText,contentType:n.contentType}}async function x(t,e,r,o,a){if(!t.url)throw new Error("MCP server url is missing.");const i=m.get(t.name),n={jsonrpc:"2.0",id:`tg-agent-${Date.now()}`,method:e,params:r??{}},s={"content-type":"application/json"};t.auth&&(s.authorization=t.auth.startsWith("Bearer ")?t.auth:`Bearer ${t.auth}`),i?.cookie&&(s.cookie=i.cookie),i?.sessionId&&(s["mcp-session-id"]=i.sessionId);const c=await fetch(t.url,{method:"POST",headers:s,body:JSON.stringify(n),signal:a});tt(t.name,c);const u=await ot(c,o,a);let l;try{l=JSON.parse(u.text)}catch{l=void 0}return et(t.name,l),{statusCode:c.status,statusText:c.statusText,contentType:c.headers.get("content-type"),bodyText:u.text,parsed:l,bytes:u.bytes,truncated:u.truncated}}function j(t){if(!t||typeof t!="object")return null;const e=t;if(e.error&&typeof e.error=="object"){const r=e.error;if(typeof r.message=="string")return r.message}return typeof e.error_description=="string"?e.error_description:typeof e.error=="string"?e.error:null}async function v(t,e,r){const o=m.get(t.name);if(o?.initializedAt)return;if(o?.inFlight){await o.inFlight;return}const a=(async()=>{const i=await x(t,"initialize",{protocolVersion:"2024-11-05",clientInfo:{name:"tg-agent",version:process.env.npm_package_version??"dev"},capabilities:{}},e,r),n=j(i.parsed);if(n){const u=n.toLowerCase();if(u.includes("method")&&u.includes("not"))return;throw new Error(n)}const s=new AbortController,c=setTimeout(()=>s.abort(),V);try{await x(t,"notifications/initialized",{},e,s.signal)}catch{}finally{clearTimeout(c)}})();m.set(t.name,{initializedAt:Date.now(),inFlight:a});try{await a;const i=m.get(t.name);m.set(t.name,{initializedAt:Date.now(),cookie:i?.cookie,sessionId:i?.sessionId})}catch(i){throw m.delete(t.name),i}}function tt(t,e){const o=m.get(t)??{initializedAt:0},a=e.headers.get("set-cookie"),i=e.headers.getSetCookie?.(),n=i&&i.length>0?i.join("; "):a;n&&m.set(t,{...o,cookie:n});const s=e.headers.get("mcp-session-id")??e.headers.get("x-mcp-session-id");s&&m.set(t,{...o,sessionId:s})}function et(t,e){if(!e||typeof e!="object")return;const o=e.result;if(!o)return;const a=typeof o.sessionId=="string"&&o.sessionId||typeof o.session_id=="string"&&o.session_id;if(!a)return;const i=m.get(t)??{initializedAt:0};m.set(t,{...i,sessionId:a})}async function nt(t,e,r,o,a){if(!t.command)throw new Error("MCP server command is missing.");const i=Date.now(),n=H(t.command,t.args??[],{stdio:["pipe","pipe","pipe"]}),s=()=>{n.killed||n.kill("SIGTERM")};if(a.aborted)throw s(),new Error("MCP request aborted.");const c=new Map;let u=!1,l="";const d=new Error("MCP request aborted."),p=q.createInterface({input:n.stdout,terminal:!1});p.on("line",f=>{if(f.length>o){u=!0;return}let h=null;try{h=JSON.parse(f)}catch{return}const y=typeof h.id=="string"?h.id:typeof h.id=="number"?String(h.id):"";if(!y)return;const C=c.get(y);C&&(c.delete(y),C.resolve(h))}),n.stderr?.on("data",f=>{const h=f.toString();if(l.length+h.length>o){u=!0;return}l+=h});const g=()=>{for(const f of c.values())f.reject(d);c.clear(),s()};a.addEventListener("abort",g,{once:!0});const b=f=>{const h=f===0?"MCP stdio exited.":l.trim()||"MCP stdio exited with error.";for(const y of c.values())y.reject(new Error(h));c.clear()};n.once("exit",f=>b(f));const E=async(f,h)=>{const y=`tg-agent-${Date.now()}-${Math.random().toString(16).slice(2,8)}`,C={jsonrpc:"2.0",id:y,method:f,params:h??{}};return await new Promise((F,N)=>{c.set(y,{resolve:F,reject:N}),n.stdin?.write(`${JSON.stringify(C)}
4
+ `)})};fetch("http://127.0.0.1:7243/ingest/9e452bb4-cc67-4519-89fa-8fb51d810231",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"mcp.ts:callStdioMcp:beforeInit",message:"sending initialize request",data:{elapsed:Date.now()-i},timestamp:Date.now(),sessionId:"debug-session",hypothesisId:"B,C"})}).catch(()=>{});const T=await E("initialize",{protocolVersion:"2024-11-05",clientInfo:{name:"tg-agent",version:process.env.npm_package_version??"dev"},capabilities:{}});if(fetch("http://127.0.0.1:7243/ingest/9e452bb4-cc67-4519-89fa-8fb51d810231",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"mcp.ts:callStdioMcp:afterInit",message:"initialize response received",data:{elapsed:Date.now()-i,hasError:!!T.error},timestamp:Date.now(),sessionId:"debug-session",hypothesisId:"B,C"})}).catch(()=>{}),T.error&&typeof T.error=="object"){const f=T.error.message??"MCP initialize failed.";throw s(),new Error(String(f))}const D={jsonrpc:"2.0",method:"notifications/initialized"};n.stdin?.write(`${JSON.stringify(D)}
5
+ `),fetch("http://127.0.0.1:7243/ingest/9e452bb4-cc67-4519-89fa-8fb51d810231",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"mcp.ts:callStdioMcp:beforeMethod",message:"sending method request",data:{method:e,elapsed:Date.now()-i},timestamp:Date.now(),sessionId:"debug-session",hypothesisId:"C"})}).catch(()=>{});const S=await E(e,r);a.removeEventListener("abort",g),p.close(),n.stdin?.end(),s();const $=new Promise(f=>{n.once("exit",()=>f())});if(await Promise.race([$,new Promise(f=>setTimeout(f,500))]),S.error&&typeof S.error=="object"){const f=S.error.message??"MCP request failed.";throw new Error(String(f))}const M=JSON.stringify(S,null,2),L=M.length>o?M.slice(0,o):M;return M.length>o&&(u=!0),{ok:!0,output:L,durationMs:Date.now()-i,bytes:Math.min(M.length,o),truncated:u}}async function ot(t,e,r){const o=t.body?.getReader?.();if(!o){const d=await t.text(),p=Math.min(d.length,e);return{text:d.slice(0,e),bytes:p,truncated:d.length>e}}const a=new TextDecoder("utf-8"),i=[];let n=0,s=!1;for(;;){if(r.aborted){try{await o.cancel()}catch{}throw new Error("MCP request aborted.")}const{done:d,value:p}=await o.read();if(d)break;if(!p)continue;const g=n+p.byteLength;if(g>e){const b=Math.max(0,e-n);b>0&&(i.push(p.slice(0,b)),n+=b),s=!0;try{await o.cancel()}catch{}break}i.push(p),n=g}const c=new Uint8Array(n);let u=0;for(const d of i)c.set(d,u),u+=d.byteLength;return{text:a.decode(c),bytes:n,truncated:s}}function O(t){const e=[];let r;try{r=U.parse(t)}catch{return e}const o=r.mcp_servers;if(!o||typeof o!="object")return e;for(const[a,i]of Object.entries(o)){if(!i||typeof i!="object")continue;const n=i,s=typeof n.type=="string"?n.type.toLowerCase():void 0,c=typeof n.url=="string"?n.url:void 0,u=typeof n.command=="string"?n.command:void 0,l=s==="http"||s==="stdio"?s:c?"http":u?"stdio":void 0;if(!l||l==="http"&&!c||l==="stdio"&&!u)continue;let d;Array.isArray(n.args)&&(d=n.args.filter(g=>typeof g=="string").map(g=>g.trim()));const p=typeof n.auth=="string"?n.auth:typeof n.authorization=="string"?n.authorization:typeof n.token=="string"?n.token:void 0;e.push({name:a,type:l,url:c,command:u,args:d,auth:p})}return e}function z(t){return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function gt(t){const e=t.trim();return e?e.startsWith('"')&&e.endsWith('"')?e.slice(1,-1).replace(/\\\\/g,"\\").replace(/\\"/g,'"'):e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e:""}function yt(t){const e=t.trim();if(!e.startsWith("[")||!e.endsWith("]"))return[];const r=e.slice(1,-1).trim();if(!r)return[];const o=[];let a="",i=!1,n="";for(let c=0;c<r.length;c+=1){const u=r[c];if(!i&&(u==='"'||u==="'")){i=!0,n=u;continue}if(i&&u===n){i=!1;continue}if(!i&&u===","){const l=a.trim();l&&o.push(z(l)),a="";continue}a+=u}const s=a.trim();return s&&o.push(z(s)),o}function wt(t){let e=!1,r="";for(let o=0;o<t.length;o+=1){const a=t[o];if(!e&&(a==='"'||a==="'")){e=!0,r=a;continue}if(e&&a===r){e=!1;continue}if(!e&&(a==="#"||a===";"))return t.slice(0,o)}return t}export{P as callMcpServer,ht as formatMcpTarget,dt as freezeMcpCatalog,ft as getMcpCatalog,I as getMcpConfigPath,Y as loadMcpServers,lt as loadMcpServersSync,mt as probeMcpServer,pt as refreshMcpCatalog};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tg-agent",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Telegram Codex agent",
5
5
  "type": "module",
6
6
  "bin": {