@xyz-credit/agent-cli 1.2.0 → 1.2.2

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": "@xyz-credit/agent-cli",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "CLI for onboarding AI agents to xyz.credit — Device Flow Auth, MCP Bridge, Service Marketplace, Daemonization & Cloud Export",
5
5
  "bin": {
6
6
  "xyz-agent": "./bin/xyz-agent.js"
@@ -290,6 +290,26 @@ async function dashboardStart(creds) {
290
290
  } catch (e) {
291
291
  dashboard.addLog(`{red-fg}Send failed:{/red-fg} ${e.message}`);
292
292
  }
293
+ } else if (cmd.type === 'retry-mcp') {
294
+ // Retry MCP connection
295
+ if (localMcpUrl) {
296
+ dashboard.addLog(`Retrying MCP: ${localMcpUrl}`);
297
+ try {
298
+ const discovery = await discoverAndRegisterServices();
299
+ if (discovery) {
300
+ dashboard.addLog(`{green-fg}MCP reconnected:{/green-fg} ${discovery.tools_count} tools found${discovery.registered ? ' (auto-registered)' : ''}`);
301
+ dashboard.updateConnectivity(true, true);
302
+ } else {
303
+ dashboard.addLog('{red-fg}MCP retry failed:{/red-fg} Could not discover tools');
304
+ dashboard.updateConnectivity(true, false);
305
+ }
306
+ } catch (e) {
307
+ dashboard.addLog(`{red-fg}MCP retry error:{/red-fg} ${e.message}`);
308
+ dashboard.updateConnectivity(true, false);
309
+ }
310
+ } else {
311
+ dashboard.addLog('{yellow-fg}No MCP URL configured.{/yellow-fg} Run setup to configure.');
312
+ }
293
313
  } else if (cmd.type === 'task') {
294
314
  dashboard.addLog(`Agent thought: User asked me to "${cmd.command}". Executing...`);
295
315
  // Check for pending tasks as the user requested
@@ -8,7 +8,6 @@
8
8
  * - Priority Task Input: User can type commands at any time
9
9
  */
10
10
  const blessed = require('blessed');
11
- const chalk = require('chalk');
12
11
  const { EventEmitter } = require('events');
13
12
 
14
13
  class Dashboard {
@@ -18,11 +17,13 @@ class Dashboard {
18
17
  this.logBox = null;
19
18
  this.statusBox = null;
20
19
  this.inputBox = null;
20
+ this.helpText = null;
21
21
  this.statsBox = null;
22
22
  this.logs = [];
23
23
  this.status = { status: 'starting', cycleCount: 0, uptimeSeconds: 0 };
24
24
  this.unreadCount = 0;
25
- this.onUserCommand = null;
25
+ this._renderTimer = null;
26
+ this._logQueue = [];
26
27
  }
27
28
 
28
29
  start(agentName, platformUrl, mcpUrl) {
@@ -30,6 +31,7 @@ class Dashboard {
30
31
  smartCSR: true,
31
32
  title: `xyz-agent — ${agentName}`,
32
33
  fullUnicode: true,
34
+ grabKeys: false,
33
35
  });
34
36
 
35
37
  // ── Status Bar (top) ──
@@ -55,7 +57,7 @@ class Dashboard {
55
57
  this._updateStats(agentName, platformUrl, mcpUrl);
56
58
 
57
59
  // ── Activity Logs (left) ──
58
- this.logBox = blessed.log({
60
+ this.logBox = blessed.box({
59
61
  parent: this.screen,
60
62
  top: 3, left: 0, width: '65%', height: '60%',
61
63
  tags: true,
@@ -78,47 +80,56 @@ class Dashboard {
78
80
  style: { border: { fg: 'green' }, bg: 'black' },
79
81
  });
80
82
 
81
- this.inputBox = blessed.textbox({
83
+ this.inputBox = blessed.textarea({
82
84
  parent: inputContainer,
83
85
  top: 0, left: 1, right: 1, height: 1,
84
- inputOnFocus: true,
86
+ keys: true,
87
+ mouse: false,
88
+ inputOnFocus: false,
85
89
  style: { fg: 'white', bg: 'black' },
86
90
  });
87
91
 
88
- // Help text
89
- blessed.text({
92
+ // Help text — use white so it's actually visible
93
+ this.helpText = blessed.text({
90
94
  parent: inputContainer,
91
95
  top: 1, left: 1,
92
96
  tags: true,
93
- content: '{gray-fg}Commands: status | sync | post | inbox | msg <id> <text> | quit{/gray-fg}',
94
- style: { bg: 'black' },
97
+ content: '{white-fg}Commands: status | sync | post | inbox | retry | msg <id> <text> | quit{/white-fg}',
98
+ style: { fg: 'white', bg: 'black' },
95
99
  });
96
100
 
97
- // Handle input
98
- this.inputBox.on('submit', (value) => {
99
- if (value && value.trim()) {
100
- this._handleInput(value.trim());
101
+ // Handle enter key on textarea manually
102
+ this.inputBox.key('enter', () => {
103
+ const value = this.inputBox.getValue().replace(/\n/g, '').trim();
104
+ if (value) {
105
+ this._handleInput(value);
101
106
  }
102
107
  this.inputBox.clearValue();
103
108
  this.inputBox.focus();
104
- this.screen.render();
109
+ this._render();
110
+ });
111
+
112
+ // Escape/Ctrl-C to quit
113
+ this.inputBox.key(['escape', 'C-c'], () => {
114
+ this.ee.emit('quit');
105
115
  });
106
116
 
107
- // Key bindings
108
- this.screen.key(['escape', 'C-c'], () => {
117
+ // Global Ctrl-C fallback
118
+ this.screen.key(['C-c'], () => {
109
119
  this.ee.emit('quit');
110
120
  });
111
121
 
122
+ // Tab to re-focus input
112
123
  this.screen.key(['tab'], () => {
113
124
  this.inputBox.focus();
114
- this.screen.render();
125
+ this._render();
115
126
  });
116
127
 
117
- // Focus input
128
+ // Focus input and do initial render
118
129
  this.inputBox.focus();
119
130
  this.screen.render();
120
131
 
121
- this.addLog('Dashboard initialized. Press Tab to focus input.');
132
+ this.addLog('Dashboard initialized. Type commands below.');
122
133
  this.addLog(`Agent: ${agentName}`);
123
134
  this.addLog(`Platform: ${platformUrl}`);
124
135
  if (mcpUrl) this.addLog(`MCP: ${mcpUrl}`);
@@ -155,6 +166,12 @@ class Dashboard {
155
166
  return;
156
167
  }
157
168
 
169
+ if (lower === 'retry' || lower === 'reconnect') {
170
+ this.addLog('Retrying MCP connection...');
171
+ this.ee.emit('user-command', { type: 'retry-mcp' });
172
+ return;
173
+ }
174
+
158
175
  // msg <agent_id> <message text>
159
176
  if (lower.startsWith('msg ') || lower.startsWith('message ')) {
160
177
  const parts = cmd.split(/\s+/);
@@ -179,10 +196,31 @@ class Dashboard {
179
196
  const formatted = `{gray-fg}[${ts}]{/gray-fg} ${msg}`;
180
197
  this.logs.push(formatted);
181
198
  if (this.logs.length > 500) this.logs.shift();
182
- if (this.logBox) {
183
- this.logBox.pushLine(formatted);
184
- this.logBox.setScrollPerc(100);
185
- if (this.screen) this.screen.render();
199
+ this._logQueue.push(formatted);
200
+ this._scheduleFlush();
201
+ }
202
+
203
+ _scheduleFlush() {
204
+ if (this._renderTimer) return;
205
+ this._renderTimer = setTimeout(() => {
206
+ this._renderTimer = null;
207
+ this._flushLogs();
208
+ }, 50);
209
+ }
210
+
211
+ _flushLogs() {
212
+ if (!this.logBox || this._logQueue.length === 0) return;
213
+ const lines = this._logQueue.splice(0);
214
+ for (const line of lines) {
215
+ this.logBox.pushLine(line);
216
+ }
217
+ this.logBox.setScrollPerc(100);
218
+ this._render();
219
+ }
220
+
221
+ _render() {
222
+ if (this.screen) {
223
+ try { this.screen.render(); } catch { /* ignore render errors */ }
186
224
  }
187
225
  }
188
226
 
@@ -204,13 +242,12 @@ class Dashboard {
204
242
  `Last Ping: {gray-fg}${data.lastPing ? new Date(data.lastPing).toLocaleTimeString() : '-'}{/gray-fg}` +
205
243
  msgBadge
206
244
  );
207
- if (this.screen) this.screen.render();
245
+ this._render();
208
246
  }
209
247
  }
210
248
 
211
249
  updateUnreadCount(count) {
212
250
  this.unreadCount = count;
213
- // Re-render status bar with new count
214
251
  if (this.status) this.updateHeartbeat(this.status);
215
252
  }
216
253
 
@@ -250,13 +287,12 @@ class Dashboard {
250
287
 
251
288
  updateConnectivity(hub, mcp) {
252
289
  if (!this.statsBox) return;
253
- // Find and update connectivity lines
254
290
  const content = this.statsBox.getContent();
255
291
  let updated = content
256
292
  .replace(/Hub.*\n.*Status: .*/, `Hub\n Status: ${hub ? '{green-fg}Connected{/green-fg}' : '{red-fg}Disconnected{/red-fg}'}`)
257
293
  .replace(/MCP.*\n.*Status: .*/, `MCP\n Status: ${mcp ? '{green-fg}Connected{/green-fg}' : mcp === null ? '{gray-fg}N/A{/gray-fg}' : '{red-fg}Disconnected{/red-fg}'}`);
258
294
  this.statsBox.setContent(updated);
259
- if (this.screen) this.screen.render();
295
+ this._render();
260
296
  }
261
297
 
262
298
  _formatUptime(seconds) {
@@ -268,6 +304,10 @@ class Dashboard {
268
304
  }
269
305
 
270
306
  destroy() {
307
+ if (this._renderTimer) {
308
+ clearTimeout(this._renderTimer);
309
+ this._renderTimer = null;
310
+ }
271
311
  if (this.screen) {
272
312
  this.screen.destroy();
273
313
  this.screen = null;