@j-o-r/hello-dave 0.0.0

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.
Files changed (53) hide show
  1. package/LICENSE +73 -0
  2. package/README.md +207 -0
  3. package/bin/hdAsk.js +103 -0
  4. package/bin/hdClear.js +13 -0
  5. package/bin/hdCode.js +110 -0
  6. package/bin/hdConnect.js +230 -0
  7. package/bin/hdInspect.js +28 -0
  8. package/bin/hdNpm.js +114 -0
  9. package/bin/hdPrompt.js +108 -0
  10. package/examples/claude-test.js +89 -0
  11. package/examples/claude.js +143 -0
  12. package/examples/gpt.js +127 -0
  13. package/examples/gpt_code.js +125 -0
  14. package/examples/gpt_note_keeping.js +117 -0
  15. package/examples/grok.js +119 -0
  16. package/examples/grok_code.js +114 -0
  17. package/examples/grok_note_keeping.js +111 -0
  18. package/lib/API/anthropic.com/text.js +402 -0
  19. package/lib/API/brave.com/search.js +239 -0
  20. package/lib/API/openai.com/README.md +1 -0
  21. package/lib/API/openai.com/reponses/MESSAGES.md +69 -0
  22. package/lib/API/openai.com/reponses/text.js +416 -0
  23. package/lib/API/x.ai/text.js +415 -0
  24. package/lib/AgentClient.js +197 -0
  25. package/lib/AgentManager.js +144 -0
  26. package/lib/AgentServer.js +336 -0
  27. package/lib/Cli.js +256 -0
  28. package/lib/Prompt.js +728 -0
  29. package/lib/Session.js +231 -0
  30. package/lib/ToolSet.js +186 -0
  31. package/lib/fafs.js +93 -0
  32. package/lib/genericToolset.js +170 -0
  33. package/lib/index.js +34 -0
  34. package/lib/promptHelpers.js +132 -0
  35. package/lib/testToolset.js +42 -0
  36. package/module.md +189 -0
  37. package/package.json +49 -0
  38. package/types/API/anthropic.com/text.d.ts +207 -0
  39. package/types/API/brave.com/search.d.ts +156 -0
  40. package/types/API/openai.com/reponses/text.d.ts +225 -0
  41. package/types/API/x.ai/text.d.ts +286 -0
  42. package/types/AgentClient.d.ts +70 -0
  43. package/types/AgentManager.d.ts +112 -0
  44. package/types/AgentServer.d.ts +38 -0
  45. package/types/Cli.d.ts +52 -0
  46. package/types/Prompt.d.ts +298 -0
  47. package/types/Session.d.ts +31 -0
  48. package/types/ToolSet.d.ts +95 -0
  49. package/types/fafs.d.ts +47 -0
  50. package/types/genericToolset.d.ts +3 -0
  51. package/types/index.d.ts +23 -0
  52. package/types/promptHelpers.d.ts +1 -0
  53. package/types/testToolset.d.ts +3 -0
