openkbs 0.0.31 → 0.0.33

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/MODIFY.md CHANGED
@@ -1,12 +1,12 @@
1
- #### HUMAN-DEFINED REQUIREMENTS
1
+ #### USER-DEFINED REQUIREMENTS
2
2
 
3
3
  Define any additional features, integrations, or behavior modifications
4
4
 
5
- #### HUMAN-DEFINED REQUIREMENTS END
6
-
5
+ #### USER-DEFINED REQUIREMENTS END
7
6
 
8
7
  # OpenKBS Framework Technical Guide (For AI Coding Agents)
9
8
 
9
+ OpenKBS is claud AI platform designed to build, deploy and integrate AI agents and applications.
10
10
  This guide provides a brief overview of developing backend and frontend logic for your OpenKBS app/agent.
11
11
 
12
12
  ## Project Structure Overview
@@ -14,8 +14,8 @@ This guide provides a brief overview of developing backend and frontend logic fo
14
14
  A typical OpenKBS project has the following key directories:
15
15
 
16
16
  * **`src/`**: Contains your custom source code.
17
- * **`Events/`**: Houses backend event handlers.
18
- * `actions.js`: Often used for shared logic/tool definitions (does not have own NPM dependencies).
17
+ * **`Events/`**: Houses backend LLM event handlers.
18
+ * `actions.js`: Example file commonly used for shared backend logic/tools definitions.
19
19
  * `onRequest.js`: Handles incoming user messages before LLM processing.
20
20
  * `onRequest.json`: NPM dependencies for `onRequest.js`.
21
21
  * `onResponse.js`: Handles LLM responses before sending to the user.
@@ -32,52 +32,101 @@ A typical OpenKBS project has the following key directories:
32
32
  * `instructions.txt`: Instructions for the LLM.
33
33
  * `icon.png`: Application icon.
34
34
 
35
+ #### Backend Handlers
36
+ - Files with names starting with "on" (like onRequest.js) are called handler
37
+ - Each handler functions as NodeJS execution entry point executed by the platform upon certain events.
38
+ - use ES6, user import, do not use require()
39
+
35
40
  #### onRequest and onResponse Handlers
36
41
 
37
- The core of the OpenKBS backend framework revolves around the `onRequest` and `onResponse` event handlers.
38
- These handlers act as middleware, intercepting messages before and after they are processed by the LLM.
42
+ The core of the OpenKBS backend framework revolves around the `onRequest` and `onResponse` Node.js backend event handlers.
43
+ These handlers act as middleware, intercepting user messages and messages generated by the LLM.
44
+
45
+ - onRequest handler is invoked every time a user sends a message to the chat. It provides an opportunity to process the user's input, extract commands and perform actions based on the user's message.
46
+ - onResponse handler is invoked after the LLM generates a response. It allows processing of the LLM's output, execution of commands based on the LLM's message.
39
47
 
40
- * **`onRequest` Handler:** This handler is invoked every time a user sends a message to the chat. It provides an opportunity to pre-process the user's input, extract commands and perform actions based on the user's message.
48
+ #### NPM Dependencies for Backend Handlers
41
49
 
42
- * **`onResponse` Handler:** This handler is invoked after the LLM generates a response. It allows post-processing of the LLM's output, execution of commands based on the LLM's intentions.
50
+ 1. If a file imports an NPM dependency and is then imported by a handler, this dependency must be defined in the handler's corresponding json file
51
+ 2. Example: If actions.js imports got and onRequest.js imports actions.js, then got must be in onRequest.json
43
52
 
44
53
 
45
54
  ## Backend Dependencies
46
55
 
47
56
  To use external NPM packages in your backend event handlers, you must declare them in the corresponding `.json` file.
48
- OpenKBS also provides a secure way to handle sensitive information using the `{{secrets.your_secret_name}}` syntax.
49
57
 
50
- **Example: Using axios with an API key**
58
+ **Example: Using https module with an API key**
51
59
 
