hudada-cli
Version:
专为程序员准备的本地文档搜索,快捷开发工具
3 lines (2 loc) • 4.44 kB
JavaScript
import e from"openai";import o from"chalk";import{marked as r}from"marked";import{markedTerminal as s}from"marked-terminal";import{setApiKey as t,getApiKey as n}from"./config.mjs";import i from"path";import c from"fs";import{templates as a,loadTemplates as l,saveTemplates as m}from"./ai/templates.mjs";import{createInterface as u}from"readline";import"@inquirer/prompts";import{saveHistory as d,loadHistory as p}from"./ai/history.mjs";import f from"inquirer-autocomplete-standalone";const g=u({input:process.stdin,output:process.stdout});async function y(e){return new Promise((o=>{g.question(e,(e=>{o(e)}))}))}function w(e){return new Promise((o=>setTimeout(o,e)))}function h(e){return[/^#{1,6}\s/,/\*\*.+?\*\*/,/\*.+?\*/,/`{1,3}[^`]+`{1,3}/,/\[.+?\]\(.+?\)/,/!\[.+?\]\(.+?\)/,/^\s*[-*+]\s/,/^\s*\d+\.\s/,/^\s*>\s/,/\|.+\|.+\|/,/^-{3,}$/].some((o=>o.test(e)))}async function v(e,o=!1){const s=o?await r(e):e;for(const e of s)process.stdout.write(e),await w(25)}async function k(r,s){try{let s=!1,u="";if("key"===r[0])return r[1]?(t(r[1]),void console.log(o.green("API key 已设置"))):(console.error(o.yellow("格式为:my ai key <your-api-key>")),void process.exit(0));if("clear"===r[0])return d({messages:[]}),void console.log(o.green("聊天历史已清除"));if("list"===r[0]){const e=await async function(){const e=a.map((e=>({name:`${e.act}`,value:e,description:e.prompt.slice(0,300)})));try{const o=await f({message:"请选择或搜索模板:",source:async o=>{let r=e.filter((e=>e.name.includes(o)));return o||(r=e),r.map((e=>({value:e.name,description:`${e.description}`})))}});return e.filter((e=>e.name.includes(o)))[0].value}catch(e){console.log("已取消选择",e)}}();if(e){console.log(o.green(`已选择模板: ${e.act}`));const r={messages:[{role:"system",content:e.prompt}]};d(r),console.log(o.gray("现在可以开始对话了!"))}return}if("add"===r[0])return await async function(){try{console.log(o.cyan("\n添加新的 AI 对话模板\n"));const e=await y("模板名称: ");if(!e.trim())return void console.error(o.red("模板名称不能为空"));if(a.find((o=>o.act===e)))return void console.error(o.red("模板名称已存在"));const r=await y("模板提示词: ");if(!r.trim())return void console.error(o.red("提示词不能为空"));const s={act:e,prompt:r},t=l();t.push(s),m(t)?console.log(o.green("\n模板添加成功!")):console.error(o.red("\n模板保存失败"))}catch(e){console.error(o.red("添加模板失败:",e))}}(),void process.exit(0);if("read"===r[0]){if(!r[1])return void console.error(o.yellow("请提供文件路径"));const e=i.resolve(process.cwd(),r[1]);if(!c.existsSync(e))return void console.error(o.red(`文件不存在: ${e}`));try{const o=c.readFileSync(e,"utf-8"),s=i.extname(e).toLowerCase();r=[`这是一个${s}文件的内容,如果读取文件完毕,请回复 "已读取文件完毕"`,o]}catch(e){return void console.error(o.red(`读取文件失败: ${e.message}`))}}const w=p();let k="";for(k=r[1]?r[0]:r.join(" ");;){if(!k.trim()){if(k=await y(o.cyan("你: ")),"exit"===k.toLowerCase()){console.log(o.yellow("退出对话")),g.close();break}continue}if(w.messages.length>1&&"user"===w.messages[w.messages.length-1].role&&w.messages[w.messages.length-1].content===k)return void console.log(o.yellow("检测到重复输入,已忽略"));w.messages.push({role:"user",content:k});const r={baseURL:"https://api.deepseek.com/",apiKey:n().trim()},t=new e(r),i=await t.chat.completions.create({messages:w.messages,model:"deepseek-chat",stream:!0});process.stdout.write(o.green("AI: \n"));let a="",l=!1,m="",p="";for await(const e of i){const o=e.choices[0]?.delta?.content||"";if(p+=o,o.includes("```")){if(l=!l,!l){a+=o,await v(a,!0),a="";continue}m&&(await v(m,h(m)),m="")}l?a+=o:(m+=o,(o.includes("\n")||o.includes("."))&&m&&(await v(m,h(m)),m=""))}if(a||m){const e=a+m;await v(e,h(e))}if(s)try{c.appendFileSync(u,"\n"+p,"utf-8"),console.log(o.green(`\n内容已保存到文件: ${u}`))}catch(e){console.error(o.red(`保存到文件失败: ${e.message}`))}if(w.messages.push({role:"assistant",content:p}),d(w),process.stdout.write("\n"),k=await y(o.cyan("你: ")),"exit"===k.toLowerCase()){console.log(o.yellow("退出对话")),g.close();break}}}catch(e){console.error(o.red(`AI 响应失败: ${e.message}`)),g.close()}}r.use(s({code:o.yellow,codespan:o.yellow,strong:o.bold,em:o.italic,heading:o.bold.green,listitem:o.cyan}));export{k as handleAi};