deepdebug-local-agent 1.0.2 → 1.0.4
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/bin/install.js +491 -209
- package/package.json +13 -18
package/bin/install.js
CHANGED
|
@@ -1,282 +1,564 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* DeepDebug Local Agent - Interactive Installer with Authorization Flow
|
|
5
|
+
*
|
|
6
|
+
* Supports: Windows, macOS, Linux
|
|
5
7
|
*
|
|
6
8
|
* Usage:
|
|
7
|
-
* npx deepdebug-local-agent install
|
|
8
|
-
* npx deepdebug-local-agent
|
|
9
|
-
* npx deepdebug-local-agent
|
|
10
|
-
* npx deepdebug-local-agent
|
|
11
|
-
* npx deepdebug-local-agent
|
|
9
|
+
* npx deepdebug-local-agent install
|
|
10
|
+
* npx deepdebug-local-agent install --api-key=YOUR_KEY
|
|
11
|
+
* npx deepdebug-local-agent start
|
|
12
|
+
* npx deepdebug-local-agent stop
|
|
13
|
+
* npx deepdebug-local-agent status
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import { join, dirname } from 'path';
|
|
17
|
-
import { fileURLToPath } from 'url';
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
import path from 'path';
|
|
18
18
|
import os from 'os';
|
|
19
|
+
import crypto from 'crypto';
|
|
20
|
+
import { spawn, execSync } from 'child_process';
|
|
19
21
|
import readline from 'readline';
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const bold = (msg) => `${BOLD}${msg}${RESET}`;
|
|
43
|
-
|
|
44
|
-
function printBanner() {
|
|
45
|
-
console.log('');
|
|
46
|
-
console.log(`${BLUE}${BOLD}╔═══════════════════════════════════════╗${RESET}`);
|
|
47
|
-
console.log(`${BLUE}${BOLD}║ Insptech AI — DeepDebug Agent ║${RESET}`);
|
|
48
|
-
console.log(`${BLUE}${BOLD}║ deepdebug-local-agent ║${RESET}`);
|
|
49
|
-
console.log(`${BLUE}${BOLD}╚═══════════════════════════════════════╝${RESET}`);
|
|
50
|
-
console.log('');
|
|
23
|
+
// ============================================
|
|
24
|
+
// Configuration
|
|
25
|
+
// ============================================
|
|
26
|
+
const CONFIG_DIR = path.join(os.homedir(), '.deepdebug');
|
|
27
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
28
|
+
const MACHINE_ID_FILE = path.join(CONFIG_DIR, 'machine-id');
|
|
29
|
+
const API_BASE_URL = process.env.DEEPDEBUG_API_URL || 'https://deepdebug-gateway-lszyf7c4ia-uc.a.run.app';
|
|
30
|
+
const DOCKER_IMAGE = 'williambella/deepdebug-local-agent:latest';
|
|
31
|
+
|
|
32
|
+
// ============================================
|
|
33
|
+
// Cross-platform utilities
|
|
34
|
+
// ============================================
|
|
35
|
+
const isWindows = os.platform() === 'win32';
|
|
36
|
+
const isMac = os.platform() === 'darwin';
|
|
37
|
+
const isLinux = os.platform() === 'linux';
|
|
38
|
+
|
|
39
|
+
function getOsType() {
|
|
40
|
+
if (isWindows) return 'WINDOWS';
|
|
41
|
+
if (isMac) return 'MACOS';
|
|
42
|
+
if (isLinux) return 'LINUX';
|
|
43
|
+
return 'UNKNOWN';
|
|
51
44
|
}
|
|
52
45
|
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
return new Promise(resolve => rl.question(question, ans => { rl.close(); resolve(ans.trim()); }));
|
|
46
|
+
function getOsVersion() {
|
|
47
|
+
return `${os.type()} ${os.release()}`;
|
|
56
48
|
}
|
|
57
49
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
50
|
+
// ============================================
|
|
51
|
+
// Console colors (cross-platform)
|
|
52
|
+
// ============================================
|
|
53
|
+
const colors = {
|
|
54
|
+
reset: '\x1b[0m',
|
|
55
|
+
bright: '\x1b[1m',
|
|
56
|
+
green: '\x1b[32m',
|
|
57
|
+
yellow: '\x1b[33m',
|
|
58
|
+
red: '\x1b[31m',
|
|
59
|
+
cyan: '\x1b[36m',
|
|
60
|
+
magenta: '\x1b[35m',
|
|
61
|
+
blue: '\x1b[34m'
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
function log(message, color = 'reset') {
|
|
65
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), 'utf8');
|
|
68
|
+
function logSuccess(message) {
|
|
69
|
+
console.log(`${colors.green}✅ ${message}${colors.reset}`);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
if (major < 18) {
|
|
75
|
-
err(`Node.js ${process.version} detected. Version 18 or higher is required.`);
|
|
76
|
-
err('Download: https://nodejs.org');
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
ok(`Node.js ${process.version} detected`);
|
|
72
|
+
function logError(message) {
|
|
73
|
+
console.log(`${colors.red}❌ ${message}${colors.reset}`);
|
|
80
74
|
}
|
|
81
75
|
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
const candidates = [
|
|
85
|
-
join(ROOT_DIR, 'server.js'),
|
|
86
|
-
join(ROOT_DIR, 'src', 'server.js'),
|
|
87
|
-
];
|
|
88
|
-
for (const c of candidates) {
|
|
89
|
-
if (existsSync(c)) return c;
|
|
90
|
-
}
|
|
91
|
-
// Fallback: server.js next to bin/
|
|
92
|
-
return join(ROOT_DIR, 'server.js');
|
|
76
|
+
function logInfo(message) {
|
|
77
|
+
console.log(`${colors.cyan}ℹ️ ${message}${colors.reset}`);
|
|
93
78
|
}
|
|
94
79
|
|
|
95
|
-
|
|
80
|
+
function logWarning(message) {
|
|
81
|
+
console.log(`${colors.yellow}⚠️ ${message}${colors.reset}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ============================================
|
|
85
|
+
// Input utilities
|
|
86
|
+
// ============================================
|
|
87
|
+
async function prompt(question) {
|
|
88
|
+
const rl = readline.createInterface({
|
|
89
|
+
input: process.stdin,
|
|
90
|
+
output: process.stdout
|
|
91
|
+
});
|
|
96
92
|
|
|
97
|
-
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
rl.question(`${colors.cyan}${question}${colors.reset}`, (answer) => {
|
|
95
|
+
rl.close();
|
|
96
|
+
resolve(answer.trim());
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
98
100
|
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
+
async function promptYesNo(question, defaultYes = true) {
|
|
102
|
+
const hint = defaultYes ? '(Y/n)' : '(y/N)';
|
|
103
|
+
const answer = await prompt(`${question} ${hint}: `);
|
|
104
|
+
if (answer === '') return defaultYes;
|
|
105
|
+
return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
|
|
101
106
|
}
|
|
102
107
|
|
|
103
|
-
|
|
108
|
+
// ============================================
|
|
109
|
+
// Machine ID generation (unique per machine)
|
|
110
|
+
// ============================================
|
|
111
|
+
function getMachineId() {
|
|
112
|
+
if (fs.existsSync(MACHINE_ID_FILE)) {
|
|
113
|
+
return fs.readFileSync(MACHINE_ID_FILE, 'utf-8').trim();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let machineId;
|
|
104
117
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
if (isWindows) {
|
|
119
|
+
try {
|
|
120
|
+
const result = execSync('wmic bios get serialnumber', { encoding: 'utf-8' });
|
|
121
|
+
const serial = result.split('\n')[1]?.trim();
|
|
122
|
+
if (serial && serial !== 'To be filled by O.E.M.') {
|
|
123
|
+
machineId = crypto.createHash('sha256').update(serial).digest('hex').substring(0, 32);
|
|
124
|
+
}
|
|
125
|
+
} catch {}
|
|
126
|
+
} else if (isMac) {
|
|
127
|
+
try {
|
|
128
|
+
const result = execSync('ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID', { encoding: 'utf-8' });
|
|
129
|
+
const match = result.match(/"([A-F0-9-]+)"/);
|
|
130
|
+
if (match) {
|
|
131
|
+
machineId = crypto.createHash('sha256').update(match[1]).digest('hex').substring(0, 32);
|
|
132
|
+
}
|
|
133
|
+
} catch {}
|
|
134
|
+
} else if (isLinux) {
|
|
135
|
+
try {
|
|
136
|
+
if (fs.existsSync('/etc/machine-id')) {
|
|
137
|
+
const mid = fs.readFileSync('/etc/machine-id', 'utf-8').trim();
|
|
138
|
+
machineId = crypto.createHash('sha256').update(mid).digest('hex').substring(0, 32);
|
|
139
|
+
}
|
|
140
|
+
} catch {}
|
|
141
|
+
}
|
|
142
|
+
} catch (e) {}
|
|
143
|
+
|
|
144
|
+
if (!machineId) {
|
|
145
|
+
const seed = `${os.hostname()}-${os.userInfo().username}-${Date.now()}-${crypto.randomBytes(8).toString('hex')}`;
|
|
146
|
+
machineId = crypto.createHash('sha256').update(seed).digest('hex').substring(0, 32);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
ensureConfigDir();
|
|
150
|
+
fs.writeFileSync(MACHINE_ID_FILE, machineId, { mode: 0o600 });
|
|
151
|
+
return machineId;
|
|
108
152
|
}
|
|
109
153
|
|
|
110
|
-
function
|
|
111
|
-
|
|
154
|
+
function getMachineName() {
|
|
155
|
+
return os.hostname();
|
|
112
156
|
}
|
|
113
157
|
|
|
114
|
-
//
|
|
158
|
+
// ============================================
|
|
159
|
+
// Config management
|
|
160
|
+
// ============================================
|
|
161
|
+
function ensureConfigDir() {
|
|
162
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
163
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
115
166
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
167
|
+
function saveConfig(config) {
|
|
168
|
+
ensureConfigDir();
|
|
169
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
170
|
+
logSuccess(`Configuration saved to: ${CONFIG_FILE}`);
|
|
171
|
+
}
|
|
120
172
|
|
|
121
|
-
|
|
173
|
+
function loadConfig() {
|
|
174
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
175
|
+
try {
|
|
176
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
177
|
+
} catch (error) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
122
183
|
|
|
123
|
-
|
|
124
|
-
|
|
184
|
+
function isConfigured() {
|
|
185
|
+
const config = loadConfig();
|
|
186
|
+
return config && config.authorizationToken && config.tenantId;
|
|
187
|
+
}
|
|
125
188
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
189
|
+
// ============================================
|
|
190
|
+
// Permission definitions
|
|
191
|
+
// ============================================
|
|
192
|
+
const PERMISSIONS = [
|
|
193
|
+
{ id: 'read_files', name: 'Read Files', description: 'Read source code files in your workspaces', required: true },
|
|
194
|
+
{ id: 'write_config', name: 'Write Configuration', description: 'Create configuration files in ~/.deepdebug', required: true },
|
|
195
|
+
{ id: 'send_snippets', name: 'Send Code Snippets', description: 'Send code snippets to DeepDebug for AI analysis (encrypted)', required: true },
|
|
196
|
+
{ id: 'execute_build', name: 'Execute Build Commands', description: 'Run build commands (maven, npm, gradle)', required: false },
|
|
197
|
+
{ id: 'apply_patches', name: 'Apply Code Patches', description: 'Apply AI-suggested code fixes to your files', required: false },
|
|
198
|
+
{ id: 'git_operations', name: 'Git Operations', description: 'Perform git operations (commit, branch)', required: false },
|
|
199
|
+
{ id: 'create_pr', name: 'Create Pull Requests', description: 'Create pull requests with suggested fixes', required: false }
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
// ============================================
|
|
203
|
+
// Authorization display
|
|
204
|
+
// ============================================
|
|
205
|
+
function displayAuthorizationTerms() {
|
|
206
|
+
console.log();
|
|
207
|
+
log('╔══════════════════════════════════════════════════════════════════╗', 'yellow');
|
|
208
|
+
log('║ AUTHORIZATION REQUEST ║', 'yellow');
|
|
209
|
+
log('╚══════════════════════════════════════════════════════════════════╝', 'yellow');
|
|
210
|
+
console.log();
|
|
211
|
+
log('The DeepDebug Local Agent needs your permission to:', 'bright');
|
|
212
|
+
console.log();
|
|
213
|
+
log('Required permissions:', 'cyan');
|
|
214
|
+
PERMISSIONS.filter(p => p.required).forEach(p => {
|
|
215
|
+
log(` ✓ ${p.name}`, 'green');
|
|
216
|
+
log(` ${p.description}`, 'reset');
|
|
217
|
+
});
|
|
218
|
+
console.log();
|
|
219
|
+
log('Optional permissions (you can enable/disable later):', 'cyan');
|
|
220
|
+
PERMISSIONS.filter(p => !p.required).forEach(p => {
|
|
221
|
+
log(` ○ ${p.name}`, 'yellow');
|
|
222
|
+
log(` ${p.description}`, 'reset');
|
|
223
|
+
});
|
|
224
|
+
console.log();
|
|
225
|
+
log('Security notes:', 'magenta');
|
|
226
|
+
log(' • Your code is only analyzed when errors occur', 'reset');
|
|
227
|
+
log(' • Code snippets are encrypted in transit (TLS 1.3)', 'reset');
|
|
228
|
+
log(' • You can revoke this authorization at any time', 'reset');
|
|
229
|
+
log(' • Full audit trail is maintained in your dashboard', 'reset');
|
|
230
|
+
console.log();
|
|
231
|
+
log('Privacy Policy: https://deepdebug.ai/privacy', 'blue');
|
|
232
|
+
log('Terms of Service: https://deepdebug.ai/terms', 'blue');
|
|
233
|
+
console.log();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function selectPermissions() {
|
|
237
|
+
const granted = PERMISSIONS.filter(p => p.required).map(p => p.id);
|
|
238
|
+
console.log();
|
|
239
|
+
log('Select optional permissions:', 'cyan');
|
|
240
|
+
console.log();
|
|
241
|
+
for (const perm of PERMISSIONS.filter(p => !p.required)) {
|
|
242
|
+
const enable = await promptYesNo(` Enable "${perm.name}"?`, false);
|
|
243
|
+
if (enable) granted.push(perm.id);
|
|
139
244
|
}
|
|
245
|
+
return granted;
|
|
246
|
+
}
|
|
140
247
|
|
|
141
|
-
|
|
142
|
-
|
|
248
|
+
// ============================================
|
|
249
|
+
// API functions
|
|
250
|
+
// ============================================
|
|
251
|
+
async function authorizeWithServer(apiKey, permissions) {
|
|
252
|
+
const machineId = getMachineId();
|
|
253
|
+
const machineName = getMachineName();
|
|
254
|
+
const osType = getOsType();
|
|
255
|
+
const osVersion = getOsVersion();
|
|
256
|
+
const agentVersion = getAgentVersion();
|
|
143
257
|
|
|
144
|
-
|
|
145
|
-
ok(`Configuration saved to ${CONFIG_FILE}`);
|
|
258
|
+
logInfo('Requesting authorization from DeepDebug server...');
|
|
146
259
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
260
|
+
try {
|
|
261
|
+
const response = await fetch(`${API_BASE_URL}/api/v1/agent/authorize`, {
|
|
262
|
+
method: 'POST',
|
|
263
|
+
headers: { 'Content-Type': 'application/json' },
|
|
264
|
+
body: JSON.stringify({
|
|
265
|
+
apiKey,
|
|
266
|
+
machineId,
|
|
267
|
+
machineName,
|
|
268
|
+
osType,
|
|
269
|
+
osVersion,
|
|
270
|
+
permissions,
|
|
271
|
+
workspacePaths: [], // Empty - workspaces configured in dashboard
|
|
272
|
+
agentVersion
|
|
273
|
+
})
|
|
274
|
+
});
|
|
275
|
+
const data = await response.json();
|
|
276
|
+
if (data.success) {
|
|
277
|
+
return { success: true, ...data };
|
|
156
278
|
}
|
|
157
|
-
|
|
158
|
-
|
|
279
|
+
return { success: false, error: data.message || 'Authorization failed' };
|
|
280
|
+
} catch (error) {
|
|
281
|
+
return { success: false, error: `Failed to connect: ${error.message}` };
|
|
159
282
|
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function validateAuthorization(token, machineId) {
|
|
286
|
+
try {
|
|
287
|
+
const response = await fetch(`${API_BASE_URL}/api/v1/agent/validate`, {
|
|
288
|
+
method: 'POST',
|
|
289
|
+
headers: { 'Content-Type': 'application/json' },
|
|
290
|
+
body: JSON.stringify({ authorizationToken: token, machineId })
|
|
291
|
+
});
|
|
292
|
+
return await response.json();
|
|
293
|
+
} catch (error) {
|
|
294
|
+
return { valid: false, message: error.message };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
160
297
|
|
|
161
|
-
|
|
298
|
+
function getAgentVersion() {
|
|
299
|
+
try {
|
|
300
|
+
const pkgPath = path.join(path.dirname(new URL(import.meta.url).pathname), '..', 'package.json');
|
|
301
|
+
return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version || '1.0.0';
|
|
302
|
+
} catch { return '1.0.0'; }
|
|
303
|
+
}
|
|
162
304
|
|
|
163
|
-
|
|
164
|
-
|
|
305
|
+
// ============================================
|
|
306
|
+
// Docker functions
|
|
307
|
+
// ============================================
|
|
308
|
+
function isDockerInstalled() {
|
|
309
|
+
try { execSync('docker --version', { stdio: 'ignore' }); return true; } catch { return false; }
|
|
310
|
+
}
|
|
165
311
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
console.log('');
|
|
169
|
-
log(`Health check: ${bold(`http://localhost:${cfg.port}/health`)}`);
|
|
170
|
-
log(`Platform: ${bold('https://app.insptech.pt')}`);
|
|
171
|
-
console.log('');
|
|
312
|
+
function isDockerRunning() {
|
|
313
|
+
try { execSync('docker info', { stdio: 'ignore' }); return true; } catch { return false; }
|
|
172
314
|
}
|
|
173
315
|
|
|
174
|
-
|
|
175
|
-
|
|
316
|
+
function isAgentContainerRunning() {
|
|
317
|
+
try {
|
|
318
|
+
const result = execSync('docker ps --filter "name=deepdebug-agent" --format "{{.Names}}"', { encoding: 'utf-8' });
|
|
319
|
+
return result.trim() === 'deepdebug-agent';
|
|
320
|
+
} catch { return false; }
|
|
321
|
+
}
|
|
176
322
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
323
|
+
function startDockerAgent(config) {
|
|
324
|
+
try {
|
|
325
|
+
try { execSync('docker stop deepdebug-agent', { stdio: 'ignore' }); execSync('docker rm deepdebug-agent', { stdio: 'ignore' }); } catch {}
|
|
326
|
+
logInfo('Pulling latest DeepDebug Local Agent image...');
|
|
327
|
+
execSync(`docker pull ${DOCKER_IMAGE}`, { stdio: 'inherit' });
|
|
328
|
+
|
|
329
|
+
let configMount = CONFIG_DIR;
|
|
330
|
+
if (isWindows) {
|
|
331
|
+
configMount = configMount.replace(/\\/g, '/').replace(/^([A-Za-z]):/, '/$1');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const dockerArgs = [
|
|
335
|
+
'run', '-d', '--name', 'deepdebug-agent', '-p', '5055:5055',
|
|
336
|
+
'-v', `${configMount}:/home/deepdebug/.deepdebug:ro`,
|
|
337
|
+
'-e', `DEEPDEBUG_GATEWAY_URL=${config.gatewayUrl}`,
|
|
338
|
+
'-e', `DEEPDEBUG_TENANT_ID=${config.tenantId}`,
|
|
339
|
+
'-e', `DEEPDEBUG_AUTH_TOKEN=${config.authorizationToken}`,
|
|
340
|
+
'--restart', 'unless-stopped', DOCKER_IMAGE
|
|
341
|
+
];
|
|
342
|
+
execSync(`docker ${dockerArgs.join(' ')}`, { stdio: 'inherit' });
|
|
343
|
+
logSuccess('DeepDebug Local Agent started successfully!');
|
|
344
|
+
return true;
|
|
345
|
+
} catch (error) {
|
|
346
|
+
logError(`Failed to start Docker container: ${error.message}`);
|
|
347
|
+
return false;
|
|
181
348
|
}
|
|
349
|
+
}
|
|
182
350
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
351
|
+
// ============================================
|
|
352
|
+
// Commands
|
|
353
|
+
// ============================================
|
|
354
|
+
async function install(options = {}) {
|
|
355
|
+
console.log();
|
|
356
|
+
log('╔══════════════════════════════════════════════════════════════════╗', 'magenta');
|
|
357
|
+
log('║ DeepDebug Local Agent - Installation ║', 'magenta');
|
|
358
|
+
log('╚══════════════════════════════════════════════════════════════════╝', 'magenta');
|
|
359
|
+
console.log();
|
|
360
|
+
logInfo(`Detected OS: ${getOsType()} (${getOsVersion()})`);
|
|
361
|
+
logInfo(`Machine: ${getMachineName()}`);
|
|
362
|
+
console.log();
|
|
363
|
+
|
|
364
|
+
if (isConfigured() && !options.force) {
|
|
365
|
+
const config = loadConfig();
|
|
366
|
+
logInfo(`Already configured for tenant: ${config.tenantName || config.tenantId}`);
|
|
367
|
+
if (!await promptYesNo('Do you want to reconfigure?', false)) {
|
|
368
|
+
logInfo('Run "npx deepdebug-local-agent start" to start the agent.');
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
187
371
|
}
|
|
188
372
|
|
|
189
|
-
|
|
373
|
+
let apiKey = options.apiKey;
|
|
374
|
+
if (!apiKey) {
|
|
375
|
+
console.log();
|
|
376
|
+
log('┌────────────────────────────────────────────────────────────────┐', 'cyan');
|
|
377
|
+
log('│ Find your API Key: DeepDebug Dashboard → Settings → Agent │', 'cyan');
|
|
378
|
+
log('└────────────────────────────────────────────────────────────────┘', 'cyan');
|
|
379
|
+
console.log();
|
|
380
|
+
apiKey = await prompt('Enter your API Key: ');
|
|
381
|
+
}
|
|
382
|
+
if (!apiKey) { logError('API Key is required'); process.exit(1); }
|
|
190
383
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
384
|
+
displayAuthorizationTerms();
|
|
385
|
+
if (!await promptYesNo('Do you authorize the DeepDebug Local Agent with these permissions?', false)) {
|
|
386
|
+
logWarning('Authorization declined. Installation cancelled.');
|
|
387
|
+
process.exit(0);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const permissions = await selectPermissions();
|
|
391
|
+
logInfo(`Selected permissions: ${permissions.join(', ')}`);
|
|
392
|
+
|
|
393
|
+
console.log();
|
|
394
|
+
const authResult = await authorizeWithServer(apiKey, permissions);
|
|
395
|
+
if (!authResult.success) { logError(`Authorization failed: ${authResult.error}`); process.exit(1); }
|
|
396
|
+
logSuccess(`Authorized for tenant: ${authResult.tenantName}`);
|
|
397
|
+
|
|
398
|
+
const config = {
|
|
399
|
+
authorizationToken: authResult.authorizationToken,
|
|
400
|
+
authorizationId: authResult.authorizationId,
|
|
401
|
+
tenantId: authResult.tenantId,
|
|
402
|
+
tenantName: authResult.tenantName,
|
|
403
|
+
gatewayUrl: authResult.gatewayUrl,
|
|
404
|
+
websocketUrl: authResult.websocketUrl,
|
|
405
|
+
permissions,
|
|
406
|
+
machineId: getMachineId(),
|
|
407
|
+
machineName: getMachineName(),
|
|
408
|
+
osType: getOsType(),
|
|
409
|
+
installedAt: new Date().toISOString(),
|
|
410
|
+
expiresAt: authResult.expiresAt
|
|
197
411
|
};
|
|
412
|
+
saveConfig(config);
|
|
413
|
+
|
|
414
|
+
console.log();
|
|
415
|
+
logInfo('Checking Docker installation...');
|
|
416
|
+
if (!isDockerInstalled()) {
|
|
417
|
+
logWarning('Docker is not installed.');
|
|
418
|
+
log(`Please install Docker Desktop: https://www.docker.com/products/docker-desktop/`, 'blue');
|
|
419
|
+
logInfo('After installing, run: npx deepdebug-local-agent start');
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (!isDockerRunning()) {
|
|
423
|
+
logWarning('Docker is not running. Please start Docker Desktop.');
|
|
424
|
+
logInfo('Then run: npx deepdebug-local-agent start');
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
logSuccess('Docker is installed and running');
|
|
198
428
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
429
|
+
console.log();
|
|
430
|
+
if (await promptYesNo('Start DeepDebug Local Agent now?', true)) {
|
|
431
|
+
await start();
|
|
432
|
+
} else {
|
|
433
|
+
logInfo('To start later: npx deepdebug-local-agent start');
|
|
434
|
+
}
|
|
205
435
|
|
|
206
|
-
|
|
436
|
+
console.log();
|
|
437
|
+
log('╔══════════════════════════════════════════════════════════════════╗', 'green');
|
|
438
|
+
log('║ Installation Complete! 🎉 ║', 'green');
|
|
439
|
+
log('╚══════════════════════════════════════════════════════════════════╝', 'green');
|
|
440
|
+
console.log();
|
|
441
|
+
logInfo('Workspaces can be configured in the DeepDebug dashboard.');
|
|
442
|
+
log('Dashboard: https://deepdebug.ai/settings/agent', 'blue');
|
|
443
|
+
console.log();
|
|
444
|
+
}
|
|
207
445
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
446
|
+
async function start() {
|
|
447
|
+
console.log();
|
|
448
|
+
log('🚀 Starting DeepDebug Local Agent...', 'cyan');
|
|
449
|
+
const config = loadConfig();
|
|
450
|
+
if (!config?.authorizationToken) { logError('Not configured. Run "npx deepdebug-local-agent install" first.'); process.exit(1); }
|
|
211
451
|
|
|
212
|
-
|
|
213
|
-
|
|
452
|
+
logInfo('Validating authorization...');
|
|
453
|
+
const validation = await validateAuthorization(config.authorizationToken, config.machineId);
|
|
454
|
+
if (!validation.valid) { logError(`Authorization invalid: ${validation.message}`); process.exit(1); }
|
|
455
|
+
logSuccess('Authorization valid');
|
|
214
456
|
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
457
|
+
if (!isDockerInstalled()) { logError('Docker is not installed.'); process.exit(1); }
|
|
458
|
+
if (!isDockerRunning()) { logError('Docker is not running.'); process.exit(1); }
|
|
459
|
+
|
|
460
|
+
if (isAgentContainerRunning()) {
|
|
461
|
+
logWarning('Agent is already running.');
|
|
462
|
+
if (!await promptYesNo('Restart it?', false)) return;
|
|
219
463
|
}
|
|
220
|
-
}
|
|
221
464
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
ok(`Agent stopped (PID ${pid})`);
|
|
229
|
-
} catch (e) {
|
|
230
|
-
err(`Failed to stop agent: ${e.message}`);
|
|
465
|
+
if (startDockerAgent(config)) {
|
|
466
|
+
console.log();
|
|
467
|
+
logSuccess('Agent is running!');
|
|
468
|
+
logInfo('Dashboard: https://deepdebug.ai/monitoring');
|
|
469
|
+
logInfo('Local API: http://localhost:5055/health');
|
|
470
|
+
logInfo('Logs: docker logs -f deepdebug-agent');
|
|
231
471
|
}
|
|
232
472
|
}
|
|
233
473
|
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
474
|
+
async function stop() {
|
|
475
|
+
log('🛑 Stopping DeepDebug Local Agent...', 'yellow');
|
|
476
|
+
try { execSync('docker stop deepdebug-agent', { stdio: 'inherit' }); logSuccess('Agent stopped.'); }
|
|
477
|
+
catch { logWarning('Agent is not running.'); }
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async function status() {
|
|
481
|
+
console.log();
|
|
482
|
+
log('╔══════════════════════════════════════════════════════════════════╗', 'cyan');
|
|
483
|
+
log('║ DeepDebug Local Agent - Status ║', 'cyan');
|
|
484
|
+
log('╚══════════════════════════════════════════════════════════════════╝', 'cyan');
|
|
485
|
+
console.log();
|
|
486
|
+
logInfo(`OS: ${getOsType()} | Machine: ${getMachineName()}`);
|
|
487
|
+
|
|
488
|
+
const config = loadConfig();
|
|
489
|
+
if (config) {
|
|
490
|
+
logSuccess(`Configured for: ${config.tenantName || config.tenantId}`);
|
|
491
|
+
logInfo(`Permissions: ${config.permissions?.join(', ') || 'N/A'}`);
|
|
492
|
+
} else {
|
|
493
|
+
logWarning('Not configured. Run: npx deepdebug-local-agent install');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (isDockerInstalled() && isDockerRunning() && isAgentContainerRunning()) {
|
|
497
|
+
logSuccess('Agent: Running');
|
|
498
|
+
try {
|
|
499
|
+
const resp = await fetch('http://localhost:5055/health');
|
|
500
|
+
logSuccess(resp.ok ? 'Health: OK' : 'Health: Degraded');
|
|
501
|
+
} catch { logWarning('Health: Cannot connect'); }
|
|
243
502
|
} else {
|
|
244
|
-
|
|
503
|
+
logWarning('Agent: Not running');
|
|
245
504
|
}
|
|
246
|
-
console.log(
|
|
247
|
-
console.log(` Config : ${CONFIG_FILE}`);
|
|
248
|
-
console.log(` Gateway : ${cfg.gatewayUrl || DEFAULT_GATEWAY}`);
|
|
249
|
-
console.log(` Health : http://localhost:${port}/health`);
|
|
250
|
-
console.log('');
|
|
505
|
+
console.log();
|
|
251
506
|
}
|
|
252
507
|
|
|
253
|
-
function
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
log(`Running in foreground on port ${cfg.port || DEFAULT_PORT} (Ctrl+C to stop)...`);
|
|
265
|
-
const child = spawn(process.execPath, [serverScript], { env, stdio: 'inherit', cwd: ROOT_DIR });
|
|
266
|
-
child.on('exit', code => process.exit(code || 0));
|
|
508
|
+
async function logs() {
|
|
509
|
+
spawn('docker', ['logs', '-f', 'deepdebug-agent'], { stdio: 'inherit' });
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function uninstall() {
|
|
513
|
+
if (!await promptYesNo('Uninstall DeepDebug Local Agent?', false)) return;
|
|
514
|
+
try { execSync('docker stop deepdebug-agent', { stdio: 'ignore' }); execSync('docker rm deepdebug-agent', { stdio: 'ignore' }); } catch {}
|
|
515
|
+
if (fs.existsSync(CONFIG_FILE)) fs.unlinkSync(CONFIG_FILE);
|
|
516
|
+
if (fs.existsSync(MACHINE_ID_FILE)) fs.unlinkSync(MACHINE_ID_FILE);
|
|
517
|
+
try { fs.rmdirSync(CONFIG_DIR); } catch {}
|
|
518
|
+
logSuccess('DeepDebug Local Agent uninstalled.');
|
|
267
519
|
}
|
|
268
520
|
|
|
269
|
-
|
|
521
|
+
function showHelp() {
|
|
522
|
+
console.log(`
|
|
523
|
+
DeepDebug Local Agent - AI-powered debugging assistant
|
|
524
|
+
|
|
525
|
+
Usage: npx deepdebug-local-agent <command>
|
|
526
|
+
|
|
527
|
+
Commands:
|
|
528
|
+
install Configure and authorize the agent
|
|
529
|
+
start Start the agent
|
|
530
|
+
stop Stop the agent
|
|
531
|
+
status Show agent status
|
|
532
|
+
logs Show agent logs
|
|
533
|
+
uninstall Remove the agent
|
|
534
|
+
|
|
535
|
+
Options:
|
|
536
|
+
--api-key=KEY Provide API key
|
|
537
|
+
--force Force reconfiguration
|
|
538
|
+
--help Show this help
|
|
539
|
+
|
|
540
|
+
Documentation: https://docs.deepdebug.ai
|
|
541
|
+
`);
|
|
542
|
+
}
|
|
270
543
|
|
|
271
|
-
|
|
544
|
+
// ============================================
|
|
545
|
+
// Main
|
|
546
|
+
// ============================================
|
|
547
|
+
const args = process.argv.slice(2);
|
|
548
|
+
const command = args[0];
|
|
549
|
+
const options = {};
|
|
550
|
+
args.forEach(arg => {
|
|
551
|
+
if (arg.startsWith('--api-key=')) options.apiKey = arg.split('=')[1];
|
|
552
|
+
if (arg === '--force') options.force = true;
|
|
553
|
+
});
|
|
272
554
|
|
|
273
555
|
switch (command) {
|
|
274
|
-
case 'install':
|
|
275
|
-
case 'start':
|
|
276
|
-
case 'stop':
|
|
277
|
-
case 'status':
|
|
278
|
-
case '
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
556
|
+
case 'install': install(options); break;
|
|
557
|
+
case 'start': start(); break;
|
|
558
|
+
case 'stop': stop(); break;
|
|
559
|
+
case 'status': status(); break;
|
|
560
|
+
case 'logs': logs(); break;
|
|
561
|
+
case 'uninstall': uninstall(); break;
|
|
562
|
+
case 'help': case '--help': case '-h': showHelp(); break;
|
|
563
|
+
default: if (command) logError(`Unknown command: ${command}`); showHelp();
|
|
282
564
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepdebug-local-agent",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "DeepDebug Local Agent - AI-powered code debugging assistant",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -12,34 +12,27 @@
|
|
|
12
12
|
"start": "node src/server.js",
|
|
13
13
|
"dev": "NODE_ENV=development node src/server.js",
|
|
14
14
|
"mcp": "node src/mcp-server.js",
|
|
15
|
+
"bundle": "esbuild src/server.js --bundle --platform=node --format=cjs --outfile=dist/bundle.cjs --external:@anthropic-ai/sdk",
|
|
15
16
|
"build": "npm run build:all",
|
|
16
|
-
"build:all": "npm run build:win && npm run build:mac && npm run build:linux",
|
|
17
|
-
"build:win": "pkg . --target
|
|
18
|
-
"build:mac": "pkg . --target
|
|
19
|
-
"build:mac-arm": "pkg . --target
|
|
20
|
-
"build:linux": "pkg . --target
|
|
17
|
+
"build:all": "npm run build:win && npm run build:mac && npm run build:mac-arm && npm run build:linux",
|
|
18
|
+
"build:win": "npm run bundle && pkg dist/bundle.cjs --target node18-win-x64 --output dist/deepdebug-agent-win.exe",
|
|
19
|
+
"build:mac": "npm run bundle && pkg dist/bundle.cjs --target node18-macos-x64 --output dist/deepdebug-agent-macos",
|
|
20
|
+
"build:mac-arm": "npm run bundle && pkg dist/bundle.cjs --target node18-macos-arm64 --output dist/deepdebug-agent-macos-arm64",
|
|
21
|
+
"build:linux": "npm run bundle && pkg dist/bundle.cjs --target node18-linux-x64 --output dist/deepdebug-agent-linux",
|
|
21
22
|
"docker:build": "docker build -t deepdebug/local-agent:latest .",
|
|
22
23
|
"docker:push": "docker push deepdebug/local-agent:latest",
|
|
23
24
|
"test": "node --test",
|
|
24
25
|
"lint": "eslint src/"
|
|
25
26
|
},
|
|
26
27
|
"pkg": {
|
|
27
|
-
"scripts": [
|
|
28
|
-
"src/**/*.js"
|
|
29
|
-
],
|
|
30
28
|
"assets": [
|
|
31
|
-
"
|
|
32
|
-
],
|
|
33
|
-
"targets": [
|
|
34
|
-
"node20-win-x64",
|
|
35
|
-
"node20-macos-x64",
|
|
36
|
-
"node20-macos-arm64",
|
|
37
|
-
"node20-linux-x64"
|
|
29
|
+
"node_modules/@anthropic-ai/**/*"
|
|
38
30
|
],
|
|
39
31
|
"outputPath": "dist"
|
|
40
32
|
},
|
|
41
33
|
"dependencies": {
|
|
42
|
-
"@
|
|
34
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
43
36
|
"body-parser": "^1.20.3",
|
|
44
37
|
"cors": "^2.8.5",
|
|
45
38
|
"express": "^4.19.2",
|
|
@@ -47,9 +40,11 @@
|
|
|
47
40
|
"pidusage": "^3.0.2",
|
|
48
41
|
"properties-reader": "^2.3.0",
|
|
49
42
|
"strip-ansi": "^7.1.0",
|
|
50
|
-
"unidiff": "^1.0.2"
|
|
43
|
+
"unidiff": "^1.0.2",
|
|
44
|
+
"ws": "^8.18.0"
|
|
51
45
|
},
|
|
52
46
|
"devDependencies": {
|
|
47
|
+
"esbuild": "^0.20.0",
|
|
53
48
|
"pkg": "^5.8.1",
|
|
54
49
|
"eslint": "^8.57.0"
|
|
55
50
|
},
|