@kadi.build/core 0.0.1-alpha.1 → 0.0.1-alpha.3

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 (39) hide show
  1. package/README.md +1145 -216
  2. package/examples/example-abilities/echo-js/README.md +131 -0
  3. package/examples/example-abilities/echo-js/agent.json +63 -0
  4. package/examples/example-abilities/echo-js/package.json +24 -0
  5. package/examples/example-abilities/echo-js/service.js +43 -0
  6. package/examples/example-abilities/hash-go/agent.json +53 -0
  7. package/examples/example-abilities/hash-go/cmd/hash_ability/main.go +340 -0
  8. package/examples/example-abilities/hash-go/go.mod +3 -0
  9. package/examples/example-agent/abilities/echo-js/0.0.1/README.md +131 -0
  10. package/examples/example-agent/abilities/echo-js/0.0.1/agent.json +63 -0
  11. package/examples/example-agent/abilities/echo-js/0.0.1/package-lock.json +93 -0
  12. package/examples/example-agent/abilities/echo-js/0.0.1/package.json +24 -0
  13. package/examples/example-agent/abilities/echo-js/0.0.1/service.js +41 -0
  14. package/examples/example-agent/abilities/hash-go/0.0.1/agent.json +53 -0
  15. package/examples/example-agent/abilities/hash-go/0.0.1/bin/hash_ability +0 -0
  16. package/examples/example-agent/abilities/hash-go/0.0.1/cmd/hash_ability/main.go +340 -0
  17. package/examples/example-agent/abilities/hash-go/0.0.1/go.mod +3 -0
  18. package/examples/example-agent/agent.json +39 -0
  19. package/examples/example-agent/index.js +102 -0
  20. package/examples/example-agent/package-lock.json +93 -0
  21. package/examples/example-agent/package.json +17 -0
  22. package/package.json +4 -2
  23. package/src/KadiAbility.js +478 -0
  24. package/src/index.js +65 -0
  25. package/src/loadAbility.js +1086 -0
  26. package/src/servers/BaseRpcServer.js +404 -0
  27. package/src/servers/BrokerRpcServer.js +776 -0
  28. package/src/servers/StdioRpcServer.js +360 -0
  29. package/src/transport/BrokerMessageBuilder.js +377 -0
  30. package/src/transport/IpcMessageBuilder.js +1229 -0
  31. package/src/utils/agentUtils.js +137 -0
  32. package/src/utils/commandUtils.js +64 -0
  33. package/src/utils/configUtils.js +72 -0
  34. package/src/utils/logger.js +161 -0
  35. package/src/utils/pathUtils.js +86 -0
  36. package/broker.js +0 -214
  37. package/index.js +0 -370
  38. package/ipc.js +0 -220
  39. package/ipcInterfaces/pythonAbilityIPC.py +0 -177
