@indra.ai/deva 1.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Quinn Michaels
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.
package/README.md ADDED
@@ -0,0 +1,391 @@
1
+ # DEVA
2
+ Deva is a lightweight class object that provides events and object management with inherited properties.
3
+
4
+ ## Install
5
+ ```bash
6
+ $> npm i @feecting/deva
7
+ ```
8
+
9
+ ## Basic Structure
10
+ ```javascript
11
+ // include the main Deva class
12
+ const Deva = require('feecting/deva');
13
+
14
+ // setup the Deva object
15
+ const deva = new Deva({
16
+ agent: {
17
+ uid: '*uinique identifier*'
18
+ key: '*DEVA KEY*',
19
+ name: '*DEVA NAME*',
20
+ describe: '*DEVA LONG DESCRIPTION*',
21
+ prompt: {
22
+ emoji: '🐶',
23
+ text: '*DEVA*',
24
+ color: 'white',
25
+ },
26
+ voice: {
27
+ speech: 'Alex',
28
+ speed: 1
29
+ },
30
+ profile: {
31
+ emoji: 'the graphic emoji the agent travels with. 50px x 50px'
32
+ avatar: 'the graphic avatar the agent travels with 150px x 150px',
33
+ background: 'a background asset for page displays for th eagent',
34
+ describe: 'the profile description for the agent that is used in displays.',
35
+ gender: 'the preferred gender string for the agent',
36
+ },
37
+ translate(input) {
38
+ return input.trim();
39
+ },
40
+ parse(input) {
41
+ return input.trim();
42
+ }
43
+ },
44
+ vars: {},
45
+ listeners: {},
46
+ modules: {},
47
+ func: {},
48
+ methods: {},
49
+ deva: {},
50
+ onStart() {},
51
+ onStop() {},
52
+ onEnter() {},
53
+ onExit() {},
54
+ onDone() {},
55
+ onInit() {},
56
+ });
57
+
58
+ // initialize the class
59
+ deva.init();
60
+
61
+ ```
62
+
63
+ ## Breakdown
64
+ ### Agent
65
+ ```javascript
66
+ this.agent
67
+ ```
68
+ The "me" object contains the profile information for the DEVA.
69
+ ##### Data Attributes
70
+ - **uid:** The unique id for the Deva.
71
+ - **key:** The unique key for the Deva.
72
+ - **name:** The name of the Deva.
73
+ - **description:** A description of what the Deva does.
74
+ - **prompt:** Define how prompt displays.
75
+ - **emoji:** The emoji for use as a prompt indicator.
76
+ - **text:** Short text for prompt display.
77
+ - **color:** The color of the prompt for the Deva.
78
+ - **voice:** Voice properties of the Deva.
79
+ - **speech:** The name of the voice speech to use.
80
+ - **speed:** The speed of the voice.
81
+ - **profile:** Public Profile Information
82
+ - **emoji:** The emoji for the Deva
83
+ - **avatar:** The avatar for the Deva
84
+ - **background:** The background image for the Deva
85
+ - **gender:** The gender of the Deva
86
+ - **describe:** A short description of the deva 100 characters or less.
87
+
88
+ ### Vars
89
+ ```javascript
90
+ this.vars
91
+ ```
92
+ The vars can be use to set local variables for the deva that need to be used in your program.
93
+
94
+ There are no default variables, so the scope is for you and your imagination to figure out.
95
+
96
+ ##### Example
97
+ ```javascript
98
+ ...
99
+ vars: {
100
+ foo: 'bar',
101
+ steps: 10,
102
+ strings: 'Some variable string',
103
+ adding: 1 + 9 + 11,
104
+ objects: {
105
+ key: 'key value'
106
+ },
107
+ arrays: [
108
+ 'value 1',
109
+ 'value 2',
110
+ ]
111
+ }
112
+ ...
113
+
114
+ ```
115
+ ### Listeners
116
+ Listeners are what you setup that allow your Deva to communicate with other Deva or parts of your application/system.
117
+
118
+ ```javascript
119
+ this.listeners
120
+ ```
121
+
122
+ #### Default Listeners
123
+
124
+ Each Deva comes with a set of default listeners to provide basic functionality.
125
+
126
+ ##### Question
127
+ The question event is the functionality that exposes the methods to the outside world. When a deva asks a question the string is parsed into a question format so that commands to access various methods can be exposed.
128
+
129
+ ```javascript
130
+ const question = await this.question(`#*agent_key* *method*:*params* *question string*`);
131
+ ```
132
+
133
+ ##### Start
134
+ This will trigger an event to start the Deva.
135
+
136
+ ```javascript
137
+ this.talk(`*agent_key*:start`);
138
+ ```
139
+
140
+ ##### Stop
141
+ This will trigger an event to stop the Deva.
142
+
143
+ ```javascript
144
+ this.talk(`*agent_key*:stop`);
145
+ ```
146
+
147
+ ##### Status
148
+ This will trigger an event to broadcast the Deva status.
149
+
150
+ ```javascript
151
+ this.talk(`*agent_key*:status`);
152
+ ```
153
+
154
+ ### Deva
155
+ ```javascript
156
+ this.deva
157
+ ```
158
+
159
+ The main object for Deva that are bwlow this Deva.
160
+
161
+ ### Modules
162
+ The external modules that your Deva might require to function.
163
+
164
+ ```javascript
165
+ this.modules
166
+ ```
167
+
168
+
169
+ ### Func
170
+ The functions that your deva uses to operate. Functions are not exposed through
171
+ the api to public access.
172
+
173
+ ```javascript
174
+ this.func
175
+ ```
176
+
177
+ ### Methods
178
+ ```javascript
179
+ this.methods
180
+ ```
181
+ The methods are exposed publicly through the question event that parses a string
182
+ and sends a request to the question method that then interacts with functions, modules, and variables.
183
+
184
+ ### State Functions
185
+ Provided are a set of state functions that trigger when a Deva is at various states of starting/stopping.
186
+
187
+ #### onStart()
188
+
189
+ The `onStart()` function runs after the `start` function has completed.
190
+
191
+ ```javascript
192
+ this.onStart() {
193
+ // some code to run when the deva starts.
194
+ }
195
+ ```
196
+
197
+ #### onStop()
198
+
199
+ The `onStop()` function runs after the `stop` function has completed.
200
+
201
+ ```javascript
202
+ this.onStop() {
203
+ // some code to run when the deva stops
204
+ }
205
+ ```
206
+
207
+
208
+ #### onEnter()
209
+
210
+ The `onEnter()` function runs after the `enter` event has fired.
211
+
212
+ ```javascript
213
+ this.onEnter() {
214
+ // some code to run when the deva is loaded
215
+ }
216
+ ```
217
+
218
+
219
+ #### onExit()
220
+
221
+ The `onExit()`function runs after the `exit` event has fired.
222
+
223
+ ```javascript
224
+ this.onExit() {
225
+ // some code to run when the deva logs out.
226
+ }
227
+ ```
228
+
229
+ #### onDone()
230
+
231
+ The `onDone()`function runs after the `done` event has fired.
232
+
233
+ ```javascript
234
+ this.onDone() {
235
+ // some code to run when the deva logs out.
236
+ }
237
+ ```
238
+
239
+
240
+ #### onInit()
241
+
242
+ The `onInit()` function runs after the `init()` function has completed.
243
+
244
+ ```javascript
245
+ this.onInit() {
246
+ // some code to run when the Deva initializes.
247
+ }
248
+ ```
249
+
250
+ ## Utility Functions
251
+
252
+ ### uid()
253
+ Generates a unique ID that is used in packet transfer and other various ways.
254
+
255
+ ```javascript
256
+ this.uid() // inside the object
257
+ deva.uid() // outside the object
258
+
259
+ // example
260
+ this.vars.id = this.uid()
261
+
262
+ ```
263
+
264
+ ### talk(evt, resource=false)
265
+
266
+ The `talk()` function is used when your Deva needs to broadcast an event that other Deva
267
+ or functions would be listening for.
268
+
269
+ ```javascript
270
+ this.talk('event', resource); // inside the object
271
+ deva.talk('event', resource); // outside the object
272
+
273
+ // example
274
+ const evt_id = this.uid();
275
+ const evt_data = {
276
+ task_id: 1,
277
+ task_name: 'this is blank data',
278
+ task_contact: 'joe@schmo.com',
279
+ };
280
+
281
+ this.talk('big-event', {
282
+ id: evt_id,
283
+ key: this.me.key,
284
+ q: {
285
+ bot: this.me,
286
+ text: 'text to send to the event',
287
+ data: evt_data,
288
+ },
289
+ created: Date.now(),
290
+ });
291
+ ```
292
+
293
+ ### listen(evt callback)
294
+
295
+ The `listen` function can assign listeners to the Deva and designate which `callback`
296
+ function to run when an event is fired.
297
+
298
+ Listeners can be set up individually this way or also added to the listeners object
299
+ independently.
300
+
301
+ ```javascript
302
+ this.listen('some-event', this.func.listener);
303
+
304
+ this.func.listenter = packet => {
305
+ console.log('some-event-fired');
306
+ };
307
+
308
+ ```
309
+
310
+ ### once(evt, callback)
311
+
312
+ The `once()` function can assign a one-time listener to a function. This is useful when returning data with an id that one Deva has submitted to another Deva. Also very useful for submit responses that are unique to the request.
313
+
314
+ ```javascript
315
+ this.once(`some-once-event`, this.func.listener)
316
+ this.func.listener = packet => {
317
+ console.log('some-once-event-fired');
318
+ }
319
+
320
+ ```
321
+
322
+ ### ignore(evt, callback)
323
+
324
+ The `ignore()` function removes a listener from the designated event. This is useful for adding and removing events dynamically or as needed.
325
+
326
+ ```javascript
327
+ this.ignore('ignore-event', this.func.listener);
328
+ this.func.listener = packet => {
329
+ console.log('ignore-event-fired');
330
+ }
331
+ ```
332
+
333
+
334
+ ### Load(agent, opts)
335
+
336
+ To add a Deva dynamically use the `load()` function. This can be utilized to add Deva to an existing Deva after the object has already been created.
337
+
338
+ ```javascript
339
+ const opts = {
340
+ me: {...},
341
+ vars: {...},
342
+ listeners: {...},
343
+ deva: {...},
344
+ func: {...},
345
+ onStart() {},
346
+ onStop() {},
347
+ onEnter() {},
348
+ onExit() {},
349
+ }
350
+ this.load('deva-name', opts);
351
+ ```
352
+
353
+ ### unload(agent)
354
+ To delete a Deva for any reason use `unload()`. This will delete the Deva and all it's parts from the current Deva.
355
+
356
+ ```javascript
357
+
358
+ this.unload('deva-key');
359
+
360
+ ```
361
+
362
+ ### question(packet)
363
+ The `question(packet)` function is a default function that allows the system to ask questions of itself or other Deva.
364
+
365
+ The function checks the beginning of a string for a `#` to determine wether to issue a command to run a specific method.
366
+
367
+ See [Question Listener](#question) for usage.
368
+
369
+ ```bash
370
+ #deva method:params message
371
+ ```
372
+
373
+
374
+ ### status()
375
+ The `status()` function will return the running status of the current Deva.
376
+
377
+ ### start()
378
+ The `start()` function will start the Deva and run the `onStart()` state function.
379
+
380
+ ### stop()
381
+ The `stop()` function will stop the Deva and run the `onStop()` state function.
382
+
383
+ ### init(deva=false)
384
+ The `init()` function will initialize the Deva and run the `onInit()` state function.
385
+
386
+ ### initDeva()
387
+ The `initDeva()` function will initialize the Deva located under the current Deva set. To be used in instances of a main Deva parent situation.
388
+
389
+ -
390
+
391
+ ©2021 Quinn Michaels; All Rights Reserved.
@@ -0,0 +1,61 @@
1
+ // Copyright (c)2021 Quinn Michaels
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file LICENSE.md or http://www.opensource.org/licenses/mit-license.php.
4
+ const Deva = require('../index');
5
+ const HelloWorld = new Deva({
6
+ agent: {
7
+ key: 'hello',
8
+ name: 'Hello World',
9
+ description: 'The most over complex Hello World in the Universe',
10
+ prompt: {
11
+ emoji: '🐶',
12
+ text: 'hello',
13
+ color: 'white',
14
+ },
15
+ voice: {
16
+ speech: 'Alex',
17
+ speed: 1
18
+ },
19
+ profile: {
20
+ avatar: '',
21
+ background: '',
22
+ describe: 'Hello World Deva',
23
+ gender: 'N',
24
+ },
25
+ translate(input) {
26
+ return input.trim();
27
+ },
28
+ parse(input) {
29
+ return input.trim();
30
+ }
31
+ },
32
+ vars: {
33
+ hello: 'Hello World'
34
+ },
35
+ listeners: {},
36
+ deva: {},
37
+ modules: {},
38
+ func: {
39
+ hello() {
40
+ return this.agent.translate(this.vars.hello);
41
+ }
42
+ },
43
+ methods: {
44
+ hello() {
45
+ return this.func.hello();
46
+ }
47
+ },
48
+
49
+ onStart() {
50
+ console.log(this.methods.hello());
51
+ },
52
+
53
+ onStop() {},
54
+ onEnter() {},
55
+ onExit() {},
56
+ onDone() {},
57
+ onInit() {
58
+ this.start();
59
+ },
60
+ });
61
+ HelloWorld.init();
package/index.js ADDED
@@ -0,0 +1,528 @@
1
+ // Copyright (c)2021 Quinn Michaels
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file LICENSE.md or http://www.opensource.org/licenses/mit-license.php.
4
+ const {EventEmitter} = require('events');
5
+ class Deva {
6
+ constructor(opts) {
7
+ opts = opts || {};
8
+ this._uid = this.uid(); // the unique id assigned to the agent at load
9
+ this.state = 'offline'; // current state of agent.
10
+ this.active = false; // the active/birth date.
11
+ this.security = false; // inherited Security features.
12
+ this.config = opts.config || {}; // local Config Object
13
+ this.events = opts.events || new EventEmitter({}); // Event Bus
14
+ this.lib = opts.lib || {}; // used for loading library functions
15
+ this.agent = opts.agent || false; // Agent profile object
16
+ this.client = opts.client || false; // Client profile object
17
+ this.devas = opts.devas || {}; // Devas which are loaded
18
+ this.vars = opts.vars || {}; // Variables object
19
+ this.listeners = opts.listeners || {}; // local Listeners
20
+ this.modules = opts.modules || {}; // 3rd Party Modules
21
+ this.func = opts.func || {}; // local Functions
22
+ this.methods = opts.methods || {}; // local Methods
23
+ this.maxListeners = opts.maxListenners || 0; // set the local maxListeners
24
+
25
+ for (var opt in opts) {
26
+ if (!this[opt]) this[opt] = opts[opt]; // set any remaining opts to this.
27
+ }
28
+
29
+ this.cmdChr = '!';
30
+ this.askChr = '#';
31
+ this.inherit = ["events", "config", "lib", "security", "client"];
32
+ this.bind = ["listeners", "methods", "func", "lib", "security", "agent", "client"];
33
+ this.states = {
34
+ offline: 'OFFLINE',
35
+ init: 'INITIALIZE',
36
+ start: 'START',
37
+ stop: 'STOP',
38
+ enter: 'ENTER',
39
+ exit: 'EXIT',
40
+ done: 'DONE',
41
+ wait: 'WAITING',
42
+ ask: 'ASK',
43
+ question: 'QUESTION',
44
+ answer: 'ANSWER',
45
+ error: 'ERROR',
46
+ security: 'SECURITY',
47
+ medic: 'MEDICAL',
48
+ };
49
+ this.messages = {
50
+ offline: 'AGENT OFFLINE',
51
+ loaded: 'DEVAS LOADED',
52
+ stopped: 'DEVAS STOPPED',
53
+ notext: 'NO TEXT',
54
+ }
55
+ }
56
+
57
+ // set the state of the agent with the passed value to match the valid keys.
58
+ // this will also talk a global state event with the agent data and state.
59
+ // then security can watch all the glorious state change events.
60
+ set _state(st) {
61
+ this.state = this.states[st];
62
+ this.talk(`state`,{
63
+ uid: this.uid(),
64
+ agent: this.agent,
65
+ state: this.state,
66
+ created: Date.now(),
67
+ });
68
+ this.prompt(this.state);
69
+ }
70
+ // Called from the init function to bind the elements defined in the this.bind variable.
71
+ // the assign bind ensures that the *this* scope is available to child elements/functions.
72
+ _assignBind() {
73
+ return new Promise((resolve, reject) => {
74
+ try {
75
+ this.bind.forEach(bind => {
76
+ if (this[bind]) for (let x in this[bind]) {
77
+ if (typeof this[bind][x] === 'function') {
78
+ this[bind][x] = this[bind][x].bind(this);
79
+ }
80
+ }
81
+ });
82
+ // bind translate
83
+ const translate = this.agent && this.agent.translate && typeof this.agent.translate === 'function';
84
+ if (translate) this.agent.translate = this.agent.translate.bind(this);
85
+ // bind parser
86
+ const parse = this.agent && this.agent.parse && typeof this.agent.parse === 'function';
87
+ if (parse) this.agent.parse = this.agent.parse.bind(this);
88
+ }
89
+ catch (e) {
90
+ return reject(e);
91
+ }
92
+ finally {
93
+ return resolve();
94
+ }
95
+ });
96
+ }
97
+
98
+ // Called from the init function to assign the listeners to various states.
99
+ // when the listener fires it will call the associated named function.
100
+ _assignListeners() {
101
+ return new Promise((resolve, reject) => {
102
+ try {
103
+ // set the default listeners for the states of the agent.
104
+ for (let state in this.states) {
105
+ this.events.on(`${this.agent.key}:${state}`, packet => {
106
+ return this[state](packet);
107
+ })
108
+ }
109
+ // set the assigned listeners for the agent.
110
+ for (let listener in this.listeners) {
111
+ this.events.on(listener, packet => {
112
+ return this.listeners[listener](packet);
113
+ })
114
+ }
115
+ }
116
+ catch (e) {
117
+ return reject(e);
118
+ }
119
+ finally {
120
+ return resolve();
121
+ }
122
+ });
123
+ }
124
+
125
+ // Some elements will inherit the data of the parent. this object will loop over
126
+ // any children data that theis deva has and assign the inherited information.
127
+ _assignInherit() {
128
+ return new Promise((resolve, reject) => {
129
+ try {
130
+ for (let d in this.devas) {
131
+ this.inherit.forEach(inherit => {
132
+ this.devas[d][inherit] = this[inherit];
133
+ });
134
+ }
135
+ }
136
+ catch (e) {
137
+ return reject(e);
138
+ }
139
+ finally {
140
+ return resolve();
141
+ }
142
+ });
143
+ }
144
+
145
+ // General handler for when a method is NOT found from a user command.
146
+ _methodNotFound(packet) {
147
+ packet.a = {
148
+ agent: this.agent || false,
149
+ client: this.client || false,
150
+ text: `${packet.q.meta.method} is NOT a valid method.`,
151
+ meta: {
152
+ key: this.agent.key,
153
+ method: packet.q.meta.method,
154
+ },
155
+ created: Date.now(),
156
+ };
157
+ return packet;
158
+ }
159
+
160
+ // A simple interface to generate a unique id for the agent. As agents will
161
+ // quite oftne have to key their transactions. This will provide all agents
162
+ // with the same key generator which can also be modified to link into a remote
163
+ // generator or some other solution if needed.
164
+ uid() {
165
+ const min = Math.floor(Date.now() - (Date.now() / Math.PI));
166
+ const max = Math.floor(Date.now() + (Date.now() * Math.PI));
167
+ return Math.floor(Math.random() * (max - min)) + min;
168
+ }
169
+
170
+ // The talk interface binds to the events emitter to allow agents to perform a
171
+ // this.talk(*) feature.
172
+ talk(evt, resource=false) {
173
+ return this.events.emit(evt, resource);
174
+ }
175
+
176
+ // the listen interface binds to the event emitter to allow agents to listen
177
+ // this.listen(*) feature.
178
+ listen(evt, callback) {
179
+ return this.events.on(evt, callback);
180
+ }
181
+
182
+ // Instances where listening for only one unique keyed event is required
183
+ // this interface is provided for the this.once(*) feature.
184
+ once(evt, callback) {
185
+ return this.events.once(evt, callback);
186
+ }
187
+
188
+ // where an agent needs to ignore events or remove a lisatener this interface
189
+ // serves the this.ignore(*) feature.
190
+ ignore(evt, callback) {
191
+ return this.events.removeListener(evt, callback);
192
+ }
193
+
194
+ // Used when loading a Deva dynamically into the current set.
195
+ load(opts) {
196
+ this.devas[opts.key] = opts;
197
+ // inherit the data to the new deva.
198
+ this.inherit.forEach(inherit => {
199
+ this.devas[agent][inherit] = this[inherit];
200
+ });
201
+
202
+ return Promise.resolve();
203
+ }
204
+
205
+ // Used when unloading a deva dynamically from the set.
206
+ unload(agent) {
207
+ delete this.devas[agent];
208
+ return Promise.resolve();
209
+ }
210
+
211
+ // Askign a question to another deva in the set besides itself. If the question
212
+ // interface detects the action is to ask another deva a question with
213
+ // this.askChr variable match. Then an answer packet is generated and the
214
+ // deva is asked to process the question asked and return it's proper data set
215
+ // from the requested method.
216
+ // this is an event function that relies on talk/listen
217
+
218
+ ask(packet) {
219
+ if (!this.active) return Promise.resolve(this.messages.offline);
220
+
221
+ this._state = this.states.ask;
222
+ packet.a = {
223
+ agent: this.agent || false,
224
+ client: this.client || false,
225
+ meta: {
226
+ key: this.agent.key,
227
+ method: packet.q.meta.method,
228
+ params: packet.q.meta.params,
229
+ },
230
+ text: false,
231
+ html: false,
232
+ data: false,
233
+ created: Date.now(),
234
+ };
235
+
236
+ try {
237
+ if (this.methods[packet.q.meta.method] === undefined) {
238
+ return setImmediate(() => {
239
+ packet.a.text = `INVALID METHOD (${packet.q.meta.method})`;
240
+ this.talk(`${this.agent.key}:ask:${packet.id}`, packet);
241
+ });
242
+ }
243
+
244
+ // The method is parsed and depending on what method is asked for it returns
245
+ // the response based on the passed through packet.
246
+ this.methods[packet.q.meta.method](packet).then(result => {
247
+ if (typeof result === 'object') {
248
+ packet.a.text = result.text || false;
249
+ packet.a.html = result.html || false;
250
+ packet.a.data = result.data || false;
251
+ }
252
+ else {
253
+ packet.a.text = result;
254
+ }
255
+ // talk back to the once event with the ask key.
256
+ this.talk(`${this.agent.key}:ask:${packet.id}`, packet);
257
+ }).catch(err => {
258
+ this._state = this.states.error;
259
+ // If the ask method fails then a reject error is returned from the this.error
260
+ // interface.
261
+ this.talk(`${this.agent.key}:ask:${packet.id}`, {error:err.toString()});
262
+ return this.error(err, packet);
263
+ })
264
+ }
265
+ catch (e) {
266
+ this._state = this.states.error;
267
+ // if any error is caught in the processing of the ask then it returns and
268
+ // executes the this.error(*) interface.
269
+ this.talk(`${this.agent.key}:ask:${packet.id}`, {error:e.toString()});
270
+ return this.error(e, packet)
271
+ }
272
+ // now when we ask the meta params[0] should be the method
273
+ }
274
+
275
+ // general question interface.
276
+ // accepts two arguments a *TEXT* and *DATA* object.
277
+ // if the question being asked is a simple text command then
278
+ // this.question('#*agent.key *method* *text*')
279
+ // if the question is a data object
280
+ // this.question('#*agent.key* *method* *properties*', {*data*});
281
+ question(TEXT=false, DATA=false) {
282
+ this._state = this.states.question;
283
+ const id = this.uid();
284
+ const t_split = TEXT.split(' ');
285
+ const isAsk = t_split[0].startsWith(this.askChr) ? t_split[0].substring(1) : false;
286
+ const isCmd = t_split[0].startsWith(this.cmdChr) ? t_split[0].substring(1) : false;
287
+ // Format the packet for return on the request.
288
+ const orig = TEXT;
289
+ const data = DATA;
290
+ const packet = {
291
+ id,
292
+ q: {},
293
+ a: {},
294
+ created: Date.now(),
295
+ };
296
+
297
+ let text = TEXT,
298
+ params = false,
299
+ method = 'question',
300
+ key = this.agent.key;
301
+
302
+ return new Promise((resolve, reject) => {
303
+ if (!TEXT) return reject(this.messages.notext);
304
+ try {
305
+ if (!this.active) return reject(this.messages.offline);
306
+
307
+ // *: send just a string of text
308
+ // !: send a command to the local agent
309
+ // #: ask another agent a question
310
+ // #agent method:param1:param2 with text strings for proccessing
311
+ // !method param:list:parse for the local agent
312
+ // if is an ask then we format one way
313
+ if (isAsk) {
314
+ params = t_split[1] ? t_split[1].split(':') : false;
315
+ method = params[0];
316
+ text = t_split.slice(2).join(' ').trim();
317
+ key = isAsk;
318
+ }
319
+ else if (isCmd) {
320
+ params = t_split[1] ? t_split[1].split(':') : false;
321
+ method = isCmd;
322
+ text = t_split.slice(1).join(' ').trim()
323
+ }
324
+
325
+ packet.q = {
326
+ agent: this.agent || false,
327
+ client: this.client || false,
328
+ meta: {
329
+ key,
330
+ orig,
331
+ method,
332
+ params,
333
+ },
334
+ text,
335
+ data,
336
+ created: Date.now(),
337
+ }
338
+
339
+ // if is a command then we format another way
340
+ // if the user asks a question to another deva '#' then issue the talk/once
341
+ // event combination.
342
+ if (isAsk) {
343
+ this._state = this.states.ask;
344
+ this.talk(`${isAsk}:ask`, packet);
345
+ this.once(`${isAsk}:ask:${packet.id}`, answer => {
346
+ return resolve(answer);
347
+ });
348
+ }
349
+ // if the user sends a local command '$' then it will ask of the self.
350
+ else {
351
+ if (typeof this.methods[method] !== 'function') return resolve(this._methodNotFound(packet));
352
+ this.methods[method](packet).then(result => {
353
+ const text = typeof result === 'object' ? result.text : result;
354
+ const html = typeof result === 'object' ? result.html : result;
355
+ const data = typeof result === 'object' ? result.data : false;
356
+ packet.a = {
357
+ agent: this.agent || false,
358
+ client: this.client || false,
359
+ meta: {
360
+ key: this.agent.key,
361
+ method,
362
+ },
363
+ text,
364
+ html,
365
+ data,
366
+ created: Date.now(),
367
+ };
368
+ this._state = this.states.answer;
369
+ return resolve(packet);
370
+ }).catch(err => {
371
+ return this.error(err, packet);
372
+ });
373
+ }
374
+ }
375
+ catch(e) {
376
+ return this.error(e);
377
+ }
378
+ });
379
+ }
380
+
381
+
382
+ // The main init interface where the chain begins.
383
+ // a set of options is passed into the init function which is the configuration
384
+ // object. from this opts object the system is built. After the opts object is processed
385
+ // the inherit is assigned and then bind then listners then
386
+ // opts: The options object containing the necessary vaules to build a Deva.
387
+ init() {
388
+ // set client
389
+ this._state = this.states.init;
390
+ return new Promise((resolve, reject) => {
391
+ this.events.setMaxListeners(this.maxListeners);
392
+ this._assignInherit().then(() => {
393
+ return this._assignBind();
394
+ }).then(() => {
395
+ return this._assignListeners();
396
+ }).then(() => {
397
+ return this.onInit ? this.onInit() : this.start();
398
+ }).then(started => {
399
+ return resolve(started)
400
+ }).catch(err => {
401
+ return reject(err);
402
+ });
403
+ });
404
+ }
405
+
406
+ // Interface for unified error reporting within all devas.
407
+ // this.error(*error*, *packet*) can be issued at time of any error.
408
+ // e: is the error to pass into the interface.
409
+ // packet: the packet that caused the error.
410
+ error(err,packet=false,reject=false) {
411
+ this._state = 'error';
412
+ // broadcast a global uniform error event.
413
+ this.talk('error', {
414
+ id: this.uid(),
415
+ agent: this.agent,
416
+ client: this.client,
417
+ error: err.toString('utf8'),
418
+ data: packet,
419
+ created: Date.now(),
420
+ });
421
+ // call the onError if there is a logcal one.
422
+ // if there is no local error return a promise reject.
423
+ if (this.onError) return this.onError(err, packet, reject);
424
+ return reject ? reject(err) : false;
425
+ }
426
+
427
+ // start the deva then return the 'onStart' function.
428
+ start() {
429
+ if (this.active) return;
430
+ this._state = 'start';
431
+ this.active = Date.now();
432
+ return this.onStart ? this.onStart() : this.enter();
433
+ }
434
+
435
+ // stop teh deva then return the onStop function.
436
+ stop() {
437
+ if (!this.active) return Promise.resolve(this.messages.offline);
438
+ this.active = false;
439
+ this._state = 'stop';
440
+ return this.onStop ? this.onStop() : this.exit();
441
+ }
442
+
443
+ // enter the deva then return the onEnter function.
444
+ enter() {
445
+ if (!this.active) return Promise.resolve(this.messages.offline);
446
+ this._state = 'enter';
447
+ return this.onEnter ? this.onEnter() : this.done(this.state)
448
+ }
449
+
450
+ // exit the deva then return the onExit function.
451
+ exit() {
452
+ if (!this.active) return Promise.resolve(this.messages.offline);
453
+ this._state = 'exit';
454
+ return this.onExit ? this.onExit() : this.done(this.state)
455
+ }
456
+
457
+ // set the deva as done then return the oDone function.
458
+ done(msg=false) {
459
+ if (!this.active) return Promise.resolve(this.messages.offline);
460
+ this._state = 'done';
461
+ msg = msg ? msg : this.state;
462
+ return this.onDone ? this.onDone() : Promise.resolve({msg,agent:this.agent})
463
+ }
464
+
465
+ // interface to return the status of the current deva with the time/date requested.
466
+ status(addtl=false) {
467
+ if (!this.active) return Promise.resolve(this.messages.offline);
468
+ const id = this.uid();
469
+ const dateFormat = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeStyle: 'medium' }).format(this.active);
470
+ let text = `${this.agent.name} is ONLINE since ${dateFormat}`;
471
+ if (addtl) text = text + `\n${addtl}`;
472
+ return Promise.resolve({text});
473
+ }
474
+
475
+ // universal prompt emitter
476
+ prompt(text) {
477
+ this.talk('prompt', {text, prompt:this.agent.prompt});
478
+ }
479
+
480
+ // universal hash builder
481
+ hash(packet) {
482
+ if (!this.vars.hash) this.vars.hash = '0x';
483
+ // setup basic hashing
484
+ // what type of hash depends on param 1
485
+ return new Promise((resolve, reject) => {
486
+ if (!packet) return reject('NO PACKET');
487
+ const params = packet.q.text.split(' ');
488
+ const hashType = params[0] ? params[0] : 'view';
489
+ switch (hashType) {
490
+ case 'clear':
491
+ this.vars.hash = '0x';
492
+ break;
493
+ case 'add':
494
+ this.vars.hash = `${this.vars.hash}${params[1]}`;
495
+ break;
496
+ }
497
+ return resolve({
498
+ text:this.vars.hash,
499
+ html:`<div class="hash">${this.vars.hash}</div>`
500
+ });
501
+ });
502
+ }
503
+ // initDeva interface is to initialize devas that this deva is a parent of.
504
+ // This feature allows a Deva to be a parent of a parent of a parent etc....
505
+ initDevas() {
506
+ return new Promise((resolve, reject) => {
507
+ const devas = [];
508
+ for (let x in this.devas) {
509
+ devas.push(this.devas[x].init());
510
+ }
511
+ Promise.all(devas).then(() => {
512
+ return resolve(this.messages.loaded);
513
+ }).catch(reject);
514
+ });
515
+ }
516
+ stopDevas() {
517
+ return new Promise((resolve, reject) => {
518
+ const devas = [];
519
+ for (let x in this.devas) {
520
+ devas.push(this.devas[x].stop());
521
+ }
522
+ Promise.all(devas).then(() => {
523
+ return resolve(this.messages.stopped);
524
+ }).catch(reject);
525
+ });
526
+ }
527
+ }
528
+ module.exports = Deva;
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@indra.ai/deva",
3
+ "version": "1.0.27",
4
+ "description": "The Deva Core",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/indraai/deva.git"
12
+ },
13
+ "keywords": [
14
+ "indra",
15
+ "deva",
16
+ "ai"
17
+ ],
18
+ "author": "Quinn Michaels",
19
+ "license": "MIT",
20
+ "bugs": {
21
+ "url": "https://github.com/indraai/deva/issues"
22
+ },
23
+ "homepage": "https://github.com/indraai/deva#readme",
24
+ "eslintConfig": {
25
+ "parserOptions": {
26
+ "ecmaVersion": 6
27
+ }
28
+ },
29
+ "engines": {
30
+ "npm": ">=6.0.0",
31
+ "node": ">=10.0.0"
32
+ },
33
+ "dependencies": {}
34
+ }