52
- 1. **Declare dependencies** in both `src/Events/onRequest.json` and `src/Events/onResponse.json` (as each handler have separate build):
60
+ 1. **Declare dependencies** in both `src/Events/onRequest.json` and `src/Events/onResponse.json` (as each handler have separate Node.js build):
53
61
  ```json
54
62
  {
55
63
  "dependencies": {
56
- "axios": "^1.6.2"
64
+ "got": "^11.8.5"
57
65
  }
58
66
  }
59
67
  ```
60
68
 
61
- 2. **Implement and use in `src/Events/actions.js`**:
62
- ```javascript
63
- import axios from 'axios';
64
-
65
- export const getActions = (meta) => {
66
- return [
67
- [/\/?getNews\("(.*)"\)/, async (match) => {
68
- const topic = match[1];
69
- const response = await axios.get('https://newsapi.org/v2/everything', {
70
- params: {
71
- q: topic,
72
- apiKey: '{{secrets.news_api_key}}' // Securely injected at runtime
73
- }
74
- });
75
- return { result: response.data.articles, ...meta };
76
- }],
77
- // ... other actions
78
- ];
79
- };
80
- ```
69
+ 2. **Implement in any JavaScript file** (actions.js is just an example name):
70
+
71
+ ```javascript
72
+ import got from 'got';
73
+ export const getActions = (meta) => {
74
+ return [
75
+ [/\/?getNews\("(.*)"\)/, async (match) => {
76
+ const { body } = await got(`https://newsapi.org/v2/everything?q=${match[1]}&apiKey={{secrets.news_api_key}}`,
77
+ { responseType: 'json' });
78
+
79
+ return { result: body.articles, ...meta };
80
+ }],
81
+ // ... other actions
82
+ ];
83
+ };
84
+ ```
85
+
81
86
  ## Secrets Management
82
- Define `news_api_key` in your application's secrets manager on the OpenKBS platform.
83
- The platform will inject the actual value at runtime, keeping your credentials secure while enabling you to make API calls with authenticated services.
87
+ OpenKBS provides a secure way to handle sensitive information using the `{{secrets.your_secret_name}}` syntax.
88
+ Never hardcode secrets in the code, if any secrets are provided by the user replace them with placeholders syntax above.
89
+ The user will later insert the secrets using the secrets manager
90
+
91
+ #### LLM Instructions
92
+ `app/instructions.txt`
93
+ This file contains the instructions for the agent
94
+
95
+ **Example Instructions:**
96
+
97
+ ```
98
+ You are an AI assistant.
99
+
100
+ You can execute the following commands:
101
+
102
+ /getNews("query")
103
+ Description: """
104
+ Get the latest news
105
+ """
106
+ ```
107
+
108
+ #### Important Coding guidelines
109
+ - Organize new features across multiple files (like Events/utils.js) rather than adding everything to actions.js
110
+ - Similar for Frontend: keep newly implemented React components outside contentRender.js for maintainability.
111
+ - Keep in mind onRenderChatMessage is not a React component but simple JS function
112
+
113
+
114
+ #### onRenderChatMessage injected functions
115
+ These functions are automatically injected by the framework to onRenderChatMessage, providing access to system features
116
+ ```
117
+ // Where functions are injected
118
+ const onRenderChatMessage = async (params) => {
119
+ const { APIResponseComponent, theme, setBlockingLoading, setSystemAlert, RequestChatAPI,
120
+ kbUserData, generateMsgId, messages, msgIndex } = params;
121
+ ...
122
+ }
123
+ ```
124
+
125
+ ```
126
+ // Shows toast notifications in top-right corner
127
+ setSystemAlert({
128
+ msg: 'Payment successful',
129
+ type: 'success', //'error'|'success'|'info'
130
+ duration: 5000
131
+ });
132
+ ```
package/README.md CHANGED
@@ -597,6 +597,8 @@ The `openkbs` object provides a set of utility functions and services to interac
597
597
 
598
598
  * **`openkbs.googleSearch(q, params)`:** Performs a Google search using the provided query and parameters.
599
599
 
600
+ * **`openkbs.sendMail(email, subject, content)`:** Sends an email to the specified recipient
601
+
600
602
  * **`openkbs.documentToText(documentURL, params)`:** Extracts text from various document formats.
601
603
 
602
604
  * **`openkbs.imageToText(imageUrl, params)`:** Extracts text from an image.
@@ -619,6 +621,8 @@ The `openkbs` object provides a set of utility functions and services to interac
619
621
 
620
622
  * **`openkbs.clientHeaders`:** Exposes client headers for accessing information like IP address, location, etc. (e.g., `openkbs.clientHeaders['x-forwarded-for']`).
621
623
 
624
+ * **`openkbs.createEmbeddings(input, model)`:** Create embeddings from input text
625
+
622
626
  **Example SDK Usage:**
623
627
 
624
628
  ```javascript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openkbs",
