@dropout-ai/runtime 0.3.9 → 0.3.10

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 CHANGED
@@ -1,36 +1,186 @@
1
- # @dropout-ai/runtime
1
+ # đŸ•ĩī¸ Dropout AI Runtime (`@dropout-ai/runtime`)
2
2
 
3
- The invisible Node.js sensor for AI observability.
3
+ **Universal AI Behavior Analysis & Capture for Node.js**
4
4
 
5
- ## Overview
5
+ Dropout is a lightweight, "install-and-forget" runtime that automatically captures AI interactions (prompts & responses) from your application without requiring code changes. It acts as a passive observer, patching the network layer to ensure you never miss a conversation.
6
6
 
7
- `@dropout-ai/runtime` is a zero-dependency, fire-and-forget telemetry sensor that automatically captures OpenAI API interactions from your Node.js or Next.js application.
7
+ * **🔌 Universal:** Works with OpenAI, Anthropic, Genkit, LangChain, Axios, and `fetch`.
8
+ * **đŸ›Ąī¸ Safe:** Designed for production with silent failure modes and browser guards.
9
+ * **⚡ Zero-Latency:** Uses fire-and-forget logging to ensure your app stays fast.
10
+ * **🔋 Auto-Discovery:** Automatically detects framework (Next.js, NestJS) and configures itself.
8
11
 
9
- ## Installation
12
+ ---
13
+
14
+ ## 🚀 Quick Start (Recommended)
15
+
16
+ Run our initialization wizard in your project root. It will detect your framework and automatically create the necessary configuration files.
17
+
18
+ ```bash
19
+ npx @dropout-ai/runtime init
20
+
21
+ ```
22
+
23
+ *Supports: **Next.js** (App Router), **NestJS**, **Express**, and standard **Node.js** apps.*
24
+
25
+ ---
26
+
27
+ ## đŸ› ī¸ Manual Installation
28
+
29
+ If you prefer to configure it yourself, install the package and follow the guide for your specific framework.
10
30
 
11
31
  ```bash
12
32
  npm install @dropout-ai/runtime
33
+ # or
34
+ yarn add @dropout-ai/runtime
35
+
13
36
  ```
14
37
 
15
- ## Usage
38
+ ### 1. Next.js (App Router)
39
+
40
+ Next.js uses lazy-loading, so we use `instrumentation.ts` to ensure Dropout loads immediately on server boot.
41
+
42
+ **Create/Edit:** `src/instrumentation.ts` (or `instrumentation.ts` in root)
43
+
44
+ ```typescript
45
+ export async function register() {
46
+ // Ensure we only run on the Node.js server runtime
47
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
48
+ await import('@dropout-ai/runtime');
49
+ console.log('✅ Dropout AI Monitoring Active');
50
+ }
51
+ }
52
+
53
+ ```
16
54
 
17
- Simply import the package at the very top of your application's entry point (e.g., `index.js` or `instrumentation.ts`):
55
+ ### 2. Node.js / Express / Fastify
56
+
57
+ Initialize Dropout at the **very top** of your entry file (`index.js` or `server.js`), before importing other libraries.
18
58
 
19
59
  ```javascript
20
- import "@dropout-ai/runtime";
60
+ // MUST be the first line
61
+ require('@dropout-ai/runtime');
62
+
63
+ const express = require('express');
64
+ const app = express();
65
+ // ... rest of your code
66
+
21
67
  ```
22
68
 
