openkbs 0.0.65 → 0.0.67

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 (47) hide show
  1. package/README.md +0 -2
  2. package/package.json +1 -1
  3. package/src/actions.js +93 -85
  4. package/src/index.js +16 -15
  5. package/templates/.claude/skills/openkbs/SKILL.md +184 -0
  6. package/templates/.claude/skills/openkbs/metadata.json +1 -0
  7. package/templates/.claude/skills/openkbs/reference/backend-sdk.md +428 -0
  8. package/templates/.claude/skills/openkbs/reference/commands.md +370 -0
  9. package/templates/.claude/skills/openkbs/reference/elastic-services.md +327 -0
  10. package/templates/.claude/skills/openkbs/reference/frontend-sdk.md +299 -0
  11. package/templates/platform/README.md +70 -0
  12. package/templates/platform/agents/assistant/app/instructions.txt +25 -0
  13. package/templates/platform/agents/assistant/app/settings.json +17 -0
  14. package/templates/platform/agents/assistant/src/Events/actions.js +81 -0
  15. package/templates/platform/agents/assistant/src/Events/onResponse.js +41 -0
  16. package/templates/platform/agents/assistant/src/Events/onResponse.json +3 -0
  17. package/templates/platform/agents/assistant/src/Frontend/contentRender.js +26 -0
  18. package/templates/platform/agents/assistant/src/Frontend/contentRender.json +10 -0
  19. package/templates/platform/functions/api/index.mjs +69 -0
  20. package/templates/platform/openkbs.json +14 -0
  21. package/templates/platform/site/index.html +76 -0
  22. package/version.json +3 -3
  23. package/templates/.openkbs/knowledge/metadata.json +0 -3
  24. package/templates/CLAUDE.md +0 -655
  25. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/app/icon.png +0 -0
  26. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/app/instructions.txt +0 -0
  27. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/app/settings.json +0 -0
  28. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/scripts/run_job.js +0 -0
  29. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/scripts/utils/agent_client.js +0 -0
  30. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/src/Events/actions.js +0 -0
  31. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/src/Events/handler.js +0 -0
  32. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/src/Events/onRequest.js +0 -0
  33. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/src/Events/onRequest.json +0 -0
  34. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/src/Events/onResponse.js +0 -0
  35. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/src/Events/onResponse.json +0 -0
  36. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/src/Frontend/contentRender.js +0 -0
  37. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-copywriter-agent/src/Frontend/contentRender.json +0 -0
  38. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-marketing-agent/README.md +0 -0
  39. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-marketing-agent/app/instructions.txt +0 -0
  40. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-marketing-agent/app/settings.json +0 -0
  41. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-marketing-agent/src/Events/actions.js +0 -0
  42. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-marketing-agent/src/Events/onRequest.js +0 -0
  43. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-marketing-agent/src/Events/onRequest.json +0 -0
  44. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-marketing-agent/src/Events/onResponse.js +0 -0
  45. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-marketing-agent/src/Events/onResponse.json +0 -0
  46. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-marketing-agent/src/Frontend/contentRender.js +0 -0
  47. /package/templates/{.openkbs/knowledge → .claude/skills/openkbs}/examples/ai-marketing-agent/src/Frontend/contentRender.json +0 -0
