aigo 1.0.0 → 1.0.1
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 +50 -50
- package/bin/vigo.js +31 -31
- package/lib/cli.js +17 -17
- package/lib/config.js +2 -2
- package/package.json +1 -1
- package/public/index.html +2 -2
- package/public/terminal.js +5 -5
- package/specs/claude-code-web-terminal.md +28 -28
- package/specs/cursor-cli-support.md +12 -12
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# aigo
|
|
2
2
|
|
|
3
3
|
Stream AI coding assistants to the web - run Claude Code or Cursor Agent remotely from any device.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**aigo** wraps Claude Code or Cursor Agent in a tmux session and streams it to your browser via WebSocket, letting you access your AI coding sessions from your phone, tablet, or any browser.
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
@@ -14,13 +14,13 @@ npm install
|
|
|
14
14
|
npm link
|
|
15
15
|
|
|
16
16
|
# Start Claude Code with web access (ngrok tunnel enabled by default)
|
|
17
|
-
|
|
17
|
+
aigo claude
|
|
18
18
|
|
|
19
19
|
# Or start Cursor Agent
|
|
20
|
-
|
|
20
|
+
aigo cursor
|
|
21
21
|
|
|
22
22
|
# Local only (no tunnel)
|
|
23
|
-
|
|
23
|
+
aigo --tunnel none claude
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
## Requirements
|
|
@@ -72,10 +72,10 @@ export CURSOR_API_KEY=your_api_key_here
|
|
|
72
72
|
## Usage
|
|
73
73
|
|
|
74
74
|
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
aigo [options] claude [claude-args]
|
|
76
|
+
aigo [options] cursor [cursor-args]
|
|
77
|
+
aigo [options] agent [agent-args]
|
|
78
|
+
aigo [options] --attach <session>
|
|
79
79
|
|
|
80
80
|
Tools:
|
|
81
81
|
claude Use Claude Code CLI
|
|
@@ -97,26 +97,26 @@ Options:
|
|
|
97
97
|
|
|
98
98
|
```bash
|
|
99
99
|
# Claude Code Examples (ngrok tunnel by default)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
aigo claude # Start Claude with ngrok tunnel
|
|
101
|
+
aigo --tunnel none claude # Local only (no tunnel)
|
|
102
|
+
aigo -p 8080 -s myproject claude # Custom port and session
|
|
103
|
+
aigo claude --model sonnet # Pass args to Claude
|
|
104
104
|
|
|
105
105
|
# Cursor Agent Examples
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
aigo cursor # Start Cursor Agent with ngrok tunnel
|
|
107
|
+
aigo --tunnel cloudflared cursor # Use cloudflared instead of ngrok
|
|
108
|
+
aigo -p 8080 -s myproject cursor # Custom port and session
|
|
109
|
+
aigo cursor --model gpt-5 # Pass args to Cursor Agent
|
|
110
110
|
|
|
111
111
|
# General Examples
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
aigo --attach myproject # Attach to existing tmux session
|
|
113
|
+
aigo -P mypass123 claude # Custom password
|
|
114
|
+
aigo -T 15 -E 30 cursor # 15min lock, 30min exit timeout
|
|
115
|
+
aigo -T 0 -E 0 claude # Disable timeouts (default behavior)
|
|
116
116
|
|
|
117
117
|
# Run multiple sessions (ports auto-selected)
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
aigo -s project-a claude # Gets port 3000
|
|
119
|
+
aigo -s project-b cursor # Gets port 3001 (auto)
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
## Configuration
|
|
@@ -146,7 +146,7 @@ export const config = {
|
|
|
146
146
|
|
|
147
147
|
## How It Works
|
|
148
148
|
|
|
149
|
-
1. **
|
|
149
|
+
1. **aigo** creates a tmux session and runs `claude` or `agent` inside it
|
|
150
150
|
2. A local web server starts, serving an xterm.js terminal
|
|
151
151
|
3. The server attaches to the tmux session via node-pty
|
|
152
152
|
4. Your browser connects via WebSocket for real-time terminal streaming
|
|
@@ -154,7 +154,7 @@ export const config = {
|
|
|
154
154
|
|
|
155
155
|
```
|
|
156
156
|
┌────────────────────────────────────────┐
|
|
157
|
-
│
|
|
157
|
+
│ aigo - Claude Code Web Terminal │
|
|
158
158
|
├────────────────────────────────────────┤
|
|
159
159
|
│ Session: claude-code │
|
|
160
160
|
│ Local: http://localhost:3000 │
|
|
@@ -171,7 +171,7 @@ Access URL:
|
|
|
171
171
|
|
|
172
172
|
| Feature | Claude Code | Cursor Agent |
|
|
173
173
|
|---------|-------------|--------------|
|
|
174
|
-
| Command | `
|
|
174
|
+
| Command | `aigo claude` | `aigo cursor` |
|
|
175
175
|
| CLI Binary | `claude` | `agent` |
|
|
176
176
|
| Authentication | Claude API key / OAuth | Browser login or `CURSOR_API_KEY` |
|
|
177
177
|
| Models | Claude models | Multiple (GPT-5, Claude, etc.) via subscription |
|
|
@@ -181,7 +181,7 @@ Access URL:
|
|
|
181
181
|
|
|
182
182
|
## Security
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
aigo uses multiple layers of security:
|
|
185
185
|
|
|
186
186
|
1. **URL Token** - A random 4-character token in the URL path (`/t/<token>`)
|
|
187
187
|
2. **Password** - A 6-character alphanumeric password (or custom password via `-P`)
|
|
@@ -204,7 +204,7 @@ vigo uses multiple layers of security:
|
|
|
204
204
|
|
|
205
205
|
## Session Timeouts
|
|
206
206
|
|
|
207
|
-
|
|
207
|
+
aigo has two optional timeout mechanisms (both disabled by default for persistent sessions):
|
|
208
208
|
|
|
209
209
|
### Lock Timeout (default: disabled)
|
|
210
210
|
When enabled with `-T <minutes>`, the session locks after the specified period of no interaction (mouse movement, typing, etc.) and requires password re-entry. The WebSocket connection stays open, so you don't lose any terminal state.
|
|
@@ -215,11 +215,11 @@ When enabled with `-E <minutes>`, the session completely terminates after the sp
|
|
|
215
215
|
- Claude Code / Cursor Agent is stopped
|
|
216
216
|
- The server exits
|
|
217
217
|
|
|
218
|
-
Enable timeouts for security in shared environments: `
|
|
218
|
+
Enable timeouts for security in shared environments: `aigo -T 15 -E 30 claude`
|
|
219
219
|
|
|
220
220
|
## Browser Notifications
|
|
221
221
|
|
|
222
|
-
|
|
222
|
+
aigo can send browser notifications when:
|
|
223
223
|
- The AI assistant is waiting for input
|
|
224
224
|
- The WebSocket connection is lost
|
|
225
225
|
|
|
@@ -227,7 +227,7 @@ Notifications are only sent when the browser tab is not active. Allow notificati
|
|
|
227
227
|
|
|
228
228
|
## Mobile Controls
|
|
229
229
|
|
|
230
|
-
When accessing
|
|
230
|
+
When accessing aigo from a mobile device (phone or tablet), control buttons appear at the bottom of the screen:
|
|
231
231
|
|
|
232
232
|
| Button | Action | Description |
|
|
233
233
|
|--------|--------|-------------|
|
|
@@ -246,34 +246,34 @@ The buttons are automatically shown based on:
|
|
|
246
246
|
|
|
247
247
|
## Running Persistently
|
|
248
248
|
|
|
249
|
-
To keep
|
|
249
|
+
To keep aigo running indefinitely (even after closing your terminal), run it inside tmux or use a process manager:
|
|
250
250
|
|
|
251
251
|
```bash
|
|
252
|
-
# Run
|
|
253
|
-
tmux new-session -d -s
|
|
252
|
+
# Run aigo inside its own tmux session
|
|
253
|
+
tmux new-session -d -s aigo-server 'aigo claude'
|
|
254
254
|
|
|
255
255
|
# Attach to check status
|
|
256
|
-
tmux attach -t
|
|
256
|
+
tmux attach -t aigo-server
|
|
257
257
|
|
|
258
258
|
# Or use pm2 for auto-restart on crash
|
|
259
|
-
pm2 start "
|
|
259
|
+
pm2 start "aigo claude" --name aigo-session
|
|
260
260
|
```
|
|
261
261
|
|
|
262
|
-
When the
|
|
262
|
+
When the aigo server stays running, your ngrok URL remains stable and you can return to the same URL anytime.
|
|
263
263
|
|
|
264
264
|
## Auto Port Selection
|
|
265
265
|
|
|
266
|
-
If the default port (3000) or your specified port is already in use,
|
|
266
|
+
If the default port (3000) or your specified port is already in use, aigo automatically finds the next available port:
|
|
267
267
|
|
|
268
268
|
```
|
|
269
269
|
⚠ Port 3000 in use, using port 3001 instead
|
|
270
270
|
```
|
|
271
271
|
|
|
272
|
-
This allows running multiple
|
|
272
|
+
This allows running multiple aigo sessions simultaneously without manual port management.
|
|
273
273
|
|
|
274
274
|
## Exiting and Cleanup
|
|
275
275
|
|
|
276
|
-
|
|
276
|
+
aigo provides different exit behaviors depending on how you terminate the session:
|
|
277
277
|
|
|
278
278
|
### Exit Behavior Summary
|
|
279
279
|
|
|
@@ -290,7 +290,7 @@ Click the **Exit** button in the top-right corner of the web interface to:
|
|
|
290
290
|
2. Kill the PTY process
|
|
291
291
|
3. Kill the tmux session
|
|
292
292
|
4. Close all WebSocket connections
|
|
293
|
-
5. Shut down the
|
|
293
|
+
5. Shut down the aigo server
|
|
294
294
|
6. Exit the process
|
|
295
295
|
|
|
296
296
|
**Result:** Everything is cleaned up - tmux session, server, and process all terminate. The web client shows a "Session Ended" message.
|
|
@@ -299,7 +299,7 @@ This is useful when you're completely done with a session and want to free all r
|
|
|
299
299
|
|
|
300
300
|
### Session Persistence (Ctrl+C on console)
|
|
301
301
|
|
|
302
|
-
When you press `Ctrl+C` on the terminal running
|
|
302
|
+
When you press `Ctrl+C` on the terminal running aigo:
|
|
303
303
|
1. The web server stops
|
|
304
304
|
2. The tunnel closes (if running)
|
|
305
305
|
3. **The tmux session keeps running!**
|
|
@@ -310,9 +310,9 @@ When you press `Ctrl+C` on the terminal running vigo:
|
|
|
310
310
|
Re-attach later:
|
|
311
311
|
|
|
312
312
|
```bash
|
|
313
|
-
# Via
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
# Via aigo (starts new web server for existing session)
|
|
314
|
+
aigo --attach claude-code
|
|
315
|
+
aigo --attach cursor-agent
|
|
316
316
|
|
|
317
317
|
# Or directly with tmux (local terminal only)
|
|
318
318
|
tmux attach -t claude-code
|
|
@@ -401,7 +401,7 @@ ngrok config add-authtoken YOUR_TOKEN
|
|
|
401
401
|
|
|
402
402
|
Free ngrok accounts are limited to 1 simultaneous tunnel. If you see this error, it means an ngrok process from a previous session is still running.
|
|
403
403
|
|
|
404
|
-
|
|
404
|
+
aigo will detect this and show you the command to kill it:
|
|
405
405
|
|
|
406
406
|
```
|
|
407
407
|
Error: An ngrok process is already running.
|
|
@@ -411,7 +411,7 @@ To kill the existing ngrok process, run:
|
|
|
411
411
|
kill 12345
|
|
412
412
|
|
|
413
413
|
Or to run without a tunnel:
|
|
414
|
-
|
|
414
|
+
aigo --tunnel none cursor
|
|
415
415
|
```
|
|
416
416
|
|
|
417
417
|
You can also find and kill ngrok manually:
|
|
@@ -441,11 +441,11 @@ Try resizing your browser window to trigger a terminal resize.
|
|
|
441
441
|
|
|
442
442
|
```bash
|
|
443
443
|
# Run without installing globally
|
|
444
|
-
node bin/
|
|
445
|
-
node bin/
|
|
444
|
+
node bin/aigo.js claude
|
|
445
|
+
node bin/aigo.js cursor
|
|
446
446
|
|
|
447
447
|
# Watch for changes (requires nodemon)
|
|
448
|
-
npx nodemon bin/
|
|
448
|
+
npx nodemon bin/aigo.js claude
|
|
449
449
|
```
|
|
450
450
|
|
|
451
451
|
## License
|
package/bin/vigo.js
CHANGED
|
@@ -21,7 +21,7 @@ function printBanner(info) {
|
|
|
21
21
|
const timeoutStr = `lock:${lockStr} exit:${exitStr}`;
|
|
22
22
|
|
|
23
23
|
const toolTitle = info.tool === 'cursor' ? 'Cursor Agent' : 'Claude Code';
|
|
24
|
-
const header = `
|
|
24
|
+
const header = `aigo - ${toolTitle} Web Terminal`;
|
|
25
25
|
|
|
26
26
|
console.log('');
|
|
27
27
|
console.log('┌────────────────────────────────────────┐');
|
|
@@ -45,13 +45,13 @@ function printBanner(info) {
|
|
|
45
45
|
|
|
46
46
|
function printHelp() {
|
|
47
47
|
console.log(`
|
|
48
|
-
|
|
48
|
+
aigo v${VERSION} - Stream Claude Code or Cursor Agent to the web
|
|
49
49
|
|
|
50
50
|
Usage:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
aigo [options] claude [claude-args]
|
|
52
|
+
aigo [options] cursor [cursor-args]
|
|
53
|
+
aigo [options] agent [agent-args]
|
|
54
|
+
aigo [options] --attach <session>
|
|
55
55
|
|
|
56
56
|
Tools:
|
|
57
57
|
claude Use Claude Code CLI
|
|
@@ -69,28 +69,28 @@ Options:
|
|
|
69
69
|
--version, -v Show version
|
|
70
70
|
|
|
71
71
|
Examples:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
aigo claude Start Claude Code with ngrok tunnel
|
|
73
|
+
aigo cursor Start Cursor Agent with ngrok tunnel
|
|
74
|
+
aigo --tunnel none claude Local only (no tunnel)
|
|
75
|
+
aigo -p 8080 -s myproject cursor Custom port and session
|
|
76
|
+
aigo claude --model sonnet Pass args to Claude
|
|
77
|
+
aigo cursor --model gpt-5 Pass args to Cursor Agent
|
|
78
|
+
aigo --attach myproject Attach to existing session
|
|
79
|
+
aigo -P mypass123 cursor Custom password
|
|
80
80
|
`);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
async function main() {
|
|
84
|
-
const {
|
|
84
|
+
const { aigoArgs, toolArgs, tool } = parseArgs(process.argv.slice(2));
|
|
85
85
|
|
|
86
86
|
// Handle help and version
|
|
87
|
-
if (
|
|
87
|
+
if (aigoArgs.help) {
|
|
88
88
|
printHelp();
|
|
89
89
|
process.exit(0);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
if (
|
|
93
|
-
console.log(`
|
|
92
|
+
if (aigoArgs.version) {
|
|
93
|
+
console.log(`aigo v${VERSION}`);
|
|
94
94
|
process.exit(0);
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -110,7 +110,7 @@ async function main() {
|
|
|
110
110
|
const toolConfig = config.tools[activeTool];
|
|
111
111
|
|
|
112
112
|
// Check if the tool CLI is installed (for non-attach mode)
|
|
113
|
-
if (!
|
|
113
|
+
if (!aigoArgs.attach && activeTool === 'cursor') {
|
|
114
114
|
if (!isAgentInstalled()) {
|
|
115
115
|
console.error('Error: Cursor Agent CLI is not installed or not in PATH');
|
|
116
116
|
console.error('');
|
|
@@ -135,9 +135,9 @@ async function main() {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
const sessionName =
|
|
139
|
-
const requestedPort =
|
|
140
|
-
const tunnelType =
|
|
138
|
+
const sessionName = aigoArgs.session || toolConfig.defaultSession;
|
|
139
|
+
const requestedPort = aigoArgs.port || config.defaultPort;
|
|
140
|
+
const tunnelType = aigoArgs.tunnel || config.defaultTunnel;
|
|
141
141
|
|
|
142
142
|
// Find available port
|
|
143
143
|
let port;
|
|
@@ -156,8 +156,8 @@ async function main() {
|
|
|
156
156
|
|
|
157
157
|
// Use custom password or generate password from config
|
|
158
158
|
let password;
|
|
159
|
-
if (
|
|
160
|
-
password =
|
|
159
|
+
if (aigoArgs.password) {
|
|
160
|
+
password = aigoArgs.password;
|
|
161
161
|
} else {
|
|
162
162
|
password = '';
|
|
163
163
|
const randomBytes = crypto.randomBytes(config.passwordLength);
|
|
@@ -167,12 +167,12 @@ async function main() {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
// Timeout settings (in minutes) - use config defaults
|
|
170
|
-
const lockTimeout =
|
|
171
|
-
const exitTimeout =
|
|
170
|
+
const lockTimeout = aigoArgs.timeout !== null ? aigoArgs.timeout : config.lockTimeout;
|
|
171
|
+
const exitTimeout = aigoArgs.exitTimeout !== null ? aigoArgs.exitTimeout : config.exitTimeout;
|
|
172
172
|
|
|
173
173
|
// Handle --attach mode
|
|
174
|
-
if (
|
|
175
|
-
const attachSession = typeof
|
|
174
|
+
if (aigoArgs.attach) {
|
|
175
|
+
const attachSession = typeof aigoArgs.attach === 'string' ? aigoArgs.attach : sessionName;
|
|
176
176
|
if (!hasSession(attachSession)) {
|
|
177
177
|
console.error(`Error: tmux session '${attachSession}' does not exist`);
|
|
178
178
|
console.error(`Create it first with: tmux new-session -s ${attachSession}`);
|
|
@@ -190,7 +190,7 @@ async function main() {
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
const actualSession =
|
|
193
|
+
const actualSession = aigoArgs.attach || sessionName;
|
|
194
194
|
|
|
195
195
|
// Start web server
|
|
196
196
|
console.log(`✓ Starting web server on port ${port}...`);
|
|
@@ -223,7 +223,7 @@ async function main() {
|
|
|
223
223
|
}
|
|
224
224
|
console.error('');
|
|
225
225
|
console.error('Or to run without a tunnel:');
|
|
226
|
-
console.error('
|
|
226
|
+
console.error(' aigo --tunnel none cursor');
|
|
227
227
|
console.error('');
|
|
228
228
|
process.exit(1);
|
|
229
229
|
}
|
|
@@ -261,7 +261,7 @@ async function main() {
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
console.log(`tmux session '${actualSession}' is still running.`);
|
|
264
|
-
console.log(`Re-attach with:
|
|
264
|
+
console.log(`Re-attach with: aigo --attach ${actualSession}`);
|
|
265
265
|
console.log(`Or directly: tmux attach -t ${actualSession}`);
|
|
266
266
|
server.close();
|
|
267
267
|
process.exit(0);
|
package/lib/cli.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Parse command line arguments, separating
|
|
2
|
+
* Parse command line arguments, separating aigo args from tool args
|
|
3
3
|
*
|
|
4
|
-
* Example:
|
|
5
|
-
* Example:
|
|
4
|
+
* Example: aigo --port 8080 --tunnel ngrok claude --model sonnet
|
|
5
|
+
* Example: aigo --port 8080 cursor --model gpt-5
|
|
6
6
|
* Returns:
|
|
7
|
-
*
|
|
7
|
+
* aigoArgs: { port: 8080, tunnel: 'ngrok' }
|
|
8
8
|
* toolArgs: ['--model', 'sonnet']
|
|
9
9
|
* tool: 'claude' | 'cursor' | null
|
|
10
10
|
*/
|
|
11
11
|
export function parseArgs(args) {
|
|
12
|
-
const
|
|
12
|
+
const aigoArgs = {
|
|
13
13
|
port: null,
|
|
14
14
|
session: null,
|
|
15
15
|
tunnel: null,
|
|
@@ -39,31 +39,31 @@ export function parseArgs(args) {
|
|
|
39
39
|
break;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
// Parse
|
|
42
|
+
// Parse aigo options
|
|
43
43
|
if (arg === '--port' || arg === '-p') {
|
|
44
|
-
|
|
44
|
+
aigoArgs.port = parseInt(args[++i], 10);
|
|
45
45
|
} else if (arg === '--session' || arg === '-s') {
|
|
46
|
-
|
|
46
|
+
aigoArgs.session = args[++i];
|
|
47
47
|
} else if (arg === '--tunnel' || arg === '-t') {
|
|
48
|
-
|
|
48
|
+
aigoArgs.tunnel = args[++i];
|
|
49
49
|
} else if (arg === '--attach' || arg === '-a') {
|
|
50
50
|
const next = args[i + 1];
|
|
51
51
|
// Check if next arg is a session name (not another flag)
|
|
52
52
|
if (next && !next.startsWith('-')) {
|
|
53
|
-
|
|
53
|
+
aigoArgs.attach = args[++i];
|
|
54
54
|
} else {
|
|
55
|
-
|
|
55
|
+
aigoArgs.attach = true; // Use default session name
|
|
56
56
|
}
|
|
57
57
|
} else if (arg === '--password' || arg === '-P') {
|
|
58
|
-
|
|
58
|
+
aigoArgs.password = args[++i];
|
|
59
59
|
} else if (arg === '--timeout' || arg === '-T') {
|
|
60
|
-
|
|
60
|
+
aigoArgs.timeout = parseInt(args[++i], 10);
|
|
61
61
|
} else if (arg === '--exit-timeout' || arg === '-E') {
|
|
62
|
-
|
|
62
|
+
aigoArgs.exitTimeout = parseInt(args[++i], 10);
|
|
63
63
|
} else if (arg === '--help' || arg === '-h') {
|
|
64
|
-
|
|
64
|
+
aigoArgs.help = true;
|
|
65
65
|
} else if (arg === '--version' || arg === '-v') {
|
|
66
|
-
|
|
66
|
+
aigoArgs.version = true;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
i++;
|
|
@@ -71,7 +71,7 @@ export function parseArgs(args) {
|
|
|
71
71
|
|
|
72
72
|
// For backward compatibility, also export as claudeArgs/foundClaude
|
|
73
73
|
return {
|
|
74
|
-
|
|
74
|
+
aigoArgs,
|
|
75
75
|
toolArgs,
|
|
76
76
|
tool,
|
|
77
77
|
// Backward compatibility
|
package/lib/config.js
CHANGED
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
6
|
-
<title>
|
|
6
|
+
<title>aigo - Claude Code Terminal</title>
|
|
7
7
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>▸</text></svg>">
|
|
8
8
|
|
|
9
9
|
<!-- xterm.js from CDN (use older compatible CDN paths) -->
|
|
@@ -483,7 +483,7 @@
|
|
|
483
483
|
<body>
|
|
484
484
|
<div id="app">
|
|
485
485
|
<header id="header">
|
|
486
|
-
<div class="logo">
|
|
486
|
+
<div class="logo">aigo</div>
|
|
487
487
|
<div class="header-right">
|
|
488
488
|
<div id="status" class="connecting">
|
|
489
489
|
<span class="dot"></span>
|
package/public/terminal.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* aigo terminal client
|
|
3
3
|
* Connects to the WebSocket server and renders the terminal
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -261,7 +261,7 @@
|
|
|
261
261
|
const notification = new Notification(title, {
|
|
262
262
|
body: body,
|
|
263
263
|
icon: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">▸</text></svg>',
|
|
264
|
-
tag: '
|
|
264
|
+
tag: 'aigo-notification',
|
|
265
265
|
requireInteraction: false
|
|
266
266
|
});
|
|
267
267
|
|
|
@@ -289,7 +289,7 @@
|
|
|
289
289
|
// Check patterns
|
|
290
290
|
for (const pattern of idlePatterns) {
|
|
291
291
|
if (pattern.test(outputBuffer)) {
|
|
292
|
-
showNotification('
|
|
292
|
+
showNotification('aigo', 'Claude Code is waiting for input');
|
|
293
293
|
outputBuffer = ''; // Reset to avoid repeated notifications
|
|
294
294
|
return;
|
|
295
295
|
}
|
|
@@ -630,7 +630,7 @@
|
|
|
630
630
|
|
|
631
631
|
if (event.code !== 1000) {
|
|
632
632
|
// Notify user of unexpected disconnect
|
|
633
|
-
showNotification('
|
|
633
|
+
showNotification('aigo', 'Connection lost. Attempting to reconnect...');
|
|
634
634
|
scheduleReconnect();
|
|
635
635
|
} else {
|
|
636
636
|
showOverlay(true, 'Session ended.', true);
|
|
@@ -737,7 +737,7 @@
|
|
|
737
737
|
connect();
|
|
738
738
|
|
|
739
739
|
// Expose for debugging
|
|
740
|
-
window.
|
|
740
|
+
window.aigoTerminal = {
|
|
741
741
|
terminal,
|
|
742
742
|
fitAddon,
|
|
743
743
|
reconnect: () => {
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**aigo** is a CLI tool that lets you run Claude Code remotely from any device. It wraps Claude in a tmux session and streams it to your browser via WebSocket.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Start Claude Code accessible from anywhere
|
|
9
|
-
|
|
9
|
+
aigo --tunnel ngrok claude --model sonnet
|
|
10
10
|
|
|
11
11
|
# Output:
|
|
12
12
|
# ✓ tmux session: claude-code
|
|
@@ -19,8 +19,8 @@ vigo --tunnel ngrok claude --model sonnet
|
|
|
19
19
|
## CLI Interface
|
|
20
20
|
|
|
21
21
|
```
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
aigo [aigo-options] claude [claude-args]
|
|
23
|
+
aigo [aigo-options] --attach <session>
|
|
24
24
|
|
|
25
25
|
Vigo Options:
|
|
26
26
|
--port, -p <port> Web server port (default: 3000)
|
|
@@ -30,19 +30,19 @@ Vigo Options:
|
|
|
30
30
|
--help, -h Show help
|
|
31
31
|
|
|
32
32
|
Examples:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
aigo claude # Basic: start claude in tmux + web server
|
|
34
|
+
aigo --tunnel ngrok claude # With ngrok tunnel
|
|
35
|
+
aigo -p 8080 -s myproject claude # Custom port and session
|
|
36
|
+
aigo claude --model sonnet # Pass args to claude
|
|
37
|
+
aigo --attach myproject # Attach to existing session
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Architecture
|
|
41
41
|
|
|
42
42
|
```mermaid
|
|
43
43
|
flowchart TB
|
|
44
|
-
subgraph cli [
|
|
45
|
-
CLI[bin/
|
|
44
|
+
subgraph cli [aigo CLI]
|
|
45
|
+
CLI[bin/aigo.js]
|
|
46
46
|
CLI --> TMUX_MGR[tmux manager]
|
|
47
47
|
CLI --> SERVER[web server]
|
|
48
48
|
CLI --> TUNNEL[tunnel manager]
|
|
@@ -78,10 +78,10 @@ flowchart TB
|
|
|
78
78
|
## Project Structure
|
|
79
79
|
|
|
80
80
|
```
|
|
81
|
-
|
|
81
|
+
aigo/
|
|
82
82
|
├── package.json # dependencies + bin entry
|
|
83
83
|
├── bin/
|
|
84
|
-
│ └──
|
|
84
|
+
│ └── aigo.js # CLI entry point (#!/usr/bin/env node)
|
|
85
85
|
├── lib/
|
|
86
86
|
│ ├── cli.js # Argument parsing
|
|
87
87
|
│ ├── tmux.js # tmux session management
|
|
@@ -95,25 +95,25 @@ vigo/
|
|
|
95
95
|
|
|
96
96
|
## Implementation Details
|
|
97
97
|
|
|
98
|
-
### 1. CLI Entry Point (bin/
|
|
98
|
+
### 1. CLI Entry Point (bin/aigo.js)
|
|
99
99
|
|
|
100
100
|
- Shebang for direct execution: `#!/usr/bin/env node`
|
|
101
|
-
- Parse arguments, separating
|
|
101
|
+
- Parse arguments, separating aigo args from claude args
|
|
102
102
|
- Orchestrate: tmux → server → tunnel (if requested)
|
|
103
103
|
- Handle graceful shutdown (Ctrl+C kills tunnel, keeps tmux alive)
|
|
104
104
|
|
|
105
105
|
### 2. Argument Parsing (lib/cli.js)
|
|
106
106
|
|
|
107
107
|
```javascript
|
|
108
|
-
//
|
|
108
|
+
// aigo --port 8080 --tunnel ngrok claude --model sonnet --allowedTools Bash
|
|
109
109
|
//
|
|
110
110
|
// Parsed as:
|
|
111
|
-
//
|
|
111
|
+
// aigoArgs = { port: 8080, tunnel: 'ngrok', session: 'claude-code' }
|
|
112
112
|
// claudeArgs = ['--model', 'sonnet', '--allowedTools', 'Bash']
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
- Split on `claude` keyword
|
|
116
|
-
- Everything before =
|
|
116
|
+
- Everything before = aigo options
|
|
117
117
|
- Everything after = passed to claude CLI
|
|
118
118
|
|
|
119
119
|
### 3. tmux Manager (lib/tmux.js)
|
|
@@ -164,7 +164,7 @@ console.log(`Tunnel URL: ${tunnel.url}`);
|
|
|
164
164
|
|
|
165
165
|
## Token Security
|
|
166
166
|
|
|
167
|
-
- Generated once per `
|
|
167
|
+
- Generated once per `aigo` invocation
|
|
168
168
|
- 32 bytes from `crypto.randomBytes()` → 64 char hex
|
|
169
169
|
- Embedded in URL path: `/t/<token>`
|
|
170
170
|
- Required for both HTTP and WebSocket
|
|
@@ -176,12 +176,12 @@ console.log(`Tunnel URL: ${tunnel.url}`);
|
|
|
176
176
|
# 1. Install globally (or use npx)
|
|
177
177
|
npm install -g .
|
|
178
178
|
|
|
179
|
-
# 2. Run
|
|
180
|
-
|
|
179
|
+
# 2. Run aigo with Claude
|
|
180
|
+
aigo --tunnel ngrok claude --model sonnet
|
|
181
181
|
|
|
182
182
|
# 3. Output shows access URL
|
|
183
183
|
# ┌─────────────────────────────────────────────────┐
|
|
184
|
-
# │
|
|
184
|
+
# │ aigo - Claude Code Web Terminal │
|
|
185
185
|
# ├─────────────────────────────────────────────────┤
|
|
186
186
|
# │ tmux session: claude-code │
|
|
187
187
|
# │ Local server: http://localhost:3000 │
|
|
@@ -194,18 +194,18 @@ vigo --tunnel ngrok claude --model sonnet
|
|
|
194
194
|
# 4. Open URL on phone/tablet - full Claude Code access!
|
|
195
195
|
|
|
196
196
|
# 5. Ctrl+C stops server + tunnel, tmux session persists
|
|
197
|
-
# Re-attach later with:
|
|
197
|
+
# Re-attach later with: aigo --attach claude-code
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
## Dependencies
|
|
201
201
|
|
|
202
202
|
```json
|
|
203
203
|
{
|
|
204
|
-
"name": "
|
|
204
|
+
"name": "aigo",
|
|
205
205
|
"version": "1.0.0",
|
|
206
206
|
"type": "module",
|
|
207
207
|
"bin": {
|
|
208
|
-
"
|
|
208
|
+
"aigo": "./bin/aigo.js"
|
|
209
209
|
},
|
|
210
210
|
"dependencies": {
|
|
211
211
|
"express": "^4.21.0",
|
|
@@ -226,14 +226,14 @@ No build step. Frontend loads xterm.js from CDN.
|
|
|
226
226
|
### New CLI option
|
|
227
227
|
|
|
228
228
|
```bash
|
|
229
|
-
|
|
229
|
+
aigo --tunnel relay claude # Use self-hosted WebRTC relay
|
|
230
230
|
```
|
|
231
231
|
|
|
232
232
|
### Architecture
|
|
233
233
|
|
|
234
234
|
```mermaid
|
|
235
235
|
sequenceDiagram
|
|
236
|
-
participant Local as
|
|
236
|
+
participant Local as aigo (local)
|
|
237
237
|
participant Signal as Railway Signaling Server
|
|
238
238
|
participant Browser as Mobile Browser
|
|
239
239
|
|
|
@@ -248,7 +248,7 @@ sequenceDiagram
|
|
|
248
248
|
### Components to add
|
|
249
249
|
|
|
250
250
|
1. **Signaling server** (new repo, deploy to Railway)
|
|
251
|
-
2. **WebRTC module** in
|
|
251
|
+
2. **WebRTC module** in aigo (`lib/webrtc.js`)
|
|
252
252
|
3. **Frontend WebRTC client**
|
|
253
253
|
|
|
254
254
|
### Benefits
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
# Cursor CLI Support for
|
|
1
|
+
# Cursor CLI Support for aigo
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
Add support for Cursor Agent CLI alongside Claude Code, allowing
|
|
5
|
+
Add support for Cursor Agent CLI alongside Claude Code, allowing aigo to stream either tool to a browser via WebSocket. This enables users with Cursor subscriptions to access Cursor's AI features remotely.
|
|
6
6
|
|
|
7
7
|
## Architecture Comparison
|
|
8
8
|
|
|
9
9
|
```mermaid
|
|
10
10
|
flowchart TB
|
|
11
11
|
subgraph current [Current: Claude Code]
|
|
12
|
-
|
|
12
|
+
aigo1[aigo] --> tmux1[tmux session]
|
|
13
13
|
tmux1 --> claude[claude CLI]
|
|
14
14
|
claude --> ws1[WebSocket]
|
|
15
15
|
ws1 --> browser1[Browser xterm.js]
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
subgraph new [New: Cursor Agent]
|
|
19
|
-
|
|
19
|
+
aigo2[aigo] --> tmux2[tmux session]
|
|
20
20
|
tmux2 --> agent[agent CLI]
|
|
21
21
|
agent --> ws2[WebSocket]
|
|
22
22
|
ws2 --> browser2[Browser xterm.js]
|
|
@@ -95,10 +95,10 @@ export function getCursorAgentPath() {
|
|
|
95
95
|
|
|
96
96
|
**For Cursor CLI:**
|
|
97
97
|
|
|
98
|
-
1. **Browser login (recommended for interactive)**: User runs `agent login` before starting
|
|
98
|
+
1. **Browser login (recommended for interactive)**: User runs `agent login` before starting aigo
|
|
99
99
|
2. **API key (for headless/automation)**: Set `CURSOR_API_KEY` environment variable
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
aigo should:
|
|
102
102
|
|
|
103
103
|
- Check if authentication is configured before starting
|
|
104
104
|
- Provide clear error messages if not authenticated
|
|
@@ -108,18 +108,18 @@ vigo should:
|
|
|
108
108
|
|
|
109
109
|
```bash
|
|
110
110
|
# Start Claude Code (existing)
|
|
111
|
-
|
|
111
|
+
aigo claude
|
|
112
112
|
|
|
113
113
|
# Start Cursor Agent (new)
|
|
114
|
-
|
|
114
|
+
aigo cursor
|
|
115
115
|
# or
|
|
116
|
-
|
|
116
|
+
aigo agent
|
|
117
117
|
|
|
118
118
|
# With tunnel for remote access
|
|
119
|
-
|
|
119
|
+
aigo --tunnel ngrok cursor
|
|
120
120
|
|
|
121
121
|
# Pass arguments to Cursor Agent
|
|
122
|
-
|
|
122
|
+
aigo cursor --model gpt-5
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
## Requirements Update
|
|
@@ -134,6 +134,6 @@ Add to README:
|
|
|
134
134
|
|
|
135
135
|
1. [lib/cli.js](lib/cli.js) - Add cursor/agent command detection
|
|
136
136
|
2. [lib/tmux.js](lib/tmux.js) - Generalize command execution
|
|
137
|
-
3. [bin/
|
|
137
|
+
3. [bin/aigo.js](bin/aigo.js) - Update startup logic for both tools
|
|
138
138
|
4. [README.md](README.md) - Document Cursor support
|
|
139
139
|
5. [lib/cursor.js](lib/cursor.js) - Cursor-specific utilities
|