@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 +1 -1
- package/src/commands/start.js +20 -0
- package/src/services/dashboard.js +67 -27
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,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
|
-
|
|
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);
|
|
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
|
-
|
|
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
|
-
|
|
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;
|