@kadi.build/core 0.0.1-alpha.2 → 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 -382
  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,382 +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
-
9
- function resolveKadiExecPath() {
10
- const command = process.platform === 'win32' ? 'where kadi' : 'which kadi';
11
-
12
- try {
13
- const kadiPath = execSync(command, { encoding: 'utf8' }).trim();
14
- if (kadiPath) {
15
- return kadiPath;
16
- } else {
17
- throw new Error('Kadi CLI tool not found in the system PATH');
18
- }
19
- } catch (error) {
20
- console.error('Error resolving Kadi path:', error);
21
- return null;
22
- }
23
- }
24
-
25
- function resolveKadiInstallPath() {
26
- /* 1 - production / normal install -------------------------------- */
27
- try {
28
- if (typeof import.meta.resolve === 'function') {
29
- const p = import.meta.resolve('@kadi.build/cli/package.json');
30
- if (p) return path.dirname(new URL(p).pathname);
31
- }
32
- } catch {
33
- /* ignore – fall through */
34
- }
35
-
36
- /* 2 - legacy strategy – follow the kadi executable --------------- */
37
- try {
38
- const cmd = process.platform === 'win32' ? 'where kadi' : 'which kadi';
39
- const kadiBinaryPath = execSync(cmd, { encoding: 'utf8' }).trim();
40
- if (!kadiBinaryPath) {
41
- throw new Error('Kadi CLI tool not found in the system PATH');
42
- }
43
- const realKadiBinaryPath = fs.realpathSync(kadiBinaryPath); // resolve symlink
44
- let kadiDir = path.dirname(realKadiBinaryPath); // start alongside the binary
45
-
46
- const kadiPackageJsonPath = path.join(kadiDir, 'package.json');
47
- if (fs.existsSync(kadiPackageJsonPath)) {
48
- const kadiPackageJson = JSON.parse(
49
- fs.readFileSync(kadiPackageJsonPath, 'utf8')
50
- );
51
- if (
52
- kadiPackageJson.name === '@kadi.build/cli' ||
53
- (kadiPackageJson.bin && kadiPackageJson.bin.kadi)
54
- ) {
55
- return kadiDir;
56
- } else {
57
- throw new Error(
58
- 'Package.json found, but it is not for @kadi.build/cli'
59
- );
60
- }
61
- } else {
62
- throw new Error(
63
- '@kadi.build/cli package.json not found in the resolved path.'
64
- );
65
- }
66
- } catch {
67
- /* ignore – fall through */
68
- }
69
-
70
- /* 3 - give up ----------------------------------------------------- */
71
- throw new Error(
72
- 'Could not locate @kadi.build/cli installation. ' +
73
- 'Make sure the CLI is installed (`npm i -g @kadi.build/cli`) ' +
74
- 'or run commands from inside the CLI repository during development.'
75
- );
76
- }
77
-
78
- const __filename = fileURLToPath(import.meta.url);
79
- const __dirname = path.dirname(__filename);
80
-
81
- const kadiExecDir = resolveKadiExecPath();
82
- const kadiRootDir = resolveKadiInstallPath(); //path.join(kadiCLIInstallPath);
83
- const kadiAgentPath = path.join(kadiRootDir, 'agent.json');
84
-
85
- const rootDir = process.cwd();
86
- const abilitiesDir = path.join(rootDir, 'abilities');
87
- const projectAgentPath = path.join(rootDir, 'agent.json');
88
- const kadiCoreAgentPath = path.join(__dirname, 'agent.json');
89
-
90
- //Gets the agent.json for the ability, so dependencies can be installed
91
- export function getAbilityJSON(abilityName, abilityVersion) {
92
- //Get the agent.json for the ability
93
- let abilityPath = path.join(
94
- rootDir,
95
- 'abilities',
96
- abilityName,
97
- abilityVersion,
98
- 'agent.json'
99
- );
100
- let abilityJSON = fs.readFileSync(abilityPath, 'utf8');
101
- abilityJSON = JSON.parse(abilityJSON);
102
- return abilityJSON;
103
- }
104
-
105
- //Gets the file path for the ability agent.json
106
- export function getAbilityJSONPath(abilityName, abilityVersion) {
107
- return path.join(
108
- rootDir,
109
- 'abilities',
110
- abilityName,
111
- abilityVersion,
112
- 'agent.json'
113
- );
114
- }
115
-
116
- //Gets the abilities directory
117
- export function getAbilitiesDir() {
118
- return abilitiesDir;
119
- }
120
-
121
- //Gets the agent.json for the ability, so dependencies can be installed
122
- export function getProjectJSON() {
123
- //Get the agent.json for kadi
124
- let projectJSON = fs.readFileSync(projectAgentPath, 'utf8');
125
- projectJSON = JSON.parse(projectJSON);
126
- return projectJSON;
127
- }
128
-
129
- //Get the file path for the project agent.json
130
- export function getProjectJSONPath() {
131
- return projectAgentPath;
132
- }
133
-
134
- //Get Kadi Core agent.json
135
- export function getKadiCoreJSON() {
136
- //Get the agent.json for kadi
137
- let kadiCoreJSON = fs.readFileSync(kadiCoreAgentPath, 'utf8');
138
- kadiCoreJSON = JSON.parse(kadiCoreJSON);
139
- return kadiCoreJSON;
140
- }
141
-
142
- //Get the file path for the KADI Core Agent.json
143
- export function getKadiCoreJSONPath() {
144
- return kadiCoreAgentPath;
145
- }
146
-
147
- //Gets the agent.json for KADI system
148
- export function getKadiJSON() {
149
- //Get the agent.json for kadi
150
- let kadiJSON = fs.readFileSync(kadiAgentPath, 'utf8');
151
- kadiJSON = JSON.parse(kadiJSON);
152
- return kadiJSON;
153
- }
154
-
155
- //Gets the file path for the KADI Agent.json
156
- export function getKadiJSONPath() {
157
- return kadiAgentPath;
158
- }
159
-
160
- //Get the exec path of the KADI system
161
- export function getKadiExecPath() {
162
- return kadiExecDir;
163
- }
164
-
165
- //Get the Install directory of KADI system
166
- export function getKadiInstallPath() {
167
- return kadiRootDir;
168
- }
169
-
170
- //Save agent.json file
171
- export async function saveAgentJSON(agentJSON, agentJSONPath) {
172
- // Convert the JSON object to a string with pretty-print formatting
173
- const jsonString = JSON.stringify(agentJSON, null, 2);
174
-
175
- // Write the JSON string to the specified file
176
- fs.writeFile(agentJSONPath, jsonString, 'utf8', (err) => {
177
- if (err) {
178
- console.error('Error saving JSON to file:', err);
179
- }
180
- // else {
181
- // console.log('JSON saved successfully to', filePath);
182
- // }
183
- });
184
- }
185
-
186
- export function getAbilityVersionFromArray(abilities, name) {
187
- const ability = abilities.find((ability) => ability.name === name);
188
- if (ability) {
189
- return ability.version;
190
- } else {
191
- return null;
192
- }
193
- }
194
-
195
- // Get the API URL from the agent.json
196
- let kadijson = getKadiJSON();
197
-
198
- // Define the API URL (Adjust according to your API)
199
- export const KADI_API_URL = kadijson.api;
200
- export const SEARCH_API_URL = kadijson.api + '/search';
201
- export const GET_API_URL = kadijson.api + '/get';
202
-
203
- // Export all broker URLs
204
- const brokers = {};
205
- if (kadijson.brokers && typeof kadijson.brokers === 'object') {
206
- for (const [name, url] of Object.entries(kadijson.brokers)) {
207
- const brokerUrl = new URL(url);
208
- brokers[name] =
209
- `${brokerUrl.hostname}:${brokerUrl.port}${brokerUrl.pathname}`;
210
- }
211
- }
212
-
213
- export const KADI_BROKERS = brokers;
214
-
215
- // Get default broker (first one defined)
216
- const defaultBrokerName = kadijson.brokers
217
- ? Object.keys(kadijson.brokers)[0]
218
- : null;
219
- export const KADI_BROKER_URL = brokers[defaultBrokerName]; // Backward compatibility
220
-
221
- // Utility functions
222
- export function getBrokerUrl(brokerName) {
223
- return brokers[brokerName] || null;
224
- }
225
-
226
- export function getBrokerNames() {
227
- return Object.keys(brokers);
228
- }
229
-
230
- export function getDefaultBrokerName() {
231
- return defaultBrokerName;
232
- }
233
-
234
- // Active broker selection
235
- let activeBrokerName = defaultBrokerName;
236
-
237
- export function setActiveBroker(brokerName) {
238
- if (brokers[brokerName]) {
239
- activeBrokerName = brokerName;
240
- return true;
241
- }
242
- return false;
243
- }
244
-
245
- export function getActiveBrokerName() {
246
- return activeBrokerName;
247
- }
248
-
249
- export function getActiveBrokerUrl() {
250
- return brokers[activeBrokerName];
251
- }
252
-
253
- //Runs a command line string, used for abilities init functions
254
- export async function runExecCommand(name, version, command) {
255
- let execDirectory;
256
- if (name === '' || version === '') {
257
- execDirectory = path.join(rootDir);
258
- } else {
259
- execDirectory = path.join(rootDir, 'abilities', name, version);
260
- }
261
- console.log('execDirectory: ', execDirectory);
262
- return new Promise((resolve, reject) => {
263
- exec(
264
- command,
265
- { cwd: execDirectory, maxBuffer: 1024 * 1024 },
266
- (error, stdout, stderr) => {
267
- if (error) {
268
- console.error(`exec error: ${error}`);
269
- reject(error); // Reject the promise on error
270
- return;
271
- }
272
- if (stderr.trim()) {
273
- console.error(stderr);
274
- }
275
- resolve(stdout.trim()); // Resolve the promise when exec completes successfully
276
- }
277
- );
278
- });
279
- }
280
-
281
- export async function runSpawnCommand(name, version, command) {
282
- let execDirectory;
283
- if (name === '' || version === '') {
284
- execDirectory = path.join(rootDir);
285
- } else {
286
- execDirectory = path.join(rootDir, 'abilities', name, version);
287
- }
288
- console.log('execDirectory: ', execDirectory);
289
- return new Promise((resolve, reject) => {
290
- const child = spawn(command, {
291
- cwd: execDirectory,
292
- stdio: 'inherit', // This pipes the child process's stdio to the parent
293
- shell: true // This ensures that the command is run within a shell
294
- });
295
-
296
- child.on('error', (error) => {
297
- console.error(`Error: ${error}`);
298
- reject(error);
299
- });
300
-
301
- child.on('close', (code) => {
302
- if (code !== 0) {
303
- console.error(`Process exited with code: ${code}`);
304
- reject(new Error(`Process exited with code: ${code}`));
305
- } else {
306
- resolve();
307
- }
308
- });
309
- });
310
- }
311
-
312
- // Function to find the version of an ability by name
313
- export function findAbilityVersionByName(abilities, name) {
314
- const ability = abilities.find((ability) => ability.name === name);
315
- if (ability) {
316
- return ability.version;
317
- } else {
318
- return 'No ability found with the specified name.';
319
- }
320
- }
321
-
322
- export async function loadAbility(abilityName) {
323
- let agentJSON = await getProjectJSON();
324
- let abilityConfig = agentJSON.abilities.find(
325
- (ability) => ability.name === abilityName
326
- );
327
- if (!abilityConfig) {
328
- // throw new Error(`Ability ${abilityName} not found in configuration`);
329
- console.error(`Ability ${abilityName} not found in Project configuration`);
330
- const filePath = __filename;
331
- console.log('filePath:', filePath);
332
- // Extract the directory path of the file
333
- const dirPath = path.dirname(filePath);
334
- console.log(' dirPath:', dirPath);
335
-
336
- // Split the directory path into an array of directories
337
- const pathParts = dirPath.split(path.sep);
338
-
339
- // Find the index of the "abilities" folder
340
- const abilitiesIndex = pathParts.indexOf('abilities');
341
-
342
- // Ensure that the folder above 'phone-ability' is 'abilities'
343
- if (abilitiesIndex !== -1 && pathParts.length > abilitiesIndex + 2) {
344
- const ability = pathParts[abilitiesIndex + 1]; // Extract 'phone-ability'
345
- const version = pathParts[abilitiesIndex + 2]; // Extract '0.0.1'
346
-
347
- console.log('Loading ability dependency from an ability');
348
- agentJSON = await getAbilityJSON(ability, version);
349
- abilityConfig = agentJSON.abilities.find(
350
- (ability) => ability.name === abilityName
351
- );
352
- if (!abilityConfig) {
353
- throw new Error(
354
- `Ability ${abilityName} not found in ${ability} : ${version} configuration`
355
- );
356
- }
357
- } else {
358
- throw new Error('Ability not found in Project agent.json');
359
- }
360
- }
361
- const moduleJSON = getAbilityJSON(abilityName, abilityConfig.version);
362
- const modulePath = path.join(
363
- rootDir,
364
- `abilities/${abilityName}/${abilityConfig.version}/${moduleJSON.entry}`
365
- );
366
- const module = await import(modulePath);
367
- return module.default || module; // Return the default export directly
368
- }
369
-
370
- export async function launchAbility(language, path = '') {}
371
-
372
- export { Broker };
373
-
374
- export { BrokerMessageBuilder };
375
-
376
- export { IPCManager };
377
-
378
- export { IPCMessageBuilder };
379
-
380
- //Load the IPC interfaces
381
- let kadiCoreJSON = getKadiCoreJSON();
382
- IPCManager.setIPCInterfaces(kadiCoreJSON.IPCInterfaces);