@0x2e8/phantom-ai-crawler 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 +165 -0
- package/dashboard/index.html +258 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +234 -0
- package/dist/cli.js.map +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +140 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +69 -0
- package/prisma/migrations/20260206200051_init/migration.sql +107 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +116 -0
- package/scripts/postinstall.js +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# š Phantom AI
|
|
2
|
+
|
|
3
|
+
Adaptive AI-powered web crawler with behavioral mutation engine.
|
|
4
|
+
|
|
5
|
+
Powered by **Claude Sonnet 4.5+** - the crawler evolves its behavior to match what targets expect.
|
|
6
|
+
|
|
7
|
+
## ⨠Features
|
|
8
|
+
|
|
9
|
+
- š§ **AI-Driven**: Uses Claude Sonnet 4.5 for behavioral analysis
|
|
10
|
+
- š **Self-Mutating**: DNA evolves based on target responses
|
|
11
|
+
- š¦ **Green Light System**: Trust-based progression from RED ā GREEN
|
|
12
|
+
- š **Real-time Dashboard**: Web UI for monitoring and control
|
|
13
|
+
- šÆ **Multi-target**: Crawl multiple sites simultaneously
|
|
14
|
+
- š **Stealth**: Adapts to avoid detection
|
|
15
|
+
|
|
16
|
+
## š Quick Start
|
|
17
|
+
|
|
18
|
+
### Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Via npx (no install)
|
|
22
|
+
npx phantom-ai-crawler
|
|
23
|
+
|
|
24
|
+
# Or install globally
|
|
25
|
+
npm install -g phantom-ai-crawler
|
|
26
|
+
phantom-ai
|
|
27
|
+
|
|
28
|
+
# Or local install
|
|
29
|
+
npm install phantom-ai-crawler
|
|
30
|
+
npx phantom-ai
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### First Run
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# 1. Setup (configure API key)
|
|
37
|
+
phantom-ai setup
|
|
38
|
+
|
|
39
|
+
# 2. Start server
|
|
40
|
+
phantom-ai start
|
|
41
|
+
|
|
42
|
+
# 3. Open dashboard
|
|
43
|
+
# http://localhost:8081
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## š ļø Commands
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
phantom-ai # Start server (default)
|
|
50
|
+
phantom-ai start # Start backend + dashboard
|
|
51
|
+
phantom-ai setup # Run configuration wizard
|
|
52
|
+
phantom-ai status # Check configuration
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Options
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
phantom-ai start -p 3000 # Backend on port 3000
|
|
59
|
+
phantom-ai start -u 8080 # Dashboard on port 8080
|
|
60
|
+
phantom-ai start --setup # Force reconfiguration
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## š§ Configuration
|
|
64
|
+
|
|
65
|
+
The setup wizard will ask for:
|
|
66
|
+
|
|
67
|
+
1. **Anthropic API Key** - Get from https://console.anthropic.com
|
|
68
|
+
2. **Claude Model** - Recommended: `claude-4-5-sonnet-20250929`
|
|
69
|
+
3. **Backend Port** - Default: `4000`
|
|
70
|
+
4. **Dashboard Port** - Default: `8081`
|
|
71
|
+
|
|
72
|
+
Config is saved to `.env` in your working directory.
|
|
73
|
+
|
|
74
|
+
## š Project Structure
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
phantom-ai/
|
|
78
|
+
āāā .env # Your configuration
|
|
79
|
+
āāā phantom.db # SQLite database
|
|
80
|
+
āāā src/
|
|
81
|
+
ā āāā cli.ts # CLI entry point
|
|
82
|
+
ā āāā server/ # Backend API
|
|
83
|
+
āāā dashboard/ # Web UI
|
|
84
|
+
āāā prisma/ # Database schema
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## š API Endpoints
|
|
88
|
+
|
|
89
|
+
| Endpoint | Method | Description |
|
|
90
|
+
|----------|--------|-------------|
|
|
91
|
+
| `/health` | GET | Server status |
|
|
92
|
+
| `/api/targets` | GET | List targets |
|
|
93
|
+
| `/api/targets` | POST | Create target |
|
|
94
|
+
| `/api/targets/:id` | GET | Target details |
|
|
95
|
+
| `/api/dna/:id/current` | GET | Current DNA |
|
|
96
|
+
| `/api/mcp/analyze/:id` | POST | Run MCP analysis |
|
|
97
|
+
|
|
98
|
+
## 𧬠How It Works
|
|
99
|
+
|
|
100
|
+
1. **Discovery** (š“ RED): Initial reconnaissance phase
|
|
101
|
+
2. **Learning** (š” YELLOW): Testing behavioral patterns
|
|
102
|
+
3. **Established** (š¢ GREEN): Trusted access achieved
|
|
103
|
+
4. **Maintenance**: Continuous adaptation
|
|
104
|
+
|
|
105
|
+
The MCP (Model Context Protocol) analyzes each interaction and suggests DNA mutations to improve trust scores.
|
|
106
|
+
|
|
107
|
+
## š Requirements
|
|
108
|
+
|
|
109
|
+
- Node.js 18+
|
|
110
|
+
- Anthropic API key
|
|
111
|
+
- Claude Sonnet 4.5+ access
|
|
112
|
+
|
|
113
|
+
## š Troubleshooting
|
|
114
|
+
|
|
115
|
+
**Port already in use:**
|
|
116
|
+
```bash
|
|
117
|
+
phantom-ai start -p 3000 -u 8080 # Use different ports
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Database issues:**
|
|
121
|
+
```bash
|
|
122
|
+
rm phantom.db # Reset database
|
|
123
|
+
phantom-ai setup
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**API key not working:**
|
|
127
|
+
```bash
|
|
128
|
+
phantom-ai status # Check configuration
|
|
129
|
+
phantom-ai setup # Reconfigure
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## šļø Development
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
git clone https://github.com/0x2e8/phantom-ai.git
|
|
136
|
+
cd phantom-ai
|
|
137
|
+
npm install
|
|
138
|
+
npm run dev # Development mode
|
|
139
|
+
npm run build # Build for production
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## š¦ Publishing to NPM
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# 1. Update version
|
|
146
|
+
npm version patch
|
|
147
|
+
|
|
148
|
+
# 2. Build
|
|
149
|
+
npm run build
|
|
150
|
+
|
|
151
|
+
# 3. Publish
|
|
152
|
+
npm publish
|
|
153
|
+
|
|
154
|
+
# Or dry run first
|
|
155
|
+
npm publish --dry-run
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## š License
|
|
159
|
+
|
|
160
|
+
MIT
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
Built with š by 0x2e8
|
|
165
|
+
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Phantom AI - Dashboard</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
9
|
+
<style>
|
|
10
|
+
* { font-family: 'Inter', sans-serif; }
|
|
11
|
+
.mono { font-family: 'JetBrains Mono', monospace; }
|
|
12
|
+
.glass {
|
|
13
|
+
background: rgba(17, 24, 39, 0.8);
|
|
14
|
+
backdrop-filter: blur(12px);
|
|
15
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
16
|
+
}
|
|
17
|
+
.pulse-ring {
|
|
18
|
+
animation: pulse-ring 2s cubic-bezier(0.215, 0.61, 0.355, 1) infinite;
|
|
19
|
+
}
|
|
20
|
+
@keyframes pulse-ring {
|
|
21
|
+
0% { transform: scale(0.8); opacity: 0.8; }
|
|
22
|
+
100% { transform: scale(2); opacity: 0; }
|
|
23
|
+
}
|
|
24
|
+
.gradient-text {
|
|
25
|
+
background: linear-gradient(135deg, #a78bfa 0%, #60a5fa 100%);
|
|
26
|
+
-webkit-background-clip: text;
|
|
27
|
+
-webkit-text-fill-color: transparent;
|
|
28
|
+
}
|
|
29
|
+
</style>
|
|
30
|
+
</head>
|
|
31
|
+
<body class="bg-gray-950 text-gray-100 min-h-screen">
|
|
32
|
+
<!-- Header -->
|
|
33
|
+
<header class="glass sticky top-0 z-50 border-b border-gray-800">
|
|
34
|
+
<div class="max-w-7xl mx-auto px-4 py-4">
|
|
35
|
+
<div class="flex items-center justify-between">
|
|
36
|
+
<div class="flex items-center gap-3">
|
|
37
|
+
<div class="relative">
|
|
38
|
+
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-purple-600 to-blue-600 flex items-center justify-center">
|
|
39
|
+
<span class="text-xl">š</span>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="absolute inset-0 rounded-xl bg-purple-500 pulse-ring opacity-50"></div>
|
|
42
|
+
</div>
|
|
43
|
+
<div>
|
|
44
|
+
<h1 class="text-xl font-bold gradient-text">Phantom AI</h1>
|
|
45
|
+
<p class="text-xs text-gray-500">Adaptive Web Crawler</p>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="flex items-center gap-3">
|
|
49
|
+
<div id="api-status" class="flex items-center gap-2 px-3 py-1.5 rounded-full bg-gray-900 border border-gray-800">
|
|
50
|
+
<div class="w-2 h-2 rounded-full bg-gray-500"></div>
|
|
51
|
+
<span class="text-xs text-gray-400 mono">API</span>
|
|
52
|
+
</div>
|
|
53
|
+
<div id="mcp-status" class="flex items-center gap-2 px-3 py-1.5 rounded-full bg-gray-900 border border-gray-800">
|
|
54
|
+
<div class="w-2 h-2 rounded-full bg-gray-500"></div>
|
|
55
|
+
<span class="text-xs text-gray-400 mono">MCP</span>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</header>
|
|
61
|
+
|
|
62
|
+
<main class="max-w-7xl mx-auto px-4 py-8">
|
|
63
|
+
<!-- Stats -->
|
|
64
|
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
|
65
|
+
<div class="glass rounded-xl p-4">
|
|
66
|
+
<p class="text-gray-500 text-xs mb-1 uppercase tracking-wider">Targets</p>
|
|
67
|
+
<p id="stat-targets" class="text-2xl font-bold text-white mono">0</p>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="glass rounded-xl p-4">
|
|
70
|
+
<p class="text-gray-500 text-xs mb-1 uppercase tracking-wider">Active</p>
|
|
71
|
+
<p id="stat-active" class="text-2xl font-bold text-green-400 mono">0</p>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="glass rounded-xl p-4">
|
|
74
|
+
<p class="text-gray-500 text-xs mb-1 uppercase tracking-wider">Green Light</p>
|
|
75
|
+
<p id="stat-green" class="text-2xl font-bold text-emerald-400 mono">0</p>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="glass rounded-xl p-4">
|
|
78
|
+
<p class="text-gray-500 text-xs mb-1 uppercase tracking-wider">MCP</p>
|
|
79
|
+
<p id="stat-mcp" class="text-sm font-medium text-purple-400 mono truncate">-</p>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<!-- Add Target -->
|
|
84
|
+
<div class="glass rounded-xl p-6 mb-8">
|
|
85
|
+
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
|
|
86
|
+
<span class="text-purple-400">ā</span> New Target
|
|
87
|
+
</h2>
|
|
88
|
+
<form id="add-target-form" class="flex gap-3">
|
|
89
|
+
<input
|
|
90
|
+
type="url"
|
|
91
|
+
id="target-url"
|
|
92
|
+
placeholder="https://example.com"
|
|
93
|
+
required
|
|
94
|
+
class="flex-1 bg-gray-900 border border-gray-800 rounded-lg px-4 py-3 text-white placeholder-gray-600 focus:outline-none focus:border-purple-500 focus:ring-1 focus:ring-purple-500/50"
|
|
95
|
+
>
|
|
96
|
+
<select id="target-type" class="bg-gray-900 border border-gray-800 rounded-lg px-4 py-3 text-white focus:outline-none focus:border-purple-500">
|
|
97
|
+
<option value="web">Web</option>
|
|
98
|
+
<option value="api">API</option>
|
|
99
|
+
<option value="mobile">Mobile</option>
|
|
100
|
+
</select>
|
|
101
|
+
<button type="submit" class="bg-gradient-to-r from-purple-600 to-blue-600 hover:from-purple-700 hover:to-blue-700 text-white px-6 py-3 rounded-lg font-medium transition-all">
|
|
102
|
+
Add
|
|
103
|
+
</button>
|
|
104
|
+
</form>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- Targets -->
|
|
108
|
+
<div class="glass rounded-xl overflow-hidden">
|
|
109
|
+
<div class="px-6 py-4 border-b border-gray-800 flex items-center justify-between">
|
|
110
|
+
<h2 class="text-lg font-semibold flex items-center gap-2">
|
|
111
|
+
<span class="text-purple-400">šÆ</span> Targets
|
|
112
|
+
</h2>
|
|
113
|
+
<button onclick="loadTargets()" class="text-sm text-purple-400 hover:text-purple-300 transition-colors">
|
|
114
|
+
ā» Refresh
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
<div id="targets-list" class="divide-y divide-gray-800/50">
|
|
118
|
+
<div class="px-6 py-12 text-center text-gray-500">
|
|
119
|
+
<div class="animate-pulse flex flex-col items-center">
|
|
120
|
+
<div class="w-12 h-12 rounded-full bg-gray-800 mb-4"></div>
|
|
121
|
+
<div class="h-4 w-32 bg-gray-800 rounded"></div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</main>
|
|
127
|
+
|
|
128
|
+
<script>
|
|
129
|
+
// Auto-detect API URL (works with proxy)
|
|
130
|
+
const API_URL = window.location.origin;
|
|
131
|
+
|
|
132
|
+
async function checkHealth() {
|
|
133
|
+
try {
|
|
134
|
+
const res = await fetch('/health');
|
|
135
|
+
const data = await res.json();
|
|
136
|
+
|
|
137
|
+
// API status
|
|
138
|
+
const apiStatus = document.getElementById('api-status');
|
|
139
|
+
apiStatus.innerHTML = `<div class="w-2 h-2 rounded-full bg-green-500 animate-pulse"></div><span class="text-xs text-gray-300 mono">API:ON</span>`;
|
|
140
|
+
apiStatus.classList.add('border-green-500/30');
|
|
141
|
+
|
|
142
|
+
// MCP status
|
|
143
|
+
const mcpStatus = document.getElementById('mcp-status');
|
|
144
|
+
if (data.mcp?.configured) {
|
|
145
|
+
mcpStatus.innerHTML = `<div class="w-2 h-2 rounded-full bg-purple-500 animate-pulse"></div><span class="text-xs text-gray-300 mono">MCP:ON</span>`;
|
|
146
|
+
mcpStatus.classList.add('border-purple-500/30');
|
|
147
|
+
document.getElementById('stat-mcp').textContent = data.mcp.model.split('-').slice(2, 4).join('-');
|
|
148
|
+
} else {
|
|
149
|
+
mcpStatus.innerHTML = `<div class="w-2 h-2 rounded-full bg-yellow-500"></div><span class="text-xs text-gray-300 mono">MCP:MOCK</span>`;
|
|
150
|
+
mcpStatus.classList.add('border-yellow-500/30');
|
|
151
|
+
}
|
|
152
|
+
} catch (e) {
|
|
153
|
+
document.getElementById('api-status').innerHTML = `<div class="w-2 h-2 rounded-full bg-red-500"></div><span class="text-xs text-gray-300 mono">API:ERR</span>`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function getStatusColor(status) {
|
|
158
|
+
return {
|
|
159
|
+
'RED': 'bg-red-500',
|
|
160
|
+
'YELLOW': 'bg-yellow-500',
|
|
161
|
+
'GREEN': 'bg-green-500',
|
|
162
|
+
'ESTABLISHED': 'bg-emerald-500'
|
|
163
|
+
}[status] || 'bg-gray-500';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getStatusBg(status) {
|
|
167
|
+
return {
|
|
168
|
+
'ESTABLISHED': 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',
|
|
169
|
+
'GREEN': 'bg-green-500/10 text-green-400 border-green-500/20'
|
|
170
|
+
}[status] || 'bg-gray-800 text-gray-400 border-gray-700';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function loadTargets() {
|
|
174
|
+
try {
|
|
175
|
+
const res = await fetch('/api/targets');
|
|
176
|
+
const targets = await res.json();
|
|
177
|
+
|
|
178
|
+
// Update stats
|
|
179
|
+
document.getElementById('stat-targets').textContent = targets.length;
|
|
180
|
+
document.getElementById('stat-active').textContent = targets.filter(t => t.status === 'learning').length;
|
|
181
|
+
document.getElementById('stat-green').textContent = targets.filter(t =>
|
|
182
|
+
t.greenLightStatus === 'GREEN' || t.greenLightStatus === 'ESTABLISHED'
|
|
183
|
+
).length;
|
|
184
|
+
|
|
185
|
+
const list = document.getElementById('targets-list');
|
|
186
|
+
if (targets.length === 0) {
|
|
187
|
+
list.innerHTML = `
|
|
188
|
+
<div class="px-6 py-16 text-center text-gray-500">
|
|
189
|
+
<p class="text-4xl mb-4 opacity-50">š»</p>
|
|
190
|
+
<p class="mb-2">No targets yet</p>
|
|
191
|
+
<p class="text-sm text-gray-600">Add your first target above</p>
|
|
192
|
+
</div>
|
|
193
|
+
`;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
list.innerHTML = targets.map(t => `
|
|
198
|
+
<div class="px-6 py-4 flex items-center justify-between hover:bg-gray-800/30 transition-colors group">
|
|
199
|
+
<div class="flex items-center gap-4">
|
|
200
|
+
<div class="w-2.5 h-2.5 rounded-full ${getStatusColor(t.greenLightStatus)} ring-2 ring-gray-800"></div>
|
|
201
|
+
<div>
|
|
202
|
+
<p class="font-medium text-white group-hover:text-purple-400 transition-colors">${t.url}</p>
|
|
203
|
+
<p class="text-xs text-gray-600 mono">${t.id.slice(0, 8)}... ⢠${t.status}</p>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
<div class="flex items-center gap-4">
|
|
207
|
+
<div class="text-right">
|
|
208
|
+
<p class="text-sm font-medium text-white">${t.trustScore}%</p>
|
|
209
|
+
<p class="text-xs text-gray-600">trust</p>
|
|
210
|
+
</div>
|
|
211
|
+
<span class="px-3 py-1 rounded-full text-xs font-medium border ${getStatusBg(t.greenLightStatus)}">
|
|
212
|
+
${t.greenLightStatus}
|
|
213
|
+
</span>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
`).join('');
|
|
217
|
+
} catch (e) {
|
|
218
|
+
document.getElementById('targets-list').innerHTML = `
|
|
219
|
+
<div class="px-6 py-12 text-center text-red-400">
|
|
220
|
+
<p>Failed to load targets</p>
|
|
221
|
+
<p class="text-sm text-gray-600 mt-2">${e.message}</p>
|
|
222
|
+
</div>
|
|
223
|
+
`;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
document.getElementById('add-target-form').addEventListener('submit', async (e) => {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
const url = document.getElementById('target-url').value;
|
|
230
|
+
const type = document.getElementById('target-type').value;
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const res = await fetch('/api/targets', {
|
|
234
|
+
method: 'POST',
|
|
235
|
+
headers: { 'Content-Type': 'application/json' },
|
|
236
|
+
body: JSON.stringify({ url, type })
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (res.ok) {
|
|
240
|
+
document.getElementById('target-url').value = '';
|
|
241
|
+
loadTargets();
|
|
242
|
+
} else {
|
|
243
|
+
const err = await res.json();
|
|
244
|
+
alert('Error: ' + (err.error || 'Unknown error'));
|
|
245
|
+
}
|
|
246
|
+
} catch (e) {
|
|
247
|
+
alert('Error: ' + e.message);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Init
|
|
252
|
+
checkHealth();
|
|
253
|
+
loadTargets();
|
|
254
|
+
setInterval(() => { checkHealth(); loadTargets(); }, 5000);
|
|
255
|
+
</script>
|
|
256
|
+
</body>
|
|
257
|
+
</html>
|
|
258
|
+
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const ora_1 = __importDefault(require("ora"));
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
const fs_1 = require("fs");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
const program = new commander_1.Command();
|
|
15
|
+
const pkg = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../package.json'), 'utf8'));
|
|
16
|
+
const ENV_PATH = (0, path_1.resolve)(process.cwd(), '.env');
|
|
17
|
+
const DASHBOARD_DIR = (0, path_1.join)(__dirname, '../dashboard');
|
|
18
|
+
// Banner
|
|
19
|
+
console.log(chalk_1.default.magenta(`
|
|
20
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
21
|
+
ā ā
|
|
22
|
+
ā š ${chalk_1.default.bold('PHANTOM AI')} - Adaptive Web Crawler v${pkg.version} ā
|
|
23
|
+
ā ā
|
|
24
|
+
ā AI-powered behavioral mutation engine ā
|
|
25
|
+
ā Requires: Claude Sonnet 4.5+ ā
|
|
26
|
+
ā ā
|
|
27
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
28
|
+
`));
|
|
29
|
+
// Check if setup is needed
|
|
30
|
+
function needsSetup() {
|
|
31
|
+
if (!(0, fs_1.existsSync)(ENV_PATH))
|
|
32
|
+
return true;
|
|
33
|
+
const env = (0, fs_1.readFileSync)(ENV_PATH, 'utf8');
|
|
34
|
+
return !env.includes('ANTHROPIC_API_KEY') || env.includes('your-api-key-here');
|
|
35
|
+
}
|
|
36
|
+
// Setup wizard
|
|
37
|
+
async function setupWizard() {
|
|
38
|
+
console.log(chalk_1.default.yellow('āļø Initial Setup Required\n'));
|
|
39
|
+
const answers = await inquirer_1.default.prompt([
|
|
40
|
+
{
|
|
41
|
+
type: 'input',
|
|
42
|
+
name: 'apiKey',
|
|
43
|
+
message: chalk_1.default.cyan('š Enter your Anthropic API Key:'),
|
|
44
|
+
validate: (input) => {
|
|
45
|
+
if (!input.startsWith('sk-ant-api')) {
|
|
46
|
+
return 'Please enter a valid Anthropic API key (starts with sk-ant-api)';
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'list',
|
|
53
|
+
name: 'model',
|
|
54
|
+
message: chalk_1.default.cyan('š§ Select Claude Model:'),
|
|
55
|
+
choices: [
|
|
56
|
+
{ name: 'Claude 4.5 Sonnet (Recommended)', value: 'claude-4-5-sonnet-20250929' },
|
|
57
|
+
{ name: 'Claude 4.5 Sonnet Latest', value: 'claude-4-5-sonnet-latest' },
|
|
58
|
+
{ name: 'Claude 4 Opus', value: 'claude-4-opus-20251001' }
|
|
59
|
+
],
|
|
60
|
+
default: 'claude-4-5-sonnet-20250929'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'input',
|
|
64
|
+
name: 'apiPort',
|
|
65
|
+
message: chalk_1.default.cyan('š Backend Port:'),
|
|
66
|
+
default: '4000',
|
|
67
|
+
validate: (input) => !isNaN(parseInt(input)) || 'Please enter a valid port number'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: 'input',
|
|
71
|
+
name: 'uiPort',
|
|
72
|
+
message: chalk_1.default.cyan('š Dashboard Port:'),
|
|
73
|
+
default: '8081',
|
|
74
|
+
validate: (input) => !isNaN(parseInt(input)) || 'Please enter a valid port number'
|
|
75
|
+
}
|
|
76
|
+
]);
|
|
77
|
+
const envContent = `# Phantom AI Configuration
|
|
78
|
+
# Generated: ${new Date().toISOString()}
|
|
79
|
+
|
|
80
|
+
ANTHROPIC_API_KEY="${answers.apiKey}"
|
|
81
|
+
CLAUDE_MODEL="${answers.model}"
|
|
82
|
+
MCP_MAX_TOKENS=8192
|
|
83
|
+
MCP_TEMPERATURE=0.2
|
|
84
|
+
|
|
85
|
+
PORT=${answers.apiPort}
|
|
86
|
+
NODE_ENV=development
|
|
87
|
+
|
|
88
|
+
NEXT_PUBLIC_API_URL="http://localhost:${answers.apiPort}"
|
|
89
|
+
NEXT_PUBLIC_WS_URL="http://localhost:${answers.apiPort}"
|
|
90
|
+
UI_PORT=${answers.uiPort}
|
|
91
|
+
|
|
92
|
+
DATABASE_URL="file:${process.cwd()}/phantom.db"
|
|
93
|
+
|
|
94
|
+
JWT_SECRET="${Math.random().toString(36).substring(2)}${Math.random().toString(36).substring(2)}"
|
|
95
|
+
`;
|
|
96
|
+
(0, fs_1.writeFileSync)(ENV_PATH, envContent);
|
|
97
|
+
console.log(chalk_1.default.green('\nā
Configuration saved to .env'));
|
|
98
|
+
// Initialize database
|
|
99
|
+
const spinner = (0, ora_1.default)('Initializing database...').start();
|
|
100
|
+
try {
|
|
101
|
+
(0, child_process_1.execSync)('npx prisma migrate deploy', {
|
|
102
|
+
cwd: process.cwd(),
|
|
103
|
+
stdio: 'pipe'
|
|
104
|
+
});
|
|
105
|
+
spinner.succeed('Database initialized');
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
spinner.warn('Database may already be initialized');
|
|
109
|
+
}
|
|
110
|
+
return answers;
|
|
111
|
+
}
|
|
112
|
+
// Start backend
|
|
113
|
+
async function startBackend(port) {
|
|
114
|
+
return new Promise((resolve, reject) => {
|
|
115
|
+
const spinner = (0, ora_1.default)('Starting backend...').start();
|
|
116
|
+
const backend = (0, child_process_1.spawn)('node', [(0, path_1.join)(__dirname, 'server/index.js')], {
|
|
117
|
+
cwd: process.cwd(),
|
|
118
|
+
env: { ...process.env, PORT: port },
|
|
119
|
+
detached: false
|
|
120
|
+
});
|
|
121
|
+
backend.stdout?.on('data', (data) => {
|
|
122
|
+
const line = data.toString();
|
|
123
|
+
if (line.includes('running on port')) {
|
|
124
|
+
spinner.succeed(chalk_1.default.green(`Backend running on port ${port}`));
|
|
125
|
+
resolve(backend);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
backend.stderr?.on('data', (data) => {
|
|
129
|
+
// Ignore common startup warnings
|
|
130
|
+
});
|
|
131
|
+
setTimeout(() => {
|
|
132
|
+
spinner.fail('Backend failed to start');
|
|
133
|
+
reject(new Error('Timeout'));
|
|
134
|
+
}, 30000);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// Start frontend with Python (simple and reliable)
|
|
138
|
+
async function startFrontend(port) {
|
|
139
|
+
return new Promise((resolve, reject) => {
|
|
140
|
+
const spinner = (0, ora_1.default)('Starting dashboard...').start();
|
|
141
|
+
const frontend = (0, child_process_1.spawn)('python3', ['-m', 'http.server', port], {
|
|
142
|
+
cwd: DASHBOARD_DIR,
|
|
143
|
+
detached: false
|
|
144
|
+
});
|
|
145
|
+
// Give it a moment to start
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
spinner.succeed(chalk_1.default.green(`Dashboard running on port ${port}`));
|
|
148
|
+
resolve(frontend);
|
|
149
|
+
}, 2000);
|
|
150
|
+
frontend.on('error', (err) => {
|
|
151
|
+
spinner.fail(`Dashboard failed: ${err.message}`);
|
|
152
|
+
reject(err);
|
|
153
|
+
});
|
|
154
|
+
setTimeout(() => {
|
|
155
|
+
spinner.fail('Dashboard failed to start');
|
|
156
|
+
reject(new Error('Timeout'));
|
|
157
|
+
}, 10000);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// Main start command
|
|
161
|
+
program
|
|
162
|
+
.command('start')
|
|
163
|
+
.alias('s')
|
|
164
|
+
.description('Start Phantom AI (backend + dashboard)')
|
|
165
|
+
.option('-p, --port <port>', 'Backend port', '4000')
|
|
166
|
+
.option('-u, --ui-port <port>', 'Dashboard port', '8081')
|
|
167
|
+
.option('--setup', 'Force setup wizard')
|
|
168
|
+
.action(async (options) => {
|
|
169
|
+
try {
|
|
170
|
+
// Setup if needed
|
|
171
|
+
if (options.setup || needsSetup()) {
|
|
172
|
+
await setupWizard();
|
|
173
|
+
}
|
|
174
|
+
// Read config
|
|
175
|
+
const env = (0, fs_1.readFileSync)(ENV_PATH, 'utf8');
|
|
176
|
+
const apiPort = options.port || env.match(/PORT=(\d+)/)?.[1] || '4000';
|
|
177
|
+
const uiPort = options.uiPort || env.match(/UI_PORT=(\d+)/)?.[1] || '8081';
|
|
178
|
+
console.log(chalk_1.default.blue('\nš Starting Phantom AI...\n'));
|
|
179
|
+
// Start services
|
|
180
|
+
const backend = await startBackend(apiPort);
|
|
181
|
+
const frontend = await startFrontend(uiPort);
|
|
182
|
+
// Print success
|
|
183
|
+
console.log(chalk_1.default.green('\nā
Phantom AI is running!\n'));
|
|
184
|
+
console.log(chalk_1.default.white('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
185
|
+
console.log(chalk_1.default.white('ā') + chalk_1.default.cyan(' š Dashboard: ') + chalk_1.default.yellow(`http://localhost:${uiPort} `) + chalk_1.default.white('ā'));
|
|
186
|
+
console.log(chalk_1.default.white('ā') + chalk_1.default.cyan(' š API: ') + chalk_1.default.yellow(`http://localhost:${apiPort} `) + chalk_1.default.white('ā'));
|
|
187
|
+
console.log(chalk_1.default.white('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
188
|
+
console.log(chalk_1.default.gray('\nPress Ctrl+C to stop\n'));
|
|
189
|
+
// Handle shutdown
|
|
190
|
+
process.on('SIGINT', () => {
|
|
191
|
+
console.log(chalk_1.default.yellow('\n\nš Shutting down...'));
|
|
192
|
+
backend.kill();
|
|
193
|
+
frontend.kill();
|
|
194
|
+
process.exit(0);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
console.error(chalk_1.default.red('\nā Error:'), error);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
// Setup command
|
|
203
|
+
program
|
|
204
|
+
.command('setup')
|
|
205
|
+
.description('Run setup wizard')
|
|
206
|
+
.action(async () => {
|
|
207
|
+
await setupWizard();
|
|
208
|
+
console.log(chalk_1.default.green('\nā
Setup complete! Run "phantom-ai start" to begin.'));
|
|
209
|
+
});
|
|
210
|
+
// Status command
|
|
211
|
+
program
|
|
212
|
+
.command('status')
|
|
213
|
+
.description('Check Phantom AI status')
|
|
214
|
+
.action(async () => {
|
|
215
|
+
if (needsSetup()) {
|
|
216
|
+
console.log(chalk_1.default.yellow('ā ļø Not configured. Run: phantom-ai setup'));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const env = (0, fs_1.readFileSync)(ENV_PATH, 'utf8');
|
|
220
|
+
const model = env.match(/CLAUDE_MODEL="([^"]+)"/)?.[1];
|
|
221
|
+
const hasKey = !env.includes('your-api-key-here');
|
|
222
|
+
console.log(chalk_1.default.blue('\nš Configuration Status:\n'));
|
|
223
|
+
console.log(chalk_1.default.white(' API Key: '), hasKey ? chalk_1.default.green('ā Configured') : chalk_1.default.red('ā Missing'));
|
|
224
|
+
console.log(chalk_1.default.white(' Model: '), chalk_1.default.cyan(model || 'Not set'));
|
|
225
|
+
console.log(chalk_1.default.white(' Database: '), (0, fs_1.existsSync)('./phantom.db') ? chalk_1.default.green('ā Initialized') : chalk_1.default.yellow('Will create on start'));
|
|
226
|
+
});
|
|
227
|
+
// Default action
|
|
228
|
+
if (process.argv.length === 2) {
|
|
229
|
+
program.parse(['node', 'cli', 'start']);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
program.parse();
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client';
|
|
2
|
+
export declare const prisma: PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
|
|
3
|
+
export declare const redis: {
|
|
4
|
+
get: (key: string) => Promise<any>;
|
|
5
|
+
setex: (key: string, seconds: number, value: string) => Promise<Map<any, any>>;
|
|
6
|
+
ping: () => Promise<string>;
|
|
7
|
+
quit: () => Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAU9C,eAAO,MAAM,MAAM,gIAAqB,CAAC;AAIzC,eAAO,MAAM,KAAK;eACC,MAAM;iBACJ,MAAM,WAAW,MAAM,SAAS,MAAM;;;CAG1D,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.redis = exports.prisma = void 0;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const cors_1 = __importDefault(require("cors"));
|
|
9
|
+
const http_1 = require("http");
|
|
10
|
+
const socket_io_1 = require("socket.io");
|
|
11
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
12
|
+
const client_1 = require("@prisma/client");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
// Load env
|
|
15
|
+
dotenv_1.default.config({ path: (0, path_1.resolve)(process.cwd(), '.env') });
|
|
16
|
+
const app = (0, express_1.default)();
|
|
17
|
+
const httpServer = (0, http_1.createServer)(app);
|
|
18
|
+
const io = new socket_io_1.Server(httpServer, { cors: { origin: '*' } });
|
|
19
|
+
exports.prisma = new client_1.PrismaClient();
|
|
20
|
+
// In-memory store (no Redis required)
|
|
21
|
+
const memoryStore = new Map();
|
|
22
|
+
exports.redis = {
|
|
23
|
+
get: async (key) => memoryStore.get(key) || null,
|
|
24
|
+
setex: async (key, seconds, value) => memoryStore.set(key, value),
|
|
25
|
+
ping: async () => 'PONG',
|
|
26
|
+
quit: async () => { }
|
|
27
|
+
};
|
|
28
|
+
// Middleware
|
|
29
|
+
app.use((0, cors_1.default)());
|
|
30
|
+
app.use(express_1.default.json());
|
|
31
|
+
// Health check
|
|
32
|
+
app.get('/health', async (req, res) => {
|
|
33
|
+
try {
|
|
34
|
+
await exports.prisma.$queryRaw `SELECT 1`;
|
|
35
|
+
res.json({
|
|
36
|
+
status: 'ok',
|
|
37
|
+
timestamp: new Date().toISOString(),
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
mcp: {
|
|
40
|
+
model: process.env.CLAUDE_MODEL || 'not configured',
|
|
41
|
+
configured: !!process.env.ANTHROPIC_API_KEY && !process.env.ANTHROPIC_API_KEY?.includes('your-api-key')
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
res.status(503).json({ status: 'unhealthy', error: String(error) });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
// Targets API
|
|
50
|
+
app.get('/api/targets', async (req, res) => {
|
|
51
|
+
try {
|
|
52
|
+
const targets = await exports.prisma.target.findMany({
|
|
53
|
+
include: {
|
|
54
|
+
_count: { select: { learningEvents: true, requestLogs: true } }
|
|
55
|
+
},
|
|
56
|
+
orderBy: { createdAt: 'desc' }
|
|
57
|
+
});
|
|
58
|
+
res.json(targets);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
res.status(500).json({ error: String(error) });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
app.post('/api/targets', async (req, res) => {
|
|
65
|
+
try {
|
|
66
|
+
const { url, type = 'web' } = req.body;
|
|
67
|
+
if (!url)
|
|
68
|
+
return res.status(400).json({ error: 'URL required' });
|
|
69
|
+
const target = await exports.prisma.target.create({
|
|
70
|
+
data: {
|
|
71
|
+
url,
|
|
72
|
+
type,
|
|
73
|
+
status: 'discovering',
|
|
74
|
+
greenLightStatus: 'RED',
|
|
75
|
+
trustScore: 0
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
res.json(target);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
res.status(500).json({ error: String(error) });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
app.get('/api/targets/:id', async (req, res) => {
|
|
85
|
+
try {
|
|
86
|
+
const target = await exports.prisma.target.findUnique({
|
|
87
|
+
where: { id: req.params.id },
|
|
88
|
+
include: {
|
|
89
|
+
dnaSnapshots: { orderBy: { createdAt: 'desc' }, take: 5 },
|
|
90
|
+
learningEvents: { orderBy: { createdAt: 'desc' }, take: 10 },
|
|
91
|
+
_count: { select: { requestLogs: true } }
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
if (!target)
|
|
95
|
+
return res.status(404).json({ error: 'Not found' });
|
|
96
|
+
res.json(target);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
res.status(500).json({ error: String(error) });
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
// DNA API
|
|
103
|
+
app.get('/api/dna/:targetId/current', async (req, res) => {
|
|
104
|
+
try {
|
|
105
|
+
const target = await exports.prisma.target.findUnique({
|
|
106
|
+
where: { id: req.params.targetId },
|
|
107
|
+
include: { currentDna: true }
|
|
108
|
+
});
|
|
109
|
+
if (!target?.currentDna)
|
|
110
|
+
return res.json(null);
|
|
111
|
+
res.json(JSON.parse(target.currentDna.dnaJson));
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
res.status(500).json({ error: String(error) });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
// MCP API
|
|
118
|
+
app.post('/api/mcp/analyze/:targetId', async (req, res) => {
|
|
119
|
+
// Placeholder - MCP analysis would go here
|
|
120
|
+
res.json({
|
|
121
|
+
analysis: 'MCP analysis endpoint ready',
|
|
122
|
+
mock: !process.env.ANTHROPIC_API_KEY,
|
|
123
|
+
model: process.env.CLAUDE_MODEL
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
// Start server
|
|
127
|
+
const PORT = parseInt(process.env.PORT || '4000');
|
|
128
|
+
httpServer.listen(PORT, () => {
|
|
129
|
+
console.log(`š Backend running on port ${PORT}`);
|
|
130
|
+
});
|
|
131
|
+
// Graceful shutdown
|
|
132
|
+
process.on('SIGTERM', async () => {
|
|
133
|
+
await exports.prisma.$disconnect();
|
|
134
|
+
httpServer.close(() => process.exit(0));
|
|
135
|
+
});
|
|
136
|
+
process.on('SIGINT', async () => {
|
|
137
|
+
await exports.prisma.$disconnect();
|
|
138
|
+
httpServer.close(() => process.exit(0));
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@0x2e8/phantom-ai-crawler",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Adaptive AI-powered web crawler with behavioral mutation engine",
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"phantom-ai": "dist/cli.js",
|
|
8
|
+
"phantom": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/cli.js",
|
|
13
|
+
"dev": "tsx src/cli.ts",
|
|
14
|
+
"postinstall": "node scripts/postinstall.js"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"crawler",
|
|
18
|
+
"scraper",
|
|
19
|
+
"ai",
|
|
20
|
+
"claude",
|
|
21
|
+
"anthropic",
|
|
22
|
+
"adaptive",
|
|
23
|
+
"stealth",
|
|
24
|
+
"pentest"
|
|
25
|
+
],
|
|
26
|
+
"author": "0x2e8",
|
|
27
|
+
"private": false,
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@anthropic-ai/sdk": "^0.32.0",
|
|
34
|
+
"@prisma/client": "^5.22.0",
|
|
35
|
+
"commander": "^12.0.0",
|
|
36
|
+
"cors": "^2.8.5",
|
|
37
|
+
"dotenv": "^16.4.5",
|
|
38
|
+
"express": "^4.21.0",
|
|
39
|
+
"inquirer": "^8.2.6",
|
|
40
|
+
"socket.io": "^4.8.0",
|
|
41
|
+
"chalk": "^4.1.2",
|
|
42
|
+
"ora": "^5.4.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/cors": "^2.8.17",
|
|
46
|
+
"@types/express": "^4.17.21",
|
|
47
|
+
"@types/inquirer": "^9.0.7",
|
|
48
|
+
"@types/node": "^20.0.0",
|
|
49
|
+
"prisma": "^5.22.0",
|
|
50
|
+
"tsx": "^4.0.0",
|
|
51
|
+
"typescript": "^5.6.0"
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"dist/",
|
|
55
|
+
"dashboard/",
|
|
56
|
+
"prisma/",
|
|
57
|
+
"scripts/",
|
|
58
|
+
"README.md"
|
|
59
|
+
],
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/0x2e8/phantom-ai.git"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "Target" (
|
|
3
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
4
|
+
"url" TEXT NOT NULL,
|
|
5
|
+
"type" TEXT NOT NULL DEFAULT 'web',
|
|
6
|
+
"status" TEXT NOT NULL DEFAULT 'discovering',
|
|
7
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
8
|
+
"updatedAt" DATETIME NOT NULL,
|
|
9
|
+
"lastSeen" DATETIME,
|
|
10
|
+
"greenLightStatus" TEXT NOT NULL DEFAULT 'RED',
|
|
11
|
+
"trustScore" INTEGER NOT NULL DEFAULT 0,
|
|
12
|
+
"establishedAt" DATETIME,
|
|
13
|
+
"maintainedFor" INTEGER NOT NULL DEFAULT 0,
|
|
14
|
+
"currentDnaId" TEXT,
|
|
15
|
+
CONSTRAINT "Target_currentDnaId_fkey" FOREIGN KEY ("currentDnaId") REFERENCES "DnaSnapshot" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
-- CreateTable
|
|
19
|
+
CREATE TABLE "DnaSnapshot" (
|
|
20
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
21
|
+
"targetId" TEXT NOT NULL,
|
|
22
|
+
"version" TEXT NOT NULL DEFAULT '1.0.0',
|
|
23
|
+
"dnaJson" TEXT NOT NULL,
|
|
24
|
+
"parentId" TEXT,
|
|
25
|
+
"isActive" BOOLEAN NOT NULL DEFAULT false,
|
|
26
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
27
|
+
CONSTRAINT "DnaSnapshot_targetId_fkey" FOREIGN KEY ("targetId") REFERENCES "Target" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
28
|
+
CONSTRAINT "DnaSnapshot_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "DnaSnapshot" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
-- CreateTable
|
|
32
|
+
CREATE TABLE "LearningEvent" (
|
|
33
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
34
|
+
"targetId" TEXT NOT NULL,
|
|
35
|
+
"dnaVersionId" TEXT NOT NULL,
|
|
36
|
+
"eventType" TEXT NOT NULL,
|
|
37
|
+
"title" TEXT NOT NULL,
|
|
38
|
+
"description" TEXT NOT NULL,
|
|
39
|
+
"mcpInsight" TEXT,
|
|
40
|
+
"mcpConfidence" REAL,
|
|
41
|
+
"mcpModel" TEXT NOT NULL DEFAULT 'claude-4-5-sonnet',
|
|
42
|
+
"dnaChanges" TEXT,
|
|
43
|
+
"beforeState" TEXT,
|
|
44
|
+
"afterState" TEXT,
|
|
45
|
+
"trustImpact" INTEGER NOT NULL DEFAULT 0,
|
|
46
|
+
"durationMs" INTEGER,
|
|
47
|
+
"challengeType" TEXT,
|
|
48
|
+
"challengeSolved" BOOLEAN,
|
|
49
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
50
|
+
CONSTRAINT "LearningEvent_targetId_fkey" FOREIGN KEY ("targetId") REFERENCES "Target" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
51
|
+
CONSTRAINT "LearningEvent_dnaVersionId_fkey" FOREIGN KEY ("dnaVersionId") REFERENCES "DnaSnapshot" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
-- CreateTable
|
|
55
|
+
CREATE TABLE "GreenLightState" (
|
|
56
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
57
|
+
"targetId" TEXT NOT NULL,
|
|
58
|
+
"status" TEXT NOT NULL,
|
|
59
|
+
"trustScore" INTEGER NOT NULL,
|
|
60
|
+
"signalsJson" TEXT NOT NULL,
|
|
61
|
+
"establishedAt" DATETIME,
|
|
62
|
+
"maintainedFor" INTEGER NOT NULL DEFAULT 0,
|
|
63
|
+
"lostAt" DATETIME,
|
|
64
|
+
"reasonLost" TEXT,
|
|
65
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
66
|
+
CONSTRAINT "GreenLightState_targetId_fkey" FOREIGN KEY ("targetId") REFERENCES "Target" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
-- CreateTable
|
|
70
|
+
CREATE TABLE "RequestLog" (
|
|
71
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
72
|
+
"targetId" TEXT NOT NULL,
|
|
73
|
+
"dnaVersionId" TEXT,
|
|
74
|
+
"method" TEXT NOT NULL,
|
|
75
|
+
"url" TEXT NOT NULL,
|
|
76
|
+
"headers" TEXT NOT NULL,
|
|
77
|
+
"body" TEXT,
|
|
78
|
+
"responseStatus" INTEGER,
|
|
79
|
+
"responseHeaders" TEXT,
|
|
80
|
+
"responseBodyPreview" TEXT,
|
|
81
|
+
"wasBlocked" BOOLEAN NOT NULL DEFAULT false,
|
|
82
|
+
"blockReason" TEXT,
|
|
83
|
+
"challengeDetected" BOOLEAN NOT NULL DEFAULT false,
|
|
84
|
+
"challengeType" TEXT,
|
|
85
|
+
"timingMs" INTEGER,
|
|
86
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
87
|
+
CONSTRAINT "RequestLog_targetId_fkey" FOREIGN KEY ("targetId") REFERENCES "Target" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
88
|
+
CONSTRAINT "RequestLog_dnaVersionId_fkey" FOREIGN KEY ("dnaVersionId") REFERENCES "DnaSnapshot" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
-- CreateIndex
|
|
92
|
+
CREATE INDEX "Target_url_idx" ON "Target"("url");
|
|
93
|
+
|
|
94
|
+
-- CreateIndex
|
|
95
|
+
CREATE INDEX "Target_status_idx" ON "Target"("status");
|
|
96
|
+
|
|
97
|
+
-- CreateIndex
|
|
98
|
+
CREATE INDEX "DnaSnapshot_targetId_idx" ON "DnaSnapshot"("targetId");
|
|
99
|
+
|
|
100
|
+
-- CreateIndex
|
|
101
|
+
CREATE INDEX "LearningEvent_targetId_idx" ON "LearningEvent"("targetId");
|
|
102
|
+
|
|
103
|
+
-- CreateIndex
|
|
104
|
+
CREATE INDEX "GreenLightState_targetId_idx" ON "GreenLightState"("targetId");
|
|
105
|
+
|
|
106
|
+
-- CreateIndex
|
|
107
|
+
CREATE INDEX "RequestLog_targetId_idx" ON "RequestLog"("targetId");
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
datasource db {
|
|
6
|
+
provider = "sqlite"
|
|
7
|
+
url = env("DATABASE_URL")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
model Target {
|
|
11
|
+
id String @id @default(uuid())
|
|
12
|
+
url String
|
|
13
|
+
type String @default("web")
|
|
14
|
+
status String @default("discovering")
|
|
15
|
+
createdAt DateTime @default(now())
|
|
16
|
+
updatedAt DateTime @updatedAt
|
|
17
|
+
lastSeen DateTime?
|
|
18
|
+
|
|
19
|
+
greenLightStatus String @default("RED")
|
|
20
|
+
trustScore Int @default(0)
|
|
21
|
+
establishedAt DateTime?
|
|
22
|
+
maintainedFor Int @default(0)
|
|
23
|
+
|
|
24
|
+
currentDnaId String?
|
|
25
|
+
currentDna DnaSnapshot? @relation("CurrentDNA", fields: [currentDnaId], references: [id])
|
|
26
|
+
|
|
27
|
+
dnaSnapshots DnaSnapshot[]
|
|
28
|
+
learningEvents LearningEvent[]
|
|
29
|
+
greenLightHistory GreenLightState[]
|
|
30
|
+
requestLogs RequestLog[]
|
|
31
|
+
|
|
32
|
+
@@index([url])
|
|
33
|
+
@@index([status])
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
model DnaSnapshot {
|
|
37
|
+
id String @id @default(uuid())
|
|
38
|
+
targetId String
|
|
39
|
+
target Target @relation(fields: [targetId], references: [id])
|
|
40
|
+
version String @default("1.0.0")
|
|
41
|
+
dnaJson String
|
|
42
|
+
parentId String?
|
|
43
|
+
parent DnaSnapshot? @relation("DNALineage", fields: [parentId], references: [id])
|
|
44
|
+
children DnaSnapshot[] @relation("DNALineage")
|
|
45
|
+
isActive Boolean @default(false)
|
|
46
|
+
createdAt DateTime @default(now())
|
|
47
|
+
currentFor Target[] @relation("CurrentDNA")
|
|
48
|
+
events LearningEvent[]
|
|
49
|
+
requests RequestLog[]
|
|
50
|
+
|
|
51
|
+
@@index([targetId])
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
model LearningEvent {
|
|
55
|
+
id String @id @default(uuid())
|
|
56
|
+
targetId String
|
|
57
|
+
target Target @relation(fields: [targetId], references: [id])
|
|
58
|
+
dnaVersionId String
|
|
59
|
+
dnaVersion DnaSnapshot @relation(fields: [dnaVersionId], references: [id])
|
|
60
|
+
eventType String
|
|
61
|
+
title String
|
|
62
|
+
description String
|
|
63
|
+
mcpInsight String?
|
|
64
|
+
mcpConfidence Float?
|
|
65
|
+
mcpModel String @default("claude-4-5-sonnet")
|
|
66
|
+
dnaChanges String?
|
|
67
|
+
beforeState String?
|
|
68
|
+
afterState String?
|
|
69
|
+
trustImpact Int @default(0)
|
|
70
|
+
durationMs Int?
|
|
71
|
+
challengeType String?
|
|
72
|
+
challengeSolved Boolean?
|
|
73
|
+
createdAt DateTime @default(now())
|
|
74
|
+
|
|
75
|
+
@@index([targetId])
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
model GreenLightState {
|
|
79
|
+
id String @id @default(uuid())
|
|
80
|
+
targetId String
|
|
81
|
+
target Target @relation(fields: [targetId], references: [id])
|
|
82
|
+
status String
|
|
83
|
+
trustScore Int
|
|
84
|
+
signalsJson String
|
|
85
|
+
establishedAt DateTime?
|
|
86
|
+
maintainedFor Int @default(0)
|
|
87
|
+
lostAt DateTime?
|
|
88
|
+
reasonLost String?
|
|
89
|
+
createdAt DateTime @default(now())
|
|
90
|
+
|
|
91
|
+
@@index([targetId])
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
model RequestLog {
|
|
95
|
+
id String @id @default(uuid())
|
|
96
|
+
targetId String
|
|
97
|
+
target Target @relation(fields: [targetId], references: [id])
|
|
98
|
+
dnaVersionId String?
|
|
99
|
+
dnaVersion DnaSnapshot? @relation(fields: [dnaVersionId], references: [id])
|
|
100
|
+
method String
|
|
101
|
+
url String
|
|
102
|
+
headers String
|
|
103
|
+
body String?
|
|
104
|
+
responseStatus Int?
|
|
105
|
+
responseHeaders String?
|
|
106
|
+
responseBodyPreview String?
|
|
107
|
+
wasBlocked Boolean @default(false)
|
|
108
|
+
blockReason String?
|
|
109
|
+
challengeDetected Boolean @default(false)
|
|
110
|
+
challengeType String?
|
|
111
|
+
timingMs Int?
|
|
112
|
+
createdAt DateTime @default(now())
|
|
113
|
+
|
|
114
|
+
@@index([targetId])
|
|
115
|
+
}
|
|
116
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const { existsSync } = require('fs');
|
|
5
|
+
const { resolve } = require('path');
|
|
6
|
+
|
|
7
|
+
console.log('š§ Phantom AI - Post Install Setup\n');
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
// Generate Prisma client
|
|
11
|
+
console.log('š¦ Generating Prisma client...');
|
|
12
|
+
execSync('npx prisma generate', {
|
|
13
|
+
stdio: 'inherit',
|
|
14
|
+
cwd: resolve(__dirname, '..')
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
console.log('\nā
Setup complete!');
|
|
18
|
+
console.log('\nRun: phantom-ai setup - to configure API key');
|
|
19
|
+
console.log('Run: phantom-ai start - to start the server\n');
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error('ā Setup failed:', e.message);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|