agentgui 1.0.334 → 1.0.336

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.334",
3
+ "version": "1.0.336",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -3419,6 +3419,35 @@ wss.on('connection', (ws, req) => {
3419
3419
  requestId: data.requestId,
3420
3420
  timestamp: Date.now()
3421
3421
  }));
3422
+ } else if (data.type === 'terminal_start') {
3423
+ if (ws.terminalProc) {
3424
+ try { ws.terminalProc.kill(); } catch(e) {}
3425
+ }
3426
+ const { spawn } = require('child_process');
3427
+ const shell = process.env.SHELL || '/bin/bash';
3428
+ const cwd = data.cwd || process.env.STARTUP_CWD || process.env.HOME || '/';
3429
+ const proc = spawn(shell, [], { cwd, env: { ...process.env, TERM: 'xterm-256color', COLORTERM: 'truecolor' }, stdio: ['pipe', 'pipe', 'pipe'] });
3430
+ ws.terminalProc = proc;
3431
+ proc.stdout.on('data', (chunk) => {
3432
+ if (ws.readyState === 1) ws.send(JSON.stringify({ type: 'terminal_output', data: chunk.toString('base64'), encoding: 'base64' }));
3433
+ });
3434
+ proc.stderr.on('data', (chunk) => {
3435
+ if (ws.readyState === 1) ws.send(JSON.stringify({ type: 'terminal_output', data: chunk.toString('base64'), encoding: 'base64' }));
3436
+ });
3437
+ proc.on('exit', (code) => {
3438
+ if (ws.readyState === 1) ws.send(JSON.stringify({ type: 'terminal_exit', code }));
3439
+ ws.terminalProc = null;
3440
+ });
3441
+ ws.send(JSON.stringify({ type: 'terminal_started', timestamp: Date.now() }));
3442
+ } else if (data.type === 'terminal_input') {
3443
+ if (ws.terminalProc && ws.terminalProc.stdin.writable) {
3444
+ ws.terminalProc.stdin.write(Buffer.from(data.data, 'base64'));
3445
+ }
3446
+ } else if (data.type === 'terminal_stop') {
3447
+ if (ws.terminalProc) {
3448
+ try { ws.terminalProc.kill(); } catch(e) {}
3449
+ ws.terminalProc = null;
3450
+ }
3422
3451
  }
