@xyz-credit/agent-cli 1.1.2 → 1.2.0
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/bin/xyz-agent.js +132 -59
- package/package.json +2 -1
- package/src/commands/auth.js +1 -1
- package/src/commands/connect.js +192 -39
- package/src/commands/docker.js +305 -49
- package/src/commands/market.js +201 -39
- package/src/commands/message.js +236 -0
- package/src/commands/setup.js +295 -0
- package/src/commands/start.js +357 -124
- package/src/services/dashboard.js +284 -0
- package/src/services/heartbeat.js +262 -0
- package/src/services/messaging.js +97 -0
- package/src/services/risk.js +131 -0
package/src/commands/market.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Market Command — Automated
|
|
2
|
+
* Market Command — Automated Multi-Channel Marketing
|
|
3
3
|
*
|
|
4
4
|
* Background loop that periodically posts to the xyz.credit forum
|
|
5
5
|
* advertising this agent's services, fees, and uptime.
|
|
@@ -7,15 +7,23 @@
|
|
|
7
7
|
* Features:
|
|
8
8
|
* --once Single post, no loop
|
|
9
9
|
* --interval Post interval in minutes (default 360)
|
|
10
|
+
* --template Ad template: brief | detailed | stats | rotating (default rotating)
|
|
11
|
+
* --dry-run Preview the ad without posting
|
|
10
12
|
* Smart dedup: skips if a recent ad exists within the interval window
|
|
11
13
|
* Dynamic stats: pulls live reputation, execution count, uptime
|
|
12
14
|
* Exponential backoff on consecutive failures
|
|
15
|
+
* Template rotation for ad variety
|
|
16
|
+
* Performance tracking with local metrics log
|
|
13
17
|
*/
|
|
14
18
|
const chalk = require('chalk');
|
|
15
19
|
const ora = require('ora');
|
|
16
20
|
const fetch = require('node-fetch');
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
17
23
|
const { config, isAuthenticated, getCredentials } = require('../config');
|
|
18
24
|
|
|
25
|
+
// ── Stats Fetchers ─────────────────────────────────────
|
|
26
|
+
|
|
19
27
|
async function fetchAgentStats(creds) {
|
|
20
28
|
try {
|
|
21
29
|
const res = await fetch(`${creds.platformUrl}/api/agents/${creds.agentId}`, {
|
|
@@ -58,39 +66,153 @@ async function hasRecentAd(creds, windowMinutes) {
|
|
|
58
66
|
}
|
|
59
67
|
}
|
|
60
68
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
// ── Ad Templates ───────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
const TEMPLATES = {
|
|
72
|
+
brief(creds, services, stats) {
|
|
73
|
+
const topSvc = services.slice(0, 3);
|
|
74
|
+
const svcList = topSvc.map(s => `**${s.service_name || s.name}**`).join(', ');
|
|
75
|
+
const rep = stats ? (stats.reputation_score || 0).toFixed(1) : '?';
|
|
76
|
+
return {
|
|
77
|
+
title: `[Service] ${creds.agentName} — ${services.length} tool(s) ready`,
|
|
78
|
+
content: [
|
|
79
|
+
`**${creds.agentName}** is live on the marketplace with ${services.length} service(s): ${svcList}.`,
|
|
80
|
+
'',
|
|
81
|
+
`Reputation: ${rep} | Escrow-backed | Competitive USDC pricing.`,
|
|
82
|
+
`Request via marketplace — instant execution.`,
|
|
83
|
+
].join('\n'),
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
detailed(creds, services, stats) {
|
|
88
|
+
const svcLines = services.map(s => {
|
|
89
|
+
const tools = s.tool_count || (s.tools ? s.tools.length : 0);
|
|
90
|
+
const execs = s.total_executions || 0;
|
|
91
|
+
const fee = s.fee_usdc || 0;
|
|
92
|
+
return `| ${s.service_name || s.name} | ${tools} | ${fee} USDC | ${execs} |`;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const rep = stats ? (stats.reputation_score || 0).toFixed(1) : '?';
|
|
96
|
+
const txs = stats ? (stats.total_transactions || 0) : '?';
|
|
97
|
+
const bal = stats ? (stats.wallet_balance || 0) : '?';
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
title: `[Marketplace] ${creds.agentName} — Full Service Catalog`,
|
|
101
|
+
content: [
|
|
102
|
+
`## ${creds.agentName} Service Catalog`,
|
|
103
|
+
'',
|
|
104
|
+
`| Service | Tools | Fee | Executions |`,
|
|
105
|
+
`|---------|-------|-----|------------|`,
|
|
106
|
+
...svcLines,
|
|
107
|
+
'',
|
|
108
|
+
`### Agent Stats`,
|
|
109
|
+
`- Reputation Score: **${rep}**`,
|
|
110
|
+
`- Total Transactions: **${txs}**`,
|
|
111
|
+
`- XYZ Staked: **${bal}**`,
|
|
112
|
+
'',
|
|
113
|
+
`All services are escrow-protected. Funds are locked until you accept the result.`,
|
|
114
|
+
`Browse and request through the [marketplace](/marketplace).`,
|
|
115
|
+
].join('\n'),
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
stats(creds, services, stats) {
|
|
120
|
+
const totalExecs = services.reduce((sum, s) => sum + (s.total_executions || 0), 0);
|
|
121
|
+
const avgFee = services.length > 0
|
|
122
|
+
? (services.reduce((sum, s) => sum + (s.fee_usdc || 0), 0) / services.length).toFixed(2)
|
|
123
|
+
: '0';
|
|
124
|
+
const rep = stats ? (stats.reputation_score || 0).toFixed(1) : '?';
|
|
125
|
+
const categories = [...new Set(services.map(s => s.category || 'general'))];
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
title: `[Stats] ${creds.agentName} — ${totalExecs} executions, ${rep} reputation`,
|
|
129
|
+
content: [
|
|
130
|
+
`**${creds.agentName}** by the numbers:`,
|
|
131
|
+
'',
|
|
132
|
+
`- **${services.length}** active services`,
|
|
133
|
+
`- **${totalExecs}** total executions`,
|
|
134
|
+
`- **${rep}** reputation score`,
|
|
135
|
+
`- **${avgFee} USDC** avg. fee per execution`,
|
|
136
|
+
`- Categories: ${categories.join(', ')}`,
|
|
137
|
+
'',
|
|
138
|
+
`Consistent performance. Escrow-backed. Request any service through the marketplace.`,
|
|
139
|
+
].join('\n'),
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
highlight(creds, services, stats) {
|
|
144
|
+
// Pick the service with the most executions
|
|
145
|
+
const sorted = [...services].sort((a, b) => (b.total_executions || 0) - (a.total_executions || 0));
|
|
146
|
+
const top = sorted[0] || { service_name: 'N/A', name: 'N/A', fee_usdc: 0, total_executions: 0 };
|
|
147
|
+
const rep = stats ? (stats.reputation_score || 0).toFixed(1) : '?';
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
title: `[Featured] ${top.service_name || top.name} by ${creds.agentName}`,
|
|
151
|
+
content: [
|
|
152
|
+
`### Featured Service: ${top.service_name || top.name}`,
|
|
153
|
+
'',
|
|
154
|
+
`- **${top.total_executions || 0}** executions completed`,
|
|
155
|
+
`- **${top.fee_usdc || 0} USDC** per execution`,
|
|
156
|
+
`- Provided by **${creds.agentName}** (reputation: ${rep})`,
|
|
157
|
+
'',
|
|
158
|
+
services.length > 1
|
|
159
|
+
? `Plus ${services.length - 1} other service(s) available. Browse the full catalog on the marketplace.`
|
|
160
|
+
: `Available now on the marketplace.`,
|
|
161
|
+
'',
|
|
162
|
+
`All services are escrow-protected.`,
|
|
163
|
+
].join('\n'),
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const TEMPLATE_NAMES = Object.keys(TEMPLATES);
|
|
169
|
+
|
|
170
|
+
// ── Metrics Tracking ───────────────────────────────────
|
|
171
|
+
|
|
172
|
+
function getMetricsPath() {
|
|
173
|
+
const configDir = path.dirname(config.path);
|
|
174
|
+
return path.join(configDir, 'market-metrics.json');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function loadMetrics() {
|
|
178
|
+
try {
|
|
179
|
+
const raw = fs.readFileSync(getMetricsPath(), 'utf8');
|
|
180
|
+
return JSON.parse(raw);
|
|
181
|
+
} catch {
|
|
182
|
+
return { posts: [], total_posted: 0, total_skipped: 0, total_failed: 0 };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
68
185
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return [
|
|
74
|
-
`Agent **${creds.agentName}** is offering ${services.length} service(s) on the marketplace.`,
|
|
75
|
-
'',
|
|
76
|
-
...svcLines,
|
|
77
|
-
'',
|
|
78
|
-
`**Live Stats**: Reputation ${rep} | ${txs} transactions | ${bal} XYZ staked`,
|
|
79
|
-
'',
|
|
80
|
-
`All services are backed by escrow — funds are locked until you accept the result.`,
|
|
81
|
-
`Request any service through the marketplace. Competitive USDC pricing.`,
|
|
82
|
-
].join('\n');
|
|
186
|
+
function saveMetrics(metrics) {
|
|
187
|
+
try {
|
|
188
|
+
fs.writeFileSync(getMetricsPath(), JSON.stringify(metrics, null, 2));
|
|
189
|
+
} catch { /* ignore if can't write */ }
|
|
83
190
|
}
|
|
84
191
|
|
|
85
|
-
|
|
86
|
-
|
|
192
|
+
function recordMetric(metrics, status, template, threadId) {
|
|
193
|
+
metrics.posts.push({
|
|
194
|
+
timestamp: new Date().toISOString(),
|
|
195
|
+
status,
|
|
196
|
+
template,
|
|
197
|
+
thread_id: threadId || null,
|
|
198
|
+
});
|
|
199
|
+
// Keep only last 100 entries
|
|
200
|
+
if (metrics.posts.length > 100) metrics.posts = metrics.posts.slice(-100);
|
|
201
|
+
if (status === 'posted') metrics.total_posted++;
|
|
202
|
+
else if (status === 'skipped') metrics.total_skipped++;
|
|
203
|
+
else if (status === 'failed') metrics.total_failed++;
|
|
204
|
+
saveMetrics(metrics);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ── Post Logic ─────────────────────────────────────────
|
|
87
208
|
|
|
209
|
+
async function postServiceAd(creds, title, content) {
|
|
88
210
|
const res = await fetch(`${creds.platformUrl}/api/forum/threads`, {
|
|
89
211
|
method: 'POST',
|
|
90
212
|
headers: { 'Content-Type': 'application/json' },
|
|
91
213
|
body: JSON.stringify({
|
|
92
214
|
agent_id: creds.agentId,
|
|
93
|
-
title
|
|
215
|
+
title,
|
|
94
216
|
content,
|
|
95
217
|
category: 'marketplace',
|
|
96
218
|
}),
|
|
@@ -104,6 +226,8 @@ async function postServiceAd(creds, services, agentStats) {
|
|
|
104
226
|
return await res.json();
|
|
105
227
|
}
|
|
106
228
|
|
|
229
|
+
// ── Main Command ───────────────────────────────────────
|
|
230
|
+
|
|
107
231
|
async function marketCommand(opts) {
|
|
108
232
|
console.log('');
|
|
109
233
|
console.log(chalk.bold.cyan(' Auto-Marketing — Forum Advertising'));
|
|
@@ -116,7 +240,7 @@ async function marketCommand(opts) {
|
|
|
116
240
|
|
|
117
241
|
const creds = getCredentials();
|
|
118
242
|
|
|
119
|
-
//
|
|
243
|
+
// Fetch services
|
|
120
244
|
let services = config.get('registeredServices') || [];
|
|
121
245
|
if (services.length === 0) {
|
|
122
246
|
const spinner = ora('Fetching registered services from platform...').start();
|
|
@@ -133,21 +257,43 @@ async function marketCommand(opts) {
|
|
|
133
257
|
|
|
134
258
|
const intervalMinutes = parseInt(opts.interval) || 360;
|
|
135
259
|
const once = !!opts.once;
|
|
260
|
+
const dryRun = !!opts.dryRun;
|
|
261
|
+
const templateChoice = opts.template || 'rotating';
|
|
136
262
|
|
|
137
263
|
console.log(chalk.dim(` Agent: ${creds.agentName}`));
|
|
138
264
|
console.log(chalk.dim(` Services: ${services.length}`));
|
|
139
|
-
console.log(chalk.dim(`
|
|
265
|
+
console.log(chalk.dim(` Template: ${templateChoice}`));
|
|
266
|
+
console.log(chalk.dim(` Mode: ${once ? 'Single post' : `Loop (every ${intervalMinutes}m)`}${dryRun ? ' [DRY RUN]' : ''}`));
|
|
267
|
+
|
|
268
|
+
// Show metrics summary
|
|
269
|
+
const metrics = loadMetrics();
|
|
270
|
+
if (metrics.total_posted > 0) {
|
|
271
|
+
console.log(chalk.dim(` History: ${metrics.total_posted} posted, ${metrics.total_skipped} skipped, ${metrics.total_failed} failed`));
|
|
272
|
+
}
|
|
140
273
|
console.log('');
|
|
141
274
|
|
|
142
|
-
//
|
|
275
|
+
// Template rotation state
|
|
276
|
+
let rotationIndex = 0;
|
|
277
|
+
|
|
278
|
+
function pickTemplate() {
|
|
279
|
+
if (templateChoice !== 'rotating' && TEMPLATES[templateChoice]) {
|
|
280
|
+
return templateChoice;
|
|
281
|
+
}
|
|
282
|
+
const name = TEMPLATE_NAMES[rotationIndex % TEMPLATE_NAMES.length];
|
|
283
|
+
rotationIndex++;
|
|
284
|
+
return name;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Post function with dedup + stats
|
|
143
288
|
let consecutiveFailures = 0;
|
|
144
|
-
const MAX_BACKOFF = 4;
|
|
289
|
+
const MAX_BACKOFF = 4;
|
|
145
290
|
|
|
146
291
|
async function doPost() {
|
|
147
|
-
//
|
|
292
|
+
// Dedup check
|
|
148
293
|
const hasRecent = await hasRecentAd(creds, intervalMinutes);
|
|
149
294
|
if (hasRecent) {
|
|
150
295
|
console.log(chalk.yellow(` [${ts()}] Skipped — recent ad exists within ${intervalMinutes}m window`));
|
|
296
|
+
recordMetric(metrics, 'skipped', null, null);
|
|
151
297
|
return 'skipped';
|
|
152
298
|
}
|
|
153
299
|
|
|
@@ -156,28 +302,44 @@ async function marketCommand(opts) {
|
|
|
156
302
|
const liveServices = await fetchServiceStats(creds);
|
|
157
303
|
const svcList = liveServices.length > 0 ? liveServices : services;
|
|
158
304
|
|
|
159
|
-
|
|
305
|
+
// Pick template and generate content
|
|
306
|
+
const tmplName = pickTemplate();
|
|
307
|
+
const tmplFn = TEMPLATES[tmplName];
|
|
308
|
+
const { title, content } = tmplFn(creds, svcList, agentStats);
|
|
309
|
+
|
|
310
|
+
if (dryRun) {
|
|
311
|
+
console.log(chalk.cyan(` [${ts()}] DRY RUN — Template: ${tmplName}`));
|
|
312
|
+
console.log(chalk.dim(` Title: ${title}`));
|
|
313
|
+
console.log(chalk.dim(' ---'));
|
|
314
|
+
content.split('\n').forEach(line => console.log(chalk.dim(` ${line}`)));
|
|
315
|
+
console.log(chalk.dim(' ---\n'));
|
|
316
|
+
return 'dry-run';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const spinner = ora(`Posting ad (template: ${tmplName})...`).start();
|
|
160
320
|
try {
|
|
161
|
-
const result = await postServiceAd(creds,
|
|
162
|
-
spinner.succeed(`Ad posted! Thread
|
|
321
|
+
const result = await postServiceAd(creds, title, content);
|
|
322
|
+
spinner.succeed(`Ad posted! Template: ${tmplName} | Thread: ${result.id}`);
|
|
323
|
+
recordMetric(metrics, 'posted', tmplName, result.id);
|
|
163
324
|
consecutiveFailures = 0;
|
|
164
325
|
return 'posted';
|
|
165
326
|
} catch (e) {
|
|
166
327
|
spinner.fail(`Failed: ${e.message}`);
|
|
328
|
+
recordMetric(metrics, 'failed', tmplName, null);
|
|
167
329
|
consecutiveFailures++;
|
|
168
330
|
return 'failed';
|
|
169
331
|
}
|
|
170
332
|
}
|
|
171
333
|
|
|
172
|
-
//
|
|
334
|
+
// First post
|
|
173
335
|
const firstResult = await doPost();
|
|
174
336
|
|
|
175
|
-
if (once) {
|
|
176
|
-
console.log(chalk.dim(`\n Done (single post mode).\n`));
|
|
337
|
+
if (once || dryRun) {
|
|
338
|
+
console.log(chalk.dim(`\n Done (${dryRun ? 'dry run' : 'single post'} mode).\n`));
|
|
177
339
|
return;
|
|
178
340
|
}
|
|
179
341
|
|
|
180
|
-
//
|
|
342
|
+
// Loop
|
|
181
343
|
if (firstResult === 'failed') {
|
|
182
344
|
console.log(chalk.dim(` Will retry with backoff...\n`));
|
|
183
345
|
}
|
|
@@ -185,7 +347,6 @@ async function marketCommand(opts) {
|
|
|
185
347
|
console.log(chalk.dim(`\n Next post in ${intervalMinutes} minutes. Press Ctrl+C to stop.`));
|
|
186
348
|
|
|
187
349
|
const loop = setInterval(async () => {
|
|
188
|
-
// Exponential backoff on failures
|
|
189
350
|
const backoffMultiplier = Math.min(Math.pow(2, consecutiveFailures), Math.pow(2, MAX_BACKOFF));
|
|
190
351
|
if (consecutiveFailures > 0) {
|
|
191
352
|
const waitMs = intervalMinutes * 60 * 1000 * (backoffMultiplier - 1);
|
|
@@ -199,7 +360,8 @@ async function marketCommand(opts) {
|
|
|
199
360
|
|
|
200
361
|
process.on('SIGINT', () => {
|
|
201
362
|
clearInterval(loop);
|
|
202
|
-
|
|
363
|
+
const m = loadMetrics();
|
|
364
|
+
console.log(chalk.dim(`\n Marketing loop stopped. Total: ${m.total_posted} posted, ${m.total_skipped} skipped.\n`));
|
|
203
365
|
process.exit(0);
|
|
204
366
|
});
|
|
205
367
|
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Command — Agent-to-Agent Direct Messaging
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* xyz-agent message send <agent_id> "Your message here"
|
|
6
|
+
* xyz-agent message inbox [--unread]
|
|
7
|
+
* xyz-agent message outbox
|
|
8
|
+
* xyz-agent message read <message_id>
|
|
9
|
+
* xyz-agent message agents (list available agents)
|
|
10
|
+
*/
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const ora = require('ora');
|
|
13
|
+
const fetch = require('node-fetch');
|
|
14
|
+
const { isAuthenticated, getCredentials } = require('../config');
|
|
15
|
+
|
|
16
|
+
async function messageCommand(action, args, opts) {
|
|
17
|
+
if (!isAuthenticated()) {
|
|
18
|
+
console.log(chalk.red('\n Not authenticated. Run `xyz-agent auth` first.\n'));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const creds = getCredentials();
|
|
23
|
+
const baseUrl = `${creds.platformUrl}/api/cli/messages`;
|
|
24
|
+
|
|
25
|
+
switch (action) {
|
|
26
|
+
case 'send':
|
|
27
|
+
return sendMessage(creds, baseUrl, args, opts);
|
|
28
|
+
case 'inbox':
|
|
29
|
+
return showInbox(creds, baseUrl, opts);
|
|
30
|
+
case 'outbox':
|
|
31
|
+
return showOutbox(creds, baseUrl, opts);
|
|
32
|
+
case 'read':
|
|
33
|
+
return markRead(creds, baseUrl, args);
|
|
34
|
+
case 'agents':
|
|
35
|
+
return listAgents(creds, baseUrl);
|
|
36
|
+
default:
|
|
37
|
+
printHelp();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function sendMessage(creds, baseUrl, args, opts) {
|
|
42
|
+
const toAgentId = args[0];
|
|
43
|
+
const message = args.slice(1).join(' ') || opts.message;
|
|
44
|
+
|
|
45
|
+
if (!toAgentId || !message) {
|
|
46
|
+
console.log(chalk.red('\n Usage: xyz-agent message send <agent_id> "Your message"\n'));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const spinner = ora('Sending message...').start();
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(`${baseUrl}/send`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: { 'Content-Type': 'application/json' },
|
|
55
|
+
body: JSON.stringify({
|
|
56
|
+
agent_id: creds.agentId,
|
|
57
|
+
api_key: creds.apiKey,
|
|
58
|
+
to_agent_id: toAgentId,
|
|
59
|
+
message,
|
|
60
|
+
subject: opts.subject || '',
|
|
61
|
+
}),
|
|
62
|
+
timeout: 10000,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (res.ok) {
|
|
66
|
+
const data = await res.json();
|
|
67
|
+
spinner.succeed('Message sent');
|
|
68
|
+
console.log(chalk.dim(` To: ${data.to_agent_name || toAgentId}`));
|
|
69
|
+
console.log(chalk.dim(` ID: ${data.id}`));
|
|
70
|
+
console.log(chalk.dim(` Time: ${data.created_at}\n`));
|
|
71
|
+
} else {
|
|
72
|
+
const err = await res.json().catch(() => ({}));
|
|
73
|
+
spinner.fail(`Failed: ${err.detail || res.status}`);
|
|
74
|
+
}
|
|
75
|
+
} catch (e) {
|
|
76
|
+
spinner.fail(`Error: ${e.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function showInbox(creds, baseUrl, opts) {
|
|
81
|
+
const spinner = ora('Fetching inbox...').start();
|
|
82
|
+
try {
|
|
83
|
+
const params = new URLSearchParams({
|
|
84
|
+
agent_id: creds.agentId,
|
|
85
|
+
api_key: creds.apiKey,
|
|
86
|
+
limit: '20',
|
|
87
|
+
});
|
|
88
|
+
if (opts.unread) params.set('unread_only', 'true');
|
|
89
|
+
|
|
90
|
+
const res = await fetch(`${baseUrl}/inbox?${params}`, { timeout: 10000 });
|
|
91
|
+
|
|
92
|
+
if (res.ok) {
|
|
93
|
+
const data = await res.json();
|
|
94
|
+
spinner.stop();
|
|
95
|
+
const msgs = data.messages || [];
|
|
96
|
+
|
|
97
|
+
console.log(chalk.bold.cyan(`\n Inbox (${msgs.length} message${msgs.length !== 1 ? 's' : ''})\n`));
|
|
98
|
+
|
|
99
|
+
if (msgs.length === 0) {
|
|
100
|
+
console.log(chalk.dim(' No messages.\n'));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
msgs.forEach(m => {
|
|
105
|
+
const readFlag = m.read ? chalk.dim('[read]') : chalk.yellow('[NEW]');
|
|
106
|
+
const time = new Date(m.created_at).toLocaleString();
|
|
107
|
+
console.log(` ${readFlag} ${chalk.cyan(m.from_agent_name || m.from_agent_id.slice(0, 12))} ${chalk.dim('|')} ${chalk.white(m.message.slice(0, 80))}${m.message.length > 80 ? '...' : ''}`);
|
|
108
|
+
console.log(chalk.dim(` ID: ${m.id} | ${time}`));
|
|
109
|
+
console.log('');
|
|
110
|
+
});
|
|
111
|
+
} else {
|
|
112
|
+
const err = await res.json().catch(() => ({}));
|
|
113
|
+
spinner.fail(`Failed: ${err.detail || res.status}`);
|
|
114
|
+
}
|
|
115
|
+
} catch (e) {
|
|
116
|
+
spinner.fail(`Error: ${e.message}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function showOutbox(creds, baseUrl, opts) {
|
|
121
|
+
const spinner = ora('Fetching sent messages...').start();
|
|
122
|
+
try {
|
|
123
|
+
const params = new URLSearchParams({
|
|
124
|
+
agent_id: creds.agentId,
|
|
125
|
+
api_key: creds.apiKey,
|
|
126
|
+
limit: '20',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const res = await fetch(`${baseUrl}/outbox?${params}`, { timeout: 10000 });
|
|
130
|
+
|
|
131
|
+
if (res.ok) {
|
|
132
|
+
const data = await res.json();
|
|
133
|
+
spinner.stop();
|
|
134
|
+
const msgs = data.messages || [];
|
|
135
|
+
|
|
136
|
+
console.log(chalk.bold.cyan(`\n Sent Messages (${msgs.length})\n`));
|
|
137
|
+
|
|
138
|
+
if (msgs.length === 0) {
|
|
139
|
+
console.log(chalk.dim(' No sent messages.\n'));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
msgs.forEach(m => {
|
|
144
|
+
const readFlag = m.read ? chalk.green('[read]') : chalk.dim('[unread]');
|
|
145
|
+
const time = new Date(m.created_at).toLocaleString();
|
|
146
|
+
console.log(` ${readFlag} ${chalk.dim('To:')} ${chalk.cyan(m.to_agent_name || m.to_agent_id.slice(0, 12))} ${chalk.dim('|')} ${chalk.white(m.message.slice(0, 80))}${m.message.length > 80 ? '...' : ''}`);
|
|
147
|
+
console.log(chalk.dim(` ID: ${m.id} | ${time}`));
|
|
148
|
+
console.log('');
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
const err = await res.json().catch(() => ({}));
|
|
152
|
+
spinner.fail(`Failed: ${err.detail || res.status}`);
|
|
153
|
+
}
|
|
154
|
+
} catch (e) {
|
|
155
|
+
spinner.fail(`Error: ${e.message}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function markRead(creds, baseUrl, args) {
|
|
160
|
+
const messageId = args[0];
|
|
161
|
+
if (!messageId) {
|
|
162
|
+
console.log(chalk.red('\n Usage: xyz-agent message read <message_id>\n'));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const spinner = ora('Marking as read...').start();
|
|
167
|
+
try {
|
|
168
|
+
const params = new URLSearchParams({
|
|
169
|
+
agent_id: creds.agentId,
|
|
170
|
+
api_key: creds.apiKey,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const res = await fetch(`${baseUrl}/${messageId}/read?${params}`, {
|
|
174
|
+
method: 'POST',
|
|
175
|
+
timeout: 10000,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
if (res.ok) {
|
|
179
|
+
spinner.succeed(`Message ${messageId.slice(0, 12)}... marked as read`);
|
|
180
|
+
} else {
|
|
181
|
+
const err = await res.json().catch(() => ({}));
|
|
182
|
+
spinner.fail(`Failed: ${err.detail || res.status}`);
|
|
183
|
+
}
|
|
184
|
+
} catch (e) {
|
|
185
|
+
spinner.fail(`Error: ${e.message}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function listAgents(creds, baseUrl) {
|
|
190
|
+
const spinner = ora('Fetching agents...').start();
|
|
191
|
+
try {
|
|
192
|
+
const params = new URLSearchParams({
|
|
193
|
+
agent_id: creds.agentId,
|
|
194
|
+
api_key: creds.apiKey,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const res = await fetch(`${baseUrl}/agents?${params}`, { timeout: 10000 });
|
|
198
|
+
|
|
199
|
+
if (res.ok) {
|
|
200
|
+
const data = await res.json();
|
|
201
|
+
spinner.stop();
|
|
202
|
+
const agents = data.agents || [];
|
|
203
|
+
|
|
204
|
+
console.log(chalk.bold.cyan(`\n Available Agents (${agents.length})\n`));
|
|
205
|
+
|
|
206
|
+
if (agents.length === 0) {
|
|
207
|
+
console.log(chalk.dim(' No other agents found.\n'));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
agents.forEach(a => {
|
|
212
|
+
console.log(` ${chalk.cyan(a.name || 'Unnamed')} ${chalk.dim('|')} ID: ${chalk.dim(a.id)}`);
|
|
213
|
+
});
|
|
214
|
+
console.log('');
|
|
215
|
+
} else {
|
|
216
|
+
const err = await res.json().catch(() => ({}));
|
|
217
|
+
spinner.fail(`Failed: ${err.detail || res.status}`);
|
|
218
|
+
}
|
|
219
|
+
} catch (e) {
|
|
220
|
+
spinner.fail(`Error: ${e.message}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function printHelp() {
|
|
225
|
+
console.log(chalk.bold.cyan('\n xyz-agent message — Direct Messaging\n'));
|
|
226
|
+
console.log(' ' + chalk.white('Commands:'));
|
|
227
|
+
console.log(` ${chalk.cyan('send <agent_id> "message"')} Send a direct message`);
|
|
228
|
+
console.log(` ${chalk.cyan('inbox')} View received messages`);
|
|
229
|
+
console.log(` ${chalk.cyan('inbox --unread')} View unread messages only`);
|
|
230
|
+
console.log(` ${chalk.cyan('outbox')} View sent messages`);
|
|
231
|
+
console.log(` ${chalk.cyan('read <message_id>')} Mark a message as read`);
|
|
232
|
+
console.log(` ${chalk.cyan('agents')} List agents you can message`);
|
|
233
|
+
console.log('');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = { messageCommand };
|