package/broker.js DELETED
@@ -1,214 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { WebSocket } from 'ws';
3
- import { randomUUID } from 'crypto';
4
-
5
- function formatJSON(jsonString) {
6
- try {
7
- const jsonObj = JSON.parse(jsonString);
8
- return JSON.stringify(jsonObj, null, 2);
9
- } catch (error) {
10
- return `Error parsing JSON: ${error}`;
11
- }
12
- }
13
-
14
- //List Message
15
- // {
16
- // "type": "list",
17
- // "data": {
18
- // "clients": [
19
- // {
20
- // "id": "9143a10c-5b4f-11ef-bb3a-2250bcde1118",
21
- // "name": "TestAgent",
22
- // "description": "A Broker Testing Agent"
23
- // }
24
- // ]
25
- // }
26
- // }
27
-
28
- class IBroker extends EventEmitter {
29
- constructor(url, name) {
30
- super();
31
- this.url = url;
32
- this.name = name;
33
- this.uuid = null; //public id for the broker
34
- this.key = null; //private id for the broker
35
- this.queryPending = null; //used for Syncronous queries, disables forwarding to onMessage
36
- this.ws = new WebSocket(url);
37
-
38
- this.ws.on('open', () => {
39
- this.emit('open');
40
- console.log(`Connected to ${this.url}`);
41
- });
42
-
43
- this.ws.on('message', (data) => {
44
- let msg = JSON.parse(data);
45
-
46
- //Emit messages to listeners
47
- if (msg.type != this.queryPending) {
48
- //A syncronous query is pending
49
- this.emit('message', data);
50
- } else {
51
- // Emit synQuery to listener, to proces event
52
- this.queryPending = null;
53
- this.emit('syncQuery', data);
54
- }
55
-
56
- // console.log('Received:', data);
57
- });
58
-
59
- this.ws.on('error', (error) => {
60
- this.emit('error', error);
61
- console.error('WebSocket error:', error);
62
- });
63
-
64
- this.ws.on('close', () => {
65
- this.emit('close');
66
- console.log('Disconnected from the server');
67
- });
68
- }
69
-
70
- getConnectedAgents() {
71
- return new Promise((resolve, reject) => {
72
- // Set the query pending to list
73
- this.queryPending = 'list';
74
-
75
- // Set up a timeout to reject if no response is received in 10 seconds
76
- const timeout = setTimeout(() => {
77
- reject(new Error('Get Connected Agents timed out after 10 seconds'));
78
- }, 10000);
79
-
80
- const handleListRequest = (data) => {
81
- const msg = JSON.parse(data);
82
-
83
- if (msg.type === 'list') {
84
- clearTimeout(timeout);
85
- this.off('syncQuery', handleListRequest);
86
- resolve(msg.data.clients);
87
- }
88
- };
89
-
90
- const jsonObject = BrokerMessageBuilder.list();
91
- this.send(jsonObject);
92
- // console.log("Requested List of Agents");
93
- this.on('syncQuery', handleListRequest);
94
- });
95
- }
96
-
97
- send(message) {
98
- // console.log("WS Ready State: ", this.ws.readyState)
99
- // console.log("WS OPEN: ", WebSocket.OPEN)
100
- if (this.ws.readyState === WebSocket.OPEN) {
101
- // console.log("Sending: ", JSON.stringify(message, null, 2));
102
- this.ws.send(message);
103
- // console.log(`Sending message to ${this.url}: ${message}`);
104
- } else {
105
- // console.log('WebSocket is not open.');
106
- }
107
- }
108
- }
109
-
110
- let brokers = new Map();
111
-
112
- export const Broker = {
113
- addBroker: function (url, name = 'default') {
114
- if (!brokers.has(name)) {
115
- let broker = new IBroker(url, name);
116
- brokers.set(name, broker);
117
- } else {
118
- console.log(`Broker ${name} already exists.`);
119
- }
120
- },
121
-
122
- disconnect: function (name = 'default') {
123
- let broker = brokers.get(name);
124
- if (broker) {
125
- broker.ws.close();
126
- } else {
127
- console.error(`Broker ${name} not found`);
128
- }
129
- },
130
-
131
- deleteBroker: function (name = 'default') {
132
- let broker = brokers.get(name);
133
- if (broker) {
134
- broker.ws.close();
135
- brokers.delete(name);
136
- } else {
137
- console.error(`Broker ${name} not found`);
138
- }
139
- },
140
-
141
- send: function (message, brokerName = 'default') {
142
- let broker = brokers.get(brokerName);
143
- if (broker) {
144
- broker.send(message);
145
- } else {
146
- console.error(`Broker ${brokerName} not found`);
147
- }
148
- },
149
-
150
- addEventListener: function (event, listener, brokerName = 'default') {
151
- let broker = brokers.get(brokerName);
152
- if (broker) {
153
- broker.on(event, listener);
154
- } else {
155
- console.error(`Broker ${brokerName} not found`);
156
- }
157
- },
158
-
159
- removeEventListener: function (event, listener, brokerName = 'default') {
160
- let broker = brokers.get(brokerName);
161
- if (broker) {
162
- broker.off(event, listener);
163
- } else {
164
- console.error(`Broker ${brokerName} not found`);
165
- }
166
- },
167
-
168
- removeAllListeners: function (brokerName = 'default') {
169
- let broker = brokers.get(brokerName);
170
- if (broker) {
171
- broker.removeAllListeners();
172
- } else {
173
- console.error(`Broker ${brokerName} not found`);
174
- }
175
- },
176
-
177
- getBroker: function (brokerName = 'default') {
178
- return brokers.get(brokerName);
179
- }
180
- };
181
-
182
- //Need to add a message that will return a list of agents connected
183
- // for a specific name/type
184
- export const BrokerMessageBuilder = {
185
- create_message: function (type, data) {
186
- return JSON.stringify({
187
- type: type,
188
- data: data
189
- });
190
- },
191
- message: function (to, data) {
192
- return BrokerMessageBuilder.create_message('sendmessage', {
193
- peer: to,
194
- data: data
195
- });
196
- },
197
- setup: function (name, description, limit, uuid) {
198
- return BrokerMessageBuilder.create_message('setup', {
199
- name: name,
200
- description: description,
201
- limit: limit,
202
- key: uuid
203
- });
204
- },
205
- suspend: function () {
206
- return BrokerMessageBuilder.create_message('suspend', {});
207
- },
208
- finish: function () {
209
- return BrokerMessageBuilder.create_message('finish', {});
210
- },
211
- list: function () {
212
- return BrokerMessageBuilder.create_message('list', {});
213
- }
214
- };
package/index.js DELETED
@@ -1,370 +0,0 @@
1
- import path from 'path';
2
- import fs from 'fs';
3
- // import { promises as fsPromises } from 'fs';
4
- import { fileURLToPath } from 'url';
5
- import { exec, execSync, spawn } from 'child_process';
6
- import { Broker, BrokerMessageBuilder } from './broker.js';
7
- import { IPCManager, IPCMessageBuilder } from './ipc.js';
8
- import { createRequire } from 'module'; // Import createRequire to use require in ES Modules
9
-
10
- // Create a require function scoped to the current module
11
- const require = createRequire(import.meta.url);
12
-
13
- function resolveKadiExecPath() {
14
- const command = process.platform === 'win32' ? 'where kadi' : 'which kadi';
15
-
16
- try {
17
- const kadiPath = execSync(command, { encoding: 'utf8' }).trim();
18
- if (kadiPath) {
19
- return kadiPath;
20
- } else {
21
- throw new Error('Kadi CLI tool not found in the system PATH');
22
- }
23
- } catch (error) {
24
- console.error('Error resolving Kadi path:', error);
25
- return null;
26
- }
27
- }
28
-
29
- function resolveKadiInstallPath() {
30
- try {
31
- // Step 1: Get the path to the kadi binary
32
- const command = process.platform === 'win32' ? 'where kadi' : 'which kadi';
33
- const kadiBinaryPath = execSync(command, { encoding: 'utf8' }).trim();
34
-
35
- if (!kadiBinaryPath) {
36
- throw new Error('Kadi CLI tool not found in the system PATH');
37
- }
38
- // console.log('Resolved Kadi binary path:', kadiBinaryPath);
39
-
40
- // Step 2: Resolve the symlink to find the actual location of the binary
41
- const resolvedBinaryPath = fs.realpathSync(kadiBinaryPath);
42
- // console.log('Resolved binary real path:', resolvedBinaryPath);
43
-
44
- // Step 3: Correctly determine the root directory of the kadi package
45
- const kadiDir = path.dirname(resolvedBinaryPath); // Move up one level to get the kadi directory
46
- // console.log('Kadi directory:', kadiDir);
47
-
48
- // Step 4: Verify if this directory contains the kadi package.json
49
- const packageJsonPath = path.join(kadiDir, 'package.json');
50
- // console.log('Checking for package.json at:', packageJsonPath);
51
-
52
- if (fs.existsSync(packageJsonPath)) {
53
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
54
-
55
- if (packageJson.name === 'kadi') {
56
- // console.log('Kadi package.json found:', packageJsonPath);
57
- return kadiDir;
58
- } else {
59
- throw new Error('Package.json found, but it is not for kadi.');
60
- }
61
- } else {
62
- throw new Error('Kadi package.json not found in the resolved path.');
63
- }
64
- } catch (error) {
65
- console.error('Error resolving Kadi CLI install path:', error);
66
- return null;
67
- }
68
- }
69
-
70
- const __filename = fileURLToPath(import.meta.url);
71
- const __dirname = path.dirname(__filename);
72
-
73
- const kadiExecDir = resolveKadiExecPath();
74
- const kadiRootDir = resolveKadiInstallPath(); //path.join(kadiCLIInstallPath);
75
- const kadiAgentPath = path.join(kadiRootDir, 'agent.json');
76
-
77
- const rootDir = process.cwd();
78
- const abilitiesDir = path.join(rootDir, 'abilities');
79
- const projectAgentPath = path.join(rootDir, 'agent.json');
80
- const kadiCoreAgentPath = path.join(__dirname, 'agent.json');
81
-
82
- //Gets the agent.json for the ability, so dependencies can be installed
83
- export function getAbilityJSON(abilityName, abilityVersion) {
84
- //Get the agent.json for the ability
85
- let abilityPath = path.join(
86
- rootDir,
87
- 'abilities',
88
- abilityName,
89
- abilityVersion,
90
- 'agent.json'
91
- );
92
- let abilityJSON = fs.readFileSync(abilityPath, 'utf8');
93
- abilityJSON = JSON.parse(abilityJSON);
94
- return abilityJSON;
95
- }
96
-
97
- //Gets the file path for the ability agent.json
98
- export function getAbilityJSONPath(abilityName, abilityVersion) {
99
- return path.join(
100
- rootDir,
101
- 'abilities',
102
- abilityName,
103
- abilityVersion,
104
- 'agent.json'
105
- );
106
- }
107
-
108
- //Gets the abilities directory
109
- export function getAbilitiesDir() {
110
- return abilitiesDir;
111
- }
112
-
113
- //Gets the agent.json for the ability, so dependencies can be installed
114
- export function getProjectJSON() {
115
- //Get the agent.json for kadi
116
- let projectJSON = fs.readFileSync(projectAgentPath, 'utf8');
117
- projectJSON = JSON.parse(projectJSON);
118
- return projectJSON;
119
- }
120
-
121
- //Get the file path for the project agent.json
122
- export function getProjectJSONPath() {
123
- return projectAgentPath;
124
- }
125
-
126
- //Get Kadi Core agent.json
127
- export function getKadiCoreJSON() {
128
- //Get the agent.json for kadi
129
- let kadiCoreJSON = fs.readFileSync(kadiCoreAgentPath, 'utf8');
130
- kadiCoreJSON = JSON.parse(kadiCoreJSON);
131
- return kadiCoreJSON;
132
- }
133
-
134
- //Get the file path for the KADI Core Agent.json
135
- export function getKadiCoreJSONPath() {
136
- return kadiCoreAgentPath;
137
- }
138
-
139
- //Gets the agent.json for KADI system
140
- export function getKadiJSON() {
141
- //Get the agent.json for kadi
142
- let kadiJSON = fs.readFileSync(kadiAgentPath, 'utf8');
143
- kadiJSON = JSON.parse(kadiJSON);
144
- return kadiJSON;
145
- }
146
-
147
- //Gets the file path for the KADI Agent.json
148
- export function getKadiJSONPath() {
149
- return kadiAgentPath;
150
- }
151
-
152
- //Get the exec path of the KADI system
153
- export function getKadiExecPath() {
154
- return kadiExecDir;
155
- }
156
-
157
- //Get the Install directory of KADI system
158
- export function getKadiInstallPath() {
159
- return kadiRootDir;
160
- }
161
-
162
- //Save agent.json file
163
- export async function saveAgentJSON(agentJSON, agentJSONPath) {
164
- // Convert the JSON object to a string with pretty-print formatting
165
- const jsonString = JSON.stringify(agentJSON, null, 2);
166
-
167
- // Write the JSON string to the specified file
168
- fs.writeFile(agentJSONPath, jsonString, 'utf8', (err) => {
169
- if (err) {
170
- console.error('Error saving JSON to file:', err);
171
- }
172
- // else {
173
- // console.log('JSON saved successfully to', filePath);
174
- // }
175
- });
176
- }
177
-
178
- export function getAbilityVersionFromArray(abilities, name) {
179
- const ability = abilities.find((ability) => ability.name === name);
180
- if (ability) {
181
- return ability.version;
182
- } else {
183
- return null;
184
- }
185
- }
186
-
187
- // Get the API URL from the agent.json
188
- let kadijson = getKadiJSON();
189
-
190
- // Define the API URL (Adjust according to your API)
191
- export const KADI_API_URL = kadijson.api;
192
- export const SEARCH_API_URL = kadijson.api + '/search';
193
- export const GET_API_URL = kadijson.api + '/get';
194
-
195
- // Export all broker URLs
196
- const brokers = {};
197
- for (const [name, url] of Object.entries(kadijson.brokers)) {
198
- const brokerUrl = new URL(url);
199
- brokers[name] =
200
- `${brokerUrl.hostname}:${brokerUrl.port}${brokerUrl.pathname}`;
201
- }
202
-
203
- export const KADI_BROKERS = brokers;
204
-
205
- // Get default broker (first one defined)
206
- const defaultBrokerName = Object.keys(kadijson.brokers)[0];
207
- export const KADI_BROKER_URL = brokers[defaultBrokerName]; // Backward compatibility
208
-
209
- // Utility functions
210
- export function getBrokerUrl(brokerName) {
211
- return brokers[brokerName] || null;
212
- }
213
-
214
- export function getBrokerNames() {
215
- return Object.keys(brokers);
216
- }
217
-
218
- export function getDefaultBrokerName() {
219
- return defaultBrokerName;
220
- }
221
-
222
- // Active broker selection
223
- let activeBrokerName = defaultBrokerName;
224
-
225
- export function setActiveBroker(brokerName) {
226
- if (brokers[brokerName]) {
227
- activeBrokerName = brokerName;
228
- return true;
229
- }
230
- return false;
231
- }
232
-
233
- export function getActiveBrokerName() {
234
- return activeBrokerName;
235
- }
236
-
237
- export function getActiveBrokerUrl() {
238
- return brokers[activeBrokerName];
239
- }
240
-
241
- //Runs a command line string, used for abilities init functions
242
- export async function runExecCommand(name, version, command) {
243
- let execDirectory;
244
- if (name === '' || version === '') {
245
- execDirectory = path.join(rootDir);
246
- } else {
247
- execDirectory = path.join(rootDir, 'abilities', name, version);
248
- }
249
- console.log('execDirectory: ', execDirectory);
250
- return new Promise((resolve, reject) => {
251
- exec(
252
- command,
253
- { cwd: execDirectory, maxBuffer: 1024 * 1024 },
254
- (error, stdout, stderr) => {
255
- if (error) {
256
- console.error(`exec error: ${error}`);
257
- reject(error); // Reject the promise on error
258
- return;
259
- }
260
- if (stderr.trim()) {
261
- console.error(stderr);
262
- }
263
- resolve(stdout.trim()); // Resolve the promise when exec completes successfully
264
- }
265
- );
266
- });
267
- }
268
-
269
- export async function runSpawnCommand(name, version, command) {
270
- let execDirectory;
271
- if (name === '' || version === '') {
272
- execDirectory = path.join(rootDir);
273
- } else {
274
- execDirectory = path.join(rootDir, 'abilities', name, version);
275
- }
276
- console.log('execDirectory: ', execDirectory);
277
- return new Promise((resolve, reject) => {
278
- const child = spawn(command, {
279
- cwd: execDirectory,
280
- stdio: 'inherit', // This pipes the child process's stdio to the parent
281
- shell: true // This ensures that the command is run within a shell
282
- });
283
-
284
- child.on('error', (error) => {
285
- console.error(`Error: ${error}`);
286
- reject(error);
287
- });
288
-
289
- child.on('close', (code) => {
290
- if (code !== 0) {
291
- console.error(`Process exited with code: ${code}`);
292
- reject(new Error(`Process exited with code: ${code}`));
293
- } else {
294
- resolve();
295
- }
296
- });
297
- });
298
- }
299
-
300
- // Function to find the version of an ability by name
301
- export function findAbilityVersionByName(abilities, name) {
302
- const ability = abilities.find((ability) => ability.name === name);
303
- if (ability) {
304
- return ability.version;
305
- } else {
306
- return 'No ability found with the specified name.';
307
- }
308
- }
309
-
310
- export async function loadAbility(abilityName) {
311
- let agentJSON = await getProjectJSON();
312
- let abilityConfig = agentJSON.abilities.find(
313
- (ability) => ability.name === abilityName
314
- );
315
- if (!abilityConfig) {
316
- // throw new Error(`Ability ${abilityName} not found in configuration`);
317
- console.error(`Ability ${abilityName} not found in Project configuration`);
318
- const filePath = __filename;
319
- console.log('filePath:', filePath);
320
- // Extract the directory path of the file
321
- const dirPath = path.dirname(filePath);
322
- console.log(' dirPath:', dirPath);
323
-
324
- // Split the directory path into an array of directories
325
- const pathParts = dirPath.split(path.sep);
326
-
327
- // Find the index of the "abilities" folder
328
- const abilitiesIndex = pathParts.indexOf('abilities');
329
-
330
- // Ensure that the folder above 'phone-ability' is 'abilities'
331
- if (abilitiesIndex !== -1 && pathParts.length > abilitiesIndex + 2) {
332
- const ability = pathParts[abilitiesIndex + 1]; // Extract 'phone-ability'
333
- const version = pathParts[abilitiesIndex + 2]; // Extract '0.0.1'
334
-
335
- console.log('Loading ability dependency from an ability');
336
- agentJSON = await getAbilityJSON(ability, version);
337
- abilityConfig = agentJSON.abilities.find(
338
- (ability) => ability.name === abilityName
339
- );
340
- if (!abilityConfig) {
341
- throw new Error(
342
- `Ability ${abilityName} not found in ${ability} : ${version} configuration`
343
- );
344
- }
345
- } else {
346
- throw new Error('Ability not found in Project agent.json');
347
- }
348
- }
349
- const moduleJSON = getAbilityJSON(abilityName, abilityConfig.version);
350
- const modulePath = path.join(
351
- rootDir,
352
- `abilities/${abilityName}/${abilityConfig.version}/${moduleJSON.entry}`
353
- );
354
- const module = await import(modulePath);
355
- return module.default || module; // Return the default export directly
356
- }
357
-
358
- export async function launchAbility(language, path = '') {}
359
-
360
- export { Broker };
361
-
362
- export { BrokerMessageBuilder };
363
-
364
- export { IPCManager };
365
-
366
- export { IPCMessageBuilder };
367
-
368
- //Load the IPC interfaces
369
- let kadiCoreJSON = getKadiCoreJSON();
370
- IPCManager.setIPCInterfaces(kadiCoreJSON.IPCInterfaces);