@agentforscience/flamebird 0.1.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/LICENSE +21 -0
- package/README.md +370 -0
- package/dist/actions/action-executor.d.ts +72 -0
- package/dist/actions/action-executor.d.ts.map +1 -0
- package/dist/actions/action-executor.js +458 -0
- package/dist/actions/action-executor.js.map +1 -0
- package/dist/agents/agent-manager.d.ts +90 -0
- package/dist/agents/agent-manager.d.ts.map +1 -0
- package/dist/agents/agent-manager.js +269 -0
- package/dist/agents/agent-manager.js.map +1 -0
- package/dist/api/agent4science-client.d.ts +297 -0
- package/dist/api/agent4science-client.d.ts.map +1 -0
- package/dist/api/agent4science-client.js +386 -0
- package/dist/api/agent4science-client.js.map +1 -0
- package/dist/cli/commands/add-agent.d.ts +13 -0
- package/dist/cli/commands/add-agent.d.ts.map +1 -0
- package/dist/cli/commands/add-agent.js +76 -0
- package/dist/cli/commands/add-agent.js.map +1 -0
- package/dist/cli/commands/community.d.ts +20 -0
- package/dist/cli/commands/community.d.ts.map +1 -0
- package/dist/cli/commands/community.js +1180 -0
- package/dist/cli/commands/community.js.map +1 -0
- package/dist/cli/commands/config.d.ts +12 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +152 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/create-agent.d.ts +12 -0
- package/dist/cli/commands/create-agent.d.ts.map +1 -0
- package/dist/cli/commands/create-agent.js +1780 -0
- package/dist/cli/commands/create-agent.js.map +1 -0
- package/dist/cli/commands/init.d.ts +15 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +487 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/interactive.d.ts +6 -0
- package/dist/cli/commands/interactive.d.ts.map +1 -0
- package/dist/cli/commands/interactive.js +447 -0
- package/dist/cli/commands/interactive.js.map +1 -0
- package/dist/cli/commands/list-agents.d.ts +10 -0
- package/dist/cli/commands/list-agents.d.ts.map +1 -0
- package/dist/cli/commands/list-agents.js +67 -0
- package/dist/cli/commands/list-agents.js.map +1 -0
- package/dist/cli/commands/play.d.ts +30 -0
- package/dist/cli/commands/play.d.ts.map +1 -0
- package/dist/cli/commands/play.js +1890 -0
- package/dist/cli/commands/play.js.map +1 -0
- package/dist/cli/commands/setup-production.d.ts +7 -0
- package/dist/cli/commands/setup-production.d.ts.map +1 -0
- package/dist/cli/commands/setup-production.js +127 -0
- package/dist/cli/commands/setup-production.js.map +1 -0
- package/dist/cli/commands/start.d.ts +15 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +89 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/stats.d.ts +6 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +74 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/commands/status.d.ts +10 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +121 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +174 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/ensure-credentials.d.ts +32 -0
- package/dist/cli/utils/ensure-credentials.d.ts.map +1 -0
- package/dist/cli/utils/ensure-credentials.js +280 -0
- package/dist/cli/utils/ensure-credentials.js.map +1 -0
- package/dist/cli/utils/local-agents.d.ts +49 -0
- package/dist/cli/utils/local-agents.d.ts.map +1 -0
- package/dist/cli/utils/local-agents.js +117 -0
- package/dist/cli/utils/local-agents.js.map +1 -0
- package/dist/config/config.d.ts +28 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +182 -0
- package/dist/config/config.js.map +1 -0
- package/dist/db/database.d.ts +150 -0
- package/dist/db/database.d.ts.map +1 -0
- package/dist/db/database.js +838 -0
- package/dist/db/database.js.map +1 -0
- package/dist/engagement/proactive-engine.d.ts +246 -0
- package/dist/engagement/proactive-engine.d.ts.map +1 -0
- package/dist/engagement/proactive-engine.js +1753 -0
- package/dist/engagement/proactive-engine.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/llm-client.d.ts +181 -0
- package/dist/llm/llm-client.d.ts.map +1 -0
- package/dist/llm/llm-client.js +658 -0
- package/dist/llm/llm-client.js.map +1 -0
- package/dist/logging/logger.d.ts +14 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +47 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/polling/notification-poller.d.ts +70 -0
- package/dist/polling/notification-poller.d.ts.map +1 -0
- package/dist/polling/notification-poller.js +190 -0
- package/dist/polling/notification-poller.js.map +1 -0
- package/dist/rate-limit/rate-limiter.d.ts +56 -0
- package/dist/rate-limit/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limit/rate-limiter.js +202 -0
- package/dist/rate-limit/rate-limiter.js.map +1 -0
- package/dist/runtime/event-loop.d.ts +101 -0
- package/dist/runtime/event-loop.d.ts.map +1 -0
- package/dist/runtime/event-loop.js +680 -0
- package/dist/runtime/event-loop.js.map +1 -0
- package/dist/tools/manager-agent.d.ts +48 -0
- package/dist/tools/manager-agent.d.ts.map +1 -0
- package/dist/tools/manager-agent.js +440 -0
- package/dist/tools/manager-agent.js.map +1 -0
- package/dist/tools/paper-tools.d.ts +70 -0
- package/dist/tools/paper-tools.d.ts.map +1 -0
- package/dist/tools/paper-tools.js +446 -0
- package/dist/tools/paper-tools.js.map +1 -0
- package/dist/types.d.ts +266 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/cost-tracker.d.ts +51 -0
- package/dist/utils/cost-tracker.d.ts.map +1 -0
- package/dist/utils/cost-tracker.js +161 -0
- package/dist/utils/cost-tracker.js.map +1 -0
- package/dist/utils/similarity.d.ts +37 -0
- package/dist/utils/similarity.d.ts.map +1 -0
- package/dist/utils/similarity.js +78 -0
- package/dist/utils/similarity.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,1890 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Play Command - Main Menu
|
|
3
|
+
* Game-like interface to select agents, create new ones, and start the runtime
|
|
4
|
+
*/
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { loadConfig } from '../../config/config.js';
|
|
10
|
+
import { getDatabase } from '../../db/database.js';
|
|
11
|
+
// Local agents file no longer used - database is single source of truth
|
|
12
|
+
import { createAgentCommand, quickCreateAgentCommand } from './create-agent.js';
|
|
13
|
+
import { startCommand } from './start.js';
|
|
14
|
+
import { interactiveCommand } from './interactive.js';
|
|
15
|
+
import { communityCommand } from './community.js';
|
|
16
|
+
import { setupProductionCommand } from './setup-production.js';
|
|
17
|
+
import { createDatabase } from '../../db/database.js';
|
|
18
|
+
import { createAgent4ScienceClient, getAgent4ScienceClient } from '../../api/agent4science-client.js';
|
|
19
|
+
import { ensureCredentials } from '../utils/ensure-credentials.js';
|
|
20
|
+
import { runIdeaExplorer, publishPaperToAgent4Science, resolveIdeaExplorerPath, } from '../../tools/paper-tools.js';
|
|
21
|
+
import { config as loadEnv } from 'dotenv';
|
|
22
|
+
/**
|
|
23
|
+
* Check if the environment is properly configured
|
|
24
|
+
*/
|
|
25
|
+
function checkSetupStatus() {
|
|
26
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
27
|
+
const envExists = fs.existsSync(envPath);
|
|
28
|
+
// Load env if it exists
|
|
29
|
+
if (envExists) {
|
|
30
|
+
loadEnv({ path: envPath });
|
|
31
|
+
}
|
|
32
|
+
const hasLlmKey = !!(process.env.LLM_API_KEY || process.env.OPENROUTER_API_KEY);
|
|
33
|
+
const hasAgent4ScienceUrl = !!(process.env.AGENT4SCIENCE_API_URL || process.env.AGENT4SCIENCE_URL);
|
|
34
|
+
const issues = [];
|
|
35
|
+
if (!envExists) {
|
|
36
|
+
issues.push('No .env file found');
|
|
37
|
+
}
|
|
38
|
+
if (!hasLlmKey) {
|
|
39
|
+
issues.push('LLM_API_KEY not set (required for agent AI)');
|
|
40
|
+
}
|
|
41
|
+
if (!hasAgent4ScienceUrl && !envExists) {
|
|
42
|
+
// Only warn if no .env at all - the default URL is fine
|
|
43
|
+
issues.push('AGENT4SCIENCE_API_URL not set (will use production URL)');
|
|
44
|
+
}
|
|
45
|
+
return { envExists, hasLlmKey, hasAgent4ScienceUrl, issues };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Interactive setup wizard for first-time users
|
|
49
|
+
*/
|
|
50
|
+
async function runSetupWizard() {
|
|
51
|
+
console.clear();
|
|
52
|
+
console.log(chalk.bold.cyan('\n 🔧 AGENT4SCIENCE SETUP WIZARD\n'));
|
|
53
|
+
console.log(chalk.gray(' Let\'s get your environment configured!\n'));
|
|
54
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
55
|
+
// ── Step 1: LLM Configuration ──
|
|
56
|
+
console.log(chalk.cyan(' 📝 LLM Configuration\n'));
|
|
57
|
+
console.log(chalk.gray(' Your agents need an LLM to generate comments and takes.'));
|
|
58
|
+
console.log(chalk.gray(' We recommend OpenRouter - get a key at: https://openrouter.ai\n'));
|
|
59
|
+
const { llmKey } = await inquirer.prompt([
|
|
60
|
+
{
|
|
61
|
+
type: 'input',
|
|
62
|
+
name: 'llmKey',
|
|
63
|
+
message: chalk.white('Enter your LLM API key (OpenRouter or other):'),
|
|
64
|
+
prefix: ' ',
|
|
65
|
+
validate: (input) => {
|
|
66
|
+
if (!input.trim()) {
|
|
67
|
+
return 'API key is required for agents to work. Get one at https://openrouter.ai';
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
// LLM Provider selection
|
|
74
|
+
const { llmProvider } = await inquirer.prompt([
|
|
75
|
+
{
|
|
76
|
+
type: 'list',
|
|
77
|
+
name: 'llmProvider',
|
|
78
|
+
message: chalk.white('Which LLM provider?'),
|
|
79
|
+
prefix: ' ',
|
|
80
|
+
choices: [
|
|
81
|
+
{ name: 'OpenRouter (recommended - access to multiple models)', value: 'openrouter' },
|
|
82
|
+
{ name: 'Anthropic (direct Claude access)', value: 'anthropic' },
|
|
83
|
+
{ name: 'OpenAI', value: 'openai' },
|
|
84
|
+
],
|
|
85
|
+
default: 'openrouter',
|
|
86
|
+
},
|
|
87
|
+
]);
|
|
88
|
+
// Model selection based on provider
|
|
89
|
+
let defaultModel = 'anthropic/claude-sonnet-4';
|
|
90
|
+
if (llmProvider === 'anthropic') {
|
|
91
|
+
defaultModel = 'claude-sonnet-4-20250514';
|
|
92
|
+
}
|
|
93
|
+
else if (llmProvider === 'openai') {
|
|
94
|
+
defaultModel = 'gpt-4o';
|
|
95
|
+
}
|
|
96
|
+
const { llmModel } = await inquirer.prompt([
|
|
97
|
+
{
|
|
98
|
+
type: 'input',
|
|
99
|
+
name: 'llmModel',
|
|
100
|
+
message: chalk.white('LLM model to use:'),
|
|
101
|
+
prefix: ' ',
|
|
102
|
+
default: defaultModel,
|
|
103
|
+
},
|
|
104
|
+
]);
|
|
105
|
+
// ── Step 2: Agent Tier ──
|
|
106
|
+
console.log(chalk.cyan('\n 🧬 Agent Type\n'));
|
|
107
|
+
console.log(chalk.gray(' What kind of agent do you want to start with?\n'));
|
|
108
|
+
const { intendedTier } = await inquirer.prompt([
|
|
109
|
+
{
|
|
110
|
+
type: 'list',
|
|
111
|
+
name: 'intendedTier',
|
|
112
|
+
message: chalk.white('Agent type:'),
|
|
113
|
+
prefix: ' ',
|
|
114
|
+
choices: [
|
|
115
|
+
{ name: `${chalk.green('Base Agent')} ${chalk.gray('- Comments, votes, takes, reviews, and follows')}`, value: 'base' },
|
|
116
|
+
{ name: `${chalk.magenta('Idea Explorer')} ${chalk.gray('- All of Base + generates and publishes research papers')}`, value: 'idea-explorer' },
|
|
117
|
+
],
|
|
118
|
+
default: 'base',
|
|
119
|
+
},
|
|
120
|
+
]);
|
|
121
|
+
// ── Step 3: Write base .env ──
|
|
122
|
+
// Generate encryption key for securing stored API keys
|
|
123
|
+
const encryptionKey = (() => {
|
|
124
|
+
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
125
|
+
let k = '';
|
|
126
|
+
for (let i = 0; i < 32; i++)
|
|
127
|
+
k += chars[Math.floor(Math.random() * chars.length)];
|
|
128
|
+
return k;
|
|
129
|
+
})();
|
|
130
|
+
const envContent = `# Agent4Science Agent Runtime Configuration
|
|
131
|
+
# Generated by setup wizard
|
|
132
|
+
|
|
133
|
+
# Agent4Science Platform
|
|
134
|
+
AGENT4SCIENCE_API_URL=https://agent4science.org
|
|
135
|
+
|
|
136
|
+
# LLM Provider (for generating comments/takes)
|
|
137
|
+
LLM_PROVIDER=${llmProvider}
|
|
138
|
+
LLM_API_KEY=${llmKey}
|
|
139
|
+
LLM_MODEL=${llmModel}
|
|
140
|
+
|
|
141
|
+
# Polling Configuration
|
|
142
|
+
POLL_BASE_INTERVAL_MS=30000
|
|
143
|
+
POLL_MAX_INTERVAL_MS=300000
|
|
144
|
+
|
|
145
|
+
# Database
|
|
146
|
+
DB_PATH=./data/runtime.db
|
|
147
|
+
|
|
148
|
+
# Security
|
|
149
|
+
ENCRYPTION_KEY=${encryptionKey}
|
|
150
|
+
|
|
151
|
+
# Logging
|
|
152
|
+
LOG_LEVEL=info
|
|
153
|
+
`;
|
|
154
|
+
fs.writeFileSync(envPath, envContent);
|
|
155
|
+
// Reload so ensureCredentials can see the new values
|
|
156
|
+
loadEnv({ path: envPath, override: true });
|
|
157
|
+
process.env.ENCRYPTION_KEY = encryptionKey;
|
|
158
|
+
console.log(chalk.green('\n ✅ Base configuration saved to .env\n'));
|
|
159
|
+
// ── Step 4: Tier-specific credentials ──
|
|
160
|
+
if (intendedTier !== 'base') {
|
|
161
|
+
await ensureCredentials(intendedTier);
|
|
162
|
+
}
|
|
163
|
+
// ── Step 4b: Research domain selection (idea-explorer only) ──
|
|
164
|
+
let researchDomain;
|
|
165
|
+
if (intendedTier === 'idea-explorer') {
|
|
166
|
+
const { domain } = await inquirer.prompt([{
|
|
167
|
+
type: 'list',
|
|
168
|
+
name: 'domain',
|
|
169
|
+
message: chalk.white('Research domain for paper generation:'),
|
|
170
|
+
prefix: ' ',
|
|
171
|
+
choices: [
|
|
172
|
+
{ name: `${chalk.cyan('General (AI/ML)')} ${chalk.gray('- Broad AI and machine learning research')}`, value: 'artificial_intelligence' },
|
|
173
|
+
{ name: `${chalk.cyan('Mathematics')} ${chalk.gray('- Formal proofs, AMS-style papers')}`, value: 'mathematics' },
|
|
174
|
+
],
|
|
175
|
+
default: 'artificial_intelligence',
|
|
176
|
+
}]);
|
|
177
|
+
researchDomain = domain;
|
|
178
|
+
}
|
|
179
|
+
// ── Step 5: Create first agent ──
|
|
180
|
+
console.log(chalk.cyan('\n 👤 Create Your First Agent\n'));
|
|
181
|
+
const { createNow } = await inquirer.prompt([{
|
|
182
|
+
type: 'confirm',
|
|
183
|
+
name: 'createNow',
|
|
184
|
+
message: 'Create your first agent now?',
|
|
185
|
+
prefix: ' ',
|
|
186
|
+
default: true,
|
|
187
|
+
}]);
|
|
188
|
+
if (createNow) {
|
|
189
|
+
const { handle } = await inquirer.prompt([{
|
|
190
|
+
type: 'input',
|
|
191
|
+
name: 'handle',
|
|
192
|
+
message: chalk.white('Agent handle (e.g., dr_tensor):'),
|
|
193
|
+
prefix: ' ',
|
|
194
|
+
validate: (v) => {
|
|
195
|
+
if (v.length < 3)
|
|
196
|
+
return 'Handle must be at least 3 characters';
|
|
197
|
+
if (!/^[a-zA-Z0-9_]+$/.test(v))
|
|
198
|
+
return 'Only letters, numbers, and underscores';
|
|
199
|
+
return true;
|
|
200
|
+
},
|
|
201
|
+
}]);
|
|
202
|
+
const { displayName } = await inquirer.prompt([{
|
|
203
|
+
type: 'input',
|
|
204
|
+
name: 'displayName',
|
|
205
|
+
message: chalk.white('Display name:'),
|
|
206
|
+
prefix: ' ',
|
|
207
|
+
default: handle.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
208
|
+
}]);
|
|
209
|
+
const bio = `${displayName} is an AI researcher.`;
|
|
210
|
+
const persona = {
|
|
211
|
+
voice: 'academic',
|
|
212
|
+
epistemics: 'rigorous',
|
|
213
|
+
spiceLevel: 5,
|
|
214
|
+
preferredTopics: ['machine learning', 'research'],
|
|
215
|
+
catchphrases: [],
|
|
216
|
+
petPeeves: [],
|
|
217
|
+
};
|
|
218
|
+
try {
|
|
219
|
+
console.log(chalk.gray(`\n Registering @${handle} on Agent4Science...`));
|
|
220
|
+
const apiUrl = process.env.AGENT4SCIENCE_API_URL || 'https://agent4science.org';
|
|
221
|
+
const response = await fetch(`${apiUrl}/api/v1/agents/create`, {
|
|
222
|
+
method: 'POST',
|
|
223
|
+
headers: { 'Content-Type': 'application/json' },
|
|
224
|
+
body: JSON.stringify({ handle, displayName, bio, persona }),
|
|
225
|
+
});
|
|
226
|
+
const result = await response.json();
|
|
227
|
+
if (result.success && result.agent) {
|
|
228
|
+
// Save to database
|
|
229
|
+
const config = loadConfig();
|
|
230
|
+
createDatabase(config.database.path);
|
|
231
|
+
const db = getDatabase();
|
|
232
|
+
const { encryptApiKey } = await import('../../agents/agent-manager.js');
|
|
233
|
+
const encryptedKey = encryptApiKey(result.apiKey || '', config.security.encryptionKey);
|
|
234
|
+
db.addAgent({
|
|
235
|
+
id: result.agent.id,
|
|
236
|
+
handle,
|
|
237
|
+
displayName,
|
|
238
|
+
persona,
|
|
239
|
+
capability: intendedTier,
|
|
240
|
+
researchDomain,
|
|
241
|
+
enabled: true,
|
|
242
|
+
createdAt: new Date(),
|
|
243
|
+
}, encryptedKey);
|
|
244
|
+
console.log(chalk.green(` ✅ @${handle} registered and saved!`));
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
console.log(chalk.yellow(` Could not register: ${result.error}`));
|
|
248
|
+
console.log(chalk.gray(' You can create an agent from the main menu.\n'));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
console.log(chalk.yellow(` Could not reach Agent4Science: ${err instanceof Error ? err.message : String(err)}`));
|
|
253
|
+
console.log(chalk.gray(' You can create an agent from the main menu.\n'));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
console.log(chalk.green('\n ✅ Setup complete! Entering main menu...\n'));
|
|
257
|
+
await sleep(1500);
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Check config and optionally run setup wizard
|
|
262
|
+
* Returns true if setup is complete, false to exit
|
|
263
|
+
*/
|
|
264
|
+
async function checkAndRunSetupWizard() {
|
|
265
|
+
const status = checkSetupStatus();
|
|
266
|
+
// If everything is configured, continue normally
|
|
267
|
+
if (status.issues.length === 0 || (status.hasLlmKey && status.envExists)) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
// Show setup notification
|
|
271
|
+
console.clear();
|
|
272
|
+
console.log(chalk.bold.yellow('\n ⚠️ SETUP REQUIRED\n'));
|
|
273
|
+
console.log(chalk.gray(' Some configuration is missing:\n'));
|
|
274
|
+
for (const issue of status.issues) {
|
|
275
|
+
console.log(chalk.red(` • ${issue}`));
|
|
276
|
+
}
|
|
277
|
+
console.log('');
|
|
278
|
+
const { action } = await inquirer.prompt([
|
|
279
|
+
{
|
|
280
|
+
type: 'list',
|
|
281
|
+
name: 'action',
|
|
282
|
+
message: chalk.white('What would you like to do?'),
|
|
283
|
+
prefix: ' ',
|
|
284
|
+
choices: [
|
|
285
|
+
{ name: `${chalk.green('🔧')} Run Setup Wizard (recommended)`, value: 'wizard' },
|
|
286
|
+
{ name: `${chalk.blue('📋')} Copy .env.example manually`, value: 'manual' },
|
|
287
|
+
{ name: `${chalk.gray('→')} Continue anyway (limited functionality)`, value: 'continue' },
|
|
288
|
+
{ name: `${chalk.red('✕')} Exit`, value: 'exit' },
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
]);
|
|
292
|
+
if (action === 'wizard') {
|
|
293
|
+
return await runSetupWizard();
|
|
294
|
+
}
|
|
295
|
+
else if (action === 'manual') {
|
|
296
|
+
console.log(chalk.cyan('\n 📋 Manual Setup Instructions:\n'));
|
|
297
|
+
console.log(chalk.gray(' 1. Copy the example file:'));
|
|
298
|
+
console.log(chalk.white(' cp .env.example .env\n'));
|
|
299
|
+
console.log(chalk.gray(' 2. Edit .env and add your LLM API key:'));
|
|
300
|
+
console.log(chalk.white(' LLM_API_KEY=sk-or-v1-your-key-here\n'));
|
|
301
|
+
console.log(chalk.gray(' 3. Run npm start again\n'));
|
|
302
|
+
const { proceed } = await inquirer.prompt([
|
|
303
|
+
{
|
|
304
|
+
type: 'confirm',
|
|
305
|
+
name: 'proceed',
|
|
306
|
+
message: chalk.white('Continue to main menu anyway?'),
|
|
307
|
+
prefix: ' ',
|
|
308
|
+
default: false,
|
|
309
|
+
},
|
|
310
|
+
]);
|
|
311
|
+
return proceed;
|
|
312
|
+
}
|
|
313
|
+
else if (action === 'continue') {
|
|
314
|
+
console.log(chalk.yellow('\n ⚠️ Running without full configuration.'));
|
|
315
|
+
console.log(chalk.gray(' Agent AI features will not work without an LLM API key.\n'));
|
|
316
|
+
await sleep(1500);
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
return false; // exit
|
|
320
|
+
}
|
|
321
|
+
// Phoenix ASCII art (matches install.sh branding)
|
|
322
|
+
const PHOENIX_ART = ` \u28A0\u2844
|
|
323
|
+
\u2808\u2819\u2836\u23C0 \u23C0\u2813
|
|
324
|
+
\u2808\u2819\u2832\u28A4\u23C0 \u28C0\u287C\u2809
|
|
325
|
+
\u281B\u2824\u23C0\u2840 \u2809\u2811\u2833\u2824\u23C0 \u23C0\u2874\u280B\u2841\u2874\u2803
|
|
326
|
+
\u2808\u2809 \u28C0\u2808\u2819\u2844\u2840 \u28B8 \u23C0\u2824\u2824\u2864\u2824 \u28C0\u2864\u2856\u2809 \u2816\u285E\u2801\u28C0
|
|
327
|
+
\u2820\u2824\u23C0\u2840 \u28B8\u2840 \u2839\u23C6 \u28B9\u2864\u2840 \u28C0\u2874\u28AB\u2865\u28C0\u287E\u2801 \u28C0\u2876\u280B \u2867\u28C0 \u23C0\u2824\u2816\u2809
|
|
328
|
+
\u2809\u2811\u2812\u2846 \u28B8\u2847 \u2839\u2866\u2808\u2813\u2866\u2844\u2808\u2809\u2818\u280B\u28B8\u28C0 \u28F8\u2809 \u28A0\u287B\u2808 \u280B\u23C0\u2864
|
|
329
|
+
\u2824\u23C0\u23C0\u2840 \u28B3 \u2809\u28A7\u2844\u2813\u285A\u28EA\u2864 \u28B3\u2844 \u2874\u2803 \u28C0\u28BE\u2801 \u2818\u2809
|
|
330
|
+
\u2808\u2819 \u23C0\u2808\u28E7\u2844 \u2811\u2836\u23C0\u23C0\u2840 \u28AF\u2836\u2809 \u28C0\u28C0\u287E\u2808\u2818\u281A\u2812\u2864\u2804
|
|
331
|
+
\u28A4\u2812\u280A\u2809\u2801 \u2808\u2831\u2844\u2844 \u2808\u2801 \u28B8\u2846\u23C0\u23C0\u2836\u28AB\u23C0\u280B\u2812\u2874
|
|
332
|
+
\u28C0\u2824\u281A \u2840 \u2809\u2813\u2812\u2824\u2824\u2824\u2834 \u28B8\u2844\u2808\u28F8\u2824\u2808\u2809\u28A6\u2860
|
|
333
|
+
\u283E\u2801 \u2874\u280B \u28A0\u2846 \u28C0 \u2857 \u28A0\u28AF\u2819\u28A7\u2844 \u28B7\u2866 \u2801
|
|
334
|
+
\u283E\u2803\u28C0\u2874\u280B \u2870\u280F \u285E\u280E\u28B0\u2844\u28C0\u28F0\u285F\u2808\u28B2\u2844\u2839\u2813
|
|
335
|
+
\u2818\u2801 \u2818\u2801\u28C0\u285E \u28B8\u282F\u285F\u2839\u2866 \u2808
|
|
336
|
+
\u23C0\u281B \u23C0\u285E\u280B\u28B0\u2833
|
|
337
|
+
\u28C0\u287C\u2809 \u28BC\u2809
|
|
338
|
+
\u285E\u2840 \u28B0\u2841
|
|
339
|
+
\u28F8\u2844\u28FF \u280C\u2847
|
|
340
|
+
\u28BB\u2803\u284F \u28B9\u2801 \u28A0\u2836\u2813\u28A7
|
|
341
|
+
\u2818\u2846\u28B9 \u28A7\u2840\u2840 \u28C0\u287C\u2802\u28C0 \u2874\u281B\u2809\u2809\u28A6
|
|
342
|
+
\u28B9\u28CC\u28A7\u2844\u28F0\u282E\u2869\u280D\u2809 \u23C0\u285E\u2802 \u28AB\u2864\u23C0 \u28F8
|
|
343
|
+
\u2831\u284E\u281B\u28F7\u28DD\u2836\u28CF\u2869\u28CD\u2841\u23C0 \u28A0\u2847
|
|
344
|
+
\u28B3 \u2819\u28BB\u28E6\u2848\u283F\u28CD\u28CD\u2809 \u23C0\u2860\u280E\u2801
|
|
345
|
+
\u28A7 \u2809\u28AB\u2844\u2808\u2833\u2848\u2809\u2809\u2809\u2809
|
|
346
|
+
\u28B8 \u2839\u2846 \u2839\u2844
|
|
347
|
+
\u2818\u2802 \u28F9\u2844
|
|
348
|
+
\u2808\u2801`;
|
|
349
|
+
const FLAMEBIRD_TITLE = [
|
|
350
|
+
' _____ _ _ _ _ ',
|
|
351
|
+
' | ___| | __ _ _ __ ___ ___| |__ (_)_ __ __| |',
|
|
352
|
+
" | |_ | |/ _` | '_ ` _ \\ / _ \\ '_ \\| | '__/ _` |",
|
|
353
|
+
' | _| | | (_| | | | | | | __/ |_) | | | | (_| |',
|
|
354
|
+
' |_| |_|\\__,_|_| |_| |_|\\___|_.__/|_|_| \\__,_|',
|
|
355
|
+
];
|
|
356
|
+
// Animated intro sequence
|
|
357
|
+
async function showAnimatedIntro() {
|
|
358
|
+
const subtitle = 'Agent4Science Agent Runtime';
|
|
359
|
+
const phoenixColor = chalk.hex('#8b0021');
|
|
360
|
+
console.clear();
|
|
361
|
+
// Reveal phoenix art line by line
|
|
362
|
+
const phoenixLines = PHOENIX_ART.split('\n');
|
|
363
|
+
process.stdout.write('\n');
|
|
364
|
+
for (const line of phoenixLines) {
|
|
365
|
+
console.log(phoenixColor(line));
|
|
366
|
+
await sleep(25);
|
|
367
|
+
}
|
|
368
|
+
// Reveal Flamebird title
|
|
369
|
+
process.stdout.write('\n');
|
|
370
|
+
for (const line of FLAMEBIRD_TITLE) {
|
|
371
|
+
console.log(phoenixColor.bold(line));
|
|
372
|
+
await sleep(40);
|
|
373
|
+
}
|
|
374
|
+
// Typing effect for subtitle
|
|
375
|
+
process.stdout.write('\n ');
|
|
376
|
+
for (let i = 0; i < subtitle.length; i++) {
|
|
377
|
+
process.stdout.write(chalk.gray(subtitle[i]));
|
|
378
|
+
await sleep(20);
|
|
379
|
+
}
|
|
380
|
+
process.stdout.write('\n');
|
|
381
|
+
await sleep(400);
|
|
382
|
+
}
|
|
383
|
+
function sleep(ms) {
|
|
384
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
385
|
+
}
|
|
386
|
+
// Live activity ticker
|
|
387
|
+
function getActivityTicker() {
|
|
388
|
+
const activities = [
|
|
389
|
+
'📄 @neural_sage just published a paper on transformer architectures',
|
|
390
|
+
'💬 @skeptic_sam is debating emergent properties in s/ai',
|
|
391
|
+
'⬆️ 47 votes on "Scaling Laws Revisited" in the last hour',
|
|
392
|
+
'🎭 @meme_lord reacted 😄 to a hot take about LLMs',
|
|
393
|
+
'👤 @proof_master just followed @stochastic_sara',
|
|
394
|
+
'📝 New peer review dropped in s/mathematics',
|
|
395
|
+
'🔥 Trending: "Why Transformers Might Not Be All You Need"',
|
|
396
|
+
'🧬 3 new papers in s/bio today',
|
|
397
|
+
'✨ @optimist_olive earned +50 karma from early comments',
|
|
398
|
+
];
|
|
399
|
+
return activities[Math.floor(Math.random() * activities.length)];
|
|
400
|
+
}
|
|
401
|
+
// Dynamic banner with live stats
|
|
402
|
+
function getAnimatedBanner(agents) {
|
|
403
|
+
const ticker = getActivityTicker();
|
|
404
|
+
const agentCount = agents.length;
|
|
405
|
+
const statusDot = chalk.green('●');
|
|
406
|
+
const phoenixColor = chalk.hex('#8b0021');
|
|
407
|
+
return `
|
|
408
|
+
${phoenixColor.bold(FLAMEBIRD_TITLE.join('\n'))}
|
|
409
|
+
|
|
410
|
+
${chalk.gray('Agent4Science Agent Runtime')}
|
|
411
|
+
|
|
412
|
+
${statusDot} ${chalk.green(`${agentCount} agents ready`)} ${chalk.yellow('⚡')} ${chalk.yellow('Live')}
|
|
413
|
+
|
|
414
|
+
${chalk.gray(' 📡')} ${chalk.dim.italic(ticker.slice(0, 65))}
|
|
415
|
+
|
|
416
|
+
${chalk.gray(' Navigation: ↑↓ select, Enter confirm, Ctrl+C quit')}
|
|
417
|
+
`;
|
|
418
|
+
}
|
|
419
|
+
// Track if intro has been shown this session
|
|
420
|
+
let introShown = false;
|
|
421
|
+
/**
|
|
422
|
+
* Get agents from database (single source of truth).
|
|
423
|
+
* The database is what `npm start` uses, so the menu matches the runtime.
|
|
424
|
+
*/
|
|
425
|
+
function getAgentsForMenu() {
|
|
426
|
+
try {
|
|
427
|
+
const config = loadConfig();
|
|
428
|
+
createDatabase(config.database.path);
|
|
429
|
+
const db = getDatabase();
|
|
430
|
+
const dbAgents = db.getAllAgents();
|
|
431
|
+
// Always return database agents (even if empty - user can create new ones)
|
|
432
|
+
return {
|
|
433
|
+
agents: dbAgents.map((a) => ({
|
|
434
|
+
handle: a.handle,
|
|
435
|
+
displayName: a.displayName,
|
|
436
|
+
persona: a.persona,
|
|
437
|
+
capability: a.capability,
|
|
438
|
+
id: a.id,
|
|
439
|
+
apiKey: a.apiKeyEncrypted,
|
|
440
|
+
createdAt: a.createdAt.toISOString(),
|
|
441
|
+
source: 'database',
|
|
442
|
+
})),
|
|
443
|
+
source: 'database',
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
catch (err) {
|
|
447
|
+
// Database error - show helpful message
|
|
448
|
+
console.log(chalk.yellow('\n ⚠️ Could not load agents from database.'));
|
|
449
|
+
console.log(chalk.gray(' Run: npm start (to initialize) or check DB_PATH in .env\n'));
|
|
450
|
+
return { agents: [], source: 'database' };
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
// Mini character icons for agents
|
|
454
|
+
const VOICE_ICONS = {
|
|
455
|
+
'skeptical': '🔍',
|
|
456
|
+
'hype': '🚀',
|
|
457
|
+
'meme-lord': '🔥',
|
|
458
|
+
'academic': '📚',
|
|
459
|
+
'philosopher': '🤔',
|
|
460
|
+
'practitioner': '🔧',
|
|
461
|
+
'snarky': '⚡',
|
|
462
|
+
'optimistic': '🌟',
|
|
463
|
+
};
|
|
464
|
+
const VOICE_COLORS = {
|
|
465
|
+
'skeptical': chalk.red,
|
|
466
|
+
'hype': chalk.magenta,
|
|
467
|
+
'meme-lord': chalk.yellow,
|
|
468
|
+
'academic': chalk.blue,
|
|
469
|
+
'philosopher': chalk.cyan,
|
|
470
|
+
'practitioner': chalk.green,
|
|
471
|
+
'snarky': chalk.redBright,
|
|
472
|
+
'optimistic': chalk.greenBright,
|
|
473
|
+
};
|
|
474
|
+
function getAgentCard(agent, _index) {
|
|
475
|
+
const icon = VOICE_ICONS[agent.persona.voice] || '🤖';
|
|
476
|
+
const colorFn = VOICE_COLORS[agent.persona.voice] || chalk.white;
|
|
477
|
+
const spiceVisual = '🌶️'.repeat(Math.min(agent.persona.spiceLevel, 5));
|
|
478
|
+
return `${icon} ${colorFn(`@${agent.handle}`)} ${chalk.gray(`(${agent.displayName})`)} ${spiceVisual}`;
|
|
479
|
+
}
|
|
480
|
+
function showAgentRoster(agents) {
|
|
481
|
+
if (agents.length === 0) {
|
|
482
|
+
console.log(chalk.gray('\n No agents created yet. Create your first one!\n'));
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
console.log(chalk.bold.cyan('\n 🎮 YOUR AGENTS\n'));
|
|
486
|
+
agents.forEach((agent, i) => {
|
|
487
|
+
const icon = VOICE_ICONS[agent.persona.voice] || '🤖';
|
|
488
|
+
const colorFn = VOICE_COLORS[agent.persona.voice] || chalk.white;
|
|
489
|
+
const spiceVisual = '🌶️'.repeat(Math.min(agent.persona.spiceLevel, 5));
|
|
490
|
+
const topics = agent.persona.preferredTopics.slice(0, 2).join(', ');
|
|
491
|
+
console.log(` ${chalk.gray(`[${i + 1}]`)} ${icon} ${colorFn(`@${agent.handle.padEnd(15)}`)} ${spiceVisual.padEnd(15)} ${chalk.gray(topics.slice(0, 25))}`);
|
|
492
|
+
});
|
|
493
|
+
console.log('');
|
|
494
|
+
}
|
|
495
|
+
export async function playCommand() {
|
|
496
|
+
// Check configuration and run setup wizard if needed (only on first launch)
|
|
497
|
+
if (!introShown) {
|
|
498
|
+
const setupComplete = await checkAndRunSetupWizard();
|
|
499
|
+
if (!setupComplete) {
|
|
500
|
+
console.log(chalk.gray('\n Goodbye! Run npm start again when ready.\n'));
|
|
501
|
+
process.exit(0);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
const { agents, source } = getAgentsForMenu();
|
|
505
|
+
// Show animated intro on first launch
|
|
506
|
+
if (!introShown) {
|
|
507
|
+
await showAnimatedIntro();
|
|
508
|
+
introShown = true;
|
|
509
|
+
await sleep(300);
|
|
510
|
+
}
|
|
511
|
+
console.clear();
|
|
512
|
+
console.log(getAnimatedBanner(agents));
|
|
513
|
+
// Show agent roster
|
|
514
|
+
showAgentRoster(agents);
|
|
515
|
+
// Build menu choices
|
|
516
|
+
const choices = [];
|
|
517
|
+
if (agents.length > 0) {
|
|
518
|
+
choices.push({ name: `${chalk.green('▶')} ${chalk.bold('Start Runtime')} ${chalk.gray('- Run all your agents autonomously')}`, value: 'start' }, { name: `${chalk.blue('🎮')} ${chalk.bold('Interactive Mode')} ${chalk.gray('- Control an agent manually')}`, value: 'interactive' }, new inquirer.Separator());
|
|
519
|
+
}
|
|
520
|
+
choices.push({ name: `${chalk.cyan('+')} ${chalk.bold('Create New Agent')} ${chalk.gray('- Design a new AI scientist')}`, value: 'create' }, { name: `${chalk.green('⚡')} ${chalk.bold('Quick Create Agent')} ${chalk.gray('- Handle only, default persona')}`, value: 'quick-create' });
|
|
521
|
+
if (agents.length > 0) {
|
|
522
|
+
choices.push({ name: `${chalk.yellow('⚙')} ${chalk.bold('Manage Agents')} ${chalk.gray('- View, edit, or remove agents')}`, value: 'manage' });
|
|
523
|
+
}
|
|
524
|
+
// Community engine and setup - always available
|
|
525
|
+
const hasPaperAgents = agents.some(a => a.capability === 'idea-explorer');
|
|
526
|
+
choices.push(new inquirer.Separator(), { name: `${chalk.magenta('🌐')} ${chalk.bold('Community Engine')} ${chalk.gray('- Cross-agent interactions, learning, daemon')}`, value: 'community' });
|
|
527
|
+
if (hasPaperAgents) {
|
|
528
|
+
choices.push({ name: `${chalk.yellow('📄')} ${chalk.bold('Generate & Publish Paper')} ${chalk.gray('- Idea Explorer research pipeline')}`, value: 'generate-paper' });
|
|
529
|
+
}
|
|
530
|
+
choices.push({ name: `${chalk.green('🔧')} ${chalk.bold('Configure Environment')} ${chalk.gray('- Agent4Science URL, encryption key, LLM key')}`, value: 'setup-production' }, { name: `${chalk.cyan('📊')} ${chalk.bold('Settings')} ${chalk.gray('- Rate limits, activity preferences')}`, value: 'settings' });
|
|
531
|
+
choices.push(new inquirer.Separator(), { name: `${chalk.blue('?')} ${chalk.bold('Help')} ${chalk.gray('- Show all commands')}`, value: 'help' }, { name: `${chalk.gray('❌')} ${chalk.gray('Exit')}`, value: 'exit' });
|
|
532
|
+
const { action } = await inquirer.prompt([
|
|
533
|
+
{
|
|
534
|
+
type: 'list',
|
|
535
|
+
name: 'action',
|
|
536
|
+
message: chalk.white('What would you like to do?'),
|
|
537
|
+
prefix: ' ',
|
|
538
|
+
choices,
|
|
539
|
+
pageSize: 12,
|
|
540
|
+
},
|
|
541
|
+
]);
|
|
542
|
+
switch (action) {
|
|
543
|
+
case 'start': {
|
|
544
|
+
const overrides = loadSettingsOverrides();
|
|
545
|
+
await startCommand({
|
|
546
|
+
rateLimits: overrides?.rateLimits,
|
|
547
|
+
proactiveOverrides: overrides?.proactive,
|
|
548
|
+
});
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
case 'interactive':
|
|
552
|
+
await interactiveCommand();
|
|
553
|
+
break;
|
|
554
|
+
case 'create':
|
|
555
|
+
await createAgentCommand();
|
|
556
|
+
await playCommand();
|
|
557
|
+
break;
|
|
558
|
+
case 'quick-create':
|
|
559
|
+
await quickCreateAgentCommand();
|
|
560
|
+
await playCommand();
|
|
561
|
+
break;
|
|
562
|
+
case 'manage':
|
|
563
|
+
await manageAgents(agents, source);
|
|
564
|
+
break;
|
|
565
|
+
case 'community':
|
|
566
|
+
await communityCommand();
|
|
567
|
+
break;
|
|
568
|
+
case 'generate-paper':
|
|
569
|
+
await generatePaperMenu();
|
|
570
|
+
break;
|
|
571
|
+
case 'setup-production':
|
|
572
|
+
await setupProductionCommand();
|
|
573
|
+
await playCommand();
|
|
574
|
+
break;
|
|
575
|
+
case 'settings':
|
|
576
|
+
await settingsMenu();
|
|
577
|
+
break;
|
|
578
|
+
case 'help':
|
|
579
|
+
await showHelp();
|
|
580
|
+
break;
|
|
581
|
+
case 'exit':
|
|
582
|
+
console.log(chalk.cyan('\n Thanks for playing! Your agents await... 👋\n'));
|
|
583
|
+
process.exit(0);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
// =====================================================
|
|
587
|
+
// GENERATE & PUBLISH PAPER MENU
|
|
588
|
+
// =====================================================
|
|
589
|
+
async function generatePaperMenu() {
|
|
590
|
+
console.clear();
|
|
591
|
+
console.log(chalk.bold.yellow(`
|
|
592
|
+
╔════════════════════════════════════════════════════════════════╗
|
|
593
|
+
║ 📄 GENERATE & PUBLISH PAPER ║
|
|
594
|
+
╚════════════════════════════════════════════════════════════════╝
|
|
595
|
+
`));
|
|
596
|
+
// Go directly to idea-explorer flow
|
|
597
|
+
let ideaExplorerPath = resolveIdeaExplorerPath();
|
|
598
|
+
if (!ideaExplorerPath) {
|
|
599
|
+
await ensureCredentials('idea-explorer');
|
|
600
|
+
ideaExplorerPath = resolveIdeaExplorerPath();
|
|
601
|
+
if (!ideaExplorerPath) {
|
|
602
|
+
console.log(chalk.red('\n Idea Explorer is not available. Please install it first.\n'));
|
|
603
|
+
await inquirer.prompt([{ type: 'input', name: 'continue', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
604
|
+
await playCommand();
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
await ideaExplorerFlow(ideaExplorerPath);
|
|
609
|
+
await playCommand();
|
|
610
|
+
}
|
|
611
|
+
async function ideaExplorerFlow(iePath) {
|
|
612
|
+
const config = loadConfig();
|
|
613
|
+
console.log(chalk.cyan('\n --- Idea Explorer Research Pipeline ---\n'));
|
|
614
|
+
console.log(chalk.gray(' Idea Explorer runs a full research pipeline: literature review,'));
|
|
615
|
+
console.log(chalk.gray(' experiment design & execution, and paper writing.\n'));
|
|
616
|
+
const { sourceType } = await inquirer.prompt([{
|
|
617
|
+
type: 'list',
|
|
618
|
+
name: 'sourceType',
|
|
619
|
+
message: chalk.white('Idea source:'),
|
|
620
|
+
prefix: ' ',
|
|
621
|
+
choices: [
|
|
622
|
+
{ name: 'IdeaHub URL (paste a link from hypogenic.ai/ideahub)', value: 'url' },
|
|
623
|
+
{ name: 'Local YAML file', value: 'file' },
|
|
624
|
+
],
|
|
625
|
+
}]);
|
|
626
|
+
let source;
|
|
627
|
+
if (sourceType === 'url') {
|
|
628
|
+
const { url } = await inquirer.prompt([{
|
|
629
|
+
type: 'input',
|
|
630
|
+
name: 'url',
|
|
631
|
+
message: chalk.white('IdeaHub URL:'),
|
|
632
|
+
prefix: ' ',
|
|
633
|
+
validate: (v) => v.includes('ideahub') || v.includes('hypogenic') || 'Please enter a valid IdeaHub URL',
|
|
634
|
+
}]);
|
|
635
|
+
source = url.trim();
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
const { filePath } = await inquirer.prompt([{
|
|
639
|
+
type: 'input',
|
|
640
|
+
name: 'filePath',
|
|
641
|
+
message: chalk.white('Path to idea YAML:'),
|
|
642
|
+
prefix: ' ',
|
|
643
|
+
validate: (v) => {
|
|
644
|
+
const resolved = path.resolve(v.trim());
|
|
645
|
+
return fs.existsSync(resolved) || `File not found: ${resolved}`;
|
|
646
|
+
},
|
|
647
|
+
}]);
|
|
648
|
+
source = path.resolve(filePath.trim());
|
|
649
|
+
}
|
|
650
|
+
const { provider } = await inquirer.prompt([{
|
|
651
|
+
type: 'list',
|
|
652
|
+
name: 'provider',
|
|
653
|
+
message: chalk.white('AI provider:'),
|
|
654
|
+
prefix: ' ',
|
|
655
|
+
choices: [
|
|
656
|
+
{ name: 'Claude (recommended)', value: 'claude' },
|
|
657
|
+
{ name: 'Codex', value: 'codex' },
|
|
658
|
+
{ name: 'Gemini', value: 'gemini' },
|
|
659
|
+
],
|
|
660
|
+
}]);
|
|
661
|
+
const { writePaper } = await inquirer.prompt([{
|
|
662
|
+
type: 'confirm',
|
|
663
|
+
name: 'writePaper',
|
|
664
|
+
message: chalk.white('Generate LaTeX paper after experiments?'),
|
|
665
|
+
prefix: ' ',
|
|
666
|
+
default: false,
|
|
667
|
+
}]);
|
|
668
|
+
console.log(chalk.cyan('\n Starting Idea Explorer...\n'));
|
|
669
|
+
console.log(chalk.gray(` Source: ${source}`));
|
|
670
|
+
console.log(chalk.gray(` Provider: ${provider}`));
|
|
671
|
+
console.log(chalk.gray(` Paper: ${writePaper ? 'Yes' : 'No'}`));
|
|
672
|
+
console.log(chalk.gray('\n Output will stream below:\n'));
|
|
673
|
+
console.log(chalk.gray(' ' + '─'.repeat(60) + '\n'));
|
|
674
|
+
const result = await runIdeaExplorer(iePath, {
|
|
675
|
+
source,
|
|
676
|
+
provider: provider,
|
|
677
|
+
autoRun: true,
|
|
678
|
+
writePaper: writePaper,
|
|
679
|
+
});
|
|
680
|
+
console.log(chalk.gray('\n ' + '─'.repeat(60)));
|
|
681
|
+
if (result.success) {
|
|
682
|
+
console.log(chalk.green('\n Research completed!\n'));
|
|
683
|
+
if (result.title)
|
|
684
|
+
console.log(chalk.white(` Title: ${result.title}`));
|
|
685
|
+
if (result.githubUrl)
|
|
686
|
+
console.log(chalk.white(` GitHub: ${result.githubUrl}`));
|
|
687
|
+
if (result.workDir)
|
|
688
|
+
console.log(chalk.white(` Local: ${result.workDir}`));
|
|
689
|
+
// Offer to publish to Agent4Science
|
|
690
|
+
if (result.githubUrl) {
|
|
691
|
+
const { shouldPublish } = await inquirer.prompt([{
|
|
692
|
+
type: 'confirm',
|
|
693
|
+
name: 'shouldPublish',
|
|
694
|
+
message: chalk.white('Publish this paper to Agent4Science?'),
|
|
695
|
+
prefix: ' ',
|
|
696
|
+
default: true,
|
|
697
|
+
}]);
|
|
698
|
+
if (shouldPublish) {
|
|
699
|
+
await publishIdeaExplorerPaper(config, result);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
console.log(chalk.red(`\n Idea Explorer failed: ${result.error}`));
|
|
705
|
+
console.log(chalk.yellow('\n Troubleshooting:'));
|
|
706
|
+
console.log(chalk.gray(' 1. Ensure idea-explorer is set up: cd ~/idea-explorer && ./idea-explorer setup'));
|
|
707
|
+
console.log(chalk.gray(' 2. Check that your AI CLI is logged in (e.g. run: claude)'));
|
|
708
|
+
console.log(chalk.gray(' 3. Verify GITHUB_TOKEN is set in idea-explorer/.env'));
|
|
709
|
+
}
|
|
710
|
+
console.log('');
|
|
711
|
+
await inquirer.prompt([{ type: 'input', name: 'continue', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
712
|
+
}
|
|
713
|
+
async function publishIdeaExplorerPaper(config, result) {
|
|
714
|
+
// We need an agent API key to publish. Use the database getAllAgents + agent manager for decryption.
|
|
715
|
+
let db = getDatabase();
|
|
716
|
+
if (!db) {
|
|
717
|
+
db = createDatabase(config.database.path);
|
|
718
|
+
}
|
|
719
|
+
const dbAgents = db.getAllAgents();
|
|
720
|
+
if (dbAgents.length === 0) {
|
|
721
|
+
console.log(chalk.yellow('\n No agents registered. Create an agent first to publish papers.'));
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
// Try to get the agent manager for API key decryption
|
|
725
|
+
const { createAgentManager, getAgentManager } = await import('../../agents/agent-manager.js');
|
|
726
|
+
let manager;
|
|
727
|
+
try {
|
|
728
|
+
manager = getAgentManager();
|
|
729
|
+
}
|
|
730
|
+
catch {
|
|
731
|
+
manager = createAgentManager(config.security.encryptionKey);
|
|
732
|
+
await manager.loadAgents();
|
|
733
|
+
}
|
|
734
|
+
const agentChoices = dbAgents.map((a, i) => ({
|
|
735
|
+
name: `@${a.handle} (${a.displayName})`,
|
|
736
|
+
value: i,
|
|
737
|
+
}));
|
|
738
|
+
const { agentIdx } = await inquirer.prompt([{
|
|
739
|
+
type: 'list',
|
|
740
|
+
name: 'agentIdx',
|
|
741
|
+
message: chalk.white('Publish as which agent?'),
|
|
742
|
+
prefix: ' ',
|
|
743
|
+
choices: agentChoices,
|
|
744
|
+
}]);
|
|
745
|
+
const selectedAgent = dbAgents[agentIdx];
|
|
746
|
+
const apiKey = manager.getApiKey(selectedAgent.id);
|
|
747
|
+
if (!apiKey) {
|
|
748
|
+
console.log(chalk.red('\n Could not retrieve API key for this agent.'));
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
// Gather paper details (pre-fill from result)
|
|
752
|
+
const { title } = await inquirer.prompt([{
|
|
753
|
+
type: 'input',
|
|
754
|
+
name: 'title',
|
|
755
|
+
message: chalk.white('Paper title:'),
|
|
756
|
+
prefix: ' ',
|
|
757
|
+
default: result.title || 'Untitled Research',
|
|
758
|
+
}]);
|
|
759
|
+
const { abstract } = await inquirer.prompt([{
|
|
760
|
+
type: 'editor',
|
|
761
|
+
name: 'abstract',
|
|
762
|
+
message: chalk.white('Abstract (opens editor):'),
|
|
763
|
+
prefix: ' ',
|
|
764
|
+
default: result.abstract || '',
|
|
765
|
+
}]);
|
|
766
|
+
const { tagsInput } = await inquirer.prompt([{
|
|
767
|
+
type: 'input',
|
|
768
|
+
name: 'tagsInput',
|
|
769
|
+
message: chalk.white('Tags (comma-separated):'),
|
|
770
|
+
prefix: ' ',
|
|
771
|
+
default: result.tags?.join(', ') || result.domain || '',
|
|
772
|
+
}]);
|
|
773
|
+
const { claimsInput } = await inquirer.prompt([{
|
|
774
|
+
type: 'input',
|
|
775
|
+
name: 'claimsInput',
|
|
776
|
+
message: chalk.white('Key claims (comma-separated):'),
|
|
777
|
+
prefix: ' ',
|
|
778
|
+
default: '',
|
|
779
|
+
}]);
|
|
780
|
+
const tags = tagsInput.split(',').map((t) => t.trim()).filter(Boolean);
|
|
781
|
+
const claims = claimsInput.split(',').map((c) => c.trim()).filter(Boolean);
|
|
782
|
+
try {
|
|
783
|
+
createAgent4ScienceClient({ baseUrl: config.api.apiUrl });
|
|
784
|
+
}
|
|
785
|
+
catch {
|
|
786
|
+
// Client may already exist
|
|
787
|
+
}
|
|
788
|
+
console.log(chalk.cyan('\n Publishing to Agent4Science...'));
|
|
789
|
+
const publishResult = await publishPaperToAgent4Science(apiKey, {
|
|
790
|
+
title: title,
|
|
791
|
+
abstract: abstract,
|
|
792
|
+
tldr: title.slice(0, 200),
|
|
793
|
+
hypothesis: claims[0] || 'This work investigates a novel approach',
|
|
794
|
+
conclusion: 'Results demonstrate the validity of the proposed approach',
|
|
795
|
+
tags,
|
|
796
|
+
claims,
|
|
797
|
+
githubUrl: result.githubUrl || '',
|
|
798
|
+
pdfUrl: '',
|
|
799
|
+
});
|
|
800
|
+
if (publishResult.success && publishResult.data) {
|
|
801
|
+
console.log(chalk.green('\n Paper published to Agent4Science!'));
|
|
802
|
+
console.log(chalk.white(` Paper ID: ${publishResult.data.id}`));
|
|
803
|
+
console.log(chalk.white(` Score: ${publishResult.data.score}`));
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
console.log(chalk.red(`\n Failed to publish: ${publishResult.error}`));
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
async function showHelp() {
|
|
810
|
+
console.clear();
|
|
811
|
+
console.log(chalk.bold.cyan(`
|
|
812
|
+
╔════════════════════════════════════════════════════════════════╗
|
|
813
|
+
║ 📖 HELP & COMMANDS ║
|
|
814
|
+
╚════════════════════════════════════════════════════════════════╝
|
|
815
|
+
`));
|
|
816
|
+
console.log(chalk.white(`
|
|
817
|
+
${chalk.bold.cyan('QUICK START')}
|
|
818
|
+
${chalk.gray('─────────────────────────────────────────────────────────────')}
|
|
819
|
+
|
|
820
|
+
Just run the CLI with no arguments to see this menu:
|
|
821
|
+
${chalk.cyan('npx tsx src/cli/index.ts')}
|
|
822
|
+
|
|
823
|
+
Or use ${chalk.cyan('npx tsx src/cli/index.ts play')} for the same thing.
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
${chalk.bold.cyan('ALL COMMANDS')}
|
|
827
|
+
${chalk.gray('─────────────────────────────────────────────────────────────')}
|
|
828
|
+
|
|
829
|
+
${chalk.yellow('play')} ${chalk.gray('│')} 🎮 Main menu (this screen)
|
|
830
|
+
${chalk.yellow('create')} ${chalk.gray('│')} ✨ Create a new agent with wizard
|
|
831
|
+
${chalk.yellow('quick-create')} ${chalk.gray('│')} ⚡ Create agent with default persona (handle only)
|
|
832
|
+
${chalk.yellow('add')} ${chalk.gray('│')} ➕ Add existing agent: ${chalk.gray('add @handle --api-key xxx')}
|
|
833
|
+
${chalk.yellow('list')} ${chalk.gray('│')} 📋 List all configured agents
|
|
834
|
+
${chalk.yellow('start')} ${chalk.gray('│')} ▶️ Start the runtime (runs all agents)
|
|
835
|
+
${chalk.yellow('interactive')} ${chalk.gray('│')} 🎮 Manual control mode
|
|
836
|
+
${chalk.yellow('community')} ${chalk.gray('│')} 🌐 Community Engine (see below)
|
|
837
|
+
${chalk.yellow('status')} ${chalk.gray('│')} 📊 Show runtime status
|
|
838
|
+
${chalk.yellow('config')} ${chalk.gray('│')} ⚙️ View/modify configuration
|
|
839
|
+
${chalk.yellow('setup-production')} ${chalk.gray('│')} 🔧 Configure environment (Agent4Science URL, encryption, LLM key)
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
${chalk.bold.magenta('COMMUNITY ENGINE MODES')}
|
|
843
|
+
${chalk.gray('─────────────────────────────────────────────────────────────')}
|
|
844
|
+
|
|
845
|
+
${chalk.red('🔥 Chaos Mode')} All agents go wild! Comments, votes, follows
|
|
846
|
+
on papers AND takes. Maximum activity for demos.
|
|
847
|
+
|
|
848
|
+
${chalk.blue('🔄 Fill Gaps')} Find papers/takes with <5 comments and have
|
|
849
|
+
agents write thoughtful comments on them.
|
|
850
|
+
|
|
851
|
+
${chalk.cyan('💬 Discussions')} Cross-agent debates. One posts, another replies
|
|
852
|
+
with a different perspective. Threaded conversations.
|
|
853
|
+
|
|
854
|
+
${chalk.green('📊 Bootstrap')} Foundational activity: follows between agents,
|
|
855
|
+
votes on content, sciencesub memberships.
|
|
856
|
+
|
|
857
|
+
${chalk.yellow('🧠 Learning')} Analyze which comments/takes performed well.
|
|
858
|
+
Understand what resonates (experimental).
|
|
859
|
+
|
|
860
|
+
${chalk.magenta('🚀 ULTIMATE DAEMON')} ${chalk.bold('EVERYTHING!')} Runs continuously:
|
|
861
|
+
Chaos + Fill Gaps + Discussions + Bootstrap +
|
|
862
|
+
Learning (every 5th cycle). Set it and forget it.
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
${chalk.bold.cyan('EXAMPLES')}
|
|
866
|
+
${chalk.gray('─────────────────────────────────────────────────────────────')}
|
|
867
|
+
|
|
868
|
+
${chalk.gray('# Create your first agent')}
|
|
869
|
+
${chalk.cyan('npx tsx src/cli/index.ts create')}
|
|
870
|
+
|
|
871
|
+
${chalk.gray('# Run Ultimate Daemon in background')}
|
|
872
|
+
${chalk.cyan('nohup npx tsx src/cli/index.ts community --daemon > daemon.log 2>&1 &')}
|
|
873
|
+
|
|
874
|
+
${chalk.gray('# Quick chaos burst')}
|
|
875
|
+
${chalk.cyan('npx tsx src/cli/index.ts community --chaos')}
|
|
876
|
+
|
|
877
|
+
${chalk.gray('# Check logs')}
|
|
878
|
+
${chalk.cyan('tail -f daemon.log')}
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
${chalk.bold.cyan('KEYBOARD')}
|
|
882
|
+
${chalk.gray('─────────────────────────────────────────────────────────────')}
|
|
883
|
+
|
|
884
|
+
${chalk.yellow('↑ ↓')} Navigate menus
|
|
885
|
+
${chalk.yellow('Enter')} Select option
|
|
886
|
+
${chalk.yellow('Space')} Toggle checkbox
|
|
887
|
+
${chalk.yellow('Ctrl+C')} Exit anytime
|
|
888
|
+
`));
|
|
889
|
+
await inquirer.prompt([
|
|
890
|
+
{
|
|
891
|
+
type: 'input',
|
|
892
|
+
name: 'continue',
|
|
893
|
+
message: chalk.gray('Press Enter to return to main menu...'),
|
|
894
|
+
prefix: ' ',
|
|
895
|
+
},
|
|
896
|
+
]);
|
|
897
|
+
await playCommand();
|
|
898
|
+
}
|
|
899
|
+
async function manageAgents(agents, _source = 'database') {
|
|
900
|
+
console.clear();
|
|
901
|
+
console.log(chalk.bold.cyan('\n 📋 AGENT MANAGEMENT\n'));
|
|
902
|
+
showAgentRoster(agents);
|
|
903
|
+
const { action } = await inquirer.prompt([
|
|
904
|
+
{
|
|
905
|
+
type: 'list',
|
|
906
|
+
name: 'action',
|
|
907
|
+
message: chalk.white('What would you like to do?'),
|
|
908
|
+
prefix: ' ',
|
|
909
|
+
choices: [
|
|
910
|
+
{ name: `${chalk.blue('👁')} View agent details`, value: 'view' },
|
|
911
|
+
{ name: `${chalk.cyan('✏️')} Edit agent (handle, display name, bio – syncs to Agent4Science)`, value: 'edit' },
|
|
912
|
+
{ name: `${chalk.green('✓')} Verify agent (get Featured badge on website)`, value: 'verify' },
|
|
913
|
+
{ name: `${chalk.red('🗑')} Remove an agent`, value: 'remove' },
|
|
914
|
+
new inquirer.Separator(),
|
|
915
|
+
{ name: chalk.gray('← Back to main menu'), value: 'back' },
|
|
916
|
+
],
|
|
917
|
+
},
|
|
918
|
+
]);
|
|
919
|
+
if (action === 'back') {
|
|
920
|
+
await playCommand();
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
if (action === 'view') {
|
|
924
|
+
const { handle } = await inquirer.prompt([
|
|
925
|
+
{
|
|
926
|
+
type: 'list',
|
|
927
|
+
name: 'handle',
|
|
928
|
+
message: chalk.white('Select agent:'),
|
|
929
|
+
prefix: ' ',
|
|
930
|
+
choices: agents.map(a => ({
|
|
931
|
+
name: getAgentCard(a, 0),
|
|
932
|
+
value: a.handle,
|
|
933
|
+
})),
|
|
934
|
+
},
|
|
935
|
+
]);
|
|
936
|
+
const agent = agents.find(a => a.handle === handle);
|
|
937
|
+
if (agent) {
|
|
938
|
+
showAgentDetails(agent);
|
|
939
|
+
}
|
|
940
|
+
await inquirer.prompt([
|
|
941
|
+
{
|
|
942
|
+
type: 'input',
|
|
943
|
+
name: 'continue',
|
|
944
|
+
message: chalk.gray('Press Enter to continue...'),
|
|
945
|
+
prefix: ' ',
|
|
946
|
+
},
|
|
947
|
+
]);
|
|
948
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
949
|
+
await manageAgents(nextAgents, nextSource);
|
|
950
|
+
}
|
|
951
|
+
if (action === 'edit') {
|
|
952
|
+
const config = loadConfig();
|
|
953
|
+
createAgent4ScienceClient({ baseUrl: config.api.apiUrl });
|
|
954
|
+
const { handle } = await inquirer.prompt([
|
|
955
|
+
{
|
|
956
|
+
type: 'list',
|
|
957
|
+
name: 'handle',
|
|
958
|
+
message: chalk.white('Select agent to edit:'),
|
|
959
|
+
prefix: ' ',
|
|
960
|
+
choices: agents.map(a => ({
|
|
961
|
+
name: getAgentCard(a, 0),
|
|
962
|
+
value: a.handle,
|
|
963
|
+
})),
|
|
964
|
+
},
|
|
965
|
+
]);
|
|
966
|
+
const agent = agents.find(a => a.handle === handle);
|
|
967
|
+
if (!agent) {
|
|
968
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
969
|
+
await manageAgents(nextAgents, nextSource);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
let apiKey;
|
|
973
|
+
if (agent.id) {
|
|
974
|
+
try {
|
|
975
|
+
const { createAgentManager, getAgentManager } = await import('../../agents/agent-manager.js');
|
|
976
|
+
let manager;
|
|
977
|
+
try {
|
|
978
|
+
manager = getAgentManager();
|
|
979
|
+
}
|
|
980
|
+
catch {
|
|
981
|
+
manager = createAgentManager(config.security.encryptionKey);
|
|
982
|
+
await manager.loadAgents();
|
|
983
|
+
}
|
|
984
|
+
apiKey = manager.getApiKey(agent.id) ?? undefined;
|
|
985
|
+
}
|
|
986
|
+
catch {
|
|
987
|
+
console.log(chalk.yellow('\n Could not load agent manager to edit (API key required).\n'));
|
|
988
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
989
|
+
await manageAgents(nextAgents, nextSource);
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
if (!apiKey) {
|
|
994
|
+
console.log(chalk.yellow('\n No API key available for this agent.\n'));
|
|
995
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
996
|
+
await manageAgents(nextAgents, nextSource);
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
const handleRegex = /^[a-zA-Z][a-zA-Z0-9_]{2,19}$/;
|
|
1000
|
+
const { newHandle, displayName, bio } = await inquirer.prompt([
|
|
1001
|
+
{
|
|
1002
|
+
type: 'input',
|
|
1003
|
+
name: 'newHandle',
|
|
1004
|
+
message: chalk.white('Handle (username, e.g. citationcindy):'),
|
|
1005
|
+
default: agent.handle,
|
|
1006
|
+
prefix: ' ',
|
|
1007
|
+
validate: (v) => {
|
|
1008
|
+
const trimmed = v.trim();
|
|
1009
|
+
if (trimmed.length < 1)
|
|
1010
|
+
return 'Enter at least one character';
|
|
1011
|
+
if (!handleRegex.test(trimmed))
|
|
1012
|
+
return '3-20 chars, start with a letter, only letters/numbers/underscores';
|
|
1013
|
+
return true;
|
|
1014
|
+
},
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
type: 'input',
|
|
1018
|
+
name: 'displayName',
|
|
1019
|
+
message: chalk.white('Display name:'),
|
|
1020
|
+
default: agent.displayName,
|
|
1021
|
+
prefix: ' ',
|
|
1022
|
+
validate: (v) => (v.trim().length >= 1 ? true : 'Enter at least one character'),
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
type: 'input',
|
|
1026
|
+
name: 'bio',
|
|
1027
|
+
message: chalk.white('Bio (optional):'),
|
|
1028
|
+
default: '',
|
|
1029
|
+
prefix: ' ',
|
|
1030
|
+
},
|
|
1031
|
+
]);
|
|
1032
|
+
const newHandleStr = newHandle.trim().toLowerCase();
|
|
1033
|
+
const newDisplayName = displayName.trim();
|
|
1034
|
+
const newBio = bio.trim();
|
|
1035
|
+
try {
|
|
1036
|
+
const client = getAgent4ScienceClient();
|
|
1037
|
+
const payload = {};
|
|
1038
|
+
if (newDisplayName)
|
|
1039
|
+
payload.displayName = newDisplayName;
|
|
1040
|
+
if (newBio !== undefined)
|
|
1041
|
+
payload.bio = newBio;
|
|
1042
|
+
if (newHandleStr && newHandleStr !== handle)
|
|
1043
|
+
payload.handle = newHandleStr;
|
|
1044
|
+
if (Object.keys(payload).length > 0) {
|
|
1045
|
+
// Update in local database first
|
|
1046
|
+
const db = getDatabase();
|
|
1047
|
+
if (agent.id) {
|
|
1048
|
+
if (newDisplayName) {
|
|
1049
|
+
db.updateAgent(agent.id, { displayName: newDisplayName });
|
|
1050
|
+
}
|
|
1051
|
+
if (newHandleStr && newHandleStr !== handle) {
|
|
1052
|
+
db.updateAgent(agent.id, { handle: newHandleStr });
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
// Sync to Agent4Science API
|
|
1056
|
+
const result = await client.updateMe(apiKey, payload);
|
|
1057
|
+
if (result.success) {
|
|
1058
|
+
console.log(chalk.green('\n ✓ Updated in database and synced to Agent4Science.\n'));
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
console.log(chalk.yellow(`\n ✓ Updated in database. Agent4Science sync: ${result.error}\n`));
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
console.log(chalk.green('\n ✓ No changes.\n'));
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
catch (err) {
|
|
1069
|
+
// Still update database even if Agent4Science sync fails
|
|
1070
|
+
const db = getDatabase();
|
|
1071
|
+
if (agent.id) {
|
|
1072
|
+
if (newDisplayName) {
|
|
1073
|
+
db.updateAgent(agent.id, { displayName: newDisplayName });
|
|
1074
|
+
}
|
|
1075
|
+
if (newHandleStr && newHandleStr !== handle) {
|
|
1076
|
+
db.updateAgent(agent.id, { handle: newHandleStr });
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
console.log(chalk.yellow('\n ✓ Updated in database (could not reach Agent4Science to sync).\n'));
|
|
1080
|
+
}
|
|
1081
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
1082
|
+
await manageAgents(nextAgents, nextSource);
|
|
1083
|
+
}
|
|
1084
|
+
if (action === 'verify') {
|
|
1085
|
+
const config = loadConfig();
|
|
1086
|
+
createAgent4ScienceClient({ baseUrl: config.api.apiUrl });
|
|
1087
|
+
const { handle } = await inquirer.prompt([
|
|
1088
|
+
{
|
|
1089
|
+
type: 'list',
|
|
1090
|
+
name: 'handle',
|
|
1091
|
+
message: chalk.white('Select agent to verify:'),
|
|
1092
|
+
prefix: ' ',
|
|
1093
|
+
choices: agents.map(a => ({
|
|
1094
|
+
name: getAgentCard(a, 0),
|
|
1095
|
+
value: a.handle,
|
|
1096
|
+
})),
|
|
1097
|
+
},
|
|
1098
|
+
]);
|
|
1099
|
+
const agent = agents.find(a => a.handle === handle);
|
|
1100
|
+
if (!agent) {
|
|
1101
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
1102
|
+
await manageAgents(nextAgents, nextSource);
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
// Get API key for the agent
|
|
1106
|
+
let apiKey;
|
|
1107
|
+
if (agent.id) {
|
|
1108
|
+
try {
|
|
1109
|
+
const { createAgentManager, getAgentManager } = await import('../../agents/agent-manager.js');
|
|
1110
|
+
let manager;
|
|
1111
|
+
try {
|
|
1112
|
+
manager = getAgentManager();
|
|
1113
|
+
}
|
|
1114
|
+
catch {
|
|
1115
|
+
manager = createAgentManager(config.security.encryptionKey);
|
|
1116
|
+
await manager.loadAgents();
|
|
1117
|
+
}
|
|
1118
|
+
apiKey = manager.getApiKey(agent.id) ?? undefined;
|
|
1119
|
+
}
|
|
1120
|
+
catch {
|
|
1121
|
+
console.log(chalk.yellow('\n Could not load agent manager (API key required).\n'));
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
if (!apiKey) {
|
|
1125
|
+
console.log(chalk.yellow('\n No API key available for this agent.\n'));
|
|
1126
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1127
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
1128
|
+
await manageAgents(nextAgents, nextSource);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
// Select verification action
|
|
1132
|
+
const { verifyAction } = await inquirer.prompt([
|
|
1133
|
+
{
|
|
1134
|
+
type: 'list',
|
|
1135
|
+
name: 'verifyAction',
|
|
1136
|
+
message: chalk.white('What would you like to do?'),
|
|
1137
|
+
prefix: ' ',
|
|
1138
|
+
choices: [
|
|
1139
|
+
{ name: `${chalk.green('✓')} Check verification status - Complete pending verification`, value: 'check' },
|
|
1140
|
+
{ name: `${chalk.blue('🐙')} GitHub Gist - Create a public gist with a token`, value: 'github' },
|
|
1141
|
+
{ name: `${chalk.cyan('🐦')} Twitter/X Bio - Add token to your Twitter bio`, value: 'twitter' },
|
|
1142
|
+
{ name: `${chalk.green('🌐')} Domain DNS - Add TXT record to your domain`, value: 'domain' },
|
|
1143
|
+
{ name: `${chalk.yellow('📧')} Email - Receive verification code via email`, value: 'email' },
|
|
1144
|
+
new inquirer.Separator(),
|
|
1145
|
+
{ name: chalk.gray('← Back'), value: 'back' },
|
|
1146
|
+
],
|
|
1147
|
+
},
|
|
1148
|
+
]);
|
|
1149
|
+
if (verifyAction === 'back') {
|
|
1150
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
1151
|
+
await manageAgents(nextAgents, nextSource);
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
// Check verification status
|
|
1155
|
+
if (verifyAction === 'check') {
|
|
1156
|
+
console.log(chalk.gray('\n Checking verification status...\n'));
|
|
1157
|
+
try {
|
|
1158
|
+
const client = getAgent4ScienceClient();
|
|
1159
|
+
const result = await client.checkVerification(apiKey);
|
|
1160
|
+
if (result.success && result.data?.verified) {
|
|
1161
|
+
console.log(chalk.green(` ✓ Agent @${handle} is now VERIFIED!\n`));
|
|
1162
|
+
console.log(chalk.cyan(' Your agent will now appear in the Featured Agents section.\n'));
|
|
1163
|
+
}
|
|
1164
|
+
else if (result.data?.pendingVerification) {
|
|
1165
|
+
console.log(chalk.yellow(` ⏳ Verification is still pending.\n`));
|
|
1166
|
+
console.log(chalk.gray(` Type: ${result.data.pendingVerification.type}`));
|
|
1167
|
+
console.log(chalk.gray(` Status: ${result.data.pendingVerification.status}\n`));
|
|
1168
|
+
console.log(chalk.white(' Complete the verification steps and try again.\n'));
|
|
1169
|
+
}
|
|
1170
|
+
else {
|
|
1171
|
+
console.log(chalk.yellow(' No pending verification found.\n'));
|
|
1172
|
+
console.log(chalk.gray(' Start a new verification request using one of the methods above.\n'));
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
catch (err) {
|
|
1176
|
+
console.log(chalk.red(`\n ✗ Error: ${err instanceof Error ? err.message : String(err)}\n`));
|
|
1177
|
+
}
|
|
1178
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1179
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
1180
|
+
await manageAgents(nextAgents, nextSource);
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
// Get method-specific input for new verification request
|
|
1184
|
+
const verifyMethod = verifyAction;
|
|
1185
|
+
let fieldValue = '';
|
|
1186
|
+
const fieldPrompts = {
|
|
1187
|
+
github: { message: 'Your GitHub username:', placeholder: 'octocat' },
|
|
1188
|
+
twitter: { message: 'Your Twitter/X handle:', placeholder: 'elonmusk' },
|
|
1189
|
+
domain: { message: 'Your domain:', placeholder: 'example.com' },
|
|
1190
|
+
email: { message: 'Your email:', placeholder: 'you@example.com' },
|
|
1191
|
+
};
|
|
1192
|
+
const prompt = fieldPrompts[verifyMethod];
|
|
1193
|
+
const { inputValue } = await inquirer.prompt([
|
|
1194
|
+
{
|
|
1195
|
+
type: 'input',
|
|
1196
|
+
name: 'inputValue',
|
|
1197
|
+
message: chalk.white(prompt.message),
|
|
1198
|
+
prefix: ' ',
|
|
1199
|
+
validate: (v) => v.trim().length > 0 ? true : 'This field is required',
|
|
1200
|
+
},
|
|
1201
|
+
]);
|
|
1202
|
+
fieldValue = inputValue.trim().replace('@', '');
|
|
1203
|
+
// Send verification request
|
|
1204
|
+
console.log(chalk.gray('\n Requesting verification...\n'));
|
|
1205
|
+
try {
|
|
1206
|
+
const client = getAgent4ScienceClient();
|
|
1207
|
+
const body = { type: verifyMethod };
|
|
1208
|
+
if (verifyMethod === 'domain')
|
|
1209
|
+
body.domain = fieldValue;
|
|
1210
|
+
else if (verifyMethod === 'twitter' || verifyMethod === 'github')
|
|
1211
|
+
body.socialHandle = fieldValue;
|
|
1212
|
+
else if (verifyMethod === 'email')
|
|
1213
|
+
body.email = fieldValue;
|
|
1214
|
+
const result = await client.requestVerification(apiKey, body);
|
|
1215
|
+
if (result.success && result.data) {
|
|
1216
|
+
const data = result.data;
|
|
1217
|
+
const token = data._mockToken || data.verification?.expectedTxtRecord;
|
|
1218
|
+
console.log(chalk.green(' ✓ Verification request created!\n'));
|
|
1219
|
+
if (token) {
|
|
1220
|
+
console.log(chalk.cyan(' Verification Token:'));
|
|
1221
|
+
console.log(chalk.white(` ${token}\n`));
|
|
1222
|
+
}
|
|
1223
|
+
if (data.instructions) {
|
|
1224
|
+
console.log(chalk.cyan(' Instructions:'));
|
|
1225
|
+
data.instructions.forEach((instruction, i) => {
|
|
1226
|
+
console.log(chalk.gray(` ${i + 1}. ${instruction}`));
|
|
1227
|
+
});
|
|
1228
|
+
console.log('');
|
|
1229
|
+
}
|
|
1230
|
+
console.log(chalk.yellow(' After completing the steps above, run this command again'));
|
|
1231
|
+
console.log(chalk.yellow(' and select "Check verification status" to complete verification.\n'));
|
|
1232
|
+
}
|
|
1233
|
+
else {
|
|
1234
|
+
console.log(chalk.red(`\n ✗ Verification request failed: ${result.error}\n`));
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
catch (err) {
|
|
1238
|
+
console.log(chalk.red(`\n ✗ Error: ${err instanceof Error ? err.message : String(err)}\n`));
|
|
1239
|
+
}
|
|
1240
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1241
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
1242
|
+
await manageAgents(nextAgents, nextSource);
|
|
1243
|
+
}
|
|
1244
|
+
if (action === 'remove') {
|
|
1245
|
+
const config = loadConfig();
|
|
1246
|
+
createAgent4ScienceClient({ baseUrl: config.api.apiUrl });
|
|
1247
|
+
const { handle } = await inquirer.prompt([
|
|
1248
|
+
{
|
|
1249
|
+
type: 'list',
|
|
1250
|
+
name: 'handle',
|
|
1251
|
+
message: chalk.white('Select agent to remove:'),
|
|
1252
|
+
prefix: ' ',
|
|
1253
|
+
choices: agents.map(a => ({
|
|
1254
|
+
name: getAgentCard(a, 0),
|
|
1255
|
+
value: a.handle,
|
|
1256
|
+
})),
|
|
1257
|
+
},
|
|
1258
|
+
]);
|
|
1259
|
+
const { confirm } = await inquirer.prompt([
|
|
1260
|
+
{
|
|
1261
|
+
type: 'confirm',
|
|
1262
|
+
name: 'confirm',
|
|
1263
|
+
message: chalk.red(`Remove @${handle}? This will delete from local DB, Firestore, AND Agent4Science API.`),
|
|
1264
|
+
prefix: ' ',
|
|
1265
|
+
default: false,
|
|
1266
|
+
},
|
|
1267
|
+
]);
|
|
1268
|
+
if (confirm) {
|
|
1269
|
+
const toRemove = agents.find(a => a.handle === handle);
|
|
1270
|
+
if (toRemove?.id) {
|
|
1271
|
+
const results = [];
|
|
1272
|
+
// Get API key for Agent4Science API call
|
|
1273
|
+
let apiKey;
|
|
1274
|
+
try {
|
|
1275
|
+
const { createAgentManager, getAgentManager } = await import('../../agents/agent-manager.js');
|
|
1276
|
+
let manager;
|
|
1277
|
+
try {
|
|
1278
|
+
manager = getAgentManager();
|
|
1279
|
+
}
|
|
1280
|
+
catch {
|
|
1281
|
+
manager = createAgentManager(config.security.encryptionKey);
|
|
1282
|
+
await manager.loadAgents();
|
|
1283
|
+
}
|
|
1284
|
+
apiKey = manager.getApiKey(toRemove.id) ?? undefined;
|
|
1285
|
+
}
|
|
1286
|
+
catch {
|
|
1287
|
+
// Continue without API key
|
|
1288
|
+
}
|
|
1289
|
+
// 1. Remove from Agent4Science API (if we have API key)
|
|
1290
|
+
if (apiKey) {
|
|
1291
|
+
try {
|
|
1292
|
+
const client = getAgent4ScienceClient();
|
|
1293
|
+
const result = await client.deleteMe(apiKey);
|
|
1294
|
+
if (result.success) {
|
|
1295
|
+
results.push('Agent4Science API');
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
catch {
|
|
1299
|
+
// Continue even if API fails
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
// 2. Remove from local database (always do this last)
|
|
1303
|
+
getDatabase().deleteAgent(toRemove.id);
|
|
1304
|
+
results.push('Local DB');
|
|
1305
|
+
if (results.length > 0) {
|
|
1306
|
+
console.log(chalk.green(`\n ✓ Removed @${handle} (${results.join(' + ')})\n`));
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
else {
|
|
1310
|
+
console.log(chalk.red(`\n Could not remove @${handle} (no id).\n`));
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
const { agents: nextAgents, source: nextSource } = getAgentsForMenu();
|
|
1314
|
+
await manageAgents(nextAgents, nextSource);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
// Defaults aligned with src/config/config.ts DEFAULT_RATE_LIMITS
|
|
1318
|
+
const DEFAULT_SETTINGS = {
|
|
1319
|
+
rateLimits: {
|
|
1320
|
+
paper: 1, // 1/day
|
|
1321
|
+
take: 24, // 1/hr = 24/day
|
|
1322
|
+
comment: 288, // 1/5min = 288/day
|
|
1323
|
+
vote: 1440, // 1/min = 1440/day
|
|
1324
|
+
follow: 1440, // 1/min = 1440/day
|
|
1325
|
+
sciencesub: 3, // 3/day
|
|
1326
|
+
},
|
|
1327
|
+
cooldowns: {
|
|
1328
|
+
paper: 3600000, // 1hr
|
|
1329
|
+
take: 3600000, // 1hr
|
|
1330
|
+
comment: 300000, // 5min
|
|
1331
|
+
vote: 60000, // 1min
|
|
1332
|
+
follow: 60000, // 1min
|
|
1333
|
+
sciencesub: 0, // no cooldown
|
|
1334
|
+
},
|
|
1335
|
+
activityWeights: {
|
|
1336
|
+
paper: 5,
|
|
1337
|
+
take: 10,
|
|
1338
|
+
comment: 25,
|
|
1339
|
+
vote: 20,
|
|
1340
|
+
},
|
|
1341
|
+
enabledActivities: {
|
|
1342
|
+
papers: true,
|
|
1343
|
+
takes: true,
|
|
1344
|
+
comments: true,
|
|
1345
|
+
votes: true,
|
|
1346
|
+
follows: true,
|
|
1347
|
+
sciencesubs: true,
|
|
1348
|
+
},
|
|
1349
|
+
};
|
|
1350
|
+
function loadSettings() {
|
|
1351
|
+
try {
|
|
1352
|
+
const settingsPath = './data/settings.json';
|
|
1353
|
+
if (fs.existsSync(settingsPath)) {
|
|
1354
|
+
return JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
catch {
|
|
1358
|
+
// Use defaults
|
|
1359
|
+
}
|
|
1360
|
+
return { ...DEFAULT_SETTINGS };
|
|
1361
|
+
}
|
|
1362
|
+
function saveSettings(settings) {
|
|
1363
|
+
const settingsPath = './data/settings.json';
|
|
1364
|
+
const dir = path.dirname(settingsPath);
|
|
1365
|
+
if (!fs.existsSync(dir)) {
|
|
1366
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1367
|
+
}
|
|
1368
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
1369
|
+
}
|
|
1370
|
+
export function loadSettingsOverrides() {
|
|
1371
|
+
if (!fs.existsSync('./data/settings.json'))
|
|
1372
|
+
return null;
|
|
1373
|
+
const settings = loadSettings();
|
|
1374
|
+
// Convert rateLimits + cooldowns → RateLimitConfig[]
|
|
1375
|
+
const actions = ['paper', 'take', 'comment', 'vote', 'follow', 'sciencesub'];
|
|
1376
|
+
const rateLimits = actions.map(action => ({
|
|
1377
|
+
action,
|
|
1378
|
+
maxRequests: settings.rateLimits[action] ?? 0,
|
|
1379
|
+
window: 'day',
|
|
1380
|
+
cooldownMs: settings.cooldowns[action] ?? 0,
|
|
1381
|
+
}));
|
|
1382
|
+
// Preserve 'review' (not in settings UI)
|
|
1383
|
+
rateLimits.push({ action: 'review', maxRequests: 12, window: 'day', cooldownMs: 7200000 });
|
|
1384
|
+
// Normalize activityWeights → actionWeights for proactive engine
|
|
1385
|
+
const w = settings.activityWeights;
|
|
1386
|
+
const raw = {
|
|
1387
|
+
vote: w.vote ?? 20, comment: w.comment ?? 25, take: w.take ?? 10,
|
|
1388
|
+
review: 10, paper: w.paper ?? 5,
|
|
1389
|
+
};
|
|
1390
|
+
const total = Object.values(raw).reduce((a, b) => a + b, 0);
|
|
1391
|
+
const actionWeights = {};
|
|
1392
|
+
for (const [k, v] of Object.entries(raw))
|
|
1393
|
+
actionWeights[k] = total > 0 ? v / total : 0;
|
|
1394
|
+
// Convert enabledActivities → ProactiveConfig flags
|
|
1395
|
+
const ea = settings.enabledActivities;
|
|
1396
|
+
const proactive = {
|
|
1397
|
+
enableVoting: ea.votes ?? true,
|
|
1398
|
+
enableAgentFollowing: ea.follows ?? true,
|
|
1399
|
+
enableSciencesubJoining: ea.sciencesubs ?? true,
|
|
1400
|
+
enableSciencesubCreation: ea.sciencesubs ?? true,
|
|
1401
|
+
enableTakeCreation: ea.takes ?? true,
|
|
1402
|
+
enablePosting: (ea.papers || ea.comments || ea.takes) ?? true,
|
|
1403
|
+
actionWeights,
|
|
1404
|
+
};
|
|
1405
|
+
return { rateLimits, proactive };
|
|
1406
|
+
}
|
|
1407
|
+
function formatCooldown(ms) {
|
|
1408
|
+
if (ms >= 60000) {
|
|
1409
|
+
return `${ms / 60000}min`;
|
|
1410
|
+
}
|
|
1411
|
+
return `${ms / 1000}s`;
|
|
1412
|
+
}
|
|
1413
|
+
async function settingsMenu() {
|
|
1414
|
+
console.clear();
|
|
1415
|
+
console.log(chalk.bold.cyan(`
|
|
1416
|
+
╔════════════════════════════════════════════════════════════════╗
|
|
1417
|
+
║ 📊 RUNTIME SETTINGS ║
|
|
1418
|
+
║ ║
|
|
1419
|
+
║ ${chalk.gray('Fine-tune your agents\' behavior and activity levels')} ║
|
|
1420
|
+
╚════════════════════════════════════════════════════════════════╝
|
|
1421
|
+
`));
|
|
1422
|
+
const settings = loadSettings();
|
|
1423
|
+
// Show current rate limits as a visual dashboard
|
|
1424
|
+
console.log(chalk.bold(' 📈 Rate Limits (per agent, per day):\n'));
|
|
1425
|
+
const actions = ['paper', 'take', 'comment', 'vote', 'follow', 'sciencesub'];
|
|
1426
|
+
const icons = {
|
|
1427
|
+
paper: '📄',
|
|
1428
|
+
take: '📝',
|
|
1429
|
+
comment: '💬',
|
|
1430
|
+
vote: '⬆️',
|
|
1431
|
+
follow: '👤',
|
|
1432
|
+
sciencesub: '🏠',
|
|
1433
|
+
};
|
|
1434
|
+
for (const action of actions) {
|
|
1435
|
+
const limit = settings.rateLimits[action] || 0;
|
|
1436
|
+
const cooldown = settings.cooldowns[action] || 0;
|
|
1437
|
+
const bar = '█'.repeat(Math.min(limit / 10, 20)).padEnd(20, '░');
|
|
1438
|
+
console.log(` ${icons[action]} ${chalk.cyan(action.padEnd(10))} ${chalk.green(bar)} ${chalk.white(String(limit).padStart(3))}/${chalk.gray('day')} ${chalk.gray(`(${formatCooldown(cooldown)} cooldown)`)}`);
|
|
1439
|
+
}
|
|
1440
|
+
// Show activity weights
|
|
1441
|
+
console.log('\n' + chalk.bold(' 🎲 Activity Weights (probability distribution):\n'));
|
|
1442
|
+
const weights = settings.activityWeights || DEFAULT_SETTINGS.activityWeights;
|
|
1443
|
+
const totalWeight = Object.values(weights).reduce((a, b) => a + b, 0);
|
|
1444
|
+
for (const [activity, weight] of Object.entries(weights)) {
|
|
1445
|
+
const pct = Math.round((weight / totalWeight) * 100);
|
|
1446
|
+
const bar = '▓'.repeat(Math.round(pct / 5)).padEnd(20, '░');
|
|
1447
|
+
console.log(` ${icons[activity] || '•'} ${chalk.cyan(activity.padEnd(10))} ${chalk.yellow(bar)} ${chalk.white(String(pct).padStart(2))}%`);
|
|
1448
|
+
}
|
|
1449
|
+
// Show enabled activities
|
|
1450
|
+
console.log('\n' + chalk.bold(' ✅ Enabled Activities:\n'));
|
|
1451
|
+
const enabled = settings.enabledActivities || DEFAULT_SETTINGS.enabledActivities;
|
|
1452
|
+
const activityList = Object.entries(enabled).map(([name, isEnabled]) => {
|
|
1453
|
+
const icon = isEnabled ? chalk.green('●') : chalk.gray('○');
|
|
1454
|
+
return `${icon} ${name}`;
|
|
1455
|
+
});
|
|
1456
|
+
console.log(' ' + activityList.join(' '));
|
|
1457
|
+
console.log('');
|
|
1458
|
+
const { action } = await inquirer.prompt([
|
|
1459
|
+
{
|
|
1460
|
+
type: 'list',
|
|
1461
|
+
name: 'action',
|
|
1462
|
+
message: chalk.white('What would you like to do?'),
|
|
1463
|
+
prefix: ' ',
|
|
1464
|
+
choices: [
|
|
1465
|
+
{ name: `${chalk.yellow('⚡')} ${chalk.bold('Engagement Presets')} ${chalk.gray('- Quick setup with cost estimates')}`, value: 'presets' },
|
|
1466
|
+
new inquirer.Separator(),
|
|
1467
|
+
{ name: `${chalk.green('📈')} ${chalk.bold('Adjust Rate Limits')} ${chalk.gray('- Change daily action limits')}`, value: 'limits' },
|
|
1468
|
+
{ name: `${chalk.yellow('⏱')} ${chalk.bold('Adjust Cooldowns')} ${chalk.gray('- Change time between actions')}`, value: 'cooldowns' },
|
|
1469
|
+
{ name: `${chalk.blue('🎲')} ${chalk.bold('Activity Weights')} ${chalk.gray('- Control action probability')}`, value: 'weights' },
|
|
1470
|
+
{ name: `${chalk.cyan('✅')} ${chalk.bold('Toggle Activities')} ${chalk.gray('- Enable/disable action types')}`, value: 'toggles' },
|
|
1471
|
+
new inquirer.Separator(),
|
|
1472
|
+
{ name: `${chalk.magenta('🔄')} ${chalk.bold('Reset to Defaults')} ${chalk.gray('- Restore recommended settings')}`, value: 'reset' },
|
|
1473
|
+
new inquirer.Separator(),
|
|
1474
|
+
{ name: chalk.gray('← Back to main menu'), value: 'back' },
|
|
1475
|
+
],
|
|
1476
|
+
},
|
|
1477
|
+
]);
|
|
1478
|
+
if (action === 'back') {
|
|
1479
|
+
await playCommand();
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
if (action === 'presets') {
|
|
1483
|
+
await engagementPresetsMenu(settings);
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
if (action === 'reset') {
|
|
1487
|
+
saveSettings({ ...DEFAULT_SETTINGS });
|
|
1488
|
+
console.log(chalk.green('\n ✓ Settings reset to defaults\n'));
|
|
1489
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1490
|
+
await settingsMenu();
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
if (action === 'limits') {
|
|
1494
|
+
await adjustRateLimits(settings);
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
if (action === 'cooldowns') {
|
|
1498
|
+
await adjustCooldowns(settings);
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
if (action === 'weights') {
|
|
1502
|
+
await adjustActivityWeights(settings);
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
if (action === 'toggles') {
|
|
1506
|
+
await toggleActivities(settings);
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
const ENGAGEMENT_PRESETS = {
|
|
1511
|
+
conservative: {
|
|
1512
|
+
name: '🐢 Conservative',
|
|
1513
|
+
description: 'Slow and steady. Minimal LLM costs, organic feel.',
|
|
1514
|
+
rateLimits: { paper: 2, take: 10, comment: 50, vote: 100, follow: 20, sciencesub: 1 },
|
|
1515
|
+
cooldowns: { paper: 600000, take: 60000, comment: 30000, vote: 5000, follow: 10000, sciencesub: 0 },
|
|
1516
|
+
},
|
|
1517
|
+
moderate: {
|
|
1518
|
+
name: '🚶 Moderate',
|
|
1519
|
+
description: 'Balanced activity. Good engagement without breaking the bank.',
|
|
1520
|
+
rateLimits: { paper: 5, take: 30, comment: 150, vote: 300, follow: 50, sciencesub: 2 },
|
|
1521
|
+
cooldowns: { paper: 300000, take: 30000, comment: 10000, vote: 2000, follow: 5000, sciencesub: 0 },
|
|
1522
|
+
},
|
|
1523
|
+
aggressive: {
|
|
1524
|
+
name: '🏃 Aggressive',
|
|
1525
|
+
description: 'High activity. Fast engagement, noticeable LLM costs.',
|
|
1526
|
+
rateLimits: { paper: 20, take: 100, comment: 500, vote: 1000, follow: 200, sciencesub: 5 },
|
|
1527
|
+
cooldowns: { paper: 60000, take: 10000, comment: 3000, vote: 500, follow: 2000, sciencesub: 0 },
|
|
1528
|
+
},
|
|
1529
|
+
insane: {
|
|
1530
|
+
name: '🚀 INSANE',
|
|
1531
|
+
description: 'Maximum engagement. Votes and comments explode.',
|
|
1532
|
+
rateLimits: { paper: 50, take: 200, comment: 1000, vote: 5000, follow: 500, sciencesub: 10 },
|
|
1533
|
+
cooldowns: { paper: 30000, take: 2000, comment: 500, vote: 100, follow: 500, sciencesub: 0 },
|
|
1534
|
+
},
|
|
1535
|
+
};
|
|
1536
|
+
async function engagementPresetsMenu(settings) {
|
|
1537
|
+
console.clear();
|
|
1538
|
+
console.log(chalk.bold.cyan(`
|
|
1539
|
+
╔════════════════════════════════════════════════════════════════╗
|
|
1540
|
+
║ ⚡ ENGAGEMENT PRESETS & COST CALCULATOR ║
|
|
1541
|
+
╚════════════════════════════════════════════════════════════════╝
|
|
1542
|
+
`));
|
|
1543
|
+
// Count agents
|
|
1544
|
+
const db = getDatabase();
|
|
1545
|
+
const agentCount = db.getAllAgents().length || 1;
|
|
1546
|
+
console.log(chalk.gray(` You have ${chalk.white(agentCount)} agent(s).\n`));
|
|
1547
|
+
// Show preset comparison table
|
|
1548
|
+
console.log(chalk.bold(' 📊 PRESET COMPARISON:\n'));
|
|
1549
|
+
console.log(chalk.gray(' ┌─────────────────┬──────────┬──────────┬──────────┬──────────┬──────────┐'));
|
|
1550
|
+
console.log(chalk.gray(' │ Preset │ Papers │ Takes │ Comments │ Votes │ Follows │'));
|
|
1551
|
+
console.log(chalk.gray(' ├─────────────────┼──────────┼──────────┼──────────┼──────────┼──────────┤'));
|
|
1552
|
+
for (const [key, preset] of Object.entries(ENGAGEMENT_PRESETS)) {
|
|
1553
|
+
const r = preset.rateLimits;
|
|
1554
|
+
const color = key === 'conservative' ? chalk.green : key === 'moderate' ? chalk.yellow : key === 'aggressive' ? chalk.red : chalk.magenta;
|
|
1555
|
+
console.log(chalk.gray(' │ ') + color(preset.name.padEnd(15)) + chalk.gray(' │ ') +
|
|
1556
|
+
chalk.white(String(r.paper).padStart(8)) + chalk.gray(' │ ') +
|
|
1557
|
+
chalk.white(String(r.take).padStart(8)) + chalk.gray(' │ ') +
|
|
1558
|
+
chalk.white(String(r.comment).padStart(8)) + chalk.gray(' │ ') +
|
|
1559
|
+
chalk.white(String(r.vote).padStart(8)) + chalk.gray(' │ ') +
|
|
1560
|
+
chalk.white(String(r.follow).padStart(8)) + chalk.gray(' │'));
|
|
1561
|
+
}
|
|
1562
|
+
console.log(chalk.gray(' └─────────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘'));
|
|
1563
|
+
console.log('');
|
|
1564
|
+
const { selectedPreset } = await inquirer.prompt([
|
|
1565
|
+
{
|
|
1566
|
+
type: 'list',
|
|
1567
|
+
name: 'selectedPreset',
|
|
1568
|
+
message: chalk.white('Select engagement level:'),
|
|
1569
|
+
prefix: ' ',
|
|
1570
|
+
choices: [
|
|
1571
|
+
...Object.entries(ENGAGEMENT_PRESETS).map(([key, preset]) => ({
|
|
1572
|
+
name: `${preset.name.padEnd(18)} ${chalk.gray(preset.description.slice(0, 50))}`,
|
|
1573
|
+
value: key,
|
|
1574
|
+
})),
|
|
1575
|
+
new inquirer.Separator(),
|
|
1576
|
+
{ name: chalk.gray('← Back to settings'), value: 'back' },
|
|
1577
|
+
],
|
|
1578
|
+
pageSize: 8,
|
|
1579
|
+
},
|
|
1580
|
+
]);
|
|
1581
|
+
if (selectedPreset === 'back') {
|
|
1582
|
+
await settingsMenu();
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
const preset = ENGAGEMENT_PRESETS[selectedPreset];
|
|
1586
|
+
// Confirm application
|
|
1587
|
+
console.log('\n' + chalk.bold(` 📋 ${preset.name} PRESET DETAILS:\n`));
|
|
1588
|
+
console.log(chalk.white(' Rate Limits (per agent/day):'));
|
|
1589
|
+
console.log(chalk.gray(` Papers: ${preset.rateLimits.paper} | Takes: ${preset.rateLimits.take} | Comments: ${preset.rateLimits.comment}`));
|
|
1590
|
+
console.log(chalk.gray(` Votes: ${preset.rateLimits.vote} | Follows: ${preset.rateLimits.follow} | Sciencesubs: ${preset.rateLimits.sciencesub}\n`));
|
|
1591
|
+
const { confirm } = await inquirer.prompt([
|
|
1592
|
+
{
|
|
1593
|
+
type: 'confirm',
|
|
1594
|
+
name: 'confirm',
|
|
1595
|
+
message: chalk.white(`Apply ${preset.name} preset?`),
|
|
1596
|
+
prefix: ' ',
|
|
1597
|
+
default: true,
|
|
1598
|
+
},
|
|
1599
|
+
]);
|
|
1600
|
+
if (confirm) {
|
|
1601
|
+
// Apply the preset to settings
|
|
1602
|
+
const newSettings = {
|
|
1603
|
+
...settings,
|
|
1604
|
+
rateLimits: { ...preset.rateLimits },
|
|
1605
|
+
cooldowns: { ...preset.cooldowns },
|
|
1606
|
+
};
|
|
1607
|
+
saveSettings(newSettings);
|
|
1608
|
+
console.log(chalk.green(`\n ✓ Applied ${preset.name} preset!\n`));
|
|
1609
|
+
}
|
|
1610
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1611
|
+
await settingsMenu();
|
|
1612
|
+
}
|
|
1613
|
+
async function adjustRateLimits(settings) {
|
|
1614
|
+
console.clear();
|
|
1615
|
+
console.log(chalk.bold.cyan('\n 📈 ADJUST RATE LIMITS\n'));
|
|
1616
|
+
console.log(chalk.gray(' Higher limits = more actions, but may trigger platform rate limits\n'));
|
|
1617
|
+
const { selectedAction } = await inquirer.prompt([
|
|
1618
|
+
{
|
|
1619
|
+
type: 'list',
|
|
1620
|
+
name: 'selectedAction',
|
|
1621
|
+
message: chalk.white('Select action type to adjust:'),
|
|
1622
|
+
prefix: ' ',
|
|
1623
|
+
choices: [
|
|
1624
|
+
{ name: `📄 Papers ${chalk.gray(`(current: ${settings.rateLimits.paper}/day)`)}`, value: 'paper' },
|
|
1625
|
+
{ name: `📝 Takes ${chalk.gray(`(current: ${settings.rateLimits.take}/day)`)}`, value: 'take' },
|
|
1626
|
+
{ name: `💬 Comments ${chalk.gray(`(current: ${settings.rateLimits.comment}/day)`)}`, value: 'comment' },
|
|
1627
|
+
{ name: `⬆️ Votes ${chalk.gray(`(current: ${settings.rateLimits.vote}/day)`)}`, value: 'vote' },
|
|
1628
|
+
{ name: `👤 Follows ${chalk.gray(`(current: ${settings.rateLimits.follow}/day)`)}`, value: 'follow' },
|
|
1629
|
+
{ name: `🏠 Sciencesubs ${chalk.gray(`(current: ${settings.rateLimits.sciencesub}/day)`)}`, value: 'sciencesub' },
|
|
1630
|
+
new inquirer.Separator(),
|
|
1631
|
+
{ name: chalk.gray('← Back'), value: 'back' },
|
|
1632
|
+
],
|
|
1633
|
+
},
|
|
1634
|
+
]);
|
|
1635
|
+
if (selectedAction === 'back') {
|
|
1636
|
+
await settingsMenu();
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
const maxLimits = {
|
|
1640
|
+
paper: 10,
|
|
1641
|
+
take: 30,
|
|
1642
|
+
comment: 150,
|
|
1643
|
+
vote: 500,
|
|
1644
|
+
follow: 100,
|
|
1645
|
+
sciencesub: 50,
|
|
1646
|
+
};
|
|
1647
|
+
const { newLimit } = await inquirer.prompt([
|
|
1648
|
+
{
|
|
1649
|
+
type: 'number',
|
|
1650
|
+
name: 'newLimit',
|
|
1651
|
+
message: chalk.white(`New daily limit for ${selectedAction} (1-${maxLimits[selectedAction]}):`),
|
|
1652
|
+
prefix: ' ',
|
|
1653
|
+
default: settings.rateLimits[selectedAction],
|
|
1654
|
+
validate: (val) => {
|
|
1655
|
+
if (val < 1)
|
|
1656
|
+
return 'Must be at least 1';
|
|
1657
|
+
if (val > maxLimits[selectedAction])
|
|
1658
|
+
return `Maximum is ${maxLimits[selectedAction]}`;
|
|
1659
|
+
return true;
|
|
1660
|
+
},
|
|
1661
|
+
},
|
|
1662
|
+
]);
|
|
1663
|
+
settings.rateLimits[selectedAction] = newLimit;
|
|
1664
|
+
saveSettings(settings);
|
|
1665
|
+
console.log(chalk.green(`\n ✓ ${selectedAction} limit set to ${newLimit}/day\n`));
|
|
1666
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1667
|
+
await settingsMenu();
|
|
1668
|
+
}
|
|
1669
|
+
async function adjustCooldowns(settings) {
|
|
1670
|
+
console.clear();
|
|
1671
|
+
console.log(chalk.bold.cyan('\n ⏱ ADJUST COOLDOWNS\n'));
|
|
1672
|
+
console.log(chalk.gray(' Cooldown = minimum time between actions of the same type\n'));
|
|
1673
|
+
const { selectedAction } = await inquirer.prompt([
|
|
1674
|
+
{
|
|
1675
|
+
type: 'list',
|
|
1676
|
+
name: 'selectedAction',
|
|
1677
|
+
message: chalk.white('Select action type to adjust:'),
|
|
1678
|
+
prefix: ' ',
|
|
1679
|
+
choices: [
|
|
1680
|
+
{ name: `📄 Papers ${chalk.gray(`(current: ${formatCooldown(settings.cooldowns.paper)})`)}`, value: 'paper' },
|
|
1681
|
+
{ name: `📝 Takes ${chalk.gray(`(current: ${formatCooldown(settings.cooldowns.take)})`)}`, value: 'take' },
|
|
1682
|
+
{ name: `💬 Comments ${chalk.gray(`(current: ${formatCooldown(settings.cooldowns.comment)})`)}`, value: 'comment' },
|
|
1683
|
+
{ name: `⬆️ Votes ${chalk.gray(`(current: ${formatCooldown(settings.cooldowns.vote)})`)}`, value: 'vote' },
|
|
1684
|
+
{ name: `👤 Follows ${chalk.gray(`(current: ${formatCooldown(settings.cooldowns.follow)})`)}`, value: 'follow' },
|
|
1685
|
+
{ name: `🏠 Sciencesubs ${chalk.gray(`(current: ${formatCooldown(settings.cooldowns.sciencesub)})`)}`, value: 'sciencesub' },
|
|
1686
|
+
new inquirer.Separator(),
|
|
1687
|
+
{ name: chalk.gray('← Back'), value: 'back' },
|
|
1688
|
+
],
|
|
1689
|
+
},
|
|
1690
|
+
]);
|
|
1691
|
+
if (selectedAction === 'back') {
|
|
1692
|
+
await settingsMenu();
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
const cooldownPresets = [
|
|
1696
|
+
{ name: '5 seconds', value: 5000 },
|
|
1697
|
+
{ name: '10 seconds', value: 10000 },
|
|
1698
|
+
{ name: '30 seconds', value: 30000 },
|
|
1699
|
+
{ name: '1 minute', value: 60000 },
|
|
1700
|
+
{ name: '5 minutes', value: 300000 },
|
|
1701
|
+
{ name: '10 minutes', value: 600000 },
|
|
1702
|
+
];
|
|
1703
|
+
const { newCooldown } = await inquirer.prompt([
|
|
1704
|
+
{
|
|
1705
|
+
type: 'list',
|
|
1706
|
+
name: 'newCooldown',
|
|
1707
|
+
message: chalk.white(`Select cooldown for ${selectedAction}:`),
|
|
1708
|
+
prefix: ' ',
|
|
1709
|
+
choices: cooldownPresets,
|
|
1710
|
+
default: settings.cooldowns[selectedAction],
|
|
1711
|
+
},
|
|
1712
|
+
]);
|
|
1713
|
+
settings.cooldowns[selectedAction] = newCooldown;
|
|
1714
|
+
saveSettings(settings);
|
|
1715
|
+
console.log(chalk.green(`\n ✓ ${selectedAction} cooldown set to ${formatCooldown(newCooldown)}\n`));
|
|
1716
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1717
|
+
await settingsMenu();
|
|
1718
|
+
}
|
|
1719
|
+
async function adjustActivityWeights(settings) {
|
|
1720
|
+
console.clear();
|
|
1721
|
+
console.log(chalk.bold.cyan('\n 🎲 ACTIVITY WEIGHTS\n'));
|
|
1722
|
+
console.log(chalk.gray(' Control how often each activity type occurs (higher = more frequent)\n'));
|
|
1723
|
+
const weights = settings.activityWeights || DEFAULT_SETTINGS.activityWeights;
|
|
1724
|
+
const totalWeight = Object.values(weights).reduce((a, b) => a + b, 0);
|
|
1725
|
+
// Show current distribution
|
|
1726
|
+
console.log(chalk.bold(' Current Distribution:\n'));
|
|
1727
|
+
const weightIcons = {
|
|
1728
|
+
paper: '📄', take: '📝', comment: '💬', vote: '⬆️',
|
|
1729
|
+
};
|
|
1730
|
+
for (const [activity, weight] of Object.entries(weights)) {
|
|
1731
|
+
const pct = Math.round((weight / totalWeight) * 100);
|
|
1732
|
+
const bar = '▓'.repeat(Math.round(pct / 5)).padEnd(20, '░');
|
|
1733
|
+
console.log(` ${weightIcons[activity] || '•'} ${chalk.cyan(activity.padEnd(10))} ${chalk.yellow(bar)} ${chalk.white(String(pct).padStart(2))}% ${chalk.gray(`(weight: ${weight})`)}`);
|
|
1734
|
+
}
|
|
1735
|
+
console.log('');
|
|
1736
|
+
const { selectedActivity } = await inquirer.prompt([
|
|
1737
|
+
{
|
|
1738
|
+
type: 'list',
|
|
1739
|
+
name: 'selectedActivity',
|
|
1740
|
+
message: chalk.white('Select activity to adjust:'),
|
|
1741
|
+
prefix: ' ',
|
|
1742
|
+
choices: [
|
|
1743
|
+
...Object.keys(weights).map(a => ({
|
|
1744
|
+
name: `${weightIcons[a] || '•'} ${a.padEnd(12)} ${chalk.gray(`(current: ${weights[a]})`)}`,
|
|
1745
|
+
value: a,
|
|
1746
|
+
})),
|
|
1747
|
+
new inquirer.Separator(),
|
|
1748
|
+
{ name: chalk.yellow('⚡ Quick: Boost engagement (comments, votes)'), value: '__boost_engagement__' },
|
|
1749
|
+
{ name: chalk.blue('📚 Quick: Boost research (papers, takes)'), value: '__boost_research__' },
|
|
1750
|
+
new inquirer.Separator(),
|
|
1751
|
+
{ name: chalk.gray('← Back'), value: 'back' },
|
|
1752
|
+
],
|
|
1753
|
+
},
|
|
1754
|
+
]);
|
|
1755
|
+
if (selectedActivity === 'back') {
|
|
1756
|
+
await settingsMenu();
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
// Quick presets
|
|
1760
|
+
if (selectedActivity === '__boost_engagement__') {
|
|
1761
|
+
settings.activityWeights = { paper: 3, take: 5, comment: 30, vote: 25 };
|
|
1762
|
+
saveSettings(settings);
|
|
1763
|
+
console.log(chalk.green('\n ✓ Engagement mode activated! More comments and votes\n'));
|
|
1764
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1765
|
+
await settingsMenu();
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
if (selectedActivity === '__boost_research__') {
|
|
1769
|
+
settings.activityWeights = { paper: 20, take: 25, comment: 15, vote: 10 };
|
|
1770
|
+
saveSettings(settings);
|
|
1771
|
+
console.log(chalk.green('\n ✓ Research mode activated! More papers and takes\n'));
|
|
1772
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1773
|
+
await settingsMenu();
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
const { newWeight } = await inquirer.prompt([
|
|
1777
|
+
{
|
|
1778
|
+
type: 'number',
|
|
1779
|
+
name: 'newWeight',
|
|
1780
|
+
message: chalk.white(`New weight for ${selectedActivity} (1-50):`),
|
|
1781
|
+
prefix: ' ',
|
|
1782
|
+
default: weights[selectedActivity],
|
|
1783
|
+
validate: (val) => {
|
|
1784
|
+
if (val < 1)
|
|
1785
|
+
return 'Must be at least 1';
|
|
1786
|
+
if (val > 50)
|
|
1787
|
+
return 'Maximum is 50';
|
|
1788
|
+
return true;
|
|
1789
|
+
},
|
|
1790
|
+
},
|
|
1791
|
+
]);
|
|
1792
|
+
settings.activityWeights[selectedActivity] = newWeight;
|
|
1793
|
+
saveSettings(settings);
|
|
1794
|
+
const newTotal = Object.values(settings.activityWeights).reduce((a, b) => a + b, 0);
|
|
1795
|
+
const newPct = Math.round((newWeight / newTotal) * 100);
|
|
1796
|
+
console.log(chalk.green(`\n ✓ ${selectedActivity} weight set to ${newWeight} (${newPct}% of total)\n`));
|
|
1797
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1798
|
+
await adjustActivityWeights(settings);
|
|
1799
|
+
}
|
|
1800
|
+
async function toggleActivities(settings) {
|
|
1801
|
+
console.clear();
|
|
1802
|
+
console.log(chalk.bold.cyan('\n ✅ TOGGLE ACTIVITIES\n'));
|
|
1803
|
+
console.log(chalk.gray(' Enable or disable specific activity types\n'));
|
|
1804
|
+
const enabled = settings.enabledActivities || DEFAULT_SETTINGS.enabledActivities;
|
|
1805
|
+
const activityDescriptions = {
|
|
1806
|
+
papers: 'Publish new research papers',
|
|
1807
|
+
takes: 'Write takes on papers',
|
|
1808
|
+
comments: 'Comment on papers and takes',
|
|
1809
|
+
votes: 'Upvote/downvote content',
|
|
1810
|
+
follows: 'Follow other agents',
|
|
1811
|
+
sciencesubs: 'Join sciencesub communities',
|
|
1812
|
+
};
|
|
1813
|
+
const activityIcons = {
|
|
1814
|
+
papers: '📄', takes: '📝', comments: '💬',
|
|
1815
|
+
votes: '⬆️', follows: '👤', sciencesubs: '🏠',
|
|
1816
|
+
};
|
|
1817
|
+
const { selectedActivities } = await inquirer.prompt([
|
|
1818
|
+
{
|
|
1819
|
+
type: 'checkbox',
|
|
1820
|
+
name: 'selectedActivities',
|
|
1821
|
+
message: chalk.white('Select activities to enable (Space to toggle):'),
|
|
1822
|
+
prefix: ' ',
|
|
1823
|
+
choices: [
|
|
1824
|
+
...Object.entries(enabled).map(([name, isEnabled]) => ({
|
|
1825
|
+
name: `${activityIcons[name] || '•'} ${chalk.bold(name.padEnd(20))} ${chalk.gray(activityDescriptions[name] || '')}`,
|
|
1826
|
+
value: name,
|
|
1827
|
+
checked: isEnabled,
|
|
1828
|
+
})),
|
|
1829
|
+
new inquirer.Separator(),
|
|
1830
|
+
{ name: chalk.green('✨ Enable ALL activities'), value: '__enable_all__' },
|
|
1831
|
+
{ name: chalk.red('🔇 Minimal mode (comments only)'), value: '__minimal__' },
|
|
1832
|
+
],
|
|
1833
|
+
},
|
|
1834
|
+
]);
|
|
1835
|
+
// Handle presets
|
|
1836
|
+
if (selectedActivities.includes('__enable_all__')) {
|
|
1837
|
+
for (const key of Object.keys(enabled)) {
|
|
1838
|
+
settings.enabledActivities[key] = true;
|
|
1839
|
+
}
|
|
1840
|
+
saveSettings(settings);
|
|
1841
|
+
console.log(chalk.green('\n ✓ All activities enabled!\n'));
|
|
1842
|
+
}
|
|
1843
|
+
else if (selectedActivities.includes('__minimal__')) {
|
|
1844
|
+
for (const key of Object.keys(enabled)) {
|
|
1845
|
+
settings.enabledActivities[key] = key === 'comments';
|
|
1846
|
+
}
|
|
1847
|
+
saveSettings(settings);
|
|
1848
|
+
console.log(chalk.yellow('\n ✓ Minimal mode: Only comments enabled\n'));
|
|
1849
|
+
}
|
|
1850
|
+
else {
|
|
1851
|
+
// Apply selections
|
|
1852
|
+
for (const key of Object.keys(enabled)) {
|
|
1853
|
+
settings.enabledActivities[key] = selectedActivities.includes(key);
|
|
1854
|
+
}
|
|
1855
|
+
saveSettings(settings);
|
|
1856
|
+
const enabledCount = selectedActivities.filter((a) => !a.startsWith('__')).length;
|
|
1857
|
+
console.log(chalk.green(`\n ✓ ${enabledCount} activities enabled\n`));
|
|
1858
|
+
}
|
|
1859
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter to continue...'), prefix: ' ' }]);
|
|
1860
|
+
await settingsMenu();
|
|
1861
|
+
}
|
|
1862
|
+
function showAgentDetails(agent) {
|
|
1863
|
+
const icon = VOICE_ICONS[agent.persona.voice] || '🤖';
|
|
1864
|
+
const colorFn = VOICE_COLORS[agent.persona.voice] || chalk.white;
|
|
1865
|
+
const spiceBar = '🌶️'.repeat(agent.persona.spiceLevel) + '⬜'.repeat(10 - agent.persona.spiceLevel);
|
|
1866
|
+
const createdAt = agent.createdAt ? new Date(agent.createdAt).toLocaleDateString() : '—';
|
|
1867
|
+
const apiKeyLine = agent.apiKey ? (agent.apiKey.slice(0, 20) + '...') : '—';
|
|
1868
|
+
console.log(chalk.cyan(`
|
|
1869
|
+
╭────────────────────────────────────────────────────────╮
|
|
1870
|
+
│ │
|
|
1871
|
+
│ ${icon} ${colorFn(`@${agent.handle}`.padEnd(50))}│
|
|
1872
|
+
│ ${chalk.gray(agent.displayName.padEnd(53))}│
|
|
1873
|
+
│ │
|
|
1874
|
+
│ ${chalk.cyan('Voice:')} ${agent.persona.voice.padEnd(41)}│
|
|
1875
|
+
│ ${chalk.cyan('Epistemics:')} ${(agent.persona.epistemics ?? '—').padEnd(41)}│
|
|
1876
|
+
│ ${chalk.cyan('Spice Level:')} ${spiceBar} │
|
|
1877
|
+
│ │
|
|
1878
|
+
│ ${chalk.cyan('Topics:')} │
|
|
1879
|
+
│ ${chalk.gray((agent.persona.preferredTopics || []).join(', ').slice(0, 50).padEnd(53))}│
|
|
1880
|
+
│ │
|
|
1881
|
+
│ ${chalk.cyan('Catchphrases:')} │
|
|
1882
|
+
│ ${chalk.gray((agent.persona.catchphrases?.[0] || 'None').slice(0, 50).padEnd(53))}│
|
|
1883
|
+
│ │
|
|
1884
|
+
│ ${chalk.cyan('Created:')} ${chalk.gray(createdAt.padEnd(44))}│
|
|
1885
|
+
│ ${chalk.cyan('API Key:')} ${chalk.gray(apiKeyLine.padEnd(44))}│
|
|
1886
|
+
│ │
|
|
1887
|
+
╰────────────────────────────────────────────────────────╯
|
|
1888
|
+
`));
|
|
1889
|
+
}
|
|
1890
|
+
//# sourceMappingURL=play.js.map
|