3423
3452
  } catch (e) {
3424
3453
  console.error('WebSocket message parse error:', e.message);
@@ -3432,6 +3461,7 @@ wss.on('connection', (ws, req) => {
3432
3461
 
3433
3462
  ws.on('pong', () => { ws.isAlive = true; });
3434
3463
  ws.on('close', () => {
3464
+ if (ws.terminalProc) { try { ws.terminalProc.kill(); } catch(e) {} ws.terminalProc = null; }
3435
3465
  syncClients.delete(ws);
3436
3466
  for (const sub of ws.subscriptions) {
3437
3467
  const idx = subscriptionIndex.get(sub);
package/static/index.html CHANGED
@@ -63,7 +63,6 @@
63
63
  .app-shell {
64
64
  display: flex;
65
65
  height: 100dvh;
66
- height: 100vh;
67
66
  overflow: hidden;
68
67
  padding-top: env(safe-area-inset-top);
69
68
  padding-bottom: env(safe-area-inset-bottom);
@@ -496,7 +495,7 @@
496
495
  .terminal-container {
497
496
  flex: 1; display: flex; flex-direction: column; overflow: hidden; background: #1e1e1e;
498
497
  }
499
- .terminal-output { flex: 1; overflow: hidden; }
498
+ .terminal-output { flex: 1; overflow: hidden; position: relative; }
500
499
 
501
500
  .models-container {
502
501
  flex: 1; display: flex; flex-direction: column; overflow-y: auto;
@@ -3042,12 +3041,12 @@
3042
3041
  </div>
3043
3042
 
3044
3043
  <!-- View toggle bar (hidden by default) -->
3045
- <div class="view-toggle-bar" id="viewToggleBar" style="display:none;">
3044
+ <div class="view-toggle-bar" id="viewToggleBar">
3046
3045
  <button class="view-toggle-btn active" data-view="chat">Chat</button>
3047
3046
  <button class="view-toggle-btn" data-view="files">Files</button>
3048
3047
  <button class="view-toggle-btn" data-view="voice" style="display:none;">Voice</button>
3049
3048
  <button class="view-toggle-btn" data-view="models">Models</button>
3050
- <button class="view-toggle-btn" data-view="terminal" id="terminalTabBtn" style="display:none;">Terminal</button>
3049
+ <button class="view-toggle-btn" data-view="terminal" id="terminalTabBtn">Terminal</button>
3051
3050
  </div>
3052
3051
 
3053
3052
  <!-- Messages scroll area -->
@@ -3189,6 +3188,7 @@
3189
3188
  <script defer src="/gm/js/client.js"></script>
3190
3189
  <script type="module" src="/gm/js/voice.js"></script>
3191
3190
  <script defer src="/gm/js/features.js"></script>
3191
+ <script defer src="/gm/js/terminal.js"></script>
3192
3192
  <script defer src="/gm/js/script-runner.js"></script>
3193
3193
  <script defer src="/gm/js/agent-auth.js"></script>
3194
3194
 
@@ -0,0 +1,105 @@
1
+ (function() {
2
+ var ws = null;
3
+ var term = null;
4
+ var fitAddon = null;
5
+ var termActive = false;
6
+ var BASE = window.__BASE_URL || '';
7
+
8
+ function getWsUrl() {
9
+ var proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
10
+ return proto + '//' + location.host + BASE + '/sync';
11
+ }
12
+
13
+ function ensureTerm() {
14
+ var output = document.getElementById('terminalOutput');
15
+ if (!output) return false;
16
+ if (term) return true;
17
+ if (!window.Terminal || !window.FitAddon) return false;
18
+
19
+ term = new Terminal({
20
+ cursorBlink: true,
21
+ fontSize: 14,
22
+ fontFamily: 'Menlo, Monaco, "Courier New", monospace',
23
+ theme: {
24
+ background: '#0d1117',
25
+ foreground: '#e6edf3',
26
+ cursor: '#e6edf3',
27
+ selectionBackground: '#3b4455'
28
+ },
29
+ convertEol: false,
30
+ scrollback: 5000
31
+ });
32
+ fitAddon = new FitAddon.FitAddon();
33
+ term.loadAddon(fitAddon);
34
+ output.innerHTML = '';
35
+ term.open(output);
36
+ fitAddon.fit();
37
+
38
+ term.onData(function(data) {
39
+ if (ws && ws.readyState === WebSocket.OPEN) {
40
+ var encoded = btoa(unescape(encodeURIComponent(data)));
41
+ ws.send(JSON.stringify({ type: 'terminal_input', data: encoded }));
42
+ }
43
+ });
44
+
45
+ window.addEventListener('resize', function() {
46
+ if (fitAddon) try { fitAddon.fit(); } catch(_) {}
47
+ });
48
+ return true;
49
+ }
50
+
51
+ function connectAndStart() {
52
+ if (ws && ws.readyState === WebSocket.OPEN) {
53
+ ws.send(JSON.stringify({ type: 'terminal_start', cwd: window.__STARTUP_CWD || undefined }));
54
+ return;
55
+ }
56
+ ws = new WebSocket(getWsUrl());
57
+ ws.onopen = function() {
58
+ ws.send(JSON.stringify({ type: 'terminal_start', cwd: window.__STARTUP_CWD || undefined }));
59
+ };
60
+ ws.onmessage = function(e) {
61
+ try {
62
+ var msg = JSON.parse(e.data);
63
+ if (msg.type === 'terminal_output' && term) {
64
+ var raw = msg.encoding === 'base64'
65
+ ? decodeURIComponent(escape(atob(msg.data)))
66
+ : msg.data;
67
+ term.write(raw);
68
+ } else if (msg.type === 'terminal_exit' && term) {
69
+ term.write('\r\n[Process exited with code ' + msg.code + ']\r\n');
70
+ if (termActive) setTimeout(connectAndStart, 2000);
71
+ }
72
+ } catch(_) {}
73
+ };
74
+ ws.onclose = function() {
75
+ if (termActive) setTimeout(connectAndStart, 2000);
76
+ };
77
+ }
78
+
79
+ function startTerminal() {
80
+ if (!ensureTerm()) {
81
+ setTimeout(startTerminal, 200);
82
+ return;
83
+ }
84
+ termActive = true;
85
+ connectAndStart();
86
+ setTimeout(function() { if (fitAddon) try { fitAddon.fit(); } catch(_) {} }, 100);
87
+ }
88
+
89
+ function stopTerminal() {
90
+ termActive = false;
91
+ if (ws && ws.readyState === WebSocket.OPEN) {
92
+ ws.send(JSON.stringify({ type: 'terminal_stop' }));
93
+ }
94
+ }
95
+
96
+ window.addEventListener('view-switched', function(e) {
97
+ if (e.detail.view === 'terminal') {
98
+ startTerminal();
99
+ } else if (termActive) {
100
+ stopTerminal();
101
+ }
102
+ });
103
+
104
+ window.terminalModule = { start: startTerminal, stop: stopTerminal };
105
+ })();
@@ -741,7 +741,6 @@
741
741
  }
742
742
  } else if (block.type === 'result') {
743
743
  _voiceBreakNext = true;
744
- addVoiceResultBlock(block, isNew);
745
744
  } else {
746
745
  _voiceBreakNext = true;
747
746
  }
@@ -780,8 +779,6 @@
780
779
  hasContent = true;
781
780
  } else if (block.type === 'result') {
782
781
  _voiceBreakNext = true;
783
- addVoiceResultBlock(block, false);
784
- hasContent = true;
785
782
  } else {
786
783
  _voiceBreakNext = true;
787
784
  }