deepdebug-local-agent 1.0.15 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/server.js +158 -0
- package/src/setup-agent-routes.js +368 -0
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -5539,8 +5539,166 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5539
5539
|
connect();
|
|
5540
5540
|
}
|
|
5541
5541
|
|
|
5542
|
+
// ============================================
|
|
5543
|
+
// 🤖 SETUP AGENT ROUTES
|
|
5544
|
+
// Supports the AI Assistant chat commands
|
|
5545
|
+
// ============================================
|
|
5546
|
+
|
|
5547
|
+
/**
|
|
5548
|
+
* POST /setup/run-command
|
|
5549
|
+
* Run any shell command on the user's machine.
|
|
5550
|
+
*/
|
|
5551
|
+
app.post("/setup/run-command", async (req, res) => {
|
|
5552
|
+
const { command, description, workingDir } = req.body || {};
|
|
5553
|
+
if (!command) return res.status(400).json({ ok: false, error: "command is required" });
|
|
5554
|
+
|
|
5555
|
+
const dangerous = ['rm -rf /', 'mkfs', 'dd if=', 'shutdown', 'reboot', 'format c:', 'del /f /s /q'];
|
|
5556
|
+
if (dangerous.some(d => command.toLowerCase().includes(d))) {
|
|
5557
|
+
return res.status(403).json({ ok: false, error: "Command blocked for safety" });
|
|
5558
|
+
}
|
|
5559
|
+
|
|
5560
|
+
console.log(`🤖 [SetupAgent] Running: ${command.substring(0, 100)}`);
|
|
5561
|
+
const { exec: execCb } = await import('child_process');
|
|
5562
|
+
const { promisify } = await import('util');
|
|
5563
|
+
const execAsync = promisify(execCb);
|
|
5564
|
+
|
|
5565
|
+
try {
|
|
5566
|
+
const cwd = workingDir || WORKSPACE_ROOT || process.env.HOME || process.cwd();
|
|
5567
|
+
const result = await execAsync(command, {
|
|
5568
|
+
cwd, timeout: 60000, maxBuffer: 2 * 1024 * 1024,
|
|
5569
|
+
env: { ...process.env, FORCE_COLOR: '0' }, shell: true
|
|
5570
|
+
});
|
|
5571
|
+
res.json({ ok: true, stdout: result.stdout, stderr: result.stderr, exitCode: 0, command });
|
|
5572
|
+
} catch (err) {
|
|
5573
|
+
res.json({ ok: false, stdout: err.stdout || '', stderr: err.stderr || err.message, exitCode: err.code || 1, command });
|
|
5574
|
+
}
|
|
5575
|
+
});
|
|
5576
|
+
|
|
5577
|
+
/**
|
|
5578
|
+
* POST /setup/check-ports
|
|
5579
|
+
* Check which ports are open/in-use on this machine.
|
|
5580
|
+
*/
|
|
5581
|
+
app.post("/setup/check-ports", async (req, res) => {
|
|
5582
|
+
const { exec: execCb } = await import('child_process');
|
|
5583
|
+
const { promisify } = await import('util');
|
|
5584
|
+
const execAsync = promisify(execCb);
|
|
5585
|
+
|
|
5586
|
+
const cmd = process.platform === 'win32'
|
|
5587
|
+
? 'netstat -ano | findstr LISTENING'
|
|
5588
|
+
: 'lsof -i -P -n 2>/dev/null | grep LISTEN | head -40 || netstat -tlnp 2>/dev/null | head -40 || ss -tlnp 2>/dev/null | head -40';
|
|
5589
|
+
|
|
5590
|
+
try {
|
|
5591
|
+
const result = await execAsync(cmd, { timeout: 10000, shell: true });
|
|
5592
|
+
res.json({ ok: true, output: result.stdout, ports_raw: result.stdout });
|
|
5593
|
+
} catch (err) {
|
|
5594
|
+
res.json({ ok: false, stdout: err.stdout || '', error: err.message });
|
|
5595
|
+
}
|
|
5596
|
+
});
|
|
5597
|
+
|
|
5598
|
+
/**
|
|
5599
|
+
* GET /setup/check-tool?name=<tool>
|
|
5600
|
+
* Check if a CLI tool is installed on this machine.
|
|
5601
|
+
*/
|
|
5602
|
+
app.get("/setup/check-tool", async (req, res) => {
|
|
5603
|
+
const { name } = req.query;
|
|
5604
|
+
if (!name) return res.status(400).json({ error: "name is required" });
|
|
5605
|
+
|
|
5606
|
+
const { exec: execCb } = await import('child_process');
|
|
5607
|
+
const { promisify } = await import('util');
|
|
5608
|
+
const execAsync = promisify(execCb);
|
|
5609
|
+
|
|
5610
|
+
const cmd = process.platform === 'win32'
|
|
5611
|
+
? `where ${name} 2>nul && ${name} --version 2>nul`
|
|
5612
|
+
: `which ${name} 2>/dev/null && ${name} --version 2>&1 | head -1`;
|
|
5613
|
+
|
|
5614
|
+
try {
|
|
5615
|
+
const result = await execAsync(cmd, { timeout: 8000, shell: true });
|
|
5616
|
+
res.json({ installed: true, tool: name, version: result.stdout.trim() });
|
|
5617
|
+
} catch (err) {
|
|
5618
|
+
res.json({ installed: false, tool: name, output: err.stdout || err.message });
|
|
5619
|
+
}
|
|
5620
|
+
});
|
|
5621
|
+
|
|
5622
|
+
/**
|
|
5623
|
+
* POST /setup/install-tool
|
|
5624
|
+
* Auto-install a CLI tool using the available package manager.
|
|
5625
|
+
*/
|
|
5626
|
+
app.post("/setup/install-tool", async (req, res) => {
|
|
5627
|
+
const { tool } = req.body || {};
|
|
5628
|
+
if (!tool) return res.status(400).json({ error: "tool is required" });
|
|
5629
|
+
|
|
5630
|
+
const { exec: execCb } = await import('child_process');
|
|
5631
|
+
const { promisify } = await import('util');
|
|
5632
|
+
const execAsync = promisify(execCb);
|
|
5633
|
+
|
|
5634
|
+
const brewNames = { 'azure-cli': 'azure-cli', 'github-cli': 'gh', 'aws-cli': 'awscli', 'gcloud': 'google-cloud-sdk' };
|
|
5635
|
+
const brewName = brewNames[tool] || tool;
|
|
5636
|
+
const cmd = process.platform === 'win32'
|
|
5637
|
+
? `choco install ${tool} -y 2>&1`
|
|
5638
|
+
: `brew install ${brewName} 2>&1 || sudo apt-get install -y ${tool} 2>&1`;
|
|
5639
|
+
|
|
5640
|
+
try {
|
|
5641
|
+
const result = await execAsync(cmd, { timeout: 120000, shell: true });
|
|
5642
|
+
res.json({ ok: true, stdout: result.stdout, tool });
|
|
5643
|
+
} catch (err) {
|
|
5644
|
+
res.json({ ok: false, stdout: err.stdout || '', error: err.message, tool });
|
|
5645
|
+
}
|
|
5646
|
+
});
|
|
5647
|
+
|
|
5648
|
+
/**
|
|
5649
|
+
* POST /setup/run-auth-command
|
|
5650
|
+
* Run an auth command (gcloud, az login, gh auth) opening browser on user's machine.
|
|
5651
|
+
*/
|
|
5652
|
+
app.post("/setup/run-auth-command", async (req, res) => {
|
|
5653
|
+
const { command, session_id } = req.body || {};
|
|
5654
|
+
if (!command) return res.status(400).json({ error: "command is required" });
|
|
5655
|
+
|
|
5656
|
+
const { exec: execCb } = await import('child_process');
|
|
5657
|
+
const bgCmd = process.platform === 'win32' ? `start cmd /c "${command}"` : `${command} &`;
|
|
5658
|
+
console.log(`🔐 [SetupAgent] Starting auth: ${command.substring(0, 80)}`);
|
|
5659
|
+
|
|
5660
|
+
execCb(bgCmd, { shell: true }, (err) => {
|
|
5661
|
+
if (err) console.warn(`⚠️ Auth command warning: ${err.message}`);
|
|
5662
|
+
});
|
|
5663
|
+
|
|
5664
|
+
res.json({ ok: true, status: "started", session_id, message: "Auth command started, browser should open" });
|
|
5665
|
+
});
|
|
5666
|
+
|
|
5667
|
+
/**
|
|
5668
|
+
* GET /setup/auth-status?provider=<provider>
|
|
5669
|
+
* Check if authentication completed for a provider.
|
|
5670
|
+
*/
|
|
5671
|
+
app.get("/setup/auth-status", async (req, res) => {
|
|
5672
|
+
const { provider } = req.query;
|
|
5673
|
+
const { exec: execCb } = await import('child_process');
|
|
5674
|
+
const { promisify } = await import('util');
|
|
5675
|
+
const execAsync = promisify(execCb);
|
|
5676
|
+
|
|
5677
|
+
const checkCmds = {
|
|
5678
|
+
gcp: "gcloud auth list --format='value(account)' 2>&1 | head -1",
|
|
5679
|
+
azure: "az account show --query name -o tsv 2>&1",
|
|
5680
|
+
aws_sso: "aws sts get-caller-identity --query Account -o text 2>&1",
|
|
5681
|
+
github: "gh auth status 2>&1",
|
|
5682
|
+
gitlab: "glab auth status 2>&1"
|
|
5683
|
+
};
|
|
5684
|
+
|
|
5685
|
+
const cmd = checkCmds[provider] || 'echo unknown_provider';
|
|
5686
|
+
try {
|
|
5687
|
+
const result = await execAsync(cmd, { timeout: 10000, shell: true });
|
|
5688
|
+
const output = result.stdout.trim();
|
|
5689
|
+
const success = output && !output.toLowerCase().includes('error') &&
|
|
5690
|
+
!output.toLowerCase().includes('not logged') &&
|
|
5691
|
+
!output.toLowerCase().includes('no account') &&
|
|
5692
|
+
output !== 'unknown_provider';
|
|
5693
|
+
res.json({ status: success ? 'success' : 'pending', provider, output });
|
|
5694
|
+
} catch (err) {
|
|
5695
|
+
res.json({ status: 'pending', provider, output: err.stdout || err.message });
|
|
5696
|
+
}
|
|
5697
|
+
});
|
|
5698
|
+
|
|
5542
5699
|
// ============================================
|
|
5543
5700
|
// START SERVER
|
|
5701
|
+
|
|
5544
5702
|
// ============================================
|
|
5545
5703
|
app.listen(PORT, '0.0.0.0', async () => {
|
|
5546
5704
|
console.log(`\n DeepDebug Local Agent listening on port ${PORT}`);
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* setup-agent-routes.js
|
|
3
|
+
*
|
|
4
|
+
* Local Agent routes for the Setup Agent.
|
|
5
|
+
* These endpoints are called by SetupAgentToolExecutor (Java backend)
|
|
6
|
+
* and execute on the user's local machine.
|
|
7
|
+
*
|
|
8
|
+
* Routes:
|
|
9
|
+
* POST /setup/run-auth-command — Run OAuth CLI command (opens browser)
|
|
10
|
+
* GET /setup/auth-status — Poll for OAuth completion
|
|
11
|
+
* POST /setup/run-command — Run a general setup command
|
|
12
|
+
* POST /setup/install-tool — Install a CLI tool (OS-aware)
|
|
13
|
+
* GET /setup/check-tool — Check if a tool is installed
|
|
14
|
+
*
|
|
15
|
+
* Mount in server.js with:
|
|
16
|
+
* import { setupAgentRouter } from './setup-agent-routes.js';
|
|
17
|
+
* app.use(setupAgentRouter);
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import express from 'express';
|
|
21
|
+
import { spawn, exec } from 'child_process';
|
|
22
|
+
import { promisify } from 'util';
|
|
23
|
+
import os from 'os';
|
|
24
|
+
|
|
25
|
+
const execAsync = promisify(exec);
|
|
26
|
+
const router = express.Router();
|
|
27
|
+
|
|
28
|
+
// Tracks running OAuth sessions: sessionId -> { provider, process, status, startedAt }
|
|
29
|
+
const oauthSessions = new Map();
|
|
30
|
+
|
|
31
|
+
// ============================================
|
|
32
|
+
// POST /setup/run-auth-command
|
|
33
|
+
// Run an OAuth CLI command asynchronously (opens browser)
|
|
34
|
+
// ============================================
|
|
35
|
+
|
|
36
|
+
router.post('/setup/run-auth-command', async (req, res) => {
|
|
37
|
+
const { command, description, session_id, async: isAsync } = req.body;
|
|
38
|
+
|
|
39
|
+
if (!command) {
|
|
40
|
+
return res.status(400).json({ error: 'command is required' });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(`[SetupAgent] Running auth command: ${command}`);
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
// Split command into parts
|
|
47
|
+
const parts = command.trim().split(/\s+/);
|
|
48
|
+
const cmd = parts[0];
|
|
49
|
+
const args = parts.slice(1);
|
|
50
|
+
|
|
51
|
+
const child = spawn(cmd, args, {
|
|
52
|
+
shell: true,
|
|
53
|
+
detached: false,
|
|
54
|
+
stdio: ['inherit', 'pipe', 'pipe'], // inherit stdin so browser auth works
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const session = {
|
|
58
|
+
provider: detectProviderFromCommand(command),
|
|
59
|
+
process: child,
|
|
60
|
+
status: 'pending',
|
|
61
|
+
output: [],
|
|
62
|
+
error: [],
|
|
63
|
+
startedAt: Date.now(),
|
|
64
|
+
command,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if (session_id) {
|
|
68
|
+
oauthSessions.set(session_id, session);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
child.stdout.on('data', (data) => {
|
|
72
|
+
const text = data.toString();
|
|
73
|
+
session.output.push(text);
|
|
74
|
+
console.log(`[SetupAgent][auth-stdout] ${text.trim()}`);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
child.stderr.on('data', (data) => {
|
|
78
|
+
const text = data.toString();
|
|
79
|
+
session.error.push(text);
|
|
80
|
+
console.log(`[SetupAgent][auth-stderr] ${text.trim()}`);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
child.on('close', (code) => {
|
|
84
|
+
session.status = code === 0 ? 'success' : 'failed';
|
|
85
|
+
session.exitCode = code;
|
|
86
|
+
console.log(`[SetupAgent] Auth command finished: code=${code} session=${session_id}`);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
child.on('error', (err) => {
|
|
90
|
+
session.status = 'failed';
|
|
91
|
+
session.lastError = err.message;
|
|
92
|
+
console.error(`[SetupAgent] Auth command error: ${err.message}`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// If async, respond immediately — client polls for status
|
|
96
|
+
if (isAsync) {
|
|
97
|
+
return res.json({
|
|
98
|
+
status: 'started',
|
|
99
|
+
session_id,
|
|
100
|
+
message: `${description || command} started — browser should open shortly`,
|
|
101
|
+
pid: child.pid,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Otherwise wait for completion (not recommended for auth flows)
|
|
106
|
+
child.on('close', (code) => {
|
|
107
|
+
res.json({
|
|
108
|
+
status: code === 0 ? 'success' : 'failed',
|
|
109
|
+
exit_code: code,
|
|
110
|
+
output: session.output.join(''),
|
|
111
|
+
error: session.error.join(''),
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.error(`[SetupAgent] Failed to start auth command: ${err.message}`);
|
|
117
|
+
res.status(500).json({ error: err.message });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// ============================================
|
|
122
|
+
// GET /setup/auth-status
|
|
123
|
+
// Poll for OAuth completion
|
|
124
|
+
// ============================================
|
|
125
|
+
|
|
126
|
+
router.get('/setup/auth-status', (req, res) => {
|
|
127
|
+
const { provider, session_id } = req.query;
|
|
128
|
+
|
|
129
|
+
if (!session_id || !oauthSessions.has(session_id)) {
|
|
130
|
+
return res.json({ status: 'unknown', message: 'Session not found' });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const session = oauthSessions.get(session_id);
|
|
134
|
+
const elapsed = Math.round((Date.now() - session.startedAt) / 1000);
|
|
135
|
+
|
|
136
|
+
res.json({
|
|
137
|
+
status: session.status, // pending | success | failed
|
|
138
|
+
provider: session.provider || provider,
|
|
139
|
+
session_id,
|
|
140
|
+
elapsed_seconds: elapsed,
|
|
141
|
+
output_preview: session.output.slice(-3).join('').trim().substring(0, 200),
|
|
142
|
+
message: session.status === 'success'
|
|
143
|
+
? 'Authentication completed successfully'
|
|
144
|
+
: session.status === 'failed'
|
|
145
|
+
? `Authentication failed: ${session.lastError || 'unknown error'}`
|
|
146
|
+
: `Waiting for browser authentication... (${elapsed}s)`,
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ============================================
|
|
151
|
+
// POST /setup/run-command
|
|
152
|
+
// Run a general setup command and wait for result
|
|
153
|
+
// ============================================
|
|
154
|
+
|
|
155
|
+
router.post('/setup/run-command', async (req, res) => {
|
|
156
|
+
const { command, description, working_directory } = req.body;
|
|
157
|
+
|
|
158
|
+
if (!command) {
|
|
159
|
+
return res.status(400).json({ error: 'command is required' });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(`[SetupAgent] Running command: ${command}`);
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const options = {
|
|
166
|
+
cwd: working_directory || process.cwd(),
|
|
167
|
+
timeout: 60000, // 60s timeout
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const { stdout, stderr } = await execAsync(command, options);
|
|
171
|
+
|
|
172
|
+
res.json({
|
|
173
|
+
status: 'success',
|
|
174
|
+
stdout: stdout.trim(),
|
|
175
|
+
stderr: stderr.trim(),
|
|
176
|
+
exit_code: 0,
|
|
177
|
+
command,
|
|
178
|
+
description,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error(`[SetupAgent] Command failed: ${command} — ${err.message}`);
|
|
183
|
+
res.json({
|
|
184
|
+
status: 'failed',
|
|
185
|
+
stdout: err.stdout ? err.stdout.trim() : '',
|
|
186
|
+
stderr: err.stderr ? err.stderr.trim() : err.message,
|
|
187
|
+
exit_code: err.code || 1,
|
|
188
|
+
command,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ============================================
|
|
194
|
+
// POST /setup/install-tool
|
|
195
|
+
// Install a CLI tool using the appropriate package manager
|
|
196
|
+
// ============================================
|
|
197
|
+
|
|
198
|
+
router.post('/setup/install-tool', async (req, res) => {
|
|
199
|
+
const { tool } = req.body;
|
|
200
|
+
|
|
201
|
+
if (!tool) {
|
|
202
|
+
return res.status(400).json({ error: 'tool is required' });
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const platform = os.platform(); // 'darwin', 'linux', 'win32'
|
|
206
|
+
const installCommand = getInstallCommand(tool, platform);
|
|
207
|
+
|
|
208
|
+
if (!installCommand) {
|
|
209
|
+
return res.json({
|
|
210
|
+
status: 'manual_required',
|
|
211
|
+
tool,
|
|
212
|
+
platform,
|
|
213
|
+
message: `Cannot auto-install ${tool} on ${platform}. Please install it manually.`,
|
|
214
|
+
install_url: getInstallUrl(tool),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
console.log(`[SetupAgent] Installing ${tool} on ${platform}: ${installCommand}`);
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
// For brew/apt installs that may take time, stream output
|
|
222
|
+
const { stdout, stderr } = await execAsync(installCommand, { timeout: 300000 }); // 5 min
|
|
223
|
+
|
|
224
|
+
res.json({
|
|
225
|
+
status: 'installed',
|
|
226
|
+
tool,
|
|
227
|
+
platform,
|
|
228
|
+
command_used: installCommand,
|
|
229
|
+
output: stdout.trim(),
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
} catch (err) {
|
|
233
|
+
res.json({
|
|
234
|
+
status: 'failed',
|
|
235
|
+
tool,
|
|
236
|
+
platform,
|
|
237
|
+
error: err.stderr || err.message,
|
|
238
|
+
install_url: getInstallUrl(tool),
|
|
239
|
+
message: `Auto-install failed. Please install ${tool} manually.`,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// ============================================
|
|
245
|
+
// GET /setup/check-tool
|
|
246
|
+
// Check if a CLI tool is installed
|
|
247
|
+
// ============================================
|
|
248
|
+
|
|
249
|
+
router.get('/setup/check-tool', async (req, res) => {
|
|
250
|
+
const { name } = req.query;
|
|
251
|
+
|
|
252
|
+
if (!name) {
|
|
253
|
+
return res.status(400).json({ error: 'name is required' });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const checkCommand = getVersionCommand(name);
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const { stdout } = await execAsync(checkCommand, { timeout: 5000 });
|
|
260
|
+
const version = stdout.trim().split('\n')[0]; // first line usually has version
|
|
261
|
+
|
|
262
|
+
res.json({
|
|
263
|
+
installed: true,
|
|
264
|
+
tool: name,
|
|
265
|
+
version,
|
|
266
|
+
message: `${name} is installed: ${version}`,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
} catch (err) {
|
|
270
|
+
res.json({
|
|
271
|
+
installed: false,
|
|
272
|
+
tool: name,
|
|
273
|
+
version: null,
|
|
274
|
+
message: `${name} is not installed`,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// ============================================
|
|
280
|
+
// Helpers
|
|
281
|
+
// ============================================
|
|
282
|
+
|
|
283
|
+
function detectProviderFromCommand(command) {
|
|
284
|
+
if (command.includes('gcloud')) return 'gcp';
|
|
285
|
+
if (command.includes('az ')) return 'azure';
|
|
286
|
+
if (command.includes('aws ')) return 'aws_sso';
|
|
287
|
+
if (command.includes('gh ')) return 'github';
|
|
288
|
+
if (command.includes('glab')) return 'gitlab';
|
|
289
|
+
return 'unknown';
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function getVersionCommand(tool) {
|
|
293
|
+
const commands = {
|
|
294
|
+
gcloud: 'gcloud --version',
|
|
295
|
+
'azure-cli': 'az --version',
|
|
296
|
+
'aws-cli': 'aws --version',
|
|
297
|
+
'github-cli': 'gh --version',
|
|
298
|
+
git: 'git --version',
|
|
299
|
+
node: 'node --version',
|
|
300
|
+
docker: 'docker --version',
|
|
301
|
+
brew: 'brew --version',
|
|
302
|
+
python: 'python3 --version',
|
|
303
|
+
java: 'java -version',
|
|
304
|
+
mvn: 'mvn --version',
|
|
305
|
+
};
|
|
306
|
+
return commands[tool] || `${tool} --version`;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function getInstallCommand(tool, platform) {
|
|
310
|
+
const isMac = platform === 'darwin';
|
|
311
|
+
const isLinux = platform === 'linux';
|
|
312
|
+
const isWin = platform === 'win32';
|
|
313
|
+
|
|
314
|
+
const commands = {
|
|
315
|
+
gcloud: {
|
|
316
|
+
darwin: 'brew install --cask google-cloud-sdk',
|
|
317
|
+
linux: 'curl https://sdk.cloud.google.com | bash',
|
|
318
|
+
win32: 'winget install Google.CloudSDK',
|
|
319
|
+
},
|
|
320
|
+
'azure-cli': {
|
|
321
|
+
darwin: 'brew install azure-cli',
|
|
322
|
+
linux: 'curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash',
|
|
323
|
+
win32: 'winget install Microsoft.AzureCLI',
|
|
324
|
+
},
|
|
325
|
+
'aws-cli': {
|
|
326
|
+
darwin: 'brew install awscli',
|
|
327
|
+
linux: 'curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o awscliv2.zip && unzip awscliv2.zip && sudo ./aws/install',
|
|
328
|
+
win32: 'winget install Amazon.AWSCLI',
|
|
329
|
+
},
|
|
330
|
+
'github-cli': {
|
|
331
|
+
darwin: 'brew install gh',
|
|
332
|
+
linux: 'sudo apt install gh -y',
|
|
333
|
+
win32: 'winget install GitHub.cli',
|
|
334
|
+
},
|
|
335
|
+
git: {
|
|
336
|
+
darwin: 'brew install git',
|
|
337
|
+
linux: 'sudo apt install git -y',
|
|
338
|
+
win32: 'winget install Git.Git',
|
|
339
|
+
},
|
|
340
|
+
node: {
|
|
341
|
+
darwin: 'brew install node',
|
|
342
|
+
linux: 'curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs',
|
|
343
|
+
win32: 'winget install OpenJS.NodeJS',
|
|
344
|
+
},
|
|
345
|
+
docker: {
|
|
346
|
+
darwin: 'brew install --cask docker',
|
|
347
|
+
linux: 'curl -fsSL https://get.docker.com | sh',
|
|
348
|
+
win32: 'winget install Docker.DockerDesktop',
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
return commands[tool]?.[platform] || null;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function getInstallUrl(tool) {
|
|
356
|
+
const urls = {
|
|
357
|
+
gcloud: 'https://cloud.google.com/sdk/docs/install',
|
|
358
|
+
'azure-cli': 'https://learn.microsoft.com/en-us/cli/azure/install-azure-cli',
|
|
359
|
+
'aws-cli': 'https://aws.amazon.com/cli/',
|
|
360
|
+
'github-cli': 'https://cli.github.com/',
|
|
361
|
+
git: 'https://git-scm.com/downloads',
|
|
362
|
+
node: 'https://nodejs.org/',
|
|
363
|
+
docker: 'https://docs.docker.com/get-docker/',
|
|
364
|
+
};
|
|
365
|
+
return urls[tool] || `https://www.google.com/search?q=install+${tool}`;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export { router as setupAgentRouter };
|