@dropout-ai/runtime 0.3.4 → 0.3.5
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/package.json +1 -1
- package/src/index.js +12 -27
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -20,6 +20,14 @@ const KNOWN_AI_DOMAINS = [
|
|
|
20
20
|
|
|
21
21
|
class Dropout {
|
|
22
22
|
constructor(config = {}) {
|
|
23
|
+
// 🚨 BROWSER GUARD 🚨
|
|
24
|
+
// If this runs in the browser (Client Component), do nothing.
|
|
25
|
+
// This prevents the "listener" error and protects your API keys.
|
|
26
|
+
if (typeof window !== 'undefined') {
|
|
27
|
+
console.warn("[Dropout] ⚠️ Initialization Skipped: This SDK is for Node.js/Server environments only.");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
// 1. Validation
|
|
24
32
|
if (!config.apiKey || !config.projectId) {
|
|
25
33
|
console.warn("[Dropout] ⚠️ Initialization Skipped: Missing apiKey or projectId.");
|
|
@@ -49,7 +57,7 @@ class Dropout {
|
|
|
49
57
|
// 5. Start the Wiretap
|
|
50
58
|
if (this.debug) console.log(`[Dropout] 🟢 Initialized for Project: ${this.projectId}`);
|
|
51
59
|
|
|
52
|
-
// Bind methods
|
|
60
|
+
// Bind methods
|
|
53
61
|
this.log = this.log.bind(this);
|
|
54
62
|
this.isAiRequest = this.isAiRequest.bind(this);
|
|
55
63
|
this.patchNetwork();
|
|
@@ -101,13 +109,10 @@ class Dropout {
|
|
|
101
109
|
|
|
102
110
|
isAiRequest(url, bodyString) {
|
|
103
111
|
if (!url) return false;
|
|
104
|
-
// Guard: Infinite Loop Prevention
|
|
105
112
|
if (url.includes(this.captureEndpoint)) return false;
|
|
106
113
|
|
|
107
|
-
// 1. Check Known Domains
|
|
108
114
|
if (KNOWN_AI_DOMAINS.some(d => url.includes(d))) return true;
|
|
109
115
|
|
|
110
|
-
// 2. Heuristic Check (for custom endpoints)
|
|
111
116
|
if (bodyString) {
|
|
112
117
|
return (bodyString.includes('"model"') || bodyString.includes('"messages"')) &&
|
|
113
118
|
(bodyString.includes('"user"') || bodyString.includes('"prompt"'));
|
|
@@ -117,22 +122,18 @@ class Dropout {
|
|
|
117
122
|
|
|
118
123
|
// --- EMITTER ---
|
|
119
124
|
emit(payload) {
|
|
120
|
-
// 1. Guard
|
|
121
125
|
if (!this.apiKey || !this.projectId) return;
|
|
122
126
|
|
|
123
|
-
// 2. Construct Payload
|
|
124
127
|
const finalPayload = {
|
|
125
128
|
...payload,
|
|
126
129
|
project_id: this.projectId,
|
|
127
|
-
content_blob: payload.content,
|
|
128
|
-
content: payload.content,
|
|
130
|
+
//content_blob: payload.content,
|
|
131
|
+
content: payload.content,
|
|
129
132
|
received_at: new Date().toISOString()
|
|
130
133
|
};
|
|
131
134
|
|
|
132
135
|
this.log(`🚀 Sending Capture (${payload.turn_role})`);
|
|
133
136
|
|
|
134
|
-
// 3. Fire and Forget using HTTPS (Bypassing our own patches via pure request)
|
|
135
|
-
// We use a clean request to avoid triggering our own hooks if we used global.fetch
|
|
136
137
|
const req = https.request(this.captureEndpoint, {
|
|
137
138
|
method: 'POST',
|
|
138
139
|
headers: {
|
|
@@ -148,7 +149,7 @@ class Dropout {
|
|
|
148
149
|
|
|
149
150
|
// --- CORE PATCHING ---
|
|
150
151
|
patchNetwork() {
|
|
151
|
-
const _this = this;
|
|
152
|
+
const _this = this;
|
|
152
153
|
|
|
153
154
|
// --- A. PATCH FETCH ---
|
|
154
155
|
if (global.fetch) {
|
|
@@ -156,13 +157,11 @@ class Dropout {
|
|
|
156
157
|
global.fetch = async function (input, init) {
|
|
157
158
|
const url = typeof input === 'string' ? input : input?.url;
|
|
158
159
|
|
|
159
|
-
// Safe Body Check
|
|
160
160
|
let bodyStr = "";
|
|
161
161
|
if (init && init.body) {
|
|
162
162
|
try { bodyStr = typeof init.body === 'string' ? init.body : JSON.stringify(init.body); } catch (e) { }
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
// Guard
|
|
166
165
|
if (!_this.isAiRequest(url, bodyStr)) {
|
|
167
166
|
return originalFetch.apply(this, arguments);
|
|
168
167
|
}
|
|
@@ -170,7 +169,6 @@ class Dropout {
|
|
|
170
169
|
_this.log(`⚡ [FETCH] Intercepting: ${url}`);
|
|
171
170
|
const start = Date.now();
|
|
172
171
|
|
|
173
|
-
// Emit Request
|
|
174
172
|
const { provider, model } = _this.normalize(url, bodyStr);
|
|
175
173
|
_this.emit({
|
|
176
174
|
session_id: global.__dropout_session_id__,
|
|
@@ -182,7 +180,6 @@ class Dropout {
|
|
|
182
180
|
metadata_flags: { method: 'FETCH', url: url }
|
|
183
181
|
});
|
|
184
182
|
|
|
185
|
-
// Execute
|
|
186
183
|
let response;
|
|
187
184
|
try {
|
|
188
185
|
response = await originalFetch.apply(this, arguments);
|
|
@@ -190,7 +187,6 @@ class Dropout {
|
|
|
190
187
|
|
|
191
188
|
const latency = Date.now() - start;
|
|
192
189
|
|
|
193
|
-
// Emit Response
|
|
194
190
|
try {
|
|
195
191
|
const cloned = response.clone();
|
|
196
192
|
const oText = await cloned.text();
|
|
@@ -216,7 +212,6 @@ class Dropout {
|
|
|
216
212
|
const originalRequest = module.request;
|
|
217
213
|
module.request = function (...args) {
|
|
218
214
|
let url;
|
|
219
|
-
// Argument Resolution (url, options, callback) or (options, callback)
|
|
220
215
|
if (typeof args[0] === 'string') {
|
|
221
216
|
url = args[0];
|
|
222
217
|
} else {
|
|
@@ -235,35 +230,26 @@ class Dropout {
|
|
|
235
230
|
const start = Date.now();
|
|
236
231
|
|
|
237
232
|
const clientRequest = originalRequest.apply(this, args);
|
|
238
|
-
|
|
239
|
-
// Capture Buffers
|
|
240
233
|
const reqChunks = [];
|
|
241
234
|
const originalWrite = clientRequest.write;
|
|
242
235
|
const originalEnd = clientRequest.end;
|
|
243
236
|
|
|
244
|
-
// SAFE WRITE PATCH
|
|
245
237
|
clientRequest.write = function (...writeArgs) {
|
|
246
238
|
const chunk = writeArgs[0];
|
|
247
|
-
// Only buffer if it's data (String or Buffer), ignore objects/callbacks
|
|
248
239
|
if (chunk && (typeof chunk === 'string' || Buffer.isBuffer(chunk))) {
|
|
249
240
|
reqChunks.push(Buffer.from(chunk));
|
|
250
241
|
}
|
|
251
242
|
return originalWrite.apply(this, writeArgs);
|
|
252
243
|
};
|
|
253
244
|
|
|
254
|
-
// SAFE END PATCH
|
|
255
245
|
clientRequest.end = function (...endArgs) {
|
|
256
246
|
const chunk = endArgs[0];
|
|
257
|
-
// Only buffer if it's data
|
|
258
247
|
if (chunk && (typeof chunk === 'string' || Buffer.isBuffer(chunk))) {
|
|
259
248
|
reqChunks.push(Buffer.from(chunk));
|
|
260
249
|
}
|
|
261
250
|
|
|
262
|
-
// Emit Request
|
|
263
251
|
const reqBody = Buffer.concat(reqChunks).toString('utf8');
|
|
264
252
|
const { provider, model } = _this.normalize(url, reqBody);
|
|
265
|
-
|
|
266
|
-
// Attach state to request for response handler
|
|
267
253
|
clientRequest._dropout_meta = { provider, model };
|
|
268
254
|
|
|
269
255
|
_this.emit({
|
|
@@ -279,7 +265,6 @@ class Dropout {
|
|
|
279
265
|
return originalEnd.apply(this, endArgs);
|
|
280
266
|
};
|
|
281
267
|
|
|
282
|
-
// Capture Response
|
|
283
268
|
clientRequest.on('response', (res) => {
|
|
284
269
|
const resChunks = [];
|
|
285
270
|
res.on('data', (chunk) => resChunks.push(chunk));
|