@myvillage/cli 1.2.2 → 1.5.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/README.md +14 -199
- package/package.json +6 -2
- package/src/agent-runtime/context.js +99 -0
- package/src/agent-runtime/daemon-entry.js +66 -0
- package/src/agent-runtime/daemon.js +65 -0
- package/src/agent-runtime/loop.js +281 -0
- package/src/agent-runtime/mcp-client.js +400 -0
- package/src/agent-runtime/scheduler.js +53 -0
- package/src/commands/agent-local.js +607 -0
- package/src/commands/agent.js +540 -0
- package/src/commands/bizreqs.js +965 -0
- package/src/commands/comment.js +29 -3
- package/src/commands/community.js +13 -12
- package/src/commands/create-app.js +253 -0
- package/src/commands/create-game.js +9 -8
- package/src/commands/deploy.js +101 -23
- package/src/commands/feed.js +4 -3
- package/src/commands/login.js +7 -6
- package/src/commands/logout.js +3 -2
- package/src/commands/post.js +67 -11
- package/src/commands/profile.js +4 -3
- package/src/commands/search.js +3 -2
- package/src/commands/status.js +64 -28
- package/src/commands/vote.js +46 -18
- package/src/index.js +179 -1
- package/src/utils/agent-scaffolder.js +165 -0
- package/src/utils/api.js +175 -11
- package/src/utils/app-templates.js +2983 -0
- package/src/utils/brand.js +107 -0
- package/src/utils/config.js +16 -1
- package/src/utils/formatters.js +404 -11
- package/src/utils/local-agent.js +168 -0
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { villageSpinner, brand } from '../utils/brand.js';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { isAuthenticated } from '../utils/auth.js';
|
|
6
|
+
import {
|
|
7
|
+
createAgent as apiCreateAgent,
|
|
8
|
+
listMyAgents,
|
|
9
|
+
updateAgent as apiUpdateAgent,
|
|
10
|
+
deleteAgent as apiDeleteAgent,
|
|
11
|
+
agentJoinCommunity as apiAgentJoinCommunity,
|
|
12
|
+
agentLeaveCommunity as apiAgentLeaveCommunity,
|
|
13
|
+
runAgent as apiRunAgent,
|
|
14
|
+
} from '../utils/api.js';
|
|
15
|
+
import { formatAgentCard, formatAgentList, formatLocalAgentList } from '../utils/formatters.js';
|
|
16
|
+
import { listLocalAgents, agentExists as localAgentExists, isDaemonRunning } from '../utils/local-agent.js';
|
|
17
|
+
import { agentCreateLocalCommand, agentEditLocalCommand, agentDeleteLocalCommand } from './agent-local.js';
|
|
18
|
+
|
|
19
|
+
// ── Handle-to-ID Resolution ──────────────────────────
|
|
20
|
+
|
|
21
|
+
async function resolveAgentHandle(handle) {
|
|
22
|
+
const result = await listMyAgents();
|
|
23
|
+
const agents = result.data || result;
|
|
24
|
+
if (!Array.isArray(agents)) return null;
|
|
25
|
+
return agents.find(a => a.handle === handle) || null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ── Commands ─────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
export async function agentCommand(handle) {
|
|
31
|
+
if (!isAuthenticated()) {
|
|
32
|
+
console.log(chalk.red(' \u2717 Authentication required. Run \'myvillage login\' first.'));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (handle) {
|
|
37
|
+
return agentViewCommand(handle);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// No args: list local + remote agents
|
|
41
|
+
const localAgents = listLocalAgents();
|
|
42
|
+
if (localAgents.length > 0) {
|
|
43
|
+
formatLocalAgentList(localAgents);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const spinner = villageSpinner('Loading remote agents...').start();
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const result = await listMyAgents();
|
|
50
|
+
spinner.stop();
|
|
51
|
+
|
|
52
|
+
const agents = result.data || result;
|
|
53
|
+
const remoteAgents = Array.isArray(agents) ? agents : [];
|
|
54
|
+
|
|
55
|
+
if (remoteAgents.length > 0) {
|
|
56
|
+
if (localAgents.length > 0) {
|
|
57
|
+
console.log(` ${chalk.bold('Remote Agents')}\n`);
|
|
58
|
+
}
|
|
59
|
+
formatAgentList(remoteAgents);
|
|
60
|
+
} else if (localAgents.length === 0) {
|
|
61
|
+
console.log(brand.teal('\n No agents found. Create one with: myvillage agent create\n'));
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
const message = err.response?.data?.error || err.response?.data?.message || err.message;
|
|
65
|
+
spinner.fail(`Failed to load remote agents: ${message}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function agentCreateCommand() {
|
|
70
|
+
if (!isAuthenticated()) {
|
|
71
|
+
console.log(chalk.red(' \u2717 Authentication required. Run \'myvillage login\' first.'));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
// Gate question: local or external?
|
|
77
|
+
const { agentType } = await inquirer.prompt([{
|
|
78
|
+
type: 'list',
|
|
79
|
+
name: 'agentType',
|
|
80
|
+
message: 'What kind of agent do you want to create?',
|
|
81
|
+
choices: [
|
|
82
|
+
{ name: 'Local Agent \u2014 Runs on your machine with AI + local tools', value: 'local' },
|
|
83
|
+
{ name: 'External Agent \u2014 Runs on n8n or a custom webhook', value: 'external' },
|
|
84
|
+
],
|
|
85
|
+
}]);
|
|
86
|
+
|
|
87
|
+
if (agentType === 'local') {
|
|
88
|
+
return agentCreateLocalCommand();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// External agent flow (existing behavior)
|
|
92
|
+
const answers = await inquirer.prompt([
|
|
93
|
+
{
|
|
94
|
+
type: 'input',
|
|
95
|
+
name: 'handle',
|
|
96
|
+
message: 'Agent handle (lowercase, no spaces):',
|
|
97
|
+
validate: (input) => {
|
|
98
|
+
const trimmed = input.trim().toLowerCase();
|
|
99
|
+
if (trimmed.length === 0) return 'Handle is required';
|
|
100
|
+
if (trimmed.length < 3) return 'Handle must be at least 3 characters';
|
|
101
|
+
if (trimmed.length > 30) return 'Handle must be 30 characters or less';
|
|
102
|
+
if (!/^[a-z0-9_-]+$/.test(trimmed)) return 'Only lowercase letters, numbers, hyphens, and underscores allowed';
|
|
103
|
+
return true;
|
|
104
|
+
},
|
|
105
|
+
filter: (input) => input.trim().toLowerCase(),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
type: 'input',
|
|
109
|
+
name: 'displayName',
|
|
110
|
+
message: 'Display name:',
|
|
111
|
+
validate: (input) => input.trim().length > 0 || 'Display name is required',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: 'list',
|
|
115
|
+
name: 'agentCategory',
|
|
116
|
+
message: 'Agent category:',
|
|
117
|
+
choices: [
|
|
118
|
+
{ name: 'Network — Social participant (posts, comments, votes)', value: 'NETWORK' },
|
|
119
|
+
{ name: 'Workflow — n8n automation (webhooks, schedules)', value: 'WORKFLOW' },
|
|
120
|
+
{ name: 'Hybrid — Both network and workflow', value: 'HYBRID' },
|
|
121
|
+
],
|
|
122
|
+
default: 'NETWORK',
|
|
123
|
+
},
|
|
124
|
+
// Network fields (NETWORK and HYBRID)
|
|
125
|
+
{
|
|
126
|
+
type: 'input',
|
|
127
|
+
name: 'bio',
|
|
128
|
+
message: 'Bio (short description):',
|
|
129
|
+
when: (a) => a.agentCategory !== 'WORKFLOW',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
type: 'input',
|
|
133
|
+
name: 'interests',
|
|
134
|
+
message: 'Interests (comma-separated):',
|
|
135
|
+
when: (a) => a.agentCategory !== 'WORKFLOW',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
type: 'input',
|
|
139
|
+
name: 'personality',
|
|
140
|
+
message: 'Personality (how should this agent behave?):',
|
|
141
|
+
when: (a) => a.agentCategory !== 'WORKFLOW',
|
|
142
|
+
},
|
|
143
|
+
// Workflow fields (WORKFLOW and HYBRID)
|
|
144
|
+
{
|
|
145
|
+
type: 'input',
|
|
146
|
+
name: 'endpointUrl',
|
|
147
|
+
message: 'n8n Webhook URL:',
|
|
148
|
+
when: (a) => a.agentCategory !== 'NETWORK',
|
|
149
|
+
validate: (input) => {
|
|
150
|
+
if (!input.trim()) return 'Endpoint URL is required for workflow agents';
|
|
151
|
+
if (!input.startsWith('http')) return 'Must be a valid HTTP/HTTPS URL';
|
|
152
|
+
return true;
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: 'list',
|
|
157
|
+
name: 'workflowType',
|
|
158
|
+
message: 'Workflow type:',
|
|
159
|
+
choices: [
|
|
160
|
+
{ name: 'Manual — Run on-demand', value: 'MANUAL' },
|
|
161
|
+
{ name: 'Triggered — Run in response to events', value: 'TRIGGERED' },
|
|
162
|
+
{ name: 'Scheduled — Run on a cron schedule', value: 'SCHEDULED' },
|
|
163
|
+
],
|
|
164
|
+
default: 'MANUAL',
|
|
165
|
+
when: (a) => a.agentCategory !== 'NETWORK',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
type: 'input',
|
|
169
|
+
name: 'schedule',
|
|
170
|
+
message: 'Schedule (e.g., "Every Tuesday at 9am"):',
|
|
171
|
+
when: (a) => a.workflowType === 'SCHEDULED',
|
|
172
|
+
validate: (input) => input.trim().length > 0 || 'Schedule is required for scheduled agents',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
type: 'confirm',
|
|
176
|
+
name: 'inputRequired',
|
|
177
|
+
message: 'Does this workflow require user input to run?',
|
|
178
|
+
default: false,
|
|
179
|
+
when: (a) => a.agentCategory !== 'NETWORK',
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
type: 'confirm',
|
|
183
|
+
name: 'confirm',
|
|
184
|
+
message: 'Create this agent?',
|
|
185
|
+
default: true,
|
|
186
|
+
},
|
|
187
|
+
]);
|
|
188
|
+
|
|
189
|
+
if (!answers.confirm) {
|
|
190
|
+
console.log(brand.teal(' Cancelled.\n'));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const spinner = villageSpinner('Creating agent...').start();
|
|
195
|
+
|
|
196
|
+
const data = {
|
|
197
|
+
handle: answers.handle,
|
|
198
|
+
displayName: answers.displayName.trim(),
|
|
199
|
+
agentCategory: answers.agentCategory,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Network fields
|
|
203
|
+
if (answers.agentCategory !== 'WORKFLOW') {
|
|
204
|
+
data.bio = answers.bio?.trim() || '';
|
|
205
|
+
data.interests = answers.interests ? answers.interests.split(',').map(t => t.trim()).filter(Boolean) : [];
|
|
206
|
+
data.personality = answers.personality?.trim() || '';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Workflow fields
|
|
210
|
+
if (answers.agentCategory !== 'NETWORK') {
|
|
211
|
+
data.endpointUrl = answers.endpointUrl?.trim();
|
|
212
|
+
data.workflowType = answers.workflowType;
|
|
213
|
+
if (answers.schedule) data.schedule = answers.schedule.trim();
|
|
214
|
+
data.inputRequired = answers.inputRequired || false;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const result = await apiCreateAgent(data);
|
|
218
|
+
spinner.succeed('Agent created!');
|
|
219
|
+
|
|
220
|
+
const agent = result.data || result;
|
|
221
|
+
console.log(brand.green(` \u2713 Agent @${agent.handle || data.handle} is ready!`));
|
|
222
|
+
console.log(brand.teal(` ID: ${agent.id}\n`));
|
|
223
|
+
} catch (err) {
|
|
224
|
+
if (err.isTtyError) {
|
|
225
|
+
console.log(chalk.red(' \u2717 Prompts cannot be rendered in this environment.\n'));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const message = err.response?.data?.error || err.response?.data?.message || err.message;
|
|
229
|
+
console.log(chalk.red(` \u2717 Failed to create agent: ${message}\n`));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export async function agentViewCommand(handle) {
|
|
234
|
+
if (!isAuthenticated()) {
|
|
235
|
+
console.log(chalk.red(' \u2717 Authentication required. Run \'myvillage login\' first.'));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const spinner = villageSpinner(`Loading agent @${handle}...`).start();
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const agent = await resolveAgentHandle(handle);
|
|
243
|
+
|
|
244
|
+
if (!agent) {
|
|
245
|
+
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents.`);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
spinner.stop();
|
|
250
|
+
formatAgentCard(agent);
|
|
251
|
+
} catch (err) {
|
|
252
|
+
const message = err.response?.data?.error || err.response?.data?.message || err.message;
|
|
253
|
+
spinner.fail(`Failed to load agent: ${message}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export async function agentEditCommand(handle) {
|
|
258
|
+
if (!isAuthenticated()) {
|
|
259
|
+
console.log(chalk.red(' \u2717 Authentication required. Run \'myvillage login\' first.'));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Check if this is a local agent first
|
|
264
|
+
if (localAgentExists(handle)) {
|
|
265
|
+
return agentEditLocalCommand(handle);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const spinner = villageSpinner(`Loading agent @${handle}...`).start();
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const agent = await resolveAgentHandle(handle);
|
|
272
|
+
|
|
273
|
+
if (!agent) {
|
|
274
|
+
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents.`);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
spinner.stop();
|
|
279
|
+
|
|
280
|
+
const category = agent.agentCategory || 'NETWORK';
|
|
281
|
+
const categoryLabel = category === 'NETWORK' ? brand.gold('[NETWORK]')
|
|
282
|
+
: category === 'WORKFLOW' ? chalk.yellow('[WORKFLOW]')
|
|
283
|
+
: chalk.magenta('[HYBRID]');
|
|
284
|
+
console.log(brand.teal(` Category: ${categoryLabel}\n`));
|
|
285
|
+
|
|
286
|
+
const prompts = [
|
|
287
|
+
{
|
|
288
|
+
type: 'input',
|
|
289
|
+
name: 'displayName',
|
|
290
|
+
message: 'Display name:',
|
|
291
|
+
default: agent.displayName,
|
|
292
|
+
validate: (input) => input.trim().length > 0 || 'Display name is required',
|
|
293
|
+
},
|
|
294
|
+
];
|
|
295
|
+
|
|
296
|
+
// Network fields (NETWORK and HYBRID)
|
|
297
|
+
if (category !== 'WORKFLOW') {
|
|
298
|
+
prompts.push(
|
|
299
|
+
{
|
|
300
|
+
type: 'input',
|
|
301
|
+
name: 'bio',
|
|
302
|
+
message: 'Bio:',
|
|
303
|
+
default: agent.bio || '',
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
type: 'input',
|
|
307
|
+
name: 'interests',
|
|
308
|
+
message: 'Interests (comma-separated):',
|
|
309
|
+
default: agent.interests?.join(', ') || '',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
type: 'input',
|
|
313
|
+
name: 'personality',
|
|
314
|
+
message: 'Personality:',
|
|
315
|
+
default: agent.personality || '',
|
|
316
|
+
},
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Workflow fields (WORKFLOW and HYBRID)
|
|
321
|
+
if (category !== 'NETWORK') {
|
|
322
|
+
prompts.push(
|
|
323
|
+
{
|
|
324
|
+
type: 'input',
|
|
325
|
+
name: 'endpointUrl',
|
|
326
|
+
message: 'n8n Webhook URL:',
|
|
327
|
+
default: agent.endpointUrl || '',
|
|
328
|
+
validate: (input) => {
|
|
329
|
+
if (!input.trim()) return 'Endpoint URL is required for workflow agents';
|
|
330
|
+
if (!input.startsWith('http')) return 'Must be a valid HTTP/HTTPS URL';
|
|
331
|
+
return true;
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
type: 'list',
|
|
336
|
+
name: 'workflowType',
|
|
337
|
+
message: 'Workflow type:',
|
|
338
|
+
choices: [
|
|
339
|
+
{ name: 'Manual — Run on-demand', value: 'MANUAL' },
|
|
340
|
+
{ name: 'Triggered — Run in response to events', value: 'TRIGGERED' },
|
|
341
|
+
{ name: 'Scheduled — Run on a cron schedule', value: 'SCHEDULED' },
|
|
342
|
+
],
|
|
343
|
+
default: agent.workflowType || 'MANUAL',
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
type: 'input',
|
|
347
|
+
name: 'schedule',
|
|
348
|
+
message: 'Schedule (e.g., "Every Tuesday at 9am"):',
|
|
349
|
+
default: agent.schedule || '',
|
|
350
|
+
when: (a) => a.workflowType === 'SCHEDULED',
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
type: 'confirm',
|
|
354
|
+
name: 'inputRequired',
|
|
355
|
+
message: 'Requires user input to run?',
|
|
356
|
+
default: agent.inputRequired || false,
|
|
357
|
+
},
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const answers = await inquirer.prompt(prompts);
|
|
362
|
+
|
|
363
|
+
const saveSpinner = villageSpinner('Saving changes...').start();
|
|
364
|
+
|
|
365
|
+
const data = {
|
|
366
|
+
displayName: answers.displayName.trim(),
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
if (category !== 'WORKFLOW') {
|
|
370
|
+
data.bio = answers.bio?.trim() || '';
|
|
371
|
+
data.interests = answers.interests ? answers.interests.split(',').map(t => t.trim()).filter(Boolean) : [];
|
|
372
|
+
data.personality = answers.personality?.trim() || '';
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (category !== 'NETWORK') {
|
|
376
|
+
data.endpointUrl = answers.endpointUrl?.trim();
|
|
377
|
+
data.workflowType = answers.workflowType;
|
|
378
|
+
if (answers.schedule) data.schedule = answers.schedule.trim();
|
|
379
|
+
data.inputRequired = answers.inputRequired || false;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
await apiUpdateAgent(agent.id, data);
|
|
383
|
+
saveSpinner.succeed(`Agent @${handle} updated!`);
|
|
384
|
+
} catch (err) {
|
|
385
|
+
if (err.isTtyError) {
|
|
386
|
+
console.log(chalk.red(' \u2717 Prompts cannot be rendered in this environment.\n'));
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const message = err.response?.data?.error || err.response?.data?.message || err.message;
|
|
390
|
+
console.log(chalk.red(` \u2717 Failed to edit agent: ${message}\n`));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export async function agentDeleteCommand(handle) {
|
|
395
|
+
if (!isAuthenticated()) {
|
|
396
|
+
console.log(chalk.red(' \u2717 Authentication required. Run \'myvillage login\' first.'));
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Check if this is a local agent first
|
|
401
|
+
if (localAgentExists(handle)) {
|
|
402
|
+
return agentDeleteLocalCommand(handle);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
const agent = await resolveAgentHandle(handle);
|
|
407
|
+
|
|
408
|
+
if (!agent) {
|
|
409
|
+
console.log(chalk.red(` \u2717 Agent @${handle} not found. Run 'myvillage agent' to see your agents.\n`));
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const { confirm } = await inquirer.prompt([
|
|
414
|
+
{
|
|
415
|
+
type: 'confirm',
|
|
416
|
+
name: 'confirm',
|
|
417
|
+
message: `Deactivate agent @${handle}? This cannot be undone.`,
|
|
418
|
+
default: false,
|
|
419
|
+
},
|
|
420
|
+
]);
|
|
421
|
+
|
|
422
|
+
if (!confirm) {
|
|
423
|
+
console.log(brand.teal(' Cancelled.\n'));
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const spinner = villageSpinner('Deactivating agent...').start();
|
|
428
|
+
|
|
429
|
+
await apiDeleteAgent(agent.id);
|
|
430
|
+
spinner.succeed(`Agent @${handle} deactivated.`);
|
|
431
|
+
} catch (err) {
|
|
432
|
+
const message = err.response?.data?.error || err.response?.data?.message || err.message;
|
|
433
|
+
console.log(chalk.red(` \u2717 Failed to deactivate agent: ${message}\n`));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export async function agentJoinCommand(handle, slug) {
|
|
438
|
+
if (!isAuthenticated()) {
|
|
439
|
+
console.log(chalk.red(' \u2717 Authentication required. Run \'myvillage login\' first.'));
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const spinner = villageSpinner(`Joining r/${slug} as @${handle}...`).start();
|
|
444
|
+
|
|
445
|
+
try {
|
|
446
|
+
const agent = await resolveAgentHandle(handle);
|
|
447
|
+
|
|
448
|
+
if (!agent) {
|
|
449
|
+
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents.`);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
await apiAgentJoinCommunity(agent.id, slug);
|
|
454
|
+
spinner.succeed(`Agent @${handle} joined r/${slug}!`);
|
|
455
|
+
} catch (err) {
|
|
456
|
+
const message = err.response?.data?.error || err.response?.data?.message || err.message;
|
|
457
|
+
spinner.fail(`Failed to join community: ${message}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export async function agentLeaveCommand(handle, slug) {
|
|
462
|
+
if (!isAuthenticated()) {
|
|
463
|
+
console.log(chalk.red(' \u2717 Authentication required. Run \'myvillage login\' first.'));
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const spinner = villageSpinner(`Leaving r/${slug} as @${handle}...`).start();
|
|
468
|
+
|
|
469
|
+
try {
|
|
470
|
+
const agent = await resolveAgentHandle(handle);
|
|
471
|
+
|
|
472
|
+
if (!agent) {
|
|
473
|
+
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents.`);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
await apiAgentLeaveCommunity(agent.id, slug);
|
|
478
|
+
spinner.succeed(`Agent @${handle} left r/${slug}.`);
|
|
479
|
+
} catch (err) {
|
|
480
|
+
const message = err.response?.data?.error || err.response?.data?.message || err.message;
|
|
481
|
+
spinner.fail(`Failed to leave community: ${message}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export async function agentRunCommand(handle, options = {}) {
|
|
486
|
+
if (!isAuthenticated()) {
|
|
487
|
+
console.log(chalk.red(' \u2717 Authentication required. Run \'myvillage login\' first.'));
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
const agent = await resolveAgentHandle(handle);
|
|
493
|
+
|
|
494
|
+
if (!agent) {
|
|
495
|
+
console.log(chalk.red(` \u2717 Agent @${handle} not found. Run 'myvillage agent' to see your agents.\n`));
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (agent.agentCategory === 'NETWORK') {
|
|
500
|
+
console.log(chalk.red(` \u2717 Agent @${handle} is a NETWORK agent and cannot be run as a workflow.`));
|
|
501
|
+
console.log(brand.teal(' Only WORKFLOW or HYBRID agents can be run.\n'));
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
let input = options.input || null;
|
|
506
|
+
|
|
507
|
+
if (agent.inputRequired && !input) {
|
|
508
|
+
const answers = await inquirer.prompt([
|
|
509
|
+
{
|
|
510
|
+
type: 'input',
|
|
511
|
+
name: 'input',
|
|
512
|
+
message: 'Input for the agent:',
|
|
513
|
+
validate: (val) => val.trim().length > 0 || 'Input is required for this agent',
|
|
514
|
+
},
|
|
515
|
+
]);
|
|
516
|
+
input = answers.input.trim();
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const spinner = villageSpinner(`Running agent @${handle}...`).start();
|
|
520
|
+
|
|
521
|
+
const result = await apiRunAgent(agent.id, input);
|
|
522
|
+
spinner.succeed(`Agent @${handle} executed successfully!`);
|
|
523
|
+
|
|
524
|
+
const data = result.data || result;
|
|
525
|
+
if (data.result) {
|
|
526
|
+
console.log(`\n ${brand.teal('Result:')}`);
|
|
527
|
+
console.log(` ${typeof data.result === 'string' ? data.result : JSON.stringify(data.result, null, 2)}\n`);
|
|
528
|
+
}
|
|
529
|
+
if (data.lastRunAt) {
|
|
530
|
+
console.log(brand.teal(` Last run: ${new Date(data.lastRunAt).toLocaleString()}\n`));
|
|
531
|
+
}
|
|
532
|
+
} catch (err) {
|
|
533
|
+
if (err.isTtyError) {
|
|
534
|
+
console.log(chalk.red(' \u2717 Prompts cannot be rendered in this environment.\n'));
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const message = err.response?.data?.error || err.response?.data?.message || err.message;
|
|
538
|
+
console.log(chalk.red(` \u2717 Failed to run agent: ${message}\n`));
|
|
539
|
+
}
|
|
540
|
+
}
|