@enhearten/knowledge-base-module 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/SettingsModal.d.ts +8 -0
- package/dist/components/SettingsModal.d.ts.map +1 -0
- package/dist/components/SettingsModal.js +84 -0
- package/dist/hooks/useServiceConnection.d.ts +1 -0
- package/dist/hooks/useServiceConnection.d.ts.map +1 -1
- package/dist/hooks/useServiceConnection.js +16 -3
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/dist/services/api.d.ts +9 -3
- package/dist/services/api.d.ts.map +1 -1
- package/dist/services/api.js +26 -13
- package/package.json +3 -3
- package/src/components/SettingsModal.tsx +155 -0
- package/src/hooks/useServiceConnection.ts +15 -3
- package/src/index.ts +1 -0
- package/src/services/api.ts +29 -14
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SettingsModal.d.ts","sourceRoot":"","sources":["../../src/components/SettingsModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAMnD,UAAU,kBAAkB;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA+ItD,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Button, Input, Label, Sheet, Text, XStack, YStack, Theme, Spinner } from 'tamagui';
|
|
3
|
+
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
4
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
5
|
+
import { configure } from '../services/api';
|
|
6
|
+
export const SettingsModal = ({ open, onClose }) => {
|
|
7
|
+
const [backendUrl, setBackendUrl] = useState('');
|
|
8
|
+
const [composioKey, setComposioKey] = useState('');
|
|
9
|
+
const [googleKey, setGoogleKey] = useState('');
|
|
10
|
+
const [loading, setLoading] = useState(false);
|
|
11
|
+
const [saving, setSaving] = useState(false);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (open) {
|
|
14
|
+
loadSettings();
|
|
15
|
+
}
|
|
16
|
+
}, [open]);
|
|
17
|
+
const loadSettings = async () => {
|
|
18
|
+
setLoading(true);
|
|
19
|
+
try {
|
|
20
|
+
const storedUrl = await AsyncStorage.getItem('kb_backend_url');
|
|
21
|
+
const storedComposioKey = await AsyncStorage.getItem('kb_composio_key');
|
|
22
|
+
const storedGoogleKey = await AsyncStorage.getItem('kb_google_key');
|
|
23
|
+
if (storedUrl)
|
|
24
|
+
setBackendUrl(storedUrl);
|
|
25
|
+
if (storedComposioKey)
|
|
26
|
+
setComposioKey(storedComposioKey);
|
|
27
|
+
if (storedGoogleKey)
|
|
28
|
+
setGoogleKey(storedGoogleKey);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error('Failed to load settings', error);
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
setLoading(false);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const handleSave = async () => {
|
|
38
|
+
setSaving(true);
|
|
39
|
+
try {
|
|
40
|
+
// Remove trailing slash from URL if present
|
|
41
|
+
const cleanUrl = backendUrl.replace(/\/$/, '');
|
|
42
|
+
await AsyncStorage.setItem('kb_backend_url', cleanUrl);
|
|
43
|
+
await AsyncStorage.setItem('kb_composio_key', composioKey);
|
|
44
|
+
await AsyncStorage.setItem('kb_google_key', googleKey);
|
|
45
|
+
configure({
|
|
46
|
+
backendUrl: cleanUrl,
|
|
47
|
+
headers: {
|
|
48
|
+
'x-composio-key': composioKey,
|
|
49
|
+
'x-google-key': googleKey
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
onClose();
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error('Failed to save settings', error);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
setSaving(false);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return (React.createElement(Theme, { name: "light" },
|
|
62
|
+
React.createElement(Sheet, { modal: true, open: open, onOpenChange: (isOpen) => !isOpen && onClose(), snapPoints: [85], dismissOnSnapToBottom: true },
|
|
63
|
+
React.createElement(Sheet.Overlay, null),
|
|
64
|
+
React.createElement(Sheet.Frame, { padding: "$4", backgroundColor: "white" },
|
|
65
|
+
React.createElement(Sheet.Handle, null),
|
|
66
|
+
React.createElement(YStack, { space: "$4", paddingTop: "$4" },
|
|
67
|
+
React.createElement(XStack, { alignItems: "center", space: "$2" },
|
|
68
|
+
React.createElement(MaterialCommunityIcons, { name: "cog", size: 28, color: "#333" }),
|
|
69
|
+
React.createElement(Text, { fontSize: "$6", fontWeight: "bold" }, "Settings")),
|
|
70
|
+
React.createElement(Text, { color: "$gray11" }, "Configure your Cloudflare Worker URL and API keys."),
|
|
71
|
+
loading ? (React.createElement(Spinner, { size: "large", color: "$blue10" })) : (React.createElement(YStack, { space: "$4" },
|
|
72
|
+
React.createElement(YStack, { space: "$2" },
|
|
73
|
+
React.createElement(Label, { htmlFor: "backendUrl", fontWeight: "600" }, "Backend URL (Cloudflare Worker)"),
|
|
74
|
+
React.createElement(Input, { id: "backendUrl", placeholder: "https://knowledge-base-worker.your-subdomain.workers.dev", value: backendUrl, onChangeText: setBackendUrl, autoCapitalize: "none", autoCorrect: false })),
|
|
75
|
+
React.createElement(YStack, { space: "$2" },
|
|
76
|
+
React.createElement(Label, { htmlFor: "composioKey", fontWeight: "600" }, "Composio API Key"),
|
|
77
|
+
React.createElement(Input, { id: "composioKey", placeholder: "Enter your Composio API Key", value: composioKey, onChangeText: setComposioKey, secureTextEntry: true })),
|
|
78
|
+
React.createElement(YStack, { space: "$2" },
|
|
79
|
+
React.createElement(Label, { htmlFor: "googleKey", fontWeight: "600" }, "Google Gemini API Key"),
|
|
80
|
+
React.createElement(Input, { id: "googleKey", placeholder: "Enter your Google Gemini API Key", value: googleKey, onChangeText: setGoogleKey, secureTextEntry: true })),
|
|
81
|
+
React.createElement(XStack, { gap: "$3", justifyContent: "flex-end", marginTop: "$4" },
|
|
82
|
+
React.createElement(Button, { size: "$4", variant: "outlined", onPress: onClose, disabled: saving }, "Cancel"),
|
|
83
|
+
React.createElement(Button, { size: "$4", themeInverse: true, onPress: handleSave, disabled: saving, icon: saving ? React.createElement(Spinner, { size: "small", color: "white" }) : undefined }, saving ? 'Saving...' : 'Save Settings')))))))));
|
|
84
|
+
};
|
|
@@ -2,6 +2,7 @@ export declare const useServiceConnection: (appName: string, entityId: string, s
|
|
|
2
2
|
connected: boolean;
|
|
3
3
|
loading: boolean;
|
|
4
4
|
disconnecting: boolean;
|
|
5
|
+
error: string | null;
|
|
5
6
|
handleConnect: () => Promise<void>;
|
|
6
7
|
handleCancel: () => void;
|
|
7
8
|
handleDisconnect: () => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useServiceConnection.d.ts","sourceRoot":"","sources":["../../src/hooks/useServiceConnection.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,EAAE,cAAa,MAAM,EAAc
|
|
1
|
+
{"version":3,"file":"useServiceConnection.d.ts","sourceRoot":"","sources":["../../src/hooks/useServiceConnection.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,EAAE,cAAa,MAAM,EAAc;;;;;;;;CA4FxG,CAAC"}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { useState, useEffect, useRef } from 'react';
|
|
2
|
-
import { Linking } from 'react-native';
|
|
3
|
-
import { startConnection, getConnectionStatus, disconnectApp } from '../services/api';
|
|
2
|
+
import { Linking, Alert } from 'react-native';
|
|
3
|
+
import { startConnection, getConnectionStatus, disconnectApp, isConfigured } from '../services/api';
|
|
4
4
|
export const useServiceConnection = (appName, entityId, searchTerms = [appName]) => {
|
|
5
5
|
const [loading, setLoading] = useState(false);
|
|
6
6
|
const [disconnecting, setDisconnecting] = useState(false);
|
|
7
7
|
const [connected, setConnected] = useState(false);
|
|
8
|
+
const [error, setError] = useState(null);
|
|
8
9
|
const pollingInterval = useRef(null);
|
|
9
10
|
const checkStatus = async () => {
|
|
11
|
+
if (!isConfigured())
|
|
12
|
+
return false;
|
|
10
13
|
try {
|
|
11
14
|
const data = await getConnectionStatus(entityId);
|
|
12
15
|
const isConnected = data.connectedApps && data.connectedApps.some((a) => searchTerms.some(term => a.toLowerCase().includes(term.toLowerCase())));
|
|
@@ -28,8 +31,14 @@ export const useServiceConnection = (appName, entityId, searchTerms = [appName])
|
|
|
28
31
|
};
|
|
29
32
|
}, [entityId]);
|
|
30
33
|
const handleConnect = async () => {
|
|
34
|
+
if (!isConfigured()) {
|
|
35
|
+
setError('Configuration missing. Please set up the API URL and keys.');
|
|
36
|
+
Alert.alert('Configuration Missing', 'Please configure the backend URL and API keys in settings.');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
31
39
|
try {
|
|
32
40
|
setLoading(true);
|
|
41
|
+
setError(null);
|
|
33
42
|
const data = await startConnection(appName, entityId);
|
|
34
43
|
if (data.url)
|
|
35
44
|
await Linking.openURL(data.url);
|
|
@@ -54,6 +63,7 @@ export const useServiceConnection = (appName, entityId, searchTerms = [appName])
|
|
|
54
63
|
catch (error) {
|
|
55
64
|
console.error(error);
|
|
56
65
|
console.error(`Failed to connect ${appName}`);
|
|
66
|
+
setError(`Failed to connect ${appName}`);
|
|
57
67
|
setLoading(false);
|
|
58
68
|
}
|
|
59
69
|
};
|
|
@@ -65,6 +75,8 @@ export const useServiceConnection = (appName, entityId, searchTerms = [appName])
|
|
|
65
75
|
setLoading(false);
|
|
66
76
|
};
|
|
67
77
|
const handleDisconnect = async () => {
|
|
78
|
+
if (!isConfigured())
|
|
79
|
+
return;
|
|
68
80
|
try {
|
|
69
81
|
setDisconnecting(true);
|
|
70
82
|
await disconnectApp(appName, entityId);
|
|
@@ -72,10 +84,11 @@ export const useServiceConnection = (appName, entityId, searchTerms = [appName])
|
|
|
72
84
|
}
|
|
73
85
|
catch (error) {
|
|
74
86
|
console.error(`Failed to disconnect ${appName}:`, error);
|
|
87
|
+
setError(`Failed to disconnect ${appName}`);
|
|
75
88
|
}
|
|
76
89
|
finally {
|
|
77
90
|
setDisconnecting(false);
|
|
78
91
|
}
|
|
79
92
|
};
|
|
80
|
-
return { connected, loading, disconnecting, handleConnect, handleCancel, handleDisconnect };
|
|
93
|
+
return { connected, loading, disconnecting, error, handleConnect, handleCancel, handleDisconnect };
|
|
81
94
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -9,6 +9,7 @@ export { ConnectInstagram } from './components/ConnectInstagram';
|
|
|
9
9
|
export { ConnectLinkedIn } from './components/ConnectLinkedIn';
|
|
10
10
|
export { ConnectWhatsApp } from './components/ConnectWhatsApp';
|
|
11
11
|
export { DataUploadModal } from './components/DataUploadModal';
|
|
12
|
+
export { SettingsModal } from './components/SettingsModal';
|
|
12
13
|
export { useServiceConnection } from './hooks/useServiceConnection';
|
|
13
14
|
export { configure, startConnection, executeAction, getConnectionStatus, disconnectApp, uploadUserData, getUserDataStatus, deleteUserData } from './services/api';
|
|
14
15
|
export { searchPerson, getGiftIdeas, draftEmail, summarizeRecent } from './services/api';
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { ConnectInstagram } from './components/ConnectInstagram';
|
|
|
9
9
|
export { ConnectLinkedIn } from './components/ConnectLinkedIn';
|
|
10
10
|
export { ConnectWhatsApp } from './components/ConnectWhatsApp';
|
|
11
11
|
export { DataUploadModal } from './components/DataUploadModal';
|
|
12
|
+
export { SettingsModal } from './components/SettingsModal';
|
|
12
13
|
export { useServiceConnection } from './hooks/useServiceConnection';
|
|
13
14
|
export { configure, startConnection, executeAction, getConnectionStatus, disconnectApp, uploadUserData, getUserDataStatus, deleteUserData } from './services/api';
|
|
14
15
|
export { searchPerson, getGiftIdeas, draftEmail, summarizeRecent } from './services/api';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGpE,OAAO,EACH,SAAS,EACT,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,cAAc,EACjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACH,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,eAAe,EAClB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EACR,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACjB,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ export { ConnectInstagram } from './components/ConnectInstagram';
|
|
|
10
10
|
export { ConnectLinkedIn } from './components/ConnectLinkedIn';
|
|
11
11
|
export { ConnectWhatsApp } from './components/ConnectWhatsApp';
|
|
12
12
|
export { DataUploadModal } from './components/DataUploadModal';
|
|
13
|
+
export { SettingsModal } from './components/SettingsModal';
|
|
13
14
|
// Hooks
|
|
14
15
|
export { useServiceConnection } from './hooks/useServiceConnection';
|
|
15
16
|
// Core API functions
|
package/dist/index.mjs
CHANGED
|
@@ -10,6 +10,7 @@ export { ConnectInstagram } from './components/ConnectInstagram';
|
|
|
10
10
|
export { ConnectLinkedIn } from './components/ConnectLinkedIn';
|
|
11
11
|
export { ConnectWhatsApp } from './components/ConnectWhatsApp';
|
|
12
12
|
export { DataUploadModal } from './components/DataUploadModal';
|
|
13
|
+
export { SettingsModal } from './components/SettingsModal';
|
|
13
14
|
// Hooks
|
|
14
15
|
export { useServiceConnection } from './hooks/useServiceConnection';
|
|
15
16
|
// Core API functions
|
package/dist/services/api.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Configure the backend URL for the knowledge base API.
|
|
3
|
-
* Call this before using any other functions if your backend is not at localhost:3000
|
|
4
|
-
*
|
|
2
|
+
* Configure the backend URL and optional headers for the knowledge base API.
|
|
3
|
+
* Call this before using any other functions if your backend is not at localhost:3000
|
|
4
|
+
* or if you need to pass specific API keys.
|
|
5
|
+
* @param options - The configuration options
|
|
5
6
|
*/
|
|
6
7
|
export declare const configure: (options: {
|
|
7
8
|
backendUrl: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
8
10
|
}) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Check if the API is configured (has a backend URL set).
|
|
13
|
+
*/
|
|
14
|
+
export declare const isConfigured: () => boolean;
|
|
9
15
|
export declare const startConnection: (appName: string, entityId: string) => Promise<any>;
|
|
10
16
|
export declare const executeAction: (prompt: string, entityId: string) => Promise<any>;
|
|
11
17
|
export declare const getConnectionStatus: (entityId: string) => Promise<any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/services/api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/services/api.ts"],"names":[],"mappings":"AAaA;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,SAAS;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,SAK1F,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,eAExB,CAAC;AASF,eAAO,MAAM,eAAe,GAAU,SAAS,MAAM,EAAE,UAAU,MAAM,iBAatE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,QAAQ,MAAM,EAAE,UAAU,MAAM,iBAanE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,UAAU,MAAM,iBAYzD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,SAAS,MAAM,EAAE,UAAU,MAAM,iBAapE,CAAC;AAEF,MAAM,WAAW,cAAc;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,GACvB,aAAa,WAAW,GAAG,UAAU,EACrC,UAAU,MAAM,EAChB,MAAM,cAAc,iBAgCvB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,UAAU,MAAM,EAAE,aAAa,MAAM,iBAY5E,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,aAAa,MAAM,EAAE,UAAU,MAAM,iBAezE,CAAC;AAMF,MAAM,WAAW,mBAAmB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAU,SAAS,mBAAmB,iBAM9D,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAU,SAAS,gBAAgB,iBAO3D,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAU,SAAS,iBAAiB,iBAM1D,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAU,SAAS,sBAAsB,iBAOpE,CAAC"}
|
package/dist/services/api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Platform } from 'react-native';
|
|
2
2
|
let _backendUrl = null;
|
|
3
|
+
let _headers = {};
|
|
3
4
|
const getBackendUrl = () => {
|
|
4
5
|
if (_backendUrl)
|
|
5
6
|
return _backendUrl;
|
|
@@ -9,20 +10,34 @@ const getBackendUrl = () => {
|
|
|
9
10
|
});
|
|
10
11
|
};
|
|
11
12
|
/**
|
|
12
|
-
* Configure the backend URL for the knowledge base API.
|
|
13
|
-
* Call this before using any other functions if your backend is not at localhost:3000
|
|
14
|
-
*
|
|
13
|
+
* Configure the backend URL and optional headers for the knowledge base API.
|
|
14
|
+
* Call this before using any other functions if your backend is not at localhost:3000
|
|
15
|
+
* or if you need to pass specific API keys.
|
|
16
|
+
* @param options - The configuration options
|
|
15
17
|
*/
|
|
16
18
|
export const configure = (options) => {
|
|
17
19
|
_backendUrl = options.backendUrl;
|
|
20
|
+
if (options.headers) {
|
|
21
|
+
_headers = { ..._headers, ...options.headers };
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Check if the API is configured (has a backend URL set).
|
|
26
|
+
*/
|
|
27
|
+
export const isConfigured = () => {
|
|
28
|
+
return _backendUrl !== null;
|
|
29
|
+
};
|
|
30
|
+
const getHeaders = () => {
|
|
31
|
+
return {
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
..._headers
|
|
34
|
+
};
|
|
18
35
|
};
|
|
19
36
|
export const startConnection = async (appName, entityId) => {
|
|
20
37
|
try {
|
|
21
38
|
const response = await fetch(`${getBackendUrl()}/start-connection`, {
|
|
22
39
|
method: 'POST',
|
|
23
|
-
headers:
|
|
24
|
-
'Content-Type': 'application/json',
|
|
25
|
-
},
|
|
40
|
+
headers: getHeaders(),
|
|
26
41
|
body: JSON.stringify({ appName, entityId }),
|
|
27
42
|
});
|
|
28
43
|
const data = await response.json();
|
|
@@ -37,9 +52,7 @@ export const executeAction = async (prompt, entityId) => {
|
|
|
37
52
|
try {
|
|
38
53
|
const response = await fetch(`${getBackendUrl()}/execute`, {
|
|
39
54
|
method: 'POST',
|
|
40
|
-
headers:
|
|
41
|
-
'Content-Type': 'application/json',
|
|
42
|
-
},
|
|
55
|
+
headers: getHeaders(),
|
|
43
56
|
body: JSON.stringify({ prompt, entityId }),
|
|
44
57
|
});
|
|
45
58
|
const data = await response.json();
|
|
@@ -52,7 +65,9 @@ export const executeAction = async (prompt, entityId) => {
|
|
|
52
65
|
};
|
|
53
66
|
export const getConnectionStatus = async (entityId) => {
|
|
54
67
|
try {
|
|
55
|
-
const response = await fetch(`${getBackendUrl()}/connection-status?entityId=${entityId}
|
|
68
|
+
const response = await fetch(`${getBackendUrl()}/connection-status?entityId=${entityId}`, {
|
|
69
|
+
headers: getHeaders()
|
|
70
|
+
});
|
|
56
71
|
if (!response.ok)
|
|
57
72
|
throw new Error('Failed to fetch status');
|
|
58
73
|
const data = await response.json();
|
|
@@ -67,9 +82,7 @@ export const disconnectApp = async (appName, entityId) => {
|
|
|
67
82
|
try {
|
|
68
83
|
const response = await fetch(`${getBackendUrl()}/disconnect`, {
|
|
69
84
|
method: 'POST',
|
|
70
|
-
headers:
|
|
71
|
-
'Content-Type': 'application/json',
|
|
72
|
-
},
|
|
85
|
+
headers: getHeaders(),
|
|
73
86
|
body: JSON.stringify({ appName, entityId }),
|
|
74
87
|
});
|
|
75
88
|
const data = await response.json();
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enhearten/knowledge-base-module",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Connect accounts (Gmail, GitHub, Discord, etc.) and search across them with AI to execute actions",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
10
11
|
"import": "./dist/index.mjs",
|
|
11
|
-
"require": "./dist/index.js"
|
|
12
|
-
"types": "./dist/index.d.ts"
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
13
|
},
|
|
14
14
|
"./backend": {
|
|
15
15
|
"require": "./backend/app.js"
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Button, Card, Input, Label, Sheet, Text, XStack, YStack, Theme, Spinner } from 'tamagui';
|
|
3
|
+
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
4
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
5
|
+
import { configure } from '../services/api';
|
|
6
|
+
|
|
7
|
+
interface SettingsModalProps {
|
|
8
|
+
open: boolean;
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const SettingsModal: React.FC<SettingsModalProps> = ({ open, onClose }) => {
|
|
13
|
+
const [backendUrl, setBackendUrl] = useState('');
|
|
14
|
+
const [composioKey, setComposioKey] = useState('');
|
|
15
|
+
const [googleKey, setGoogleKey] = useState('');
|
|
16
|
+
const [loading, setLoading] = useState(false);
|
|
17
|
+
const [saving, setSaving] = useState(false);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (open) {
|
|
21
|
+
loadSettings();
|
|
22
|
+
}
|
|
23
|
+
}, [open]);
|
|
24
|
+
|
|
25
|
+
const loadSettings = async () => {
|
|
26
|
+
setLoading(true);
|
|
27
|
+
try {
|
|
28
|
+
const storedUrl = await AsyncStorage.getItem('kb_backend_url');
|
|
29
|
+
const storedComposioKey = await AsyncStorage.getItem('kb_composio_key');
|
|
30
|
+
const storedGoogleKey = await AsyncStorage.getItem('kb_google_key');
|
|
31
|
+
|
|
32
|
+
if (storedUrl) setBackendUrl(storedUrl);
|
|
33
|
+
if (storedComposioKey) setComposioKey(storedComposioKey);
|
|
34
|
+
if (storedGoogleKey) setGoogleKey(storedGoogleKey);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('Failed to load settings', error);
|
|
37
|
+
} finally {
|
|
38
|
+
setLoading(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const handleSave = async () => {
|
|
43
|
+
setSaving(true);
|
|
44
|
+
try {
|
|
45
|
+
// Remove trailing slash from URL if present
|
|
46
|
+
const cleanUrl = backendUrl.replace(/\/$/, '');
|
|
47
|
+
|
|
48
|
+
await AsyncStorage.setItem('kb_backend_url', cleanUrl);
|
|
49
|
+
await AsyncStorage.setItem('kb_composio_key', composioKey);
|
|
50
|
+
await AsyncStorage.setItem('kb_google_key', googleKey);
|
|
51
|
+
|
|
52
|
+
configure({
|
|
53
|
+
backendUrl: cleanUrl,
|
|
54
|
+
headers: {
|
|
55
|
+
'x-composio-key': composioKey,
|
|
56
|
+
'x-google-key': googleKey
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
onClose();
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error('Failed to save settings', error);
|
|
63
|
+
} finally {
|
|
64
|
+
setSaving(false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<Theme name="light">
|
|
70
|
+
<Sheet
|
|
71
|
+
modal
|
|
72
|
+
open={open}
|
|
73
|
+
onOpenChange={(isOpen: boolean) => !isOpen && onClose()}
|
|
74
|
+
snapPoints={[85]}
|
|
75
|
+
dismissOnSnapToBottom
|
|
76
|
+
>
|
|
77
|
+
<Sheet.Overlay />
|
|
78
|
+
<Sheet.Frame padding="$4" backgroundColor="white">
|
|
79
|
+
<Sheet.Handle />
|
|
80
|
+
|
|
81
|
+
<YStack space="$4" paddingTop="$4">
|
|
82
|
+
<XStack alignItems="center" space="$2">
|
|
83
|
+
<MaterialCommunityIcons name="cog" size={28} color="#333" />
|
|
84
|
+
<Text fontSize="$6" fontWeight="bold">Settings</Text>
|
|
85
|
+
</XStack>
|
|
86
|
+
|
|
87
|
+
<Text color="$gray11">
|
|
88
|
+
Configure your Cloudflare Worker URL and API keys.
|
|
89
|
+
</Text>
|
|
90
|
+
|
|
91
|
+
{loading ? (
|
|
92
|
+
<Spinner size="large" color="$blue10" />
|
|
93
|
+
) : (
|
|
94
|
+
<YStack space="$4">
|
|
95
|
+
<YStack space="$2">
|
|
96
|
+
<Label htmlFor="backendUrl" fontWeight="600">Backend URL (Cloudflare Worker)</Label>
|
|
97
|
+
<Input
|
|
98
|
+
id="backendUrl"
|
|
99
|
+
placeholder="https://knowledge-base-worker.your-subdomain.workers.dev"
|
|
100
|
+
value={backendUrl}
|
|
101
|
+
onChangeText={setBackendUrl}
|
|
102
|
+
autoCapitalize="none"
|
|
103
|
+
autoCorrect={false}
|
|
104
|
+
/>
|
|
105
|
+
</YStack>
|
|
106
|
+
|
|
107
|
+
<YStack space="$2">
|
|
108
|
+
<Label htmlFor="composioKey" fontWeight="600">Composio API Key</Label>
|
|
109
|
+
<Input
|
|
110
|
+
id="composioKey"
|
|
111
|
+
placeholder="Enter your Composio API Key"
|
|
112
|
+
value={composioKey}
|
|
113
|
+
onChangeText={setComposioKey}
|
|
114
|
+
secureTextEntry
|
|
115
|
+
/>
|
|
116
|
+
</YStack>
|
|
117
|
+
|
|
118
|
+
<YStack space="$2">
|
|
119
|
+
<Label htmlFor="googleKey" fontWeight="600">Google Gemini API Key</Label>
|
|
120
|
+
<Input
|
|
121
|
+
id="googleKey"
|
|
122
|
+
placeholder="Enter your Google Gemini API Key"
|
|
123
|
+
value={googleKey}
|
|
124
|
+
onChangeText={setGoogleKey}
|
|
125
|
+
secureTextEntry
|
|
126
|
+
/>
|
|
127
|
+
</YStack>
|
|
128
|
+
|
|
129
|
+
<XStack gap="$3" justifyContent="flex-end" marginTop="$4">
|
|
130
|
+
<Button
|
|
131
|
+
size="$4"
|
|
132
|
+
variant="outlined"
|
|
133
|
+
onPress={onClose}
|
|
134
|
+
disabled={saving}
|
|
135
|
+
>
|
|
136
|
+
Cancel
|
|
137
|
+
</Button>
|
|
138
|
+
<Button
|
|
139
|
+
size="$4"
|
|
140
|
+
themeInverse
|
|
141
|
+
onPress={handleSave}
|
|
142
|
+
disabled={saving}
|
|
143
|
+
icon={saving ? <Spinner size="small" color="white" /> : undefined}
|
|
144
|
+
>
|
|
145
|
+
{saving ? 'Saving...' : 'Save Settings'}
|
|
146
|
+
</Button>
|
|
147
|
+
</XStack>
|
|
148
|
+
</YStack>
|
|
149
|
+
)}
|
|
150
|
+
</YStack>
|
|
151
|
+
</Sheet.Frame>
|
|
152
|
+
</Sheet>
|
|
153
|
+
</Theme>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { useState, useEffect, useRef } from 'react';
|
|
2
|
-
import { Linking } from 'react-native';
|
|
3
|
-
import { startConnection, getConnectionStatus, disconnectApp } from '../services/api';
|
|
2
|
+
import { Linking, Alert } from 'react-native';
|
|
3
|
+
import { startConnection, getConnectionStatus, disconnectApp, isConfigured } from '../services/api';
|
|
4
4
|
|
|
5
5
|
export const useServiceConnection = (appName: string, entityId: string, searchTerms: string[] = [appName]) => {
|
|
6
6
|
const [loading, setLoading] = useState(false);
|
|
7
7
|
const [disconnecting, setDisconnecting] = useState(false);
|
|
8
8
|
const [connected, setConnected] = useState(false);
|
|
9
|
+
const [error, setError] = useState<string | null>(null);
|
|
9
10
|
const pollingInterval = useRef<NodeJS.Timeout | null>(null);
|
|
10
11
|
|
|
11
12
|
const checkStatus = async () => {
|
|
13
|
+
if (!isConfigured()) return false;
|
|
12
14
|
try {
|
|
13
15
|
const data = await getConnectionStatus(entityId);
|
|
14
16
|
const isConnected = data.connectedApps && data.connectedApps.some((a: string) =>
|
|
@@ -32,8 +34,15 @@ export const useServiceConnection = (appName: string, entityId: string, searchTe
|
|
|
32
34
|
}, [entityId]);
|
|
33
35
|
|
|
34
36
|
const handleConnect = async () => {
|
|
37
|
+
if (!isConfigured()) {
|
|
38
|
+
setError('Configuration missing. Please set up the API URL and keys.');
|
|
39
|
+
Alert.alert('Configuration Missing', 'Please configure the backend URL and API keys in settings.');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
35
43
|
try {
|
|
36
44
|
setLoading(true);
|
|
45
|
+
setError(null);
|
|
37
46
|
const data = await startConnection(appName, entityId);
|
|
38
47
|
if (data.url) await Linking.openURL(data.url);
|
|
39
48
|
|
|
@@ -57,6 +66,7 @@ export const useServiceConnection = (appName: string, entityId: string, searchTe
|
|
|
57
66
|
} catch (error) {
|
|
58
67
|
console.error(error);
|
|
59
68
|
console.error(`Failed to connect ${appName}`);
|
|
69
|
+
setError(`Failed to connect ${appName}`);
|
|
60
70
|
setLoading(false);
|
|
61
71
|
}
|
|
62
72
|
};
|
|
@@ -70,16 +80,18 @@ export const useServiceConnection = (appName: string, entityId: string, searchTe
|
|
|
70
80
|
};
|
|
71
81
|
|
|
72
82
|
const handleDisconnect = async () => {
|
|
83
|
+
if (!isConfigured()) return;
|
|
73
84
|
try {
|
|
74
85
|
setDisconnecting(true);
|
|
75
86
|
await disconnectApp(appName, entityId);
|
|
76
87
|
setConnected(false);
|
|
77
88
|
} catch (error) {
|
|
78
89
|
console.error(`Failed to disconnect ${appName}:`, error);
|
|
90
|
+
setError(`Failed to disconnect ${appName}`);
|
|
79
91
|
} finally {
|
|
80
92
|
setDisconnecting(false);
|
|
81
93
|
}
|
|
82
94
|
};
|
|
83
95
|
|
|
84
|
-
return { connected, loading, disconnecting, handleConnect, handleCancel, handleDisconnect };
|
|
96
|
+
return { connected, loading, disconnecting, error, handleConnect, handleCancel, handleDisconnect };
|
|
85
97
|
};
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { ConnectInstagram } from './components/ConnectInstagram';
|
|
|
10
10
|
export { ConnectLinkedIn } from './components/ConnectLinkedIn';
|
|
11
11
|
export { ConnectWhatsApp } from './components/ConnectWhatsApp';
|
|
12
12
|
export { DataUploadModal } from './components/DataUploadModal';
|
|
13
|
+
export { SettingsModal } from './components/SettingsModal';
|
|
13
14
|
|
|
14
15
|
// Hooks
|
|
15
16
|
export { useServiceConnection } from './hooks/useServiceConnection';
|
package/src/services/api.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Platform } from 'react-native';
|
|
2
2
|
|
|
3
3
|
let _backendUrl: string | null = null;
|
|
4
|
+
let _headers: Record<string, string> = {};
|
|
4
5
|
|
|
5
6
|
const getBackendUrl = () => {
|
|
6
7
|
if (_backendUrl) return _backendUrl;
|
|
@@ -11,21 +12,37 @@ const getBackendUrl = () => {
|
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* Configure the backend URL for the knowledge base API.
|
|
15
|
-
* Call this before using any other functions if your backend is not at localhost:3000
|
|
16
|
-
*
|
|
15
|
+
* Configure the backend URL and optional headers for the knowledge base API.
|
|
16
|
+
* Call this before using any other functions if your backend is not at localhost:3000
|
|
17
|
+
* or if you need to pass specific API keys.
|
|
18
|
+
* @param options - The configuration options
|
|
17
19
|
*/
|
|
18
|
-
export const configure = (options: { backendUrl: string }) => {
|
|
20
|
+
export const configure = (options: { backendUrl: string; headers?: Record<string, string> }) => {
|
|
19
21
|
_backendUrl = options.backendUrl;
|
|
22
|
+
if (options.headers) {
|
|
23
|
+
_headers = { ..._headers, ...options.headers };
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if the API is configured (has a backend URL set).
|
|
29
|
+
*/
|
|
30
|
+
export const isConfigured = () => {
|
|
31
|
+
return _backendUrl !== null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const getHeaders = () => {
|
|
35
|
+
return {
|
|
36
|
+
'Content-Type': 'application/json',
|
|
37
|
+
..._headers
|
|
38
|
+
};
|
|
20
39
|
};
|
|
21
40
|
|
|
22
41
|
export const startConnection = async (appName: string, entityId: string) => {
|
|
23
42
|
try {
|
|
24
43
|
const response = await fetch(`${getBackendUrl()}/start-connection`, {
|
|
25
44
|
method: 'POST',
|
|
26
|
-
headers:
|
|
27
|
-
'Content-Type': 'application/json',
|
|
28
|
-
},
|
|
45
|
+
headers: getHeaders(),
|
|
29
46
|
body: JSON.stringify({ appName, entityId }),
|
|
30
47
|
});
|
|
31
48
|
const data = await response.json();
|
|
@@ -40,9 +57,7 @@ export const executeAction = async (prompt: string, entityId: string) => {
|
|
|
40
57
|
try {
|
|
41
58
|
const response = await fetch(`${getBackendUrl()}/execute`, {
|
|
42
59
|
method: 'POST',
|
|
43
|
-
headers:
|
|
44
|
-
'Content-Type': 'application/json',
|
|
45
|
-
},
|
|
60
|
+
headers: getHeaders(),
|
|
46
61
|
body: JSON.stringify({ prompt, entityId }),
|
|
47
62
|
});
|
|
48
63
|
const data = await response.json();
|
|
@@ -55,7 +70,9 @@ export const executeAction = async (prompt: string, entityId: string) => {
|
|
|
55
70
|
|
|
56
71
|
export const getConnectionStatus = async (entityId: string) => {
|
|
57
72
|
try {
|
|
58
|
-
const response = await fetch(`${getBackendUrl()}/connection-status?entityId=${entityId}
|
|
73
|
+
const response = await fetch(`${getBackendUrl()}/connection-status?entityId=${entityId}`, {
|
|
74
|
+
headers: getHeaders()
|
|
75
|
+
});
|
|
59
76
|
if (!response.ok) throw new Error('Failed to fetch status');
|
|
60
77
|
const data = await response.json();
|
|
61
78
|
return data;
|
|
@@ -69,9 +86,7 @@ export const disconnectApp = async (appName: string, entityId: string) => {
|
|
|
69
86
|
try {
|
|
70
87
|
const response = await fetch(`${getBackendUrl()}/disconnect`, {
|
|
71
88
|
method: 'POST',
|
|
72
|
-
headers:
|
|
73
|
-
'Content-Type': 'application/json',
|
|
74
|
-
},
|
|
89
|
+
headers: getHeaders(),
|
|
75
90
|
body: JSON.stringify({ appName, entityId }),
|
|
76
91
|
});
|
|
77
92
|
const data = await response.json();
|