├── init.js ├── commands ├── do_nothing.js ├── task_complete.js ├── askai.js ├── subagent.js ├── index.js ├── summarize.js ├── scholar.js └── search.js ├── test ├── test_search.js ├── test_scholar.js ├── test_browse.js ├── test_agents.js └── test_summarize.js ├── server ├── msgBus.js ├── insider │ └── galanet │ │ ├── shutdown.js │ │ ├── shakehand.js │ │ └── message.js ├── websocket.js ├── center.js ├── personel.js ├── socket.js ├── subprocess.js └── web.js ├── core ├── connection │ ├── default.js │ ├── index.js │ ├── manifold.js │ ├── section.js │ └── bundle.js ├── events │ ├── eventloop.js │ ├── synclock.js │ ├── delaytrigger.js │ ├── channel.js │ ├── broadcast.js │ ├── pipe.js │ └── finiteStateMachine.js ├── threads │ ├── threadEvaluater.js │ ├── threadTunnel.js │ └── threadWorker.js ├── extends │ ├── math.js │ ├── class.js │ ├── symbol.js │ ├── string.js │ ├── timer.js │ ├── object.js │ └── function.js ├── extend.js ├── moduleManager.js ├── namespace.js ├── index.js ├── math │ └── vector.js ├── utils │ ├── health.js │ ├── datetime.js │ ├── loadall.js │ ├── version.js │ └── logger.js ├── fs │ └── prepare.js ├── datastore │ ├── lrucache.js │ └── ufcache.js └── commandline │ └── setConsoleStyle.js ├── package.json ├── ai ├── agent │ └── abstract.js ├── index.js └── agents.js ├── LICENSE ├── template.json ├── kernel ├── thread │ ├── tx_thread_pool.js │ ├── cm_thread_pool.js │ └── log.js ├── common.js ├── message.js ├── multihash.js ├── crypto.js ├── error.js ├── atom.js ├── log.js ├── watcher.js └── udp.js ├── README.md ├── prepare.js ├── .gitignore ├── prompts ├── claude-default.ini └── claude.ini ├── index.js └── server.js /init.js: -------------------------------------------------------------------------------- 1 | console.log('Initialized...'); -------------------------------------------------------------------------------- /commands/do_nothing.js: -------------------------------------------------------------------------------- 1 | const command = { 2 | "name": "Do Nothing", 3 | "cmd": "do_nothing" 4 | }; 5 | 6 | command.execute = (type, caller, reason) => { 7 | return { 8 | speak: "Nothing to do.", 9 | noReply: true 10 | }; 11 | }; 12 | 13 | module.exports = command; -------------------------------------------------------------------------------- /test/test_search.js: -------------------------------------------------------------------------------- 1 | require('../core'); 2 | const {prepareFolders} = require('../prepare'); 3 | const Search = require('../commands/search.js'); 4 | 5 | (async () => { 6 | await prepareFolders(); 7 | console.log('Google Searching...'); 8 | var result = await Search.execute('', '', {q: 'Nanotechnological Armor'}); 9 | console.log(result); 10 | }) (); -------------------------------------------------------------------------------- /server/msgBus.js: -------------------------------------------------------------------------------- 1 | const LRUCache = _('DataStore.LRUCache'); 2 | 3 | const messageHistory = new LRUCache(200); 4 | const MessageBus = { 5 | hasMsgRecord (id) { 6 | return messageHistory.has(id); 7 | }, 8 | addMsgRecord (id) { 9 | return messageHistory.set(id, true); 10 | } 11 | }; 12 | 13 | module.exports = MessageBus; 14 | _('Core.MessageBus', MessageBus); -------------------------------------------------------------------------------- /core/connection/default.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | sender: { 3 | limit: { 4 | connection: 100, 5 | contemporary: 10, 6 | }, 7 | retry: 5, 8 | timeout: [300, 500, 1000, 1500, 1500], 9 | blockSize: 4070, // FID(2)+SIG(1)+MID(15)+BCOUNT(4)+BINDEX(4)+DATA 10 | }, 11 | receiver: { 12 | connection: 200, 13 | retry: 3, 14 | timeout: 10000, 15 | } 16 | }; -------------------------------------------------------------------------------- /test/test_scholar.js: -------------------------------------------------------------------------------- 1 | require('../core'); 2 | const {prepareFolders} = require('../prepare'); 3 | const Scholar = require('../commands/scholar.js'); 4 | 5 | (async () => { 6 | await prepareFolders(); 7 | console.log('Google Schoar Searching...'); 8 | var result = await Scholar.execute('', '', {query: 'Nanotechnological Armor'}); 9 | console.log(result); 10 | }) (); -------------------------------------------------------------------------------- /core/events/eventloop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: 事件循环器 3 | * Desc: 以一定时间间隔触发响应事件,可用于批量化事件处理。 4 | * 没有事件时不工作,有事件后延时触发,并继续收集事件,等待下一次触发。 5 | * Author: LostAbaddon 6 | * Version: 0.0.1 7 | * Date: 2019.06.20 8 | */ 9 | 10 | const Trigger = require('./delaytrigger.js'); 11 | 12 | class EventLoop { 13 | constructor (duration) { 14 | } 15 | } 16 | 17 | exports.EventLoop = EventLoop; 18 | _('Events.EventLoop', EventLoop); -------------------------------------------------------------------------------- /server/insider/galanet/shutdown.js: -------------------------------------------------------------------------------- 1 | const responsor = (param, query, url, data, method, source, ip, port) => { 2 | setTimeout(() => { 3 | if (isSlaver) { 4 | process.send({ event: 'extinct' }); 5 | } 6 | else { 7 | require(require('path').join(process.cwd(), '../../responser')).extinct(); 8 | } 9 | }, 100); 10 | return { 11 | ok: true, 12 | data: 'EXSTINCTUS MUNDE' 13 | }; 14 | }; 15 | 16 | module.exports = { responsor }; -------------------------------------------------------------------------------- /core/threads/threadEvaluater.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Thread Evaluate Worker 3 | * Desc: 线程内辅助工具 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2018.11.04 7 | * 备注:未来可以用VM来取代eval 8 | */ 9 | 10 | register('init', (data, event) => { 11 | var result; 12 | try { 13 | result = eval(data.fun)(data.data); 14 | } 15 | catch (err) { 16 | request('evaluate', { err: err.toString(), result: null }); 17 | return; 18 | } 19 | request('evaluate', { err: null, result }); 20 | }); -------------------------------------------------------------------------------- /commands/task_complete.js: -------------------------------------------------------------------------------- 1 | const command = { 2 | "name": "Task Complete", 3 | "cmd": "task_complete", 4 | "alias": ["shutdown", "mission_complete"], 5 | "args": { 6 | "reason": "finalReply" 7 | }, 8 | "scope": ['main'] 9 | }; 10 | 11 | command.execute = (type, caller, reason) => { 12 | if (!reason || !reason.reason) reason = "Job Done."; 13 | else reason = reason.reason; 14 | return { 15 | speak: reason, 16 | reply: null, 17 | exit: true 18 | }; 19 | }; 20 | 21 | module.exports = command; -------------------------------------------------------------------------------- /core/extends/math.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Math Utils 3 | * Desc: Math 类拓展工具 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2017.11.09 7 | */ 8 | 9 | Math.pick = list => { 10 | if (Array.is(list)) { 11 | let i = Math.floor(Math.random() * list.length); 12 | return list[i]; 13 | } 14 | else if (!isNaN(list)) { 15 | return Math.random() <= list; 16 | } 17 | return null; 18 | }; 19 | Math.range = (l, r) => { 20 | if (isNaN(r)) { 21 | r = l; 22 | l = 0; 23 | } 24 | return l + Math.random() * (r - l); 25 | }; -------------------------------------------------------------------------------- /server/insider/galanet/shakehand.js: -------------------------------------------------------------------------------- 1 | const Galanet = require('../../galanet'); 2 | 3 | const responsor = (param, query, url, data, method, source, host, port) => { 4 | if (isSlaver) { 5 | setTimeout(() => { 6 | process.send({ 7 | event: "command", 8 | action: "command::request::shakehand", 9 | data: host 10 | }); 11 | }, 0); 12 | } 13 | else { 14 | setTimeout(() => { 15 | process.emit('command::request::shakehand', host); 16 | }, 0); 17 | } 18 | 19 | return { 20 | ok: true, 21 | data: Galanet.getNodeInfo() 22 | } 23 | }; 24 | 25 | module.exports = { 26 | responsor, 27 | methods: 'get', 28 | }; -------------------------------------------------------------------------------- /core/extend.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Auxillary Utils and Extends 3 | * Desc: 常用基础类拓展 4 | * Author: LostAbaddon 5 | * Version: 0.0.4 6 | * Date: 2019.06.03 7 | */ 8 | 9 | const Version = require('./utils/version'); 10 | 11 | if (global._env === 'node') { 12 | let ver = new Version(process.version); 13 | if (ver.isLessThan('10.5')) { 14 | global._canThread = false; 15 | } else if (ver.isLargerThan('12.0')) { 16 | global._canThread = true; 17 | } else if (process.execArgv.indexOf('--experimental-worker') >= 0) { 18 | global._canThread = true; 19 | } else { 20 | global._canThread = false; 21 | } 22 | } 23 | 24 | loadall(__dirname + '/extends/'); -------------------------------------------------------------------------------- /core/moduleManager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Module Manager 3 | * Desc: 模块管理 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2017.08.26 7 | */ 8 | 9 | const ModuleManager = {}; 10 | 11 | ModuleManager.dump = path => { 12 | path = require.resolve(path); 13 | var module = require.cache[path]; 14 | if (module.parent) { 15 | module.parent.children.splice(module.parent.children.indexOf(module), 1); 16 | } 17 | delete require.cache[path]; 18 | }; 19 | ModuleManager.reload = path => { 20 | ModuleManager.dump(path); 21 | return require(path); 22 | }; 23 | 24 | module.exports = ModuleManager; 25 | 26 | _('Utils.ModuleManager', ModuleManager); -------------------------------------------------------------------------------- /test/test_browse.js: -------------------------------------------------------------------------------- 1 | require('../core'); 2 | const {prepareFolders} = require('../prepare'); 3 | const Browse = require('../commands/browse.js'); 4 | 5 | (async () => { 6 | await prepareFolders(); 7 | console.log('Browsing web page...'); 8 | var result = await Browse.execute('', '', {url: 'https://www.jianshu.com/p/d633bb9bd463'}); 9 | // var result = await Browse.execute('', '', {url: 'https://www.zhihu.com/question/359948448/answer/1014333716'}); 10 | // var result = await Browse.execute('', '', {url: 'https://zhuanlan.zhihu.com/p/463715925'}); 11 | // var result = await Browse.execute('', '', {url: 'https://www.zhihu.com/collection/190519403'}); 12 | console.log(result); 13 | }) (); -------------------------------------------------------------------------------- /core/namespace.js: -------------------------------------------------------------------------------- 1 | const ModuleList = new Map; 2 | global._ = (path, mod) => { 3 | path = path.split(/[\/\\,\.\:;]/).map(p => p.trim()).filter(p => p.length > 0); 4 | if (path.length < 1) return global; 5 | path = path.join("/"); 6 | if (!!mod) { 7 | ModuleList.set(path, mod); 8 | return mod 9 | } 10 | mod = ModuleList.get(path); 11 | if (!!mod) return mod; 12 | var names = [...ModuleList.keys()]; 13 | names = names.filter(n => n.indexOf(path) === 0); 14 | mod = {}; 15 | names.forEach(n => { 16 | var m = ModuleList.get(n); 17 | n = n.replace(path + '/', ''); 18 | n = n.split('/'); 19 | var l = n.pop(); 20 | var t = mod; 21 | n.forEach(m => { 22 | if (!!t[m]) t[m] = {}; 23 | t = t[m]; 24 | }); 25 | t[l] = m; 26 | }); 27 | return mod; 28 | }; 29 | 30 | _('Utils', {}); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Agentverse", 3 | "version": "0.0.2", 4 | "author": { 5 | "name": "LostAbaddon", 6 | "email": "LostAbaddon@gmail.com" 7 | }, 8 | "description": "A multi-agent AI tool, based on Claude", 9 | "license": "MIT", 10 | "homepage": "https://github.com/LostAbaddon/Agentverse", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/LostAbaddon/Agentverse" 14 | }, 15 | "main": "src/index.js", 16 | "engines": { 17 | "node": ">=19.0.0" 18 | }, 19 | "contributors": [ 20 | "LostAbaddon " 21 | ], 22 | "dependencies": { 23 | "axios": "^0.18.0", 24 | "koa": "^2.11.0", 25 | "koa-body": "^4.1.1", 26 | "koa-static": "^5.0.0", 27 | "request": "^2.88.2", 28 | "socket.io": "^2.3.0", 29 | "socks-proxy-agent": "^7.0.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/test_agents.js: -------------------------------------------------------------------------------- 1 | require('../core'); 2 | const SocksProxyAgent = require('socks-proxy-agent'); 3 | const prepareSystem = require('../prepare'); 4 | const Commands = require('../commands'); 5 | const ClaudeAgent = require('../ai/agent/claude'); 6 | const SubAgent = require('../commands/subagent.js'); 7 | const config = require('../config.json'); 8 | 9 | (async () => { 10 | await prepareSystem.prepareFolders(); 11 | await Commands.loadCommands(); 12 | await ClaudeAgent.loadPrompt(); 13 | prepareSystem.prepareProxy(config); 14 | 15 | var claude = new ClaudeAgent('', config.setting.Claude); 16 | console.log('Creating Sub Agent...'); 17 | var result = await SubAgent.execute('', claude, { 18 | role: '', 19 | task: "How to build a Dyson Sphere?", 20 | use: false 21 | }); 22 | console.log(result); 23 | }) (); -------------------------------------------------------------------------------- /core/extends/class.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Class Utils 3 | * Desc: Class 类拓展工具 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2017.11.09 7 | */ 8 | 9 | global.ProtoType = Function.prototype; 10 | Object.defineProperty(ProtoType, 'kerneltype', { 11 | get () { 12 | if (this === ProtoType) return this; 13 | if (this.__proto__ === ProtoType) return this; 14 | if (!this.__proto__.kerneltype) return this; 15 | return this.__proto__.kerneltype; 16 | }, 17 | enumerable: false, 18 | configurable: false 19 | }); 20 | 21 | Object.prototype.isSubClassOf = function (target) { 22 | if (typeof this !== 'function') return false; 23 | var cls = this; 24 | while (!!cls) { 25 | if (cls === target) return true; 26 | cls = Object.getPrototypeOf(cls); 27 | } 28 | return false; 29 | }; 30 | Object.defineProperty(Object.prototype, 'isSubClassOf', { enumerable: false }); 31 | -------------------------------------------------------------------------------- /server/insider/galanet/message.js: -------------------------------------------------------------------------------- 1 | const Logger = new (_("Utils.Logger"))('MessageDealer'); 2 | var Galanet; 3 | 4 | const responsor = (param, query, url, data, method, protocol, host, port) => { 5 | if (global.isSlaver) { 6 | process.send({ 7 | event: 'galanet::message', 8 | type: param.type, 9 | sender: { protocol, host, port }, 10 | msg: param.data, 11 | }); 12 | } 13 | else { 14 | process.emit('galanet::message', param.type, { protocol, host, port }, param.data); 15 | } 16 | 17 | Galanet = Galanet || _('Core.Galanet'); 18 | if (param.type === 'broadcast' && !!param.toAll) { 19 | Galanet.broadcast(param.data, true, param.mid); 20 | } 21 | else if (param.type === 'narrowcast' && param.count > 0) { 22 | Galanet.narrowcast(param.data, param.count, param.mid); 23 | } 24 | 25 | return { 26 | ok: true, 27 | data: '' 28 | } 29 | }; 30 | 31 | module.exports = { 32 | responsor, 33 | methods: 'post', 34 | }; -------------------------------------------------------------------------------- /test/test_summarize.js: -------------------------------------------------------------------------------- 1 | require('../core'); 2 | const SocksProxyAgent = require('socks-proxy-agent'); 3 | const prepareSystem = require('../prepare'); 4 | const ClaudeAgent = require('../ai/agent/claude'); 5 | const Summarize = require('../commands/summarize.js'); 6 | const config = require('../config.json'); 7 | 8 | (async () => { 9 | await prepareSystem.prepareFolders(); 10 | await ClaudeAgent.loadPrompt(); 11 | prepareSystem.prepareProxy(config); 12 | 13 | var claude = new ClaudeAgent('', config.setting.Claude); 14 | console.log('Summarizing web page...'); 15 | var result = await Summarize.execute('', claude, {url: 'https://www.jianshu.com/p/d633bb9bd463'}); 16 | // var result = await Summarize.execute('', '', {url: 'https://www.zhihu.com/question/359948448/answer/1014333716'}); 17 | // var result = await Summarize.execute('', '', {url: 'https://zhuanlan.zhihu.com/p/463715925'}); 18 | // var result = await Summarize.execute('', '', {url: 'https://www.zhihu.com/collection/190519403'}); 19 | console.log(result); 20 | }) (); -------------------------------------------------------------------------------- /core/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Common Core 3 | * Desc: 辅助工具 4 | * Author: LostAbaddon 5 | * Version: 0.0.4 6 | * Date: 2019.06.03 7 | * 8 | * 热更新require库 9 | * 字符串拓展、随机穿 10 | * 日志工具 11 | * 文件夹生成 12 | * 辅助工具 13 | * Object的copy与extent功能 14 | */ 15 | 16 | try { 17 | if (!!window) { 18 | window.global = window; 19 | global._env = 'browser'; 20 | global.require = () => {}; 21 | } else { 22 | global._env = 'node'; 23 | } 24 | } catch (err) { 25 | global._env = 'node'; 26 | } 27 | 28 | if (!process.execArgv.includes('--expose-gc')) global.gc = () => {}; 29 | 30 | require('./namespace.js'); 31 | require('./utils/loadall'); 32 | require('./extend'); 33 | require('./utils/datetime'); 34 | require('./utils/logger'); 35 | 36 | require('./fs/prepare'); 37 | 38 | if (!global.noEventModules) { 39 | require('./events/synclock'); 40 | require('./events/eventManager'); 41 | if (global._canThread) { 42 | require('./events/channel'); 43 | require('./threads/threadManager'); 44 | } 45 | } 46 | 47 | require('./moduleManager'); -------------------------------------------------------------------------------- /core/connection/index.js: -------------------------------------------------------------------------------- 1 | const Config = require('./default.js'); 2 | const Manifold = require('./manifold'); 3 | const Section = require('./section.js'); 4 | 5 | // 服务端每协议、端口是一个独立的线程 6 | const createServer = (protocol, port, callback) => { 7 | var server = new Manifold(protocol, port); 8 | server.onReady(err => { 9 | callback(server, err); 10 | }); 11 | }; 12 | 13 | // 发送端由统一线程进行管理 14 | const createClient = (callback) => { 15 | Section.init(callback); 16 | }; 17 | 18 | const setConfig = cfg => { 19 | if (!cfg) return; 20 | var limit; 21 | 22 | if (!!cfg.sender) { 23 | limit = Config.sender.limit; 24 | Object.assign(Config.sender, cfg.sender); 25 | Config.sender.limit = Object.assign({}, limit, cfg.sender.limit); 26 | } 27 | 28 | if (!!cfg.receiver) { 29 | limit = Config.receiver.limit; 30 | Object.assign(Config.receiver, cfg.receiver); 31 | Config.receiver.limit = Object.assign({}, limit, cfg.receiver.limit); 32 | } 33 | }; 34 | 35 | module.exports = { 36 | server: createServer, 37 | client: createClient, 38 | setConfig 39 | }; -------------------------------------------------------------------------------- /ai/agent/abstract.js: -------------------------------------------------------------------------------- 1 | class AbstractAgent { 2 | static State = Symbol.set("IDLE", "STANDBY", "RUNNING", "WAITING"); 3 | state = AbstractAgent.State.IDLE; 4 | id = ''; 5 | 6 | constructor (id, config) { 7 | this.state = AbstractAgent.State.STANDBY; 8 | this.id = id; 9 | } 10 | 11 | loadKnowledge (filepath) {} // load knowledge from file 12 | getKnowledge () {} // get all knowledge 13 | addKnowledge (knowledge) {} // add new knowledge 14 | removeKnowledge (index) {} // remove knowledge 15 | 16 | loadMemory (filepath) {} // load memory from file 17 | getMemory () {} // get all memory 18 | addMemory (human, ai) {} // add a conversation to history 19 | 20 | send (prompt, heat, session) {} // send request to AI backend 21 | ask (prompt, heat) {} // continue the chat 22 | task (task) {} // complete the mission automonously 23 | action (action, option) {} // extra actions 24 | 25 | copy () {} // copy a whole new agent with initial setting 26 | fork () {} // copy an agent with current knowledge 27 | clone () {} // copy an agent with current knowledge and memory 28 | } 29 | 30 | module.exports = AbstractAgent; -------------------------------------------------------------------------------- /core/math/vector.js: -------------------------------------------------------------------------------- 1 | Math.Vector = {}; 2 | Math.Vector.normalize = vec => { 3 | var t = vec.reduce((r, n) => r + n * n, 0); 4 | t = Math.sqrt(t); 5 | t = 1 / t; 6 | return vec.map(n => n * t); 7 | }; 8 | Math.Vector.averagize = vec => { 9 | var t = vec.reduce((r, n) => r + n, 0); 10 | t = 1 / t; 11 | return vec.map(n => n * t); 12 | }; 13 | Math.Vector.times = (v, n) => { 14 | return v.map(i => i * n); 15 | }; 16 | 17 | Math.Matrix = {}; 18 | Math.Matrix.transpose = m => { 19 | var l = m.length; 20 | var n = 0; 21 | m.forEach(mm => { 22 | var j = mm.length; 23 | if (j > n) n = j; 24 | }); 25 | var result = Array.generate(n, () => Array.generate(l, 0)); 26 | for (let i = 0; i < l; i ++) { 27 | let mm = m[i] || []; 28 | for (let j = 0; j < n; j ++) { 29 | result[j][i] = mm[j] || 0; 30 | } 31 | } 32 | return result; 33 | }; 34 | Math.Matrix.actOnVector = (m, v) => { 35 | var result = [], l = m.length; 36 | for (let i = 0; i < l; i ++) { 37 | let r = 0, n = m[i]; 38 | if (!!n) for (let j = 0, k = n.length; j < k; j ++) { 39 | r += n[j] * (v[j] || 0); 40 | } 41 | result[i] = r; 42 | } 43 | return result; 44 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 LostAbaddon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /template.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Agentverse", 3 | "version": "0.0.2", 4 | "config": { 5 | "process": 2, 6 | "port": { 7 | "http": 80, 8 | "https": 443 9 | }, 10 | "certification": { 11 | "privateKey": "key-file-path", 12 | "certificate": "pem-file-path" 13 | }, 14 | "page": { 15 | "path": "./", 16 | "maxage": 60000 17 | }, 18 | "api": { 19 | "url": "/api", 20 | "local": "./api-folder" 21 | }, 22 | "log": { 23 | "output": "./logs" 24 | }, 25 | "init": "./init.js" 26 | }, 27 | "backend": { 28 | "port": { 29 | "http": 80, 30 | "https": 443 31 | }, 32 | "console": "/tmp/console.ipc" 33 | }, 34 | "proxy": { 35 | "socks": "some_proxy", 36 | "http": "some_proxy" 37 | }, 38 | "setting": { 39 | "Claude": { 40 | "key": "empty", 41 | "model": "claude-v1", 42 | "temperature": 1, 43 | "max_token": 1024, 44 | "interval": 300 45 | }, 46 | "retry": 3 47 | }, 48 | "agent": "Claude", 49 | "extensions": { 50 | "google_search": { 51 | "proxy": "some_proxy", 52 | "count": 10, 53 | "apikey": "google_apikey", 54 | "cx": "programmable_search_engine" 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /kernel/thread/tx_thread_pool.js: -------------------------------------------------------------------------------- 1 | global.thread = require('worker_threads'); 2 | 3 | // 设置环境变量 4 | global._env = 'node'; 5 | global._canThread = true; 6 | global.isSlaver = thread.workerData.isSlaver; 7 | global.isMultiProcess = thread.workerData.isMultiProcess; 8 | 9 | // 加载工具包 10 | require('../../core/namespace.js'); 11 | require('../../core/utils/loadall'); 12 | require('../../core/extend'); 13 | require('../../core/fs/prepare'); 14 | require('../log'); 15 | const Logger = new (_("Utils.Logger"))('TxThreadPool'); 16 | 17 | var responsor; 18 | try { 19 | responsor = require(thread.workerData.jsPath); 20 | } 21 | catch (err) { 22 | Logger.error('TTP线程加载业务模块(' + thread.workerData.jsPath + ')失败: ' + err.message); 23 | return; 24 | } 25 | if (!responsor || !responsor.responsor) return; 26 | responsor = responsor.responsor; 27 | 28 | thread.parentPort.on('message', async msg => { 29 | var result; 30 | try { 31 | result = await responsor(...(msg.task)); 32 | } 33 | catch (err) { 34 | result = { 35 | ok: false, 36 | code: err.code, 37 | message: err.message 38 | }; 39 | } 40 | thread.parentPort.postMessage({id: msg.id, result}); 41 | }); -------------------------------------------------------------------------------- /core/utils/health.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: System Health Utils 3 | * Desc: 系统运行状态检查 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2017.10.15 7 | */ 8 | 9 | var os = require('os'); 10 | 11 | const getCPUUsage = (duration, cb) => new Promise((resolve, reject) => { 12 | var last = process.cpuUsage(); 13 | var start = process.hrtime(); 14 | setTimeout(() => { 15 | var result = process.cpuUsage(last); 16 | var end = process.hrtime(start); 17 | var timespend = end[0] + end[1] / 1000000000; // nanosecond 18 | var user = result.user / 1000000, sys = result.system / 1000000; // microsecond 19 | result = {user, sys, total: timespend}; 20 | resolve(result); 21 | if (!!cb) cb(result); 22 | }, duration || 1000); 23 | }); 24 | 25 | var format = num => (Math.round(num * 10000) / 100) + '%'; 26 | var getHealth = async (duration, cb) => { 27 | var mem = process.memoryUsage(); 28 | var cpu = await getCPUUsage(duration); 29 | var result = { 30 | cpu: (cpu.user + cpu.sys) / cpu.total, 31 | mem: (mem.rss + mem.heapTotal + mem.external) / os.totalmem() 32 | }; 33 | if (!!cb) cb(result); 34 | return result; 35 | }; 36 | 37 | module.exports = getHealth; 38 | 39 | _('Utils').getHealth = getHealth; -------------------------------------------------------------------------------- /core/extends/symbol.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Symbol Utils 3 | * Desc: Symbol 类拓展工具 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2017.11.09 7 | */ 8 | 9 | Symbol.set = Symbol.setSymbols = function (host, symbols) { 10 | if (String.is(host)) { 11 | symbols = [].map.call(arguments, i => i); 12 | host = null; 13 | } 14 | else if (Array.is(host) && !symbols) { 15 | symbols = host; 16 | host = null; 17 | } 18 | host = host || {}; 19 | var symb2name = {}; 20 | var str2name = {}; 21 | symbols.forEach(symbol => { 22 | symbol = symbol.split('|'); 23 | if (symbol.length === 0) return; 24 | if (symbol.length < 2) symbol[1] = symbol[0]; 25 | var name = symbol[1]; 26 | symbol = symbol[0]; 27 | var sym = Symbol(symbol); 28 | symb2name[sym] = name; 29 | str2name[symbol] = name; 30 | Object.defineProperty(host, symbol, { 31 | value: sym, 32 | configurable: false, 33 | enumerable: true 34 | }); 35 | }); 36 | host.toString = symbol => symb2name[symbol] || str2name[symbol] || 'No Such Symbol'; 37 | Object.defineProperty(host, 'toString', { enumerable: false }); 38 | return host; 39 | }; 40 | Symbol.is = symbol => (Symbol.__proto__ === Symbol.prototype) || (typeof symbol === 'symbol'); -------------------------------------------------------------------------------- /core/fs/prepare.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Prepare Folder 3 | * Desc: 创建指定路径的文件夹 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2017.11.09 7 | */ 8 | 9 | const FS = require('fs'); 10 | const Path = require('path'); 11 | 12 | const preparePath = (path, cb) => new Promise(async res => { 13 | FS.access(path, (err) => { 14 | if (!err) { 15 | if (!!cb) cb(true); 16 | res(true); 17 | return; 18 | } 19 | var parent = Path.parse(path).dir; 20 | preparePath(parent, (result) => { 21 | if (!result) { 22 | if (!!cb) cb(false); 23 | res(false); 24 | return; 25 | } 26 | FS.mkdir(path, (err) => { 27 | if (!err) { 28 | if (!!cb) cb(true); 29 | res(true); 30 | return; 31 | } 32 | }); 33 | }); 34 | }); 35 | }); 36 | const preparePathSync = path => { 37 | var has; 38 | try { 39 | has = FS.accessSync(path); 40 | return true; 41 | } 42 | catch (err) {} 43 | var parent = Path.parse(path).dir; 44 | has = preparePathSync(parent); 45 | if (!has) return false; 46 | try { 47 | FS.mkdirSync(path); 48 | return true; 49 | } 50 | catch (err) { 51 | return false; 52 | } 53 | }; 54 | 55 | _("Utils").preparePath = preparePath; 56 | _("Utils").preparePathSync = preparePathSync; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Agentverse: the multiverse for AI-Agents 2 | 3 | This is an autonomous multi-agent AI platform, based on Claude. 4 | 5 | ## Slogan 6 | 7 | **Physics help us to know the universe. AI help us to simulate the universe!** 8 | 9 | ## Usage 10 | 11 | 1. Start AI daemon: `node index --daemon` 12 | 2. Ask AI: `node index ask "question"` 13 | 3. Task Mode: `node index task "task description" --max=max`, `max` for max loops that Agentverse can call the AI service. 14 | 4. Action Mode: `node index action act_name --option={opt1:val1;opt2:val2}`, to execute extra actions. 15 | 16 | ## Files and Folders 17 | 18 | ### ai/agent 19 | 20 | In `ai/agent` folder, there lies the AI agents, one file for one AI agent. 21 | 22 | ### commands 23 | 24 | In `commands` folder, there lies the extensions which the AI agent can use. 25 | 26 | ### action 27 | 28 | In `action` folder, there lies the extra actions for special jobs. 29 | 30 | ### prompts 31 | 32 | In `prompts` folder, there lies the prompts that every AI agent and role can use, file name format is `agentname-rolename.ini`. 33 | 34 | ## Vendor 35 | 36 | - Node.js 20.0.0 37 | - [jLAss](https://github.com/LostAbaddon/jLAss): my javascript lib. 38 | - [EmptyNodeProject](https://github.com/LostAbaddon/EmptyNodeProject): my nodejs backend architecture, based on jLAss. -------------------------------------------------------------------------------- /core/extends/string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: String Utils 3 | * Desc: String 类拓展工具 4 | * Author: LostAbaddon 5 | * Version: 0.0.2 6 | * Date: 2019.06.05 7 | */ 8 | 9 | const KeySet = []; 10 | (() => { 11 | for (let i = 0; i < 10; i ++) KeySet.push('' + i); 12 | for (let i = 65; i <= 90; i ++) KeySet.push(String.fromCharCode(i)); 13 | for (let i = 97; i <= 122; i ++) KeySet.push(String.fromCharCode(i)); 14 | }) (); 15 | String.random = (len) => { 16 | var rnd = ""; 17 | for (let i = 0; i < len; i ++) { 18 | rnd += KeySet[Math.floor(KeySet.length * Math.random())]; 19 | } 20 | return rnd; 21 | }; 22 | String.blank = (len, block = ' ') => { 23 | var line = ''; 24 | for (let i = 0; i < len; i ++) line += block; 25 | return line; 26 | }; 27 | String.is = (str) => { 28 | if (str instanceof String) return true; 29 | if (typeof str === 'string') return true; 30 | return false; 31 | }; 32 | String.prototype.copy = String.prototype.duplicate = function () { 33 | return String(this).toString(); 34 | }; 35 | Object.defineProperty(String.prototype, 'copy', { enumerable: false }); 36 | Object.defineProperty(String.prototype, 'duplicate', { enumerable: false }); 37 | 38 | if (global._env === "node") { 39 | Object.defineProperty(String.prototype, 'byteLength', { 40 | get () { 41 | var buf = Buffer.from(this, 'utf8'); 42 | return buf.byteLength; 43 | }, 44 | enumerable: false, 45 | configurable: false 46 | }); 47 | } -------------------------------------------------------------------------------- /core/connection/manifold.js: -------------------------------------------------------------------------------- 1 | // Author: LostAbaddon 2 | // Version: 0.1 3 | // Date: 2019.07.02 4 | // 5 | // 主线程中的管理接口,作为主线程与网络端口监听线程之间的桥梁 6 | // 每个Manifold与一个Unifold对应,管理一类协议-端口,在独立线程中运行。 7 | 8 | const Thread = require('worker_threads').Worker; 9 | const Config = require('./default.js'); 10 | 11 | class Manifold { 12 | #unifold; 13 | #alive = true; 14 | #status = 0; // 0:初始化;1:等待连接;2:闲置;3:工作中 15 | #protocol; 16 | #port = 0; 17 | #onReady = null; 18 | #onMsg = new Set(); 19 | constructor (protocol, port, title) { 20 | this.title = title; 21 | this.#protocol = protocol; 22 | if (!(port > 0)) port = 0; 23 | this.#port = port; 24 | 25 | this.#unifold = new Thread(__dirname + '/unifold.js', { workerData: { 26 | config: Config.receiver, 27 | protocol, port, title 28 | }}); 29 | this.#unifold.on('message', msg => { 30 | if (msg.type === 'init') { 31 | this.#status = 1; 32 | this.#port = msg.data.port; 33 | this.title = msg.data.title; 34 | if (!!this.#onReady) this.#onReady(msg.data.err || null); 35 | } else { 36 | let resp = (_msg) => { 37 | this.#unifold.postMessage({ 38 | remote: msg.data.sender, 39 | msg: _msg 40 | }); 41 | }; 42 | for (let cb of this.#onMsg) { 43 | if (Function.is(cb)) cb(msg.data, resp); 44 | } 45 | } 46 | }); 47 | } 48 | onReady (cb) { 49 | if (!this.#alive) return; 50 | this.#onReady = cb; 51 | if (this.#status > 0) cb(); 52 | } 53 | onMessage (cb) { 54 | this.#onMsg.add(cb); 55 | } 56 | get protocol () { 57 | return this.#protocol; 58 | } 59 | get port () { 60 | if (!this.#alive) return 0; 61 | return this.#port; 62 | } 63 | } 64 | 65 | module.exports = Manifold; -------------------------------------------------------------------------------- /core/connection/section.js: -------------------------------------------------------------------------------- 1 | // Author: LostAbaddon 2 | // Version: 0.1 3 | // Date: 2019.07.02 4 | // 5 | // 主线程中的管理接口,作为主线程与网络端口监听线程之间的桥梁 6 | // 每个Section与一个Bundle对应,管理所有网络通讯,主要工作是分配端口有会话管理 7 | // 整体结构: 8 | // - Section:主线程入口 9 | // - Bundle:发送线程管理入口,用户信道管理、会话管理和单向通讯 10 | // - Fiber:地址管理器(同一个address绑定一个fiber) 11 | // - Germ: 信道组管理器(同一个目标port+protocol绑定一个germ,一个germ可以开多个jet,端口紧张时自动销毁不用jet) 12 | // - Jet:通讯管理器(通讯信道,本地端口与目标端口保持绑定,用于传输数据,可以有多个jet指向同一个目标,但通讯端口不同) 13 | 14 | const Config = require('./default.js'); 15 | const Thread = require('worker_threads').Worker; 16 | 17 | const IDLength = 15; 18 | 19 | const Section = {}; 20 | var Bundle; 21 | 22 | const newTaskID = () => Buffer.from(new Uint8Array(Array.generate(IDLength, i => Math.floor(Math.random() * 256)))).toString('base64'); 23 | const TaskMap = new Map(); 24 | 25 | const onMessage = (event, data) => { 26 | if (event === 'task') { 27 | let tid = data.id; 28 | if (!tid) return; 29 | let task = TaskMap.get(tid); 30 | if (!task) return; 31 | TaskMap.delete(tid); 32 | task.callback(data); 33 | } else { 34 | console.log(event, data); 35 | } 36 | }; 37 | Section.init = (callback) => { 38 | Bundle = new Thread(__dirname + '/bundle.js', { workerData: { config: Config.sender } }); 39 | Bundle.on('message', (msg) => { 40 | if (msg.event === 'init') { 41 | callback(Section); 42 | } else { 43 | onMessage(msg.event, msg.data); 44 | } 45 | }); 46 | }; 47 | Section.sendMessage = promisify((address, port, protocol, msg, res) => { 48 | var task = { 49 | id: newTaskID(), 50 | task: { 51 | address, port, protocol, 52 | data: msg, 53 | timestamp: Date.now() 54 | }, 55 | callback: res 56 | }; 57 | TaskMap.set(task.id, task); 58 | Bundle.postMessage({ 59 | event: 'task', 60 | id: task.id, 61 | task: task.task 62 | }); 63 | }); 64 | 65 | module.exports = Section; -------------------------------------------------------------------------------- /core/events/synclock.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: SyncLock 3 | * Desc: 同步锁 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2019.09.16 7 | */ 8 | 9 | class SyncLock { 10 | #locked = false; 11 | #waiters = []; // 等待锁的人 12 | #followers = []; // 等待所有锁释放而不需要锁的人 13 | lock () { 14 | return new Promise(res => { 15 | if (!this.#locked) { 16 | this.#locked = true; 17 | return res(); 18 | } 19 | this.#waiters.push(res); 20 | }); 21 | } 22 | unlock (nextStep = false) { 23 | if (!this.#locked) return; 24 | if (this.#waiters.length === 0) { 25 | this.#locked = false; 26 | if (this.#followers.length > 0) { 27 | let foers = this.#followers; 28 | this.#followers = []; 29 | if (nextStep) setImmediate(() => { 30 | foers.forEach(cb => cb(true)); 31 | foers.splice(0, foers.length); 32 | foers = null; 33 | if (!this.#locked && this.#followers.length > 0) this.unlock(false); 34 | }); 35 | else { 36 | foers.forEach(cb => cb(true)); 37 | foers.splice(0, foers.length); 38 | foers = null; 39 | if (!this.#locked && this.#followers.length > 0) this.unlock(false); 40 | } 41 | } 42 | return; 43 | } 44 | var cb = this.#waiters.splice(0, 1)[0]; 45 | if (nextStep) setImmediate(() => { 46 | cb(); 47 | cb = null; 48 | }); 49 | else { 50 | cb(); 51 | cb = null; 52 | } 53 | } 54 | onUnlock (allDone = false) { 55 | return new Promise(res => { 56 | if (allDone) { 57 | if (this.#followers.length === 0) return res(false); 58 | } else { 59 | if (!this.#locked) return res(false); 60 | } 61 | this.#followers.push(res); 62 | }); 63 | } 64 | get locked () { 65 | return this.#locked; 66 | } 67 | get done () { 68 | return this.#waiters.length === 0; 69 | } 70 | get followers () { 71 | return this.#followers.length; 72 | } 73 | } 74 | 75 | module.exports = SyncLock; 76 | _('Events.SyncLock', SyncLock); -------------------------------------------------------------------------------- /core/events/delaytrigger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: 延时触发器 3 | * Desc: 等待指定时间来触发特定事件的触发器 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2019.06.20 7 | */ 8 | 9 | const symbLaunch = Symbol('launch'); // 伪私有方法名,只要它不暴露,外界就无法调用该名命名的方法 10 | const LifeCycle = Symbol.setSymbols("IDLE", "WAITING", "FINISHED") 11 | 12 | class DelayTrigger { 13 | #task = null; 14 | #delay = 0; 15 | #pump = 0; 16 | #trigger = null; 17 | #start = 0; 18 | #status = LifeCycle.IDLE; 19 | constructor (delay, pump, task) { 20 | if (Function.is(delay)) { 21 | this.#task = delay; 22 | this.#delay = 0; 23 | this.#pump = 0; 24 | } else if (Funcion.is(pump)) { 25 | this.#task = pump; 26 | if (!Number.is(delay) || delay < 0) delay = 0; 27 | this.#delay = delay; 28 | this.#pump = delay; 29 | } else if (!Function.is(task)) { 30 | return; 31 | } else { 32 | this.#task = task; 33 | if (!Number.is(delay) || delay < 0) delay = 0; 34 | this.#delay = delay; 35 | if (!Number.is(pump) || pump < 0) pump = 0; 36 | this.#pump = pump; 37 | } 38 | this.#start = now(); 39 | 40 | this.#status = LifeCycle.WAITING; 41 | this[symbLaunch](this.#delay); 42 | } 43 | [symbLaunch] (delay) { 44 | if (!!this.#trigger) clearTimeout(this.#trigger); 45 | this.#trigger = setTimeout(() => { 46 | this.#trigger = null; 47 | var end = now(); 48 | this.#status = LifeCycle.FINISHED; 49 | this.#task(end - this.#start); 50 | }, delay); 51 | } 52 | delay (delay) { 53 | if (this.#status !== LifeCycle.WAITING) return; 54 | if (Number.is(delay) && delay >= 0) this[symbLaunch](delay); 55 | else this[symbLaunch](this.#pump); 56 | } 57 | finish () { 58 | if (this.#status !== LifeCycle.WAITING) return; 59 | if (!!this.#trigger) { 60 | clearTimeout(this.#trigger); 61 | this.#trigger = null; 62 | } 63 | var end = now(); 64 | this.#status = LifeCycle.FINISHED; 65 | this.#task(end - this.#start); 66 | } 67 | } 68 | 69 | exports.DelayTrigger = DelayTrigger; 70 | _('Events.DelayTrigger', DelayTrigger); -------------------------------------------------------------------------------- /prepare.js: -------------------------------------------------------------------------------- 1 | const { join } = require('node:path'); 2 | const preparePath = _("Utils").preparePath; 3 | const SocksProxyAgent = require('socks-proxy-agent'); 4 | const Axios = require('axios'); 5 | const Logger = _("Utils.Logger"); 6 | const logger = new Logger('Prepare'); 7 | 8 | const prepareFolders = async () => { 9 | await preparePath(join(process.cwd(), 'out')); 10 | await preparePath(join(process.cwd(), 'out', 'log')); 11 | await preparePath(join(process.cwd(), 'out', 'search')); 12 | await preparePath(join(process.cwd(), 'out', 'scholar')); 13 | await preparePath(join(process.cwd(), 'out', 'browse')); 14 | await preparePath(join(process.cwd(), 'out', 'summarize')); 15 | }; 16 | 17 | const prepareProxy = (config) => { 18 | global.DefaultOptions = { 19 | method: 'GET', 20 | timeout: 30000, 21 | headers: { 22 | 'Accept': 'text/html,application/xhtml+xml,application/xml', 23 | 'Accept-Language': 'en', 24 | 'Cache-Control': 'max-age=0', 25 | // 'Connection': 'keep-alive', 26 | 'DNT': 1 27 | } 28 | }; 29 | if (!!config.proxy?.http) { 30 | DefaultOptions.proxy = config.proxy.http; 31 | } 32 | if (!!config.proxy?.socks) { 33 | try { 34 | global.globalHTTPSProxy = new SocksProxyAgent.SocksProxyAgent(config.proxy.socks); 35 | // global.globalHTTPSProxy.keepAlive = true; 36 | // global.globalHTTPSProxy.keepAliveMsecs = 1000; 37 | // global.globalHTTPSProxy.scheduling = 'fifo'; 38 | global.globalHTTPSProxy.options = { 39 | // keepAlive: true, 40 | // scheduling: 'fifo', 41 | timeout: 5000, 42 | // timeout: 2 * 60 * 1000, 43 | // keepAliveTimeout: 5000, 44 | // maxHeadersCount: null, 45 | // headersTimeout: 40 * 1000, 46 | noDelay: true 47 | }; 48 | global.globalHTTPSProxy.on('error', (err, req, res) => { 49 | logger.error('Global Proxy Error: ' + (err.message || err.msg || err)); 50 | res.end(); 51 | }); 52 | } 53 | catch { 54 | global.globalHTTPSProxy = null; 55 | } 56 | } 57 | Axios.defaults.timeout = 2 * 60 * 1000; 58 | }; 59 | 60 | module.exports = { 61 | prepareFolders, 62 | prepareProxy 63 | }; -------------------------------------------------------------------------------- /core/datastore/lrucache.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: LRU Cache 3 | * Desc: 基于Map的最近使用缓存 4 | * Author: LostAbaddon 5 | * Version: 0.0.4 6 | * Date: 2019.06.22 7 | */ 8 | 9 | const SymbUpdate = Symbol('update'); 10 | const DefaultSize = 100; 11 | 12 | class LRUCache { 13 | #cache; 14 | #secondary; 15 | #limit; 16 | #length; 17 | constructor (limit=DefaultSize) { 18 | this.#cache = new Map(); 19 | this.#secondary = new Map(); 20 | this.#limit = Number.is(limit) ? limit : DefaultSize; 21 | this.#length = 0; 22 | } 23 | set (k, v) { 24 | const has = this.#cache.has(k); 25 | if (!has) this[SymbUpdate](k, v); 26 | else this.#cache.set(k, v); 27 | } 28 | get (k) { 29 | var v = this.#cache.get(k); 30 | if (v !== undefined) return v; 31 | v = this.#secondary.get(k); 32 | if (v !== undefined) this[SymbUpdate](k, v); 33 | return v; 34 | } 35 | del (k) { 36 | const has = this.#cache.has(k); 37 | if (has) this.#length --; 38 | this.#cache.delete(k); 39 | this.#secondary.delete(k); 40 | } 41 | has (k) { 42 | return this.#cache.has(k) || this.#secondary.has(k); 43 | } 44 | clear () { 45 | this.#cache.clear(); 46 | this.#secondary.clear(); 47 | this.#length = 0; 48 | } 49 | [SymbUpdate] (k, v) { 50 | this.#length ++; 51 | if (this.#length >= this.#limit) { 52 | this.#secondary = this.#cache; 53 | this.#cache = new Map(); 54 | this.#length = 0; 55 | } 56 | this.#cache.set(k, v); 57 | } 58 | } 59 | 60 | class LRUCacheWithDatastore extends LRUCache { 61 | #ds; 62 | constructor (limit=DefaultSize, ds) { 63 | super(limit); 64 | this.#ds = ds || new Map(); 65 | } 66 | set (k, v) { 67 | super.set(k, v); 68 | this.#ds.set(k, v); 69 | } 70 | get (k) { 71 | var v = super.get(k); 72 | if (v === undefined) { 73 | v = this.#ds.get(k); 74 | if (v !== undefined) this[SymbUpdate](k, v); 75 | } 76 | return v; 77 | } 78 | del (k) { 79 | super.del(k); 80 | this.#ds.delete(k); 81 | } 82 | has (k) { 83 | return this.#ds.has(k); 84 | } 85 | clear () { 86 | super.clear(); 87 | this.#ds.clear(); 88 | } 89 | } 90 | 91 | LRUCache.withDatastore = LRUCacheWithDatastore; 92 | 93 | module.exports = LRUCache; 94 | _('DataStore.LRUCache', LRUCache); -------------------------------------------------------------------------------- /commands/askai.js: -------------------------------------------------------------------------------- 1 | const Commands = require('../commands'); 2 | const config = require('../config.json'); 3 | 4 | const command = { 5 | "name": "Think deeply", 6 | "cmd": "think", 7 | "alias": ['thought', 'conceive', 'envisage', 'envision', 'imagine', 'conside', 'contemplate', 'deliberate', 'ask_self', 'askself'], 8 | "args": { 9 | "problem": "problem", 10 | } 11 | }; 12 | const DefaultPrompt = "Your task is to provide a step-by-step solution to the given task. Please identify the task and provide a clear and concise breakdown of the steps needed to solve it. Your solution strategy should be well-defined and easy to follow, providing a clear path to the solution of the problem. Finally, please provide a complete solution to the problem, demonstrating your understanding of the necessary steps and how they lead to the solution.\n\nPlease note that your response should be flexible enough to allow for various relevant and creative approaches to solving the problem, while maintaining a clear structure and focus on accuracy.\n\nTask:\n"; 13 | 14 | command.execute = async (type, caller, target) => { 15 | var retryMax = config.setting?.retry || 1; 16 | if (!(retryMax > 1)) retryMax = 1; 17 | 18 | var problem, prepare = "Think something interesting."; 19 | for (let key in target) { 20 | let value = target[key]; 21 | prepare = value; 22 | if (!!key.match(/\b(task|mission|job|work|action)s?\b/i)) { 23 | problem = value; 24 | } 25 | } 26 | if (!problem) problem = prepare; 27 | 28 | var prompt = DefaultPrompt + problem; 29 | var ai = caller.copy(); 30 | 31 | var result; 32 | for (let i = retryMax; i > 0; i --) { 33 | try { 34 | result = await ai.send(prompt, 1.0, false); 35 | return { 36 | speak: `Deeply thought got result:\n${result}`, 37 | reply: result, 38 | exit: false 39 | }; 40 | } 41 | catch (err) { 42 | let msg = err.message || err.msg || err; 43 | console.error(`Deeply thougnt error: ${msg}`) 44 | if (i > 1) { 45 | await wait(1000); 46 | console.error(`Deeply thougnt retry...`); 47 | continue; 48 | } 49 | return { 50 | speak: `Deeply thougnt error: ${msg}`, 51 | reply: "failed", 52 | exit: false 53 | }; 54 | } 55 | } 56 | }; 57 | 58 | module.exports = command; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | package-lock.json 107 | config.json 108 | personel.json 109 | knowledge 110 | memory 111 | out -------------------------------------------------------------------------------- /core/utils/datetime.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Auxillary Utils and Extends for DateTime 3 | * Desc: 日期时间相关拓展 4 | * Author: LostAbaddon 5 | * Version: 0.0.2 6 | * Date: 2018.11.02 7 | */ 8 | 9 | const getDTMatch = (format, match, lim, def) => { 10 | if (isNaN(def)) def = lim; 11 | var temp = format.match(match); 12 | if (!temp) temp = def; 13 | else temp = temp.length; 14 | if (temp < lim) temp = lim; 15 | return temp; 16 | }; 17 | const formatString = (str, len) => { 18 | if (len === 0) return ''; 19 | var l = str.length; 20 | if (l > len) str = str.substring(l - len, l); 21 | else if (l < len) str = str.padStart(len, '0'); 22 | return str; 23 | }; 24 | const getDateString = (Y, M, D, link) => { 25 | var temp = []; 26 | if (Y.length > 0) temp.push(Y); 27 | if (M.length > 0) temp.push(M); 28 | if (D.length > 0) temp.push(D); 29 | return temp.join(link); 30 | }; 31 | const getTimeString = (h, m, s, ms, link) => { 32 | var temp = []; 33 | if (h.length > 0) temp.push(h); 34 | if (m.length > 0) temp.push(m); 35 | if (s.length > 0) temp.push(s); 36 | var result = temp.join(link); 37 | if (ms.length > 0) result += '.' + ms; 38 | return result; 39 | }; 40 | const timeNormalize = (time, format='YYYYMMDDhhmmss', datelink='/', timelink=':', combinelink=' ') => { 41 | time = time || new Date(); 42 | // format = format || 'YYYYMMDDhhmmssx'; 43 | 44 | var Ys = getDTMatch(format, /Y/g, 0, 0); 45 | var Ms = getDTMatch(format, /M/g, 1, 0); 46 | var Ds = getDTMatch(format, /D/g, 1, 0); 47 | var hs = getDTMatch(format, /h/g, 0, 0); 48 | var mms = getDTMatch(format, /m/g, 0, 0); 49 | var ss = getDTMatch(format, /s/g, 0, 0); 50 | var mss = getDTMatch(format, /x/g, 0); 51 | 52 | var Y = formatString(time.getYear() + 1900 + '', Ys); 53 | var M = formatString(time.getMonth() + 1 + '', Ms); 54 | var D = formatString(time.getDate() + '', Ds); 55 | var h = formatString(time.getHours() + '', hs); 56 | var m = formatString(time.getMinutes() + '', mms); 57 | var s = formatString(time.getSeconds() + '', ss); 58 | var ms = formatString(time.getMilliseconds() + '', mss); 59 | 60 | var sDate = getDateString(Y, M, D, datelink); 61 | var sTime = getTimeString(h, m, s, ms, timelink); 62 | if (sTime.length === 0) return sDate; 63 | return sDate + combinelink + sTime; 64 | }; 65 | 66 | _('Utils').getTimeString = timeNormalize; -------------------------------------------------------------------------------- /kernel/thread/cm_thread_pool.js: -------------------------------------------------------------------------------- 1 | global.thread = require('worker_threads'); 2 | 3 | // 设置环境变量 4 | global._env = 'node'; 5 | global._canThread = true; 6 | global.isSlaver = thread.workerData.isSlaver; 7 | global.isMultiProcess = thread.workerData.isMultiProcess; 8 | 9 | // 加载工具包 10 | require('../../core/namespace.js'); 11 | require('../../core/utils/loadall'); 12 | require('../../core/extend'); 13 | require('../../core/fs/prepare'); 14 | require('../../kernel/error'); 15 | require('../log'); 16 | const ModuleManager = require('../../core/moduleManager'); 17 | const Logger = new (_("Utils.Logger"))('TxThreadPool'); 18 | const responsors = new Map(); 19 | 20 | const removeModule = (url, filepath) => { 21 | var resp = responsors.get(url); 22 | if (!resp) return; 23 | responsors.delete(url); 24 | ModuleManager.dump(filepath); 25 | }; 26 | const addModule = (url, filepath) => { 27 | let resp; 28 | try { 29 | resp = require(filepath); 30 | } 31 | catch (err) { 32 | resp = null; 33 | Logger.error('CTP线程加载业务模块(' + filepath + ')失败: ' + err.message); 34 | return false; 35 | } 36 | if (!!resp && resp.responsor) { 37 | responsors.set(url, resp.responsor); 38 | Logger.info('CTP线程加载业务模块(' + filepath + ')成功'); 39 | return true; 40 | } 41 | return false; 42 | }; 43 | 44 | if (!!thread.workerData.jsModule.url && !!thread.workerData.jsModule.filepath) { 45 | addModule(thread.workerData.jsModule.url, thread.workerData.jsModule.filepath); 46 | } 47 | 48 | thread.parentPort.on('message', async msg => { 49 | if (msg.action === "changeModule") { 50 | if (!!msg.url && !!msg.filepath) { 51 | removeModule(msg.url, msg.filepath); 52 | addModule(msg.url, msg.filepath); 53 | } 54 | } 55 | else if (msg.action === "removeModule") { 56 | if (!!msg.url && !!msg.filepath) { 57 | removeModule(msg.url, msg.filepath); 58 | } 59 | } 60 | else { 61 | let result, responsor = responsors.get(msg.url); 62 | if (!responsor) { 63 | let err = new Errors.RuntimeError.ResponsorModuleMissing('CTP业务路径: ' + msg.url); 64 | result = { 65 | ok: false, 66 | code: err.code, 67 | message: err.message 68 | }; 69 | } 70 | else { 71 | try { 72 | result = await responsor(...(msg.task)); 73 | } 74 | catch (err) { 75 | result = { 76 | ok: false, 77 | code: err.code, 78 | message: err.message 79 | }; 80 | } 81 | } 82 | thread.parentPort.postMessage({id: msg.id, result}); 83 | } 84 | }); -------------------------------------------------------------------------------- /server/websocket.js: -------------------------------------------------------------------------------- 1 | // WebSocket 事件的处理分两部分: 2 | // 一部分是 Responsor 中注册的处理回调 3 | // 另一部分是通过 Regiester 机制注册的监听者 4 | 5 | const EventEmitter = require('events'); 6 | const IO = require('socket.io'); 7 | const ResponsorManager = require('./responser'); 8 | const Logger = new (_("Utils.Logger"))('WebSocket'); 9 | 10 | var io; 11 | var eventLoop = new EventEmitter(); 12 | var sockets = []; 13 | 14 | const init = (server) => { 15 | io = new IO(server); 16 | 17 | io.on('connection', socket => { 18 | sockets.push(socket); 19 | socket.on('disconnect', () => { 20 | var idx = sockets.indexOf(socket); 21 | if (idx >= 0) sockets.splice(idx, 1); 22 | eventLoop.emit('disconnected', null, socket); 23 | }); 24 | socket.on('__message__', async msg => { 25 | var event = msg.event, data = msg.data, action = msg.action || 'get'; 26 | if (Object.isBasicType(data)) data = {content: data}; 27 | var tid = -1; 28 | if (!!msg.id) tid = msg.id; 29 | var [res, query] = ResponsorManager.match(event, action, 'socket'); 30 | if (!!res) { 31 | let result = null; 32 | try { 33 | let remoteIP = socket.request.connection.remoteAddress; 34 | if (!!remoteIP.match(/::ffff:(\d+\.\d+\.\d+\.\d+)/)) remoteIP = remoteIP.replace('::ffff:', ''); 35 | result = await ResponsorManager.launch(res, data, query, event, socket, action, 'socket', remoteIP, 0); 36 | socket.send(tid, event, result); 37 | } 38 | catch (err) { 39 | socket.send(tid, event, { 40 | ok: false, 41 | code: err.code || 500, 42 | message: err.message 43 | }); 44 | Logger.error(err); 45 | } 46 | } 47 | 48 | if (!!eventLoop.eventNames().includes(event)) { 49 | eventLoop.emit(event, data, socket, msg); 50 | } 51 | else if (!res) { 52 | socket.send(tid, event, null, 'Non-Listener Request'); 53 | } 54 | }); 55 | socket.send = (id, event, data, err) => { 56 | socket.emit('__message__', { id, event, data, err }); 57 | }; 58 | eventLoop.emit('connected', null, socket); 59 | }); 60 | }; 61 | 62 | const register = (event, responser) => { 63 | eventLoop.on(event, responser); 64 | }; 65 | const unregister = (event, responser) => { 66 | eventLoop.off(event, responser); 67 | }; 68 | const broadcast = (event, data) => { 69 | sockets.forEach(socket => { 70 | if (!socket) return; 71 | socket.send(-1, event, data); 72 | }); 73 | }; 74 | 75 | module.exports = { 76 | init, 77 | register, 78 | unregister, 79 | broadcast, 80 | get io () { 81 | return io 82 | } 83 | }; -------------------------------------------------------------------------------- /kernel/thread/log.js: -------------------------------------------------------------------------------- 1 | const Path = require('path'); 2 | const FSP = require('fs').promises; 3 | 4 | global.thread = require('worker_threads'); 5 | global._env = 'node'; 6 | global._canThread = true; 7 | global.isSlaver = thread.workerData.isSlaver; 8 | global.isMultiProcess = thread.workerData.isMultiProcess; 9 | 10 | // 加载工具包 11 | require('../../core/namespace.js'); 12 | require('../../core/utils/loadall'); 13 | require('../../core/extend'); 14 | require('../../core/fs/prepare'); 15 | require('../log'); 16 | 17 | const Logger = new (_("Utils.Logger"))('ThreadLogger'); 18 | const history = []; 19 | var isReady = false; 20 | 21 | const timer = setInterval(async () => { 22 | if (!isReady) return; 23 | if (history.length === 0) return; 24 | 25 | var infos = [], logs = [], warns = [], errors = [], left = [], time = Date.now() - 1000, output = false; 26 | history.forEach(h => { 27 | if (h.stamp < time) { 28 | output = true; 29 | if (h.type === 0) infos.push(h); 30 | else if (h.type === 1) logs.push(h); 31 | else if (h.type === 2) warns.push(h); 32 | else if (h.type === 3) errors.push(h); 33 | } 34 | else { 35 | left.push(h); 36 | } 37 | }); 38 | if (!output) return; 39 | 40 | isReady = false; 41 | history.splice(0, history.length); 42 | history.push(...left); 43 | 44 | time = getDate(); 45 | var actions = [ 46 | [infos, time, 'info'], 47 | [logs, time, 'log'], 48 | [warns, time, 'warn'], 49 | [errors, time, 'error'] 50 | ]; 51 | await Promise.all(actions.map(act => outputLog(...act))); 52 | isReady = true; 53 | }, thread.workerData.duration); 54 | 55 | const getDate = () => { 56 | var date = new Date(); 57 | var Y = date.getYear() + 1900; 58 | var M = date.getMonth() + 1; 59 | M = M + ''; 60 | M = M.padStart(2, '0'); 61 | var D = date.getDate(); 62 | D = D + ''; 63 | D = D.padStart(2, '0'); 64 | return Y + '-' + M + '-' + D; 65 | } 66 | const outputLog = async (history, dateName, filename) => { 67 | if (history.length === 0) return; 68 | history.sort((a, b) => a.stamp - b.stamp); 69 | var content = history.map(item => item.msg).join('\n') + '\n'; 70 | filename = dateName + '-' + filename + '.log'; 71 | filename = Path.join(thread.workerData.output, filename); 72 | var err = await FSP.appendFile(filename, content); 73 | if (!!err) Logger.error(err); 74 | }; 75 | 76 | _("Utils").preparePath(thread.workerData.output, ok => { 77 | isReady = true; 78 | }); 79 | 80 | thread.parentPort.on('message', msg => { 81 | msg.forEach(m => { 82 | history.push(m); 83 | }); 84 | }); -------------------------------------------------------------------------------- /commands/subagent.js: -------------------------------------------------------------------------------- 1 | const Commands = require('../commands'); 2 | const config = require('../config.json'); 3 | 4 | const command = { 5 | "name": "Create AI-Agent", 6 | "cmd": "create_agent", 7 | "alias": ['new_agent', 'sub_agent', 'new_ai', 'newagent', 'subagent', 'newai'], 8 | "args": { 9 | "role": "role", 10 | "task": "task", 11 | "useCommands": "yesOrNo" 12 | }, 13 | "scope": ['main'] 14 | }; 15 | 16 | command.execute = async (type, caller, target) => { 17 | var retryMax = config.setting?.retry || 1; 18 | if (!(retryMax > 1)) retryMax = 1; 19 | 20 | var action = {}, prepare = { 21 | task: "Think something interesting.", 22 | useCommands: false 23 | }; 24 | for (let key in target) { 25 | let value = target[key]; 26 | let low = (value + '').toLowerCase(); 27 | if (!!low.match(/true|false|yes|no|use|not?[ _]*use/i)) { 28 | prepare.useCommands = !low.match(/false|no|not?[ _]*use/i); 29 | } 30 | else { 31 | prepare.task = value; 32 | } 33 | if (!!key.match(/\b(task|mission|job|work|action)s?\b/i)) { 34 | action.task = value; 35 | } 36 | else if (!!key.match(/\b(use|commands?|usecommands?)\b/i)) { 37 | action.useCommands = !low.match(/false|no|not?[ _]*use/i); 38 | } 39 | else { 40 | action.role = value; 41 | } 42 | } 43 | action = Object.assign({}, prepare, action); 44 | action.useCommands = false; // test 45 | 46 | var prompt = [], idx = caller.agents.length + 1; 47 | prompt.push(`Your name is agent-${idx}.`); 48 | if (!!action.role) { 49 | prompt.push(`From now on, you are ${action.role}.`); 50 | } 51 | prompt.push('Task:\n'); 52 | prompt.push(action.task); 53 | if (action.useCommands) { 54 | let p = 'Commands you can use:\n' + Commands.generateCommands('sub'); 55 | prompt.push(p); 56 | } 57 | prompt = prompt.join('\n\n'); 58 | 59 | var ai = caller.copy(); 60 | caller.agents.push(ai); 61 | 62 | var result; 63 | for (let i = retryMax; i > 0; i --) { 64 | try { 65 | result = await ai.send(prompt, 1.0, false); 66 | return { 67 | speak: `Sub Agent-${idx} finished the task with reply:\n${result}`, 68 | reply: `Sub Agent-${idx} finished the task:\n\n${result}`, 69 | exit: false 70 | }; 71 | } 72 | catch (err) { 73 | let msg = err.message || err.msg || err; 74 | console.error(`Sub Agent-${idx} error: ${msg}`) 75 | if (i > 1) { 76 | await wait(1000); 77 | console.error(`Sub Agent-${idx} retry...`); 78 | continue; 79 | } 80 | return { 81 | speak: `Sub Agent-${idx} error: ${msg}`, 82 | reply: "failed", 83 | exit: false 84 | }; 85 | } 86 | } 87 | }; 88 | 89 | module.exports = command; -------------------------------------------------------------------------------- /kernel/common.js: -------------------------------------------------------------------------------- 1 | const FS = require('fs'); 2 | const Path = require('path'); 3 | 4 | const getAllContents = path => new Promise(res => { 5 | var subs = []; 6 | FS.readdir(path, (err, list) => { 7 | if (!!err || !list || !list.length) { 8 | res(subs); 9 | return; 10 | } 11 | var count = list.length; 12 | if (count === 0) return res(subs); 13 | 14 | list.forEach(sub => { 15 | sub = Path.join(path, sub); 16 | FS.stat(sub, async (err, stat) => { 17 | if (!err && !!stat) { 18 | if (stat.isDirectory()) { 19 | sub = await getAllContents(sub); 20 | subs.push(...sub); 21 | } 22 | else if (stat.isFile()) { 23 | subs.push(sub); 24 | } 25 | } 26 | 27 | count --; 28 | if (count === 0) res(subs); 29 | }) 30 | }); 31 | }); 32 | }); 33 | const getAllSubFolders = path => new Promise(res => { 34 | var count = 0; 35 | var subs = []; 36 | FS.readdir(path, (err, list) => { 37 | if (!!err || !list || !list.length) { 38 | res(subs); 39 | return; 40 | } 41 | count = list.length; 42 | list.forEach(sub => { 43 | sub = Path.join(path, sub); 44 | FS.stat(sub, (err, stat) => { 45 | count --; 46 | if (!err && !!stat && stat.isDirectory()) { 47 | subs.push(sub); 48 | } 49 | if (count === 0) res(subs); 50 | }) 51 | }); 52 | }); 53 | }); 54 | const getJSON = path => new Promise(res => { 55 | FS.readFile(path, 'utf8', (err, data) => { 56 | if (!!err || !data) { 57 | res({}); 58 | return; 59 | } 60 | try { 61 | data = JSON.parse(data); 62 | } 63 | catch { 64 | res({}); 65 | return; 66 | } 67 | res(data); 68 | }); 69 | }); 70 | const saveFile = (path, content, coding='utf8') => new Promise((res, rej) => { 71 | if (coding !== null && !String.is(content) && !Number.is(content) && !Boolean.is(content)) content = JSON.stringify(content); 72 | FS.writeFile(path, content, coding, err => { 73 | if (!!err) rej(err); 74 | else res(); 75 | }) 76 | }); 77 | const getLocalIP = () => { 78 | var ips = []; 79 | var interfaces = require('os').networkInterfaces(); 80 | for (let networks in interfaces) { 81 | networks = interfaces[networks]; 82 | for (let addr of networks) { 83 | if (addr.internal) continue; 84 | if (addr.netmask === '255.255.255.255' || addr.netmask === 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff') continue; 85 | ips.push([addr.address, addr.family]); 86 | } 87 | } 88 | return ips; 89 | }; 90 | 91 | _('Utils.getAllContents', getAllContents); 92 | _('Utils.getAllSubFolders', getAllSubFolders); 93 | _('Utils.getJSON', getJSON); 94 | _('Utils.saveFile', saveFile); 95 | _('Utils.getLocalIP', getLocalIP); -------------------------------------------------------------------------------- /core/utils/loadall.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | 4 | const stat = fs.statSync; 5 | const readdir = fs.readdirSync; 6 | 7 | const AvailableExtTypes = ['.js', '.json']; 8 | 9 | var load_root = __dirname + path.sep + '..' + path.sep + '..' + path.sep; 10 | 11 | const getDir = dir => { 12 | if (dir[0] === '~' && dir[1] === path.sep) dir = process.cwd() + dir.substring(1, dir.length); 13 | else if (dir[0] !== path.sep && dir[1] !== ':') dir = load_root + dir; 14 | return path.resolve(dir); 15 | }; 16 | const loadall = (url, is_all) => { 17 | var headers = [], files = [], folders = []; 18 | readdir(url).forEach(p => { 19 | if (p[0] === '.') return; 20 | p = url + path.sep + p; 21 | if (exceptions.indexOf(p) >= 0) return; 22 | var s = stat(p); 23 | if (s.isFile()) { 24 | let dirname = path.dirname(p).split(path.sep).pop(); 25 | let filename = path.basename(p) 26 | let ext = path.extname(p); 27 | filename = filename.substring(0, filename.length - ext.length); 28 | ext = ext.toLowerCase(); 29 | if (AvailableExtTypes.indexOf(ext) >= 0) { 30 | if (filename === 'index') { 31 | headers[1] = p; 32 | } 33 | else if (filename === dirname) { 34 | headers[0] = p; 35 | } 36 | else { 37 | files.push(p); 38 | } 39 | } 40 | } 41 | else if (is_all && s.isDirectory()) { 42 | folders.push(p); 43 | } 44 | }); 45 | folders.forEach(p => loadall(p, is_all)); 46 | files.forEach(p => require(p)); 47 | if (headers[0]) require(headers[0]); 48 | if (headers[1]) require(headers[1]); 49 | }; 50 | const exceptions = []; 51 | const except = url => { 52 | if (String.is(url)) url = [url]; 53 | url = url.forEach(u => { 54 | u = getDir(u); 55 | if (exceptions.indexOf(u) < 0) exceptions.push(u); 56 | }); 57 | }; 58 | 59 | global.loadall = (...args) => { 60 | var is_all = args.filter(b => (b === true || b === false))[0]; 61 | if (is_all !== true && is_all !== false) is_all = true; 62 | var url = args.filter(s => s + '' === s); 63 | url = path.join(...url); 64 | url = getDir(url); 65 | loadall(url, is_all); 66 | process.emit('load'); 67 | }; 68 | global.loadall.except = except; 69 | global.load = url => { 70 | url = getDir(url); 71 | var s; 72 | try { 73 | s = stat(url); 74 | } 75 | catch (e) { 76 | url = url + '.js'; 77 | try { 78 | s = stat(url); 79 | } 80 | catch (err) { 81 | return null; 82 | } 83 | } 84 | if (s.isFile()) { 85 | return require(url); 86 | } 87 | else if (s.isDirectory()) { 88 | loadall(url, true); 89 | return null; 90 | } 91 | }; 92 | global.setLoadRoot = url => load_root = url; 93 | global.getLoadPath = getDir; -------------------------------------------------------------------------------- /server/center.js: -------------------------------------------------------------------------------- 1 | const Responsor = require('./responser'); 2 | const Galanet = require('./galanet'); 3 | const Logger = new (_("Utils.Logger"))('EventCenter'); 4 | 5 | process.on('command::request::shakehand', remoteIP => { 6 | Galanet.reshakehand(remoteIP); 7 | }); 8 | 9 | // 各进程负载情况 10 | process.on('console::stat::usage', (unuse, event, callback) => { 11 | callback(Responsor.getUsage()); 12 | }); 13 | // 各节点负载情况 14 | process.on('console::stat::cluster', (unuse, event, callback) => { 15 | callback(Galanet.getUsage()); 16 | }); 17 | // 重启本地子进程 18 | process.on('console::local::refresh', async (unuse, event, callback) => { 19 | var time = Date.now(); 20 | await Responsor.refresh(); 21 | time = Date.now() - time; 22 | callback(Responsor.processCount + ' 个工作进程已被更新,用时 ' + time + ' ms'); 23 | }); 24 | // 设置业务子进程数 25 | process.on('console::local::set::subprocess', async (data, event, callback) => { 26 | var ok = Responsor.setProcessCount(data); 27 | callback('设置业务进程数' + (ok ? '成功' : '失败') + ',重启业务子进程后生效'); 28 | }); 29 | // 设置并发数 30 | process.on('console::local::set::concurrence', async (data, event, callback) => { 31 | var ok = Responsor.setConcurrence(data); 32 | callback('设置并发数' + (ok ? '成功' : '失败')); 33 | }); 34 | // 增加节点 35 | process.on('console::network::addNode', async (node, event, callback) => { 36 | callback(...(await Galanet.addNode(node))); 37 | }); 38 | // 移除节点 39 | process.on('console::network::removeNode', (node, event, callback) => { 40 | callback(...Galanet.removeNode(node)); 41 | }); 42 | // 显示友邻 43 | process.on('console::network::show::friends', (node, event, callback) => { 44 | var userList = {}; 45 | Galanet.getFriends().forEach(user => { 46 | userList[user.name] = user.connList.map(conn => { 47 | return { 48 | name: conn.name, 49 | connected: conn.connected 50 | }; 51 | }); 52 | }); 53 | callback(userList); 54 | }); 55 | // 关闭系统 56 | process.on('console::shutdown', async (isAll, event, callback) => { 57 | var msg = ''; 58 | if (isAll) { 59 | let count = await Galanet.shutdown(true); 60 | msg = '已通知 ' + count + ' 个集群友机离线'; 61 | Logger.log(msg); 62 | msg = '已离线,并' + msg; 63 | } 64 | else { 65 | await Galanet.shutdown(false); 66 | msg = '已离线'; 67 | } 68 | setTimeout(() => { 69 | if (isSlaver) { 70 | process.send({ event: 'extinct' }); 71 | } 72 | else { 73 | Responsor.extinct(); 74 | } 75 | }, 100); 76 | callback(msg); 77 | }); 78 | 79 | // 广播 80 | process.on('galanet::message', (msgType, sender, msg) => { 81 | sender = sender.protocol + '/' + sender.host + '/' + sender.port; 82 | Logger.log('Got Galanet ' + msgType + ' Message from ' + sender + ' : ' + JSON.stringify(msg)); 83 | }); -------------------------------------------------------------------------------- /server/personel.js: -------------------------------------------------------------------------------- 1 | const Path = require('path'); 2 | const FSP = require('fs').promises; 3 | const Crypto = require('../kernel/crypto'); 4 | const MultiHash = require('../kernel/multihash'); 5 | const newLongID = _('Message.newLongID'); 6 | const Shakehand = _('Message.Shakehand'); 7 | const Logger = new (_("Utils.Logger"))('Personel'); 8 | 9 | const CryptoType = 'rsa'; 10 | const HashType = 'RSA-SHA256'; 11 | 12 | global.Personel = global.Personel || { name: 'fuck' }; 13 | 14 | const init = async cfg => { 15 | var filepath = String.is(cfg.personel) ? cfg.personel : './personel.json'; 16 | if (filepath.indexOf('.') === 0) filepath = Path.join(process.cwd(), filepath); 17 | 18 | var personel; 19 | try { 20 | personel = require(filepath); 21 | } 22 | catch (err) { 23 | if (err.code !== 'MODULE_NOT_FOUND') { 24 | Logger.error(err); 25 | } 26 | personel = null; 27 | } 28 | 29 | if (!!personel) { 30 | if (!personel.id || !personel.publicKey || !personel.privateKey) { 31 | personel = null; 32 | } 33 | else { 34 | let check = false; 35 | try { 36 | check = checkKeyID(personel.publicKey, personel.id); 37 | } 38 | catch { 39 | check = false; 40 | } 41 | if (!check) personel = null; 42 | } 43 | } 44 | 45 | if (!personel) { 46 | personel = createPersonel(); 47 | await FSP.writeFile(filepath, JSON.stringify(personel, null, '\t')); 48 | } 49 | 50 | global.Personel.id = personel.id; 51 | global.Personel.publicKey = personel.publicKey; 52 | global.Personel.privateKey = personel.privateKey; 53 | global.PersonCard = (new Shakehand(personel.id, personel.publicKey, cfg.api.services, global.isDelegator)); 54 | }; 55 | const createPersonel = () => { 56 | var info = { 57 | id: '', 58 | publicKey: '', 59 | privateKey: '' 60 | }; 61 | 62 | var { privateKey, publicKey } = Crypto.generateKeyPair(CryptoType); 63 | info.publicKey = Crypto.convertKey2Base64(publicKey); 64 | info.privateKey = Crypto.convertKey2Base64(privateKey); 65 | 66 | var hash = Crypto.crypto.createHash(HashType); 67 | hash.update(Buffer.from(info.publicKey, 'base64')); 68 | hash = hash.digest(); 69 | var mh = new MultiHash(HashType, hash); 70 | info.id = mh.toString(); 71 | 72 | return info; 73 | }; 74 | const checkKeyID = (pubkey, id) => { 75 | var hash = Crypto.crypto.createHash(HashType); 76 | hash.update(Buffer.from(pubkey, 'base64')); 77 | hash = hash.digest(); 78 | var mh = new MultiHash(HashType, hash); 79 | var nid = MultiHash.fromString(id); 80 | return nid.equal(mh); 81 | }; 82 | 83 | module.exports = { 84 | init, 85 | create: createPersonel, 86 | check: checkKeyID, 87 | }; 88 | _('Core.Personel', module.exports); -------------------------------------------------------------------------------- /core/commandline/setConsoleStyle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Console Style Optimizer 3 | * Desc: 命令行样式优化 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2017.10.14 7 | * 8 | * 基于ansi-style库和chalk库 9 | */ 10 | 11 | const styles = { 12 | modifier: { 13 | reset: [0, 0], 14 | // 21 isn't widely supported and 22 does the same thing 15 | bold: [1, 21], 16 | dim: [2, 22], 17 | italic: [3, 23], 18 | underline: [4, 24], 19 | inverse: [7, 27], 20 | hidden: [8, 28], 21 | strikethrough: [9, 29] 22 | }, 23 | color: { 24 | black: [30, 39], 25 | red: [31, 39], 26 | green: [32, 39], 27 | yellow: [33, 39], 28 | blue: [34, 39], 29 | magenta: [35, 39], 30 | cyan: [36, 39], 31 | white: [37, 39], 32 | gray: [90, 39], 33 | 34 | // Bright color 35 | redBright: [91, 39], 36 | greenBright: [92, 39], 37 | yellowBright: [93, 39], 38 | blueBright: [94, 39], 39 | magentaBright: [95, 39], 40 | cyanBright: [96, 39], 41 | whiteBright: [97, 39] 42 | }, 43 | bgColor: { 44 | bgBlack: [40, 49], 45 | bgRed: [41, 49], 46 | bgGreen: [42, 49], 47 | bgYellow: [43, 49], 48 | bgBlue: [44, 49], 49 | bgMagenta: [45, 49], 50 | bgCyan: [46, 49], 51 | bgWhite: [47, 49], 52 | 53 | // Bright color 54 | bgBlackBright: [100, 49], 55 | bgRedBright: [101, 49], 56 | bgGreenBright: [102, 49], 57 | bgYellowBright: [103, 49], 58 | bgBlueBright: [104, 49], 59 | bgMagentaBright: [105, 49], 60 | bgCyanBright: [106, 49], 61 | bgWhiteBright: [107, 49] 62 | } 63 | }; 64 | styles.color.grey = styles.color.gray; 65 | for (let groupName of Object.keys(styles)) { 66 | let group = styles[groupName]; 67 | 68 | for (let styleName of Object.keys(group)) { 69 | let style = group[styleName]; 70 | 71 | styles[styleName] = { 72 | open: `\u001B[${style[0]}m`, 73 | close: `\u001B[${style[1]}m` 74 | }; 75 | 76 | group[styleName] = styles[styleName]; 77 | } 78 | 79 | styles[groupName] = group; 80 | } 81 | styles.color.close = '\u001B[39m'; 82 | styles.bgColor.close = '\u001B[49m'; 83 | 84 | const setStyle = (msg, style) => { 85 | if (style instanceof String || typeof style === 'string') { 86 | style = style.trim(); 87 | if (style.indexOf(' ') >= 0) { 88 | style = style.split(/ +/); 89 | return setStyle(msg, style); 90 | } 91 | else { 92 | style = styles[style]; 93 | if (!style || !style.open || !style.close) return msg; 94 | return style.open + msg + style.close; 95 | } 96 | } 97 | else for (let s of style) { 98 | s = styles[s]; 99 | if (!s || !s.open || !s.close) continue; 100 | msg = s.open + msg + s.close; 101 | } 102 | return msg; 103 | }; 104 | module.exports = setStyle; 105 | module.exports.styles = styles; 106 | _('CL.SetStyle', setStyle); -------------------------------------------------------------------------------- /core/events/channel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Channel 3 | * Desc: 通道 4 | * Author: LostAbaddon 5 | * Version: 0.0.2 6 | * Date: 2018.11.15 7 | */ 8 | 9 | class Channel { 10 | constructor (consumeFirst=false) { 11 | this._producer = []; 12 | this._consumer = []; 13 | this._consumeFirst = !!consumeFirst; 14 | this._running = true; 15 | this._dying = false; 16 | 17 | this._friends = new Set(); 18 | } 19 | push (data) { 20 | return new Promise((res, rej) => { 21 | if (!this._running) { 22 | res(); 23 | return; 24 | } 25 | if (this._consumer.length === 0) { 26 | if (this._dying) { 27 | res(); 28 | return; 29 | } 30 | this._producer.push([data, res]); 31 | } 32 | else { 33 | let c = this._consumer.shift(); 34 | if (this._consumeFirst) { 35 | setImmediate(() => c(data)); 36 | setImmediate(() => res()); 37 | } 38 | else { 39 | setImmediate(() => res()); 40 | setImmediate(() => c(data)); 41 | } 42 | if (this._dying) this.close(); 43 | } 44 | }); 45 | } 46 | pull () { 47 | return new Promise((res, rej) => { 48 | if (!this._running) { 49 | res(); 50 | return; 51 | } 52 | if (this._producer.length === 0) { 53 | if (this._dying) { 54 | res(); 55 | return; 56 | } 57 | this._consumer.push(res); 58 | } 59 | else { 60 | let p = this._producer.shift(); 61 | if (this._consumeFirst) { 62 | setImmediate(() => res(p[0])); 63 | setImmediate(() => p[1]()); 64 | } 65 | else { 66 | setImmediate(() => p[1]()); 67 | setImmediate(() => res(p[0])); 68 | } 69 | if (this._dying) this.close(); 70 | } 71 | }); 72 | } 73 | kill () { 74 | this._dying = true; 75 | if (!this._running) return; 76 | this._producer.forEach(p => setImmediate(() => p[1]())); 77 | this._consumer.forEach(c => setImmediate(() => c())); 78 | this._running = false; 79 | if (this._friends) { 80 | let f = this._friends; 81 | this._friends = null; 82 | f.forEach(c => c.kill()); 83 | } 84 | } 85 | close () { 86 | this._dying = true; 87 | if (this._producer.length + this._consumer.length === 0) this._running = false; 88 | if (this._friends) { 89 | let f = this._friends; 90 | this._friends = null; 91 | f.forEach(c => c.close()); 92 | } 93 | } 94 | get alive () { 95 | return !this._dying && this._running; 96 | } 97 | async combine (channel) { 98 | if (this._friends.has(channel)) return; 99 | this._friends.add(channel); 100 | channel.combine(this); 101 | 102 | while (this.alive || channel.alive) { 103 | let msg = await this.pull(); 104 | channel.push(msg); 105 | } 106 | } 107 | } 108 | 109 | exports.Channel = Channel; 110 | _('Events.Channel', Channel); -------------------------------------------------------------------------------- /commands/index.js: -------------------------------------------------------------------------------- 1 | const { readdir, writeFile, readFile } = require('node:fs/promises'); 2 | const { join } = require('node:path'); 3 | const ExtraAlias = {}; 4 | 5 | const DefaultScope = ['main', 'sub']; 6 | const Commands = { 7 | list: {}, 8 | alias: {}, 9 | }; 10 | const commands = {}; 11 | 12 | Commands.loadCommands = async () => { 13 | var list, extra; 14 | try { 15 | list = await readdir(__dirname); 16 | } 17 | catch { 18 | list = []; 19 | } 20 | try { 21 | extra = await readFile(join(process.cwd(), 'out', 'dynamicalias.txt'), 'utf-8'); 22 | extra = JSON.parse(extra); 23 | } 24 | catch { 25 | extra = {}; 26 | } 27 | Commands.alias = Object.assign({}, Commands.alias, extra); 28 | list.forEach(filename => { 29 | if (!filename) return; 30 | if (!!filename.match(/^index\.js$/i)) return; 31 | var cmd = require('./' + filename); 32 | if (!cmd || !cmd.cmd || !cmd.name) return; 33 | Commands.list[cmd.cmd] = { 34 | name: cmd.name, 35 | command: cmd.cmd, 36 | alias: cmd.alias, 37 | args: cmd.args || {}, 38 | scope: !!cmd.scope ? cmd.scope : DefaultScope 39 | }; 40 | if (!!cmd.alias && !!cmd.alias.forEach) cmd.alias.forEach(n => { 41 | Commands.alias[n] = cmd.cmd; 42 | }); 43 | commands[cmd.cmd] = cmd.execute; 44 | }); 45 | }; 46 | Commands.generateCommands = (scope="main") => { 47 | var list = [], i = 1; 48 | for (let cmd in Commands.list) { 49 | cmd = Commands.list[cmd]; 50 | if (!cmd.scope.includes(scope)) continue; 51 | let command = i + '. ' + cmd.name + '\n "' + cmd.command + '": '; 52 | let args = []; 53 | for (let arg in cmd.args) { 54 | let hint = cmd.args[arg]; 55 | args.push('"' + arg + '": "<' + hint + '>"'); 56 | } 57 | if (args.length === 0) { 58 | command += 'no arg.'; 59 | } 60 | else { 61 | command += args.join(', '); 62 | } 63 | list.push(command); 64 | i ++; 65 | } 66 | return list.join('\n'); 67 | }; 68 | Commands.executeCommands = async (type, scope, caller, cmd, args) => { 69 | var command = commands[cmd]; 70 | if (!command) { 71 | throw new Error('Nonexist command.'); 72 | } 73 | var scopeList = Commands.list[cmd].scope; 74 | if (!scopeList.includes(scope)) { 75 | throw new Error('Out of scope: ' + scope); 76 | } 77 | return await command(type, caller, args); 78 | }; 79 | Commands.normalizeCommandName = name => { 80 | var action = name.replace(/[ \t\-\.]/g, '_').toLowerCase(); 81 | return action; 82 | }; 83 | Commands.getCommandName = name => { 84 | var cmd = Commands.list[name]; 85 | if (!cmd) { 86 | let alias = Commands.alias[name]; 87 | cmd = Commands.list[alias]; 88 | } 89 | return cmd; 90 | }; 91 | Commands.addAlias = (alias, cmd) => { 92 | Commands.alias[alias] = cmd; 93 | ExtraAlias[alias] = cmd; 94 | writeFile(join(process.cwd(), 'out', 'dynamicalias.txt'), JSON.stringify(ExtraAlias), 'utf-8'); 95 | }; 96 | 97 | module.exports = Commands; -------------------------------------------------------------------------------- /core/threads/threadTunnel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: Thread Tunnel 3 | * Desc: 跨线程通道 4 | * Author: LostAbaddon 5 | * Version: 0.0.1 6 | * Date: 2018.11.14 7 | */ 8 | 9 | if (!global._canThread) return; 10 | 11 | load('./src/events/channel'); 12 | const Channel = _('Events.Channel'); 13 | 14 | class Tunnel { 15 | constructor (mgr) { 16 | this.id = String.random(64); 17 | this._mgr = mgr; 18 | this._channel = new Channel(); 19 | } 20 | push (data) { 21 | return new Promise(async (res, rej) => { 22 | await this._channel.push(data); 23 | res(); 24 | }); 25 | } 26 | pull () { 27 | return new Promise(async (res, rej) => { 28 | this._mgr._sender('__tunnel__', { 29 | event: 'pull', 30 | id: this.id 31 | }); 32 | var data = await this._channel.pull(); 33 | res(data); 34 | }); 35 | } 36 | kill () { 37 | this._mgr.killTunnel(this.id); 38 | } 39 | close () { 40 | this._mgr.closeTunnel(this.id); 41 | } 42 | get alive () { 43 | return this._channel.alive 44 | } 45 | combine (channel) { 46 | if (channel instanceof Channel) { 47 | this._channel.combine(channel); 48 | } 49 | else if (channel instanceof Tunnel) { 50 | this._channel.combine(channel._channel); 51 | } 52 | } 53 | } 54 | 55 | class TunnelManager { 56 | constructor (sender) { 57 | this._pool = new Map(); 58 | this._sender = sender; 59 | } 60 | getTunnel (id) { 61 | var tunnel; 62 | if (!!id && id.length === 64) { 63 | tunnel = this._pool.get(id); 64 | } 65 | if (!tunnel) { 66 | tunnel = new Tunnel(this); 67 | if (!!id) tunnel.id = id; 68 | this._pool.set(tunnel.id, tunnel); 69 | this._sender('tunnel', { 70 | event: 'create', 71 | id: tunnel.id 72 | }); 73 | } 74 | return tunnel; 75 | } 76 | async gotPull (tid) { 77 | var tunnel = this._pool.get(tid); 78 | if (!tunnel) { 79 | this._sender('__tunnel__', { 80 | event: 'nil', 81 | id: tid 82 | }); 83 | return; 84 | } 85 | var data = await tunnel._channel.pull(); 86 | this._sender('__tunnel__', { 87 | event: 'data', 88 | id: tid, 89 | data 90 | }); 91 | } 92 | gotNil (tid) { 93 | var tunnel = this._pool.get(tid); 94 | if (!tunnel) return; 95 | tunnel.push(undefined); 96 | } 97 | gotData (tid, data) { 98 | var tunnel = this._pool.get(tid); 99 | if (!tunnel) return; 100 | tunnel.push(data); 101 | } 102 | closeTunnel (tid, one_side=false) { 103 | var tunnel = this._pool.get(tid); 104 | if (!!tunnel) tunnel._channel.close(); 105 | if (!one_side) this._sender('__tunnel__', { 106 | event: 'close', 107 | id: tid 108 | }); 109 | } 110 | killTunnel (tid, one_side=false) { 111 | var tunnel = this._pool.get(tid); 112 | if (!!tunnel) tunnel._channel.kill(); 113 | if (!one_side) this._sender('__tunnel__', { 114 | event: 'kill', 115 | id: tid 116 | }); 117 | } 118 | closeAll () { 119 | } 120 | killAll () { 121 | } 122 | } 123 | 124 | module.exports = TunnelManager; -------------------------------------------------------------------------------- /prompts/claude-default.ini: -------------------------------------------------------------------------------- 1 | [missionStart] 2 | Now is