3
- "version": "0.0.31",
3
+ "version": "0.0.33",
4
4
  "description": "OpenKBS - Command Line Interface",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -9,6 +9,7 @@
9
9
  "deploy:linux": "aws s3 cp build/openkbs-linux-x64 s3://openkbs-downloads/cli/linux/openkbs && aws s3 cp build/openkbs-linux-arm64 s3://openkbs-downloads/cli/linux/openkbs-arm64",
10
10
  "deploy:macos": "aws s3 cp build/openkbs-macos-arm64 s3://openkbs-downloads/cli/macos/openkbs && aws s3 cp build/openkbs-macos-x64 s3://openkbs-downloads/cli/macos/openkbs-x64",
11
11
  "deploy:win": "aws s3 cp build/openkbs-win-x64.exe s3://openkbs-downloads/cli/windows/openkbs.exe && aws s3 cp build/openkbs-win-arm64.exe s3://openkbs-downloads/cli/windows/openkbs-arm64.exe",
12
+ "deploy:templates": "node scripts/deploy-templates.js",
12
13
  "postinstall": "node src/install.js"
13
14
  },
14
15
  "bin": {
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
6
+
7
+ const s3Client = new S3Client({ region: 'us-east-1' });
8
+ const BUCKET = 'openkbs-downloads';
9
+ const TEMPLATES_DIR = path.join(__dirname, '../templates');
10
+
11
+ async function uploadDirectory(localPath, s3Prefix) {
12
+ const items = await fs.readdir(localPath, { withFileTypes: true });
13
+
14
+ for (const item of items) {
15
+ const itemPath = path.join(localPath, item.name);
16
+ const s3Key = `${s3Prefix}/${item.name}`;
17
+
18
+ if (item.isDirectory()) {
19
+ await uploadDirectory(itemPath, s3Key);
20
+ } else {
21
+ const fileContent = await fs.readFile(itemPath);
22
+ await s3Client.send(new PutObjectCommand({
23
+ Bucket: BUCKET,
24
+ Key: s3Key,
25
+ Body: fileContent
26
+ }));
27
+ console.log(`Uploaded: ${s3Key}`);
28
+ }
29
+ }
30
+ }
31
+
32
+ async function deployTemplates() {
33
+ try {
34
+ console.log('Deploying templates to S3...');
35
+ await uploadDirectory(TEMPLATES_DIR, 'templates');
36
+ console.log('Templates deployed successfully!');
37
+ } catch (error) {
38
+ console.error('Error deploying templates:', error);
39
+ process.exit(1);
40
+ }
41
+ }
42
+
43
+ deployTemplates();
package/src/actions.js CHANGED
@@ -10,10 +10,10 @@ const {
10
10
  fetchAndSaveSettings, downloadFiles, downloadIcon, updateKB, uploadFiles, generateKey, generateMnemonic,
11
11
  reset, bold, red, yellow, green, createKB, saveLocalKBData, listKBs, deleteKBFile,
12
12
  deleteKB, buildPackage, replacePlaceholderInFiles, buildNodePackage, initByTemplateAction, modifyKB,
13
- listKBsSharedWithMe
13
+ listKBsSharedWithMe, downloadTemplates
14
14
  } = require("./utils");
15
15
 
16
- const TEMPLATE_DIR = path.join(__dirname, '../templates');
16
+ const TEMPLATE_DIR = path.join(os.homedir(), '.openkbs', 'templates');
17
17
  const jwtPath = path.join(os.homedir(), '.openkbs', 'clientJWT');
18
18
  const generateTransactionId = () => `${+new Date()}-${Math.floor(100000 + Math.random() * 900000)}`;
19
19
 
@@ -247,12 +247,31 @@ async function pushAction(location = 'origin', targetFile, options) {
247
247
  }
248
248
 
249
249
  async function cloneAction(kbId) {
250
+ // Store the original directory so we can restore it if needed
251
+ const originalDir = process.cwd();
252
+
250
253
  try {
254
+ // Create a subdirectory with the name of the kbId
255
+ const targetDir = path.join(originalDir, kbId);
256
+
257
+ // Check if directory already exists
258
+ if (fs.existsSync(targetDir)) {
259
+ console.red(`Directory ${kbId} already exists.`);
260
+ return;
261
+ }
262
+
263
+ // Create the subdirectory
264
+ fs.mkdirSync(targetDir);
265
+
266
+ // Change to the new directory
267
+ process.chdir(targetDir);
268
+
251
269
  const localKBData = await fetchLocalKBData({forceInit: true});
252
270
 
253
271
  if (localKBData?.kbId) {
254
272
  console.red(`KB ${localKBData?.kbId} already saved in settings.json.`);
255
273
  console.yellow(`To pull the changes from OpenKBS remote use "openkbs pull"`);
274
+ process.chdir(originalDir); // Change back to original directory
256
275
  return;
257
276
  }
258
277
 
@@ -262,14 +281,26 @@ async function cloneAction(kbId) {
262
281
  await fetchAndSaveSettings({ kbId }, kbId, kbToken);
263
282
  await downloadIcon(kbId);
264
283
  await downloadFiles(['functions', 'frontend'], kbId, kbToken);
265
- console.green('Cloning complete!');
284
+ console.green(`Cloning complete! Files created in directory: ${kbId}`);
266
285
  } catch (error) {
267
286
  console.error('Error during clone operation:', error.message);
287
+ // Make sure we return to the original directory in case of error
288
+ if (process.cwd() !== originalDir) {
289
+ process.chdir(originalDir);
290
+ }
291
+ } finally {
292
+ // Always ensure we return to the original directory
293
+ if (process.cwd() !== originalDir) {
294
+ process.chdir(originalDir);
295
+ }
268
296
  }
269
297
  }
270
298
 
271
299
  async function createByTemplateAction(name) {
272
300
  try {
301
+ // Download templates from S3 first
302
+ await downloadTemplates();
303
+
273
304
  const targetDir = path.join(process.cwd(), name);
274
305
 
275
306
  if (fs.existsSync(targetDir)) {
package/src/utils.js CHANGED
@@ -10,7 +10,7 @@ const { exec } = require('child_process');
10
10
  const readline = require('readline');
11
11
  const Spinner = require('cli-spinner').Spinner;
12
12
 
13
- const TEMPLATE_DIR = path.join(__dirname, '../templates');
13
+ const TEMPLATE_DIR = path.join(os.homedir(), '.openkbs', 'templates');
14
14
 
15
15
  /**
16
16
  * Encrypts the given text using AES encryption with a passphrase.
@@ -1021,6 +1021,65 @@ async function buildLocalFilesMap(localDir, namespaces) {
1021
1021
  return filesMap;
1022
1022
  }
1023
1023
 
1024
+ async function downloadTemplates() {
1025
+ try {
1026
+ const templatesDir = TEMPLATE_DIR;
1027
+ await fs.ensureDir(templatesDir);
1028
+
1029
+ // Clear existing templates
1030
+ await fs.emptyDir(templatesDir);
1031
+
1032
+ // Download templates from S3
1033
+ await downloadTemplatesFromS3(templatesDir);
1034
+
1035
+ console.log('Templates downloaded successfully');
1036
+ } catch (error) {
1037
+ console.error('Error downloading templates:', error);
1038
+ throw error;
1039
+ }
1040
+ }
1041
+
1042
+ async function downloadTemplatesFromS3(targetDir) {
1043
+ const s3Client = new S3Client({ region: 'us-east-1' });
1044
+ const { ListObjectsV2Command, GetObjectCommand } = require('@aws-sdk/client-s3');
1045
+
1046
+ const bucket = 'openkbs-downloads';
1047
+ const prefix = 'templates/';
1048
+
1049
+ // List all objects in templates folder
1050
+ const listResponse = await s3Client.send(new ListObjectsV2Command({
1051
+ Bucket: bucket,
1052
+ Prefix: prefix
1053
+ }));
1054
+
1055
+ // Download all files in parallel
1056
+ const downloadPromises = (listResponse.Contents || []).map(async (obj) => {
1057
+ const key = obj.Key;
1058
+ const relativePath = key.substring(prefix.length);
1059
+
1060
+ // Skip if it's a directory marker
1061
+ if (relativePath.endsWith('/')) return;
1062
+
1063
+ const localPath = path.join(targetDir, relativePath);
1064
+
1065
+ // Ensure directory exists
1066
+ await fs.ensureDir(path.dirname(localPath));
1067
+
1068
+ // Download file
1069
+ const response = await s3Client.send(new GetObjectCommand({
1070
+ Bucket: bucket,
1071
+ Key: key
1072
+ }));
1073
+
1074
+ const fileContent = await streamToBuffer(response.Body);
1075
+ await fs.writeFile(localPath, fileContent);
1076
+ console.log(`Downloaded: ${relativePath}`);
1077
+ });
1078
+
1079
+ // Wait for all downloads to complete
1080
+ await Promise.all(downloadPromises);
1081
+ }
1082
+
1024
1083
  const generateKey = (passphrase) => {
1025
1084
  const salt = 'salt';
1026
1085
  const iterations = 1000;
@@ -1054,6 +1113,9 @@ const replacePlaceholderInFiles = (dir, name) => {
1054
1113
 
1055
1114
  async function initByTemplateAction(params) {
1056
1115
  try {
1116
+ // Download templates from S3 first
1117
+ await downloadTemplates();
1118
+
1057
1119
  const targetDir = process.cwd();
1058
1120
 
1059
1121
  // Copy all files and folders, skipping existing ones
@@ -1077,5 +1139,6 @@ module.exports = {
1077
1139
  KB_API_URL, AUTH_API_URL, decryptKBFields, fetchLocalKBData, fetchKBJWT, createAccountIdFromPublicKey, signPayload,
1078
1140
  listFiles, getUserProfile, getKB, fetchAndSaveSettings, downloadIcon, downloadFiles, updateKB, uploadFiles, generateKey,
1079
1141
  generateMnemonic, reset, bold, red, yellow, green, cyan, createKB, getClientJWT, saveLocalKBData, listKBs, deleteKBFile,
1080
- deleteKB, buildPackage, replacePlaceholderInFiles, buildNodePackage, initByTemplateAction, modifyKB, listKBsSharedWithMe
1142
+ deleteKB, buildPackage, replacePlaceholderInFiles, buildNodePackage, initByTemplateAction, modifyKB, listKBsSharedWithMe,
1143
+ downloadTemplates
1081
1144
  }