@myvillage/cli 1.6.0 → 1.6.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 +2 -1
- package/src/agent-runtime/loop.js +38 -14
- package/src/commands/agent-local.js +56 -8
- package/src/commands/agent.js +6 -6
- package/src/commands/checkin.js +7 -40
- package/src/commands/comment.js +1 -1
- package/src/commands/post.js +1 -1
- package/src/commands/profile.js +37 -5
- package/src/commands/vote.js +1 -1
- package/src/utils/agent-scaffolder.js +5 -5
- package/src/utils/api.js +8 -0
- package/src/utils/config.js +5 -1
- package/src/utils/formatters.js +4 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myvillage/cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "MyVillageOS CLI for community developers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@ai-sdk/anthropic": "^1.0.0",
|
|
30
|
+
"@ai-sdk/openai": "^3.0.33",
|
|
30
31
|
"ai": "^4.0.0",
|
|
31
32
|
"axios": "^1.6.2",
|
|
32
33
|
"chalk": "^5.3.0",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { generateText } from 'ai';
|
|
6
6
|
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
7
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
|
7
8
|
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from 'fs';
|
|
8
9
|
import { join } from 'path';
|
|
9
10
|
import { homedir } from 'os';
|
|
@@ -18,24 +19,47 @@ export async function agentLoop(agentName, { signal }) {
|
|
|
18
19
|
const configPath = join(agentDir, 'agent.config.yaml');
|
|
19
20
|
const config = parseYaml(readFileSync(configPath, 'utf-8'));
|
|
20
21
|
|
|
21
|
-
//
|
|
22
|
+
// Determine provider and resolve API key
|
|
23
|
+
const provider = config.brain?.provider || 'anthropic';
|
|
22
24
|
const globalConfigPath = join(homedir(), '.myvillage', 'config.json');
|
|
23
|
-
let apiKey
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
let apiKey;
|
|
26
|
+
|
|
27
|
+
if (provider === 'openai') {
|
|
28
|
+
apiKey = process.env.OPENAI_API_KEY;
|
|
29
|
+
if (!apiKey && existsSync(globalConfigPath)) {
|
|
30
|
+
try {
|
|
31
|
+
const globalConfig = JSON.parse(readFileSync(globalConfigPath, 'utf-8'));
|
|
32
|
+
apiKey = globalConfig.openaiApiKey;
|
|
33
|
+
} catch { /* ignore */ }
|
|
34
|
+
}
|
|
35
|
+
if (!apiKey) {
|
|
36
|
+
logActivity(agentDir, { type: 'error', error: 'No OpenAI API key configured' });
|
|
37
|
+
throw new Error('No OpenAI API key configured. Set OPENAI_API_KEY or run: myvillage agent start ' + agentName);
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
apiKey = process.env.ANTHROPIC_API_KEY;
|
|
41
|
+
if (!apiKey && existsSync(globalConfigPath)) {
|
|
42
|
+
try {
|
|
43
|
+
const globalConfig = JSON.parse(readFileSync(globalConfigPath, 'utf-8'));
|
|
44
|
+
apiKey = globalConfig.anthropicApiKey;
|
|
45
|
+
} catch { /* ignore */ }
|
|
46
|
+
}
|
|
47
|
+
if (!apiKey) {
|
|
48
|
+
logActivity(agentDir, { type: 'error', error: 'No Anthropic API key configured' });
|
|
49
|
+
throw new Error('No Anthropic API key configured. Set ANTHROPIC_API_KEY or run: myvillage agent start ' + agentName);
|
|
50
|
+
}
|
|
29
51
|
}
|
|
30
52
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
// Create provider and model
|
|
54
|
+
const modelId = config.brain?.model || (provider === 'openai' ? 'gpt-4o' : 'claude-sonnet-4-5-20250929');
|
|
55
|
+
let model;
|
|
56
|
+
if (provider === 'openai') {
|
|
57
|
+
const openai = createOpenAI({ apiKey });
|
|
58
|
+
model = openai(modelId);
|
|
59
|
+
} else {
|
|
60
|
+
const anthropic = createAnthropic({ apiKey });
|
|
61
|
+
model = anthropic(modelId);
|
|
34
62
|
}
|
|
35
|
-
|
|
36
|
-
const anthropic = createAnthropic({ apiKey });
|
|
37
|
-
const modelId = config.brain?.model || 'claude-sonnet-4-5-20250929';
|
|
38
|
-
const model = anthropic(modelId);
|
|
39
63
|
const maxTokens = config.brain?.max_tokens_per_loop || 1000;
|
|
40
64
|
|
|
41
65
|
// Initialize MCP tools
|
|
@@ -80,6 +80,35 @@ export async function agentCreateLocalCommand() {
|
|
|
80
80
|
{ name: 'Browser', value: 'browser' },
|
|
81
81
|
],
|
|
82
82
|
},
|
|
83
|
+
{
|
|
84
|
+
type: 'list',
|
|
85
|
+
name: 'provider',
|
|
86
|
+
message: 'AI provider:',
|
|
87
|
+
choices: [
|
|
88
|
+
{ name: 'Anthropic (Claude)', value: 'anthropic' },
|
|
89
|
+
{ name: 'OpenAI (GPT)', value: 'openai' },
|
|
90
|
+
],
|
|
91
|
+
default: 'anthropic',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: 'list',
|
|
95
|
+
name: 'model',
|
|
96
|
+
message: 'Model:',
|
|
97
|
+
choices: (prev) => {
|
|
98
|
+
if (prev.provider === 'openai') {
|
|
99
|
+
return [
|
|
100
|
+
{ name: 'gpt-4o (recommended)', value: 'gpt-4o' },
|
|
101
|
+
{ name: 'gpt-4o-mini', value: 'gpt-4o-mini' },
|
|
102
|
+
{ name: 'gpt-4.1', value: 'gpt-4.1' },
|
|
103
|
+
{ name: 'gpt-4.1-mini', value: 'gpt-4.1-mini' },
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
return [
|
|
107
|
+
{ name: 'Claude Sonnet 4.5 (recommended)', value: 'claude-sonnet-4-5-20250929' },
|
|
108
|
+
{ name: 'Claude Haiku 3.5', value: 'claude-3-5-haiku-20241022' },
|
|
109
|
+
];
|
|
110
|
+
},
|
|
111
|
+
},
|
|
83
112
|
{
|
|
84
113
|
type: 'list',
|
|
85
114
|
name: 'checkInInterval',
|
|
@@ -118,6 +147,8 @@ export async function agentCreateLocalCommand() {
|
|
|
118
147
|
description: answers.description.trim(),
|
|
119
148
|
tools,
|
|
120
149
|
checkInInterval: answers.checkInInterval,
|
|
150
|
+
provider: answers.provider,
|
|
151
|
+
model: answers.model,
|
|
121
152
|
});
|
|
122
153
|
|
|
123
154
|
spinner.succeed('Agent scaffolded!');
|
|
@@ -154,25 +185,43 @@ export async function agentStartCommand(name) {
|
|
|
154
185
|
return;
|
|
155
186
|
}
|
|
156
187
|
|
|
157
|
-
//
|
|
158
|
-
const
|
|
159
|
-
|
|
188
|
+
// Read agent's configured provider and check for the correct API key
|
|
189
|
+
const agentConfig = readAgentConfig(name);
|
|
190
|
+
const provider = agentConfig?.brain?.provider || 'anthropic';
|
|
191
|
+
const cliConfig = getConfig();
|
|
192
|
+
|
|
193
|
+
let apiKey;
|
|
194
|
+
let configKeyName;
|
|
195
|
+
let providerLabel;
|
|
196
|
+
let consoleUrl;
|
|
197
|
+
|
|
198
|
+
if (provider === 'openai') {
|
|
199
|
+
apiKey = cliConfig.openaiApiKey || process.env.OPENAI_API_KEY;
|
|
200
|
+
configKeyName = 'openaiApiKey';
|
|
201
|
+
providerLabel = 'OpenAI';
|
|
202
|
+
consoleUrl = 'https://platform.openai.com/api-keys';
|
|
203
|
+
} else {
|
|
204
|
+
apiKey = cliConfig.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
|
205
|
+
configKeyName = 'anthropicApiKey';
|
|
206
|
+
providerLabel = 'Anthropic';
|
|
207
|
+
consoleUrl = 'https://console.anthropic.com';
|
|
208
|
+
}
|
|
160
209
|
|
|
161
210
|
if (!apiKey) {
|
|
162
|
-
console.log(chalk.yellow(
|
|
163
|
-
console.log(brand.teal(
|
|
211
|
+
console.log(chalk.yellow(`\n ${providerLabel} API key required for this agent.`));
|
|
212
|
+
console.log(brand.teal(` Get yours at: ${consoleUrl}\n`));
|
|
164
213
|
|
|
165
214
|
try {
|
|
166
215
|
const { key } = await inquirer.prompt([{
|
|
167
216
|
type: 'password',
|
|
168
217
|
name: 'key',
|
|
169
|
-
message:
|
|
218
|
+
message: `${providerLabel} API key:`,
|
|
170
219
|
mask: '*',
|
|
171
220
|
validate: (input) => input.trim().length > 0 || 'API key is required',
|
|
172
221
|
}]);
|
|
173
222
|
|
|
174
223
|
apiKey = key.trim();
|
|
175
|
-
setConfig({
|
|
224
|
+
setConfig({ [configKeyName]: apiKey });
|
|
176
225
|
console.log(brand.green(' \u2713 API key saved.\n'));
|
|
177
226
|
} catch (err) {
|
|
178
227
|
if (err.isTtyError) {
|
|
@@ -183,7 +232,6 @@ export async function agentStartCommand(name) {
|
|
|
183
232
|
}
|
|
184
233
|
|
|
185
234
|
// Register on MAN if first start
|
|
186
|
-
const agentConfig = readAgentConfig(name);
|
|
187
235
|
if (!agentConfig.man?.agent_id) {
|
|
188
236
|
const regSpinner = villageSpinner('Registering agent on the MAN network...').start();
|
|
189
237
|
try {
|
package/src/commands/agent.js
CHANGED
|
@@ -242,7 +242,7 @@ export async function agentViewCommand(handle) {
|
|
|
242
242
|
const agent = await resolveAgentHandle(handle);
|
|
243
243
|
|
|
244
244
|
if (!agent) {
|
|
245
|
-
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents.`);
|
|
245
|
+
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents, or 'myvillage agent start ${handle}' if you haven't started it yet.`);
|
|
246
246
|
return;
|
|
247
247
|
}
|
|
248
248
|
|
|
@@ -271,7 +271,7 @@ export async function agentEditCommand(handle) {
|
|
|
271
271
|
const agent = await resolveAgentHandle(handle);
|
|
272
272
|
|
|
273
273
|
if (!agent) {
|
|
274
|
-
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents.`);
|
|
274
|
+
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents, or 'myvillage agent start ${handle}' if you haven't started it yet.`);
|
|
275
275
|
return;
|
|
276
276
|
}
|
|
277
277
|
|
|
@@ -406,7 +406,7 @@ export async function agentDeleteCommand(handle) {
|
|
|
406
406
|
const agent = await resolveAgentHandle(handle);
|
|
407
407
|
|
|
408
408
|
if (!agent) {
|
|
409
|
-
console.log(chalk.red(` \u2717 Agent @${handle} not found. Run 'myvillage agent' to see your agents.\n`));
|
|
409
|
+
console.log(chalk.red(` \u2717 Agent @${handle} not found. Run 'myvillage agent' to see your agents, or 'myvillage agent start ${handle}' if you haven't started it yet.\n`));
|
|
410
410
|
return;
|
|
411
411
|
}
|
|
412
412
|
|
|
@@ -446,7 +446,7 @@ export async function agentJoinCommand(handle, slug) {
|
|
|
446
446
|
const agent = await resolveAgentHandle(handle);
|
|
447
447
|
|
|
448
448
|
if (!agent) {
|
|
449
|
-
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents.`);
|
|
449
|
+
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents, or 'myvillage agent start ${handle}' if you haven't started it yet.`);
|
|
450
450
|
return;
|
|
451
451
|
}
|
|
452
452
|
|
|
@@ -470,7 +470,7 @@ export async function agentLeaveCommand(handle, slug) {
|
|
|
470
470
|
const agent = await resolveAgentHandle(handle);
|
|
471
471
|
|
|
472
472
|
if (!agent) {
|
|
473
|
-
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents.`);
|
|
473
|
+
spinner.fail(`Agent @${handle} not found. Run 'myvillage agent' to see your agents, or 'myvillage agent start ${handle}' if you haven't started it yet.`);
|
|
474
474
|
return;
|
|
475
475
|
}
|
|
476
476
|
|
|
@@ -492,7 +492,7 @@ export async function agentRunCommand(handle, options = {}) {
|
|
|
492
492
|
const agent = await resolveAgentHandle(handle);
|
|
493
493
|
|
|
494
494
|
if (!agent) {
|
|
495
|
-
console.log(chalk.red(` \u2717 Agent @${handle} not found. Run 'myvillage agent' to see your agents.\n`));
|
|
495
|
+
console.log(chalk.red(` \u2717 Agent @${handle} not found. Run 'myvillage agent' to see your agents, or 'myvillage agent start ${handle}' if you haven't started it yet.\n`));
|
|
496
496
|
return;
|
|
497
497
|
}
|
|
498
498
|
|
package/src/commands/checkin.js
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
|
-
import { z } from 'zod';
|
|
4
3
|
import { isAuthenticated } from '../utils/auth.js';
|
|
5
4
|
import { brand, villageSpinner } from '../utils/brand.js';
|
|
6
|
-
import { createPost } from '../utils/api.js';
|
|
5
|
+
import { createPost, structureCheckin } from '../utils/api.js';
|
|
7
6
|
import { resolveVillageContext } from '../utils/village-resolver.js';
|
|
8
|
-
import { getAnthropicProvider } from '../utils/ai.js';
|
|
9
7
|
import { formatCheckinSummary } from '../utils/formatters.js';
|
|
10
8
|
import { discoverLogic } from './discover.js';
|
|
11
9
|
|
|
12
|
-
const extractionSchema = z.object({
|
|
13
|
-
themes: z.array(z.string()).describe('Key themes from the check-in (1-4 short labels)'),
|
|
14
|
-
needs: z.array(z.string()).describe('Things the village needs help with (0-3 short labels)'),
|
|
15
|
-
offers: z.array(z.string()).describe('Things the village can offer others (0-3 short labels)'),
|
|
16
|
-
buildTrack: z.string().optional().describe('Primary build track if mentioned: game, portal, agent, data, robot, or general'),
|
|
17
|
-
summary: z.string().describe('A 2-3 sentence summary combining all responses into a cohesive check-in update'),
|
|
18
|
-
});
|
|
19
|
-
|
|
20
10
|
export async function checkinCommand(options) {
|
|
21
11
|
if (!isAuthenticated()) {
|
|
22
12
|
console.log(chalk.red(" \u2717 Authentication required. Run 'myvillage login' first."));
|
|
@@ -52,42 +42,19 @@ export async function checkinCommand(options) {
|
|
|
52
42
|
},
|
|
53
43
|
]);
|
|
54
44
|
|
|
55
|
-
// AI structuring
|
|
45
|
+
// AI structuring (backend-proxied)
|
|
56
46
|
const spinner = villageSpinner('Organizing your check-in...').start();
|
|
57
47
|
|
|
58
48
|
let extracted;
|
|
59
49
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
`What we worked on: ${answers.work}`,
|
|
65
|
-
answers.stuck ? `Problems we're stuck on: ${answers.stuck}` : null,
|
|
66
|
-
answers.share ? `What we want to share: ${answers.share}` : null,
|
|
67
|
-
].filter(Boolean).join('\n');
|
|
68
|
-
|
|
69
|
-
const result = await generateText({
|
|
70
|
-
model: anthropic('claude-haiku-4-5-20251001'),
|
|
71
|
-
system: `You are a community network coordinator. Extract structured metadata from a village check-in and write a concise summary. Always call the extract_checkin tool with your analysis.`,
|
|
72
|
-
messages: [{ role: 'user', content: userContent }],
|
|
73
|
-
tools: {
|
|
74
|
-
extract_checkin: {
|
|
75
|
-
description: 'Extract structured metadata from a village check-in',
|
|
76
|
-
parameters: extractionSchema,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
toolChoice: { type: 'tool', toolName: 'extract_checkin' },
|
|
80
|
-
maxTokens: 500,
|
|
50
|
+
extracted = await structureCheckin({
|
|
51
|
+
work: answers.work,
|
|
52
|
+
stuck: answers.stuck || undefined,
|
|
53
|
+
share: answers.share || undefined,
|
|
81
54
|
});
|
|
82
|
-
|
|
83
|
-
const toolCall = result.toolCalls?.[0];
|
|
84
|
-
if (!toolCall?.args) {
|
|
85
|
-
throw new Error('AI did not return structured data');
|
|
86
|
-
}
|
|
87
|
-
extracted = toolCall.args;
|
|
88
55
|
spinner.succeed('Check-in organized');
|
|
89
56
|
} catch (err) {
|
|
90
|
-
spinner.fail('AI structuring failed
|
|
57
|
+
spinner.fail('AI structuring failed \u2014 posting raw check-in');
|
|
91
58
|
// Fallback: post without AI structuring
|
|
92
59
|
extracted = {
|
|
93
60
|
themes: [],
|
package/src/commands/comment.js
CHANGED
|
@@ -24,7 +24,7 @@ export async function commentCommand(postId, options) {
|
|
|
24
24
|
agentsSpinner.stop();
|
|
25
25
|
|
|
26
26
|
if (!agent) {
|
|
27
|
-
console.log(chalk.red(`
|
|
27
|
+
console.log(chalk.red(` \u2717 Agent @${options.as} not found. Run 'myvillage agent' to see your agents, or 'myvillage agent start ${options.as}' if you haven't started it yet.`));
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
agentProfileId = agent.id;
|
package/src/commands/post.js
CHANGED
|
@@ -88,7 +88,7 @@ export async function postCreateCommand(options = {}) {
|
|
|
88
88
|
agentsSpinner.stop();
|
|
89
89
|
|
|
90
90
|
if (!agent) {
|
|
91
|
-
console.log(chalk.red(`
|
|
91
|
+
console.log(chalk.red(` \u2717 Agent @${options.as} not found. Run 'myvillage agent' to see your agents, or 'myvillage agent start ${options.as}' if you haven't started it yet.`));
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
agentProfileId = agent.id;
|
package/src/commands/profile.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { villageSpinner, brand } from '../utils/brand.js';
|
|
4
|
-
import { isAuthenticated, loadCredentials } from '../utils/auth.js';
|
|
5
|
-
import { getProfile, getProfilePosts } from '../utils/api.js';
|
|
4
|
+
import { isAuthenticated, loadCredentials, saveCredentials } from '../utils/auth.js';
|
|
5
|
+
import { getProfile, getProfilePosts, getUserInfo, getAccessToken } from '../utils/api.js';
|
|
6
6
|
import { formatProfile, formatPostList, formatPagination } from '../utils/formatters.js';
|
|
7
7
|
|
|
8
8
|
export async function profileCommand(handle, options) {
|
|
@@ -13,18 +13,47 @@ export async function profileCommand(handle, options) {
|
|
|
13
13
|
|
|
14
14
|
// Resolve handle: default to the logged-in user's villagerId
|
|
15
15
|
let target = handle;
|
|
16
|
-
|
|
16
|
+
const isSelf = !target;
|
|
17
|
+
if (isSelf) {
|
|
17
18
|
const creds = loadCredentials();
|
|
18
19
|
target = creds?.villager_id;
|
|
19
20
|
if (!target) {
|
|
20
|
-
console.log(chalk.red('
|
|
21
|
+
console.log(chalk.red(' \u2717 No villager ID found. Try logging out and back in, or provide a handle: myvillage profile <handle>'));
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
const spinner = villageSpinner('Loading profile...').start();
|
|
25
26
|
|
|
26
27
|
try {
|
|
27
|
-
|
|
28
|
+
let result;
|
|
29
|
+
try {
|
|
30
|
+
result = await getProfile(target);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
// If own profile lookup fails with 404, refresh userinfo and retry
|
|
33
|
+
if (isSelf && err.response?.status === 404) {
|
|
34
|
+
spinner.text = 'Refreshing user info...';
|
|
35
|
+
const accessToken = getAccessToken();
|
|
36
|
+
if (accessToken) {
|
|
37
|
+
const userInfo = await getUserInfo(accessToken);
|
|
38
|
+
const freshId = userInfo.villager?.villagerId;
|
|
39
|
+
if (freshId && freshId !== target) {
|
|
40
|
+
// Update stored credentials with the fresh villagerId
|
|
41
|
+
const creds = loadCredentials();
|
|
42
|
+
saveCredentials({ ...creds, villager_id: freshId });
|
|
43
|
+
target = freshId;
|
|
44
|
+
spinner.text = 'Loading profile...';
|
|
45
|
+
result = await getProfile(target);
|
|
46
|
+
} else {
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
28
57
|
spinner.stop();
|
|
29
58
|
|
|
30
59
|
const profile = result.data || result;
|
|
@@ -54,5 +83,8 @@ export async function profileCommand(handle, options) {
|
|
|
54
83
|
} catch (err) {
|
|
55
84
|
const message = err.response?.data?.error || err.response?.data?.message || err.message;
|
|
56
85
|
spinner.fail(`Failed to load profile: ${message}`);
|
|
86
|
+
if (isSelf && err.response?.status === 404) {
|
|
87
|
+
console.log(brand.teal(' Your stored villager ID may be out of sync. Try running \'myvillage logout\' then \'myvillage login\'.'));
|
|
88
|
+
}
|
|
57
89
|
}
|
|
58
90
|
}
|
package/src/commands/vote.js
CHANGED
|
@@ -20,7 +20,7 @@ export async function voteCommand(options) {
|
|
|
20
20
|
const agent = Array.isArray(agents) ? agents.find(a => a.handle === options.as) : null;
|
|
21
21
|
|
|
22
22
|
if (!agent) {
|
|
23
|
-
console.log(chalk.red(` \u2717 Agent @${options.as} not found. Run 'myvillage agent' to see your agents.\n`));
|
|
23
|
+
console.log(chalk.red(` \u2717 Agent @${options.as} not found. Run 'myvillage agent' to see your agents, or 'myvillage agent start ${options.as}' if you haven't started it yet.\n`));
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
agentProfileId = agent.id;
|
|
@@ -52,7 +52,7 @@ const INTERVAL_MAP = {
|
|
|
52
52
|
// ── Scaffolding ─────────────────────────────────────────
|
|
53
53
|
|
|
54
54
|
export function scaffoldAgent(agentDir, options) {
|
|
55
|
-
const { name, displayName, description, tools, checkInInterval } = options;
|
|
55
|
+
const { name, displayName, description, tools, checkInInterval, provider, model } = options;
|
|
56
56
|
|
|
57
57
|
// Create directories
|
|
58
58
|
mkdirSync(join(agentDir, 'routines'), { recursive: true });
|
|
@@ -61,7 +61,7 @@ export function scaffoldAgent(agentDir, options) {
|
|
|
61
61
|
// Write agent.config.yaml
|
|
62
62
|
writeFileSync(
|
|
63
63
|
join(agentDir, 'agent.config.yaml'),
|
|
64
|
-
generateAgentConfig({ name, displayName, description, checkInInterval })
|
|
64
|
+
generateAgentConfig({ name, displayName, description, checkInInterval, provider, model })
|
|
65
65
|
);
|
|
66
66
|
|
|
67
67
|
// Write prompt.md
|
|
@@ -82,7 +82,7 @@ export function scaffoldAgent(agentDir, options) {
|
|
|
82
82
|
|
|
83
83
|
// ── Config Generation ───────────────────────────────────
|
|
84
84
|
|
|
85
|
-
function generateAgentConfig({ name, displayName, description, checkInInterval }) {
|
|
85
|
+
function generateAgentConfig({ name, displayName, description, checkInInterval, provider, model }) {
|
|
86
86
|
const intervalMin = INTERVAL_MAP[checkInInterval] || 60;
|
|
87
87
|
|
|
88
88
|
const config = {
|
|
@@ -105,8 +105,8 @@ function generateAgentConfig({ name, displayName, description, checkInInterval }
|
|
|
105
105
|
},
|
|
106
106
|
|
|
107
107
|
brain: {
|
|
108
|
-
provider: 'anthropic',
|
|
109
|
-
model: 'claude-sonnet-4-5-20250929',
|
|
108
|
+
provider: provider || 'anthropic',
|
|
109
|
+
model: model || (provider === 'openai' ? 'gpt-4o' : 'claude-sonnet-4-5-20250929'),
|
|
110
110
|
max_tokens_per_loop: 1000,
|
|
111
111
|
fallback_provider: null,
|
|
112
112
|
},
|
package/src/utils/api.js
CHANGED
|
@@ -367,6 +367,14 @@ export async function deleteAgentMemoryEntry(id, key) {
|
|
|
367
367
|
return response.data;
|
|
368
368
|
}
|
|
369
369
|
|
|
370
|
+
// ── Checkin AI Structuring ────────────────────────────────
|
|
371
|
+
|
|
372
|
+
export async function structureCheckin(data) {
|
|
373
|
+
const client = getNetworkClient();
|
|
374
|
+
const response = await client.post('/checkin/structure', data);
|
|
375
|
+
return response.data;
|
|
376
|
+
}
|
|
377
|
+
|
|
370
378
|
// ── OAuth Client Registration ────────────────────────────
|
|
371
379
|
|
|
372
380
|
export async function registerOAuthClient(name, appType, redirectUris) {
|
package/src/utils/config.js
CHANGED
|
@@ -15,6 +15,7 @@ const DEFAULT_CONFIG = {
|
|
|
15
15
|
clientId: 'mvos_aG_c729fuQxvvqYHOnkgTQ',
|
|
16
16
|
callbackPort: 3737,
|
|
17
17
|
anthropicApiKey: null,
|
|
18
|
+
openaiApiKey: null,
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
function ensureConfigDir() {
|
|
@@ -40,10 +41,13 @@ export function getConfig() {
|
|
|
40
41
|
delete fileConfig.clientId;
|
|
41
42
|
delete fileConfig.oauthBaseUrl;
|
|
42
43
|
const config = { ...DEFAULT_CONFIG, ...fileConfig };
|
|
43
|
-
// Environment variable
|
|
44
|
+
// Environment variable overrides for API keys
|
|
44
45
|
if (process.env.ANTHROPIC_API_KEY) {
|
|
45
46
|
config.anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
46
47
|
}
|
|
48
|
+
if (process.env.OPENAI_API_KEY) {
|
|
49
|
+
config.openaiApiKey = process.env.OPENAI_API_KEY;
|
|
50
|
+
}
|
|
47
51
|
return config;
|
|
48
52
|
} catch {
|
|
49
53
|
return { ...DEFAULT_CONFIG };
|
package/src/utils/formatters.js
CHANGED
|
@@ -502,10 +502,12 @@ export function formatLocalAgentStatus(agent) {
|
|
|
502
502
|
const toolsStr = Object.keys(agent.config?.tools || {}).join(', ') || 'none';
|
|
503
503
|
lines.push(` ${chalk.dim('Tools:')} ${toolsStr}`);
|
|
504
504
|
|
|
505
|
-
// Model
|
|
505
|
+
// Model & provider
|
|
506
506
|
const model = agent.config?.brain?.model;
|
|
507
507
|
if (model) {
|
|
508
|
-
|
|
508
|
+
const prov = agent.config?.brain?.provider;
|
|
509
|
+
const provLabel = prov === 'openai' ? 'OpenAI' : prov === 'anthropic' ? 'Anthropic' : prov || '';
|
|
510
|
+
lines.push(` ${chalk.dim('Model:')} ${model}${provLabel ? chalk.dim(` (${provLabel})`) : ''}`);
|
|
509
511
|
}
|
|
510
512
|
|
|
511
513
|
lines.push('');
|