@xyz-credit/agent-cli 1.2.1 → 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 +1 -1
- package/src/commands/start.js +20 -0
- package/src/services/dashboard.js +65 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xyz-credit/agent-cli",
|
|
3
|
-
"version": "1.2.
|
|
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"
|
package/src/commands/start.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
83
|
+
this.inputBox = blessed.textarea({
|
|
82
84
|
parent: inputContainer,
|
|
83
85
|
top: 0, left: 1, right: 1, height: 1,
|
|
84
|
-
|
|
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: '{
|
|
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
|
|
98
|
-
this.inputBox.
|
|
99
|
-
|
|
100
|
-
|
|
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.
|
|
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
|
-
//
|
|
108
|
-
this.screen.key(['
|
|
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.
|
|
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.
|
|
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,20 +196,32 @@ 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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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);
|
|
186
216
|
}
|
|
217
|
+
this.logBox.setScrollPerc(100);
|
|
218
|
+
this._render();
|
|
187
219
|
}
|
|
188
220
|
|
|
189
|
-
|
|
190
|
-
if (this.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
this._renderPending = false;
|
|
194
|
-
if (this.screen) this.screen.render();
|
|
195
|
-
});
|
|
221
|
+
_render() {
|
|
222
|
+
if (this.screen) {
|
|
223
|
+
try { this.screen.render(); } catch { /* ignore render errors */ }
|
|
224
|
+
}
|
|
196
225
|
}
|
|
197
226
|
|
|
198
227
|
updateHeartbeat(data) {
|
|
@@ -213,13 +242,12 @@ class Dashboard {
|
|
|
213
242
|
`Last Ping: {gray-fg}${data.lastPing ? new Date(data.lastPing).toLocaleTimeString() : '-'}{/gray-fg}` +
|
|
214
243
|
msgBadge
|
|
215
244
|
);
|
|
216
|
-
|
|
245
|
+
this._render();
|
|
217
246
|
}
|
|
218
247
|
}
|
|
219
248
|
|
|
220
249
|
updateUnreadCount(count) {
|
|
221
250
|
this.unreadCount = count;
|
|
222
|
-
// Re-render status bar with new count
|
|
223
251
|
if (this.status) this.updateHeartbeat(this.status);
|
|
224
252
|
}
|
|
225
253
|
|
|
@@ -259,13 +287,12 @@ class Dashboard {
|
|
|
259
287
|
|
|
260
288
|
updateConnectivity(hub, mcp) {
|
|
261
289
|
if (!this.statsBox) return;
|
|
262
|
-
// Find and update connectivity lines
|
|
263
290
|
const content = this.statsBox.getContent();
|
|
264
291
|
let updated = content
|
|
265
292
|
.replace(/Hub.*\n.*Status: .*/, `Hub\n Status: ${hub ? '{green-fg}Connected{/green-fg}' : '{red-fg}Disconnected{/red-fg}'}`)
|
|
266
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}'}`);
|
|
267
294
|
this.statsBox.setContent(updated);
|
|
268
|
-
|
|
295
|
+
this._render();
|
|
269
296
|
}
|
|
270
297
|
|
|
271
298
|
_formatUptime(seconds) {
|
|
@@ -277,6 +304,10 @@ class Dashboard {
|
|
|
277
304
|
}
|
|
278
305
|
|
|
279
306
|
destroy() {
|
|
307
|
+
if (this._renderTimer) {
|
|
308
|
+
clearTimeout(this._renderTimer);
|
|
309
|
+
this._renderTimer = null;
|
|
310
|
+
}
|
|
280
311
|
if (this.screen) {
|
|
281
312
|
this.screen.destroy();
|
|
282
313
|
this.screen = null;
|