agent-window 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/Dockerfile +23 -0
- package/README.md +138 -0
- package/SECURITY.md +31 -0
- package/bin/cli.js +743 -0
- package/config/config.example.json +70 -0
- package/docker-compose.yml +31 -0
- package/docs/legacy/DEVELOPMENT.md +174 -0
- package/docs/legacy/HANDOVER.md +149 -0
- package/ecosystem.config.cjs +26 -0
- package/hooks/hook.mjs +299 -0
- package/hooks/settings.json +15 -0
- package/package.json +45 -0
- package/sandbox/Dockerfile +61 -0
- package/scripts/install.sh +114 -0
- package/src/bot.js +1518 -0
- package/src/core/config.js +195 -0
- package/src/core/perf-monitor.js +360 -0
package/hooks/hook.mjs
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* PreToolUse Hook for Discord Permission Approval
|
|
4
|
+
*
|
|
5
|
+
* This hook intercepts tool calls and:
|
|
6
|
+
* - Auto-approves safe tools (Read, Glob, Grep, etc.)
|
|
7
|
+
* - Requests Discord approval for risky tools (Write, Edit, Bash)
|
|
8
|
+
*
|
|
9
|
+
* Communication with Discord bot via shared file directory.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync } from 'fs';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import { randomUUID } from 'crypto';
|
|
15
|
+
|
|
16
|
+
// Read input from stdin
|
|
17
|
+
let inputData = '';
|
|
18
|
+
process.stdin.setEncoding('utf8');
|
|
19
|
+
|
|
20
|
+
for await (const chunk of process.stdin) {
|
|
21
|
+
inputData += chunk;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const input = JSON.parse(inputData);
|
|
25
|
+
const toolName = input.tool_name || '';
|
|
26
|
+
const toolInput = input.tool_input || {};
|
|
27
|
+
const toolUseId = input.tool_use_id || randomUUID();
|
|
28
|
+
|
|
29
|
+
// Pending directory (shared with Discord bot)
|
|
30
|
+
const PENDING_DIR = process.env.HOOK_PENDING_DIR || '/pending-permissions';
|
|
31
|
+
const TIMEOUT_MS = 120000; // 2 minutes
|
|
32
|
+
|
|
33
|
+
// Auto-approve safe tools (including Write/Edit since user trusts them)
|
|
34
|
+
const AUTO_APPROVE = [
|
|
35
|
+
'Read', 'Glob', 'Grep', // File reading/search
|
|
36
|
+
'Write', 'Edit', 'NotebookEdit', // File writing/editing (user trusts these)
|
|
37
|
+
'WebFetch', 'WebSearch', // Web access
|
|
38
|
+
'TodoWrite', 'Task', 'TaskOutput', 'AskUserQuestion', // Internal tools
|
|
39
|
+
'Skill', 'EnterPlanMode', 'ExitPlanMode', // Planning tools
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// Always deny dangerous tools
|
|
43
|
+
const ALWAYS_DENY = ['KillShell'];
|
|
44
|
+
|
|
45
|
+
// Bash commands that are safe to auto-approve (read-only or common dev commands)
|
|
46
|
+
const SAFE_BASH_PATTERNS = [
|
|
47
|
+
// File reading/listing
|
|
48
|
+
/^ls\b/,
|
|
49
|
+
/^pwd$/,
|
|
50
|
+
/^cat\b/,
|
|
51
|
+
/^head\b/,
|
|
52
|
+
/^tail\b/,
|
|
53
|
+
/^less\b/,
|
|
54
|
+
/^more\b/,
|
|
55
|
+
/^file\b/,
|
|
56
|
+
/^stat\b/,
|
|
57
|
+
/^wc\b/,
|
|
58
|
+
/^find\b/,
|
|
59
|
+
/^tree\b/,
|
|
60
|
+
/^du\b/,
|
|
61
|
+
/^df\b/,
|
|
62
|
+
/^realpath\b/,
|
|
63
|
+
/^readlink\b/,
|
|
64
|
+
/^dirname\b/,
|
|
65
|
+
/^basename\b/,
|
|
66
|
+
|
|
67
|
+
// Text processing (read-only)
|
|
68
|
+
/^grep\b/,
|
|
69
|
+
/^egrep\b/,
|
|
70
|
+
/^fgrep\b/,
|
|
71
|
+
/^rg\b/,
|
|
72
|
+
/^ag\b/,
|
|
73
|
+
/^awk\b/,
|
|
74
|
+
/^sort\b/,
|
|
75
|
+
/^uniq\b/,
|
|
76
|
+
/^cut\b/,
|
|
77
|
+
/^tr\b/,
|
|
78
|
+
/^diff\b/,
|
|
79
|
+
/^cmp\b/,
|
|
80
|
+
/^comm\b/,
|
|
81
|
+
/^jq\b/,
|
|
82
|
+
/^yq\b/,
|
|
83
|
+
/^xargs\b.*\b(ls|cat|head|grep|echo|wc)\b/,
|
|
84
|
+
|
|
85
|
+
// Git (read-only operations)
|
|
86
|
+
/^git\s+(status|log|diff|branch|show|remote|config|describe|tag|rev-parse|ls-files|ls-tree|blame|shortlog|reflog|stash\s+list)/,
|
|
87
|
+
|
|
88
|
+
// Package managers (read/build/test operations)
|
|
89
|
+
/^npm\s+(list|ls|view|info|outdated|test|run|install|ci|build|start|version)/,
|
|
90
|
+
/^yarn\s+(list|info|outdated|test|run|install|build|start|version)/,
|
|
91
|
+
/^pnpm\s+(list|info|outdated|test|run|install|build|start|version)/,
|
|
92
|
+
/^pip\s+(list|show|freeze|check)/,
|
|
93
|
+
/^pip3\s+(list|show|freeze|check)/,
|
|
94
|
+
/^cargo\s+(build|test|check|run|clippy|fmt|doc|tree|metadata)/,
|
|
95
|
+
/^go\s+(build|test|run|vet|fmt|mod\s+(tidy|download|graph)|version)/,
|
|
96
|
+
/^composer\s+(show|info|outdated|validate)/,
|
|
97
|
+
/^bundle\s+(list|show|outdated|check)/,
|
|
98
|
+
/^mvn\s+(compile|test|package|verify|dependency:tree)/,
|
|
99
|
+
/^gradle\s+(build|test|check|dependencies)/,
|
|
100
|
+
|
|
101
|
+
// Version checks
|
|
102
|
+
/^node\s+(--version|-v)$/,
|
|
103
|
+
/^npm\s+(--version|-v)$/,
|
|
104
|
+
/^python\s*(--version|-V)$/,
|
|
105
|
+
/^python3\s*(--version|-V)$/,
|
|
106
|
+
/^pip\s+(--version|-V)$/,
|
|
107
|
+
/^ruby\s*(--version|-v)$/,
|
|
108
|
+
/^go\s+version$/,
|
|
109
|
+
/^cargo\s+(--version|-V)$/,
|
|
110
|
+
/^rustc\s+(--version|-V)$/,
|
|
111
|
+
/^java\s+(--version|-version)$/,
|
|
112
|
+
/^docker\s+(--version|-v)$/,
|
|
113
|
+
|
|
114
|
+
// Docker (read-only)
|
|
115
|
+
/^docker\s+(ps|images|logs|inspect|stats|top|port|version|info)/,
|
|
116
|
+
/^docker-compose\s+(ps|logs|config|version)/,
|
|
117
|
+
|
|
118
|
+
// System info
|
|
119
|
+
/^which\b/,
|
|
120
|
+
/^whereis\b/,
|
|
121
|
+
/^type\b/,
|
|
122
|
+
/^command\s+-v/,
|
|
123
|
+
/^echo\b/,
|
|
124
|
+
/^printf\b/,
|
|
125
|
+
/^date\b/,
|
|
126
|
+
/^whoami$/,
|
|
127
|
+
/^id\b/,
|
|
128
|
+
/^env$/,
|
|
129
|
+
/^printenv\b/,
|
|
130
|
+
/^hostname$/,
|
|
131
|
+
/^uname\b/,
|
|
132
|
+
/^uptime$/,
|
|
133
|
+
/^free\b/,
|
|
134
|
+
/^top\s+-bn1/,
|
|
135
|
+
/^ps\s+(aux|ef)/,
|
|
136
|
+
|
|
137
|
+
// Network (read-only)
|
|
138
|
+
/^curl\s+(-s\s+)?(-I\s+)?https?:/,
|
|
139
|
+
/^wget\s+(-q\s+)?(-O\s*-\s+)?https?:/,
|
|
140
|
+
/^ping\s+-c\s+\d/,
|
|
141
|
+
/^netstat\b/,
|
|
142
|
+
/^ss\b/,
|
|
143
|
+
/^ifconfig$/,
|
|
144
|
+
/^ip\s+(addr|link|route)/,
|
|
145
|
+
|
|
146
|
+
// Build/test commands (common and safe)
|
|
147
|
+
/^make(\s+\w+)?$/,
|
|
148
|
+
/^cmake\b/,
|
|
149
|
+
/^tsc\b/,
|
|
150
|
+
/^eslint\b/,
|
|
151
|
+
/^prettier\b/,
|
|
152
|
+
/^jest\b/,
|
|
153
|
+
/^vitest\b/,
|
|
154
|
+
/^pytest\b/,
|
|
155
|
+
/^mocha\b/,
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
// Bash commands that always need approval (destructive)
|
|
159
|
+
const DANGEROUS_BASH_PATTERNS = [
|
|
160
|
+
/\brm\s/, // Remove files
|
|
161
|
+
/\brmdir\b/, // Remove directories
|
|
162
|
+
/\bunlink\b/, // Remove files
|
|
163
|
+
/>\s*\/dev\/null/, // Redirect to null (data loss)
|
|
164
|
+
/\bgit\s+(push|reset|rebase|checkout\s+-f|clean)/, // Destructive git
|
|
165
|
+
/\bdocker\s+(rm|rmi|prune|system\s+prune)/, // Docker cleanup
|
|
166
|
+
/\bkill\b/, // Kill processes
|
|
167
|
+
/\bpkill\b/, // Kill processes
|
|
168
|
+
/\bsudo\b/, // Elevated privileges
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
function sleep(ms) {
|
|
172
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function output(decision, reason, updatedInput = null) {
|
|
176
|
+
const result = {
|
|
177
|
+
hookSpecificOutput: {
|
|
178
|
+
hookEventName: 'PreToolUse',
|
|
179
|
+
permissionDecision: decision,
|
|
180
|
+
permissionDecisionReason: reason,
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
if (updatedInput) {
|
|
184
|
+
result.hookSpecificOutput.updatedInput = updatedInput;
|
|
185
|
+
}
|
|
186
|
+
console.log(JSON.stringify(result));
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Ensure pending directory exists
|
|
191
|
+
try {
|
|
192
|
+
mkdirSync(PENDING_DIR, { recursive: true });
|
|
193
|
+
} catch (e) {}
|
|
194
|
+
|
|
195
|
+
console.error(`[Hook] Tool: ${toolName}, ID: ${toolUseId.substring(0, 8)}...`);
|
|
196
|
+
|
|
197
|
+
// Auto-approve safe tools
|
|
198
|
+
if (AUTO_APPROVE.includes(toolName)) {
|
|
199
|
+
console.error(`[Hook] Auto-approving: ${toolName}`);
|
|
200
|
+
output('allow', `Auto-approved: ${toolName} is a safe operation`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Always deny dangerous tools
|
|
204
|
+
if (ALWAYS_DENY.includes(toolName)) {
|
|
205
|
+
console.error(`[Hook] Auto-denying: ${toolName}`);
|
|
206
|
+
output('deny', `Denied: ${toolName} is not allowed for security reasons`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Special handling for Bash commands
|
|
210
|
+
if (toolName === 'Bash') {
|
|
211
|
+
const command = (toolInput.command || '').trim();
|
|
212
|
+
console.error(`[Hook] Bash command: ${command.substring(0, 80)}...`);
|
|
213
|
+
|
|
214
|
+
// Check for dangerous patterns first - these always need approval
|
|
215
|
+
const isDangerous = DANGEROUS_BASH_PATTERNS.some(p => p.test(command));
|
|
216
|
+
if (isDangerous) {
|
|
217
|
+
console.error(`[Hook] Dangerous bash pattern detected, requesting Discord approval`);
|
|
218
|
+
// Fall through to Discord approval (don't exit here)
|
|
219
|
+
} else {
|
|
220
|
+
// Check for safe patterns
|
|
221
|
+
const isSafe = SAFE_BASH_PATTERNS.some(p => p.test(command));
|
|
222
|
+
if (isSafe) {
|
|
223
|
+
console.error(`[Hook] Auto-approving safe bash: ${command.substring(0, 50)}`);
|
|
224
|
+
output('allow', `Auto-approved: Safe bash command`);
|
|
225
|
+
}
|
|
226
|
+
// Not explicitly safe - fall through to Discord approval
|
|
227
|
+
console.error(`[Hook] Bash needs Discord approval: ${command.substring(0, 50)}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// For other tools, request Discord approval
|
|
232
|
+
const requestId = toolUseId.replace(/[^a-zA-Z0-9]/g, '_').substring(0, 50);
|
|
233
|
+
const requestFile = join(PENDING_DIR, `${requestId}.request.json`);
|
|
234
|
+
const responseFile = join(PENDING_DIR, `${requestId}.response.json`);
|
|
235
|
+
|
|
236
|
+
// Write request file
|
|
237
|
+
const request = {
|
|
238
|
+
tool_use_id: toolUseId,
|
|
239
|
+
tool_name: toolName,
|
|
240
|
+
input: toolInput,
|
|
241
|
+
timestamp: Date.now(),
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
writeFileSync(requestFile, JSON.stringify(request, null, 2));
|
|
246
|
+
console.error(`[Hook] Waiting for Discord approval: ${toolName}`);
|
|
247
|
+
} catch (e) {
|
|
248
|
+
console.error(`[Hook] Failed to write request: ${e.message}`);
|
|
249
|
+
output('deny', `Permission system error: ${e.message}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Poll for response
|
|
253
|
+
const deadline = Date.now() + TIMEOUT_MS;
|
|
254
|
+
let pollCount = 0;
|
|
255
|
+
console.error(`[Hook] Polling for response: ${responseFile}`);
|
|
256
|
+
|
|
257
|
+
while (Date.now() < deadline) {
|
|
258
|
+
pollCount++;
|
|
259
|
+
|
|
260
|
+
// Debug: list files every 10 polls (3 seconds)
|
|
261
|
+
if (pollCount % 10 === 1) {
|
|
262
|
+
try {
|
|
263
|
+
const { readdirSync } = await import('fs');
|
|
264
|
+
const files = readdirSync(PENDING_DIR);
|
|
265
|
+
console.error(`[Hook] Poll #${pollCount}, files in ${PENDING_DIR}: ${files.join(', ') || '(empty)'}`);
|
|
266
|
+
} catch (e) {
|
|
267
|
+
console.error(`[Hook] Poll #${pollCount}, cannot list dir: ${e.message}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (existsSync(responseFile)) {
|
|
272
|
+
console.error(`[Hook] Response file found!`);
|
|
273
|
+
try {
|
|
274
|
+
const content = readFileSync(responseFile, 'utf-8');
|
|
275
|
+
console.error(`[Hook] Response content: ${content.substring(0, 100)}`);
|
|
276
|
+
const response = JSON.parse(content);
|
|
277
|
+
|
|
278
|
+
// Clean up files
|
|
279
|
+
try { unlinkSync(requestFile); } catch (e) {}
|
|
280
|
+
try { unlinkSync(responseFile); } catch (e) {}
|
|
281
|
+
|
|
282
|
+
console.error(`[Hook] Got response: ${response.behavior}`);
|
|
283
|
+
|
|
284
|
+
if (response.behavior === 'allow') {
|
|
285
|
+
output('allow', 'User approved via Discord', response.updatedInput);
|
|
286
|
+
} else {
|
|
287
|
+
output('deny', response.message || 'User denied via Discord');
|
|
288
|
+
}
|
|
289
|
+
} catch (e) {
|
|
290
|
+
console.error(`[Hook] Error reading/parsing response: ${e.message}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
await sleep(300);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Timeout
|
|
297
|
+
try { unlinkSync(requestFile); } catch (e) {}
|
|
298
|
+
console.error('[Hook] Timeout waiting for approval');
|
|
299
|
+
output('deny', 'Permission request timed out (2 minutes). Please try again.');
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-window",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A window to interact with AI agents through chat interfaces. Simplified interaction, powerful backend capabilities.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/bot.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"agent-window": "./bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/bot.js",
|
|
12
|
+
"dev": "node --watch src/bot.js",
|
|
13
|
+
"setup": "node bin/cli.js setup",
|
|
14
|
+
"pm2:start": "pm2 start ecosystem.config.cjs",
|
|
15
|
+
"pm2:stop": "pm2 stop agent-window",
|
|
16
|
+
"pm2:restart": "pm2 restart agent-window",
|
|
17
|
+
"pm2:logs": "pm2 logs agent-window"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"agent",
|
|
21
|
+
"window",
|
|
22
|
+
"discord",
|
|
23
|
+
"slack",
|
|
24
|
+
"bot",
|
|
25
|
+
"claude",
|
|
26
|
+
"ai",
|
|
27
|
+
"cli",
|
|
28
|
+
"docker",
|
|
29
|
+
"sandbox",
|
|
30
|
+
"chat",
|
|
31
|
+
"interface"
|
|
32
|
+
],
|
|
33
|
+
"author": "",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"discord.js": "^14.14.1"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/YOUR_USERNAME/AgentWindow"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Claude CLI Sandbox Environment with MCP Permission Support
|
|
2
|
+
FROM node:20-slim
|
|
3
|
+
|
|
4
|
+
# Install system dependencies for Playwright/Chromium (as root)
|
|
5
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
6
|
+
# Playwright dependencies
|
|
7
|
+
libglib2.0-0 \
|
|
8
|
+
libnss3 \
|
|
9
|
+
libnspr4 \
|
|
10
|
+
libdbus-1-3 \
|
|
11
|
+
libatk1.0-0 \
|
|
12
|
+
libatk-bridge2.0-0 \
|
|
13
|
+
libcups2 \
|
|
14
|
+
libexpat1 \
|
|
15
|
+
libxcb1 \
|
|
16
|
+
libxkbcommon0 \
|
|
17
|
+
libatspi2.0-0 \
|
|
18
|
+
libx11-6 \
|
|
19
|
+
libxcomposite1 \
|
|
20
|
+
libxdamage1 \
|
|
21
|
+
libxext6 \
|
|
22
|
+
libxfixes3 \
|
|
23
|
+
libxrandr2 \
|
|
24
|
+
libgbm1 \
|
|
25
|
+
libcairo2 \
|
|
26
|
+
libpango-1.0-0 \
|
|
27
|
+
libasound2 \
|
|
28
|
+
# Additional useful tools
|
|
29
|
+
git \
|
|
30
|
+
curl \
|
|
31
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
32
|
+
|
|
33
|
+
# Install Claude CLI globally
|
|
34
|
+
RUN npm install -g @anthropic-ai/claude-code
|
|
35
|
+
|
|
36
|
+
# Install MCP SDK for permission server
|
|
37
|
+
RUN npm install -g @modelcontextprotocol/sdk zod
|
|
38
|
+
|
|
39
|
+
# Create directories
|
|
40
|
+
WORKDIR /workspace
|
|
41
|
+
RUN mkdir -p /mcp-server /pending-permissions
|
|
42
|
+
|
|
43
|
+
# Create a flexible home directory that any user can use
|
|
44
|
+
RUN mkdir -p /home/claude/.cache/ms-playwright && chmod -R 777 /home/claude
|
|
45
|
+
|
|
46
|
+
# Install Playwright globally and download Chromium to shared location
|
|
47
|
+
ENV PLAYWRIGHT_BROWSERS_PATH=/home/claude/.cache/ms-playwright
|
|
48
|
+
RUN npm install -g playwright && npx playwright install chromium && chmod -R 777 /home/claude/.cache/ms-playwright
|
|
49
|
+
|
|
50
|
+
# Set environment variables
|
|
51
|
+
ENV TERM=dumb
|
|
52
|
+
ENV CI=true
|
|
53
|
+
ENV NO_COLOR=1
|
|
54
|
+
ENV FORCE_COLOR=0
|
|
55
|
+
ENV NODE_PATH=/usr/local/lib/node_modules
|
|
56
|
+
ENV HOME=/home/claude
|
|
57
|
+
ENV PLAYWRIGHT_BROWSERS_PATH=/home/claude/.cache/ms-playwright
|
|
58
|
+
|
|
59
|
+
# Default command
|
|
60
|
+
ENTRYPOINT ["claude"]
|
|
61
|
+
CMD ["--help"]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# AgentBridge - Quick Install Script
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/AgentBridge/main/scripts/install.sh | bash
|
|
7
|
+
#
|
|
8
|
+
# Or:
|
|
9
|
+
# ./scripts/install.sh
|
|
10
|
+
|
|
11
|
+
set -e
|
|
12
|
+
|
|
13
|
+
RED='\033[0;31m'
|
|
14
|
+
GREEN='\033[0;32m'
|
|
15
|
+
YELLOW='\033[1;33m'
|
|
16
|
+
CYAN='\033[0;36m'
|
|
17
|
+
NC='\033[0m'
|
|
18
|
+
|
|
19
|
+
log_info() { echo -e "${CYAN}ℹ️ $1${NC}"; }
|
|
20
|
+
log_success() { echo -e "${GREEN}✅ $1${NC}"; }
|
|
21
|
+
log_warning() { echo -e "${YELLOW}⚠️ $1${NC}"; }
|
|
22
|
+
log_error() { echo -e "${RED}❌ $1${NC}"; }
|
|
23
|
+
|
|
24
|
+
echo ""
|
|
25
|
+
echo -e "${CYAN}"
|
|
26
|
+
echo " _ _ ____ _ _"
|
|
27
|
+
echo " / \\ __ _ ___ _ __ | |_| __ ) _ __(_) __| | __ _ ___"
|
|
28
|
+
echo " / _ \\ / _\` |/ _ \\ '_ \\| __| _ \\| '__| |/ _\` |/ _\` |/ _ \\"
|
|
29
|
+
echo "/ ___ \\ (_| | __/ | | | |_| |_) | | | | (_| | (_| | __/"
|
|
30
|
+
echo "/_/ \\_\\__, |\\___|_| |_|\\__|____/|_| |_|\\__,_|\\__, |\\___|"
|
|
31
|
+
echo " |___/ |___/"
|
|
32
|
+
echo -e "${NC}"
|
|
33
|
+
echo " Bridge AI coding agents to chat platforms"
|
|
34
|
+
echo ""
|
|
35
|
+
|
|
36
|
+
# Check Node.js
|
|
37
|
+
if ! command -v node &> /dev/null; then
|
|
38
|
+
log_error "Node.js not found. Please install Node.js 18+ first."
|
|
39
|
+
echo "Visit: https://nodejs.org/"
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
|
44
|
+
if [ "$NODE_VERSION" -lt 18 ]; then
|
|
45
|
+
log_error "Node.js 18+ required. Current: $(node -v)"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
log_success "Node.js $(node -v)"
|
|
49
|
+
|
|
50
|
+
# Check Docker
|
|
51
|
+
if ! command -v docker &> /dev/null; then
|
|
52
|
+
log_error "Docker not found. Please install Docker first."
|
|
53
|
+
echo "Visit: https://docs.docker.com/get-docker/"
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
log_success "Docker found"
|
|
57
|
+
|
|
58
|
+
# Check PM2
|
|
59
|
+
if ! command -v pm2 &> /dev/null; then
|
|
60
|
+
log_info "Installing PM2..."
|
|
61
|
+
npm install -g pm2
|
|
62
|
+
log_success "PM2 installed"
|
|
63
|
+
else
|
|
64
|
+
log_success "PM2 found"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Clone or update
|
|
68
|
+
INSTALL_DIR="${INSTALL_DIR:-$HOME/AgentBridge}"
|
|
69
|
+
|
|
70
|
+
if [ -d "$INSTALL_DIR" ]; then
|
|
71
|
+
log_info "Updating existing installation..."
|
|
72
|
+
cd "$INSTALL_DIR"
|
|
73
|
+
git pull origin main
|
|
74
|
+
else
|
|
75
|
+
log_info "Cloning repository..."
|
|
76
|
+
git clone https://github.com/YOUR_USERNAME/AgentBridge.git "$INSTALL_DIR"
|
|
77
|
+
cd "$INSTALL_DIR"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Install dependencies
|
|
81
|
+
log_info "Installing dependencies..."
|
|
82
|
+
npm install
|
|
83
|
+
|
|
84
|
+
# Build Docker image
|
|
85
|
+
log_info "Building Docker sandbox..."
|
|
86
|
+
docker build -t claude-sandbox ./sandbox
|
|
87
|
+
|
|
88
|
+
# Config check
|
|
89
|
+
if [ ! -f "config/config.json" ]; then
|
|
90
|
+
log_warning "Config not found."
|
|
91
|
+
echo ""
|
|
92
|
+
echo "Create config/config.json:"
|
|
93
|
+
echo " cp config/config.example.json config/config.json"
|
|
94
|
+
echo " nano config/config.json"
|
|
95
|
+
echo ""
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Link CLI
|
|
99
|
+
log_info "Linking CLI..."
|
|
100
|
+
npm link
|
|
101
|
+
|
|
102
|
+
echo ""
|
|
103
|
+
log_success "Installation complete!"
|
|
104
|
+
echo ""
|
|
105
|
+
echo "Next steps:"
|
|
106
|
+
echo " 1. Edit config/config.json"
|
|
107
|
+
echo " 2. Run: agent-bridge start"
|
|
108
|
+
echo ""
|
|
109
|
+
echo "Commands:"
|
|
110
|
+
echo " agent-bridge setup - Interactive setup"
|
|
111
|
+
echo " agent-bridge start - Start"
|
|
112
|
+
echo " agent-bridge logs - View logs"
|
|
113
|
+
echo " agent-bridge --help - Help"
|
|
114
|
+
echo ""
|