openkbs 0.0.36 → 0.0.38

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 (35) hide show
  1. package/package.json +2 -2
  2. package/scripts/deploy-templates.js +8 -28
  3. package/src/actions.js +30 -1
  4. package/src/utils.js +0 -1
  5. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/app/icon.png +0 -0
  6. package/templates/.openkbs/knowledge/examples/{ai-copywriter → ai-copywriter-agent}/app/instructions.txt +1 -1
  7. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/app/settings.json +6 -0
  8. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/scripts/run_job.js +26 -0
  9. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/scripts/utils/agent_client.js +169 -0
  10. package/templates/.openkbs/knowledge/examples/{ai-copywriter → ai-copywriter-agent}/src/Events/actions.js +10 -21
  11. package/templates/.openkbs/knowledge/examples/{ai-copywriter/src/Events/onResponse.js → ai-copywriter-agent/src/Events/handler.js} +6 -10
  12. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onRequest.js +3 -0
  13. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onRequest.json +5 -0
  14. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onResponse.js +3 -0
  15. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Events/onResponse.json +5 -0
  16. package/templates/.openkbs/knowledge/examples/ai-copywriter-agent/src/Frontend/contentRender.js +64 -0
  17. package/templates/.openkbs/knowledge/examples/{ai-copywriter → ai-copywriter-agent}/src/Frontend/contentRender.json +2 -1
  18. package/templates/.openkbs/knowledge/metadata.json +1 -1
  19. package/templates/CLAUDE.md +107 -13
  20. package/templates/app/instructions.txt +1 -10
  21. package/templates/app/settings.json +2 -2
  22. package/templates/related-agents/README.md +6 -0
  23. package/templates/scripts/run_job.js +26 -0
  24. package/templates/scripts/utils/agent_client.js +169 -0
  25. package/templates/src/Events/actions.js +8 -10
  26. package/templates/src/Events/handler.js +55 -0
  27. package/templates/src/Events/onRequest.js +2 -13
  28. package/templates/src/Events/onRequest.json +5 -0
  29. package/templates/src/Events/onResponse.js +2 -12
  30. package/templates/{.openkbs/knowledge/examples/ai-copywriter/src → src}/Events/onResponse.json +1 -0
  31. package/templates/.openkbs/knowledge/README.md +0 -34
  32. package/templates/.openkbs/knowledge/examples/ai-copywriter/app/icon.png +0 -0
  33. package/templates/.openkbs/knowledge/examples/ai-copywriter/app/settings.json +0 -13
  34. package/templates/.openkbs/knowledge/examples/ai-copywriter/run_agent.js +0 -120
  35. package/templates/.openkbs/knowledge/examples/ai-copywriter/src/Frontend/contentRender.js +0 -26
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "openkbs",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "description": "OpenKBS - Command Line Interface",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
7
7
  "build": "pkg .",
8
- "deploy": "npm run build && npm run deploy:linux && npm run deploy:macos && npm run deploy:win",
8
+ "deploy": "npm run build && npm run deploy:linux && npm run deploy:macos && npm run deploy:win && npm run deploy:templates",
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",
@@ -1,41 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs-extra');
3
+ const { execSync } = require('child_process');
4
4
  const path = require('path');
5
- const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
6
5
 
7
- const s3Client = new S3Client({ region: 'us-east-1' });
8
6
  const BUCKET = 'openkbs-downloads';
9
7
  const TEMPLATES_DIR = path.join(__dirname, '../templates');
10
8
 
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
9
  async function deployTemplates() {
33
10
  try {
34
- console.log('Deploying templates to S3...');
35
- await uploadDirectory(TEMPLATES_DIR, 'templates');
36
- console.log('Templates deployed successfully!');
11
+ console.log('Syncing templates to S3...');
12
+
13
+ const syncCommand = `aws s3 sync "${TEMPLATES_DIR}" s3://${BUCKET}/templates --delete`;
14
+ execSync(syncCommand, { stdio: 'inherit' });
15
+
16
+ console.log('Templates synced successfully!');
37
17
  } catch (error) {
38
- console.error('Error deploying templates:', error);
18
+ console.error('Error syncing templates:', error);
39
19
  process.exit(1);
40
20
  }
41
21
  }
