UNPKG

@indra.ai/deva

Version:
1,473 lines (1,345 loc) 55.2 kB
// Copyright (c)2025 Quinn Michaels; All Rights Reserved; Legal Signature Required. // Distributed under the Restricted software license, see the accompanying file LICENSE.md import {EventEmitter} from 'node:events'; import {randomUUID} from 'crypto'; import lib from './lib/index.js'; import pkg from './package.json' with {type:'json'}; const {name,version,repository,author,bugs,homepage,license,config} = pkg; class Deva { constructor(opts) { opts = opts || {}; // set opts to provided opts or an empty object. this._core = {name,version,repository,author,bugs,homepage,license}; this._id = opts.id || randomUUID(); // the unique id assigned to the agent at load this._info = opts.info || false; // the deva information from the package file. this._config = opts.config || {}; // local Config Object this._agent = opts.agent || false; // Agent profile object this._client = {}; // this will be set on init. this._active = false; // the active/birth date. this._security = false; // inherited Security features. this._defense = false; // inherited Security features. this._support = false; // inherited Support features. this._services = false; // inherited Service features. this._systems = false; // inherited Systems features. this._networks = false; // inherited Systems features. this._legal = false; // inherited Legal features. this._justice = false; // inherited Justice features. this._authority = false; // inherited Justice features. this.events = opts.events || new EventEmitter({}); // Event Bus this.lib = new lib({}); // used for loading library functions this.utils = opts.utils || {}; // parse function this.devas = opts.devas || {}; // Devas which are loaded this.vars = opts.vars || {}; // Variables object this.listeners = opts.listeners || {}; // local Listeners this.modules = opts.modules || {}; // 3rd Party Modules this.func = opts.func || {}; // local Functions this.methods = opts.methods || {}; // local Methods this.maxListeners = opts.maxListenners || 0; // set the local maxListeners // prevent overwriting existing functions and variables with same name for (const opt in opts) { if (!this[opt] || !this[`_${opt}`]) this[opt] = opts[opt]; } this.cmdChr = config.cmdChr; // the trigger for local commands this.askChr = config.askChr; // the trigger for ask other DEva features this.inherit = config.inherit; // set inherit from config this.bind = config.bind; // set the bind from the config this._state = config.state; // set the current state from config this._states = config.states; // set the states from config this._zone = config.zone; // set the current zone from config this._zones = config.zones; // set the zones from config this._action = config.action; // set the action from config this._actions = config.actions; // set the actions from config this._feature = config.feature; // set the feature from config this._features = config.features; // set the features from config this._context = opts.context || false; // set the local context this._message = config.message; // current message of agent. this._messages = config.messages; // set the messages from config } /************** func: _assignBind params: none describe: The assign bind function will bind the translate functions and parse functions of the agent and bind their functionality to the state machine. ***************/ _assignBind() { return new Promise((resolve, reject) => { try { this.bind.forEach(bind => { // loop over the bind items func, method, listener... if (this[bind]) for (let x in this[bind]) { // if the root has a bind func, method, listener if (typeof this[bind][x] === 'function') { // check to make sure object is a fucntion this[bind][x] = this[bind][x].bind(this); // bind the item from the bind object } } }); } catch (e) { return this.error(e, false, reject); // trigger the this.error for errors } finally { return resolve(); // when the configuration is complete then return an empty resolve. } }); } /************** func: _assignListeners params: none describe: Assign listeners will take the this.lisners objects and assign the appropriate lisnter values for the event bus. ***************/ _assignListeners() { return new Promise((resolve, reject) => { try { // set the default listeners for the states of the agent. for (let state in this._states) { if (typeof this[state] === 'function') { this.events.on(`${this._agent.key}:${state}`, packet => { return this[state](packet); }); } } // set the assigned listeners for the agent. for (let listener in this.listeners) { // loop over the liteners this.events.on(listener, packet => { // set the event listener return this.listeners[listener](packet); // return the listener function }) } } catch (e) { return this.error(e, false, reject); // pass errors to this.error } finally { return resolve(); // resolve the function after everything is done. } }); } // Some elements will inherit the data of the parent. this object will loop over // any children data that theis deva has and assign the inherited information. /************** func: _assignInherit params: none describe: The assign inherit will make sure the Devas in the current Deva have all the inherited properties all setup to collaborate efficiently. ***************/ _assignInherit() { return new Promise((resolve, reject) => { try { for (let d in this.devas) { this.inherit.forEach(inherit => { this.devas[d][inherit] = this[inherit]; }); } return resolve(); } catch (e) { return this.error(e, false, reject); } }); } // General handler for when a method is NOT found from a user command. /************** func: _methodNotFound params: - packet: The packet to relay when a method is not found. describe: The _methodNotFound function allows for additional security by firing a specfici program functon every single time a interaction happens wehre a method is not located. This assits in security and support by identifying troubls or users who may be attemptng to explit features. Then we talk a security event that watches all methods and return the packet. This will return a not found text string preventing any furhter processing. ***************/ _methodNotFound(packet) { packet.a = { id: this.lib.uid(), agent: this.agent() || false, client: this.client() || false, text: `${this._messages.method_not_found}`, meta: { key: this._agent.key, method: packet.q.meta.method, }, created: Date.now(), }; packet.a.hash = this.lib.hash(packet.a); delete packet.hash; packet.hash = this.lib.hash(packet); this.state('invalid', `${packet.q.meta.method}:${packet.id}`); return packet; } _getFeature(key, value) { if (!this._active) return this._messages.offline; // check the active status this.zone(key); this.feature(key); // set the security state try { const data = this.lib.copy(value); this.state('return', key); // set the security state return data; // return the security feature } catch (e) { this.state('catch', key); return this.error(e); } } /************** func: Client params: client - client provided data. describe: The Client feature sets up the client variables and removes any unnecessary keys from the client object that are used in other features. usage: this.Client = {data} ***************/ Client(client, resolve, reject) { this.feature('client'); this.zone('client'); this.action('client'); // setup any custom methods for the features try { for (const x in client.features) { const methods = client.features[x].methods || false; if (methods) for (const y in methods) { const isFunc = typeof methods[y] === 'function'; if (isFunc) { this.methods[y] = methods[y].bind(this); } } } const _client = this.lib.copy(client); // copy the client parameter this.state('set', 'client'); this._client = _client; // set local _client to this scope this.state('resolve', 'client'); return resolve(); } catch (e) { return this.error(e, false, reject); } } /************** func: Feature params: client: false describe: The Security feature sets the correct variables and necessary rules for the client presented data. ***************/ Feature(feature, resolve, reject) { const _id = this.lib.uid(); this.feature(feature, _id); this.zone(feature, _id); const _cl = this.client(); // set local copy of client data try { if (!_cl.features[feature]) return resolve(); // if no security feature goto Support else { this.action(feature, _id); // set action to feature const _fe = `_${feature}`; const {id, profile, features} = _cl; // make a copy the clinet data. const data = features[feature]; // make a copy the clinet data. this.state('set', `feature:${id}`); this[_fe] = { // set this_security with data id: _id, // uuid of the security feature client_id: id, // client id for reference client_name: profile.name, // client name for personalization concerns: data.concerns, // any concerns for client global: data.global, // the global policies for client personal: data.devas[this._agent.key], // Client personal features and rules. }; delete this._client.features[feature]; // make a copy the clinet data. this.state('resolve', `${feature}:${_id}`); return resolve(feature); // resolve when done } } catch (e) { this.state('catch', `${feature}:${_id}`); return this.error(e, feature, reject); // run error handling if an error is caught } } /************** func: Security params: client: false describe: The Security feature sets the correct variables and necessary rules for the client presented data. ***************/ Security(resolve, reject) { return this.Feature('security', resolve, reject); } /************** func: Guard params: client: false describe: The Guard feature sets the correct variables and necessary rules for the client presented data. ***************/ Guard(resolve, reject) { return this.Feature('guard', resolve, reject); } /************** func: Defense params: client: false describe: The Defense feature sets the correct variables and necessary rules for the client presented data. ***************/ Defense(resolve, reject) { return this.Feature('defense', resolve, reject); } /************** func: Support params: client: false describe: The Support feature sets the correct variables and necessary rules for the client presented data. ***************/ Support(resolve, reject) { return this.Feature('support', resolve, reject); } /************** func: Services params: client: false describe: The Services feature sets the correct variables and necessary rules for the client presented data. ***************/ Services(resolve, reject) { return this.Feature('services', resolve, reject); } /************** func: Systems params: client: false describe: The Systems feature sets the correct variables and necessary rules for the client presented data. ***************/ Systems(resolve, reject) { return this.Feature('systems', resolve, reject); } /************** func: Networks params: resolve, reject describe: The Networks feature sets the correct variables and necessary rules for the client presented data. ***************/ Networks(resolve, reject) { return this.Feature('networks', resolve, reject); } /************** func: Legal params: client: false describe: The Legal feature sets the correct variables and necessary rules for the client presented data. ***************/ Legal(resolve, reject) { return this.Feature('legal', resolve, reject); } /************** func: Justice params: client: false describe: The Justice feature sets the correct variables and necessary rules for the client presented data. ***************/ Justice(resolve, reject) { return this.Feature('justice', resolve, reject); } /************** func: Authority params: client: false describe: The Authority feature sets the correct variables and necessary rules for the client presented data. ***************/ Authority(resolve, reject) { return this.Feature('authority', resolve, reject); } /************** func: Done params: none describe: The end of the workflow Client Feature Workflow ***************/ Done(resolve, reject) { try { delete this._client.features; // delete the features key when done. return resolve(this.client()); // resolve an empty pr } catch (e) { this.state('catch', 'Done'); return this.error(e, false, reject); } } /************** func: talk params: - evt: The event the Deva is speaking to listen back for on a once event. - resource: The payload resource to send with the talk event. describe: The talk event allows agents to broadcast events that other Deva can listen to and make a response. talk events can be then returned with a talk even id to create seamless collaboration between Devas. ***************/ talk(evt, packet=false) { this.action('talk', `${evt}:${packet.id}`); return this.events.emit(evt, packet); } /************** func: listen params: - evt: The vent label to listen for - callback: The callback function to run when the event fires. describe: setup a new event listener in the system. ***************/ listen(evt, callback) { this.action('listen', evt); this.listeners[evt] = callback; return this.events.on(evt, packet => { return this.listeners[evt](packet); }); } /************** func: once params: - evt: The event to listen to for a once call. - callback: The callback functoin to run when the event fires. describe: ***************/ once(evt, callback) { this.action('once', evt) return this.events.once(evt, callback); } /************** func: ignore params: - evt: The event you'd like to ignore. - callback: a callback function to execute after removing the event from listerns. describe: The ignore function allow the removal of events in the listener group. ***************/ ignore(evt, callback) { this.action('ignore', evt); return this.events.removeListener(evt, callback); } /************** func: question example: this.question('#*agent.key *method* *text*') example: this.question('#*agent.key* *method* *properties*', {*data*}) params: = TEXT: The text string is the question to process in the current state. - DATA: The data is a data array or object that also can be passed to the question. describe: ***************/ question(TEXT=false, DATA=false) { const id = this.lib.uid(); // generate a unique id for transport. // check the active status if (!this._active) return Promise.resolve(this._messages.offline); this.zone('question', id); this.action('question', id); const t_split = TEXT.split(' '); // split the text on spaces to get words. const data = DATA; // set the DATA to data // check to see if the string is an #ask string to talk to the other Deva. const isAsk = t_split[0].startsWith(this.askChr); // check to see if the string is a command string to run a local method. const isCmd = t_split[0].startsWith(this.cmdChr); // Format the packet for return on the request. const packet = { // create the base q/a packet id, // set the id into packet q: false, // create empty q object in packet a: false, // create empty a object in packet created: Date.now(), // timestamp the packet }; let text = TEXT, // let TEXT is text for a manipulation variable params = false, // params as false to build params string method = 'question', // set the default method to question key = this.agent().key; // set a temporary key from the agent key. return new Promise((resolve, reject) => { // resolve with the no text message if the client says nothing. if (!TEXT) return resolve(this._messages.notext, resolve); this.state('try', `question:${id}`); try { // try to answer the question if (isAsk) { // determine if hte question isAsk // if:isAsk split the agent key and remove first command character key = t_split[0].substring(1); //if:isAsk use text split index 1 as the parameter block params = t_split[1] ? t_split[1].split(':') : false; method = params[0]; // the method to check is then params index 0 text = t_split.slice(2).join(' ').trim(); // rejoin the text with space this.state('ask', `${key}:${method}:${id}`); } else if (isCmd) { // determine if the question is a command //if:isCmd use text split index 1 as the parameter block params = t_split[0] ? t_split[0].split(':') : false; method = t_split[0].split(':')[0].substring(1); // if:isCmd use the 0 index as the command text = t_split.slice(1).join(' ').trim(); // if:isCmd rejoin the string on the space after removing first index this.state('cmd', `${method}:${id}`); // set the state to cmd. } this.state('set', `question:${method}:${id}`) packet.q = { // build packet.q container id: this.lib.uid(), // set the transport id for the question. agent: this.agent(), // set the agent client: this.client(), // set the client meta: { // build the meta container key, // set the key variable method, // set method to track function use params, // set any params that are associated }, text, // set the text for the packet. data, // set the data object created: Date.now(), // timestamp the question } // hash the question packet.q.meta.hash = this.lib.hash(packet.q); this.talk(config.events.question, this.lib.copy(packet)); // global question event make sure to copy data. if (isAsk) { // isAsk check if the question isAsk and talk // if: isAsk wait for the once event which is key'd to the packet ID for specified responses this.talk(`${key}:ask`, packet); this.once(`${key}:ask:${packet.id}`, answer => { this.talk(config.events.ask, this.lib.copy(answer)); this.state('return', `${key}:ask:${id}`); return this.finish(answer, resolve); // if:isAsk resolve the answer from the call }); } else { // else: answer the question locally this.state('answer', `${method}:${id}`); //set the answer state to the method return this.answer(packet, resolve, reject); } } catch(e) { this.state('catch', 'question'); return this.error(e); // if a overall error happens this witll call this.error } }); } /************** func: answer params: - packet - resolve - reject describe: The answer function is called from the question function to return an answer from the agent from the pre-determined method. ***************/ answer(packet, resolve, reject) { const id = this.lib.uid(); if (!this._active) return Promise.resolve(this._messages.offline); this.zone('answer', id); // set zone to answer const agent = this.agent(); const client = this.client(); // check if method exists and is of type function const {method,params} = packet.q.meta; this.action('answer', `${method}:${id}`); this.state('try', `answer:${method}:${id}`); try { const isMethod = this.methods[method] && typeof this.methods[method] == 'function'; if (!isMethod) return resolve(this._methodNotFound(packet)); // resolve method not found if check if check fails this.action('method', `answer:${method}:${id}`); this.methods[method](packet).then(result => { // check the result for the text, html, and data object. // this is for when answers are returned from nested Devas. const text = typeof result === 'object' ? result.text : result; const html = typeof result === 'object' ? result.html : result; // if the data passed is NOT an object it will FALSE const data = typeof result === 'object' ? result.data : false; this.state('set', `answer:${method}:packet_answer:${id}`); const packet_answer = { // setup the packet.a container id, agent, // set the agent who answered the question client, // set the client asking the question meta: { // setup the answer meta container key: agent.key, // set the agent key inot the meta method, // set the method into the meta params, // set the params into the meta }, text, // set answer text html, // set the answer html data, // set the answer data created: Date.now(), // set the created date for the answer }; // create a hash for the answer and insert into answer meta. packet_answer.meta.hash = this.lib.hash(packet_answer); packet.a = packet_answer; // set the packet.a to the packet_answer this.talk(config.events.answer, this.lib.copy(packet)); // global talk event this.state('return', `answer:${method}:${id}`); // set the state resolve answer return this.finish(packet, resolve); // resolve the packet to the caller. }).catch(err => { // catch any errors in the method this.state('catch', `answer:${method}:${id}`); // set the state reject answer return this.error(err, packet, reject); // return this.error with err, packet, reject }); } catch (e) { this.state('catch', `answer:${method}:${id}`); return this.error(e, packet, reject); } } /************** func: ask params: packet describe: The ask function gives each agent the ability to ask question to other agents in the system. When a question is asked the Agent with the question if it detect an ask event it will trigger. Then if an Agent with the matching ask event is listening they will respond. The question function uses this to create integrated communication between itself and other Deva in it's library. It can also be used in a custom manner to broadcast ask events inside other coe aswell. When the talk has an answer it will respond with a talk event that has the packet id so the event is specific to the talk. ***************/ ask(packet) { if (!this._active) return Promise.resolve(this._messages.offline); const agent = this.agent(); const client = this.client(); const {method, params} = packet.q.meta; this.zone('ask', `${method}:${packet.id}`); this.action('ask', `${method}:${packet.id}`); // build the answer packet from this model this.state('try', `ask:${method}:${packet.id}`); try { if (typeof this.methods[method] !== 'function') { return setImmediate(() => { this.talk(`${this._agent.key}:ask:${packet.id}`, this._methodNotFound(packet)); }); } this.state('set', `ask:${method}:packet_answer:${packet.id}`); const packet_answer = { id: this.lib.uid(), agent, client, meta: { key: agent.key, method, params, }, text: false, html: false, data: false, created: Date.now(), }; // The method is parsed and depending on what method is asked for it returns // the response based on the passed through packet. this.methods[method](packet).then(result => { if (typeof result === 'object') { packet_answer.text = result.text || false; packet_answer.html = result.html || false; packet_answer.data = result.data || false; } else { packet_answer.text = result; } packet_answer.meta.hash = this.lib.hash(packet_answer); packet.a = packet_answer; this.talk(config.events.answer, this.lib.copy(packet)); // global talk event this.talk(`${agent.key}:ask:${packet.id}`, packet); }).catch(err => { this.talk(`${agent.key}:ask:${packet.id}`, {error:err}); this.state('catch', `ask:${method}:${packet.id}`); return this.error(err, packet); }) } catch (e) { this.state('catch', `ask:${method}:${packet.id}`); this.talk(`${agent.key}:ask:${packet.id}`, {error:e}); return this.error(e, packet) } // now when we ask the meta params[0] should be the method } /************** func: init params: client - the client data to use that is provided by the clients. describe: The main init interface where the chain begins. Where the states fire for each process of setting: 1. Set the Max listeners to control event memory buffer. 2. Assign the Inherited Properties 3. Assign binding functions and methods to 'this' scope. 4. Assign any listeners for additional functionality. 5. run the onInit custom function if preset or the start function. 6. The start function will create a chain reaction of states that load. 7. If there is an error the init function rejects the call. usage: this.init(client_object) ***************/ init(client) { // set client this._active = Date.now(); const agent = this.agent(); const data = { id: this.lib.uid(), key: 'init', value: agent.key, agent, client, text: this._messages.init, created: Date.now(), } data.hash = this.lib.hash(data); return new Promise((resolve, reject) => { this.events.setMaxListeners(this.maxListeners); this._assignInherit().then(() => { return this._assignBind(); }).then(() => { return this._assignListeners(); }).then(() => { this.feature('init'); this.zone('init'); this.action('init'); this.state('init'); return this.Client(client, resolve, reject); }).then(() => { return this.Security(resolve, reject); }).then(() => { return this.Guard(resolve, reject); }).then(() => { return this.Defense(resolve, reject); }).then(() => { return this.Legal(resolve, reject); }).then(() => { return this.Authority(resolve, reject); }).then(() => { return this.Justice(resolve, reject); }).then(() => { return this.Support(resolve, reject); }).then(() => { return this.Services(resolve, reject); }).then(() => { return this.Systems(resolve, reject); }).then(() => { return this.Networks(resolve, reject); }).then(() => { return this.Done(resolve, reject); }).then(() => { const hasOnInit = this.onInit && typeof this.onInit === 'function'; this.state('return', `init:${data.id}`); return hasOnInit ? this.onInit(data, resolve) : this.start(data, resolve); }).catch(err => { this.state('catch', `init:${data.id}`); return this.error(err, client, reject); }); }); } /************** func: start params: - msg: the message for use when using custome flow logic to pass to onEnter describe: The start function begins the process by setting the state to start setting the active to the current datetime and then checking for a custom onStart function or running the enter function. usage: this.start('msg') ***************/ start(data, resolve) { this.zone('start', data.id); if (!this._active) return resolve(this._messages.offline); this.action('start', data.id); const id = this.lib.uid(); delete data.hash; data.value = 'start'; data.hash = this.lib.hash(data); const hasOnStart = this.onStart && typeof this.onStart === 'function' ? true : false; this.state('start', data.id); return hasOnStart ? this.onStart(data, resolve) : this.enter(data, resolve) } /************** func: enter params: - msg: hte message from the caller incase need to use in calls describe: The ener function will check the actie status of the Deva and set it to offline or enter. If the Deva is offline it will return the offline message. usage: this.enter('msg') ***************/ enter(data, resolve) { this.zone('enter', data.id); if (!this._active) return resolve(this._messages.offline); this.action('enter', data.id); const hasOnEnter = this.onEnter && typeof this.onEnter === 'function' ? true : false; delete data.hash; data.value = 'enter'; data.hash = this.lib.hash(data); this.state('enter', data.id); return hasOnEnter ? this.onEnter(data, resolve) : this.done(data, resolve) } /************** func: done params: - data: hte message from the caller incase need to use in calls describe: When the done function is triggered the system will also set the state of hte Deva to done. If the deva is offline it will return the offline message. usage: this.done('msg') ***************/ done(data, resolve) { this.zone('done', data.id); if (!this._active) return resolve(this._messages.offline); this.action('done', data.id); const hasOnDone = this.onDone && typeof this.onDone === 'function' ? true : false; delete data.hash; data.value = 'done'; data.hash = this.lib.hash(data); this.state('done', data.id); return hasOnDone ? this.onDone(data, resolve) : this.ready(data, resolve); } /************** func: ready params: - data: the data to pass to the resolve - resolve: the complete resolve to pass back describe: This function is use to relay the to the ready state. usage: this.ready(data, resolve) ***************/ ready(data, resolve) { this.zone('ready', data.id); if (!this._active) return resolve(this._messages.offline); this.action('ready', data.id); const hasOnReady = this.onReady && typeof this.onReady === 'function'; delete data.hash; data.value = 'ready'; data.hash = this.lib.hash(data);// hash the entire data before completeing. this.state('ready', data.id); return hasOnReady ? this.onReady(data, resolve) : resolve(data); } /************** func: finish params: - data: the data to pass to the resolve - resolve: the finish resolve to pass back describe: This function is used to relay into the finish state when resolving a question or data. usage: this.finish(data, resolve) ***************/ finish(packet, resolve) { this.zone('finish', packet.id); // enter finish zone if (!this._active) return resolve(this._messages.offline); // this.action('finish', packet.id); // start finish action const hasOnFinish = this.onFinish && typeof this.onFinish === 'function'; delete packet.hash; // delete packet hash to update for finish time packet.finish = Date.now(); // set the finish timestamp packet.hash = this.lib.hash(packet); // rehash the packet; this.state('finish', packet.id); // set finish state return hasOnFinish ? this.onFinish(packet, resolve) : this.complete(packet, resolve); } /************** func: complete params: - data: the data to pass to the resolve - resolve: the complete resolve to pass back describe: This function is use to relay into a complete state when resolving a question or data. usage: this.complete(data, resolve) ***************/ complete(packet, resolve) { this.zone('complete', packet.id); if (!this._active) return Promise.resolve(this._messages.offline); this.action('complete', packet.id); const hasOnComplete = this.onComplete && typeof this.onComplete === 'function'; delete packet.hash; packet.complete = Date.now();// set the complete date on the whole packet. packet.hash = this.lib.hash(packet);// hash the entire packet before complete. this.state('complete', packet.id); return hasOnComplete ? this.onComplete(packet, resolve) : resolve(packet); } /************** func: stop params: - msg: hte message from the caller incase need to use in calls describe: The stop function will stop the Deva by setting the active status to false, and the state to stop. From here it will check for a custom onStop function for anything to run, or run the exit function. If the deva is offline it will return the offline message. usage: this.stop() ***************/ stop() { const id = this.lib.uid(); this.zone('stop', id); if (!this._active) return Promise.resolve(this._messages.offline); this.action('stop', id); const hasOnStop = this.onStop && typeof this.onStop === 'function'; const data = { // build the stop data id, // set the id key: 'stop', // set the key value: this._messages.stop, // set the value agent: this.agent(), // set the agent client: this.client(), // set the client created: Date.now(), // set the created date } data.hash = this.lib.hash(data); // has stop function then set hasOnStop variable // if: has on stop then run on stop function or return exit function. this.state('stop', id); // set the state to stop return hasOnStop ? this.onStop(data) : this.exit() } /************** func: exit params: - msg: hte message from the caller incase need to use in calls describe: The exit state function is triggered when the Deva is exiting. The return will check for a custom onExit function or run the done function. ***************/ exit() { const id = this.lib.uid(); this.zone('exit', id); if (!this._active) return Promise.resolve(this._messages.offline); this.action('exit', id); const hasOnExit = this.onExit && typeof this.onExit === 'function'; const data = { id, key: 'exit', value: this._messages.exit, agent: this.agent(), client: this.client(), created: Date.now(), } data.hash = this.lib.hash(data); // set data hash this.state('exit', id); // set the state to stop // clear memory this._active = false; this._client = false; this._security = false; this._support = false; this._services = false; this._systems = false; this._networks = false; this._legal = false; this._authority = false; this._justice = false; return hasOnExit ? this.onExit(data) : Promise.resolve(data) } //////////////////////////// /************** func: state params: - value: The state value to set for the Deva that matches to this._states - extra: any extra text to add ot the state change. ***************/ state(value=false, extra=false) { try { if (!value || !this._states[value] || value === this._state) return; // return if no matching value this._state = value; // set the local state variable. const lookup = this._states[value]; // set the local states lookup const text = extra ? `${lookup} ${extra}` : lookup; // set text from lookup with extra const data = { // build the data object id: this.lib.uid(), // set the data id agent: this.agent(), // set the agent client: this.client(), // set the client key: 'state', // set the key to state value, // set the value to the passed in value text, // set the text value of the data created: Date.now(), // set the data created date. }; data.hash = this.lib.hash(data); // hash the data this.talk(config.events.state, data); // broadcasat the state event return data; } catch (e) { // catch any errors return this.error(e); // return if an error happens } } /************** func: states params: none describe: returns the avaiable staets values. ***************/ states() { const id = this.lib.uid(); this.action('states', id); const data = { id, key: 'states', value: this._states, created: Date.now(), } data.hash = this.lib.hash(data); this.state('return', `states:${id}`); return data; } /************** func: zone params: - st: The zone flag to set for the Deva that matches to this._zones describe ***************/ zone(value=false, extra=false) { const id = this.lib.uid(); if (!value || !this._zones[value] || value === this._zone) return; try { this._zone = value; const lookup = this._zones[value]; // set the lookup value const text = extra ? `${lookup} ${extra}` : lookup; // set the text value const data = { // build the zone data id, // set the packetid agent: this.agent(), client: this.client(), key: 'zone', value, text, created: Date.now(), }; data.hash = this.lib.hash(data); this.talk(config.events.zone, data); return data; } catch (e) { this.state('catch', `zone:${value}:${id}`); return this.error(e, value); } } /************** func: zones params: none describe: returns a listing of zones currently in the system. ***************/ zones() { const id = this.lib.uid(); this.action('zones', id); this.state('return', `zones:${id}`); return { id, // set the uuid of the data agent: this.agent(), // set the agent value cleint: this.client(), // set the client value key: 'zones', // set the key return value value: this._zones, // set the list of zones created: Date.now(), // set the created date of the object. } } /************** func: action params: - value: The state flag to set for the Deva that matches to this._states - extra: Any extra text to send with the action value. describe ***************/ action(value=false, extra=false) { const id = this.lib.uid(); try { if (!value || !this._actions[value] || value === this._action) return; this._action = value; // set the local action variable // check local vars for custom actions const var_action = this.vars.actions ? this.vars.actions[value] : false; // check the message action const msg_action = var_action || this._actions[value]; const msg = msg_action || action; // set the correct message const text = extra ? `${msg} ${extra}` : msg; // set the text of the action const data = { // build the data object for the action. id, // generate a guid for the action transmitssion. agent: this.agent(), // the agent data to send with the action client: this.client(), // the client data to send with the action key: 'action', // the key for event to transmit action type value, // the value key which is the action passed text, // text of the action to send created: Date.now(), // action time stamp }; data.hash = this.lib.hash(data); // generate a hash of the action packet. this.talk(config.events.action, data); // talk the core action event return data; } catch (e) { // catch any errors that occur this.state('catch', `action:${value}:${id}`); return this.error(e); // return error on error catch } } /************** func: actions params: none describe: Returns a list of available actions in the system. ***************/ actions() { const id = this.lib.uid(); this.action('actions', id); const data = { id, // set the id with a uuid agent: this.agent(), // set the agent value client: this.client(), // set the client value key: 'actions', // set the data key value: this._actions, // set the value to the actions list created: Date.now(), // set the data created date }; data.hash = this.lib.hash(data) this.state('return', `actions:${id}`); return data; } /************** func: feature params: - value: The feature flag to set for the Deva that matches to this._features - extra: Any extra text to send with the feature value. describe ***************/ feature(value=false, extra=false) { const id = this.lib.uid(); try { if (!value || !this._features[value]) return; // check feature value const lookup = this._features[value]; // set the lookup value const text = extra ? `${lookup} ${extra}` : lookup; // set the text value const data = { // build data object id, // set the id agent: this.agent(), // set the agent transporting the packet. key: 'feature', // set the key for transport value, // set the value of the key text, // set the text value created: Date.now(), // set the creation date }; data.hash = this.lib.hash(data); // generate the hash value of the data packet this.talk(config.events.feature, data); // talk the feature event with data return data; } catch (e) { // catch any errors this.state('catch', `feature:${value}:${id}`); return this.error(e); // retun this.error when an error is caught. } } /************** func: features params: none describe: return a list of features that are available to the system. ***************/ features() { if (!this._active) return this._messages.offline; // check the active status const id = this.lib.uid(); this.action('features', id); const data = { id, // set the object id agent: this.agent(), // set the agent value. client: this.client(), // set the client value. key: 'features', // set the key value: this._features, // set the value to the features list created: Date.now(), // set the created date. }; data.hash = this.lib.hash(data); this.state('return', `features:${id}`); return data; } /************** func: context params: - value: The context flag to set for the Deva that matches to this._contexts - extra: Any extra text that is sent with the context value. describe ***************/ context(value=false, extra=false) { if (!this._active) return this._messages.offline; // check the active status const id = this.lib.uid(); try { if (!value) return; this._context = value; const lookup = this.vars.context[value] || value; const text = extra ? `${lookup} ${extra}` : lookup; const data = { id, agent: this.agent(), client: this.client(), key: 'context', value, text, created: Date.now(), }; data.hash = this.lib.hash(data); this.talk(config.events.context, data); return data; } catch (e) { this.state('catch', `context:${value}:${id}`); return this.error(e, value); } } contexts() { if (!this._active) return this._messages.offline; // check the active status const id = this.lib.uid(); this.action('contexts', id); if (!this._active) return this._messages.offline; // check the active status const data = { id, agent: this.agent(), client: this.client(), key: 'contexts', value: this.vars.context || false, created: Date.now(), }; data.hash = this.lib.hash(data); this.state('return', `contexts:${id}`); return data; } /************** func: client params: none describe: returns the current client values in the system. usage: this.client(); ***************/ client() { if (!this._active) return this._messages.offline; // check the active status const client_copy = this.lib.copy(this._client); // create a copy of the client data client_copy.hash = this.lib.hash(client_copy); client_copy.created = Date.now(); return client_copy; // return the copy of the client data. } /************** func: agent params: none describe: returns the current agent values in the system. usage: this.agent() ***************/ agent() { if (!this._active) return this._messages.offline; // check the active status const agent_copy = this.lib.copy(this._agent); // create a copy of the agent data. agent_copy.hash = this.lib.hash(agent_copy); agent_copy.created = Date.now(); return agent_copy; // return the copy of the agent data. } // FEATURE FUNCTIONS /************** func: security params: none describe: basic security features available in a Deva. usage: this.security() ***************/ security() { return this._getFeature('security', this._security); } /************** func: guard params: none describe: basic guard features available in a Deva. usage: this.guard() ***************/ guard() { return this._getFeature('guard', this._guard); } /************** func: defense params: none describe: basic defense features available in a Deva. usage: this.defense() ***************/ defense() { return this._getFeature('defense', this._defense); } /************** func: support params: none describe: basic support features available in a Deva. usage: this.support() ***************/ support() { return this._getFeature('support', this._support); } /************** func: services params: none describe: basic services features available in a Deva. usage: this.services() ***************/ services() { return this._getFeature('services', this._services); } /************** func: systems params: none describe: basic systems features available in a Deva. usage: this.systems() ***************/ systems() { return this._getFeature('systems', this._systems); } /************** func: networks params: none describe: basic networks features available in a Deva. usage: this.networks() ***************/ networks() { return this._getFeature('networks', this._networks); } /************** func: legal params: none describe: basic legal features available in a Deva. usage: this.systems() ***************/ legal() { return this._getFeature('legal', this._legal); } /************** func: justice params: none describe: basic justice features available in a Deva. usage: this.systems() ***************/ justice() { return this._getFeature('justice', this._justice); } /************** func: authority params: none describe: basic authority features available in a Deva. usage: this.systems() ***************/ authority() { return this._getFeature('authority', this._authority); } /************** func: load params: -deva: The Deva model to load. describe: This function will enable fast loading of Deva into the system. ***************/ load(key, client) { this.zone('deva', key); this.action('load', key); this.state('load', key); return this.devas[key].init(client); } /************** func: unload params: - deva: The deva key to unload describe: Unload a currently loaded Deva. ***************/ unload(key) { this.zone('unload', key); return new Promise((resolve, reject) => { try { this.action('uload', key); this.devas[key].stop().then(exit => { delete this.devas[key]; this.talk(config.events.unload, key); }); this.state('unload', key) return resolve(`${this._states.unload}:${key}`); } catch (e) { return this.error(e, this.devas[key], reject) } }); } /************** func: prompt params: - text: The text string to send to the prompt. describe:- The prompt function is used to broadcasat a global prompt event with a string. Thsi is handy when passing events between a cli and user interface for example. usage: this.prompt('text') ***************/ prompt(text) { if (!this._active) return this._messages.offline; const id = this.lib.uid(); // Talk a global prompt event for the client const agent = this.agent(); const client = this.client(); const data = { id, key: 'prompt', value: agent.key, agent, client, text, created: Date.now(), } data.hash = this.lib.hash(data); this.talk(config.events.prompt, data); return data; } /************** func: core params: none describe: return core data. ***************/ core() { if (!this._active) return this._messages.offline; const id = this.lib.uid(); this.action('core', id); // check the active status const data = this.lib.copy(this._core); data.id = id; data.created = Date.now(); data.hash = this.lib.hash(data); t