tg-agent 1.1.1 → 1.1.2

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/config.js CHANGED
@@ -1 +1 @@
1
- import t from"node:path";import i from"dotenv";import{expandHome as n}from"./utils.js";i.config();function s(e,o){if(!e)return o;const r=Number.parseInt(e,10);return Number.isNaN(r)?o:r}function c(e){if(!e)return new Set;const o=e.split(",").map(r=>r.trim()).filter(r=>r.length>0);return new Set(o)}const p=process.env.SESSION_DIR??"~/.codex/tg-sessions",m=process.env.AGENT_DIR??"~/.tg-agent",a=process.env.WORKSPACE_DIR??process.cwd(),E=process.env.LLM_PROVIDER??"openai-codex",_=process.env.LLM_MODEL??process.env.CODEX_MODEL??"gpt-5.2",M=process.env.TELEGRAM_PARSE_MODE??"",T=process.env.TELEGRAM_ALLOWED_USER_IDS??"",S={telegramToken:process.env.TELEGRAM_BOT_TOKEN?.trim()??"",openaiApiKey:process.env.OPENAI_API_KEY?.trim()??"",modelProvider:E.trim(),modelRef:_.trim(),telegramParseMode:M.trim(),telegramAllowedUsers:c(T),sessionDir:n(p),agentDir:n(m),workspaceDir:t.resolve(n(a)),maxSessions:s(process.env.MAX_SESSIONS,3),maxConcurrent:s(process.env.MAX_CONCURRENT,3),sessionTtlMs:s(process.env.SESSION_TTL_MIN,60)*60*1e3,maxHistoryMessages:s(process.env.MAX_HISTORY_MESSAGES,40),maxOutputTokens:s(process.env.MAX_OUTPUT_TOKENS,0),fetchMaxBytes:s(process.env.FETCH_MAX_BYTES,2e5),fetchTimeoutMs:s(process.env.FETCH_TIMEOUT_MS,6e4),modelTimeoutMs:s(process.env.MODEL_TIMEOUT_MS,6e4),modelTimeoutStreamingMs:s(process.env.MODEL_TIMEOUT_STREAM_MS,3e5),systemPrompt:process.env.SYSTEM_PROMPT??"You are running in user's personal computer or VPS, using telegram bot to interact with user. Be concise and practical."};function v(){const e=[];if(S.telegramToken||e.push("TELEGRAM_BOT_TOKEN"),e.length>0)throw new Error(`Missing env vars: ${e.join(", ")}`)}export{v as assertConfig,S as config};
1
+ import t from"node:path";import i from"dotenv";import{expandHome as n}from"./utils.js";i.config();function s(e,o){if(!e)return o;const r=Number.parseInt(e,10);return Number.isNaN(r)?o:r}function c(e){if(!e)return new Set;const o=e.split(",").map(r=>r.trim()).filter(r=>r.length>0);return new Set(o)}const p=process.env.SESSION_DIR??"~/.codex/tg-sessions",m=process.env.AGENT_DIR??"~/.tg-agent",a=process.env.WORKSPACE_DIR??process.cwd(),E=process.env.LLM_PROVIDER??"openai-codex",_=process.env.LLM_MODEL??process.env.CODEX_MODEL??"gpt-5.2",M=process.env.TELEGRAM_PARSE_MODE??"",T=process.env.TELEGRAM_ALLOWED_USER_IDS??"",S={telegramToken:process.env.TELEGRAM_BOT_TOKEN?.trim()??"",openaiApiKey:process.env.OPENAI_API_KEY?.trim()??"",modelProvider:E.trim(),modelRef:_.trim(),telegramParseMode:M.trim(),telegramAllowedUsers:c(T),sessionDir:n(p),agentDir:n(m),workspaceDir:t.resolve(n(a)),maxSessions:s(process.env.MAX_SESSIONS,3),maxConcurrent:s(process.env.MAX_CONCURRENT,3),maxHistoryMessages:s(process.env.MAX_HISTORY_MESSAGES,40),maxOutputTokens:s(process.env.MAX_OUTPUT_TOKENS,0),fetchMaxBytes:s(process.env.FETCH_MAX_BYTES,2e5),fetchTimeoutMs:s(process.env.FETCH_TIMEOUT_MS,6e4),modelTimeoutMs:s(process.env.MODEL_TIMEOUT_MS,6e4),modelTimeoutStreamingMs:s(process.env.MODEL_TIMEOUT_STREAM_MS,3e5),systemPrompt:process.env.SYSTEM_PROMPT??"You are running in user's personal computer or VPS, using telegram bot to interact with user. Be concise and practical."};function v(){const e=[];if(S.telegramToken||e.push("TELEGRAM_BOT_TOKEN"),e.length>0)throw new Error(`Missing env vars: ${e.join(", ")}`)}export{v as assertConfig,S as config};
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import b from"node:path";import B from"node:fs/promises";import te from"node-telegram-bot-api";import{discoverAuthStorage as ne,discoverModels as oe}from"@mariozechner/pi-coding-agent";import{getOAuthProviders as re}from"@mariozechner/pi-ai";import{config as m,assertConfig as se}from"./config.js";import{resolveApiKeyForProvider as ie,resolveProxyInfo as ae}from"./auth.js";import{applyFetchProxy as ce}from"./proxy.js";import{runPiAgentPrompt as le}from"./piAgentRunner.js";import{readCodexOAuth as ue}from"./codexAuth.js";import{formatMcpTarget as de,freezeMcpCatalog as F,getMcpCatalog as q,loadMcpServers as me,loadMcpServersSync as fe,probeMcpServer as ge,refreshMcpCatalog as pe}from"./mcp.js";import{appendMessage as W,closeSession as we,createSession as L,deleteSessionFile as k,getActiveSession as x,listSessions as he,loadUserState as H,pruneExpiredSessions as X,resetSession as ye,saveUserState as w,setActiveSession as $e}from"./sessionStore.js";import{chunkText as ve,createQueueMap as Se,createSemaphore as Me,ensureDir as U,nowMs as C}from"./utils.js";se();const y=new te(m.telegramToken,{polling:!0}),_e=Se(),Ae=Me(m.maxConcurrent),O=new Map;function Pe(e,t){O.set(e,t)}function Te(e){O.delete(e)}function xe(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 Ee="image/jpeg";function Re(){return b.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 Ce(e){return!!e?.startsWith("image/")}function De(e){switch(b.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 be(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 Le(e){return e.length===0?"":e.map(t=>{const n=[];t.mimeType&&n.push(t.mimeType),t.bytes>0&&n.push(be(t.bytes));const o=n.length>0?` (${n.join(", ")})`:"";return`Attachment saved: ${t.path}${o}`}).join(`
2
- `)}function Ue(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})}return 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}),t}async function Oe(e){if(e.length===0)return{resolved:[],images:[]};const t=Re();await U(t);const n=[],o=[];for(const s of e)try{const a=await y.downloadFile(s.fileId,t),r=await B.stat(a),c=s.fileName??b.basename(a),u=s.mimeType??De(a),i=s.kind==="photo"||Ce(u),d={path:a,name:c,mimeType:u,bytes:r.size,isImage:i};if(n.push(d),i){const f=await B.readFile(a);o.push({type:"image",data:f.toString("base64"),mimeType:u??Ee})}}catch(a){console.warn("[tg-agent] attachment download failed",a)}return{resolved:n,images:o}}const ze=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 Ne(e){if(E(e,"*")%2!==0||E(e,"_")%2!==0||E(e,"`")%2!==0)return!1;const s=E(e,"["),a=E(e,"]");return s===a}function je(e){for(let t=0;t<e.length;t+=1){const n=e[t];if(n==="\\"){t+=1;continue}if(ze.has(n))return!1}return!0}function V(e){const t=xe(m.telegramParseMode);return t?[t]:je(e)?["MarkdownV2","Markdown"]:Ne(e)?["Markdown"]:[]}const Be=600*1e3,S=new Map,z=new Set,Fe=new Map([["codex","openai-codex"],["antigravity","google-antigravity"],["gemini","google-gemini-cli"],["gemini-cli","google-gemini-cli"]]),qe={"codex-mini-latest":"gpt-5.1-codex-mini","codex-max-latest":"gpt-5.1-codex-max","codex-latest":"gpt-5.2-codex"};function A(e){const t=e.trim().toLowerCase();return Fe.get(t)??t}function We(e,t){return e!=="openai-codex"?t:qe[t]??t}async function P(){await U(m.agentDir);const e=ne(m.agentDir),t=ue();t&&e.setRuntimeApiKey("openai-codex",t.accessToken);const n=oe(e,m.agentDir);return{authStorage:e,modelRegistry:n}}function K(e){const t=S.get(e);t&&(clearTimeout(t.timeoutId),S.delete(e))}function He(e,t){const n=S.get(e);n&&(clearTimeout(n.timeoutId),S.delete(e),n.reject(new Error(t)))}async function G(e,t,n){if(S.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,a)=>{const r=setTimeout(()=>{S.delete(e),a(new Error("Login prompt timed out."))},Be);S.set(e,{resolve:s,reject:a,timeoutId:r,chatId:t})})}async function Xe(e,t){const n=O.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 Ve(){const e={https_proxy:process.env.https_proxy??process.env.HTTPS_PROXY??"",http_proxy:process.env.http_proxy??process.env.HTTP_PROXY??"",all_proxy:process.env.all_proxy??process.env.ALL_PROXY??"",no_proxy:process.env.no_proxy??process.env.NO_PROXY??""},t={NODE_EXTRA_CA_CERTS:process.env.NODE_EXTRA_CA_CERTS??"",NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED??""};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 n=fe(m.agentDir),o=n.length>0?`on (${n.length})`:"off";console.log(`[tg-agent] tools fetchMaxBytes=${m.fetchMaxBytes} fetchTimeoutMs=${m.fetchTimeoutMs} mcp=${o}`),console.log(`[tg-agent] proxy https=${e.https_proxy||"(empty)"} http=${e.http_proxy||"(empty)"} all=${e.all_proxy||"(empty)"} no=${e.no_proxy||"(empty)"}`),console.log(`[tg-agent] tls extra_ca=${t.NODE_EXTRA_CA_CERTS||"(empty)"} reject_unauthorized=${t.NODE_TLS_REJECT_UNAUTHORIZED||"(empty)"}`);try{const{source:a}=ie(m.modelProvider);console.log(`[tg-agent] authSource=${a}`)}catch(a){const r=a instanceof Error?a.message:String(a);console.warn(`[tg-agent] authSource=missing (${r})`)}const s=ae();console.log(s?`[tg-agent] proxyUrl=${s.url} kind=${s.kind} source=${s.source}`:"[tg-agent] proxyUrl=(none)")}Ve(),U(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}),F()}catch(e){const t=e instanceof Error?e.message:String(e);console.warn(`[tg-agent] mcp catalog warmup failed: ${t}`)}})();const D=ce();console.log(D?`[tg-agent] fetchProxy=${D.url} kind=${D.kind} source=${D.source}`:"[tg-agent] fetchProxy=(none)");function J(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 Y(e,t){let n=x(e);return n||(n=L(e,""),await w(e),await l(t,`Created session ${n.id}.`)),n}function Ke(e,t){const n=e.getAll(),o=new Map;for(const a of n){const r=o.get(a.provider)??{count:0,auth:t.hasAuth(a.provider)};r.count+=1,o.set(a.provider,r)}return o.size===0?"No providers found.":["Providers:",...Array.from(o.entries()).sort((a,r)=>a[0].localeCompare(r[0])).map(([a,r])=>`- ${a} (models: ${r.count}, auth: ${r.auth?"ok":"missing"})`),"","Use /models <provider> to list models."].join(`
3
- `)}function Ge(e,t,n){const o=e.getAll().filter(a=>a.provider===t);if(o.length===0)return`No models found for provider ${t}.`;const s=o.map(a=>`- ${a.id} | ${a.name}`);return[`Models for ${t} (auth: ${n?"ok":"missing"}):`,...s].join(`
2
+ `)}function Ue(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})}return 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}),t}async function Oe(e){if(e.length===0)return{resolved:[],images:[]};const t=Re();await U(t);const n=[],o=[];for(const s of e)try{const a=await y.downloadFile(s.fileId,t),r=await B.stat(a),c=s.fileName??b.basename(a),u=s.mimeType??De(a),i=s.kind==="photo"||Ce(u),d={path:a,name:c,mimeType:u,bytes:r.size,isImage:i};if(n.push(d),i){const f=await B.readFile(a);o.push({type:"image",data:f.toString("base64"),mimeType:u??Ee})}}catch(a){console.warn("[tg-agent] attachment download failed",a)}return{resolved:n,images:o}}const ze=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 Ne(e){if(E(e,"*")%2!==0||E(e,"_")%2!==0||E(e,"`")%2!==0)return!1;const s=E(e,"["),a=E(e,"]");return s===a}function je(e){for(let t=0;t<e.length;t+=1){const n=e[t];if(n==="\\"){t+=1;continue}if(ze.has(n))return!1}return!0}function V(e){const t=xe(m.telegramParseMode);return t?[t]:je(e)?["MarkdownV2","Markdown"]:Ne(e)?["Markdown"]:[]}const Be=600*1e3,S=new Map,z=new Set,Fe=new Map([["codex","openai-codex"],["antigravity","google-antigravity"],["gemini","google-gemini-cli"],["gemini-cli","google-gemini-cli"]]),qe={"codex-mini-latest":"gpt-5.1-codex-mini","codex-max-latest":"gpt-5.1-codex-max","codex-latest":"gpt-5.2-codex"};function A(e){const t=e.trim().toLowerCase();return Fe.get(t)??t}function We(e,t){return e!=="openai-codex"?t:qe[t]??t}async function P(){await U(m.agentDir);const e=ne(m.agentDir),t=ue();t&&e.setRuntimeApiKey("openai-codex",t.accessToken);const n=oe(e,m.agentDir);return{authStorage:e,modelRegistry:n}}function K(e){const t=S.get(e);t&&(clearTimeout(t.timeoutId),S.delete(e))}function He(e,t){const n=S.get(e);n&&(clearTimeout(n.timeoutId),S.delete(e),n.reject(new Error(t)))}async function G(e,t,n){if(S.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,a)=>{const r=setTimeout(()=>{S.delete(e),a(new Error("Login prompt timed out."))},Be);S.set(e,{resolve:s,reject:a,timeoutId:r,chatId:t})})}async function Xe(e,t){const n=O.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 Ve(){const e={https_proxy:process.env.https_proxy??process.env.HTTPS_PROXY??"",http_proxy:process.env.http_proxy??process.env.HTTP_PROXY??"",all_proxy:process.env.all_proxy??process.env.ALL_PROXY??"",no_proxy:process.env.no_proxy??process.env.NO_PROXY??""},t={NODE_EXTRA_CA_CERTS:process.env.NODE_EXTRA_CA_CERTS??"",NODE_TLS_REJECT_UNAUTHORIZED:process.env.NODE_TLS_REJECT_UNAUTHORIZED??""};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 n=fe(m.agentDir),o=n.length>0?`on (${n.length})`:"off";console.log(`[tg-agent] tools fetchMaxBytes=${m.fetchMaxBytes} fetchTimeoutMs=${m.fetchTimeoutMs} mcp=${o}`),console.log(`[tg-agent] proxy https=${e.https_proxy||"(empty)"} http=${e.http_proxy||"(empty)"} all=${e.all_proxy||"(empty)"} no=${e.no_proxy||"(empty)"}`),console.log(`[tg-agent] tls extra_ca=${t.NODE_EXTRA_CA_CERTS||"(empty)"} reject_unauthorized=${t.NODE_TLS_REJECT_UNAUTHORIZED||"(empty)"}`);try{const{source:a}=ie(m.modelProvider);console.log(`[tg-agent] authSource=${a}`)}catch(a){const r=a instanceof Error?a.message:String(a);console.warn(`[tg-agent] authSource=missing (${r})`)}const s=ae();console.log(s?`[tg-agent] proxyUrl=${s.url} kind=${s.kind} source=${s.source}`:"[tg-agent] proxyUrl=(none)")}Ve(),U(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}),F()}catch(e){const t=e instanceof Error?e.message:String(e);console.warn(`[tg-agent] mcp catalog warmup failed: ${t}`)}})();const D=ce();console.log(D?`[tg-agent] fetchProxy=${D.url} kind=${D.kind} source=${D.source}`:"[tg-agent] fetchProxy=(none)");function J(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 Y(e,t){let n=x(e);return n||(n=L(e,""),await w(e),await l(t,`Created session ${n.id}.`)),n}function Ke(e,t){const n=e.getAll(),o=new Map;for(const a of n){const r=o.get(a.provider)??{count:0,auth:t.hasAuth(a.provider)};r.count+=1,o.set(a.provider,r)}return o.size===0?"No providers found.":["Providers:",...Array.from(o.entries()).sort((a,r)=>a[0].localeCompare(r[0])).map(([a,r])=>`- /provider ${a} (models: ${r.count}, auth: ${r.auth?"ok":"missing"})`),"","Use /models <provider> to list models."].join(`
3
+ `)}function Ge(e,t,n){const o=e.getAll().filter(a=>a.provider===t);if(o.length===0)return`No models found for provider ${t}.`;const s=o.map(a=>`- /model ${t}/${a.id} | ${a.name}`);return[`Models for ${t} (auth: ${n?"ok":"missing"}):`,...s].join(`
4
4
  `)}function Je(e){const t=e.trim();if(!t)return null;if(t.includes("/")){const[n,o]=t.split("/",2);return!n||!o?null:{provider:A(n),modelId:o.trim()}}return{modelId:t}}const Ye=6e4,Z=new Map;function Ze(e,t){const n=Date.now(),o=Z.get(e)??0;n-o<Ye||(Z.set(e,n),console.warn(`[tg-agent] unauthorized user=${e} chat=${t}`))}function Qe(e){const t=m.telegramAllowedUsers;return!t||t.size===0?!0:t.has(e)}function Ie(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(`
5
5
  `)}async function Q(e,t,n){return n?await y.sendMessage(e,t,{parse_mode:n}):await y.sendMessage(e,t)}async function I(e,t,n,o){if(o){await y.editMessageText(n,{chat_id:e,message_id:t,parse_mode:o});return}await y.editMessageText(n,{chat_id:e,message_id:t})}async function ee(e,t){let n;for(const o of t)try{return await e(o)}catch(s){n=s;const a=s instanceof Error?s.message:String(s);console.warn(`[tg-agent] parse_mode ${o} failed, trying fallback: ${a}`)}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 et(e,t){return await ee(n=>Q(e,t,n),V(t))}async function tt(e,t,n){await ee(o=>I(e,t,n,o),V(n))}async function l(e,t){const n=ve(t,3900);for(const o of n)await et(e,o)}const nt=3900,ot=1200;function N(e,t=nt){return e.length<=t?{text:e,truncated:!1}:{text:`${e.slice(0,Math.max(0,t-15))}...
6
6
 
@@ -1 +1 @@
1
- import u from"node:fs/promises";import f from"node:path";import{config as i}from"./config.js";import{atomicWriteJson as h,ensureDir as l,nowMs as c,shortId as m}from"./utils.js";const a=new Map;function d(e){return f.join(i.sessionDir,`${e}.json`)}function p(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function v(e,s){const o=p(e),n=p(s);return f.join(i.sessionDir,`${o}-${n}.jsonl`)}async function j(e,s){const o=v(e,s);try{await u.unlink(o)}catch(n){if(n.code==="ENOENT")return;throw n}}function S(e,s){const o=s?.sessions??{},n=s?.activeSessionId??null,t={userId:e,activeSessionId:n,sessions:o};return(!n||!o[n])&&(t.activeSessionId=null),t}async function I(e){await l(i.sessionDir);const s=a.get(e);if(s)return s;const o=d(e);try{const n=await u.readFile(o,"utf8"),t=JSON.parse(n),r=S(e,t);return a.set(e,r),r}catch(n){if(n.code==="ENOENT"){const t=S(e,{});return a.set(e,t),t}throw n}}async function E(e){await l(i.sessionDir);const s=d(e.userId);await h(s,e),a.set(e.userId,e)}function O(e){return Object.values(e.sessions).sort((s,o)=>o.updatedAt-s.updatedAt)}function y(e,s=c()){const o=[];for(const[n,t]of Object.entries(e.sessions))s-t.updatedAt>i.sessionTtlMs&&(delete e.sessions[n],o.push(n));return e.activeSessionId&&!e.sessions[e.activeSessionId]&&(e.activeSessionId=null),o}function z(e,s){if(Object.keys(e.sessions).length>=i.maxSessions)throw new Error("Max sessions reached");const n=m(),t=c(),r={id:n,title:s&&s.length>0?s:`session-${n}`,messages:[],createdAt:t,updatedAt:t};return e.sessions[n]=r,e.activeSessionId=n,r}function D(e,s){return e.sessions[s]?(e.activeSessionId=s,!0):!1}function N(e){return e.activeSessionId?e.sessions[e.activeSessionId]??null:null}function P(e,s){return e.sessions[s]?(delete e.sessions[s],e.activeSessionId===s&&(e.activeSessionId=null),!0):!1}function F(e){e.messages=[],e.updatedAt=c()}function M(e,s,o){e.messages.push(s),e.messages.length>o&&(e.messages=e.messages.slice(-o)),e.updatedAt=c()}export{M as appendMessage,P as closeSession,z as createSession,j as deleteSessionFile,N as getActiveSession,O as listSessions,I as loadUserState,y as pruneExpiredSessions,F as resetSession,E as saveUserState,v as sessionFilePath,D as setActiveSession};
1
+ import u from"node:fs/promises";import f from"node:path";import{config as r}from"./config.js";import{atomicWriteJson as h,ensureDir as l,nowMs as a,shortId as m}from"./utils.js";const c=new Map;function p(e){return f.join(r.sessionDir,`${e}.json`)}function d(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function g(e,s){const t=d(e),n=d(s);return f.join(r.sessionDir,`${t}-${n}.jsonl`)}async function j(e,s){const t=g(e,s);try{await u.unlink(t)}catch(n){if(n.code==="ENOENT")return;throw n}}function S(e,s){const t=s?.sessions??{},n=s?.activeSessionId??null,o={userId:e,activeSessionId:n,sessions:t};return(!n||!t[n])&&(o.activeSessionId=null),o}async function E(e){await l(r.sessionDir);const s=c.get(e);if(s)return s;const t=p(e);try{const n=await u.readFile(t,"utf8"),o=JSON.parse(n),i=S(e,o);return c.set(e,i),i}catch(n){if(n.code==="ENOENT"){const o=S(e,{});return c.set(e,o),o}throw n}}async function y(e){await l(r.sessionDir);const s=p(e.userId);await h(s,e),c.set(e.userId,e)}function z(e){return Object.values(e.sessions).sort((s,t)=>t.updatedAt-s.updatedAt)}function D(e){return[]}function N(e,s){if(Object.keys(e.sessions).length>=r.maxSessions)throw new Error("Max sessions reached");const n=m(),o=a(),i={id:n,title:s&&s.length>0?s:`session-${n}`,messages:[],createdAt:o,updatedAt:o};return e.sessions[n]=i,e.activeSessionId=n,i}function O(e,s){return e.sessions[s]?(e.activeSessionId=s,!0):!1}function P(e){return e.activeSessionId?e.sessions[e.activeSessionId]??null:null}function F(e,s){return e.sessions[s]?(delete e.sessions[s],e.activeSessionId===s&&(e.activeSessionId=null),!0):!1}function I(e){e.messages=[],e.updatedAt=a()}function $(e,s,t){e.messages.push(s),e.messages.length>t&&(e.messages=e.messages.slice(-t)),e.updatedAt=a()}export{$ as appendMessage,F as closeSession,N as createSession,j as deleteSessionFile,P as getActiveSession,z as listSessions,E as loadUserState,D as pruneExpiredSessions,I as resetSession,y as saveUserState,g as sessionFilePath,O as setActiveSession};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tg-agent",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Telegram Codex agent",
5
5
  "type": "module",
6
6
  "bin": {