openkbs 0.0.67 → 0.0.70

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 (62) hide show
  1. package/README.md +1 -0
  2. package/elastic/README.md +1 -1
  3. package/elastic/functions.md +5 -5
  4. package/elastic/pulse.md +2 -2
  5. package/package.json +2 -2
  6. package/scripts/deploy.js +68 -0
  7. package/src/actions.js +7 -0
  8. package/templates/.claude/skills/openkbs/SKILL.md +37 -8
  9. package/templates/.claude/skills/openkbs/examples/monitoring-bot/README.md +55 -0
  10. package/templates/.claude/skills/openkbs/examples/monitoring-bot/app/instructions.txt +40 -0
  11. package/templates/.claude/skills/openkbs/examples/monitoring-bot/app/settings.json +41 -0
  12. package/templates/.claude/skills/openkbs/examples/monitoring-bot/openkbs.json +3 -0
  13. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/actions.js +141 -0
  14. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/handler.js +32 -0
  15. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/memoryHelpers.js +91 -0
  16. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onCronjob.js +105 -0
  17. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onPublicAPIRequest.js +165 -0
  18. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onRequest.js +2 -0
  19. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onResponse.js +2 -0
  20. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Frontend/contentRender.js +74 -0
  21. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Frontend/contentRender.json +3 -0
  22. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/auth/index.mjs +228 -0
  23. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/auth/package.json +7 -0
  24. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/posts/index.mjs +287 -0
  25. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/posts/package.json +10 -0
  26. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/settings.json +4 -0
  27. package/templates/.claude/skills/openkbs/examples/nodejs-demo/openkbs.json +16 -0
  28. package/templates/.claude/skills/openkbs/examples/nodejs-demo/site/index.html +658 -0
  29. package/templates/.claude/skills/openkbs/examples/nodejs-demo/site/settings.json +4 -0
  30. package/templates/.claude/skills/openkbs/patterns/cronjob-batch-processing.md +278 -0
  31. package/templates/.claude/skills/openkbs/patterns/cronjob-monitoring.md +341 -0
  32. package/templates/.claude/skills/openkbs/patterns/file-upload.md +205 -0
  33. package/templates/.claude/skills/openkbs/patterns/image-generation.md +139 -0
  34. package/templates/.claude/skills/openkbs/patterns/memory-system.md +264 -0
  35. package/templates/.claude/skills/openkbs/patterns/public-api-item-proxy.md +254 -0
  36. package/templates/.claude/skills/openkbs/patterns/scheduled-tasks.md +157 -0
  37. package/templates/.claude/skills/openkbs/patterns/telegram-webhook.md +424 -0
  38. package/templates/.claude/skills/openkbs/patterns/telegram.md +222 -0
  39. package/templates/.claude/skills/openkbs/patterns/vectordb-archive.md +231 -0
  40. package/templates/.claude/skills/openkbs/patterns/video-generation.md +145 -0
  41. package/templates/.claude/skills/openkbs/patterns/web-publishing.md +257 -0
  42. package/templates/.claude/skills/openkbs/reference/backend-sdk.md +13 -2
  43. package/templates/.claude/skills/openkbs/reference/elastic-services.md +61 -29
  44. package/templates/platform/README.md +15 -50
  45. package/templates/platform/agents/assistant/app/icon.png +0 -0
  46. package/templates/platform/agents/assistant/app/instructions.txt +9 -21
  47. package/templates/platform/agents/assistant/app/settings.json +11 -15
  48. package/templates/platform/agents/assistant/src/Events/actions.js +31 -62
  49. package/templates/platform/agents/assistant/src/Events/handler.js +54 -0
  50. package/templates/platform/agents/assistant/src/Events/onRequest.js +3 -0
  51. package/templates/platform/agents/assistant/src/Events/onRequest.json +5 -0
  52. package/templates/platform/agents/assistant/src/Events/onResponse.js +2 -40
  53. package/templates/platform/agents/assistant/src/Events/onResponse.json +4 -2
  54. package/templates/platform/agents/assistant/src/Frontend/contentRender.js +17 -16
  55. package/templates/platform/agents/assistant/src/Frontend/contentRender.json +1 -1
  56. package/templates/platform/functions/api/index.mjs +17 -23
  57. package/templates/platform/functions/api/package.json +4 -0
  58. package/templates/platform/openkbs.json +7 -2
  59. package/templates/platform/site/index.html +18 -19
  60. package/version.json +3 -3
  61. package/templates/.claude/skills/openkbs/examples/ai-copywriter-agent/scripts/run_job.js +0 -26
  62. package/templates/.claude/skills/openkbs/examples/ai-copywriter-agent/scripts/utils/agent_client.js +0 -265