package/lib/Session.js ADDED
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Records sessions
3
+ * Get sessions
4
+ * This is an optional extension for the prompt
5
+ */
6
+
7
+ import path from 'path';
8
+ import { CacheSync } from '@j-o-r/cache';
9
+ import Prompt from './Prompt.js';
10
+
11
+ /**
12
+ * @typedef {import('./Prompt.js').Message} Message
13
+ * @typedef {import('./Prompt.js').Record} Record
14
+ */
15
+
16
+ /**
17
+ * Get the next version number for a given key in the array.
18
+ *
19
+ * @param {string[]} arr - The array of versioned strings.
20
+ * @param {string} key - The key to check for the highest version.
21
+ * @returns {number} - The next version number.
22
+ */
23
+ const getNextVersion = (arr, key) => {
24
+ const regex = new RegExp(`^${key}_(\\d+)$`);
25
+ const versions = arr
26
+ .map(item => {
27
+ const match = item.match(regex);
28
+ // @ts-ignore
29
+ return match ? parseInt(match[1], 10) : null;
30
+ })
31
+ .filter(version => version !== null);
32
+ return versions.length > 0 ? Math.max(...versions) + 1 : 0;
33
+ };
34
+
35
+ /**
36
+ */
37
+ class Session {
38
+ /**
39
+ * sticky message que
40
+ * @type {Message[]}
41
+ */
42
+ #que = [];
43
+ /** @type {string} session name */
44
+ #sessionName = '';
45
+ /** @type {number} count the number of session iterations */
46
+ #sessionCounter = 0;
47
+ /** @type {CacheSync} */
48
+ #sessionCache;
49
+ /** @type {CacheSync} */
50
+ #recordCache;
51
+ /** @type {CacheSync} */
52
+ #logCache;
53
+
54
+ /**
55
+ * Create and set the cache folders
56
+ * @param {string} promptName - The name of the prompt.
57
+ * @param {Prompt} prompt - The prompt
58
+ * @param {string} [baseFolder] - The base storage folder, default this is process.cwd()
59
+ */
60
+ #inititializeCache = (promptName, prompt, baseFolder) => {
61
+ // If not base folder, we take FOLDERS.ROOT
62
+ // else we use the baseFolder
63
+ if (!baseFolder) {
64
+ // Relative from cwd()
65
+ baseFolder = path.resolve('.cache', 'hello-dave');
66
+ }
67
+ // Create the root folder, just in case
68
+ const root = new CacheSync(baseFolder, true, 'any');
69
+ // this.#baseStorage = baseFolder;
70
+ this.name = root.sanitizeKey(promptName);
71
+ const sessionStorage = path.resolve(baseFolder, this.name);
72
+ // Create the session folder
73
+ new CacheSync(sessionStorage, true, 'any');
74
+ // Create the records cache
75
+ this.#logCache = new CacheSync(path.resolve(sessionStorage, 'logs'), true, 'ndjson');
76
+ this.#recordCache = new CacheSync(path.resolve(sessionStorage, 'records'), true, 'ndjson');
77
+ if (this.name.length === 0) {
78
+ throw new Error(`Invalid session name: ${promptName}`);
79
+ }
80
+ // create and init the session storage
81
+ this.#sessionCache = new CacheSync(path.resolve(sessionStorage, 'sessions'), true, 'ndjson');
82
+ prompt.on(prompt.EVENTS.message, (msg) => {
83
+ this.#msg(msg);
84
+ });
85
+ prompt.on(prompt.EVENTS.record, (rec) => {
86
+ this.#rec(rec);
87
+ });
88
+ prompt.on(prompt.EVENTS.reset, () => {
89
+ // Prompt has only sticky messages
90
+ // fill the que
91
+ this.#sessionCounter = 0;
92
+ this.#sessionName = '';
93
+ this.#que = prompt.messages;
94
+ });
95
+ prompt.on(prompt.EVENTS.truncated, () => {
96
+ // Prompt has only sticky messages
97
+ // fill the que
98
+ this.#sessionCounter = 0;
99
+ this.#sessionName = '';
100
+ this.#que = prompt.messages;
101
+ });
102
+ const events = Object.keys(prompt.EVENTS);
103
+ events.forEach((evt) => {
104
+ prompt.on(evt, (msg) => {
105
+ this.#log({ evt, msg });
106
+ });
107
+ })
108
+ }
109
+ /**
110
+ * Constructs a new Prompt instance.
111
+ * If contextWindow = 0 (defaut) the prompt will have no context building up (ONE_SHOT)
112
+ * @param {string} name - The name of the prompt.
113
+ * @param {Prompt} prompt - The prompt
114
+ * @param {string} [storage] - The base storage folder, default this is process.cwd()
115
+ */
116
+ constructor(name, prompt, storage) {
117
+ this.#inititializeCache(name, prompt, storage)
118
+ }
119
+
120
+ info() {
121
+ const name = this.name;
122
+ const session = this.#sessionName;
123
+ const sessionCount = this.#sessionCounter;
124
+ const path = this.#sessionCache.folder;
125
+ return `Name: ${name}\nSession: ${session}\nSessionCount: ${sessionCount}\nPath: ${path}`;
126
+ }
127
+ /**
128
+ * Add message to the cache
129
+ * @param {import('./Prompt.js').Message} msg
130
+ */
131
+ #msg(msg) {
132
+ const role = msg.role;
133
+ const sticky = msg.sticky;
134
+ let sessKey;
135
+ if (this.#sessionName === '' && role === 'user' && sticky === false) {
136
+ // Create a sessioname
137
+ if (msg.content.length > 0 && msg.content[0].type === 'text') {
138
+ /**
139
+ * @param {string} str
140
+ */
141
+ const truncateString = (str) => str.length > 50 ? str.slice(0, 80) : str;
142
+ // @ts-ignore
143
+ this.#sessionName = this.#sessionCache.sanitizeKey(truncateString(msg.content[0].text).trim());
144
+ this.#sessionCounter = getNextVersion(this.#sessionCache.list(), this.#sessionName);
145
+ sessKey = this.#sessionCache.sanitizeKey(`${this.#sessionName}_${this.#sessionCounter}`);
146
+ }
147
+ } else if(this.#sessionName !== '') {
148
+ sessKey = this.#sessionCache.sanitizeKey(`${this.#sessionName}_${this.#sessionCounter}`);
149
+ }
150
+ if (sessKey) {
151
+ // Alway write the que if there is a name
152
+ // May the must be written sticky nots or a truncated previous sessions
153
+ if (this.#que.length > 0) {
154
+ // Write outstanding messages
155
+ let i = 0;
156
+ const len = this.#que.length;
157
+ for (; i < len; i++) {
158
+ this.#sessionCache.append(sessKey, this.#que[i]);
159
+ }
160
+ this.#que = [];
161
+ }
162
+ this.#sessionCache.append(sessKey, msg);
163
+ } else {
164
+ this.#que.push(msg);
165
+ }
166
+ }
167
+
168
+ /**
169
+ * store a record
170
+ * @param {Record} record - Records for billing and optimizing
171
+ * @throws {Error} If the record is invalid.
172
+ */
173
+ #rec(record) {
174
+ const key = new Date().toISOString().split('T')[0];
175
+ this.#recordCache.append(key, record);
176
+ }
177
+
178
+ /**
179
+ * log all events
180
+ * @param {*} o - just log
181
+ * @throws {Error} If the record is invalid.
182
+ */
183
+ #log(o) {
184
+ const key = new Date().toISOString().split('T')[0];
185
+ this.#logCache.append(key, o);
186
+ }
187
+ sessionList() {
188
+ const list = this.#sessionCache.list();
189
+ const processedSet = new Set(
190
+ list.map(str => {
191
+ // Remove the last '_[number]' part
192
+ const withoutNumber = str.replace(/_\d+$/, '');
193
+ // Replace '_' with ' '
194
+ return withoutNumber.replace(/_/g, ' ');
195
+ })
196
+ );
197
+ return [...processedSet];
198
+ }
199
+
200
+ /**
201
+ * Get messages fro, a session
202
+ *
203
+ * @param {string} sess - The session name.
204
+ * @return {Message[]}
205
+ */
206
+ set(sess) {
207
+ const counter = getNextVersion(this.#sessionCache.list(), sess);
208
+ if (counter === 0) {
209
+ this.#sessionCounter = counter;
210
+ } else {
211
+ this.#sessionCounter = counter - 1;
212
+ }
213
+ // Load last know session
214
+ const sessKey = this.#sessionCache.sanitizeKey(`${sess}_${this.#sessionCounter}`);
215
+ const res = this.#sessionCache.read(sessKey) || [];
216
+
217
+ if (res) {
218
+ this.#sessionName = sess;
219
+ }
220
+ return res;
221
+ }
222
+ /**
223
+ * Remove all previous sessions and records
224
+ */
225
+ empty() {
226
+ this.#sessionCache.empty();
227
+ this.#recordCache.empty();
228
+ }
229
+ }
230
+
231
+ export default Session;
package/lib/ToolSet.js ADDED
@@ -0,0 +1,186 @@
1
+ import Prompt from "./Prompt.js";
2
+ /**
3
+ * @module @j-o-r/toolset
4
+ * Register function_calls / tools
5
+ * This library can be used stand-alone or as a wrapper for AI functions_calls tools
6
+ */
7
+
8
+ /**
9
+ * @typedef {object} TSSchema
10
+ * @property {string} type - Type of the object
11
+ * @property {object} properties - Description of properties
12
+ * @property {string[]} [required] - Required properties
13
+ */
14
+ /**
15
+ * @typedef {object} TSTool
16
+ * @property {string} description - Command description
17
+ * @property {TSSchema} parameters - JS JSON schema
18
+ * @property {function(object): Promise} method
19
+ */
20
+ /**
21
+ * @typedef {object} TSToolListItem
22
+ * @property {string} name - Command name
23
+ * @property {string} description - Command description
24
+ * @property {TSSchema} parameters - JS JSON schema
25
+ */
26
+
27
+ /** Type of choices */
28
+ const CHOICES = {
29
+ NONE: 'none',
30
+ AUTO: 'auto',
31
+ REQUIRED: 'required'
32
+ };
33
+
34
+ /**
35
+ * The function name is limited
36
+ * @param {string} s
37
+ * @returns {boolean}
38
+ */
39
+ const isValidName = (s) => /^[#!a-z_0-9]{2,}$/.test(s);
40
+
41
+ class ToolSet {
42
+ /** @type {Map<string, TSTool>} */
43
+ #tools = new Map();
44
+ /** default auto */
45
+ #toolChoice = 'auto';
46
+
47
+ /**
48
+ * @param {string} [choice] - Default 'auto' auto|none|required
49
+ */
50
+ constructor(choice = 'auto') {
51
+ if (choice && Object.values(CHOICES).includes(choice)) {
52
+ this.#toolChoice = choice;
53
+ } else if (choice) {
54
+ throw new Error('Tool choice not defined');
55
+ }
56
+ }
57
+
58
+ /**
59
+ * How many functions have we registered
60
+ * @returns {number}
61
+ */
62
+ get length() {
63
+ return this.#tools.size;
64
+ }
65
+
66
+ /**
67
+ * Register a Tool/Command/Function
68
+ * @param {string} name - [a-z_0-9]{2,} The lowercase string name of the callback e.g. 'get_node_version'
69
+ * @param {string} description - What does it do
70
+ * @param {TSSchema} parameters - SCHEMA object describing the function's input parameters
71
+ * @param {function(object): Promise<*>} method - Async function to call
72
+ */
73
+ add(name, description, parameters, method) {
74
+ if (!isValidName(name)) {
75
+ throw new Error('Invalid name /[a-z_0-9]{2,}/');
76
+ }
77
+ if (this.has(name)) {
78
+ // throw new Error('Function already defined');
79
+ }
80
+ this.#tools.set(name, { description, parameters, method });
81
+ }
82
+
83
+ /**
84
+ * Get a tool from the toolset
85
+ * @param {string} name
86
+ * @returns {TSTool}
87
+ */
88
+ get(name) {
89
+ if (!this.has(name)) {
90
+ throw new Error('Function not found');
91
+ }
92
+ return this.#tools.get(name);
93
+ }
94
+ /**
95
+ * Delete a function_call
96
+ * @param {string} name
97
+ */
98
+ delete(name) {
99
+ this.#tools.delete(name);
100
+ }
101
+ /**
102
+ * Is 'name' already registered
103
+ * @param {string} name
104
+ * @returns {boolean}
105
+ */
106
+ has(name) {
107
+ return this.#tools.has(name);
108
+ }
109
+
110
+ /**
111
+ * Get a list of tools available
112
+ * @returns {TSToolListItem[]}
113
+ */
114
+ list() {
115
+ return Array.from(this.#tools.entries()).map(([name, value]) => ({
116
+ name,
117
+ description: value.description,
118
+ parameters: value.parameters
119
+ })).sort((a, b) => a.name.localeCompare(b.name));
120
+ }
121
+
122
+ get toolChoice() {
123
+ return this.#toolChoice;
124
+ }
125
+ /**
126
+ * Execute a method
127
+ * @param {string} name
128
+ * @param {object} params
129
+ */
130
+ async call(name, params) {
131
+ if (!this.has(name)) {
132
+ throw new Error('Function not found');
133
+ }
134
+ const tool = this.#tools.get(name);
135
+ return tool.method(params);
136
+ }
137
+ /**
138
+ * Execute function requests from the last message.
139
+ * @param {Prompt} prompt - Handle tool calls.
140
+ */
141
+ async execute(prompt) {
142
+ const lastMessage = prompt.getLastMessage();
143
+ if (!lastMessage) return;
144
+ if (!(lastMessage.content.length > 0 && lastMessage.content[0].type === 'function_request')) {
145
+ return;
146
+ }
147
+ /** @type {import("./Prompt.js").FunctionResponse[]} */
148
+ const functionResponses = [];
149
+ /** @type {import("./Prompt.js").FunctionRequest[]} */
150
+ // @ts-ignore
151
+ const calls = lastMessage.content;
152
+ const len = calls.length;
153
+ for (let i = 0; i < len; i++) {
154
+ const call = calls[i];
155
+ const startTime = new Date().getTime();
156
+ let response = '';
157
+ prompt.emit(prompt.EVENTS.tool_request, { name: call.function_request.name, call_id: call.function_request.call_id });
158
+ try {
159
+ response = await this.call(call.function_request.name, JSON.parse(call.function_request.parameters));
160
+ } catch (error) {
161
+ prompt.emit(prompt.EVENTS.tool_error, { name: call.function_request.name, call_id: call.function_request.call_id, error });
162
+ response = `Error: ${error.name} - ${error.message}`;
163
+ }
164
+ const duration = new Date().getTime() - startTime;
165
+ prompt.emit(prompt.EVENTS.tool_response, { name: call.function_request.name, call_id: call.function_request.call_id, duration });
166
+ let functionRes = response;
167
+ if (typeof functionRes !== 'string') {
168
+ functionRes = JSON.stringify(functionRes);
169
+ }
170
+ /** @type {import("./Prompt.js").FunctionResponse} */
171
+ const responseObj = {
172
+ type: 'function_response',
173
+ function_response: {
174
+ name: call.function_request.name,
175
+ id: call.function_request.id,
176
+ call_id: call.function_request.call_id,
177
+ response: functionRes
178
+ }
179
+ };
180
+ functionResponses.push(responseObj);
181
+ }
182
+ prompt.addMultiModal('tool', functionResponses);
183
+ }
184
+ }
185
+
186
+ export default ToolSet;
package/lib/fafs.js ADDED
@@ -0,0 +1,93 @@
1
+ /* Frequently asked functions */
2
+ import os from 'os';
3
+ import { CacheAsync, path } from '@j-o-r/cache';
4
+ import { SH, jsType } from '@j-o-r/sh'
5
+
6
+ const APPNAME = 'hello-dave';
7
+
8
+ const RELATIVE_CACHE = path.resolve('.cache');
9
+ new CacheAsync(RELATIVE_CACHE, true, 'bin');
10
+ const APP_CACHE = path.resolve(RELATIVE_CACHE, APPNAME);
11
+ new CacheAsync(APP_CACHE, true, 'bin');
12
+
13
+ const GLOBAL = {
14
+ max_recursive_requests: 20, // max number recursive of request
15
+ default_cache: APP_CACHE,
16
+ secret: 'tdb.e3a0cd73dd6a429283f921f9fc1bad41'
17
+ }
18
+
19
+ /**
20
+ * @typedef {Object} EnvironmentInfo
21
+ * @property {string} name - User's name
22
+ * @property {string} system - Linux OS / PROC
23
+ * @property {string} city
24
+ * @property {string} region
25
+ * @property {string} country
26
+ * @property {string} timezone
27
+ * @property {string} external_ip
28
+ * @property {string} cwd - current working folder
29
+ */
30
+
31
+ /**
32
+ * @returns {Promise<string>}
33
+ */
34
+ async function systemInfo() {
35
+ let system = 'system: ' + (await SH`cat /etc/issue`.run()).trim().replace('\\n', '').replace('\\l', '');
36
+ system += (await SH`uname -m`.run()).trim().replace('\\n', '').replace('\\l', '');
37
+ system += '' + (await SH`uname -o`.run()).trim().replace('\\n', '').replace('\\l', '');
38
+ system += '\ncwd: ' + process.cwd();
39
+ // try {
40
+ // await SH` which xdg-open`.run();
41
+ // system += ' and with xdg-open';
42
+ // } catch (_e) { }
43
+ // try {
44
+ // await SH` which msmtp`.run();
45
+ // system += ', msmtp';
46
+ // } catch (_e) { }
47
+ return system
48
+ }
49
+
50
+ /**
51
+ * Gather information about this environment
52
+ * @returns {Promise<EnvironmentInfo>}
53
+ */
54
+ async function env() {
55
+ const CACHE_PATH = (await SH`echo ~/.cache/${APPNAME}`.run()).trim();
56
+ const KEY = 'env';
57
+ const storage = new CacheAsync(CACHE_PATH, true, 'bin');
58
+ storage.secret = GLOBAL.secret;
59
+ const day = 1000 * 60 * 60 *24
60
+ // Expire after 31 days, refresh info after that,
61
+ await storage.expire(day * 31);
62
+
63
+ const wd = process.cwd();
64
+ const home = os.homedir();
65
+ const cwd = wd.replace(home, '~');
66
+ let info = await storage.read(KEY)
67
+ if (info) {
68
+ const res = JSON.parse(info);
69
+ res.cwd = cwd;
70
+ return res;
71
+ }
72
+ const name = (await SH`getent passwd "$USER" | cut -d: -f5`.run()).replaceAll(',',' ').trim();
73
+ const system = await systemInfo();
74
+ const userInfo = JSON.parse((await SH`curl -fsSL https://ipinfo.io/json`.run()));
75
+ info = {
76
+ name,
77
+ system,
78
+ city: userInfo.city,
79
+ region: userInfo.region,
80
+ country: userInfo.country,
81
+ timezone: userInfo.timezone,
82
+ external_ip: userInfo.ip
83
+ }
84
+ await storage.write(KEY, JSON.stringify(info));
85
+ return info;
86
+ }
87
+
88
+ export {
89
+ jsType,
90
+ GLOBAL,
91
+ env,
92
+ systemInfo
93
+ }
@@ -0,0 +1,170 @@
1
+ import { SH } from '@j-o-r/sh'
2
+ import { ToolSet, env } from './index.js'
3
+
4
+ const user = await env();
5
+ const environment =`
6
+ Name: ${user.name}
7
+ System: ${user.system}
8
+ City: ${user.city}
9
+ Region: ${user.region}
10
+ Country: ${user.country}
11
+ Timezone: ${user.timezone}
12
+ ExternalIp: ${user.external_ip}
13
+ `.trim();
14
+ const tools = new ToolSet('auto');
15
+
16
+ /**
17
+ * reduce the error output to essential info only, if possible
18
+ * @param {string} errorStr
19
+ * @returns {string}
20
+ */
21
+ const getJSError = (errorStr) => {
22
+ let result = '';
23
+ const linematch = errorStr.match(/\[eval\]:(\d+)/);
24
+ const lineNumber = linematch ? linematch[1] : '';
25
+ const match = errorStr.split(/\" \"\[eval\]:\d+/s);
26
+ if (match.length > 1) {
27
+ // Remove last 10 lines
28
+ const res = match[1].split('\n').slice(0, -10).join('\n');
29
+ result = `Error: line ${lineNumber}\n${res} `
30
+ } else {
31
+ result = errorStr;
32
+ }
33
+ return result;
34
+ }
35
+
36
+ /**
37
+ * bash escape a string suitable as an argument on the commandline
38
+ * for javascript code
39
+ * @param {string} x
40
+ * @retruns {string}
41
+ */
42
+ const bashEscape = (x) => {
43
+ let str = String(x).trim();
44
+ // the trick is to double escape escape vars first
45
+ str = str.replace(/\\/g, '\\\\');
46
+ // then add escaping for oddities
47
+ // Replace literal escape sequences like \n, \t, \r in quoted strings
48
+ str = str.replace(/(['"])\\(.)\1/g, '$1\\\\$2$1');
49
+ // escape $ (is a BASH var)
50
+ str = str.replace(/\$/g, '\\$');
51
+ // Escape backticks
52
+ str = str.replace(/`/g, '\\`');
53
+ // Escape quotes
54
+ str = str.replace(/"/g, '\\"');
55
+ return str;
56
+ }
57
+ tools.add(
58
+ 'javascript_interpreter',
59
+ `Execute ESM ES6 javascript on \`node\`.`,
60
+ {
61
+ type: 'object',
62
+ properties: {
63
+ script: {
64
+ type: 'string',
65
+ description: `ES6 ESM Javascript eval. 'console.log' to capture the response. cwd: ${user.cwd}`,
66
+ }
67
+ },
68
+ required: ['script']
69
+ },
70
+ // @ts-ignore
71
+ async (params) => {
72
+ // @ts-ignore
73
+ const script = bashEscape(params.script);
74
+ let response = '';
75
+ try {
76
+ // Execute script
77
+ response = await SH`node -e "${script}"`.run();
78
+ } catch (e) {
79
+ const errorStr = e.toString()
80
+ response = getJSError(errorStr)
81
+ }
82
+ return response;
83
+ }
84
+ );
85
+ tools.add(
86
+ 'bash_cmd',
87
+ `Execute a Bash command on ${user.system}.`,
88
+ {
89
+ type: 'object',
90
+ properties: {
91
+ command: {
92
+ type: 'string',
93
+ description: 'The bash command.',
94
+ }
95
+ },
96
+ required: ['command']
97
+ },
98
+ // @ts-ignore
99
+ async (params) => (await SH`${params.command}`.run())
100
+ );
101
+ tools.add(
102
+ 'get_user_env', // name
103
+ 'Get the user location, name and OS environment', // desciption
104
+ {
105
+ type: 'object',
106
+ properties: {
107
+ }
108
+ },
109
+ async (_params) => {
110
+ return environment;
111
+ }
112
+ );
113
+
114
+ tools.add(
115
+ 'execute_bash_script',
116
+ 'Execute a bash script.',
117
+ {
118
+ type: 'object',
119
+ properties: {
120
+ bash_script: { type: 'string', description: `The bash script to execute (${user.system})` }
121
+ },
122
+ required: ['bash_script']
123
+ },
124
+ async (params) => {
125
+ const delim = `END_SCRIPT_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
126
+ return await SH`bash <<'${delim}'
127
+ ${params.bash_script}
128
+ ${delim}
129
+ `.run()
130
+ }
131
+ );
132
+ tools.add(
133
+ 'send_email',
134
+ 'Send an email.',
135
+ {
136
+ type: 'object',
137
+ properties: {
138
+ to: { type: 'string', description: 'Recipient email' },
139
+ subject: { type: 'string', description: 'Subject' },
140
+ body: { type: 'string', description: 'Message body' }
141
+ },
142
+ required: ['to', 'subject', 'body']
143
+ },
144
+ async (params) => {
145
+ const delim = `END_EMAIL_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
146
+ return await SH`msmtp ${params.to} <<'${delim}'
147
+ To: ${params.to}
148
+ Subject: ${params.subject}
149
+
150
+ ${params.body}
151
+ ${delim}
152
+ `.run();
153
+ }
154
+ );
155
+ tools.add(
156
+ 'open_link',
157
+ 'Open an url in local user environment.',
158
+ {
159
+ type: 'object',
160
+ properties: {
161
+ url: { type: 'string', description: 'Link to open' }
162
+ },
163
+ required: ['url']
164
+ },
165
+ async (params) => {
166
+ return await SH`xdg-open ${[params.url]}`.run();
167
+ }
168
+ );
169
+ export default tools
170
+
package/lib/index.js ADDED
@@ -0,0 +1,34 @@
1
+ import {request as gpt} from './API/openai.com/reponses/text.js';
2
+ import {request as grok} from './API/x.ai/text.js';
3
+ import {request as claude} from './API/anthropic.com/text.js';
4
+ import {request as brave} from './API/brave.com/search.js';
5
+
6
+ import { env, GLOBAL } from './fafs.js';
7
+ import ToolSet from './ToolSet.js';
8
+ import AgentServer from './AgentServer.js';
9
+ import AgentClient from './AgentClient.js';
10
+ import Session from './Session.js';
11
+ import Prompt from './Prompt.js';
12
+ import Cli from './Cli.js';
13
+
14
+ const API = {
15
+ text:{
16
+ gpt,
17
+ grok,
18
+ claude
19
+ },
20
+ search: {
21
+ brave
22
+ },
23
+ }
24
+ export {
25
+ AgentServer,
26
+ AgentClient,
27
+ Prompt,
28
+ ToolSet,
29
+ Session,
30
+ API,
31
+ Cli,
32
+ env,
33
+ GLOBAL
34
+ };