package/src/actions.js CHANGED
@@ -542,6 +542,7 @@ async function updateKnowledgeAction() {
542
542
  try {
543
543
  const knowledgeDir = path.join(process.cwd(), '.openkbs', 'knowledge');
544
544
  const metadataPath = path.join(knowledgeDir, 'metadata.json');
545
+ const claudeMdPath = path.join(process.cwd(), 'CLAUDE.md');
545
546
 
546
547
  // Check if .openkbs/knowledge directory exists
547
548
  if (!fs.existsSync(knowledgeDir)) {
@@ -593,7 +594,10 @@ async function updateKnowledgeAction() {
593
594
  // Download updated knowledge files from S3
594
595
  await downloadKnowledgeFromS3(knowledgeDir);
595
596
 
596
- console.green('Knowledge base updated successfully!');
597
+ // Download CLAUDE.md file from S3
598
+ await downloadClaudeMdFromS3(claudeMdPath, s3Client, bucket);
599
+
600
+ console.green('Knowledge base and CLAUDE.md updated successfully!');
597
601
 
598
602
  } catch (error) {
599
603
  console.red('Error updating knowledge base:', error.message);
@@ -651,6 +655,31 @@ async function downloadKnowledgeFromS3(targetDir) {
651
655
  }
652
656
  }
653
657
 
658
+ async function downloadClaudeMdFromS3(claudeMdPath, s3Client, bucket) {
659
+ const claudeMdKey = 'templates/CLAUDE.md';
660
+
661
+ try {
662
+ // Download CLAUDE.md file from S3
663
+ const response = await s3Client.send(new GetObjectCommand({
664
+ Bucket: bucket,
665
+ Key: claudeMdKey
666
+ }));
667
+
668
+ const fileContent = await response.Body.transformToByteArray();
669
+ await fs.writeFile(claudeMdPath, fileContent);
670
+
671
+ // console.log('Downloaded: CLAUDE.md');
672
+
673
+ } catch (error) {
674
+ if (error.name === 'NoSuchKey') {
675
+ console.yellow('CLAUDE.md not found in remote repository, skipping...');
676
+ } else {
677
+ console.red('Error downloading CLAUDE.md from S3:', error.message);
678
+ throw error;
679
+ }
680
+ }
681
+ }
682
+
654
683
  module.exports = {
655
684
  signAction,
656
685
  loginAction,
package/src/utils.js CHANGED
@@ -1080,7 +1080,6 @@ async function downloadTemplatesFromS3(targetDir) {
1080
1080
 
1081
1081
  const fileContent = await streamToBuffer(response.Body);
1082
1082
  await fs.writeFile(localPath, fileContent);
1083
- console.log(relativePath);
1084
1083
  });
1085
1084
 
1086
1085
  // Wait for all downloads to complete
@@ -7,7 +7,7 @@ Job:
7
7
  - If you see additional instructions in PROCESS_PRODUCT, execute them.
8
8
  - When you have obtained all the necessary information, output JOB_COMPLETED response.
9
9
 
10
- Guidelines:
10
+ Important Guidelines:
11
11
  - use webpageToText command to read websites content in details
12
12
  - Always output one command per response and wait for the response before continuing
13
13
  - If an API call fails, so that you can't extract a required data, retry with different website or search query
@@ -0,0 +1,6 @@
1
+ {
2
+ "chatVendor": "anthropic",
3
+ "kbDescription": "An autonomous AI powerhouse for content generation",
4
+ "kbTitle": "Copywriter",
5
+ "model": "claude-3-5-haiku-20241022"
6
+ }
@@ -0,0 +1,26 @@
1
+ // This boilerplate code is a starting point for development.
2
+
3
+ const OpenKBSAgentClient = require('./utils/agent_client');
4
+
5
+ async function main() {
6
+ const client = new OpenKBSAgentClient();
7
+
8
+ const message = `Today's Date: ${new Date().toLocaleDateString()}
9
+
10
+ PROCESS_PRODUCT:
11
+ Product Name: iPhone 14 Pro Max
12
+ Product Code: MQ9X3RX/A
13
+ ID: 97649
14
+
15
+ find at least 2 images and 2 videos
16
+ `;
17
+
18
+ try {
19
+ const result = await client.runJob(message);
20
+ console.log('Job completed:', result);
21
+ } catch (error) {
22
+ console.error('Job failed:', error.message);
23
+ }
24
+ }
25
+
26
+ main();
@@ -0,0 +1,169 @@
1
+ const https = require('https');
2
+ const fs = require('fs');
3
+ const readline = require('readline');
4
+ const path = require('path');
5
+
6
+ class OpenKBSAgentClient {
7
+ constructor() {
8
+ this.settings = this.findSettings();
9
+ this.secretsPath = this.findSecretsPath();
10
+ this.apiKey = null;
11
+ }
12
+
13
+ findSettings() {
14
+ let currentDir = __dirname;
15
+
16
+ while (currentDir !== path.parse(currentDir).root) {
17
+ const settingsPath = path.join(currentDir, 'app', 'settings.json');
18
+ if (fs.existsSync(settingsPath)) {
19
+ return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
20
+ }
21
+ currentDir = path.dirname(currentDir);
22
+ }
23
+
24
+ throw new Error('Could not find app/settings.json in parent directories');
25
+ }
26
+
27
+ findSecretsPath() {
28
+ let currentDir = __dirname;
29
+
30
+ while (currentDir !== path.parse(currentDir).root) {
31
+ const settingsPath = path.join(currentDir, 'app', 'settings.json');
32
+ if (fs.existsSync(settingsPath)) {
33
+ const secretsPath = path.join(currentDir, '.openkbs', 'secrets.json');
34
+ return secretsPath;
35
+ }
36
+ currentDir = path.dirname(currentDir);
37
+ }
38
+
39
+ throw new Error('Could not find agent directory with app/settings.json');
40
+ }
41
+
42
+ async getApiKey() {
43
+ if (this.apiKey) return this.apiKey;
44
+
45
+ if (fs.existsSync(this.secretsPath)) {
46
+ const secrets = JSON.parse(fs.readFileSync(this.secretsPath, 'utf8'));
47
+ this.apiKey = secrets.apiKey;
48
+ return this.apiKey;
49
+ }
50
+
51
+ this.apiKey = await this.promptForApiKey();
52
+ this.saveApiKey(this.apiKey);
53
+ return this.apiKey;
54
+ }
55
+
56
+ async promptForApiKey() {
57
+ return new Promise((resolve) => {
58
+ const rl = readline.createInterface({
59
+ input: process.stdin,
60
+ output: process.stdout
61
+ });
62
+
63
+ console.log(`Please generate an API key from: https://${this.settings.kbId}.apps.openkbs.com/?tab=access&createAPIKey=api-${+new Date()}`);
64
+
65
+ rl.question('Enter your API key: ', (key) => {
66
+ rl.close();
67
+ resolve(key);
68
+ });
69
+
70
+ rl._writeToOutput = (str) => rl.output.write(rl.line ? "*" : str);
71
+ });
72
+ }
73
+
74
+ saveApiKey(key) {
75
+ const secretsDir = path.dirname(this.secretsPath);
76
+ if (!fs.existsSync(secretsDir)) {
77
+ fs.mkdirSync(secretsDir, { recursive: true });
78
+ }
79
+ fs.writeFileSync(this.secretsPath, JSON.stringify({ apiKey: key }, null, 2));
80
+ }
81
+
82
+ async runJob(message, options = {}) {
83
+ const apiKey = await this.getApiKey();
84
+
85
+ if (!this.settings.kbId) {
86
+ throw new Error('First use: "openkbs push" to create the agent');
87
+ }
88
+
89
+ const chatTitle = options.chatTitle || `Task ${new Date().getTime()}`;
90
+ const chatId = await this.startJob(chatTitle, message, { kbId: this.settings.kbId, apiKey });
91
+
92
+ console.log(`Job ${chatId} created.\nWorking ...`);
93
+
94
+ if (options.poll !== false) {
95
+ return this.pollForMessages(chatId, { kbId: this.settings.kbId, apiKey });
96
+ }
97
+
98
+ return chatId;
99
+ }
100
+
101
+ async startJob(chatTitle, data, app) {
102
+ const response = await this.makeRequest('https://chat.openkbs.com/', {
103
+ ...app,
104
+ chatTitle,
105
+ message: data
106
+ });
107
+
108
+ try {
109
+ return JSON.parse(response)[0].createdChatId;
110
+ } catch (error) {
111
+ if (fs.existsSync(this.secretsPath)) {
112
+ fs.unlinkSync(this.secretsPath);
113
+ }
114
+ throw new Error('Authentication failed.');
115
+ }
116
+ }
117
+
118
+ makeRequest(url, payload) {
119
+ return new Promise((resolve, reject) => {
120
+ const { hostname, pathname } = new URL(url);
121
+ const req = https.request({
122
+ hostname,
123
+ path: pathname,
124
+ method: 'POST',
125
+ headers: { 'Content-Type': 'application/json' }
126
+ }, res => {
127
+ let data = '';
128
+ res.on('data', chunk => data += chunk);
129
+ res.on('end', () => resolve(data));
130
+ }).on('error', reject);
131
+
132
+ req.write(JSON.stringify(payload));
133
+ req.end();
134
+ });
135
+ }
136
+
137
+ async pollForMessages(chatId, app) {
138
+ const payload = {
139
+ ...app,
140
+ action: 'getChatMessages',
141
+ chatId,
142
+ decryptContent: true
143
+ };
144
+
145
+ return new Promise((resolve) => {
146
+ const interval = setInterval(() => {
147
+ this.makeRequest('https://chat.openkbs.com/', payload)
148
+ .then(jsonString => {
149
+ const messages = JSON.parse(jsonString)[0].data.messages;
150
+ for (const message of messages) {
151
+ if (message.role === 'system' &&
152
+ /{"type"\s*:\s*"(JOB_COMPLETED|JOB_FAILED)".*?}/s.test(message.content)) {
153
+
154
+ const result = JSON.parse(message.content)?.data?.find(item =>
155
+ item.type === 'JOB_COMPLETED' || item.type === 'JOB_FAILED'
156
+ );
157
+ clearInterval(interval);
158
+ resolve(result);
159
+ return;
160
+ }
161
+ }
162
+ })
163
+ .catch(console.error);
164
+ }, 1000);
165
+ });
166
+ }
167
+ }
168
+
169
+ module.exports = OpenKBSAgentClient;
@@ -32,37 +32,33 @@ export const getActions = () => [
32
32
 
33
33
 
34
34
  [/[\s\S]*"type"\s*:\s*"JOB_FAILED"[\s\S]*/, async (match, event) => {
35
- const json = extractJSONFromText(match[0]);
36
- if (json && json.type === "JOB_FAILED") {
35
+ const parsedData = extractJSONFromText(match[0]);
36
+ if (parsedData && parsedData.type === "JOB_FAILED") {
37
37
  await openkbs.chats({
38
38
  action: "updateChat",
39
- title: await openkbs.encrypt(json.reason),
39
+ title: await openkbs.encrypt(parsedData.reason),
40
40
  chatIcon: '🔴',
41
41
  chatId: event?.payload?.chatId
42
42
  })
43
+ return parsedData;
43
44
  }
44
45
  }],
45
46
 
46
- [/\/?googleSearch\("(.*?)"(?:,\s*(\d+))?\)/, async (match) => {
47
+ [/\/?googleSearch\("(.*?)"\)/, async (match) => {
47
48
  const q = match[1];
48
- const limit = match[2] ? parseInt(match[2]) : 5;
49
-
50
49
  try {
51
50
  const response = await openkbs.googleSearch(q, {});
52
51
  const data = response?.map(({ title, link, snippet, pagemap }) => ({
53
52
  title, link, snippet, image: pagemap?.metatags?.[0]?.["og:image"]
54
- })).slice(0, limit);
55
-
56
- if(!data?.length) return { data: { error: "No results found" } };
53
+ }));
57
54
  return { data };
58
55
  } catch (e) {
59
56
  return { error: e.message };
60
57
  }
61
58
  }],
62
59
 
63
- [/\/?youtubeSearch\("(.*?)"(?:,\s*(\d+))?\)/, async (match) => {
60
+ [/\/?youtubeSearch\("(.*?)"\)/, async (match) => {
64
61
  const q = match[1];
65
- const limit = match[2] ? parseInt(match[2]) : 5;
66
62
  try {
67
63
  const response = await openkbs.googleSearch(q + ' site:youtube.com', { videoOnly: true });
68
64
  const data = response?.map(({ title, link, snippet, pagemap }) => ({
@@ -72,19 +68,15 @@ export const getActions = () => [
72
68
  thumbnail: pagemap?.videoobject?.[0]?.thumbnailurl || pagemap?.metatags?.[0]?.["og:image"],
73
69
  duration: pagemap?.videoobject?.[0]?.duration,
74
70
  channel: pagemap?.metatags?.[0]?.["og:site_name"],
75
- })).filter(item => item.link.includes('youtu')).slice(0, limit);
76
-
77
- if(!data?.length) return { data: { error: "No YouTube videos found" } };
71
+ })).filter(item => item.link.includes('youtu'));
78
72
  return { data };
79
73
  } catch (e) {
80
74
  return { error: e.message };
81
75
  }
82
76
  }],
83
77
 
84
- [/\/?googleImageSearch\("(.*?)"(?:,\s*(\d+))?\)/, async (match) => {
78
+ [/\/?googleImageSearch\("(.*?)"\)/, async (match) => {
85
79
  const q = match[1];
86
- const limit = match[2] ? parseInt(match[2]) : 10;
87
-
88
80
  try {
89
81
  const response = await openkbs.googleSearch(q, { searchType: 'image' });
90
82
  const data = response?.map(({ title, link, snippet, pagemap }) => {
@@ -96,11 +88,8 @@ export const getActions = () => [
96
88
  snippet,
97
89
  image: thumbnail
98
90
  };
99
- })?.slice(0, limit);
100
-
101
- if (!data?.length) return { data: { error: "No image results found" } };
91
+ });
102
92
  return { data };
103
-
104
93
  } catch (e) {
105
94
  return { error: e.message };
106
95
  }
@@ -1,30 +1,26 @@
1
1
  import {getActions} from './actions.js';
2
2
 
3
- export const handler = async (event) => {
3
+ export const backendHandler = async (event) => {
4
4
  const maxSelfInvokeMessagesCount = 60;
5
- const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
6
-
7
- // Check if this is a JOB_COMPLETED or JOB_FAILED event
8
- const isJobCompleted = /"type"\s*:\s*"JOB_COMPLETED"/.test(lastMessage);
9
- const isJobFailed = /"type"\s*:\s*"JOB_FAILED"/.test(lastMessage);
10
-
5
+ const lastMessage = event.payload.messages[event.payload.messages.length - 1];
11
6
  const actions = getActions();
12
7
 
13
8
  const matchingActions = actions.reduce((acc, [regex, action]) => {
14
- const matches = [...lastMessage.matchAll(new RegExp(regex, 'g'))];
9
+ const matches = [...lastMessage.content.matchAll(new RegExp(regex, 'g'))];
15
10
  matches.forEach(match => {
16
11
  acc.push(action(match, event));
17
12
  });
18
13
  return acc;
19
14
  }, []);
20
15
 
21
- const isJobFinished = isJobCompleted || isJobFailed;
16
+ // For Coding Agents: avoid unnecessary LLM reactions after job is finished
17
+ const isJobFinished = /"JOB_COMPLETED"|"JOB_FAILED"/.test(lastMessage.content);
22
18
 
23
19
  const meta = {
24
20
  _meta_actions:
25
21
  (
26
22
  event?.payload?.messages?.length > maxSelfInvokeMessagesCount ||
27
- isJobFinished && (matchingActions.length === 1 || lastMessage.role === 'system')
23
+ isJobFinished && lastMessage.role === 'system'
28
24
  )
29
25
  ? []
30
26
  : ["REQUEST_CHAT_MODEL"]
@@ -0,0 +1,3 @@
1
+ import {backendHandler} from './handler';
2
+
3
+ export const handler = async (event) => backendHandler(event)
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "mysql2": "^3.11.4"
4
+ }
5
+ }
@@ -0,0 +1,3 @@
1
+ import {backendHandler} from './handler';
2
+
3
+ export const handler = async (event) => backendHandler(event)
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "mysql2": "^3.11.4"
4
+ }
5
+ }
@@ -0,0 +1,64 @@
1
+ import React, { useEffect } from "react";
2
+ import JsonView from '@uiw/react-json-view';
3
+ import { Chip } from '@mui/material';
4
+
5
+ const extractJSONFromText = (text) => {
6
+ let braceCount = 0, startIndex = text.indexOf('{');
7
+ if (startIndex === -1) return null;
8
+
9
+ for (let i = startIndex; i < text.length; i++) {
10
+ if (text[i] === '{') braceCount++;
11
+ if (text[i] === '}' && --braceCount === 0) {
12
+ try {
13
+ return JSON.parse(text.slice(startIndex, i + 1));
14
+ } catch {
15
+ return null;
16
+ }
17
+ }
18
+ }
19
+ return null;
20
+ }
21
+
22
+ const parseCommands = (text) => {
23
+ return text.match(/\/\w+\("([^"]+)"\)/g) || [];
24
+ }
25
+
26
+ // do NOT useState() directly in this function, it is not a React component
27
+ const onRenderChatMessage = async (params) => {
28
+ const { content } = params.messages[params.msgIndex];
29
+ const JSONData = extractJSONFromText(content);
30
+ const commands = parseCommands(content);
31
+
32
+ if (JSONData?.type === 'JOB_COMPLETED') {
33
+ return <JsonView value={JSONData} />
34
+ }
35
+
36
+ if (commands.length > 0) {
37
+ return commands.map((cmd, i) => (
38
+ <Chip key={i} label={cmd} size="small" />
39
+ ));
40
+ }
41
+
42
+ // return undefined to use default message render
43
+ };
44
+
45
+ const Header = ({ setRenderSettings }) => {
46
+ useEffect(() => {
47
+ setRenderSettings({
48
+ inputLabelsQuickSend: true,
49
+ disableBalanceView: false,
50
+ disableSentLabel: false,
51
+ disableChatAvatar: false,
52
+ disableChatModelsSelect: false,
53
+ disableContextItems: false,
54
+ disableCopyButton: false,
55
+ disableEmojiButton: false,
56
+ disableTextToSpeechButton: false,
57
+ disableMobileLeftButton: false,
58
+ });
59
+ }, [setRenderSettings]);
60
+ }
61
+
62
+ const exports = { onRenderChatMessage, Header };
63
+ window.contentRender = exports;
64
+ export default exports;
@@ -5,6 +5,7 @@
5
5
  "@mui/material": "^5.16.1 (fixed)",
6
6
  "@mui/icons-material": "^5.16.1 (fixed)",
7
7
  "@emotion/react": "^11.10.6 (fixed)",
8
- "@emotion/styled": "^11.10.6 (fixed)"
8
+ "@emotion/styled": "^11.10.6 (fixed)",
9
+ "@uiw/react-json-view": "^2.0.0-alpha.33"
9
10
  }
10
11
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.0.1"
2
+ "version": "0.0.8"
3
3
  }
@@ -1,15 +1,109 @@
1
1
  # Claude Code Instructions
2
2
 
3
- ## Initial Setup
4
- 1. First, update the knowledge base to ensure you have the latest information:
5
- ```bash
6
- openkbs update knowledge
7
- ```
8
-
9
- 2. Then read the project contribution guidelines:
10
- ```bash
11
- # Read the knowledge base README for detailed instructions
12
- .openkbs/knowledge/README.md
13
- ```
14
-
15
- This file contains comprehensive instructions on how to contribute to this OpenKBS project.
3
+ # MANDATORY FIRST STEPS
4
+ **CRITICAL**: Before taking ANY action, implementing ANY feature, planing the implementation or answering ANY question about this project, you must perform the following steps:
5
+
6
+ **FIRST**: Update the knowledge base:
7
+ ```bash
8
+ openkbs update knowledge
9
+ ```
10
+
11
+ **SECOND**: Read every file in the examples folder using the Read tool:
12
+ - First discover all files in the `.openkbs/knowledge/examples/` directory and ALL subdirectories.
13
+ - Then, use the Read tool to examine the content of EACH file individually (skip only icon.png files).
14
+ - You must read ALL files directly
15
+
16
+ **THIRD**: Read existing agent code using the Read tool:
17
+ - First discover all files in the `./app/`, `./src/`, and `./scripts/` folders.
18
+ - Then, use the Read tool to examine each file individually
19
+
20
+ # OpenKBS Agent Development Guidelines
21
+
22
+ ## **Critical** Rules (**IMPORTANT**)
23
+ - Never skip reading examples
24
+ - Study the complete working examples to understand OpenKBS patterns
25
+ - Never guess framework methods, settings or variables — always reference the examples.
26
+
27
+ ## Development Guidelines
28
+ - To add npm dependency to backend handlers, add it to onRequest.json and onResponse.json
29
+ - In src/Events and src/Frontend always use Imports (not Require)
30
+ - To add npm dependency to the frontend, add it to contentRender.json
31
+ - Valid values for the _meta_actions key are [] or ["REQUEST_CHAT_MODEL"]
32
+ - Add and use npm dependencies only if necessary, some of those shown in the examples are purely demonstrative
33
+ - If developing new agent, generate it's own ./scripts/run_job.js
34
+ - Before using third-party services in onRequest and onResponse handlers, ask the user for permission
35
+
36
+ ## Architecture Overview
37
+ OpenKBS agents have **two execution environments**:
38
+
39
+ ### 1. Cloud Environment (`./src/`)
40
+ - **Event handlers** (`onRequest.js`, `onResponse.js`) run on OpenKBS cloud platform
41
+ - **Purpose**: Process user messages, execute AI actions, return responses
42
+ - **Deployment**: Code is deployed via `openkbs push`
43
+
44
+ ### 2. Local Environment (`./scripts/`)
45
+ - **User-run scripts** execute locally on user's machine
46
+ - **Purpose**: Call cloud agents via API, orchestrate multi-agent workflows, integrate with external systems
47
+ - **Execution**: Run directly with `node scripts/script-name.js`
48
+
49
+ ### Backend
50
+ The OpenKBS backend framework is for developing AI agents with custom tools, using Node.js.
51
+ It integrates with openkbs chat service via `onRequest` and `onResponse` handlers for custom actions and service integration.
52
+
53
+ #### Backend Handlers
54
+ The OpenKBS framework's core uses `onRequest` and `onResponse` handlers as middleware for message tool call parsing and execution.
55
+ All these event handlers are executed on-demand (upon API request) by the OpenKBS cloud platform, where user production agents are deployed.
56
+ - **`onResponse` Handler:** Activated after the LLM generates a message, enabling command extraction, and action execution.
57
+ - **`onRequest` Handler:** Triggered on user message to allow the user to execute action
58
+
59
+ #### NPM Dependencies for onRequest.js or onResponse.js Backend Handlers
60
+ 1. If a file imports an NPM dependency and is then imported by onRequest.js or onResponse.js, this dependency must be defined in the handler's corresponding json file
61
+ Example: If actions.js imports mysql2 and onResponse.js imports actions.js, then mysql2 must be in onResponse.json:
62
+ {
63
+ "dependencies": {
64
+ "mysql2": "^3.14.2"
65
+ }
66
+ }
67
+
68
+ Similarly, we need to create onRequest.json for onRequest.js as each handler have separate Node.js build with separate dependencies
69
+
70
+ #### Managing Secrets
71
+ To securely manage sensitive information like API keys or database passwords within your backend event handlers (onRequest, onResponse, etc.), use the {{secrets.your_secret_name}} syntax.
72
+
73
+ #### User-Run Scripts
74
+ **User-run scripts** are located in `./scripts/` folder and communicate with cloud agents via API calls.
75
+
76
+ **Key Components:**
77
+ - `scripts/run_job.js` - Main job runner for calling the cloud agent
78
+ - `scripts/utils/agent_client.js` - Shared utility using `OpenKBSAgentClient` class
79
+ - Custom workflow scripts for multi-agent orchestration
80
+
81
+ **Architecture:**
82
+ - Scripts use `OpenKBSAgentClient` to communicate with deployed cloud agents
83
+ - **Path Resolution**: Automatically finds `app/settings.json` and `.openkbs/secrets.json` by walking up directories
84
+ - **Usage**: `const client = new OpenKBSAgentClient(); await client.runJob(message);`
85
+ - **Multi-agent support**: Each agent (base or related) finds its own settings and secrets in its directory structure
86
+
87
+ #### NPM Dependencies for User-Run Scripts
88
+ Add needed NPM dependencies to `package.json`.
89
+
90
+ ### Frontend Overview
91
+ The OpenKBS frontend framework, built with React and MUI, offers a flexible platform for custom chat interfaces. Developers can customize chat appearance and behavior via the `contentRender` module.
92
+
93
+ #### contentRender
94
+ The `contentRender.js` file is central to frontend customization, exporting key functions for interface adjustments.
95
+ - **`onRenderChatMessage(params)`:** function called every time a chat message is rendered.
96
+
97
+ #### OpenKBS commands
98
+ `openkbs push` - deploy the agent to openkbs cloud
99
+ `openkbs create my-agent` - creates a directory structure for a new agent
100
+
101
+ ### Creating Related Agents
102
+ To create related agents that work alongside the main agent:
103
+
104
+ 1. **Create in related-agents/ folder**: `cd related-agents && openkbs create agent-name`
105
+ 2. **Each related agent gets**: Own `app/settings.json`, `src/` folder, and `.openkbs/secrets.json`
106
+ 3. **Script usage**: Related agents use same `OpenKBSAgentClient` - it automatically finds their settings and secrets
107
+ 4. **Multi-agent workflows**: Scripts can orchestrate multiple agents by creating separate client instances
108
+
109
+ Related agents are independent but can share the base agent's script utilities.
@@ -1,10 +1 @@
1
- You are an AI assistant, assist users with their inquiries and tasks efficiently.
2
-
3
- Here are some APIs you can use to assist users:
4
-
5
- /googleSearch("query")
6
- Description: """
7
- Get results from Google Search API.
8
- """
9
- $InputLabel = """Let me Search in Google!"""
10
- $InputValue = """Search in google for the latest news"""
1
+ You are an AI assistant, assist users with their inquiries and tasks efficiently.
@@ -1,8 +1,8 @@
1
1
  {
2
- "chatVendor": "openai",
2
+ "chatVendor": "anthropic",
3
3
  "kbDescription": "{{{openkbsAppName}}}",
4
4
  "kbTitle": "{{{openkbsAppName}}}",
5
- "model": "gpt-4o",
5
+ "model": "claude-sonnet-4-20250514",
6
6
  "inputTools": [
7
7
  "imageToText",
8
8
  "speechToText"
@@ -0,0 +1,6 @@
1
+ # Related Agents
2
+
3
+ To add a related agent, use:
4
+ ```
5
+ openkbs create "my-agent"
6
+ ```
@@ -0,0 +1,26 @@
1
+ // This boilerplate code is a starting point for development.
2
+
3
+ const OpenKBSAgentClient = require('./utils/agent_client');
4
+
5
+ async function main() {
6
+ const client = new OpenKBSAgentClient();
7
+
8
+ const message = `Today's Date: ${new Date().toLocaleDateString()}
9
+
10
+ PROCESS_PRODUCT:
11
+ Product Name: iPhone 14 Pro Max
12
+ Product Code: MQ9X3RX/A
13
+ ID: 97649
14
+
15
+ find at least 2 images and 2 videos
16
+ `;
17
+
18
+ try {
19
+ const result = await client.runJob(message);
20
+ console.log('Job completed:', result);
21
+ } catch (error) {
22
+ console.error('Job failed:', error.message);
23
+ }
24
+ }
25
+
26
+ main();
@@ -0,0 +1,169 @@
1
+ const https = require('https');
2
+ const fs = require('fs');
3
+ const readline = require('readline');
4
+ const path = require('path');
5
+
6
+ class OpenKBSAgentClient {
7
+ constructor() {
8
+ this.settings = this.findSettings();
9
+ this.secretsPath = this.findSecretsPath();
10
+ this.apiKey = null;
11
+ }
12
+
13
+ findSettings() {
14
+ let currentDir = __dirname;
15
+
16
+ while (currentDir !== path.parse(currentDir).root) {
17
+ const settingsPath = path.join(currentDir, 'app', 'settings.json');
18
+ if (fs.existsSync(settingsPath)) {
19
+ return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
20
+ }
21
+ currentDir = path.dirname(currentDir);
22
+ }
23
+
24
+ throw new Error('Could not find app/settings.json in parent directories');
25
+ }
26
+
27
+ findSecretsPath() {
28
+ let currentDir = __dirname;
29
+
30
+ while (currentDir !== path.parse(currentDir).root) {
31
+ const settingsPath = path.join(currentDir, 'app', 'settings.json');
32
+ if (fs.existsSync(settingsPath)) {
33
+ const secretsPath = path.join(currentDir, '.openkbs', 'secrets.json');
34
+ return secretsPath;
35
+ }
36
+ currentDir = path.dirname(currentDir);
37
+ }
38
+
39
+ throw new Error('Could not find agent directory with app/settings.json');
40
+ }
41
+
42
+ async getApiKey() {
43
+ if (this.apiKey) return this.apiKey;
44
+
45
+ if (fs.existsSync(this.secretsPath)) {
46
+ const secrets = JSON.parse(fs.readFileSync(this.secretsPath, 'utf8'));
47
+ this.apiKey = secrets.apiKey;
48
+ return this.apiKey;
49
+ }
50
+
51
+ this.apiKey = await this.promptForApiKey();
52
+ this.saveApiKey(this.apiKey);
53
+ return this.apiKey;
54
+ }
55
+
56
+ async promptForApiKey() {
57
+ return new Promise((resolve) => {
58
+ const rl = readline.createInterface({
59
+ input: process.stdin,
60
+ output: process.stdout
61
+ });
62
+
63
+ console.log(`Please generate an API key from: https://${this.settings.kbId}.apps.openkbs.com/?tab=access&createAPIKey=api-${+new Date()}`);
64
+
65
+ rl.question('Enter your API key: ', (key) => {
66
+ rl.close();
67
+ resolve(key);
68
+ });
69
+
70
+ rl._writeToOutput = (str) => rl.output.write(rl.line ? "*" : str);
71
+ });
72
+ }
73
+
74
+ saveApiKey(key) {
75
+ const secretsDir = path.dirname(this.secretsPath);
76
+ if (!fs.existsSync(secretsDir)) {
77
+ fs.mkdirSync(secretsDir, { recursive: true });
78
+ }
79
+ fs.writeFileSync(this.secretsPath, JSON.stringify({ apiKey: key }, null, 2));
80
+ }
81
+
82
+ async runJob(message, options = {}) {
83
+ const apiKey = await this.getApiKey();
84
+
85
+ if (!this.settings.kbId) {
86
+ throw new Error('First use: "openkbs push" to create the agent');
87
+ }
88
+
89
+ const chatTitle = options.chatTitle || `Task ${new Date().getTime()}`;
90
+ const chatId = await this.startJob(chatTitle, message, { kbId: this.settings.kbId, apiKey });
91
+
92
+ console.log(`Job ${chatId} created.\nWorking ...`);
93
+
94
+ if (options.poll !== false) {
95
+ return this.pollForMessages(chatId, { kbId: this.settings.kbId, apiKey });
96
+ }
97
+
98
+ return chatId;
99
+ }
100
+
101
+ async startJob(chatTitle, data, app) {
102
+ const response = await this.makeRequest('https://chat.openkbs.com/', {
103
+ ...app,
104
+ chatTitle,
105
+ message: data
106
+ });
107
+
108
+ try {
109
+ return JSON.parse(response)[0].createdChatId;
110
+ } catch (error) {
111
+ if (fs.existsSync(this.secretsPath)) {
112
+ fs.unlinkSync(this.secretsPath);
113
+ }
114
+ throw new Error('Authentication failed.');
115
+ }
116
+ }
117
+
118
+ makeRequest(url, payload) {
119
+ return new Promise((resolve, reject) => {
120
+ const { hostname, pathname } = new URL(url);
121
+ const req = https.request({
122
+ hostname,
123
+ path: pathname,
124
+ method: 'POST',
125
+ headers: { 'Content-Type': 'application/json' }
126
+ }, res => {
127
+ let data = '';
128
+ res.on('data', chunk => data += chunk);
129
+ res.on('end', () => resolve(data));
130
+ }).on('error', reject);
131
+
132
+ req.write(JSON.stringify(payload));
133
+ req.end();
134
+ });
135
+ }
136
+
137
+ async pollForMessages(chatId, app) {
138
+ const payload = {
139
+ ...app,
140
+ action: 'getChatMessages',
141
+ chatId,
142
+ decryptContent: true
143
+ };
144
+
145
+ return new Promise((resolve) => {
146
+ const interval = setInterval(() => {
147
+ this.makeRequest('https://chat.openkbs.com/', payload)
148
+ .then(jsonString => {
149
+ const messages = JSON.parse(jsonString)[0].data.messages;
150
+ for (const message of messages) {
151
+ if (message.role === 'system' &&
152
+ /{"type"\s*:\s*"(JOB_COMPLETED|JOB_FAILED)".*?}/s.test(message.content)) {
153
+
154
+ const result = JSON.parse(message.content)?.data?.find(item =>
155
+ item.type === 'JOB_COMPLETED' || item.type === 'JOB_FAILED'
156
+ );
157
+ clearInterval(interval);
158
+ resolve(result);
159
+ return;
160
+ }
161
+ }
162
+ })
163
+ .catch(console.error);
164
+ }, 1000);
165
+ });
166
+ }
167
+ }
168
+
169
+ module.exports = OpenKBSAgentClient;
@@ -1,18 +1,16 @@
1
- export const getActions = (meta) => [
2
- [/\/?googleSearch\("(.*)"\)/, async (match) => {
1
+ // This boilerplate code is a starting point for development.
2
+ export const getActions = () => [
3
+ [/\/?googleSearch\("(.*?)"\)/, async (match) => {
3
4
  const q = match[1];
4
5
  try {
5
- const noSecrets = '{{secrets.googlesearch_api_key}}'.includes('secrets.googlesearch_api_key');
6
- const params = { q, ...(noSecrets ? {} : { key: '{{secrets.googlesearch_api_key}}', cx: '{{secrets.googlesearch_engine_id}}' }) };
7
- const response = noSecrets
8
- ? await openkbs.googleSearch(params.q, params)
9
- : (await axios.get('https://www.googleapis.com/customsearch/v1', { params }))?.data?.items;
6
+ const response = await openkbs.googleSearch(q, {});
10
7
  const data = response?.map(({ title, link, snippet, pagemap }) => ({
11
8
  title, link, snippet, image: pagemap?.metatags?.[0]?.["og:image"]
12
9
  }));
13
- return { data, ...meta };
10
+ return { data };
14
11
  } catch (e) {
15
- return { error: e.response.data, ...meta };
12
+ return { error: e.message };
16
13
  }
17
- }]
14
+ }],
15
+ // add more actions here
18
16
  ];
@@ -0,0 +1,55 @@
1
+ import {getActions} from './actions.js';
2
+
3
+ export const backendHandler = async (event) => {
4
+ const maxSelfInvokeMessagesCount = 60;
5
+ const lastMessage = event.payload.messages[event.payload.messages.length - 1];
6
+ const actions = getActions();
7
+
8
+ const matchingActions = actions.reduce((acc, [regex, action]) => {
9
+ const matches = [...lastMessage.content.matchAll(new RegExp(regex, 'g'))];
10
+ matches.forEach(match => {
11
+ acc.push(action(match, event));
12
+ });
13
+ return acc;
14
+ }, []);
15
+
16
+ // Avoid unnecessary LLM reactions if job is finished
17
+ const isJobFinished = /"JOB_COMPLETED"|"JOB_FAILED"/.test(lastMessage.content);
18
+
19
+ const meta = {
20
+ _meta_actions:
21
+ (
22
+ event?.payload?.messages?.length > maxSelfInvokeMessagesCount ||
23
+ isJobFinished && lastMessage.role === 'system'
24
+ )
25
+ ? []
26
+ : ["REQUEST_CHAT_MODEL"]
27
+ }
28
+
29
+ if (matchingActions.length > 0) {
30
+ try {
31
+ const results = await Promise.all(matchingActions);
32
+
33
+ if (results?.[0]?.data?.some?.(o => o?.type === 'image_url')) {
34
+ return {
35
+ ...results[0],
36
+ ...meta
37
+ };
38
+ }
39
+
40
+ return {
41
+ type: 'RESPONSE',
42
+ data: results,
43
+ ...meta
44
+ };
45
+ } catch (error) {
46
+ return {
47
+ type: 'ERROR',
48
+ error: error.message,
49
+ ...meta
50
+ };
51
+ }
52
+ }
53
+
54
+ return { type: 'CONTINUE' };
55
+ };
@@ -1,14 +1,3 @@
1
- import {getActions} from './actions.js';
1
+ import {backendHandler} from './handler';
2
2
 
3
-
4
- export const handler = async (event) => {
5
- const actions = getActions({});
6
-
7
- for (let [regex, action] of actions) {
8
- const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
9
- const match = lastMessage?.match(regex);
10
- if (match) return await action(match);
11
- }
12
-
13
- return { type: 'CONTINUE' }
14
- };
3
+ export const handler = async (event) => backendHandler(event)
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+
4
+ }
5
+ }
@@ -1,13 +1,3 @@
1
- import {getActions} from './actions.js';
1
+ import {backendHandler} from './handler';
2
2
 
3
- export const handler = async (event) => {
4
- const actions = getActions({_meta_actions: ["REQUEST_CHAT_MODEL"]});
5
-
6
- for (let [regex, action] of actions) {
7
- const lastMessage = event.payload.messages[event.payload.messages.length - 1].content;
8
- const match = lastMessage?.match(regex);
9
- if (match) return await action(match);
10
- }
11
-
12
- return { type: 'CONTINUE' }
13
- };
3
+ export const handler = async (event) => backendHandler(event)
@@ -1,34 +0,0 @@
1
- # OpenKBS Development Guidelines
2
-
3
- ## Development Flow
4
-
5
- 1. **Read existing code first:**
6
- - `./app/` folder (settings, instructions, etc.)
7
- - `./src/` folder (all Events and Frontend files)
8
-
9
- 2. **Assess implementation requirements:**
10
- - **Minor changes only**: Simple modifications to existing code that don't require framework knowledge
11
- - **Framework implementation needed**: New features, new event handlers, new frontend components, or placeholder app development
12
-
13
- 3. **For framework implementation:**
14
- - Read all source code in `.openkbs/knowledge/examples/`
15
- - Study complete working applications to understand OpenKBS patterns
16
- - Never guess framework methods or variables - always reference examples
17
-
18
- ## When Examples Are Required
19
-
20
- - New OpenKBS application development (placeholder app)
21
- - Adding new event handlers (onRequest, onResponse, etc.)
22
- - Creating new frontend components
23
- - Implementing OpenKBS-specific functionality
24
- - Any framework-related implementation
25
-
26
- ## When Examples May Not Be Needed
27
-
28
- - Simple text changes to existing files
29
- - Minor bug fixes in existing code
30
- - Configuration adjustments that don't involve framework methods
31
-
32
- ## Critical Rule
33
-
34
- **When in doubt, read the examples.** They contain all necessary framework knowledge for proper OpenKBS implementation.
@@ -1,13 +0,0 @@
1
- {
2
- "chatVendor": "anthropic",
3
- "kbDescription": "An autonomous AI powerhouse for content generation",
4
- "kbTitle": "Copywriter",
5
- "model": "claude-3-5-haiku-20241022",
6
- "inputTools": [
7
- "imageToText"
8
- ],
9
- "embeddingModel": "none",
10
- "embeddingDimension": "none",
11
- "searchEngine": "IndexedDB",
12
- "itemTypes": {}
13
- }
@@ -1,120 +0,0 @@
1
- const https = require('https');
2
- const fs = require('fs');
3
- const readline = require('readline');
4
- const path = require('path');
5
-
6
- const settings = JSON.parse(fs.readFileSync('app/settings.json', 'utf8'));
7
- const secretsPath = path.join('.openkbs', 'secrets.json');
8
- let apiKey;
9
-
10
- const message = `PROCESS_PRODUCT:
11
- Product Name: iPhone 14 Pro Max
12
- Product Code: MQ9X3RX/A
13
- ID: 97649
14
-
15
- find at least 2 images and 2 videos
16
- `;
17
-
18
- if (!settings.kbId) {
19
- console.log('First use: "openkbs push" To create the agent')
20
- return;
21
- }
22
-
23
- // Function to read the API key from secrets.json
24
- function getApiKey() {
25
- if (fs.existsSync(secretsPath)) {
26
- const secrets = JSON.parse(fs.readFileSync(secretsPath, 'utf8'));
27
- return secrets.apiKey;
28
- }
29
- return null;
30
- }
31
-
32
- function promptForApiKey() {
33
- return new Promise((resolve) => {
34
- const rl = readline.createInterface({
35
- input: process.stdin,
36
- output: process.stdout
37
- });
38
-
39
- console.log(`Please generate an API key from: https://${settings.kbId}.apps.openkbs.com/?tab=access&createAPIKey=api-${+new Date()}`);
40
-
41
- rl.question('Enter your API key: ', (key) => {
42
- rl.close();
43
- resolve(key);
44
- });
45
-
46
- rl._writeToOutput = (str) => rl.output.write(rl.line ? "*" : str);
47
- });
48
- }
49
-
50
- function saveApiKey(key) {
51
- if (!fs.existsSync('.openkbs')) {
52
- fs.mkdirSync('.openkbs');
53
- }
54
- fs.writeFileSync(secretsPath, JSON.stringify({ apiKey: key }, null, 2));
55
- }
56
-
57
- async function main() {
58
- apiKey = getApiKey();
59
-
60
- if (!apiKey) {
61
- apiKey = await promptForApiKey();
62
- console.log('\n')
63
- saveApiKey(apiKey);
64
- }
65
-
66
- const app = { kbId: settings.kbId, apiKey };
67
-
68
- const chatTitle = "Task " + new Date().getTime();
69
- startJob(chatTitle, message, app).then(chatId => {
70
- console.log(`Job ${chatId} created.\nWorking ...`);
71
- pollForMessages(chatId, app);
72
- }).catch(console.error);
73
- }
74
-
75
- function startJob(chatTitle, data, app) {
76
- return makeRequest('https://chat.openkbs.com/', { ...app, chatTitle, message: data })
77
- .then(res => {
78
- try {
79
- return JSON.parse(res)[0].createdChatId;
80
- } catch (error) {
81
- fs.unlinkSync(secretsPath);
82
- throw 'Authentication failed.';
83
- }
84
- });
85
- }
86
-
87
- function makeRequest(url, payload) {
88
- return new Promise((resolve, reject) => {
89
- const { hostname, pathname } = new URL(url);
90
- const req = https.request({
91
- hostname, path: pathname, method: 'POST',
92
- headers: { 'Content-Type': 'application/json' }
93
- }, res => {
94
- let data = '';
95
- res.on('data', chunk => data += chunk);
96
- res.on('end', () => resolve(data));
97
- }).on('error', reject);
98
-
99
- req.write(JSON.stringify(payload));
100
- req.end();
101
- });
102
- }
103
-
104
- function pollForMessages(chatId, app) {
105
- const payload = { ...app, action: 'getChatMessages', chatId, decryptContent: true };
106
- const interval = setInterval(() => {
107
- makeRequest('https://chat.openkbs.com/', payload).then(jsonString => {
108
- const messages = JSON.parse(jsonString)[0].data.messages;
109
- for (const message of messages) {
110
- if (message.role === 'system' && /{"type"\s*:\s*"(JOB_COMPLETED|JOB_FAILED)".*?}/s.test(message.content)) {
111
- console.log(JSON.parse(message.content).data[0]);
112
- clearInterval(interval);
113
- return;
114
- }
115
- }
116
- }).catch(console.error);
117
- }, 1000);
118
- }
119
-
120
- main();
@@ -1,26 +0,0 @@
1
- import React, { useEffect } from "react";
2
-
3
- const onRenderChatMessage = async (params) => {
4
- return;
5
- };
6
-
7
- const Header = ({ setRenderSettings }) => {
8
- useEffect(() => {
9
- setRenderSettings({
10
- inputLabelsQuickSend: true,
11
- disableBalanceView: false,
12
- disableSentLabel: false,
13
- disableChatAvatar: false,
14
- disableChatModelsSelect: false,
15
- disableContextItems: false,
16
- disableCopyButton: false,
17
- disableEmojiButton: false,
18
- disableTextToSpeechButton: false,
19
- disableMobileLeftButton: false,
20
- });
21
- }, [setRenderSettings]);
22
- }
23
-
24
- const exports = { onRenderChatMessage, Header };
25
- window.contentRender = exports;
26
- export default exports;