@tiendung-36/openrouter-cli 1.0.0 → 1.0.3

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/src/index.js CHANGED
@@ -1 +1,518 @@
1
- const a2_0x504d89=a2_0x3d23;function a2_0x1229(){const _0x4897b3=['tting\x20larg','index','Chọn\x20chế\x20đ','ask','EjtNo','stop','join','bPzcm','rYdgh','stringify','ọn,\x20đi\x20thẳ','hars).\x20Foc','\x20tại:\x20','m\x20install\x20','ormat),\x20hã','toISOStrin','ảnh\x20chat','g367/openr','dirname','ZbiSm','Conversati','ộ\x20hoạt\x20độn','rCSkD','2978394lEHXGz','Type\x20somet','settings','initialMod','autocomple','ssion\x20Usag','choices','system','ed\x20working','ccess\x20Requ','defaultMod','tricted\x20or','y:\x20','qgnNP','JLdfJ','Switch\x20to\x20','xecute\x20com','figured!\x20C','white','mand:\x20Chạy','\x20độ:\x20','\x20mode:','MoUME','role','text','com/tiendu','có\x20sẵn:\x0a-\x20','choice','e,\x20summari','npEZI','[CONTEXT\x20S','Unknown\x20co','Theme\x20hiện','wait','\x20(Run\x20as\x20B','PVVEf','/exit\x20\x20\x20\x20-','lmLsa','người\x20dùng','Switched\x20t','c\x20chắn\x20cod','lavWN','t\x20trợ\x20lý\x20l','nRouter\x20CL','DHxka','Ready!','showHelp','ash\x20comman','FnLRh','1.\x20Luôn\x20dù','content','length','\x20CỐT\x20LÕI:\x0a','Failed\x20to\x20','startsWith','uting\x20tool','teFile:\x20Gh','diện\x20CLI','ercontent.','function','\x20file\x20trướ','iều\x20file.\x0a','theme','/model','\x20Chế\x20độ\x20ho','hệ\x20thống\x20f','KQCrr','qJwiO','write','i.\x0a2.\x20Trả\x20','uất\x20sửa\x20đổ','daCLg','name','listFiles:','ultipleFil','ng\x20vào\x20vấn','mands\x20auto','History\x20cl','\x20⚠️\x20\x20Access','ác\x20công\x20cụ','create','ng\x20minh.\x0a','Connecting','c.\x0a4.\x20Nếu\x20','ID\x20require','oXztj','CjQuR','MpsMB','oFqUj','hange\x20anyt','nURjp','requestCou','utf-8','Guest','5jnRZdw','save','readFileSy','oặc\x20\x27readF','ey\x20decisio','customId','guy\x20hiểm)','NpalY','sk\x20(Always','rsHzR','warn','ản\x20Tiendun','(max\x20500\x20c','/cost\x20\x20\x20\x20-','ry\x20','arguments','oOStE','urn:\x20','r\x20model.','definition','slice','/clear','✅\x20Mode\x20con','map','Summary\x20cr','ed.\x20Reques','get','ile\x27\x20để\x20đọ','vfXKZ','y\x20lệnh\x20-\x20N','ARmKV','i\x20viết\x20cod','eated,\x20con','3|5|4|0|2|','forEach','\x20not\x20found','cMlQQ','SZoyH','yellow','\x20Đổi\x20giao\x20','https://ra','\x20Đổi\x20model','/settings-','unknown','\x20quyền\x20tru','\x20STATUS:\x20','xNAYd','\x0a\x20\x200.\x20Hủy\x0a','CPNmW','mbZYy','EzYte','27ASDACu','bWHmI','Model:\x20','YSnir','\x20(Update/A','bold','y\x20sử\x20dụng\x20','string','sử\x20chat','eared.','chat','les:\x20Tìm\x20f','ước\x20khi\x20ch','I\x20(phiên\x20b','.\x0a\x0aQUY\x20TẮC','\x0a┌─\x20Test\x20R','keys','Dtcbj','t\x20cách\x20thô','CLI\x20Error:','Xin\x20chào','prompt','\x20Choose\x20yo','4744670QzCOXu','succeed','get\x20new\x20ke','/exit','model','/settings','user','c\x20nội\x20dung','ng367/open','YPrKX','stdout','exit','n\x20config:\x20','ạy\x20lệnh)','ng\x20\x27cat\x27\x20h','tool','hí\x20&\x20token','gray','mode','qdQBC','VbHey','AubzT','10ErBPPp','k\x20failed:\x20','value','ile\x20của\x20ng','writeFileS','mYvcf','Goodbye!','message','ion\x20failed','createChat','homedir','esponse\x20──','UNKcT','You\x20>','/history','iUyyu','Current\x20Se','\x20messages.','e,\x20hãy\x20chắ','cyan','showBanner','toolOutput','trim','.\x20Falling\x20','281943muajaG','3430518QcMfft','&\x20phím\x20tắt','/model\x20\x20\x20-','Pjabm','uto-Run\x20(E','ry:\x20','.json','iểm\x20(rm,\x20f','\x0aBạn\x20là\x20mộ','el\x20ID\x20(or\x20','tool_calls','bELXx','/cost','on:\x0a','Testing\x20mo','-status/ma','công\x20cụ\x20mộ','filter','Context\x20ge','red','sffaC','fDach','completion','green','uto-Run\x20(T','y\x20từ\x20chối.','hrslu','👋\x20Welcome!','ect\x20anothe','parse','afIGT','boKrd','vSmUY','zcpLV','an\x20update.','OEiNp','/help\x20\x20\x20\x20-','assistant','ietnamese\x20','replace','exjSs','error','r\x20to\x20retur','ime\x20with\x20/','\x20lệnh.\x0a\x0aHã','History\x20sa','Enter\x20Open','Error:\x20Too','text\x20optim','mmand:\x20','ile.\x0a-\x20wri','txt?t=','\x20Xem\x20chi\x20p','clear','\x0a🎨\x20Preview','11800811EERKnj','split','└─────────','rently\x20res','/help','existsSync','Chọn\x20theme','I\x20cao\x20cấp\x20','\x20AI','username','iefly\x20in\x20V','fail','DQzup','es:\x20Đọc\x20nh','\x0a-\x20readFil','executeCom','start','push','ized.','this\x20conve','ol:\x20','IaXiE','2374314JaOWWs','ghqWR','...','ười\x20dùng\x20t','reinitiali','Error\x20in\x20t','\x0a🎨\x20Đổi\x20the','resolve','Please\x20sel','────','true','RpeIe','us\x20on:\x20wha','confirm','sách\x20lệnh\x20','Completion','me\x0a','log','always','in/status.','dZTyV','summarize.','registerPr','version','uQvtG','n...','tGOis','3689936PmtlRz','w.githubus','toLowerCas','/theme\x20\x20\x20-','info','4tFaPjg','spinner','uncation.','Model\x20chec','ired)\x20','───\x0a','\x0a\x20🚫\x20SYSTEM','ting\x20new\x20k','/theme','dark','hing!','/history\x20-','──────────','New\x20API\x20ke','Summarize\x20','Tfekj','commands)'];a2_0x1229=function(){return _0x4897b3;};return a2_0x1229();}(function(_0x5cc3f7,_0x70d4e7){const _0x48a9f0=a2_0x3d23,_0x4a91e7=_0x5cc3f7();while(!![]){try{const _0x318e5a=-parseInt(_0x48a9f0(0x197))/0x1*(parseInt(_0x48a9f0(0x17f))/0x2)+parseInt(_0x48a9f0(0x1e5))/0x3*(-parseInt(_0x48a9f0(0x205))/0x4)+parseInt(_0x48a9f0(0x28b))/0x5*(parseInt(_0x48a9f0(0x22d))/0x6)+-parseInt(_0x48a9f0(0x198))/0x7+parseInt(_0x48a9f0(0x200))/0x8*(parseInt(_0x48a9f0(0x2be))/0x9)+parseInt(_0x48a9f0(0x169))/0xa+parseInt(_0x48a9f0(0x1cf))/0xb;if(_0x318e5a===_0x70d4e7)break;else _0x4a91e7['push'](_0x4a91e7['shift']());}catch(_0x396b82){_0x4a91e7['push'](_0x4a91e7['shift']());}}}(a2_0x1229,0xb3ba6));import a2_0x104eb8 from'inquirer';import a2_0x30bd6b from'inquirer-autocomplete-prompt';import a2_0x48b7f3 from'chalk';import a2_0x1305a9 from'path';import a2_0x2497cc from'fs';import a2_0x34af6f from'os';import{fileURLToPath}from'url';import{OpenRouterAPI}from'./api.js';import{tools}from'./tools.js';import{ui,themes,setTheme,getThemeName,previewTheme,getTheme}from'./ui.js';import{config,loadConfig,saveConfig}from'./config.js';import{truncateContext}from'./utils.js';import{preferences}from'./preferences.js';import{shouldRetryWithNewKey}from'./config.js';const __dirname=a2_0x1305a9[a2_0x504d89(0x228)](fileURLToPath(import.meta.url)),pkg=JSON[a2_0x504d89(0x1b5)](a2_0x2497cc[a2_0x504d89(0x28d)+'nc'](a2_0x1305a9[a2_0x504d89(0x1ec)](__dirname,'../package'+a2_0x504d89(0x19e)),a2_0x504d89(0x289)));function a2_0x3d23(_0x3158f2,_0x117a2c){_0x3158f2=_0x3158f2-0x165;const _0x122927=a2_0x1229();let _0x3d23f7=_0x122927[_0x3158f2];return _0x3d23f7;}a2_0x104eb8[a2_0x504d89(0x1fb)+'ompt'](a2_0x504d89(0x231)+'te',a2_0x30bd6b);const commands=[{'name':a2_0x504d89(0x1bc)+'\x20Xem\x20danh\x20'+a2_0x504d89(0x1f3)+a2_0x504d89(0x199),'value':'/help','short':a2_0x504d89(0x1d3)},{'name':a2_0x504d89(0x19a)+a2_0x504d89(0x2b4)+a2_0x504d89(0x1d7),'value':a2_0x504d89(0x26c),'short':a2_0x504d89(0x26c)},{'name':a2_0x504d89(0x203)+a2_0x504d89(0x2b2)+a2_0x504d89(0x266),'value':a2_0x504d89(0x20d),'short':'/theme'},{'name':a2_0x504d89(0x298)+a2_0x504d89(0x1cc)+a2_0x504d89(0x179)+'s','value':a2_0x504d89(0x1a4),'short':a2_0x504d89(0x1a4)},{'name':a2_0x504d89(0x210)+'\x20Lưu\x20lịch\x20'+a2_0x504d89(0x2c6),'value':'/history','short':'/history'},{'name':a2_0x504d89(0x2b5)+a2_0x504d89(0x26d)+'ạt\x20động','value':'/settings','short':a2_0x504d89(0x16e)},{'name':'/clear\x20\x20\x20-'+'\x20Xóa\x20ngữ\x20c'+a2_0x504d89(0x226),'value':'/clear','short':'/clear'},{'name':a2_0x504d89(0x251)+'\x20Thoát\x20CLI','value':a2_0x504d89(0x16c),'short':a2_0x504d89(0x16c)}];async function searchCommands(_0x1f5a32,_0x1164a5=''){const _0x4a17dd=a2_0x504d89;if(!_0x1164a5)return[];if(_0x1164a5[_0x4a17dd(0x263)]('/'))return commands[_0x4a17dd(0x1a9)](_0x4252f8=>_0x4252f8[_0x4a17dd(0x181)][_0x4a17dd(0x263)](_0x1164a5));if(_0x1164a5['startsWith']('!'))return[{'name':_0x1164a5+(_0x4a17dd(0x24f)+_0x4a17dd(0x25c)+'d)'),'value':_0x1164a5,'short':_0x1164a5}];return[];}const prefs=preferences['load']();let currentModel=null,history=[];prefs[a2_0x504d89(0x26b)]&&themes[prefs[a2_0x504d89(0x26b)]]&&setTheme(prefs[a2_0x504d89(0x26b)]);const SYSTEM_PROMPT=a2_0x504d89(0x1a0)+a2_0x504d89(0x257)+'ập\x20trình\x20A'+a2_0x504d89(0x1d6)+'tên\x20là\x20Ope'+a2_0x504d89(0x258)+a2_0x504d89(0x2cb)+a2_0x504d89(0x296)+'g).\x0aBạn\x20có'+a2_0x504d89(0x2b7)+'y\x20cập\x20vào\x20'+a2_0x504d89(0x26e)+a2_0x504d89(0x182)+a2_0x504d89(0x1e8)+'hông\x20qua\x20c'+a2_0x504d89(0x27c)+a2_0x504d89(0x2cc)+a2_0x504d89(0x261)+a2_0x504d89(0x25e)+a2_0x504d89(0x177)+a2_0x504d89(0x28e)+a2_0x504d89(0x2a6)+a2_0x504d89(0x170)+a2_0x504d89(0x269)+'c\x20khi\x20đề\x20x'+a2_0x504d89(0x273)+a2_0x504d89(0x272)+'lời\x20ngắn\x20g'+a2_0x504d89(0x220)+a2_0x504d89(0x278)+'\x20đề.\x0a3.\x20Kh'+a2_0x504d89(0x2aa)+a2_0x504d89(0x191)+a2_0x504d89(0x255)+'e\x20chạy\x20đượ'+a2_0x504d89(0x280)+a2_0x504d89(0x253)+'\x20yêu\x20cầu\x20l'+'ệnh\x20nguy\x20h'+a2_0x504d89(0x19f)+a2_0x504d89(0x224)+a2_0x504d89(0x1b1)+'\x0a\x0aCông\x20cụ\x20'+a2_0x504d89(0x247)+a2_0x504d89(0x276)+'\x20Xem\x20danh\x20'+'sách\x20file.'+a2_0x504d89(0x1dd)+'e:\x20Đọc\x20fil'+'e.\x0a-\x20readM'+a2_0x504d89(0x277)+a2_0x504d89(0x1dc)+a2_0x504d89(0x26a)+'-\x20searchFi'+a2_0x504d89(0x2c9)+a2_0x504d89(0x1ca)+a2_0x504d89(0x265)+'i\x20file.\x0a-\x20'+a2_0x504d89(0x1de)+a2_0x504d89(0x240)+a2_0x504d89(0x1c4)+a2_0x504d89(0x2c4)+a2_0x504d89(0x1a8)+a2_0x504d89(0x2d0)+a2_0x504d89(0x27e);history['push']({'role':a2_0x504d89(0x234),'content':SYSTEM_PROMPT});let api=null;async function summarizeOldMessages(_0x18a164,_0x2fd629){const _0x12c6fd=a2_0x504d89,_0x54473c={'mYvcf':function(_0x5218a5,_0x3e270c){return _0x5218a5+_0x3e270c;},'EYyEw':function(_0x4deb5b,_0x4f5207){return _0x4deb5b<_0x4f5207;},'KQCrr':'user','boKrd':'Unable\x20to\x20'+_0x12c6fd(0x1fa),'DHxka':_0x12c6fd(0x1bd),'rYdgh':_0x12c6fd(0x2a3)+_0x12c6fd(0x2ab)+_0x12c6fd(0x1c8)+_0x12c6fd(0x1e1)},_0x318c37=_0x18a164['find'](_0xe36c21=>_0xe36c21[_0x12c6fd(0x244)]===_0x12c6fd(0x234)),_0x1c43cc=0x6;if(_0x18a164[_0x12c6fd(0x260)]<=_0x54473c[_0x12c6fd(0x184)](_0x1c43cc,0x2))return _0x18a164;const _0x1ed44d=_0x18a164[_0x12c6fd(0x1a9)](_0x1f9391=>_0x1f9391[_0x12c6fd(0x244)]!==_0x12c6fd(0x234))['slice'](0x0,-_0x1c43cc),_0x31ea0f=_0x18a164[_0x12c6fd(0x1a9)](_0x494c48=>_0x494c48[_0x12c6fd(0x244)]!=='system')[_0x12c6fd(0x29f)](-_0x1c43cc);if(_0x54473c['EYyEw'](_0x1ed44d[_0x12c6fd(0x260)],0x4))return _0x18a164;ui[_0x12c6fd(0x204)](_0x12c6fd(0x1aa)+_0x12c6fd(0x216)+_0x12c6fd(0x249)+'zing\x20older'+_0x12c6fd(0x190)+'..');try{const _0x2bb12f=_0x12c6fd(0x213)+_0x12c6fd(0x1e2)+'rsation\x20br'+_0x12c6fd(0x1d9)+_0x12c6fd(0x1be)+_0x12c6fd(0x297)+_0x12c6fd(0x221)+_0x12c6fd(0x1f1)+'t\x20user\x20ask'+'ed,\x20what\x20w'+'as\x20done,\x20k'+_0x12c6fd(0x28f)+'ns\x20made.\x0a\x0a'+_0x12c6fd(0x22a)+_0x12c6fd(0x1a5)+_0x1ed44d[_0x12c6fd(0x2a2)](_0x3e5a56=>_0x3e5a56[_0x12c6fd(0x244)]+':\x20'+(typeof _0x3e5a56[_0x12c6fd(0x25f)]===_0x12c6fd(0x2c5)?_0x3e5a56[_0x12c6fd(0x25f)]?.['slice'](0x0,0x1f4):'[tool\x20data'+']'))[_0x12c6fd(0x21c)]('\x0a'),_0x5e45d2=await api['client'][_0x12c6fd(0x2c8)][_0x12c6fd(0x1ae)+'s'][_0x12c6fd(0x27d)]({'model':_0x2fd629,'messages':[{'role':_0x54473c[_0x12c6fd(0x26f)],'content':_0x2bb12f}],'max_tokens':0x12c}),_0x2c2be1=_0x5e45d2[_0x12c6fd(0x233)][0x0]?.[_0x12c6fd(0x186)]?.[_0x12c6fd(0x25f)]||_0x54473c[_0x12c6fd(0x1b7)],_0x5e6652={'role':_0x54473c[_0x12c6fd(0x259)],'content':_0x12c6fd(0x24b)+'UMMARY\x20-\x20O'+'lder\x20conve'+'rsation]\x0a'+_0x2c2be1};return ui[_0x12c6fd(0x204)](_0x54473c[_0x12c6fd(0x21e)]),_0x318c37?[_0x318c37,_0x5e6652,..._0x31ea0f]:[_0x5e6652,..._0x31ea0f];}catch(_0x3dba51){return ui['warn']('Summarizat'+_0x12c6fd(0x187)+':\x20'+_0x3dba51[_0x12c6fd(0x186)]+(_0x12c6fd(0x196)+'back\x20to\x20tr'+_0x12c6fd(0x207))),_0x18a164;}}async function handleToolCalls(_0x532e74){const _0x38b765=a2_0x504d89,_0x9fc074={'Pjabm':_0x38b765(0x178)},_0x1497f6=[];for(const _0x1a3317 of _0x532e74){const _0x17b1c5=_0x1a3317[_0x38b765(0x268)]['name'],_0x202087=JSON[_0x38b765(0x1b5)](_0x1a3317[_0x38b765(0x268)][_0x38b765(0x29a)]);ui[_0x38b765(0x204)]('Calling\x20to'+_0x38b765(0x1e3)+a2_0x48b7f3[_0x38b765(0x2c3)](_0x17b1c5));let _0x450aee;if(tools[_0x17b1c5])try{_0x450aee=await tools[_0x17b1c5](_0x202087);}catch(_0x2a32c6){_0x450aee='Error\x20exec'+_0x38b765(0x264)+'\x20'+_0x17b1c5+':\x20'+_0x2a32c6[_0x38b765(0x186)];}else _0x450aee=_0x38b765(0x1c7)+'l\x20'+_0x17b1c5+(_0x38b765(0x2ae)+'.');ui[_0x38b765(0x194)](JSON[_0x38b765(0x21f)](_0x450aee)),_0x1497f6[_0x38b765(0x1e0)]({'tool_call_id':_0x1a3317['id'],'role':_0x9fc074[_0x38b765(0x19b)],'name':_0x17b1c5,'content':_0x450aee});}return _0x1497f6;}async function processTurn(){const _0x4fb4d3=a2_0x504d89,_0xc5685f={'DUWQe':'Thinking..'+'.','sffaC':function(_0xe8f387){return _0xe8f387();},'DgPnS':_0x4fb4d3(0x268),'bPzcm':function(_0x11c2f9,_0x3a45b8){return _0x11c2f9>_0x3a45b8;},'UNKcT':function(_0x45af85,_0x2e9c2c){return _0x45af85(_0x2e9c2c);},'GODgJ':function(_0x1bd73d,_0x219e48){return _0x1bd73d<_0x219e48;},'JLdfJ':_0x4fb4d3(0x212)+'y\x20obtained'+'.\x20Retrying'+'...'};let _0x4523b7=![],_0x26efc9=0x0;const _0xe4a480=0x3;while(!_0x4523b7){const _0x181c75=ui[_0x4fb4d3(0x206)](_0xc5685f['DUWQe'])[_0x4fb4d3(0x1df)]();let _0x445786='',_0x1ef3de=[];try{const _0x1c880e=await api['chatStream'](history,currentModel,tools[_0x4fb4d3(0x29e)+'s']);_0x181c75['stop']();for await(const _0x48b4c2 of _0x1c880e){const _0x49e012=_0x48b4c2[_0x4fb4d3(0x233)][0x0]?.['delta'];_0x49e012?.[_0x4fb4d3(0x25f)]&&(process[_0x4fb4d3(0x173)]['write'](_0xc5685f[_0x4fb4d3(0x1ac)](getTheme)['ai'](_0x49e012['content'])),_0x445786+=_0x49e012[_0x4fb4d3(0x25f)]);if(_0x49e012?.[_0x4fb4d3(0x1a2)])for(const _0x440039 of _0x49e012[_0x4fb4d3(0x1a2)]){if(!_0x1ef3de[_0x440039['index']])_0x1ef3de[_0x440039[_0x4fb4d3(0x217)]]={'id':_0x440039['id']||'','function':{'name':_0x440039['function']?.[_0x4fb4d3(0x275)]||'','arguments':_0x440039[_0x4fb4d3(0x268)]?.[_0x4fb4d3(0x29a)]||''},'type':_0xc5685f['DgPnS']};else{if(_0x440039['id'])_0x1ef3de[_0x440039['index']]['id']+=_0x440039['id'];if(_0x440039[_0x4fb4d3(0x268)]?.[_0x4fb4d3(0x275)])_0x1ef3de[_0x440039['index']][_0x4fb4d3(0x268)][_0x4fb4d3(0x275)]+=_0x440039[_0x4fb4d3(0x268)]['name'];if(_0x440039[_0x4fb4d3(0x268)]?.[_0x4fb4d3(0x29a)])_0x1ef3de[_0x440039[_0x4fb4d3(0x217)]][_0x4fb4d3(0x268)][_0x4fb4d3(0x29a)]+=_0x440039['function'][_0x4fb4d3(0x29a)];}}}process['stdout'][_0x4fb4d3(0x271)]('\x0a');const _0x59b881={'role':_0x4fb4d3(0x1bd),'content':_0x445786||null};if(_0xc5685f[_0x4fb4d3(0x21d)](_0x1ef3de[_0x4fb4d3(0x260)],0x0)){_0x59b881[_0x4fb4d3(0x1a2)]=_0x1ef3de,history[_0x4fb4d3(0x1e0)](_0x59b881);const _0x18ce22=await _0xc5685f[_0x4fb4d3(0x18b)](handleToolCalls,_0x1ef3de);history[_0x4fb4d3(0x1e0)](..._0x18ce22);}else{history['push'](_0x59b881),_0x4523b7=!![];const _0x685f6f=_0xc5685f[_0x4fb4d3(0x1ac)](loadConfig)||{},_0xd0f6b5=(_0x685f6f['requestCou'+'nt']||0x0)+0x1;saveConfig({'requestCount':_0xd0f6b5});}_0x26efc9=0x0;}catch(_0x8baf5a){_0x181c75[_0x4fb4d3(0x21b)]();if(shouldRetryWithNewKey(_0x8baf5a)&&_0xc5685f['GODgJ'](_0x26efc9,_0xe4a480)){_0x26efc9++,ui[_0x4fb4d3(0x295)]('API\x20Key\x20er'+'ror\x20detect'+_0x4fb4d3(0x2a4)+_0x4fb4d3(0x20c)+'ey...\x20(Ret'+_0x4fb4d3(0x299)+_0x26efc9+'/'+_0xe4a480+')');try{const _0x29817b=_0x8baf5a[_0x4fb4d3(0x186)]['match'](/\d{3}/)?.[0x0]||_0x4fb4d3(0x2b6);await api[_0x4fb4d3(0x1e9)+'zeWithNewK'+'ey'](_0x29817b),ui[_0x4fb4d3(0x204)](_0xc5685f[_0x4fb4d3(0x23b)]);continue;}catch(_0x3c4860){ui[_0x4fb4d3(0x1c1)]('Failed\x20to\x20'+_0x4fb4d3(0x16b)+_0x4fb4d3(0x239)+_0x3c4860[_0x4fb4d3(0x186)]),_0x4523b7=!![];}}else ui[_0x4fb4d3(0x1c1)](_0x4fb4d3(0x1ea)+_0x4fb4d3(0x29c)+_0x8baf5a['message']),_0x4523b7=!![];}}}async function checkUpdate(){const _0x3c3663=a2_0x504d89,_0xa9467b={'bWHmI':function(_0x1ba89e,_0x265da0){return _0x1ba89e!==_0x265da0;},'SZoyH':_0x3c3663(0x1ef),'Dhuvd':_0x3c3663(0x2ac)+'1'};try{const _0x463203=await fetch(_0x3c3663(0x2b3)+_0x3c3663(0x201)+_0x3c3663(0x267)+_0x3c3663(0x246)+_0x3c3663(0x171)+'router-cli'+_0x3c3663(0x1a7)+_0x3c3663(0x1f8)+_0x3c3663(0x1cb)+Date['now']());if(_0x463203['ok']){const _0x40ae03=(await _0x463203[_0x3c3663(0x245)]())[_0x3c3663(0x195)]();if(_0xa9467b[_0x3c3663(0x2bf)](_0x40ae03[_0x3c3663(0x202)+'e'](),_0xa9467b[_0x3c3663(0x2b0)])){const _0xd11b57=_0xa9467b['Dhuvd'][_0x3c3663(0x1d0)]('|');let _0x3d6b86=0x0;while(!![]){switch(_0xd11b57[_0x3d6b86++]){case'0':console[_0x3c3663(0x1f6)](a2_0x48b7f3[_0x3c3663(0x1af)][_0x3c3663(0x2c3)]('\x20👉\x20Run:\x20np'+_0x3c3663(0x223)+'-g\x20tiendun'+_0x3c3663(0x227)+'outer-cli\x20'+'\x0a'));continue;case'1':process['exit'](0x1);continue;case'2':await new Promise(_0x4fdba9=>setTimeout(_0x4fdba9,0xc8));continue;case'3':console[_0x3c3663(0x1cd)]();continue;case'4':console[_0x3c3663(0x1f6)](a2_0x48b7f3[_0x3c3663(0x2b1)](_0x3c3663(0x27b)+'\x20to\x20this\x20t'+'ool\x20is\x20cur'+_0x3c3663(0x1d2)+_0x3c3663(0x238)+'\x20requires\x20'+_0x3c3663(0x1ba)));continue;case'5':console['log'](a2_0x48b7f3[_0x3c3663(0x1ab)][_0x3c3663(0x2c3)](_0x3c3663(0x20b)+_0x3c3663(0x2b8)+_0x40ae03+(_0x3c3663(0x2c2)+_0x3c3663(0x236)+_0x3c3663(0x209))));continue;}break;}}}}catch(_0x559ca7){}}export async function runCLI(){const _0x171fd5=a2_0x504d89,_0x3a8502={'zcpLV':function(_0x4ccc08){return _0x4ccc08();},'rCSkD':function(_0x3977e3,_0x79e84c){return _0x3977e3===_0x79e84c;},'QFCsf':function(_0x196ba3,_0x5e60a9){return _0x196ba3+_0x5e60a9;},'qJwiO':function(_0x235a89,_0x522fe1){return _0x235a89(_0x522fe1);},'mbZYy':_0x171fd5(0x20e),'Tfekj':_0x171fd5(0x28a),'RpeIe':'list','CPNmW':_0x171fd5(0x230)+'e','fDach':_0x171fd5(0x219),'LTWaR':_0x171fd5(0x1f7),'lmLsa':_0x171fd5(0x27f)+'...','xKVKC':_0x171fd5(0x25a),'CjQuR':'input','dZTyV':'userInput','Dtcbj':_0x171fd5(0x18c),'afIGT':_0x171fd5(0x16c),'EzYte':_0x171fd5(0x185),'ghqWR':'/help','NpalY':'wait','PVVEf':'Press\x20Ente'+_0x171fd5(0x1c2)+_0x171fd5(0x1fe),'qdQBC':function(_0x21ab51,_0x25d205){return _0x21ab51===_0x25d205;},'MpsMB':_0x171fd5(0x1a4),'rsHzR':_0x171fd5(0x2a0),'oZyLx':_0x171fd5(0x234),'MoUME':_0x171fd5(0x27a)+_0x171fd5(0x2c7),'vSmUY':function(_0x410693,_0x1864a2){return _0x410693===_0x1864a2;},'qgnNP':_0x171fd5(0x290),'exjSs':_0x171fd5(0x1c6)+'Router\x20Mod'+_0x171fd5(0x1a1)+'\x22cancel\x22):','VbHey':'cancel','bELXx':_0x171fd5(0x16f),'GzwUw':_0x171fd5(0x166),'SiHHh':_0x171fd5(0x2cd)+_0x171fd5(0x18a)+_0x171fd5(0x211)+_0x171fd5(0x1ee),'ZbiSm':function(_0x4c6be7,_0x8a3482){return _0x4c6be7+_0x8a3482;},'DQzup':_0x171fd5(0x1d1)+_0x171fd5(0x211)+_0x171fd5(0x211)+_0x171fd5(0x20a),'YPrKX':_0x171fd5(0x1f2),'vfXKZ':_0x171fd5(0x1ed)+_0x171fd5(0x1b4)+_0x171fd5(0x29d),'uQvtG':_0x171fd5(0x20d),'daCLg':_0x171fd5(0x1eb)+_0x171fd5(0x1f5),'iUyyu':_0x171fd5(0x2ba),'YSnir':'Nhập\x20số\x20để'+'\x20preview:','oFqUj':function(_0x484555,_0x22c99e){return _0x484555(_0x22c99e);},'lavWN':function(_0x4986e2,_0x37e9a0){return _0x4986e2===_0x37e9a0;},'cMlQQ':function(_0x80f606,_0x2561a8){return _0x80f606>=_0x2561a8;},'ARmKV':function(_0x457a4f,_0x3742ce){return _0x457a4f<=_0x3742ce;},'oOStE':function(_0xf14c75,_0x48d15f){return _0xf14c75-_0x48d15f;},'qYSVq':function(_0xd76ef7,_0x512696){return _0xd76ef7(_0x512696);},'oXztj':function(_0x485887){return _0x485887();},'hrslu':_0x171fd5(0x16e),'OEiNp':_0x171fd5(0x17b),'IaXiE':_0x171fd5(0x218)+_0x171fd5(0x22b)+'g:','WeaMO':'💬\x20Chat\x20/\x20A'+'sk\x20(Hỏi\x20tr'+_0x171fd5(0x2ca)+_0x171fd5(0x176),'npEZI':'⚡\x20Code\x20/\x20A'+_0x171fd5(0x1b0)+'ự\x20động\x20chạ'+_0x171fd5(0x2a8)+_0x171fd5(0x291),'FnLRh':_0x171fd5(0x18d),'nURjp':'Documents','tGOis':'openrouter','EjtNo':'utf-8','AubzT':function(_0x460792,_0x99b43f){return _0x460792>_0x99b43f;},'xNAYd':function(_0x25c9c5,_0x40f64f,_0x358f8f){return _0x25c9c5(_0x40f64f,_0x358f8f);}};!prefs['theme']&&(_0x3a8502[_0x171fd5(0x270)](setTheme,_0x3a8502[_0x171fd5(0x2bc)]),preferences[_0x171fd5(0x28c)]({'theme':_0x3a8502[_0x171fd5(0x2bc)]}));const _0x21c215=_0x3a8502[_0x171fd5(0x1b9)](loadConfig),_0x2faeb2=_0x21c215?.[_0x171fd5(0x1d8)]||_0x3a8502[_0x171fd5(0x214)];let _0x3e939c=_0x21c215?.[_0x171fd5(0x288)+'nt']||0x0;if(!preferences[_0x171fd5(0x2a5)](_0x171fd5(0x17b))){console[_0x171fd5(0x1cd)](),ui['showBanner'](pkg['version'],_0x2faeb2,_0x3e939c);const {initialMode:_0x3631b6}=await a2_0x104eb8[_0x171fd5(0x167)]([{'type':_0x3a8502[_0x171fd5(0x1f0)],'name':_0x3a8502[_0x171fd5(0x2bb)],'message':a2_0x48b7f3[_0x171fd5(0x192)](_0x171fd5(0x1b3)+_0x171fd5(0x168)+'ur\x20preferr'+_0x171fd5(0x235)+_0x171fd5(0x242)),'choices':[{'name':'💬\x20Chat\x20/\x20A'+_0x171fd5(0x293)+'\x20ask\x20befor'+'e\x20running\x20'+_0x171fd5(0x215),'value':_0x3a8502[_0x171fd5(0x1ad)]},{'name':'⚡\x20Code\x20/\x20A'+_0x171fd5(0x19c)+_0x171fd5(0x23d)+_0x171fd5(0x279)+'matically)','value':_0x3a8502['LTWaR']}]}]);preferences[_0x171fd5(0x28c)]({'mode':_0x3631b6}),ui[_0x171fd5(0x204)](_0x171fd5(0x2a1)+_0x171fd5(0x23e)+_0x171fd5(0x286)+_0x171fd5(0x1c3)+_0x171fd5(0x22f)),await new Promise(_0x3d1119=>setTimeout(_0x3d1119,0x3e8));}ui[_0x171fd5(0x193)](pkg[_0x171fd5(0x1fc)],_0x2faeb2,_0x3e939c);const _0x18b667=ui[_0x171fd5(0x206)](_0x3a8502[_0x171fd5(0x252)])[_0x171fd5(0x1df)]();try{api=await OpenRouterAPI[_0x171fd5(0x27d)](),_0x18b667[_0x171fd5(0x16a)](_0x3a8502['xKVKC']);}catch(_0x1b14dc){_0x18b667['fail']('Failed:\x20'+_0x1b14dc[_0x171fd5(0x186)]),process[_0x171fd5(0x174)](0x1);}currentModel=prefs[_0x171fd5(0x16d)]||config[_0x171fd5(0x237)+'el'],ui[_0x171fd5(0x204)](_0x171fd5(0x2c0)+a2_0x48b7f3[_0x171fd5(0x2c3)](currentModel));const _0x39f3b2=()=>{const _0x411e73=_0x171fd5,_0x27efa6=_0x3a8502[_0x411e73(0x1b9)](loadConfig);console[_0x411e73(0x1cd)](),ui['showBanner'](pkg[_0x411e73(0x1fc)],_0x2faeb2,_0x27efa6?.[_0x411e73(0x288)+'nt']||0x0),ui[_0x411e73(0x204)]('Model:\x20'+a2_0x48b7f3[_0x411e73(0x2c3)](currentModel));};while(!![]){try{const {userInput:_0x31ba48}=await a2_0x104eb8[_0x171fd5(0x167)]([{'type':_0x3a8502[_0x171fd5(0x283)],'name':_0x3a8502[_0x171fd5(0x1f9)],'message':a2_0x48b7f3['green'](_0x3a8502[_0x171fd5(0x2cf)]),'validate':_0x22b6d8=>_0x22b6d8?!![]:_0x171fd5(0x22e)+_0x171fd5(0x20f)}]);if(!_0x31ba48)continue;if(_0x31ba48['startsWith']('/')){const [_0x6a380b,..._0x5c3b73]=_0x31ba48['split']('\x20');_0x6a380b===_0x3a8502[_0x171fd5(0x1b6)]&&(ui['ai'](_0x3a8502[_0x171fd5(0x2bd)]),process[_0x171fd5(0x174)](0x0));if(_0x6a380b===_0x3a8502[_0x171fd5(0x1e6)]){ui[_0x171fd5(0x25b)](),await a2_0x104eb8[_0x171fd5(0x167)]([{'type':_0x3a8502['CjQuR'],'name':_0x3a8502[_0x171fd5(0x292)],'message':a2_0x48b7f3[_0x171fd5(0x17a)](_0x3a8502[_0x171fd5(0x250)])}]),_0x3a8502[_0x171fd5(0x1b9)](_0x39f3b2);continue;}if(_0x3a8502[_0x171fd5(0x17c)](_0x6a380b,_0x3a8502[_0x171fd5(0x284)])){const _0x629975=loadConfig();ui[_0x171fd5(0x204)](_0x171fd5(0x18f)+_0x171fd5(0x232)+'e:'),ui[_0x171fd5(0x204)]('Requests\x20i'+_0x171fd5(0x175)+(_0x629975?.['requestCou'+'nt']||0x0)),await a2_0x104eb8[_0x171fd5(0x167)]([{'type':_0x3a8502[_0x171fd5(0x283)],'name':_0x171fd5(0x24e),'message':a2_0x48b7f3[_0x171fd5(0x17a)](_0x3a8502['PVVEf'])}]),_0x3a8502[_0x171fd5(0x1b9)](_0x39f3b2);continue;}if(_0x3a8502[_0x171fd5(0x17c)](_0x6a380b,_0x3a8502[_0x171fd5(0x294)])){history=[{'role':_0x3a8502['oZyLx'],'content':SYSTEM_PROMPT}],_0x3a8502[_0x171fd5(0x1b9)](_0x39f3b2),ui[_0x171fd5(0x204)](_0x3a8502[_0x171fd5(0x243)]);continue;}if(_0x3a8502[_0x171fd5(0x1b8)](_0x6a380b,_0x171fd5(0x26c))){while(!![]){const {customId:_0x455954}=await a2_0x104eb8[_0x171fd5(0x167)]([{'type':_0x3a8502[_0x171fd5(0x283)],'name':_0x3a8502[_0x171fd5(0x23a)],'message':_0x3a8502[_0x171fd5(0x1c0)],'validate':_0x245ab7=>_0x245ab7[_0x171fd5(0x195)]()?!![]:_0x171fd5(0x281)+'d'}]);if(_0x455954[_0x171fd5(0x202)+'e']()===_0x3a8502[_0x171fd5(0x17d)])break;const _0x1ba66d=_0x455954[_0x171fd5(0x195)](),_0x576738=ui['spinner'](_0x171fd5(0x1a6)+'del\x20'+a2_0x48b7f3[_0x171fd5(0x192)](_0x1ba66d)+_0x171fd5(0x1e7))[_0x171fd5(0x1df)]();try{const _0xe5813b=await api[_0x171fd5(0x188)+_0x171fd5(0x1f4)]({'model':_0x1ba66d,'messages':[{'role':_0x3a8502[_0x171fd5(0x1a3)],'content':_0x3a8502['GzwUw']}],'max_tokens':0x32}),_0x2550df=_0xe5813b[_0x171fd5(0x233)][0x0][_0x171fd5(0x186)][_0x171fd5(0x25f)];_0x576738[_0x171fd5(0x16a)](_0x171fd5(0x208)+'k\x20passed!'),console['log'](a2_0x48b7f3['gray'](_0x3a8502['SiHHh'])),console['log'](_0x3a8502[_0x171fd5(0x229)](a2_0x48b7f3[_0x171fd5(0x17a)]('│\x20'),a2_0x48b7f3[_0x171fd5(0x23f)](_0x2550df['trim']()))),console['log'](a2_0x48b7f3[_0x171fd5(0x17a)](_0x3a8502[_0x171fd5(0x1db)]));const {confirm:_0x34d108}=await a2_0x104eb8[_0x171fd5(0x167)]([{'type':'confirm','name':_0x3a8502[_0x171fd5(0x172)],'message':_0x171fd5(0x23c)+_0x1ba66d+'?','default':!![]}]);if(_0x34d108){currentModel=_0x1ba66d,preferences['save']({'model':currentModel});break;}}catch(_0x3df563){_0x576738[_0x171fd5(0x1da)](_0x171fd5(0x208)+_0x171fd5(0x180)+_0x3df563[_0x171fd5(0x186)]),ui['warn'](_0x3a8502[_0x171fd5(0x2a7)]);}}_0x39f3b2(),ui[_0x171fd5(0x204)](_0x171fd5(0x254)+'o\x20'+a2_0x48b7f3[_0x171fd5(0x2c3)](currentModel));continue;}if(_0x3a8502[_0x171fd5(0x22c)](_0x6a380b,_0x3a8502[_0x171fd5(0x1fd)])){const _0xbba7c1=Object[_0x171fd5(0x2ce)](themes);let _0x4308eb=null;while(!_0x4308eb){console[_0x171fd5(0x1cd)](),console['log'](a2_0x48b7f3[_0x171fd5(0x192)](_0x3a8502[_0x171fd5(0x274)])),console[_0x171fd5(0x1f6)](a2_0x48b7f3[_0x171fd5(0x17a)](_0x171fd5(0x24d)+_0x171fd5(0x222)+themes[getThemeName()]?.[_0x171fd5(0x275)]+'\x0a')),_0xbba7c1[_0x171fd5(0x2ad)]((_0x183b46,_0x362b35)=>{const _0x1aea25=_0x171fd5,_0x1877c9=_0x3a8502[_0x1aea25(0x22c)](_0x183b46,_0x3a8502[_0x1aea25(0x1b9)](getThemeName))?a2_0x48b7f3[_0x1aea25(0x1af)]('\x20●'):'\x20\x20';console[_0x1aea25(0x1f6)](a2_0x48b7f3['white']('\x20\x20'+_0x3a8502['QFCsf'](_0x362b35,0x1)+'.'+_0x1877c9+'\x20'+themes[_0x183b46]['name']));}),console[_0x171fd5(0x1f6)](a2_0x48b7f3[_0x171fd5(0x17a)](_0x3a8502[_0x171fd5(0x18e)]));const {choice:_0x7a7dd5}=await a2_0x104eb8['prompt']([{'type':_0x3a8502['CjQuR'],'name':_0x171fd5(0x248),'message':_0x3a8502[_0x171fd5(0x2c1)]}]),_0x14e916=_0x3a8502[_0x171fd5(0x285)](parseInt,_0x7a7dd5);if(_0x3a8502[_0x171fd5(0x256)](_0x14e916,0x0)||!_0x7a7dd5)break;if(_0x3a8502[_0x171fd5(0x2af)](_0x14e916,0x1)&&_0x3a8502[_0x171fd5(0x2a9)](_0x14e916,_0xbba7c1[_0x171fd5(0x260)])){const _0xed5f91=_0xbba7c1[_0x3a8502[_0x171fd5(0x29b)](_0x14e916,0x1)];console['clear'](),console[_0x171fd5(0x1f6)](a2_0x48b7f3[_0x171fd5(0x192)](_0x171fd5(0x1ce)+':\x0a')),_0x3a8502[_0x171fd5(0x270)](previewTheme,_0xed5f91);const {confirm:_0x15a141}=await a2_0x104eb8[_0x171fd5(0x167)]([{'type':_0x3a8502[_0x171fd5(0x172)],'name':_0x3a8502[_0x171fd5(0x172)],'message':_0x171fd5(0x1d5)+'\x20\x22'+themes[_0xed5f91][_0x171fd5(0x275)]+'\x22?','default':!![]}]);if(_0x15a141)_0x4308eb=_0xed5f91;}}_0x4308eb&&(_0x3a8502['qYSVq'](setTheme,_0x4308eb),preferences['save']({'theme':_0x4308eb}));_0x3a8502['oXztj'](_0x39f3b2);continue;}if(_0x6a380b===_0x3a8502[_0x171fd5(0x1b2)]){const {mode:_0x4cc1e7}=await a2_0x104eb8[_0x171fd5(0x167)]([{'type':_0x3a8502['RpeIe'],'name':_0x3a8502[_0x171fd5(0x1bb)],'message':_0x3a8502[_0x171fd5(0x1e4)],'choices':[{'name':_0x3a8502['WeaMO'],'value':'ask'},{'name':_0x3a8502[_0x171fd5(0x24a)],'value':_0x3a8502['LTWaR']}],'default':preferences['get'](_0x3a8502[_0x171fd5(0x1bb)])||_0x3a8502[_0x171fd5(0x1ad)]}]);preferences[_0x171fd5(0x28c)]({'mode':_0x4cc1e7}),ui[_0x171fd5(0x204)]('Đã\x20lưu\x20chế'+_0x171fd5(0x241)+_0x4cc1e7),await a2_0x104eb8[_0x171fd5(0x167)]([{'type':_0x3a8502[_0x171fd5(0x283)],'name':_0x3a8502[_0x171fd5(0x292)],'message':a2_0x48b7f3[_0x171fd5(0x17a)](_0x3a8502[_0x171fd5(0x250)])}]),_0x3a8502[_0x171fd5(0x282)](_0x39f3b2);continue;}if(_0x6a380b===_0x3a8502[_0x171fd5(0x25d)]){try{const _0x1d7044=a2_0x1305a9[_0x171fd5(0x21c)](a2_0x34af6f[_0x171fd5(0x189)](),_0x3a8502[_0x171fd5(0x287)],_0x3a8502[_0x171fd5(0x1ff)]);!a2_0x2497cc[_0x171fd5(0x1d4)](_0x1d7044)&&a2_0x2497cc['mkdirSync'](_0x1d7044,{'recursive':!![]});const _0x3d0a42=new Date()[_0x171fd5(0x225)+'g']()[_0x171fd5(0x1bf)](/[:.]/g,'-'),_0x5d67a1='history-'+_0x3d0a42+_0x171fd5(0x19e),_0x49dbe7=a2_0x1305a9[_0x171fd5(0x21c)](_0x1d7044,_0x5d67a1);a2_0x2497cc[_0x171fd5(0x183)+'ync'](_0x49dbe7,JSON['stringify'](history,null,0x2),_0x3a8502[_0x171fd5(0x21a)]),ui[_0x171fd5(0x204)](_0x171fd5(0x1c5)+'ved\x20to:\x20'+a2_0x48b7f3[_0x171fd5(0x2c3)](_0x49dbe7));}catch(_0x3351f2){ui[_0x171fd5(0x1c1)](_0x171fd5(0x262)+'save\x20histo'+_0x171fd5(0x19d)+_0x3351f2['message']);}await a2_0x104eb8['prompt']([{'type':_0x3a8502[_0x171fd5(0x283)],'name':_0x3a8502[_0x171fd5(0x292)],'message':a2_0x48b7f3[_0x171fd5(0x17a)](_0x3a8502['PVVEf'])}]),_0x3a8502[_0x171fd5(0x282)](_0x39f3b2);continue;}ui['warn'](_0x171fd5(0x24c)+_0x171fd5(0x1c9)+_0x31ba48);continue;}history[_0x171fd5(0x1e0)]({'role':_0x171fd5(0x16f),'content':_0x31ba48}),_0x3a8502[_0x171fd5(0x17e)](history['length'],0x32)&&(history=await _0x3a8502[_0x171fd5(0x2b9)](summarizeOldMessages,history,currentModel)),history=truncateContext(history),await _0x3a8502[_0x171fd5(0x1b9)](processTurn);}catch(_0x24c9a9){ui['error'](_0x171fd5(0x165)+'\x20'+_0x24c9a9['message']);}}}
1
+ import inquirer from 'inquirer';
2
+ import inquirerAutocompletePrompt from 'inquirer-autocomplete-prompt';
3
+ import chalk from 'chalk';
4
+ import path from 'path';
5
+ import fs from 'fs';
6
+ import os from 'os';
7
+ import { fileURLToPath } from 'url';
8
+ import { OpenRouterAPI } from './api.js';
9
+ import { tools } from './tools.js';
10
+ import { ui, themes, setTheme, getThemeName, previewTheme, getTheme } from './ui.js';
11
+ import { config, loadConfig, saveConfig } from './config.js';
12
+ import { truncateContext } from './utils.js';
13
+ import { preferences } from './preferences.js';
14
+ import { shouldRetryWithNewKey } from './config.js';
15
+
16
+ // Load package.json for version
17
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
+ const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8'));
19
+
20
+ // Register autocomplete prompt
21
+ inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt);
22
+
23
+ // Command definitions for autocomplete
24
+ const commands = [
25
+ { name: '/help - Xem danh sách lệnh & phím tắt', value: '/help', short: '/help' },
26
+ { name: '/model - Đổi model AI', value: '/model', short: '/model' },
27
+ { name: '/theme - Đổi giao diện CLI', value: '/theme', short: '/theme' },
28
+ { name: '/cost - Xem chi phí & tokens', value: '/cost', short: '/cost' },
29
+ { name: '/history - Lưu lịch sử chat', value: '/history', short: '/history' },
30
+ { name: '/settings- Chế độ hoạt động', value: '/settings', short: '/settings' },
31
+ { name: '/init - Khởi tạo & phân tích dự án', value: '/init', short: '/init' },
32
+ { name: '/clear - Xóa ngữ cảnh chat', value: '/clear', short: '/clear' },
33
+ { name: '/exit - Thoát CLI', value: '/exit', short: '/exit' }
34
+ ];
35
+
36
+ async function searchCommands(answers, input = '') {
37
+ if (!input) return [];
38
+
39
+ if (input.startsWith('/')) {
40
+ return commands.filter(c => c.value.startsWith(input));
41
+ }
42
+
43
+ if (input.startsWith('!')) {
44
+ return [{
45
+ name: `${input} (Run as Bash command)`,
46
+ value: input,
47
+ short: input
48
+ }];
49
+ }
50
+ return [];
51
+ }
52
+
53
+ const prefs = preferences.load();
54
+ let currentModel = null;
55
+ let history = [];
56
+
57
+ // Load saved theme
58
+ if (prefs.theme && themes[prefs.theme]) {
59
+ setTheme(prefs.theme);
60
+ }
61
+
62
+ const SYSTEM_PROMPT = `
63
+ Bạn là một trợ lý lập trình AI cao cấp tên là OpenRouter CLI (phiên bản Tiendung).
64
+ Bạn có quyền truy cập vào hệ thống file của người dùng thông qua các công cụ.
65
+
66
+ QUY TẮC CỐT LÕI (TIENDUNG OPTIMIZED):
67
+ 1. Với dự án lớn, ĐỪNG dùng 'searchFiles' để đoán. Hãy dùng 'searchContent' (Grep) để tìm chính xác hàm/biến.
68
+ 2. Với file lớn (>2000 dòng), dùng 'readFile' có 'startLine'/'endLine' để đọc từng phần.
69
+ 3. Luôn kiểm tra kỹ output 'truncated' và yêu cầu thêm thông tin nếu thiếu.
70
+ 4. Trả lời ngắn gọn, tập trung vào code chạy được.
71
+
72
+ Công cụ có sẵn:
73
+ - listFiles: Xem danh sách file.
74
+ - readFile: Đọc file (Hỗ trợ Pagination).
75
+ - searchContent: Tìm nội dung code (Grep).
76
+ - readMultipleFiles: Đọc nhiều file.
77
+ - searchFiles: Tìm tên file.
78
+ - writeFile: Ghi file.
79
+ - executeCommand: Chạy lệnh.
80
+
81
+ Hãy sử dụng công cụ một cách tối ưu và tiết kiệm token.
82
+ `;
83
+
84
+ history.push({ role: 'system', content: SYSTEM_PROMPT });
85
+ let api = null;
86
+
87
+ async function summarizeOldMessages(messages, model) {
88
+ const systemMsg = messages.find(m => m.role === 'system');
89
+ const recentCount = 6;
90
+ if (messages.length <= recentCount + 2) return messages;
91
+ const toSummarize = messages.filter(m => m.role !== 'system').slice(0, -recentCount);
92
+ const toKeep = messages.filter(m => m.role !== 'system').slice(-recentCount);
93
+ if (toSummarize.length < 4) return messages;
94
+ ui.info('Context getting large, summarizing older messages...');
95
+ try {
96
+ const summaryPrompt = `Summarize this conversation briefly in Vietnamese (max 500 chars). Focus on: what user asked, what was done, key decisions made.\n\nConversation:\n${toSummarize.map(m => `${m.role}: ${typeof m.content === 'string' ? m.content?.slice(0, 500) : '[tool data]'}`).join('\n')}`;
97
+ const response = await api.client.chat.completions.create({
98
+ model: model,
99
+ messages: [{ role: 'user', content: summaryPrompt }],
100
+ max_tokens: 300
101
+ });
102
+ const summary = response.choices[0]?.message?.content || 'Unable to summarize.';
103
+ const summaryMessage = {
104
+ role: 'assistant',
105
+ content: `[CONTEXT SUMMARY - Older conversation]\n${summary}`
106
+ };
107
+ ui.info('Summary created, context optimized.');
108
+ return systemMsg ? [systemMsg, summaryMessage, ...toKeep] : [summaryMessage, ...toKeep];
109
+ } catch (error) {
110
+ ui.warn(`Summarization failed: ${error.message}. Falling back to truncation.`);
111
+ return messages;
112
+ }
113
+ }
114
+
115
+ async function handleToolCalls(toolCalls) {
116
+ const results = [];
117
+ for (const call of toolCalls) {
118
+ const fnName = call.function.name;
119
+ const args = JSON.parse(call.function.arguments);
120
+ ui.info(`Calling tool: ${chalk.bold(fnName)}`);
121
+ let output;
122
+ if (tools[fnName]) {
123
+ try {
124
+ output = await tools[fnName](args);
125
+ } catch (err) {
126
+ output = `Error executing tool ${fnName}: ${err.message}`;
127
+ }
128
+ } else {
129
+ output = `Error: Tool ${fnName} not found.`;
130
+ }
131
+ ui.toolOutput(JSON.stringify(output));
132
+ results.push({
133
+ tool_call_id: call.id,
134
+ role: 'tool',
135
+ name: fnName,
136
+ content: output
137
+ });
138
+ }
139
+ return results;
140
+ }
141
+
142
+ async function processTurn() {
143
+ let finishedTurn = false;
144
+ let retryCount = 0;
145
+ const MAX_RETRIES = 3;
146
+ while (!finishedTurn) {
147
+ const spinner = ui.spinner('Thinking...').start();
148
+ let fullContent = '';
149
+ let toolCalls = [];
150
+ try {
151
+ const stream = await api.chatStream(history, currentModel, tools.definitions);
152
+ spinner.stop();
153
+ for await (const chunk of stream) {
154
+ const delta = chunk.choices[0]?.delta;
155
+ if (delta?.content) {
156
+ process.stdout.write(getTheme().ai(delta.content));
157
+ fullContent += delta.content;
158
+ }
159
+ if (delta?.tool_calls) {
160
+ for (const tc of delta.tool_calls) {
161
+ if (!toolCalls[tc.index]) {
162
+ toolCalls[tc.index] = { id: tc.id || '', function: { name: tc.function?.name || '', arguments: tc.function?.arguments || '' }, type: 'function' };
163
+ } else {
164
+ if (tc.id) toolCalls[tc.index].id += tc.id;
165
+ if (tc.function?.name) toolCalls[tc.index].function.name += tc.function.name;
166
+ if (tc.function?.arguments) toolCalls[tc.index].function.arguments += tc.function.arguments;
167
+ }
168
+ }
169
+ }
170
+ }
171
+ process.stdout.write('\n');
172
+ const assistantMsg = { role: 'assistant', content: fullContent || null };
173
+ if (toolCalls.length > 0) {
174
+ assistantMsg.tool_calls = toolCalls;
175
+ history.push(assistantMsg);
176
+ const toolResults = await handleToolCalls(toolCalls);
177
+ history.push(...toolResults);
178
+ } else {
179
+ history.push(assistantMsg);
180
+ finishedTurn = true;
181
+ // Update local usage stats
182
+ const c = loadConfig() || {};
183
+ const newCount = (c.requestCount || 0) + 1;
184
+ saveConfig({ requestCount: newCount });
185
+ }
186
+ retryCount = 0;
187
+ } catch (error) {
188
+ spinner.stop();
189
+ if (shouldRetryWithNewKey(error) && retryCount < MAX_RETRIES) {
190
+ retryCount++;
191
+ ui.warn(`API Key error detected. Requesting new key... (Retry ${retryCount}/${MAX_RETRIES})`);
192
+ try {
193
+ const errorCode = error.message.match(/\d{3}/)?.[0] || 'unknown';
194
+ await api.reinitializeWithNewKey(errorCode);
195
+ ui.info('New API key obtained. Retrying...');
196
+ continue;
197
+ } catch (retryError) {
198
+ ui.error(`Failed to get new key: ${retryError.message}`);
199
+ finishedTurn = true;
200
+ }
201
+ } else {
202
+ ui.error(`Error in turn: ${error.message}`);
203
+ finishedTurn = true;
204
+ }
205
+ }
206
+ }
207
+ }
208
+
209
+ // Helper for version compare
210
+ async function checkUpdate() {
211
+ try {
212
+ // Use Raw URL with cache buster
213
+ const response = await fetch(`https://raw.githubusercontent.com/tiendung367/openrouter-cli-status/main/status.txt?t=${Date.now()}`);
214
+ if (response.ok) {
215
+ const status = (await response.text()).trim();
216
+
217
+ // Logic: True = Run, False/Other = Cloud disable/Force Update
218
+ if (status.toLowerCase() !== 'true') {
219
+ console.clear();
220
+ console.log(chalk.red.bold(`\n 🚫 SYSTEM STATUS: ${status} (Update/Access Required) `));
221
+ console.log(chalk.yellow(` ⚠️ Access to this tool is currently restricted or requires an update.`));
222
+ console.log(chalk.green.bold(` 👉 Run: npm install -g tiendung367/openrouter-cli \n`));
223
+
224
+ // Allow I/O to flush to prevent libuv assertion error
225
+ await new Promise(resolve => setTimeout(resolve, 200));
226
+ process.exit(1); // Force Exit
227
+ }
228
+ }
229
+ } catch (e) {
230
+ // Silently ignore network errors during update check
231
+ }
232
+ }
233
+
234
+ export async function runCLI() {
235
+
236
+ if (!prefs.theme) {
237
+ setTheme('dark');
238
+ preferences.save({ theme: 'dark' });
239
+ }
240
+ const userConfig = loadConfig();
241
+ const username = userConfig?.username || 'Guest';
242
+ let requestCount = userConfig?.requestCount || 0;
243
+
244
+ // First run: Ask for mode
245
+ if (!preferences.get('mode')) {
246
+ console.clear();
247
+ ui.showBanner(pkg.version, username, requestCount);
248
+
249
+ const { initialMode } = await inquirer.prompt([{
250
+ type: 'list',
251
+ name: 'initialMode',
252
+ message: chalk.cyan('👋 Welcome! Choose your preferred working mode:'),
253
+ choices: [
254
+ { name: '💬 Chat / Ask (Always ask before running commands)', value: 'ask' },
255
+ { name: '⚡ Code / Auto-Run (Execute commands automatically)', value: 'always' }
256
+ ]
257
+ }]);
258
+ preferences.save({ mode: initialMode });
259
+ ui.info(`✅ Mode configured! Change anytime with /settings`);
260
+ await new Promise(r => setTimeout(r, 1000));
261
+ }
262
+
263
+ ui.showBanner(pkg.version, username, requestCount);
264
+
265
+ const spinner = ui.spinner('Connecting...').start();
266
+ try {
267
+ api = await OpenRouterAPI.create();
268
+ spinner.succeed('Ready!');
269
+ } catch (error) {
270
+ spinner.fail(`Failed: ${error.message}`);
271
+ process.exit(1);
272
+ }
273
+ currentModel = prefs.model || config.defaultModel;
274
+ ui.info(`Model: ${chalk.bold(currentModel)}`);
275
+
276
+ const reloadUI = () => {
277
+ const updatedConfig = loadConfig();
278
+ console.clear();
279
+ ui.showBanner(pkg.version, username, updatedConfig?.requestCount || 0);
280
+ ui.info(`Model: ${chalk.bold(currentModel)}`);
281
+ };
282
+
283
+ while (true) {
284
+ try {
285
+ const { userInput } = await inquirer.prompt([{
286
+ type: 'input',
287
+ name: 'userInput',
288
+ message: chalk.green('You >'),
289
+ validate: (val) => val ? true : 'Type something!'
290
+ }]);
291
+
292
+ if (!userInput) continue;
293
+
294
+ if (userInput.startsWith('/')) {
295
+ const [cmd, ...args] = userInput.split(' ');
296
+
297
+ if (cmd === '/exit') {
298
+ ui.ai("Goodbye!");
299
+ process.exit(0);
300
+ }
301
+ if (cmd === '/help') {
302
+ ui.showHelp();
303
+ await inquirer.prompt([{ type: 'input', name: 'wait', message: chalk.gray('Press Enter to return...') }]);
304
+ reloadUI();
305
+ continue;
306
+ }
307
+ if (cmd === '/cost') {
308
+ const userConfig = loadConfig();
309
+ ui.info(`Current Session Usage:`);
310
+ ui.info(`Requests in config: ${userConfig?.requestCount || 0}`);
311
+ await inquirer.prompt([{ type: 'input', name: 'wait', message: chalk.gray('Press Enter to return...') }]);
312
+ reloadUI();
313
+ continue;
314
+ }
315
+ if (cmd === '/clear') {
316
+ history = [{ role: 'system', content: SYSTEM_PROMPT }];
317
+ reloadUI();
318
+ ui.info("History cleared.");
319
+ continue;
320
+ }
321
+ if (cmd === '/model') {
322
+ while (true) {
323
+ const { customId } = await inquirer.prompt([{
324
+ type: 'input',
325
+ name: 'customId',
326
+ message: 'Enter OpenRouter Model ID (or "cancel"):',
327
+ validate: input => input.trim() ? true : 'ID required'
328
+ }]);
329
+
330
+ if (customId.toLowerCase() === 'cancel') break;
331
+ const modelChoice = customId.trim();
332
+ const spinner = ui.spinner(`Testing model ${chalk.cyan(modelChoice)}...`).start();
333
+ try {
334
+ const testResponse = await api.createChatCompletion({
335
+ model: modelChoice,
336
+ messages: [{ role: 'user', content: 'Xin chào' }],
337
+ max_tokens: 50
338
+ });
339
+ const reply = testResponse.choices[0].message.content;
340
+ spinner.succeed('Model check passed!');
341
+ console.log(chalk.gray('\n┌─ Test Response ────────────────'));
342
+ console.log(chalk.gray('│ ') + chalk.white(reply.trim()));
343
+ console.log(chalk.gray('└────────────────────────────────\n'));
344
+ const { confirm } = await inquirer.prompt([{
345
+ type: 'confirm',
346
+ name: 'confirm',
347
+ message: `Switch to ${modelChoice}?`,
348
+ default: true
349
+ }]);
350
+ if (confirm) {
351
+ currentModel = modelChoice;
352
+ preferences.save({ model: currentModel });
353
+ break;
354
+ }
355
+ } catch (error) {
356
+ spinner.fail(`Model check failed: ${error.message}`);
357
+ ui.warn('Please select another model.');
358
+ }
359
+ }
360
+ reloadUI();
361
+ ui.info(`Switched to ${chalk.bold(currentModel)}`);
362
+ continue;
363
+ }
364
+ if (cmd === '/theme') {
365
+ const themeKeys = Object.keys(themes);
366
+ let newTheme = null;
367
+ while (!newTheme) {
368
+ console.clear();
369
+ console.log(chalk.cyan('\n🎨 Đổi theme\n'));
370
+ console.log(chalk.gray(`Theme hiện tại: ${themes[getThemeName()]?.name}\n`));
371
+ themeKeys.forEach((key, i) => {
372
+ const current = key === getThemeName() ? chalk.green(' ●') : ' ';
373
+ console.log(chalk.white(` ${i + 1}.${current} ${themes[key].name}`));
374
+ });
375
+ console.log(chalk.gray('\n 0. Hủy\n'));
376
+ const { choice } = await inquirer.prompt([{ type: 'input', name: 'choice', message: 'Nhập số để preview:' }]);
377
+ const num = parseInt(choice);
378
+ if (num === 0 || !choice) break;
379
+ if (num >= 1 && num <= themeKeys.length) {
380
+ const key = themeKeys[num - 1];
381
+ console.clear();
382
+ console.log(chalk.cyan('\n🎨 Preview:\n'));
383
+ previewTheme(key);
384
+ const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: `Chọn theme "${themes[key].name}"?`, default: true }]);
385
+ if (confirm) newTheme = key;
386
+ }
387
+ }
388
+ if (newTheme) {
389
+ setTheme(newTheme);
390
+ preferences.save({ theme: newTheme });
391
+ }
392
+ reloadUI(); // Return to main screen
393
+ continue;
394
+ }
395
+ if (cmd === '/settings') {
396
+ const { mode } = await inquirer.prompt([{
397
+ type: 'list',
398
+ name: 'mode',
399
+ message: 'Chọn chế độ hoạt động:',
400
+ choices: [
401
+ { name: '💬 Chat / Ask (Hỏi trước khi chạy lệnh)', value: 'ask' },
402
+ { name: '⚡ Code / Auto-Run (Tự động chạy lệnh - Nguy hiểm)', value: 'always' }
403
+ ],
404
+ default: preferences.get('mode') || 'ask'
405
+ }]);
406
+
407
+ preferences.save({ mode });
408
+ ui.info(`Đã lưu chế độ: ${mode}`);
409
+ await inquirer.prompt([{ type: 'input', name: 'wait', message: chalk.gray('Press Enter to return...') }]);
410
+ reloadUI();
411
+ continue;
412
+ }
413
+ if (cmd === '/history') {
414
+ try {
415
+ const docDir = path.join(os.homedir(), 'Documents', 'openrouter');
416
+ if (!fs.existsSync(docDir)) {
417
+ fs.mkdirSync(docDir, { recursive: true });
418
+ }
419
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
420
+ const filename = `history-${timestamp}.json`;
421
+ const filePath = path.join(docDir, filename);
422
+
423
+ fs.writeFileSync(filePath, JSON.stringify(history, null, 2), 'utf-8');
424
+ ui.info(`History saved to: ${chalk.bold(filePath)}`);
425
+ } catch (error) {
426
+ ui.error(`Failed to save history: ${error.message}`);
427
+ }
428
+ await inquirer.prompt([{ type: 'input', name: 'wait', message: chalk.gray('Press Enter to return...') }]);
429
+ reloadUI();
430
+ continue;
431
+ }
432
+ if (cmd === '/init') {
433
+ const spinner = ui.spinner('Scanning project files...').start();
434
+ // 1. Scan files
435
+ const files = await tools.definitions.find(t => t.name === 'listFiles').func ? await tools.definitions.find(t => t.name === 'listFiles').func()
436
+ : await tools.listFiles(); // Direct call
437
+
438
+ const fileListString = typeof files === 'string' ? files : JSON.stringify(files);
439
+ const fileList = JSON.parse(fileListString);
440
+
441
+ // 2. Read content (limit 30 files, 2KB each to save context)
442
+ let projectContext = "Project structure:\n";
443
+
444
+ let readCount = 0;
445
+ for (const file of fileList) {
446
+ const filePath = path.resolve(process.cwd(), file);
447
+ if (readCount < 30 && fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
448
+ try {
449
+ const content = fs.readFileSync(filePath, 'utf-8').slice(0, 2000);
450
+ projectContext += `\n--- File: ${file} ---\n${content}\n`;
451
+ readCount++;
452
+ } catch (e) { }
453
+ } else {
454
+ projectContext += `\n--- File: ${file} (Skipped content) ---\n`;
455
+ }
456
+ }
457
+
458
+ spinner.text = 'Analyzing project structure with AI...';
459
+
460
+ try {
461
+ const analysisPrompt = `
462
+ Analyze this project structure and content.
463
+ Create a comprehensive SUMMARY file (Markdown) that explains:
464
+ 1. What this project does.
465
+ 2. The purpose of each key file/folder.
466
+ 3. Tech stack used.
467
+ 4. Key logic flows.
468
+
469
+ Output ONLY the markdown content for the file 'project-summary.md'. Do not include extra chatter.
470
+
471
+ Project Context:
472
+ ${projectContext}
473
+ `;
474
+ const response = await api.createChatCompletion({
475
+ model: currentModel,
476
+ messages: [{ role: 'user', content: analysisPrompt }],
477
+ max_tokens: 4000
478
+ });
479
+
480
+ const summary = response.choices[0].message.content;
481
+ fs.writeFileSync('project-summary.md', summary, 'utf-8');
482
+
483
+ spinner.succeed('Project initialized!');
484
+ ui.info(`Summary saved to: ${chalk.bold('project-summary.md')}`);
485
+ ui.info('OpenRouter now understands the context of this folder.');
486
+
487
+ // Push summary to history so AI remembers it
488
+ history.push({ role: 'assistant', content: `[PROJECT INITIALIZED] I have analyzed the project. Summary:\n${summary}` });
489
+
490
+ } catch (error) {
491
+ spinner.fail(`Init failed: ${error.message}`);
492
+ }
493
+
494
+ await inquirer.prompt([{ type: 'input', name: 'wait', message: chalk.gray('Press Enter to return...') }]);
495
+ reloadUI();
496
+ continue;
497
+ }
498
+ ui.warn(`Unknown command: ${userInput}`);
499
+ continue;
500
+ }
501
+
502
+ // Normal chat
503
+ history.push({ role: 'user', content: userInput });
504
+
505
+ if (history.length > 50) {
506
+ history = await summarizeOldMessages(history, currentModel);
507
+ }
508
+ history = truncateContext(history);
509
+
510
+ await processTurn(); // Process AI response
511
+
512
+ } catch (error) {
513
+ ui.error(`CLI Error: ${error.message}`);
514
+ // Prevent infinite loop if error is persistent
515
+ // but keep loop alive
516
+ }
517
+ }
518
+ }
@@ -1 +1,36 @@
1
- const a3_0x3f7691=a3_0xf7ff;(function(_0x546334,_0x336e36){const _0x24f955=a3_0xf7ff,_0x4f6c4e=_0x546334();while(!![]){try{const _0x1f3427=parseInt(_0x24f955(0x10a))/0x1+parseInt(_0x24f955(0x104))/0x2*(parseInt(_0x24f955(0x107))/0x3)+-parseInt(_0x24f955(0x10b))/0x4+-parseInt(_0x24f955(0xf9))/0x5*(parseInt(_0x24f955(0xfe))/0x6)+-parseInt(_0x24f955(0x100))/0x7*(-parseInt(_0x24f955(0x103))/0x8)+parseInt(_0x24f955(0x110))/0x9+-parseInt(_0x24f955(0x108))/0xa;if(_0x1f3427===_0x336e36)break;else _0x4f6c4e['push'](_0x4f6c4e['shift']());}catch(_0x403592){_0x4f6c4e['push'](_0x4f6c4e['shift']());}}}(a3_0x6e65,0x88a4c));function a3_0xf7ff(_0x141ef5,_0x4c5c5b){_0x141ef5=_0x141ef5-0xf6;const _0x6e6561=a3_0x6e65();let _0xf7ff4e=_0x6e6561[_0x141ef5];return _0xf7ff4e;}function a3_0x6e65(){const _0x1c71a3=['4440540OFsmiI','ync','828699UYHoGg','2474060zzEcQZ','dirname','load\x20confi','save\x20confi','../config.','5277177hXMLav','readFileSy','json','utf-8','cOUcg','1322105HGfmbD','parse','HVMnS','g.json:','Failed\x20to\x20','24QikQIN','error','161VgJLrh','stringify','load','96344vxQhdB','2176rGUqCQ','resolve','message','2724RAuGmB'];a3_0x6e65=function(){return _0x1c71a3;};return a3_0x6e65();}import a3_0xe18bf9 from'fs';import a3_0x1e3748 from'path';import{fileURLToPath}from'url';const __dirname=a3_0x1e3748[a3_0x3f7691(0x10c)](fileURLToPath(import.meta.url)),configPath=a3_0x1e3748[a3_0x3f7691(0x105)](__dirname,a3_0x3f7691(0x10f)+a3_0x3f7691(0xf6));export const preferences={'load':()=>{const _0x11507a=a3_0x3f7691;try{if(a3_0xe18bf9['existsSync'](configPath))return JSON[_0x11507a(0xfa)](a3_0xe18bf9[_0x11507a(0x111)+'nc'](configPath,_0x11507a(0xf7)));}catch(_0x3b21ab){console[_0x11507a(0xff)](_0x11507a(0xfd)+_0x11507a(0x10d)+_0x11507a(0xfc),_0x3b21ab['message']);}return{};},'get':_0x315d42=>{const _0x40d63a=a3_0x3f7691,_0x70ce96=preferences[_0x40d63a(0x102)]();return _0x70ce96[_0x315d42];},'save':_0x78679b=>{const _0x427320=a3_0x3f7691,_0x4c5d7e={'cOUcg':'utf-8','HVMnS':'Failed\x20to\x20'+_0x427320(0x10e)+'g.json:'};try{const _0x488b38=preferences[_0x427320(0x102)](),_0x381ac4={..._0x488b38,..._0x78679b};return a3_0xe18bf9['writeFileS'+_0x427320(0x109)](configPath,JSON[_0x427320(0x101)](_0x381ac4,null,0x2),_0x4c5d7e[_0x427320(0xf8)]),!![];}catch(_0x2019ff){return console[_0x427320(0xff)](_0x4c5d7e[_0x427320(0xfb)],_0x2019ff[_0x427320(0x106)]),![];}}};
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const configPath = path.resolve(__dirname, '../config.json');
7
+
8
+ export const preferences = {
9
+ load: () => {
10
+ try {
11
+ if (fs.existsSync(configPath)) {
12
+ return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
13
+ }
14
+ } catch (error) {
15
+ console.error('Failed to load config.json:', error.message);
16
+ }
17
+ return {};
18
+ },
19
+
20
+ get: (key) => {
21
+ const conf = preferences.load();
22
+ return conf[key];
23
+ },
24
+
25
+ save: (prefs) => {
26
+ try {
27
+ const current = preferences.load();
28
+ const updated = { ...current, ...prefs };
29
+ fs.writeFileSync(configPath, JSON.stringify(updated, null, 2), 'utf-8');
30
+ return true;
31
+ } catch (error) {
32
+ console.error('Failed to save config.json:', error.message);
33
+ return false;
34
+ }
35
+ }
36
+ };