23
- Once imported, it automatically:
24
- 1. Patches `global.fetch`.
25
- 2. Detects OpenAI API calls.
26
- 3. Reports telemetry to your local Dropout backend (defaulting to `http://localhost:3000/capture`).
27
- 4. Fails gracefully and silently without affecting your production traffic.
69
+ ### 3. NestJS
70
+
71
+ Initialize Dropout inside your `bootstrap()` function before creating the Nest app.
72
+
73
+ **File:** `src/main.ts`
74
+
75
+ ```typescript
76
+ import { NestFactory } from '@nestjs/core';
77
+ import { AppModule } from './app.module';
78
+ import Dropout from '@dropout-ai/runtime';
79
+
80
+ async function bootstrap() {
81
+ // ✅ Initialize first - Sends "Boot Ping" immediately
82
+ new Dropout({
83
+ projectId: process.env.DROPOUT_PROJECT_ID,
84
+ apiKey: process.env.DROPOUT_API_KEY
85
+ });
86
+
87
+ const app = await NestFactory.create(AppModule);
88
+ await app.listen(3000);
89
+ }
90
+ bootstrap();
91
+
92
+ ```
93
+
94
+ ### 4. Docker / Zero-Code Injection
95
+
96
+ You can inject Dropout into any Node.js application without editing a single file by using the Node `--require` flag. This is perfect for Docker containers or PaaS deployments.
97
+
98
+ ```bash
99
+ # 1. Set credentials
100
+ export DROPOUT_PROJECT_ID="your_project_id"
101
+ export DROPOUT_API_KEY="dp_live_..."
102
+
103
+ # 2. Run your app with the runtime injected
104
+ node -r @dropout-ai/runtime dist/index.js
105
+
106
+ ```
107
+
108
+ ---
109
+
110
+ ## âš™ī¸ Configuration
111
+
112
+ You can configure Dropout via **Environment Variables** (Recommended) or by passing options to the constructor.
113
+
114
+ | Environment Variable | Config Option | Description | Required |
115
+ | --- | --- | --- | --- |
116
+ | `DROPOUT_PROJECT_ID` | `projectId` | Your Project ID from dashboard. | ✅ |
117
+ | `DROPOUT_API_KEY` | `apiKey` | Your Secret Key (`dp_live_...`). | ✅ |
118
+ | `DROPOUT_DEBUG` | `debug` | Set `true` to see logs in console and enable connectivity checks. | No |
119
+ | `DROPOUT_PRIVACY` | `privacy` | `full` (capture text) or `mask` (metadata only). | No |
120
+
121
+ **Example `.env` file:**
122
+
123
+ ```bash
124
+ DROPOUT_PROJECT_ID="HomeOS"
125
+ DROPOUT_API_KEY="dp_live_xyz123..."
126
+ DROPOUT_DEBUG="true"
127
+
128
+ ```
129
+
130
+ ---
131
+
132
+ ## 🌐 Supported Environments
133
+
134
+ | Language/Framework | Status | Integration Method |
135
+ | --- | --- | --- |
136
+ | **Node.js** (v18+) | ✅ Ready | `require('@dropout-ai/runtime')` |
137
+ | **Next.js** | ✅ Ready | `instrumentation.ts` |
138
+ | **NestJS** | ✅ Ready | `main.ts` import |
139
+ | **Python / Java / Go** | 🚧 Beta | Use REST API (see below) |
140
+
141
+ ### Non-Node.js Usage (REST API)
142
+
143
+ If you are using Python, Java, or Go, you can manually send interaction logs to our ingestion endpoint.
144
+
145
+ **Endpoint:** `POST https://hipughmjlwmwjxzyxfzs.supabase.co/functions/v1/capture-sealed`
146
+
147
+ **Headers:**
148
+
149
+ * `Content-Type: application/json`
150
+ * `x-dropout-key: YOUR_API_KEY`
151
+
152
+ **Payload:**
153
+
154
+ ```json
155
+ {
156
+ "project_id": "your_project_id",
157
+ "turn_role": "assistant", // or "user"
158
+ "content": "AI response text...",
159
+ "model": "gpt-4", // optional
160
+ "provider": "openai", // optional
161
+ "latency_ms": 1250 // optional
162
+ }
163
+
164
+ ```
165
+
166
+ ---
167
+
168
+ ## ❓ Troubleshooting
169
+
170
+ **Q: I don't see any logs in the dashboard.**
171
+
172
+ 1. Ensure `DROPOUT_DEBUG="true"` is set.
173
+ 2. Check your console for `[Dropout] đŸŸĸ Online: <ProjectID>`.
174
+ 3. If using Next.js, ensure you created `instrumentation.ts` in the correct folder (root or `src`).
175
+
176
+ **Q: Does this work with Edge Functions (Vercel Edge / Cloudflare)?**
177
+ No. This runtime relies on Node.js core modules (`http`, `https`) to capture traffic. It does not currently support Vercel Edge Runtime or Cloudflare Workers. Please ensure your API routes use the Node.js runtime.
178
+
179
+ **Q: Will this crash my app if the API is down?**
180
+ No. The SDK is built with a "Safety Fuse." If it cannot connect or encounters an error, it fails silently and your application continues running without interruption.
181
+
182
+ ---
28
183
 
29
- ## Features
30
- - **Zero Configuration**: Just import and it works.
31
- - **Invisible capture**: No code changes to your AI logic.
32
- - **Non-blocking**: Uses fire-and-forget network calls.
33
- - **Remote Config**: Supports dynamic output capping and provider filtering.
184
+ ### License
34
185
 