@@ -0,0 +1,287 @@
1
+ import pg from 'pg';
2
+ import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
3
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
4
+ import pulse from 'openkbs-pulse/server';
5
+
6
+ const { Client } = pg;
7
+
8
+ const db = new Client({ connectionString: process.env.DATABASE_URL });
9
+ let dbConnected = false;
10
+
11
+ const s3 = new S3Client({ region: process.env.STORAGE_REGION || 'us-east-1' });
12
+
13
+ async function connectDB() {
14
+ if (!dbConnected) {
15
+ await db.connect();
16
+ dbConnected = true;
17
+
18
+ // Posts table
19
+ await db.query(`
20
+ CREATE TABLE IF NOT EXISTS posts (
21
+ id SERIAL PRIMARY KEY,
22
+ user_id INTEGER NOT NULL,
23
+ user_name VARCHAR(255) NOT NULL,
24
+ content TEXT,
25
+ image_url TEXT,
26
+ created_at TIMESTAMP DEFAULT NOW()
27
+ )
28
+ `);
29
+
30
+ await db.query(`
31
+ ALTER TABLE posts ADD COLUMN IF NOT EXISTS image_url TEXT
32
+ `);
33
+
34
+ // Messages table for private chat
35
+ await db.query(`
36
+ CREATE TABLE IF NOT EXISTS messages (
37
+ id SERIAL PRIMARY KEY,
38
+ from_user_id INTEGER NOT NULL,
39
+ from_user_name VARCHAR(255) NOT NULL,
40
+ to_user_id INTEGER NOT NULL,
41
+ content TEXT NOT NULL,
42
+ created_at TIMESTAMP DEFAULT NOW()
43
+ )
44
+ `);
45
+ }
46
+ }
47
+
48
+ export async function handler(event) {
49
+ const headers = {
50
+ 'Content-Type': 'application/json',
51
+ 'Access-Control-Allow-Origin': '*',
52
+ 'Access-Control-Allow-Headers': 'Content-Type'
53
+ };
54
+
55
+ // Handle OPTIONS preflight
56
+ if (event.requestContext?.http?.method === 'OPTIONS') {
57
+ return { statusCode: 200, headers, body: '' };
58
+ }
59
+
60
+ try {
61
+ const body = JSON.parse(event.body || '{}');
62
+ const { action, content, imageUrl, userId, userName, fileName, contentType } = body;
63
+
64
+ // getUploadUrl doesn't need DB connection
65
+ if (action === 'getUploadUrl') {
66
+ const bucket = process.env.STORAGE_BUCKET;
67
+ if (!bucket) {
68
+ return {
69
+ statusCode: 500,
70
+ headers,
71
+ body: JSON.stringify({ error: 'Storage not configured' })
72
+ };
73
+ }
74
+
75
+ // Generate unique key for the file
76
+ // Key must match CloudFront path prefix (e.g., /media/* -> media/...)
77
+ const timestamp = Date.now();
78
+ const safeName = (fileName || 'image.jpg').replace(/[^a-zA-Z0-9.-]/g, '_');
79
+ const key = `media/uploads/${timestamp}-${safeName}`;
80
+
81
+ // Create presigned URL for PUT
82
+ const command = new PutObjectCommand({
83
+ Bucket: bucket,
84
+ Key: key,
85
+ ContentType: contentType || 'image/jpeg'
86
+ });
87
+
88
+ const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 3600 });
89
+
90
+ // Use CloudFront URL - key already contains the path prefix (media/uploads/...)
91
+ // Final URL: /media/uploads/filename.png
92
+ const publicUrl = `/${key}`;
93
+
94
+ return {
95
+ statusCode: 200,
96
+ headers,
97
+ body: JSON.stringify({ uploadUrl, publicUrl, key })
98
+ };
99
+ }
100
+
101
+ // All other actions need DB
102
+ await connectDB();
103
+
104
+ if (action === 'list') {
105
+ // Get latest 50 posts
106
+ const result = await db.query(
107
+ 'SELECT id, user_id, user_name, content, image_url, created_at FROM posts ORDER BY created_at DESC LIMIT 50'
108
+ );
109
+
110
+ const posts = result.rows.map(row => ({
111
+ id: row.id,
112
+ userId: row.user_id,
113
+ userName: row.user_name,
114
+ content: row.content,
115
+ imageUrl: row.image_url,
116
+ createdAt: row.created_at
117
+ }));
118
+
119
+ return {
120
+ statusCode: 200,
121
+ headers,
122
+ body: JSON.stringify({ posts })
123
+ };
124
+ }
125
+
126
+ if (action === 'presence') {
127
+ const result = await pulse.presence(body.channel || 'posts');
128
+ return {
129
+ statusCode: 200,
130
+ headers,
131
+ body: JSON.stringify({ count: result.count || 0 })
132
+ };
133
+ }
134
+
135
+ if (action === 'create') {
136
+ // Allow posts with just image (no content required)
137
+ if (!content && !imageUrl) {
138
+ return {
139
+ statusCode: 400,
140
+ headers,
141
+ body: JSON.stringify({ error: 'Content or image required' })
142
+ };
143
+ }
144
+ if (!userId || !userName) {
145
+ return {
146
+ statusCode: 400,
147
+ headers,
148
+ body: JSON.stringify({ error: 'Missing user info' })
149
+ };
150
+ }
151
+
152
+ // Insert post with optional image
153
+ const result = await db.query(
154
+ 'INSERT INTO posts (user_id, user_name, content, image_url) VALUES ($1, $2, $3, $4) RETURNING id, created_at',
155
+ [userId, userName, content || '', imageUrl || null]
156
+ );
157
+
158
+ const post = {
159
+ id: result.rows[0].id,
160
+ userId,
161
+ userName,
162
+ content: content || '',
163
+ imageUrl: imageUrl || null,
164
+ createdAt: result.rows[0].created_at
165
+ };
166
+
167
+ // Broadcast to Pulse
168
+ const kbId = process.env.OPENKBS_KB_ID;
169
+ const apiKey = process.env.OPENKBS_API_KEY;
170
+ const postPublish = await pulse.publish('posts', 'new_post', { post }, { kbId, apiKey });
171
+ console.log('Post publish result:', JSON.stringify(postPublish));
172
+
173
+ return {
174
+ statusCode: 200,
175
+ headers,
176
+ body: JSON.stringify({ post, debug: { postPublish } })
177
+ };
178
+ }
179
+
180
+ // Send private message - publishes to recipient's SECRET channel
181
+ if (action === 'sendMessage') {
182
+ const { toUserId, message, fromUserId, fromUserName } = body;
183
+
184
+ if (!toUserId || !message || !fromUserId || !fromUserName) {
185
+ return {
186
+ statusCode: 400,
187
+ headers,
188
+ body: JSON.stringify({ error: 'Missing required fields' })
189
+ };
190
+ }
191
+
192
+ // Look up recipient's PRIVATE channel (stored in users table)
193
+ const recipientResult = await db.query(
194
+ 'SELECT private_channel, name FROM users WHERE id = $1',
195
+ [toUserId]
196
+ );
197
+
198
+ if (recipientResult.rows.length === 0) {
199
+ return {
200
+ statusCode: 404,
201
+ headers,
202
+ body: JSON.stringify({ error: 'Recipient not found' })
203
+ };
204
+ }
205
+
206
+ const recipientChannel = recipientResult.rows[0].private_channel;
207
+
208
+ // Store message in database
209
+ const msgResult = await db.query(
210
+ 'INSERT INTO messages (from_user_id, from_user_name, to_user_id, content) VALUES ($1, $2, $3, $4) RETURNING id, created_at',
211
+ [fromUserId, fromUserName, toUserId, message]
212
+ );
213
+
214
+ const msgData = {
215
+ id: msgResult.rows[0].id,
216
+ fromUserId,
217
+ fromUserName,
218
+ toUserId,
219
+ content: message,
220
+ createdAt: msgResult.rows[0].created_at
221
+ };
222
+
223
+ // Publish to recipient's SECRET private channel
224
+ // Only the recipient is subscribed to this channel!
225
+ const kbId = process.env.OPENKBS_KB_ID;
226
+ const apiKey = process.env.OPENKBS_API_KEY;
227
+ console.log('Publishing to private channel:', recipientChannel.substring(0, 8) + '...');
228
+ const publishResult = await pulse.publish(recipientChannel, 'new_message', msgData, { kbId, apiKey });
229
+ console.log('Publish result:', JSON.stringify(publishResult));
230
+
231
+ return {
232
+ statusCode: 200,
233
+ headers,
234
+ body: JSON.stringify({
235
+ message: msgData,
236
+ debug: {
237
+ recipientChannel: recipientChannel.substring(0, 16) + '...',
238
+ publishResult
239
+ }
240
+ })
241
+ };
242
+ }
243
+
244
+ // Get chat history with a specific user
245
+ if (action === 'getMessages') {
246
+ const { userId, withUserId } = body;
247
+
248
+ const result = await db.query(
249
+ `SELECT id, from_user_id, from_user_name, to_user_id, content, created_at
250
+ FROM messages
251
+ WHERE (from_user_id = $1 AND to_user_id = $2) OR (from_user_id = $2 AND to_user_id = $1)
252
+ ORDER BY created_at ASC
253
+ LIMIT 100`,
254
+ [userId, withUserId]
255
+ );
256
+
257
+ return {
258
+ statusCode: 200,
259
+ headers,
260
+ body: JSON.stringify({
261
+ messages: result.rows.map(m => ({
262
+ id: m.id,
263
+ fromUserId: m.from_user_id,
264
+ fromUserName: m.from_user_name,
265
+ toUserId: m.to_user_id,
266
+ content: m.content,
267
+ createdAt: m.created_at
268
+ }))
269
+ })
270
+ };
271
+ }
272
+
273
+ return {
274
+ statusCode: 400,
275
+ headers,
276
+ body: JSON.stringify({ error: 'Invalid action' })
277
+ };
278
+
279
+ } catch (error) {
280
+ console.error('Posts error:', error);
281
+ return {
282
+ statusCode: 500,
283
+ headers,
284
+ body: JSON.stringify({ error: error.message })
285
+ };
286
+ }
287
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "posts",
3
+ "type": "module",
4
+ "dependencies": {
5
+ "@aws-sdk/client-s3": "^3.700.0",
6
+ "@aws-sdk/s3-request-presigner": "^3.700.0",
7
+ "openkbs-pulse": "^1.0.17",
8
+ "pg": "^8.11.3"
9
+ }
10
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "kbId": "abcdef123456",
3
+ "region": "us-east-1"
4
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "nodejs-demo",
3
+ "region": "us-east-1",
4
+
5
+ "elastic": {
6
+ "pulse": true,
7
+ "postgres": true,
8
+ "storage": {
9
+ "cloudfront": "media"
10
+ }
11
+ },
12
+
13
+ "functions": ["auth", "posts"],
14
+
15
+ "site": "./site"
16
+ }