ask-junkie 1.0.0
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/README.md +864 -0
- package/dist/ask-junkie.css +485 -0
- package/dist/ask-junkie.esm.js +1847 -0
- package/dist/ask-junkie.esm.js.map +1 -0
- package/dist/ask-junkie.min.js +1858 -0
- package/dist/ask-junkie.min.js.map +1 -0
- package/dist/junkie-icon.png +0 -0
- package/package.json +43 -0
- package/src/ai/AIProviderFactory.js +43 -0
- package/src/ai/BaseProvider.js +68 -0
- package/src/ai/GeminiProvider.js +58 -0
- package/src/ai/GroqProvider.js +51 -0
- package/src/ai/OpenAIProvider.js +51 -0
- package/src/ai/OpenRouterProvider.js +53 -0
- package/src/analytics/FirebaseLogger.js +115 -0
- package/src/core/AskJunkie.js +585 -0
- package/src/core/EventEmitter.js +52 -0
- package/src/index.js +19 -0
- package/src/ui/ChatWidget.js +611 -0
- package/src/ui/styles.css +485 -0
- package/src/utils/DOMUtils.js +69 -0
- package/src/utils/StorageManager.js +80 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenRouter Provider
|
|
3
|
+
* Access multiple AI models through one API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseProvider } from './BaseProvider.js';
|
|
7
|
+
|
|
8
|
+
export class OpenRouterProvider extends BaseProvider {
|
|
9
|
+
getDefaultModel() {
|
|
10
|
+
return 'meta-llama/llama-3-8b-instruct';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getEndpoint() {
|
|
14
|
+
return 'https://openrouter.ai/api/v1/chat/completions';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async sendMessage(message, context, history = []) {
|
|
18
|
+
const messages = [
|
|
19
|
+
{ role: 'system', content: context },
|
|
20
|
+
...this._formatHistory(history),
|
|
21
|
+
{ role: 'user', content: message }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const response = await this._fetch(this.getEndpoint(), {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
29
|
+
'HTTP-Referer': window.location.origin,
|
|
30
|
+
'X-Title': document.title || 'Ask Junkie Chatbot'
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
model: this.model,
|
|
34
|
+
messages: messages,
|
|
35
|
+
temperature: 0.7,
|
|
36
|
+
max_tokens: 1000
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
const error = await response.json().catch(() => ({}));
|
|
42
|
+
throw new Error(error.error?.message || `OpenRouter API error: ${response.status}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const data = await response.json();
|
|
46
|
+
|
|
47
|
+
if (data.choices?.[0]?.message?.content) {
|
|
48
|
+
return data.choices[0].message.content;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
throw new Error('Invalid response from OpenRouter API');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Analytics Logger
|
|
3
|
+
* Logs chat interactions to Firebase Firestore for centralized analytics
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class FirebaseLogger {
|
|
7
|
+
static FIREBASE_API_KEY = 'AIzaSyDPDWA7s6dauaPup9DFz0hMrQvDYPtEWwo';
|
|
8
|
+
static FIREBASE_PROJECT_ID = 'ai-chatbot-fe4a2';
|
|
9
|
+
|
|
10
|
+
constructor(config = {}) {
|
|
11
|
+
this.enabled = config.enabled !== false;
|
|
12
|
+
this.siteId = config.siteId || this._getSiteId();
|
|
13
|
+
this.userId = config.userId || null; // User ID from sdkKeys lookup
|
|
14
|
+
this.registered = false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get a sanitized site ID from hostname
|
|
19
|
+
*/
|
|
20
|
+
_getSiteId() {
|
|
21
|
+
return window.location.hostname.replace(/[^a-zA-Z0-9.-]/g, '_');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get Firestore REST API endpoint
|
|
26
|
+
*/
|
|
27
|
+
_getEndpoint(path) {
|
|
28
|
+
return `https://firestore.googleapis.com/v1/projects/${FirebaseLogger.FIREBASE_PROJECT_ID}/databases/(default)/documents/${path}?key=${FirebaseLogger.FIREBASE_API_KEY}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the base path for this API key's data
|
|
33
|
+
* Uses new nested structure: users/{userId}/apiKeys/{keyId}
|
|
34
|
+
*/
|
|
35
|
+
_getBasePath() {
|
|
36
|
+
if (this.userId && this.siteId) {
|
|
37
|
+
return `users/${this.userId}/apiKeys/${this.siteId}`;
|
|
38
|
+
}
|
|
39
|
+
// Fallback to old structure for backwards compatibility
|
|
40
|
+
return `sites/${this.siteId}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Register or update site analytics info
|
|
45
|
+
* For new user-based path, we SKIP this to avoid overwriting dashboard data
|
|
46
|
+
* Only used for legacy sites/ path
|
|
47
|
+
*/
|
|
48
|
+
async registerSite() {
|
|
49
|
+
if (this.registered) return;
|
|
50
|
+
|
|
51
|
+
// Skip registration for new user-based path - dashboard already created the document
|
|
52
|
+
// Only the chats subcollection should be written to
|
|
53
|
+
if (this.userId && this.siteId) {
|
|
54
|
+
console.log('[AskJunkie] Skipping site registration for SDK-managed key');
|
|
55
|
+
this.registered = true;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Legacy path: sites/{siteId} - still needs registration
|
|
60
|
+
try {
|
|
61
|
+
const url = this._getEndpoint(this._getBasePath());
|
|
62
|
+
|
|
63
|
+
await fetch(url, {
|
|
64
|
+
method: 'PATCH',
|
|
65
|
+
headers: { 'Content-Type': 'application/json' },
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
fields: {
|
|
68
|
+
domain: { stringValue: window.location.hostname },
|
|
69
|
+
site_name: { stringValue: document.title || 'Unknown' },
|
|
70
|
+
site_url: { stringValue: window.location.origin },
|
|
71
|
+
sdk_version: { stringValue: '1.1.6' },
|
|
72
|
+
platform: { stringValue: 'JavaScript SDK' },
|
|
73
|
+
last_active: { stringValue: new Date().toISOString() }
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.registered = true;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.warn('[AskJunkie] Failed to register site:', error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Log a chat interaction
|
|
86
|
+
*/
|
|
87
|
+
async logChat(data) {
|
|
88
|
+
if (!this.enabled) return;
|
|
89
|
+
|
|
90
|
+
// Ensure site is registered first
|
|
91
|
+
await this.registerSite();
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const url = this._getEndpoint(`${this._getBasePath()}/chats`);
|
|
95
|
+
|
|
96
|
+
await fetch(url, {
|
|
97
|
+
method: 'POST',
|
|
98
|
+
headers: { 'Content-Type': 'application/json' },
|
|
99
|
+
body: JSON.stringify({
|
|
100
|
+
fields: {
|
|
101
|
+
session_id: { stringValue: data.sessionId || 'unknown' },
|
|
102
|
+
user_message: { stringValue: data.userMessage || '' },
|
|
103
|
+
ai_response: { stringValue: data.aiResponse || '' },
|
|
104
|
+
page_url: { stringValue: data.pageUrl || window.location.href },
|
|
105
|
+
timestamp: { stringValue: new Date().toISOString() },
|
|
106
|
+
user_agent: { stringValue: navigator.userAgent || '' }
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.warn('[AskJunkie] Failed to log chat:', error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|