inspiration-agent 0.0.5 → 0.0.6
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/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const{spawn:spawn,exec:exec,fork:fork}=require("child_process"),fs=require("fs"),path=require("path"),os=require("os"),readline=require("readline"),net=require("net"),PACKAGE_JSON=require("../package.json"),CONFIG_DIR=path.join(os.homedir(),".inspiration"),PID_FILE=path.join(CONFIG_DIR,"inspiration.pid"),ENV_FILE=path.join(CONFIG_DIR,".env");function parseArgs(){const n=process.argv.slice(2),e=n[0]||"help",o={};for(let e=1;e<n.length;e++)if(n[e].startsWith("-")){const t=n[e].replace(/^-+/,"");n[e+1]&&!n[e+1].startsWith("-")?(o[t]=n[e+1],e++):o[t]=!0}return{command:e,flags:o,args:n}}function ensureConfigDir(){fs.existsSync(CONFIG_DIR)||fs.mkdirSync(CONFIG_DIR,{recursive:!0})}function getPid(){if(fs.existsSync(PID_FILE)){const n=parseInt(fs.readFileSync(PID_FILE,"utf-8").trim(),10);if(Number.isInteger(n))try{return process.kill(n,0),n}catch(n){return fs.unlinkSync(PID_FILE),null}}return null}function removePid(){fs.existsSync(PID_FILE)&&fs.unlinkSync(PID_FILE)}function isRunning(){return null!==getPid()}function getEnvPath(){if(fs.existsSync(ENV_FILE))return ENV_FILE;const n=path.join(process.cwd(),".env");return fs.existsSync(n)?n:null}function startDaemon(){return new Promise((n,e)=>{const o=path.join(__dirname,"index.js"),t=getEnvPath(),s=path.join(__dirname,"..","logs");fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0});const i=path.join(s,"daemon.log"),r=fs.openSync(i,"a"),a={...process.env};t&&(a.DOTENV_CONFIG_PATH=t);const c=spawn(process.execPath,[o],{detached:!0,stdio:["ignore",r,r],env:a});c.unref(),c.on("error",n=>{e(n)});const l=Date.now(),checkPid=()=>{const o=getPid();o?n(o):Date.now()-l>1e4?e(new Error(`启动超时,请检查日志: ${i}`)):setTimeout(checkPid,100)};setTimeout(checkPid,300)})}function startForeground(){return new Promise((n,e)=>{const o=path.join(__dirname,"index.js"),t=getEnvPath(),s={...process.env};t&&(s.DOTENV_CONFIG_PATH=t);const i=spawn(process.execPath,[o],{stdio:"inherit",env:s});i.on("error",n=>{e(n)}),i.on("exit",e=>{n(e)})})}function stopProcess(){return new Promise(n=>{const e=getPid();if(e)try{process.kill(e,"SIGTERM");let o=0;const checkStopped=()=>{try{process.kill(e,0),o++,o>20?(process.kill(e,"SIGKILL"),removePid(),n(!0)):setTimeout(checkStopped,100)}catch(e){removePid(),n(!0)}};setTimeout(checkStopped,100)}catch(e){removePid(),n(!1)}else n(!1)})}function showHelp(){console.log(`\n[1minspiration-agent[0m - Personal AI Assistant\n\n[1m用法:[0m\n inspiration <command> [options]\n\n[1m命令:[0m\n start 启动服务\n -d, --daemon 后台运行\n stop 停止服务\n restart 重启服务\n status 查看服务状态\n version 查看版本号\n onboard 配置向导\n cli 打开命令行交互(需要服务运行中)\n help 显示帮助信息\n\n[1m示例:[0m\n inspiration start # 前台启动\n inspiration start -d # 后台启动\n inspiration onboard # 运行配置向导\n inspiration status # 查看状态\n inspiration cli # 进入交互模式\n\n[1m配置文件:[0m\n ${ENV_FILE}\n\n[1m更多信息:[0m\n https://github.com/your-repo/inspiration-agent\n`)}function showVersion(){console.log(`inspiration-agent v${PACKAGE_JSON.version}`)}function showStatus(){const n=getPid();n?console.log(`\n✅ inspiration-agent 运行中 (PID: ${n})\n`):(console.log("\n❌ inspiration-agent 未运行"),console.log(" 使用 'inspiration start' 启动服务\n"))}async function runOnboard(){console.log("\n[1m🔧 inspiration-agent 配置向导[0m\n"),ensureConfigDir();const n=fs.existsSync(ENV_FILE)?fs.readFileSync(ENV_FILE,"utf-8"):"",getExistingValue=e=>{const o=n.match(new RegExp(`^${e}=(.*)$`,"m"));return o?o[1]:""},e=readline.createInterface({input:process.stdin,output:process.stdout}),question=(n,o="",t=!1)=>new Promise(s=>{const i=o?`${n} [${o}]: `:t?`${n} (必填): `:`${n}: `;e.question(i,e=>{const i=e.trim();!t||i||o?s(i||o):(console.log("此项为必填项,请输入有效值"),question(n,o,t).then(s))})});let o="";try{console.log("[1m========== LLM 提供商配置 ==========[0m\n");const n=["deepseek","kimi","zhipu","minimax"];let e=await question("LLM 提供商 (deepseek/kimi/zhipu/minimax)",getExistingValue("LLM_PROVIDER")||"deepseek",!0);for(;!n.includes(e.toLowerCase());)console.log("无效的提供商,请选择: deepseek, kimi, zhipu, minimax"),e=await question("LLM 提供商 (deepseek/kimi/zhipu/minimax)",getExistingValue("LLM_PROVIDER")||"deepseek",!0);if(o+=`LLM_PROVIDER=${e}\n\n`,"deepseek"===e){console.log("\n[36mDeepSeek 配置:[0m");const n=await question("API Key",getExistingValue("DEEPSEEK_API_KEY"),!0),e=await question("API URL",getExistingValue("DEEPSEEK_API_URL")||"https://api.deepseek.com/v1/chat/completions"),t=await question("模型名称",getExistingValue("DEEPSEEK_MODEL")||"deepseek-chat");o+=`DEEPSEEK_API_KEY=${n}\n`,o+=`DEEPSEEK_API_URL=${e}\n`,o+=`DEEPSEEK_MODEL=${t}\n\n`}else if("kimi"===e){console.log("\n[36mKimi (月之暗面) 配置:[0m");const n=await question("API Key",getExistingValue("KIMI_API_KEY"),!0),e=await question("API URL",getExistingValue("KIMI_API_URL")||"https://api.moonshot.cn/v1/chat/completions"),t=await question("模型名称",getExistingValue("KIMI_MODEL")||"moonshot-v1-8k");o+=`KIMI_API_KEY=${n}\n`,o+=`KIMI_API_URL=${e}\n`,o+=`KIMI_MODEL=${t}\n\n`}else if("zhipu"===e){console.log("\n[36m智谱 GLM 配置:[0m");const n=await question("API Key",getExistingValue("ZHIPU_API_KEY"),!0),e=await question("API URL",getExistingValue("ZHIPU_API_URL")||"https://open.bigmodel.cn/api/paas/v4/chat/completions"),t=await question("模型名称",getExistingValue("ZHIPU_MODEL")||"glm-4-flash");o+=`ZHIPU_API_KEY=${n}\n`,o+=`ZHIPU_API_URL=${e}\n`,o+=`ZHIPU_MODEL=${t}\n\n`}else if("minimax"===e){console.log("\n[36mMiniMax 配置:[0m");const n=await question("API Key",getExistingValue("MINIMAX_API_KEY"),!0),e=await question("Group ID",getExistingValue("MINIMAX_GROUP_ID"),!0),t=await question("API URL",getExistingValue("MINIMAX_API_URL")||"https://api.minimax.chat/v1/chat/completions"),s=await question("模型名称",getExistingValue("MINIMAX_MODEL")||"abab6.5s-chat");o+=`MINIMAX_API_KEY=${n}\n`,o+=`MINIMAX_GROUP_ID=${e}\n`,o+=`MINIMAX_API_URL=${t}\n`,o+=`MINIMAX_MODEL=${s}\n\n`}console.log("[1m========== 飞书应用配置 ==========[0m\n");const t=await question("飞书 App ID",getExistingValue("FEISHU_APP_ID"),!0),s=await question("飞书 App Secret",getExistingValue("FEISHU_APP_SECRET"),!0),i=await question("飞书 Verification Token",getExistingValue("FEISHU_VERIFICATION_TOKEN"),!0),r=await question("飞书 Encrypt Key (可选)",getExistingValue("FEISHU_ENCRYPT_KEY")),a=await question("飞书默认 Chat ID (可选)",getExistingValue("FEISHU_DEFAULT_CHAT_ID"));o+=`FEISHU_APP_ID=${t}\n`,o+=`FEISHU_APP_SECRET=${s}\n`,o+=`FEISHU_VERIFICATION_TOKEN=${i}\n`,r&&(o+=`FEISHU_ENCRYPT_KEY=${r}\n`),a&&(o+=`FEISHU_DEFAULT_CHAT_ID=${a}\n\n`),console.log("[1m========== 服务配置 ==========[0m\n");const c=await question("服务端口",getExistingValue("PORT")||"3000");let l=await question("启用流式输出 (true/false)",getExistingValue("ENABLE_STREAM")||"true");for(;!["true","false"].includes(l.toLowerCase());)console.log("请输入 true 或 false"),l=await question("启用流式输出 (true/false)",getExistingValue("ENABLE_STREAM")||"true");o+=`PORT=${c}\n`,o+=`ENABLE_STREAM=${l}\n\n`,console.log("[1m========== 命令安全配置 ==========[0m\n");let I=await question("启用命令白名单 (true/false)",getExistingValue("COMMAND_WHITELIST_ENABLED")||"true");for(;!["true","false"].includes(I.toLowerCase());)console.log("请输入 true 或 false"),I=await question("启用命令白名单 (true/false)",getExistingValue("COMMAND_WHITELIST_ENABLED")||"true");o+=`COMMAND_WHITELIST_ENABLED=${I}\n`,o+="\n",console.log("[1m========== 数据库配置 ==========[0m\n");o+=`DATABASE_PATH=${await question("数据库路径",getExistingValue("DATABASE_PATH")||"./data/ai_agent.db")}\n\n`,console.log("[1m========== 浏览器配置 ==========[0m\n");o+=`BROWSER_EXECUTABLE_PATH=${await question("浏览器路径 (留空使用默认 Chrome)",getExistingValue("BROWSER_EXECUTABLE_PATH")||"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome")}\n`,fs.writeFileSync(ENV_FILE,o),console.log("\n[32m✅ 配置已保存到:[0m",ENV_FILE),console.log("\n现在可以使用 'inspiration start' 启动服务\n")}finally{e.close()}}const SOCKET_FILE=path.join(CONFIG_DIR,"inspiration.sock");function sendIPC(n){return new Promise((e,o)=>{if(!fs.existsSync(SOCKET_FILE))return void o(new Error("服务未运行"));const t=net.createConnection(SOCKET_FILE,()=>{t.write(JSON.stringify(n)+"\n")});let s="",i=!1;const r=setTimeout(()=>{i||(t.destroy(),o(new Error("连接超时")))},3e5);t.on("data",n=>{s+=n.toString()}),t.on("end",()=>{i=!0,clearTimeout(r);try{const n=JSON.parse(s.trim());e(n)}catch(n){o(new Error("解析响应失败"))}}),t.on("error",n=>{i=!0,clearTimeout(r),o(new Error(`连接失败: ${n.message}`))})})}async function runCli(){if(!getPid())return console.log("\n❌ inspiration-agent 未运行"),void console.log(" 请先使用 'inspiration start' 启动服务\n");console.log("\n[1m💬 inspiration-agent 命令行交互[0m"),console.log("输入消息与 AI 交互,输入 'exit' 或 'quit' 退出\n");const n=new(require("./utils/markdown-renderer")),e=readline.createInterface({input:process.stdin,output:process.stdout}),question=n=>new Promise(o=>{e.question(n,o)});try{for(;;){const o=await question("你: ");if(o.trim()){if("exit"===o.toLowerCase()||"quit"===o.toLowerCase()){console.log("\n再见!\n");break}if(o.startsWith("/"))try{const n=await sendIPC({type:"command",content:o});"response"===n.type?("object"==typeof n.content?console.log("\n"+JSON.stringify(n.content,null,2)+"\n"):console.log(`\n${n.content}\n`),"/shutdown"===o.trim().toLowerCase()&&(console.log("CLI 已关闭\n"),e.close(),process.exit(0))):"error"===n.type&&console.error(`\n错误: ${n.message}\n`)}catch(n){console.error(`\n错误: ${n.message}\n`)}else try{const e=await sendIPC({type:"message",content:o});if("response"===e.type){const o=n.render(e.content);console.log(`\nAI:\n${o}\n`)}else"error"===e.type&&console.error(`\n错误: ${e.message}\n`)}catch(n){console.error(`\n错误: ${n.message}\n`)}}}}finally{e.close()}}async function main(){const{command:n,flags:e}=parseArgs();switch(n){case"start":if(isRunning()){console.log("\n⚠️ inspiration-agent 已在运行中"),console.log(" 使用 'inspiration status' 查看状态\n");break}if(e.d||e.daemon){console.log("\n🚀 启动 inspiration-agent (后台模式)...");try{const n=await startDaemon();console.log(`✅ 服务已启动 (PID: ${n})\n`)}catch(n){console.error(`❌ 启动失败: ${n.message}\n`),process.exit(1)}}else console.log("\n🚀 启动 inspiration-agent...\n"),await startForeground();break;case"stop":await stopProcess()?console.log("\n✅ inspiration-agent 已停止\n"):console.log("\n⚠️ inspiration-agent 未运行\n");break;case"restart":if(console.log("\n🔄 重启 inspiration-agent..."),await stopProcess(),await new Promise(n=>setTimeout(n,1e3)),e.d||e.daemon){const n=await startDaemon();console.log(`✅ 服务已重启 (PID: ${n})\n`)}else await startForeground();break;case"status":showStatus(),console.log();break;case"version":case"-v":case"--version":showVersion();break;case"onboard":await runOnboard();break;case"cli":await runCli();break;default:showHelp()}}main().catch(n=>{console.error(`错误: ${n.message}`),process.exit(1)});
|
|
2
|
+
const{spawn:spawn,exec:exec,fork:fork}=require("child_process"),fs=require("fs"),path=require("path"),os=require("os"),readline=require("readline"),net=require("net"),PACKAGE_JSON=require("../package.json"),CONFIG_DIR=path.join(os.homedir(),".inspiration"),PID_FILE=path.join(CONFIG_DIR,"inspiration.pid"),ENV_FILE=path.join(CONFIG_DIR,".env");function parseArgs(){const n=process.argv.slice(2),e=n[0]||"help",o={};for(let e=1;e<n.length;e++)if(n[e].startsWith("-")){const t=n[e].replace(/^-+/,"");n[e+1]&&!n[e+1].startsWith("-")?(o[t]=n[e+1],e++):o[t]=!0}return{command:e,flags:o,args:n}}function ensureConfigDir(){try{fs.existsSync(CONFIG_DIR)||fs.mkdirSync(CONFIG_DIR,{recursive:!0});const n=path.join(CONFIG_DIR,".permission_test");fs.writeFileSync(n,"test"),fs.unlinkSync(n)}catch(n){console.log("\n[33m⚠️ 需要授权访问配置目录[0m\n"),console.log("系统需要访问以下目录来存储配置和日志:"),console.log(` ${CONFIG_DIR}\n`),console.log("请运行以下命令授权:"),console.log(`\n sudo chown -R $(whoami) "${CONFIG_DIR}"\n`),console.log("或者手动创建目录并授权:"),console.log(` mkdir -p "${CONFIG_DIR}"`),console.log(` chmod 755 "${CONFIG_DIR}"\n`),process.exit(1)}}function getPid(){if(fs.existsSync(PID_FILE)){const n=parseInt(fs.readFileSync(PID_FILE,"utf-8").trim(),10);if(Number.isInteger(n))try{return process.kill(n,0),n}catch(n){return fs.unlinkSync(PID_FILE),null}}return null}function removePid(){fs.existsSync(PID_FILE)&&fs.unlinkSync(PID_FILE)}function isRunning(){return null!==getPid()}function getEnvPath(){if(fs.existsSync(ENV_FILE))return ENV_FILE;const n=path.join(process.cwd(),".env");return fs.existsSync(n)?n:null}function checkPermissions(){try{fs.existsSync(CONFIG_DIR)||fs.mkdirSync(CONFIG_DIR,{recursive:!0});const n=path.join(CONFIG_DIR,".permission_test");return fs.writeFileSync(n,"test"),fs.unlinkSync(n),!0}catch(n){return!1}}function startDaemon(){return new Promise((n,e)=>{if(!checkPermissions())return console.log("\n[33m⚠️ 需要授权访问配置目录[0m\n"),console.log("系统需要访问以下目录来存储配置和日志:"),console.log(` ${CONFIG_DIR}\n`),console.log("请运行以下命令授权:"),console.log(`\n sudo chown -R $(whoami) "${CONFIG_DIR}"\n`),console.log("或者手动创建目录并授权:"),console.log(` mkdir -p "${CONFIG_DIR}"`),console.log(` chmod 755 "${CONFIG_DIR}"\n`),void e(new Error("权限不足"));const o=path.join(__dirname,"index.js"),t=getEnvPath(),s=path.join(CONFIG_DIR,"logs");fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0});const i=path.join(s,"daemon.log"),r=fs.openSync(i,"a"),a={...process.env};t&&(a.DOTENV_CONFIG_PATH=t);const c=spawn(process.execPath,[o],{detached:!0,stdio:["ignore",r,r],env:a});c.unref(),c.on("error",n=>{e(n)});const l=Date.now(),checkPid=()=>{const o=getPid();o?n(o):Date.now()-l>1e4?e(new Error(`启动超时,请检查日志: ${i}`)):setTimeout(checkPid,100)};setTimeout(checkPid,300)})}function startForeground(){return new Promise((n,e)=>{const o=path.join(__dirname,"index.js"),t=getEnvPath(),s={...process.env};t&&(s.DOTENV_CONFIG_PATH=t);const i=spawn(process.execPath,[o],{stdio:"inherit",env:s});i.on("error",n=>{e(n)}),i.on("exit",e=>{n(e)})})}function stopProcess(){return new Promise(n=>{const e=getPid();if(e)try{process.kill(e,"SIGTERM");let o=0;const checkStopped=()=>{try{process.kill(e,0),o++,o>20?(process.kill(e,"SIGKILL"),removePid(),n(!0)):setTimeout(checkStopped,100)}catch(e){removePid(),n(!0)}};setTimeout(checkStopped,100)}catch(e){removePid(),n(!1)}else n(!1)})}function showHelp(){console.log(`\n[1minspiration-agent[0m - Personal AI Assistant\n\n[1m用法:[0m\n inspiration <command> [options]\n\n[1m命令:[0m\n start 启动服务\n -d, --daemon 后台运行\n stop 停止服务\n restart 重启服务\n status 查看服务状态\n version 查看版本号\n onboard 配置向导\n cli 打开命令行交互(需要服务运行中)\n help 显示帮助信息\n\n[1m示例:[0m\n inspiration start # 前台启动\n inspiration start -d # 后台启动\n inspiration onboard # 运行配置向导\n inspiration status # 查看状态\n inspiration cli # 进入交互模式\n\n[1m配置文件:[0m\n ${ENV_FILE}\n\n[1m更多信息:[0m\n https://github.com/your-repo/inspiration-agent\n`)}function showVersion(){console.log(`inspiration-agent v${PACKAGE_JSON.version}`)}function showStatus(){const n=getPid();n?console.log(`\n✅ inspiration-agent 运行中 (PID: ${n})\n`):(console.log("\n❌ inspiration-agent 未运行"),console.log(" 使用 'inspiration start' 启动服务\n"))}async function runOnboard(){console.log("\n[1m🔧 inspiration-agent 配置向导[0m\n"),ensureConfigDir();const n=fs.existsSync(ENV_FILE)?fs.readFileSync(ENV_FILE,"utf-8"):"",getExistingValue=e=>{const o=n.match(new RegExp(`^${e}=(.*)$`,"m"));return o?o[1]:""},e=readline.createInterface({input:process.stdin,output:process.stdout}),question=(n,o="",t=!1)=>new Promise(s=>{const i=o?`${n} [${o}]: `:t?`${n} (必填): `:`${n}: `;e.question(i,e=>{const i=e.trim();!t||i||o?s(i||o):(console.log("此项为必填项,请输入有效值"),question(n,o,t).then(s))})});let o="";try{console.log("[1m========== LLM 提供商配置 ==========[0m\n");const n=["deepseek","kimi","zhipu","minimax"];let e=await question("LLM 提供商 (deepseek/kimi/zhipu/minimax)",getExistingValue("LLM_PROVIDER")||"deepseek",!0);for(;!n.includes(e.toLowerCase());)console.log("无效的提供商,请选择: deepseek, kimi, zhipu, minimax"),e=await question("LLM 提供商 (deepseek/kimi/zhipu/minimax)",getExistingValue("LLM_PROVIDER")||"deepseek",!0);if(o+=`LLM_PROVIDER=${e}\n\n`,"deepseek"===e){console.log("\n[36mDeepSeek 配置:[0m");const n=await question("API Key",getExistingValue("DEEPSEEK_API_KEY"),!0),e=await question("API URL",getExistingValue("DEEPSEEK_API_URL")||"https://api.deepseek.com/v1/chat/completions"),t=await question("模型名称",getExistingValue("DEEPSEEK_MODEL")||"deepseek-chat");o+=`DEEPSEEK_API_KEY=${n}\n`,o+=`DEEPSEEK_API_URL=${e}\n`,o+=`DEEPSEEK_MODEL=${t}\n\n`}else if("kimi"===e){console.log("\n[36mKimi (月之暗面) 配置:[0m");const n=await question("API Key",getExistingValue("KIMI_API_KEY"),!0),e=await question("API URL",getExistingValue("KIMI_API_URL")||"https://api.moonshot.cn/v1/chat/completions"),t=await question("模型名称",getExistingValue("KIMI_MODEL")||"moonshot-v1-8k");o+=`KIMI_API_KEY=${n}\n`,o+=`KIMI_API_URL=${e}\n`,o+=`KIMI_MODEL=${t}\n\n`}else if("zhipu"===e){console.log("\n[36m智谱 GLM 配置:[0m");const n=await question("API Key",getExistingValue("ZHIPU_API_KEY"),!0),e=await question("API URL",getExistingValue("ZHIPU_API_URL")||"https://open.bigmodel.cn/api/paas/v4/chat/completions"),t=await question("模型名称",getExistingValue("ZHIPU_MODEL")||"glm-4-flash");o+=`ZHIPU_API_KEY=${n}\n`,o+=`ZHIPU_API_URL=${e}\n`,o+=`ZHIPU_MODEL=${t}\n\n`}else if("minimax"===e){console.log("\n[36mMiniMax 配置:[0m");const n=await question("API Key",getExistingValue("MINIMAX_API_KEY"),!0),e=await question("Group ID",getExistingValue("MINIMAX_GROUP_ID"),!0),t=await question("API URL",getExistingValue("MINIMAX_API_URL")||"https://api.minimax.chat/v1/chat/completions"),s=await question("模型名称",getExistingValue("MINIMAX_MODEL")||"abab6.5s-chat");o+=`MINIMAX_API_KEY=${n}\n`,o+=`MINIMAX_GROUP_ID=${e}\n`,o+=`MINIMAX_API_URL=${t}\n`,o+=`MINIMAX_MODEL=${s}\n\n`}console.log("[1m========== 飞书应用配置 ==========[0m\n");const t=await question("飞书 App ID",getExistingValue("FEISHU_APP_ID"),!0),s=await question("飞书 App Secret",getExistingValue("FEISHU_APP_SECRET"),!0),i=await question("飞书 Verification Token",getExistingValue("FEISHU_VERIFICATION_TOKEN"),!0),r=await question("飞书 Encrypt Key (可选)",getExistingValue("FEISHU_ENCRYPT_KEY")),a=await question("飞书默认 Chat ID (可选)",getExistingValue("FEISHU_DEFAULT_CHAT_ID"));o+=`FEISHU_APP_ID=${t}\n`,o+=`FEISHU_APP_SECRET=${s}\n`,o+=`FEISHU_VERIFICATION_TOKEN=${i}\n`,r&&(o+=`FEISHU_ENCRYPT_KEY=${r}\n`),a&&(o+=`FEISHU_DEFAULT_CHAT_ID=${a}\n\n`),console.log("[1m========== 服务配置 ==========[0m\n");const c=await question("服务端口",getExistingValue("PORT")||"3000");let l=await question("启用流式输出 (true/false)",getExistingValue("ENABLE_STREAM")||"true");for(;!["true","false"].includes(l.toLowerCase());)console.log("请输入 true 或 false"),l=await question("启用流式输出 (true/false)",getExistingValue("ENABLE_STREAM")||"true");o+=`PORT=${c}\n`,o+=`ENABLE_STREAM=${l}\n\n`,console.log("[1m========== 命令安全配置 ==========[0m\n");let I=await question("启用命令白名单 (true/false)",getExistingValue("COMMAND_WHITELIST_ENABLED")||"true");for(;!["true","false"].includes(I.toLowerCase());)console.log("请输入 true 或 false"),I=await question("启用命令白名单 (true/false)",getExistingValue("COMMAND_WHITELIST_ENABLED")||"true");o+=`COMMAND_WHITELIST_ENABLED=${I}\n`,o+="\n",console.log("[1m========== 数据库配置 ==========[0m\n");o+=`DATABASE_PATH=${await question("数据库路径",getExistingValue("DATABASE_PATH")||"./data/ai_agent.db")}\n\n`,console.log("[1m========== 浏览器配置 ==========[0m\n");o+=`BROWSER_EXECUTABLE_PATH=${await question("浏览器路径 (留空使用默认 Chrome)",getExistingValue("BROWSER_EXECUTABLE_PATH")||"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome")}\n`,fs.writeFileSync(ENV_FILE,o),console.log("\n[32m✅ 配置已保存到:[0m",ENV_FILE),console.log("\n现在可以使用 'inspiration start' 启动服务\n")}finally{e.close()}}const SOCKET_FILE=path.join(CONFIG_DIR,"inspiration.sock");function sendIPC(n){return new Promise((e,o)=>{if(!fs.existsSync(SOCKET_FILE))return void o(new Error("服务未运行"));const t=net.createConnection(SOCKET_FILE,()=>{t.write(JSON.stringify(n)+"\n")});let s="",i=!1;const r=setTimeout(()=>{i||(t.destroy(),o(new Error("连接超时")))},3e5);t.on("data",n=>{s+=n.toString()}),t.on("end",()=>{i=!0,clearTimeout(r);try{const n=JSON.parse(s.trim());e(n)}catch(n){o(new Error("解析响应失败"))}}),t.on("error",n=>{i=!0,clearTimeout(r),o(new Error(`连接失败: ${n.message}`))})})}async function runCli(){if(!getPid())return console.log("\n❌ inspiration-agent 未运行"),void console.log(" 请先使用 'inspiration start' 启动服务\n");console.log("\n[1m💬 inspiration-agent 命令行交互[0m"),console.log("输入消息与 AI 交互,输入 'exit' 或 'quit' 退出\n");const n=new(require("./utils/markdown-renderer")),e=readline.createInterface({input:process.stdin,output:process.stdout}),question=n=>new Promise(o=>{e.question(n,o)});try{for(;;){const o=await question("你: ");if(o.trim()){if("exit"===o.toLowerCase()||"quit"===o.toLowerCase()){console.log("\n再见!\n");break}if(o.startsWith("/"))try{const n=await sendIPC({type:"command",content:o});"response"===n.type?("object"==typeof n.content?console.log("\n"+JSON.stringify(n.content,null,2)+"\n"):console.log(`\n${n.content}\n`),"/shutdown"===o.trim().toLowerCase()&&(console.log("CLI 已关闭\n"),e.close(),process.exit(0))):"error"===n.type&&console.error(`\n错误: ${n.message}\n`)}catch(n){console.error(`\n错误: ${n.message}\n`)}else try{const e=await sendIPC({type:"message",content:o});if("response"===e.type){const o=n.render(e.content);console.log(`\nAI:\n${o}\n`)}else"error"===e.type&&console.error(`\n错误: ${e.message}\n`)}catch(n){console.error(`\n错误: ${n.message}\n`)}}}}finally{e.close()}}async function main(){const{command:n,flags:e}=parseArgs();switch(n){case"start":if(isRunning()){console.log("\n⚠️ inspiration-agent 已在运行中"),console.log(" 使用 'inspiration status' 查看状态\n");break}if(e.d||e.daemon){console.log("\n🚀 启动 inspiration-agent (后台模式)...");try{const n=await startDaemon();console.log(`✅ 服务已启动 (PID: ${n})\n`)}catch(n){console.error(`❌ 启动失败: ${n.message}\n`),process.exit(1)}}else console.log("\n🚀 启动 inspiration-agent...\n"),await startForeground();break;case"stop":await stopProcess()?console.log("\n✅ inspiration-agent 已停止\n"):console.log("\n⚠️ inspiration-agent 未运行\n");break;case"restart":if(console.log("\n🔄 重启 inspiration-agent..."),await stopProcess(),await new Promise(n=>setTimeout(n,1e3)),e.d||e.daemon){const n=await startDaemon();console.log(`✅ 服务已重启 (PID: ${n})\n`)}else await startForeground();break;case"status":showStatus(),console.log();break;case"version":case"-v":case"--version":showVersion();break;case"onboard":await runOnboard();break;case"cli":await runCli();break;default:showHelp()}}main().catch(n=>{console.error(`错误: ${n.message}`),process.exit(1)});
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const fs=require("fs"),path=require("path"),net=require("net"),os=require("os"),CONFIG_DIR=path.join(os.homedir(),".inspiration"),PID_FILE=path.join(CONFIG_DIR,"inspiration.pid"),SOCKET_FILE=path.join(CONFIG_DIR,"inspiration.sock");function savePid(){fs.writeFileSync(PID_FILE,String(process.pid))}function removePid(){fs.existsSync(PID_FILE)&&fs.unlinkSync(PID_FILE)}function removeSocket(){fs.existsSync(SOCKET_FILE)&&fs.unlinkSync(SOCKET_FILE)}process.on("SIGINT",()=>{removeSocket(),removePid(),process.exit(0)}),process.on("SIGTERM",()=>{removeSocket(),removePid(),process.exit(0)}),process.on("exit",()=>{removeSocket(),removePid()}),process.on("uncaughtException",e=>{console.error("未捕获的异常:",e.message),removeSocket(),removePid(),process.exit(1)});const envPath=path.join(CONFIG_DIR,".env");require("dotenv").config({path:envPath});const originalConsoleLog=console.log,originalConsoleInfo=console.info,originalConsoleWarn=console.warn,originalConsoleError=console.error,shouldIgnoreLog=e=>{const o=e.join(" ");return o.includes("[ws]")||o.includes("[info]:")||o.includes("[ws]")};console.log=(...e)=>{shouldIgnoreLog(e)||originalConsoleLog.apply(console,e)},console.info=(...e)=>{shouldIgnoreLog(e)||originalConsoleInfo.apply(console,e)},console.warn=(...e)=>{shouldIgnoreLog(e)||originalConsoleWarn.apply(console,e)},console.error=(...e)=>{shouldIgnoreLog(e)||originalConsoleError.apply(console,e)};const logger=require("./utils/logger"),AIAgent=require("./agent/ai.agent"),FeishuLongConnection=require("./channel/feishu-connection"),isDaemon=!process.stdin.isTTY;function createIPCServer(e){removeSocket();const o=net.createServer(o=>{let n="";o.on("data",async s=>{n+=s.toString();const r=n.split("\n");n=r.pop()||"";for(const n of r)if(n.trim())try{const s=JSON.parse(n);if("message"===s.type){e.enqueueMessage(s.content,"cli");try{await e.messageQueue.waitForIdle(3e5),e.lastProcessedResponse?o.end(JSON.stringify({type:"response",content:e.lastProcessedResponse})+"\n"):o.end(JSON.stringify({type:"response",content:"无响应"})+"\n")}catch(e){o.end(JSON.stringify({type:"error",message:e.message})+"\n")}}else if("command"===s.type){const n=await e.handleSpecialCommand(s.content,!0);o.end(JSON.stringify({type:"response",content:n||"未知命令"})+"\n")}}catch(e){o.end(JSON.stringify({type:"error",message:e.message})+"\n")}}),o.on("error",e=>{logger.error(`IPC 连接错误: ${e.message}`)})});o.listen(SOCKET_FILE,()=>{logger.info(`IPC 服务器已启动: ${SOCKET_FILE}`)}),o.on("error",e=>{logger.error(`IPC 服务器错误: ${e.message}`)})}async function main(){savePid(),logger.info(`服务启动,PID: ${process.pid},模式: ${isDaemon?"后台":"前台"}`);try{const e=new AIAgent;await e.initDatabase(),createIPCServer(e);const o=new FeishuLongConnection;await o.init(e),isDaemon?(logger.info("后台模式运行中,等待消息..."),await new Promise(()=>{})):await e.chatLoop()}catch(e){logger.error(`启动失败: ${e.message}`),removeSocket(),removePid(),process.exit(1)}}main();
|
|
2
|
+
const fs=require("fs"),path=require("path"),net=require("net"),os=require("os"),CONFIG_DIR=path.join(os.homedir(),".inspiration"),PID_FILE=path.join(CONFIG_DIR,"inspiration.pid"),SOCKET_FILE=path.join(CONFIG_DIR,"inspiration.sock");function ensureConfigDir(){try{fs.existsSync(CONFIG_DIR)||fs.mkdirSync(CONFIG_DIR,{recursive:!0});const e=path.join(CONFIG_DIR,".permission_test");fs.writeFileSync(e,"test"),fs.unlinkSync(e)}catch(e){console.log("\n[33m⚠️ 需要授权访问配置目录[0m\n"),console.log("系统需要访问以下目录来存储配置和日志:"),console.log(` ${CONFIG_DIR}\n`),console.log("请运行以下命令授权:"),console.log(`\n sudo chown -R $(whoami) "${CONFIG_DIR}"\n`),console.log("或者手动创建目录并授权:"),console.log(` mkdir -p "${CONFIG_DIR}"`),console.log(` chmod 755 "${CONFIG_DIR}"\n`),process.exit(1)}}function savePid(){fs.writeFileSync(PID_FILE,String(process.pid))}function removePid(){fs.existsSync(PID_FILE)&&fs.unlinkSync(PID_FILE)}function removeSocket(){fs.existsSync(SOCKET_FILE)&&fs.unlinkSync(SOCKET_FILE)}ensureConfigDir(),process.on("SIGINT",()=>{removeSocket(),removePid(),process.exit(0)}),process.on("SIGTERM",()=>{removeSocket(),removePid(),process.exit(0)}),process.on("exit",()=>{removeSocket(),removePid()}),process.on("uncaughtException",e=>{console.error("未捕获的异常:",e.message),removeSocket(),removePid(),process.exit(1)});const envPath=path.join(CONFIG_DIR,".env");require("dotenv").config({path:envPath});const originalConsoleLog=console.log,originalConsoleInfo=console.info,originalConsoleWarn=console.warn,originalConsoleError=console.error,shouldIgnoreLog=e=>{const o=e.join(" ");return o.includes("[ws]")||o.includes("[info]:")||o.includes("[ws]")};console.log=(...e)=>{shouldIgnoreLog(e)||originalConsoleLog.apply(console,e)},console.info=(...e)=>{shouldIgnoreLog(e)||originalConsoleInfo.apply(console,e)},console.warn=(...e)=>{shouldIgnoreLog(e)||originalConsoleWarn.apply(console,e)},console.error=(...e)=>{shouldIgnoreLog(e)||originalConsoleError.apply(console,e)};const logger=require("./utils/logger"),AIAgent=require("./agent/ai.agent"),FeishuLongConnection=require("./channel/feishu-connection"),isDaemon=!process.stdin.isTTY;function createIPCServer(e){removeSocket();const o=net.createServer(o=>{let n="";o.on("data",async s=>{n+=s.toString();const r=n.split("\n");n=r.pop()||"";for(const n of r)if(n.trim())try{const s=JSON.parse(n);if("message"===s.type){e.enqueueMessage(s.content,"cli");try{await e.messageQueue.waitForIdle(3e5),e.lastProcessedResponse?o.end(JSON.stringify({type:"response",content:e.lastProcessedResponse})+"\n"):o.end(JSON.stringify({type:"response",content:"无响应"})+"\n")}catch(e){o.end(JSON.stringify({type:"error",message:e.message})+"\n")}}else if("command"===s.type){const n=await e.handleSpecialCommand(s.content,!0);o.end(JSON.stringify({type:"response",content:n||"未知命令"})+"\n")}}catch(e){o.end(JSON.stringify({type:"error",message:e.message})+"\n")}}),o.on("error",e=>{logger.error(`IPC 连接错误: ${e.message}`)})});o.listen(SOCKET_FILE,()=>{logger.info(`IPC 服务器已启动: ${SOCKET_FILE}`)}),o.on("error",e=>{logger.error(`IPC 服务器错误: ${e.message}`)})}async function main(){savePid(),logger.info(`服务启动,PID: ${process.pid},模式: ${isDaemon?"后台":"前台"}`);try{const e=new AIAgent;await e.initDatabase(),createIPCServer(e);const o=new FeishuLongConnection;await o.init(e),isDaemon?(logger.info("后台模式运行中,等待消息..."),await new Promise(()=>{})):await e.chatLoop()}catch(e){logger.error(`启动失败: ${e.message}`),removeSocket(),removePid(),process.exit(1)}}main();
|