35
- ## License
36
- MIT
186
+ MIT
package/bin/init.js ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ const colors = {
6
+ reset: "\x1b[0m",
7
+ green: "\x1b[32m",
8
+ yellow: "\x1b[33m",
9
+ cyan: "\x1b[36m"
10
+ };
11
+
12
+ const log = (color, msg) => console.log(`${color}${msg}${colors.reset}`);
13
+
14
+ function detectFramework() {
15
+ const cwd = process.cwd();
16
+ if (fs.existsSync(path.resolve(cwd, 'package.json'))) {
17
+ const pkg = JSON.parse(fs.readFileSync(path.resolve(cwd, 'package.json'), 'utf-8'));
18
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
19
+ if (deps.next) return 'nextjs';
20
+ if (deps['@nestjs/core']) return 'nestjs';
21
+ return 'node';
22
+ }
23
+ return 'unknown';
24
+ }
25
+
26
+ function setupNextJs() {
27
+ log(colors.cyan, '🔍 Next.js project detected.');
28
+ const hasSrc = fs.existsSync(path.resolve(process.cwd(), 'src'));
29
+ const targetDir = hasSrc ? 'src' : '.';
30
+ const isTs = fs.existsSync(path.resolve(process.cwd(), 'tsconfig.json'));
31
+ const ext = isTs ? 'ts' : 'js';
32
+ const filePath = path.resolve(process.cwd(), targetDir, `instrumentation.${ext}`);
33
+
34
+ const content = `export async function register() {
35
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
36
+ await import('@dropout-ai/runtime');
37
+ console.log('[Dropout] đŸŸĸ Initialized via instrumentation hook');
38
+ }
39
+ }`;
40
+
41
+ if (fs.existsSync(filePath)) {
42
+ log(colors.yellow, `âš ī¸ File exists: ${filePath}`);
43
+ log(colors.yellow, `👉 Please add the register() hook manually.`);
44
+ } else {
45
+ fs.writeFileSync(filePath, content);
46
+ log(colors.green, `✅ Created: ${targetDir}/instrumentation.${ext}`);
47
+ }
48
+
49
+ log(colors.cyan, '\nNEXT STEP: Add .env keys (DROPOUT_PROJECT_ID, DROPOUT_API_KEY).');
50
+ }
51
+
52
+ function run() {
53
+ console.log(colors.cyan + "\nDropout AI - Init Wizard đŸ§™â€â™‚ī¸" + colors.reset);
54
+ const framework = detectFramework();
55
+
56
+ switch (framework) {
57
+ case 'nextjs': setupNextJs(); break;
58
+ case 'nestjs':
59
+ log(colors.cyan, '🔍 NestJS detected.');
60
+ log(colors.green, '👉 Initialize in src/main.ts before bootstrap().');
61
+ break;
62
+ case 'node':
63
+ log(colors.cyan, '🔍 Node/Express detected.');
64
+ log(colors.green, '👉 Add require("@dropout-ai/runtime") at the top of index.js.');
65
+ break;
66
+ default:
67
+ log(colors.yellow, 'âš ī¸ Unknown framework. Check docs for manual setup.');
68
+ }
69
+ }
70
+
71
+ run();
package/package.json CHANGED
@@ -1,16 +1,29 @@
1
1
  {
2
2
  "name": "@dropout-ai/runtime",
3
- "version": "0.3.9",
4
- "description": "Invisible Node.js runtime for capturing AI interactions.",
3
+ "version": "0.3.10",
4
+ "description": "Invisible Node.js runtime for understanding behavoiral impact of AI interactions.",
5
5
  "main": "src/index.js",
6
+ "types": "src/index.d.ts",
7
+ "bin": {
8
+ "init": "./bin/init.js"
9
+ },
6
10
  "scripts": {
7
- "test": "node test.js"
11
+ "test": "echo \"Error: no test specified\" && exit 1"
8
12
  },
9
13
  "keywords": [
10
14
  "openai",
11
15
  "telemetry",
12
16
  "runtime",
13
- "invisible"
17
+ "invisible",
18
+ "observability",
19
+ "ai",
20
+ "behavoiral analysis",
21
+ "ai forensic",
22
+ "llm",
23
+ "monitoring",
24
+ "genkit",
25
+ "nestjs",
26
+ "nextjs"
14
27
  ],
15
28
  "repository": {
16
29
  "type": "git",
@@ -18,7 +31,8 @@
18
31
  "directory": "packages/runtime"
19
32
  },
20
33
  "files": [
21
- "src"
34
+ "src",
35
+ "bin"
22
36
  ],
23
37
  "publishConfig": {
24
38
  "access": "public"
package/src/index.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ export interface DropoutConfig {
2
+ /**
3
+ * Your project ID from the Dropout Dashboard.
4
+ */
5
+ projectId: string;
6
+
7
+ /**
8
+ * Your secret API Key (starts with dp_live_...).
9
+ */
10
+ apiKey: string;
11
+
12
+ /**
13
+ * Data Privacy Level.
14
+ * 'full' - Captures prompts and responses (Default).
15
+ * 'mask' - Captures metadata only, redacts text.
16
+ */
17
+ privacy?: 'full' | 'mask';
18
+
19
+ /**
20
+ * Enable verbose logging for connection debugging.
21
+ * Default: false
22
+ */
23
+ debug?: boolean;
24
+ }
25
+
26
+ export default class Dropout {
27
+ constructor(config: DropoutConfig);
28
+
29
+ /**
30
+ * Manually log a message to the debug stream.
31
+ */
32
+ log(msg: string, ...args: any[]): void;
33
+ }
package/src/index.js CHANGED
@@ -1,14 +1,13 @@
1
1
  /**
2
2
  * @dropout-ai/runtime
3
3
  * Universal AI Interaction Capture for Node.js
4
- * Feature: Always-On Heartbeat & Auto-Discovery
4
+ * Stability: High (Browser-Safe, Silent Failures)
5
+ * Features: Auto-Discovery, Connectivity Check, Dual-Schema Support
5
6
  */
6
7
 
7
- const https = require('https');
8
- const http = require('http');
9
- const crypto = require('crypto');
8
+ let https, http, crypto;
10
9
 
11
- // --- CONFIGURATION ---
10
+ // --- DEFAULT CONFIGURATION ---
12
11
  const SUPABASE_FUNCTION_URL = "https://hipughmjlwmwjxzyxfzs.supabase.co/functions/v1/capture-sealed";
13
12
 
14
13
  // Known AI Domains
@@ -19,53 +18,73 @@ const KNOWN_AI_DOMAINS = [
19
18
 
20
19
  class Dropout {
21
20
  constructor(config = {}) {
22
- // đŸ›Ąī¸ BROWSER GUARD
21
+ // đŸ›Ąī¸ 1. BROWSER GUARD (Client-Side Protection)
23
22
  if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
24
- if (config.debug) console.warn("[Dropout] âš ī¸ Skipped: Browser detected.");
23
+ if (config.debug) console.warn("[Dropout] âš ī¸ Skipped: Browser detected. SDK is Server-Only.");
25
24
  return;
26
25
  }
27
26
 
28
- // đŸ›Ąī¸ CREDENTIAL GUARD
27
+ // đŸ›Ąī¸ 2. CREDENTIAL GUARD
29
28
  if (!config.apiKey || !config.projectId) {
30
- if (config.debug) console.warn("[Dropout] âš ī¸ Skipped: Missing Credentials.");
29
+ if (config.debug) console.warn("[Dropout] âš ī¸ Skipped: Missing API Key or Project ID.");
31
30
  return;
32
31
  }
33
32
 
34
- // Initialize
35
- this.config = config;
36
- this.projectId = config.projectId;
37
- this.apiKey = config.apiKey;
38
- this.debug = config.debug || false;
39
- this.privacy = config.privacy || 'full';
40
- this.captureEndpoint = SUPABASE_FUNCTION_URL;
41
- this.maxOutputBytes = 32768;
42
-
43
- // State
44
- this.turnIndex = 0;
45
- this.lastTurnConfirmed = true;
46
- this.lastPromptHash = null;
47
- this.lastResponseHash = null;
48
-
49
- // Singleton Guard
50
- if (global.__dropout_initialized__) return;
51
- global.__dropout_initialized__ = true;
52
-
53
- if (!global.__dropout_session_id__) {
54
- global.__dropout_session_id__ = this.generateSessionId();
33
+ // đŸ›Ąī¸ 3. DEPENDENCY CHECK
34
+ try {
35
+ https = require('https');
36
+ http = require('http');
37
+ crypto = require('crypto');
38
+ } catch (e) {
39
+ if (config.debug) console.warn("[Dropout] âš ī¸ Skipped: Node core modules missing.");
40
+ return;
55
41
  }
56
42
 
57
- if (this.debug) console.log(`[Dropout] đŸŸĸ Online: ${this.projectId}`);
43
+ // --- INITIALIZATION ---
44
+ try {
45
+ this.config = config;
46
+ this.projectId = config.projectId;
47
+ this.apiKey = config.apiKey;
48
+ this.debug = config.debug || false;
49
+ this.privacy = config.privacy || 'full';
50
+ this.captureEndpoint = SUPABASE_FUNCTION_URL;
51
+ this.maxOutputBytes = 32768;
52
+
53
+ // State
54
+ this.turnIndex = 0;
55
+ this.lastTurnConfirmed = true;
56
+ this.lastPromptHash = null;
57
+ this.lastResponseHash = null;
58
+
59
+ // Singleton Guard
60
+ if (global.__dropout_initialized__) {
61
+ if (this.debug) console.log("[Dropout] â„šī¸ Already initialized.");
62
+ return;
63
+ }
64
+ global.__dropout_initialized__ = true;
65
+
66
+ // Identity
67
+ if (!global.__dropout_session_id__) {
68
+ global.__dropout_session_id__ = this.generateSessionId();
69
+ }
70
+
71
+ if (this.debug) console.log(`[Dropout] đŸŸĸ Online: ${this.projectId}`);
58
72
 
59
- // Bindings
60
- this.log = this.log.bind(this);
61
- this.emit = this.emit.bind(this);
73
+ // Bindings
74
+ this.log = this.log.bind(this);
75
+ this.emit = this.emit.bind(this);
62
76
 
63
- // 1. Start Network Interceptor
64
- this.patchNetwork();
77
+ // 4. Start Network Interceptor
78
+ this.patchNetwork();
65
79
 
66
- // 2. SEND STARTUP PING (Always run this, silent by default)
67
- // This turns the dashboard status GREEN immediately on app boot.
68
- this.sendStartupSignal();
80
+ // 5. SEND STARTUP PING (Always run this, silent by default)
81
+ // This turns the dashboard status GREEN immediately on app boot.
82
+ this.sendStartupSignal();
83
+
84
+ } catch (err) {
85
+ // THE ULTIMATE CATCH-ALL: Ensure app NEVER crashes
86
+ if (config.debug) console.error("[Dropout] ❌ Initialization Error:", err);
87
+ }
69
88
  }
70
89
 
71
90
  // --- UTILS ---
@@ -83,15 +102,13 @@ class Dropout {
83
102
 
84
103
  // --- HEARTBEAT ---
85
104
  sendStartupSignal() {
86
- // We send a discrete 'system_boot' event.
87
- // The Database Trigger will see this and update projects.last_active_at
88
105
  const payload = JSON.stringify({
89
106
  project_id: this.projectId,
90
107
  session_id: 'system_boot_' + Date.now(),
91
108
  turn_role: 'system',
92
109
  turn_index: 0,
93
110
  content: 'Dropout SDK Initialized',
94
- //content_blob: 'Dropout SDK Initialized',
111
+ // content_blob: 'Dropout SDK Initialized',
95
112
  metadata_flags: {
96
113
  type: 'system_boot',
97
114
  environment: process.env.NODE_ENV || 'development',
@@ -108,7 +125,6 @@ class Dropout {
108
125
  }
109
126
  });
110
127
 
111
- // Silent error handling (unless debug is on)
112
128
  req.on('error', (e) => this.log("❌ Startup Signal Failed", e.message));
113
129
  req.write(payload);
114
130
  req.end();
@@ -169,7 +185,7 @@ class Dropout {
169
185
  model: payload.model,
170
186
  latency_ms: payload.latency_ms || null,
171
187
  content: payload.content,
172
- //content_blob: payload.content,
188
+ // content_blob: payload.content,
173
189
  content_hash: payload.content_hash,
174
190
  metadata_flags: payload.metadata_flags,
175
191
  received_at: new Date().toISOString()
@@ -393,6 +409,7 @@ class Dropout {
393
409
  }
394
410
  }
395
411
 
412
+ // Auto-Start (Server-Side Only)
396
413
  if (typeof process !== 'undefined' && process.env.DROPOUT_PROJECT_ID && process.env.DROPOUT_API_KEY) {
397
414
  new Dropout({
398
415
  projectId: process.env.DROPOUT_PROJECT_ID,