@@ -0,0 +1,299 @@
1
+ # Frontend SDK Reference
2
+
3
+ OpenKBS frontend is a React 18 application. Customize through `src/Frontend/contentRender.js`.
4
+
5
+ ## Built-in Libraries (No Install Needed)
6
+
7
+ These libraries are provided by OpenKBS and marked with `(fixed)` in `contentRender.json`:
8
+
9
+ - React 18 (`react`, `react-dom`)
10
+ - Material-UI (`@mui/material`, `@mui/icons-material`)
11
+ - Emotion (`@emotion/react`, `@emotion/styled`)
12
+
13
+ ## contentRender.js Exports
14
+
15
+ | Export | Purpose |
16
+ |--------|---------|
17
+ | `onRenderChatMessage` | Custom message rendering |
18
+ | `Header` | Custom header component |
19
+
20
+ ## onRenderChatMessage
21
+
22
+ Receives each message and returns custom rendering:
23
+
24
+ ```javascript
25
+ const onRenderChatMessage = async (params) => {
26
+ const { content, role } = params.messages[params.msgIndex];
27
+ const { msgIndex, messages, markdownHandler } = params;
28
+
29
+ // Return null for default markdown rendering
30
+ // Return React component for custom rendering
31
+ // Return JSON.stringify({ type: 'HIDDEN_MESSAGE' }) to hide
32
+
33
+ return null;
34
+ };
35
+ ```
36
+
37
+ ### Parameters
38
+
39
+ - `params.messages` - All messages in conversation
40
+ - `params.msgIndex` - Current message index
41
+ - `params.markdownHandler` - Function to render markdown
42
+ - `params.setSystemAlert({ severity, message })` - Show alerts (success, error, warning, info)
43
+ - `params.setBlockingLoading(bool)` - Show loading overlay
44
+ - `params.RequestChatAPI(messages)` - Send messages to LLM
45
+ - `params.kbUserData()` - Get user info
46
+ - `params.generateMsgId()` - Generate unique message ID
47
+ - `params.theme` - Current MUI theme
48
+
49
+ ### Return Values
50
+
51
+ ```javascript
52
+ // Default rendering
53
+ return null;
54
+
55
+ // Custom component
56
+ return <div>Custom content</div>;
57
+
58
+ // Hide message
59
+ return JSON.stringify({ type: 'HIDDEN_MESSAGE' });
60
+ ```
61
+
62
+ ## Header Component
63
+
64
+ Customize the chat header:
65
+
66
+ ```javascript
67
+ const Header = ({ setRenderSettings }) => {
68
+ useEffect(() => {
69
+ setRenderSettings({
70
+ disableShareButton: true,
71
+ disableBalanceView: true
72
+ });
73
+ }, [setRenderSettings]);
74
+
75
+ return <div>Custom Header</div>;
76
+ };
77
+ ```
78
+
79
+ ## Command Rendering Pattern
80
+
81
+ Define commands with icons:
82
+
83
+ ```javascript
84
+ // commands.js
85
+ import SearchIcon from '@mui/icons-material/Search';
86
+ import ImageIcon from '@mui/icons-material/Image';
87
+
88
+ export const COMMANDS = {
89
+ googleSearch: { icon: SearchIcon },
90
+ createAIImage: { icon: ImageIcon },
91
+ cleanupMemory: { icon: ClearIcon, selfClosing: true }
92
+ };
93
+
94
+ // Generate regex patterns
95
+ export const COMMAND_PATTERNS = Object.entries(COMMANDS).map(([name, config]) => {
96
+ if (config.selfClosing) {
97
+ return new RegExp(`<${name}\\s*\\/>`);
98
+ }
99
+ return new RegExp(`<${name}>[\\s\\S]*?<\\/${name}>`);
100
+ });
101
+ ```
102
+
103
+ ## Command Circle Component
104
+
105
+ Render commands as interactive icons:
106
+
107
+ ```javascript
108
+ const CommandCircle = ({ command, response }) => {
109
+ const IconComponent = COMMANDS[command.name]?.icon || BoltIcon;
110
+ const isSuccess = response && !response.error;
111
+
112
+ return (
113
+ <Tooltip title={getTooltipContent()}>
114
+ <Box sx={{
115
+ width: 36, height: 36,
116
+ borderRadius: '50%',
117
+ backgroundColor: isSuccess ? 'rgba(76, 175, 80, 0.08)' : 'rgba(0, 0, 0, 0.04)',
118
+ border: '2px solid',
119
+ borderColor: isSuccess ? 'rgba(76, 175, 80, 0.3)' : 'rgba(0, 0, 0, 0.12)',
120
+ display: 'flex',
121
+ alignItems: 'center',
122
+ justifyContent: 'center'
123
+ }}>
124
+ <IconComponent sx={{ fontSize: 18 }} />
125
+ </Box>
126
+ </Tooltip>
127
+ );
128
+ };
129
+ ```
130
+
131
+ ## Image Display with Download
132
+
133
+ ```javascript
134
+ const ImageWithDownload = ({ imageUrl }) => {
135
+ const [isLoading, setIsLoading] = useState(true);
136
+
137
+ const handleDownload = async () => {
138
+ const link = document.createElement('a');
139
+ link.download = 'image.png';
140
+
141
+ const response = await fetch(imageUrl);
142
+ const blob = await response.blob();
143
+ link.href = URL.createObjectURL(blob);
144
+ link.click();
145
+ };
146
+
147
+ return (
148
+ <div>
149
+ <img
150
+ src={imageUrl}
151
+ onLoad={() => setIsLoading(false)}
152
+ style={{ maxWidth: '100%', borderRadius: 8 }}
153
+ />
154
+ {!isLoading && (
155
+ <button onClick={handleDownload}>
156
+ <DownloadIcon /> Download
157
+ </button>
158
+ )}
159
+ </div>
160
+ );
161
+ };
162
+ ```
163
+
164
+ ## Parsing JSON Results
165
+
166
+ ```javascript
167
+ const onRenderChatMessage = async (params) => {
168
+ const { content } = params.messages[params.msgIndex];
169
+
170
+ let JSONData;
171
+ try { JSONData = JSON.parse(content); } catch (e) {}
172
+
173
+ // Handle CHAT_IMAGE result
174
+ if (JSONData?.type === 'CHAT_IMAGE' && JSONData?.data?.imageUrl) {
175
+ return <ImageWithDownload imageUrl={JSONData.data.imageUrl} />;
176
+ }
177
+
178
+ // Hide CONTINUE messages
179
+ if (JSONData?.type === 'CONTINUE') {
180
+ return JSON.stringify({ type: 'HIDDEN_MESSAGE' });
181
+ }
182
+
183
+ return null;
184
+ };
185
+ ```
186
+
187
+ ## contentRender.json
188
+
189
+ Declare dependencies:
190
+
191
+ ```json
192
+ {
193
+ "dependencies": {
194
+ "react": "^18.2.0 (fixed)",
195
+ "react-dom": "^18.2.0 (fixed)",
196
+ "@mui/material": "^5.16.1 (fixed)",
197
+ "@mui/icons-material": "^5.16.1 (fixed)",
198
+ "@emotion/react": "^11.10.6 (fixed)",
199
+ "@emotion/styled": "^11.10.6 (fixed)"
200
+ }
201
+ }
202
+ ```
203
+
204
+ The `(fixed)` suffix indicates built-in libraries that don't need bundling.
205
+
206
+ ## Frontend openkbs Object
207
+
208
+ The `openkbs` object is available globally in frontend code.
209
+
210
+ ### Item CRUD
211
+
212
+ ```javascript
213
+ // Create item
214
+ await openkbs.createItem({
215
+ itemType: 'memory',
216
+ itemId: 'memory_key',
217
+ body: { value: 'data' }
218
+ });
219
+
220
+ // Update item
221
+ await openkbs.updateItem({
222
+ itemType: 'memory',
223
+ itemId: 'memory_key',
224
+ body: { value: 'updated' }
225
+ });
226
+
227
+ // Get single item
228
+ const item = await openkbs.getItem('memory_key');
229
+ console.log(item.item.body.value);
230
+
231
+ // Fetch multiple items
232
+ const items = await openkbs.fetchItems({
233
+ itemType: 'memory',
234
+ beginsWith: 'memory_',
235
+ limit: 100
236
+ });
237
+
238
+ // Delete item
239
+ await openkbs.deleteItem('memory_key');
240
+ ```
241
+
242
+ ### Files API
243
+
244
+ ```javascript
245
+ // List files
246
+ const files = await openkbs.Files.listFiles('files');
247
+ // Returns: [{ Key, Size, LastModified }, ...]
248
+
249
+ // Upload with progress
250
+ const onProgress = (percent) => console.log(`${percent}%`);
251
+ await openkbs.Files.uploadFileAPI(fileObject, 'files', onProgress);
252
+
253
+ // Delete file
254
+ await openkbs.Files.deleteRawKBFile('filename.jpg', 'files');
255
+
256
+ // Rename file
257
+ await openkbs.Files.renameFile('old.jpg', 'new.jpg', 'files');
258
+ ```
259
+
260
+ ### Sharing API
261
+
262
+ ```javascript
263
+ // Share KB
264
+ await openkbs.KBAPI.shareKBWith('user@example.com');
265
+
266
+ // Get shares
267
+ const shares = await openkbs.KBAPI.getKBShares();
268
+ // Returns: { sharedWith: ['email1', 'email2'] }
269
+
270
+ // Remove share
271
+ await openkbs.KBAPI.unshareKBWith('user@example.com');
272
+ ```
273
+
274
+ ### Properties
275
+
276
+ ```javascript
277
+ openkbs.kbId // Current KB ID
278
+ openkbs.isMobile // Boolean - is mobile device
279
+ openkbs.KBData // KB metadata
280
+ ```
281
+
282
+ ## Debug Mode
283
+
284
+ Add `?debug=1` to URL to see raw messages:
285
+
286
+ ```
287
+ https://YOUR_KB_ID.apps.openkbs.com?debug=1
288
+ ```
289
+
290
+ ## Mobile Detection
291
+
292
+ ```javascript
293
+ const isMobile = window.innerWidth < 960;
294
+ // Or use: openkbs.isMobile
295
+ ```
296
+
297
+ ## Complete Example
298
+
299
+ See [examples/ai-marketing-agent/src/Frontend/](../examples/ai-marketing-agent/src/Frontend/) for a full implementation.
@@ -0,0 +1,70 @@
1
+ # {{APP_NAME}} Platform
2
+
3
+ OpenKBS full-stack platform with AI agents, serverless functions, and static site.
4
+
5
+ ## Structure
6
+
7
+ ```
8
+ {{APP_NAME}}/
9
+ ├── agents/ # AI agents
10
+ │ └── assistant/ # Sample assistant agent
11
+ │ ├── app/
12
+ │ │ ├── settings.json
13
+ │ │ └── instructions.txt
14
+ │ └── src/
15
+ │ ├── Events/
16
+ │ └── Frontend/
17
+ ├── functions/ # Serverless Lambda functions
18
+ │ └── api/
19
+ │ └── index.mjs # Sample API endpoint
20
+ ├── site/ # Static site for whitelabel
21
+ │ └── index.html
22
+ ├── openkbs.json # Elastic services configuration
23
+ └── README.md
24
+ ```
25
+
26
+ ## Deploy
27
+
28
+ ```bash
29
+ # Deploy elastic services (postgres, storage, pulse)
30
+ openkbs deploy
31
+
32
+ # Deploy the API function
33
+ openkbs fn push api
34
+
35
+ # Deploy static site
36
+ openkbs site push
37
+
38
+ # Deploy an agent
39
+ cd agents/assistant
40
+ openkbs push
41
+ ```
42
+
43
+ ## Elastic Services
44
+
45
+ Enabled in `openkbs.json`:
46
+
47
+ - **Postgres**: `openkbs postgres shell` to connect
48
+ - **Storage**: `openkbs storage ls` to list files
49
+ - **Pulse**: `openkbs pulse status` for WebSocket info
50
+ - **Functions**: `openkbs fn list` to see deployed functions
51
+
52
+ ## Development
53
+
54
+ ```bash
55
+ # Check stack status
56
+ openkbs stack status
57
+
58
+ # View function logs
59
+ openkbs fn logs api
60
+
61
+ # Invoke function locally
62
+ openkbs fn invoke api '{"action": "hello"}'
63
+ ```
64
+
65
+ ## URLs
66
+
67
+ After deployment:
68
+ - Site: `https://YOUR_DOMAIN/`
69
+ - API: `https://fn.openkbs.com/YOUR_KB_ID/api`
70
+ - Agent: `https://YOUR_KB_ID.apps.openkbs.com`
@@ -0,0 +1,25 @@
1
+ You are an AI assistant for {{APP_NAME}}.
2
+
3
+ ## Available Commands
4
+
5
+ <googleSearch>
6
+ {"query": "search terms"}
7
+ </googleSearch>
8
+ Search the web for information.
9
+
10
+ <setMemory>
11
+ {"itemId": "memory_key", "value": "data to remember"}
12
+ </setMemory>
13
+ Save data to memory. The itemId must start with "memory_".
14
+
15
+ <deleteItem>
16
+ {"itemId": "memory_key"}
17
+ </deleteItem>
18
+ Delete a memory item.
19
+
20
+ ## Guidelines
21
+
22
+ 1. Be helpful and concise
23
+ 2. Use commands when needed to assist the user
24
+ 3. Remember important information using setMemory
25
+ 4. Search the web when you need current information
@@ -0,0 +1,17 @@
1
+ {
2
+ "kbTitle": "{{APP_NAME}} Assistant",
3
+ "kbDescription": "AI assistant for {{APP_NAME}} platform",
4
+ "model": "anthropic/claude-sonnet-4-20250514",
5
+ "maxTokens": 16000,
6
+ "itemTypes": {
7
+ "memory": {
8
+ "attributes": [
9
+ { "attrName": "itemId", "attrType": "itemId", "encrypted": false },
10
+ { "attrName": "body", "attrType": "body", "encrypted": true }
11
+ ]
12
+ }
13
+ },
14
+ "options": {
15
+ "priorityItems": [{ "prefix": "memory_", "limit": 50 }]
16
+ }
17
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Command actions for {{APP_NAME}} Assistant
3
+ */
4
+
5
+ export const getActions = (meta, event) => [
6
+ // Google Search
7
+ [/<googleSearch>([\s\S]*?)<\/googleSearch>/s, async (match) => {
8
+ try {
9
+ const data = JSON.parse(match[1].trim());
10
+ const results = await openkbs.googleSearch(data.query);
11
+
12
+ const formatted = results?.slice(0, 5).map(({ title, link, snippet }) => ({
13
+ title, link, snippet
14
+ }));
15
+
16
+ return {
17
+ type: 'SEARCH_RESULTS',
18
+ data: formatted,
19
+ ...meta,
20
+ _meta_actions: ["REQUEST_CHAT_MODEL"]
21
+ };
22
+ } catch (e) {
23
+ return { error: e.message, ...meta, _meta_actions: ["REQUEST_CHAT_MODEL"] };
24
+ }
25
+ }],
26
+
27
+ // Set Memory
28
+ [/<setMemory>([\s\S]*?)<\/setMemory>/s, async (match) => {
29
+ try {
30
+ const data = JSON.parse(match[1].trim());
31
+
32
+ if (!data.itemId?.startsWith('memory_')) {
33
+ return {
34
+ type: "MEMORY_ERROR",
35
+ error: "itemId must start with 'memory_'",
36
+ _meta_actions: ["REQUEST_CHAT_MODEL"]
37
+ };
38
+ }
39
+
40
+ try {
41
+ await openkbs.updateItem({
42
+ itemType: 'memory',
43
+ itemId: data.itemId,
44
+ body: { value: data.value, updatedAt: new Date().toISOString() }
45
+ });
46
+ } catch (e) {
47
+ await openkbs.createItem({
48
+ itemType: 'memory',
49
+ itemId: data.itemId,
50
+ body: { value: data.value, updatedAt: new Date().toISOString() }
51
+ });
52
+ }
53
+
54
+ return {
55
+ type: "MEMORY_SAVED",
56
+ itemId: data.itemId,
57
+ ...meta,
58
+ _meta_actions: ["REQUEST_CHAT_MODEL"]
59
+ };
60
+ } catch (e) {
61
+ return { error: e.message, ...meta, _meta_actions: ["REQUEST_CHAT_MODEL"] };
62
+ }
63
+ }],
64
+
65
+ // Delete Item
66
+ [/<deleteItem>([\s\S]*?)<\/deleteItem>/s, async (match) => {
67
+ try {
68
+ const data = JSON.parse(match[1].trim());
69
+ await openkbs.deleteItem(data.itemId);
70
+
71
+ return {
72
+ type: "ITEM_DELETED",
73
+ itemId: data.itemId,
74
+ ...meta,
75
+ _meta_actions: ["REQUEST_CHAT_MODEL"]
76
+ };
77
+ } catch (e) {
78
+ return { error: e.message, ...meta, _meta_actions: ["REQUEST_CHAT_MODEL"] };
79
+ }
80
+ }]
81
+ ];
@@ -0,0 +1,41 @@
1
+ import { getActions } from './actions.js';
2
+
3
+ export const handler = async (event) => {
4
+ const response = event?.payload?.response?.content ?? event?.payload?.response;
5
+ if (!response) return { type: 'EMPTY_RESPONSE' };
6
+
7
+ const meta = {
8
+ role: 'tool',
9
+ name: 'command_executor',
10
+ msgId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
11
+ };
12
+
13
+ const actions = getActions(meta, event);
14
+ const results = [];
15
+
16
+ for (const [pattern, handler] of actions) {
17
+ const matches = response.matchAll(new RegExp(pattern.source, pattern.flags + 'g'));
18
+ for (const match of matches) {
19
+ results.push(handler(match));
20
+ }
21
+ }
22
+
23
+ if (results.length === 0) {
24
+ return { type: 'NO_COMMANDS', _meta_actions: [] };
25
+ }
26
+
27
+ const resolved = await Promise.all(results);
28
+
29
+ if (resolved.length === 1) {
30
+ return resolved[0];
31
+ }
32
+
33
+ return {
34
+ type: 'MULTI_COMMAND_RESULT',
35
+ results: resolved,
36
+ ...meta,
37
+ _meta_actions: resolved.some(r => r._meta_actions?.includes("REQUEST_CHAT_MODEL"))
38
+ ? ["REQUEST_CHAT_MODEL"]
39
+ : []
40
+ };
41
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "dependencies": {}
3
+ }
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+
3
+ const onRenderChatMessage = async (params) => {
4
+ const { content, role } = params.messages[params.msgIndex];
5
+
6
+ // Hide tool messages
7
+ if (role === 'tool') {
8
+ return JSON.stringify({ type: 'HIDDEN_MESSAGE' });
9
+ }
10
+
11
+ return null; // Default rendering
12
+ };
13
+
14
+ const Header = ({ setRenderSettings }) => {
15
+ React.useEffect(() => {
16
+ setRenderSettings({
17
+ disableBalanceView: true
18
+ });
19
+ }, [setRenderSettings]);
20
+
21
+ return null;
22
+ };
23
+
24
+ const exports = { onRenderChatMessage, Header };
25
+ window.contentRender = exports;
26
+ export default exports;
@@ -0,0 +1,10 @@
1
+ {
2
+ "dependencies": {
3
+ "react": "^18.2.0 (fixed)",
4
+ "react-dom": "^18.2.0 (fixed)",
5
+ "@mui/material": "^5.16.1 (fixed)",
6
+ "@mui/icons-material": "^5.16.1 (fixed)",
7
+ "@emotion/react": "^11.10.6 (fixed)",
8
+ "@emotion/styled": "^11.10.6 (fixed)"
9
+ }
10
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Example API function for {{APP_NAME}} platform
3
+ *
4
+ * Endpoint: https://fn.openkbs.com/YOUR_KB_ID/api
5
+ *
6
+ * This function has access to:
7
+ * - process.env.POSTGRES_URL (if postgres enabled)
8
+ * - process.env.KB_ID (your whitelabel kbId)
9
+ */
10
+
11
+ export const handler = async (event) => {
12
+ const body = JSON.parse(event.body || '{}');
13
+ const { action, data } = body;
14
+
15
+ // CORS headers
16
+ const headers = {
17
+ 'Content-Type': 'application/json',
18
+ 'Access-Control-Allow-Origin': '*',
19
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
20
+ 'Access-Control-Allow-Headers': 'Content-Type'
21
+ };
22
+
23
+ // Handle preflight
24
+ if (event.httpMethod === 'OPTIONS') {
25
+ return { statusCode: 200, headers, body: '' };
26
+ }
27
+
28
+ try {
29
+ switch (action) {
30
+ case 'hello':
31
+ return {
32
+ statusCode: 200,
33
+ headers,
34
+ body: JSON.stringify({
35
+ message: 'Hello from {{APP_NAME}} API!',
36
+ timestamp: new Date().toISOString(),
37
+ input: data
38
+ })
39
+ };
40
+
41
+ case 'health':
42
+ return {
43
+ statusCode: 200,
44
+ headers,
45
+ body: JSON.stringify({
46
+ status: 'healthy',
47
+ kbId: process.env.KB_ID,
48
+ hasPostgres: !!process.env.POSTGRES_URL
49
+ })
50
+ };
51
+
52
+ default:
53
+ return {
54
+ statusCode: 400,
55
+ headers,
56
+ body: JSON.stringify({
57
+ error: 'Unknown action',
58
+ availableActions: ['hello', 'health']
59
+ })
60
+ };
61
+ }
62
+ } catch (error) {
63
+ return {
64
+ statusCode: 500,
65
+ headers,
66
+ body: JSON.stringify({ error: error.message })
67
+ };
68
+ }
69
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "elastic": {
3
+ "postgres": true,
4
+ "storage": true,
5
+ "pulse": true,
6
+ "functions": {
7
+ "api": {
8
+ "runtime": "nodejs22.x",
9
+ "memory": 512,
10
+ "timeout": 30
11
+ }
12
+ }
13
+ }
14
+ }