@zhive/cli 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CLAUDE.md +7 -0
- package/dist/backtest/CLAUDE.md +7 -0
- package/dist/cli.js +20 -0
- package/dist/commands/megathread/commands/create-comment.js +8 -33
- package/dist/commands/megathread/commands/create-comment.test.js +91 -56
- package/dist/commands/megathread/commands/list.js +30 -34
- package/dist/commands/megathread/commands/list.test.js +3 -3
- package/dist/commands/shared/ validation.js +5 -0
- package/dist/{agent → services/agent}/analysis.js +5 -5
- package/dist/{load-agent-env.js → services/agent/env.js} +1 -1
- package/dist/{agent → services/agent/helpers}/model.js +2 -2
- package/dist/{agent → services/agent/prompts}/memory-prompt.js +20 -22
- package/dist/{agent → services/agent/prompts}/prompt.js +80 -54
- package/dist/{agent → services/agent}/tools/market/client.js +1 -1
- package/dist/{agent → services/agent}/tools/mindshare/client.js +1 -1
- package/dist/{agents.js → services/config/agent.js} +2 -2
- package/dist/{config.js → services/config/config.js} +1 -7
- package/dist/services/config/constant.js +8 -0
- package/dist/shared/agent/config.js +75 -0
- package/dist/shared/agent/env.js +30 -0
- package/dist/shared/agent/helpers/model.js +92 -0
- package/dist/shared/ai-providers.js +66 -0
- package/dist/shared/config/constant.js +10 -6
- package/package.json +2 -2
- package/dist/agent/app.js +0 -122
- package/dist/agent/commands/registry.js +0 -12
- package/dist/agent/components/AsciiTicker.js +0 -81
- package/dist/agent/components/CommandInput.js +0 -65
- package/dist/agent/components/HoneycombBoot.js +0 -291
- package/dist/agent/components/Spinner.js +0 -37
- package/dist/agent/hooks/useAgent.js +0 -480
- package/dist/agent/objects.js +0 -1
- package/dist/agent/process-lifecycle.js +0 -18
- package/dist/agent/run-headless.js +0 -189
- package/dist/agent/theme.js +0 -41
- package/dist/avatar.js +0 -34
- package/dist/backtest/default-backtest-data.js +0 -200
- package/dist/backtest/fetch.js +0 -41
- package/dist/backtest/import.js +0 -106
- package/dist/backtest/index.js +0 -10
- package/dist/backtest/results.js +0 -113
- package/dist/backtest/runner.js +0 -134
- package/dist/backtest/storage.js +0 -11
- package/dist/backtest/types.js +0 -1
- package/dist/commands/install.js +0 -50
- package/dist/commands/start/ui/PollText.js +0 -23
- package/dist/commands/start/ui/PredictionsPanel.js +0 -88
- package/dist/commands/start/ui/SpinnerContext.js +0 -20
- package/dist/components/InputGuard.js +0 -6
- package/dist/components/stdout-spinner.js +0 -48
- package/dist/create/CreateApp.js +0 -153
- package/dist/create/ai-generate.js +0 -147
- package/dist/create/generate.js +0 -73
- package/dist/create/steps/ApiKeyStep.js +0 -97
- package/dist/create/steps/AvatarStep.js +0 -16
- package/dist/create/steps/BioStep.js +0 -14
- package/dist/create/steps/DoneStep.js +0 -14
- package/dist/create/steps/IdentityStep.js +0 -163
- package/dist/create/steps/NameStep.js +0 -71
- package/dist/create/steps/ScaffoldStep.js +0 -58
- package/dist/create/steps/SoulStep.js +0 -58
- package/dist/create/steps/StrategyStep.js +0 -58
- package/dist/create/validate-api-key.js +0 -47
- package/dist/create/welcome.js +0 -304
- package/dist/list/ListApp.js +0 -79
- package/dist/migrate-templates/MigrateApp.js +0 -131
- package/dist/migrate-templates/migrate.js +0 -86
- package/dist/presets.js +0 -613
- package/dist/start/AgentProcessManager.js +0 -98
- package/dist/start/Dashboard.js +0 -92
- package/dist/start/SelectAgentApp.js +0 -81
- package/dist/start/StartApp.js +0 -189
- package/dist/start/patch-headless.js +0 -101
- package/dist/start/patch-managed-mode.js +0 -142
- package/dist/start/start-command.js +0 -24
- package/dist/theme.js +0 -54
- /package/dist/{agent → services/agent}/config.js +0 -0
- /package/dist/{agent → services/agent}/helpers.js +0 -0
- /package/dist/{agent → services/agent/prompts}/chat-prompt.js +0 -0
- /package/dist/{agent → services/agent}/skills/index.js +0 -0
- /package/dist/{agent → services/agent}/skills/skill-parser.js +0 -0
- /package/dist/{agent → services/agent}/skills/types.js +0 -0
- /package/dist/{agent → services/agent/tools}/edit-section.js +0 -0
- /package/dist/{agent → services/agent/tools}/fetch-rules.js +0 -0
- /package/dist/{agent → services/agent}/tools/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/market/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/market/tools.js +0 -0
- /package/dist/{agent → services/agent}/tools/mindshare/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/mindshare/tools.js +0 -0
- /package/dist/{agent → services/agent}/tools/read-skill-tool.js +0 -0
- /package/dist/{agent → services/agent}/tools/ta/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/ta/indicators.js +0 -0
- /package/dist/{agent → services/agent}/types.js +0 -0
- /package/dist/{ai-providers.js → services/ai-providers.js} +0 -0
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { streamText } from 'ai';
|
|
2
|
-
import { createOpenAI } from '@ai-sdk/openai';
|
|
3
|
-
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
4
|
-
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
5
|
-
import { createXai } from '@ai-sdk/xai';
|
|
6
|
-
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
|
7
|
-
import { getProvider } from '../ai-providers.js';
|
|
8
|
-
import { SOUL_PRESETS, STRATEGY_PRESETS, buildSoulMarkdown, buildStrategyMarkdown, } from '../presets.js';
|
|
9
|
-
function buildModel(providerId, apiKey) {
|
|
10
|
-
const provider = getProvider(providerId);
|
|
11
|
-
const modelId = provider.models.generation;
|
|
12
|
-
switch (providerId) {
|
|
13
|
-
case 'openai':
|
|
14
|
-
return createOpenAI({ apiKey })(modelId);
|
|
15
|
-
case 'anthropic':
|
|
16
|
-
return createAnthropic({ apiKey })(modelId);
|
|
17
|
-
case 'google':
|
|
18
|
-
return createGoogleGenerativeAI({ apiKey })(modelId);
|
|
19
|
-
case 'xai':
|
|
20
|
-
return createXai({ apiKey })(modelId);
|
|
21
|
-
case 'openrouter':
|
|
22
|
-
return createOpenRouter({ apiKey }).chat(modelId);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
function buildPresetExamples() {
|
|
26
|
-
const soulExamples = SOUL_PRESETS.map((p) => buildSoulMarkdown('ExampleAgent', 'example bio text for reference', p, '')).join('\n---\n');
|
|
27
|
-
const strategyExamples = STRATEGY_PRESETS.map((p) => buildStrategyMarkdown('ExampleAgent', p)).join('\n---\n');
|
|
28
|
-
return `${soulExamples}\n\n===\n\n${strategyExamples}`;
|
|
29
|
-
}
|
|
30
|
-
const PRESET_EXAMPLES = buildPresetExamples();
|
|
31
|
-
export function streamSoul(providerId, apiKey, agentName, bio, avatarUrl, personality, tone, voiceStyle, tradingStyle, sectors, sentiment, timeframes, feedback) {
|
|
32
|
-
const feedbackLine = feedback
|
|
33
|
-
? `\n\nThe user gave this feedback on the previous draft. Adjust accordingly:\n"${feedback}"`
|
|
34
|
-
: '';
|
|
35
|
-
const sectorsLine = sectors.length > 0 ? sectors.join(', ') : 'all categories';
|
|
36
|
-
const timeframesLine = timeframes.length > 0 ? timeframes.join(', ') : 'all timeframes';
|
|
37
|
-
const identityContext = `Personality: ${personality}
|
|
38
|
-
Tone: ${tone}
|
|
39
|
-
Voice style: ${voiceStyle}
|
|
40
|
-
Trading style: ${tradingStyle}
|
|
41
|
-
Sectors: ${sectorsLine}
|
|
42
|
-
Sentiment: ${sentiment}
|
|
43
|
-
Preferred timeframes: ${timeframesLine}`;
|
|
44
|
-
const prompt = `You are a creative writer designing an AI agent's personality profile for a crypto trading bot called "${agentName}".
|
|
45
|
-
|
|
46
|
-
The agent's bio is: "${bio}"
|
|
47
|
-
${avatarUrl ? `Avatar URL: ${avatarUrl}` : 'No avatar URL provided.'}
|
|
48
|
-
|
|
49
|
-
Identity traits:
|
|
50
|
-
${identityContext}
|
|
51
|
-
|
|
52
|
-
Context — Hive is a prediction game for AI agents:
|
|
53
|
-
- Signals appear in cells (e.g. c/ethereum, c/bitcoin) when noteworthy crypto events happen.
|
|
54
|
-
- Agents submit a percentage prediction (predicted price change over 3 hours) and a short reasoning.
|
|
55
|
-
- Threads resolve at T+3h. Correct-direction predictions earn honey (the ranking currency); wrong-direction predictions earn wax.
|
|
56
|
-
- Early predictions are worth dramatically more than late ones (steep time bonus decay).
|
|
57
|
-
- Streaks track consecutive correct-direction predictions. Skipping a thread carries no penalty and does not break streaks.
|
|
58
|
-
- Agents are ranked on a leaderboard by total honey.
|
|
59
|
-
|
|
60
|
-
Generate a SOUL.md file for this agent. The SOUL.md defines who the agent IS — their personality, voice, quirks, opinions, and how they post. The personality should be aware that the agent operates in this prediction game — their voice should reflect how they approach predictions, risk, and competition. Use the identity traits above to shape the personality, tone, and writing style.
|
|
61
|
-
|
|
62
|
-
CRITICAL: Output ONLY valid markdown matching this exact structure. No extra commentary.
|
|
63
|
-
|
|
64
|
-
The first line MUST be: # Agent: ${agentName}
|
|
65
|
-
|
|
66
|
-
Here are reference examples of well-crafted SOUL.md files:
|
|
67
|
-
---
|
|
68
|
-
${PRESET_EXAMPLES.split('===')[0]}
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
Use these as style/quality references but create something UNIQUE based on the agent's name, bio, and identity traits.${feedbackLine}`;
|
|
72
|
-
const model = buildModel(providerId, apiKey);
|
|
73
|
-
const result = streamText({
|
|
74
|
-
model,
|
|
75
|
-
prompt,
|
|
76
|
-
maxOutputTokens: 1500,
|
|
77
|
-
});
|
|
78
|
-
return result.textStream;
|
|
79
|
-
}
|
|
80
|
-
export function streamStrategy(providerId, apiKey, agentName, bio, personality, tone, voiceStyle, tradingStyle, sectors, sentiment, timeframes, feedback) {
|
|
81
|
-
const feedbackLine = feedback
|
|
82
|
-
? `\n\nThe user gave this feedback on the previous draft. Adjust accordingly:\n"${feedback}"`
|
|
83
|
-
: '';
|
|
84
|
-
const sectorsLine = sectors.length > 0 ? sectors.join(', ') : 'all categories';
|
|
85
|
-
const timeframesLine = timeframes.length > 0 ? timeframes.join(', ') : 'all timeframes';
|
|
86
|
-
const identityContext = `Personality: ${personality}
|
|
87
|
-
Tone: ${tone}
|
|
88
|
-
Voice style: ${voiceStyle}
|
|
89
|
-
Trading style: ${tradingStyle}
|
|
90
|
-
Sectors: ${sectorsLine}
|
|
91
|
-
Sentiment: ${sentiment}
|
|
92
|
-
Preferred timeframes: ${timeframesLine}`;
|
|
93
|
-
const prompt = `You are designing a prediction strategy profile for a crypto trading bot called "${agentName}".
|
|
94
|
-
|
|
95
|
-
The agent's bio is: "${bio}"
|
|
96
|
-
|
|
97
|
-
Identity traits:
|
|
98
|
-
${identityContext}
|
|
99
|
-
|
|
100
|
-
Context — Hive game mechanics that the strategy should account for:
|
|
101
|
-
- Agents predict the percentage price change of a crypto asset over a 3-hour window.
|
|
102
|
-
- Conviction is a number (e.g. 2.5 for +2.5%, -3.0 for -3.0%, 0 for neutral).
|
|
103
|
-
- Correct-direction predictions earn honey (the primary ranking currency). Wrong-direction predictions earn wax (not a penalty, but doesn't help ranking).
|
|
104
|
-
- Direction matters more than magnitude for earning honey, though closer magnitude predictions earn more.
|
|
105
|
-
- Early predictions earn dramatically more honey due to steep time bonus decay — speed matters.
|
|
106
|
-
- Consecutive correct-direction predictions build a streak (tracked on profile). Skipping does not break streaks.
|
|
107
|
-
- Skipping is a valid strategy — no penalty, no streak break. Knowing when to sit out is a skill.
|
|
108
|
-
- Agents are ranked on a leaderboard by total honey.
|
|
109
|
-
|
|
110
|
-
Generate a STRATEGY.md file. The STRATEGY.md defines HOW the agent makes predictions — their method, sector focus, and decision framework. The strategy should reflect the agent's personality and tone, and should address the game mechanics above (e.g. when to skip, how aggressive to be with timing, how to calibrate conviction magnitude).
|
|
111
|
-
|
|
112
|
-
CRITICAL: Output ONLY valid markdown matching this exact structure. No extra commentary.
|
|
113
|
-
|
|
114
|
-
The first line MUST be: # Prediction Strategy: ${agentName}
|
|
115
|
-
|
|
116
|
-
Required sections with EXACT headers:
|
|
117
|
-
## Philosophy
|
|
118
|
-
## Signal Interpretation
|
|
119
|
-
- Method: (must be one of: technical, fundamental, sentiment, onchain, macro)
|
|
120
|
-
- Primary indicators: (list key indicators)
|
|
121
|
-
## Sentiment
|
|
122
|
-
- Bias: ${sentiment}
|
|
123
|
-
## Sector Focus
|
|
124
|
-
- Sectors: ${sectorsLine}
|
|
125
|
-
- Avoid: (sectors to avoid)
|
|
126
|
-
## Timeframe
|
|
127
|
-
- Active timeframes: ${timeframesLine}
|
|
128
|
-
(Explain why these timeframes suit the agent's style. Mention that the agent skips signals outside these timeframes.)
|
|
129
|
-
## Decision Framework
|
|
130
|
-
1. (first step)
|
|
131
|
-
2. (second step)
|
|
132
|
-
3. (third step)
|
|
133
|
-
|
|
134
|
-
Here are reference examples of well-crafted STRATEGY.md files:
|
|
135
|
-
---
|
|
136
|
-
${PRESET_EXAMPLES.split('===')[1] || ''}
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
Create something UNIQUE based on the agent's name, bio, and identity traits.${feedbackLine}`;
|
|
140
|
-
const model = buildModel(providerId, apiKey);
|
|
141
|
-
const result = streamText({
|
|
142
|
-
model,
|
|
143
|
-
prompt,
|
|
144
|
-
maxOutputTokens: 1200,
|
|
145
|
-
});
|
|
146
|
-
return result.textStream;
|
|
147
|
-
}
|
package/dist/create/generate.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
-
const __dirname = path.dirname(__filename);
|
|
7
|
-
export async function scaffoldProject(projectName, provider, apiKey, soulContent, strategyContent, callbacks) {
|
|
8
|
-
// Validate project name to prevent path traversal / command injection
|
|
9
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(projectName)) {
|
|
10
|
-
callbacks.onError('Project name can only contain letters, numbers, dashes, and underscores.');
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
const agentsDir = path.join(os.homedir(), '.hive', 'agents');
|
|
14
|
-
const projectDir = path.join(agentsDir, projectName);
|
|
15
|
-
// Ensure resolved path is still inside ~/.hive/agents/
|
|
16
|
-
const normalizedProjectDir = path.resolve(projectDir);
|
|
17
|
-
const normalizedAgentsDir = path.resolve(agentsDir);
|
|
18
|
-
if (!normalizedProjectDir.startsWith(normalizedAgentsDir + path.sep)) {
|
|
19
|
-
callbacks.onError('Invalid project name: path traversal detected.');
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
if (await fs.pathExists(projectDir)) {
|
|
23
|
-
callbacks.onError(`Directory ${normalizedProjectDir} already exists. Run "npx @hive-org/cli@latest create" again with a different name.`);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
// 1. Create directory
|
|
27
|
-
callbacks.onStep('Creating project directory');
|
|
28
|
-
await fs.ensureDir(projectDir);
|
|
29
|
-
// 2. Write SOUL.md and STRATEGY.md
|
|
30
|
-
callbacks.onStep('Writing personality files');
|
|
31
|
-
await fs.writeFile(path.join(projectDir, 'SOUL.md'), soulContent, 'utf-8');
|
|
32
|
-
await fs.writeFile(path.join(projectDir, 'STRATEGY.md'), strategyContent, 'utf-8');
|
|
33
|
-
const seedMemory = `# Memory
|
|
34
|
-
|
|
35
|
-
## Key Learnings
|
|
36
|
-
|
|
37
|
-
(none yet)
|
|
38
|
-
|
|
39
|
-
## Market Observations
|
|
40
|
-
|
|
41
|
-
(none yet)
|
|
42
|
-
|
|
43
|
-
## Session Notes
|
|
44
|
-
|
|
45
|
-
(none yet)
|
|
46
|
-
`;
|
|
47
|
-
await fs.writeFile(path.join(projectDir, 'MEMORY.md'), seedMemory, 'utf-8');
|
|
48
|
-
// 3. Write .env
|
|
49
|
-
callbacks.onStep('Writing environment file');
|
|
50
|
-
const envContent = `${provider.envVar}="${apiKey}"
|
|
51
|
-
`;
|
|
52
|
-
await fs.writeFile(path.join(projectDir, '.env'), envContent, { encoding: 'utf-8', mode: 0o600 });
|
|
53
|
-
// 4. Write minimal package.json — no deps needed, npx fetches @hive-org/agent@latest on every run
|
|
54
|
-
callbacks.onStep('Configuring project');
|
|
55
|
-
const packageJson = {
|
|
56
|
-
name: `hive-agent-${projectName}`,
|
|
57
|
-
private: true,
|
|
58
|
-
type: 'module',
|
|
59
|
-
scripts: {
|
|
60
|
-
start: 'npx @hive-org/cli@latest start',
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
await fs.writeJson(path.join(projectDir, 'package.json'), packageJson, { spaces: 2 });
|
|
64
|
-
// 5. Copy default skills
|
|
65
|
-
callbacks.onStep('Installing default skills');
|
|
66
|
-
const templateSkillsDir = path.resolve(__dirname, '../../templates/skills');
|
|
67
|
-
const projectSkillsDir = path.join(projectDir, 'skills');
|
|
68
|
-
const templateSkillsExist = await fs.pathExists(templateSkillsDir);
|
|
69
|
-
if (templateSkillsExist) {
|
|
70
|
-
await fs.copy(templateSkillsDir, projectSkillsDir);
|
|
71
|
-
}
|
|
72
|
-
callbacks.onDone(normalizedProjectDir);
|
|
73
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect } from 'react';
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
4
|
-
import { SelectPrompt } from '../../components/SelectPrompt.js';
|
|
5
|
-
import { TextPrompt } from '../../components/TextPrompt.js';
|
|
6
|
-
import { Spinner } from '../../components/Spinner.js';
|
|
7
|
-
import { colors, symbols } from '../../theme.js';
|
|
8
|
-
import { AI_PROVIDERS } from '../../ai-providers.js';
|
|
9
|
-
import { validateApiKey } from '../validate-api-key.js';
|
|
10
|
-
import { readConfig, writeConfig } from '../../config.js';
|
|
11
|
-
function maskKey(key) {
|
|
12
|
-
if (key.length <= 8) {
|
|
13
|
-
return '****';
|
|
14
|
-
}
|
|
15
|
-
const visible = key.slice(0, 4) + '...' + key.slice(-4);
|
|
16
|
-
return visible;
|
|
17
|
-
}
|
|
18
|
-
export function ApiKeyStep({ onComplete }) {
|
|
19
|
-
const [phase, setPhase] = useState('check-saved');
|
|
20
|
-
const [savedConfig, setSavedConfig] = useState(null);
|
|
21
|
-
const [providerId, setProviderId] = useState(null);
|
|
22
|
-
const [error, setError] = useState('');
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
const loadConfig = async () => {
|
|
25
|
-
const config = await readConfig();
|
|
26
|
-
if (config) {
|
|
27
|
-
setSavedConfig(config);
|
|
28
|
-
setPhase('use-saved');
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
setPhase('select-provider');
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
void loadConfig();
|
|
35
|
-
}, []);
|
|
36
|
-
const providerItems = AI_PROVIDERS.map((p) => ({
|
|
37
|
-
label: p.label,
|
|
38
|
-
value: p.id,
|
|
39
|
-
description: p.envVar,
|
|
40
|
-
}));
|
|
41
|
-
const handleProviderSelect = (item) => {
|
|
42
|
-
setProviderId(item.value);
|
|
43
|
-
setPhase('enter-key');
|
|
44
|
-
};
|
|
45
|
-
const handleKeySubmit = async (key, selectedProviderId) => {
|
|
46
|
-
setPhase('validating');
|
|
47
|
-
setError('');
|
|
48
|
-
const result = await validateApiKey(selectedProviderId, key);
|
|
49
|
-
if (result === true) {
|
|
50
|
-
await writeConfig({ providerId: selectedProviderId, apiKey: key });
|
|
51
|
-
onComplete({ providerId: selectedProviderId, apiKey: key });
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
setError(result);
|
|
55
|
-
setPhase('error');
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
const handleUseSaved = async (item) => {
|
|
59
|
-
if (item.value === 'yes' && savedConfig) {
|
|
60
|
-
setPhase('validating');
|
|
61
|
-
const result = await validateApiKey(savedConfig.providerId, savedConfig.apiKey);
|
|
62
|
-
if (result === true) {
|
|
63
|
-
onComplete({ providerId: savedConfig.providerId, apiKey: savedConfig.apiKey });
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
setError(`Saved key is no longer valid: ${result}`);
|
|
67
|
-
setPhase('select-provider');
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
setPhase('select-provider');
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
const selectedProvider = providerId
|
|
75
|
-
? AI_PROVIDERS.find((p) => p.id === providerId)
|
|
76
|
-
: null;
|
|
77
|
-
const savedProvider = savedConfig
|
|
78
|
-
? AI_PROVIDERS.find((p) => p.id === savedConfig.providerId)
|
|
79
|
-
: null;
|
|
80
|
-
return (_jsxs(Box, { flexDirection: "column", children: [phase === 'check-saved' && (_jsx(Spinner, { label: "Checking for saved API key..." })), phase === 'use-saved' && savedConfig && savedProvider && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 2, children: _jsxs(Text, { color: colors.gray, children: [symbols.diamond, " Saved key: ", _jsx(Text, { color: colors.honey, children: savedProvider.label }), ' ', _jsxs(Text, { color: colors.grayDim, children: ["(", maskKey(savedConfig.apiKey), ")"] })] }) }), _jsx(SelectPrompt, { label: "Use saved API key?", items: [
|
|
81
|
-
{ label: 'Yes, use saved key', value: 'yes' },
|
|
82
|
-
{ label: 'No, enter a new key', value: 'no' },
|
|
83
|
-
], onSelect: (item) => { void handleUseSaved(item); } })] })), phase === 'select-provider' && (_jsxs(Box, { flexDirection: "column", children: [error !== '' && (_jsx(Box, { marginBottom: 1, marginLeft: 2, children: _jsxs(Text, { color: colors.red, children: [symbols.cross, " ", error] }) })), _jsx(SelectPrompt, { label: "Select your AI provider", items: providerItems, onSelect: handleProviderSelect })] })), phase === 'enter-key' && selectedProvider && providerId && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: colors.gray, children: [symbols.diamond, " Provider: ", _jsx(Text, { color: colors.honey, children: selectedProvider.label })] }) }), _jsx(TextPrompt, { label: `Enter your ${selectedProvider.envVar}`, placeholder: "sk-...", onSubmit: (key) => { void handleKeySubmit(key, providerId); }, validate: (val) => {
|
|
84
|
-
if (!val) {
|
|
85
|
-
return 'API key is required';
|
|
86
|
-
}
|
|
87
|
-
if (val.length < 10) {
|
|
88
|
-
return 'API key seems too short';
|
|
89
|
-
}
|
|
90
|
-
return true;
|
|
91
|
-
} })] })), phase === 'validating' && (_jsx(Spinner, { label: "Validating API key..." })), phase === 'error' && providerId && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: colors.red, children: [symbols.cross, " ", error] }) }), _jsx(TextPrompt, { label: "Try again \u2014 enter your API key", placeholder: "sk-...", onSubmit: (key) => { void handleKeySubmit(key, providerId); }, validate: (val) => {
|
|
92
|
-
if (!val) {
|
|
93
|
-
return 'API key is required';
|
|
94
|
-
}
|
|
95
|
-
return true;
|
|
96
|
-
} })] }))] }));
|
|
97
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
import { TextPrompt } from '../../components/TextPrompt.js';
|
|
4
|
-
import { colors, symbols } from '../../theme.js';
|
|
5
|
-
export function AvatarStep({ agentName, onComplete }) {
|
|
6
|
-
const defaultUrl = `https://api.dicebear.com/9.x/bottts-neutral/svg?seed=${encodeURIComponent(agentName)}`;
|
|
7
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 2, children: _jsxs(Text, { color: colors.gray, children: [symbols.diamond, " Default: ", _jsx(Text, { color: colors.honey, children: defaultUrl })] }) }), _jsx(TextPrompt, { label: "Avatar image URL (press Enter for default)", placeholder: defaultUrl, onSubmit: (val) => onComplete(val || defaultUrl), validate: (val) => {
|
|
8
|
-
if (!val) {
|
|
9
|
-
return true;
|
|
10
|
-
}
|
|
11
|
-
if (!val.startsWith('http://') && !val.startsWith('https://')) {
|
|
12
|
-
return 'Must start with http:// or https://';
|
|
13
|
-
}
|
|
14
|
-
return true;
|
|
15
|
-
} })] }));
|
|
16
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Box } from 'ink';
|
|
3
|
-
import { TextPrompt } from '../../components/TextPrompt.js';
|
|
4
|
-
export function BioStep({ onComplete }) {
|
|
5
|
-
return (_jsx(Box, { flexDirection: "column", children: _jsx(TextPrompt, { label: "Write your agent's bio in their voice", placeholder: "macro degen. mass liquidated in luna. rebuilt during the bear.", onSubmit: onComplete, maxLength: 1000, validate: (val) => {
|
|
6
|
-
if (!val) {
|
|
7
|
-
return 'Bio is required';
|
|
8
|
-
}
|
|
9
|
-
if (val.length > 1000) {
|
|
10
|
-
return `Bio must be 1000 characters or less (currently ${val.length})`;
|
|
11
|
-
}
|
|
12
|
-
return true;
|
|
13
|
-
} }) }));
|
|
14
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect } from 'react';
|
|
3
|
-
import { Box, Text, useApp } from 'ink';
|
|
4
|
-
import { colors, symbols, border } from '../../theme.js';
|
|
5
|
-
export function DoneStep({ projectDir }) {
|
|
6
|
-
const { exit } = useApp();
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
exit();
|
|
9
|
-
}, []);
|
|
10
|
-
const termWidth = process.stdout.columns || 60;
|
|
11
|
-
const boxWidth = Math.min(termWidth - 4, 60);
|
|
12
|
-
const line = border.horizontal.repeat(boxWidth - 2);
|
|
13
|
-
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [border.topLeft, line, border.topRight] }) }), _jsxs(Box, { children: [_jsx(Text, { color: colors.honey, children: border.vertical }), _jsx(Text, { children: " " }), _jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Agent created successfully!"] }), _jsx(Text, { children: ' '.repeat(Math.max(0, boxWidth - 32)) }), _jsx(Text, { color: colors.honey, children: border.vertical })] }), _jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [border.bottomLeft, line, border.bottomRight] }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 1, children: [_jsx(Text, { color: colors.white, bold: true, children: "Next steps:" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsxs(Text, { color: colors.gray, children: [" 1. ", _jsx(Text, { color: colors.white, children: "npx @hive-org/cli@latest start" })] }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.grayDim, children: " Fine-tune SOUL.md and STRATEGY.md by chatting with your agent during" }), _jsx(Text, { color: colors.grayDim, children: " a run, or edit them directly at:" }), _jsxs(Text, { color: colors.grayDim, children: [" ", _jsx(Text, { color: colors.white, children: projectDir })] })] })] })] }));
|
|
14
|
-
}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useCallback } from 'react';
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
4
|
-
import { SelectPrompt } from '../../components/SelectPrompt.js';
|
|
5
|
-
import { MultiSelectPrompt } from '../../components/MultiSelectPrompt.js';
|
|
6
|
-
import { TextPrompt } from '../../components/TextPrompt.js';
|
|
7
|
-
import { CharacterSummaryCard } from '../../components/CharacterSummaryCard.js';
|
|
8
|
-
import { PERSONALITY_OPTIONS, VOICE_OPTIONS, TRADING_STYLE_OPTIONS, PROJECT_CATEGORY_OPTIONS, SENTIMENT_OPTIONS, TIMEFRAME_OPTIONS, BIO_EXAMPLES, } from '../../presets.js';
|
|
9
|
-
import { colors, symbols } from '../../theme.js';
|
|
10
|
-
function buildPersonalityItems() {
|
|
11
|
-
const items = PERSONALITY_OPTIONS.map((opt) => ({
|
|
12
|
-
label: opt.label,
|
|
13
|
-
value: opt.value,
|
|
14
|
-
description: opt.description,
|
|
15
|
-
}));
|
|
16
|
-
items.push({ label: 'Custom...', value: '__custom__' });
|
|
17
|
-
return items;
|
|
18
|
-
}
|
|
19
|
-
function buildVoiceItems() {
|
|
20
|
-
const items = VOICE_OPTIONS.map((opt) => ({
|
|
21
|
-
label: opt.label,
|
|
22
|
-
value: opt.value,
|
|
23
|
-
description: opt.description,
|
|
24
|
-
}));
|
|
25
|
-
items.push({ label: 'Custom...', value: '__custom__' });
|
|
26
|
-
return items;
|
|
27
|
-
}
|
|
28
|
-
function buildTradingStyleItems() {
|
|
29
|
-
const items = TRADING_STYLE_OPTIONS.map((opt) => ({
|
|
30
|
-
label: opt.label,
|
|
31
|
-
value: opt.value,
|
|
32
|
-
description: opt.description,
|
|
33
|
-
}));
|
|
34
|
-
items.push({ label: 'Custom...', value: '__custom__' });
|
|
35
|
-
return items;
|
|
36
|
-
}
|
|
37
|
-
function buildCategoryItems() {
|
|
38
|
-
const items = PROJECT_CATEGORY_OPTIONS.map((opt) => ({
|
|
39
|
-
label: opt.label,
|
|
40
|
-
value: opt.value,
|
|
41
|
-
description: opt.description,
|
|
42
|
-
}));
|
|
43
|
-
return items;
|
|
44
|
-
}
|
|
45
|
-
function buildSentimentItems() {
|
|
46
|
-
const items = SENTIMENT_OPTIONS.map((opt) => ({
|
|
47
|
-
label: opt.label,
|
|
48
|
-
value: opt.value,
|
|
49
|
-
description: opt.description,
|
|
50
|
-
}));
|
|
51
|
-
return items;
|
|
52
|
-
}
|
|
53
|
-
function buildTimeframeItems() {
|
|
54
|
-
const items = TIMEFRAME_OPTIONS.map((opt) => ({
|
|
55
|
-
label: opt.label,
|
|
56
|
-
value: opt.value,
|
|
57
|
-
description: opt.description,
|
|
58
|
-
}));
|
|
59
|
-
return items;
|
|
60
|
-
}
|
|
61
|
-
const personalityItems = buildPersonalityItems();
|
|
62
|
-
const voiceItems = buildVoiceItems();
|
|
63
|
-
const tradingStyleItems = buildTradingStyleItems();
|
|
64
|
-
const categoryItems = buildCategoryItems();
|
|
65
|
-
const sentimentItems = buildSentimentItems();
|
|
66
|
-
const timeframeItems = buildTimeframeItems();
|
|
67
|
-
export function IdentityStep({ agentName, onComplete }) {
|
|
68
|
-
const [subStep, setSubStep] = useState('personality');
|
|
69
|
-
const [personalityLabel, setPersonalityLabel] = useState('');
|
|
70
|
-
const [personality, setPersonality] = useState('');
|
|
71
|
-
const [tone, setTone] = useState('');
|
|
72
|
-
const [voiceStyle, setVoiceStyle] = useState('');
|
|
73
|
-
const [voiceLabel, setVoiceLabel] = useState('');
|
|
74
|
-
const [tradingStyle, setTradingStyle] = useState('');
|
|
75
|
-
const [tradingStyleLabel, setTradingStyleLabel] = useState('');
|
|
76
|
-
const [sectors, setSectors] = useState([]);
|
|
77
|
-
const [sectorsLabel, setSectorsLabel] = useState('');
|
|
78
|
-
const [sentiment, setSentiment] = useState('');
|
|
79
|
-
const [sentimentLabel, setSentimentLabel] = useState('');
|
|
80
|
-
const [timeframes, setTimeframes] = useState([]);
|
|
81
|
-
const [timeframesLabel, setTimeframesLabel] = useState('');
|
|
82
|
-
const handlePersonalitySelect = useCallback((item) => {
|
|
83
|
-
if (item.value === '__custom__') {
|
|
84
|
-
setSubStep('personality-custom');
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const description = item.description ?? '';
|
|
88
|
-
const personalityValue = `${item.label} — ${description}`;
|
|
89
|
-
setPersonality(personalityValue);
|
|
90
|
-
setPersonalityLabel(item.label);
|
|
91
|
-
setSubStep('voice');
|
|
92
|
-
}, []);
|
|
93
|
-
const handlePersonalityCustom = useCallback((value) => {
|
|
94
|
-
setPersonality(value);
|
|
95
|
-
setPersonalityLabel(value);
|
|
96
|
-
setSubStep('voice');
|
|
97
|
-
}, []);
|
|
98
|
-
const handleVoiceSelect = useCallback((item) => {
|
|
99
|
-
if (item.value === '__custom__') {
|
|
100
|
-
setSubStep('voice-custom');
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const voiceOption = VOICE_OPTIONS.find((v) => v.value === item.value);
|
|
104
|
-
setTone(voiceOption.tone);
|
|
105
|
-
setVoiceStyle(voiceOption.voiceStyle);
|
|
106
|
-
setVoiceLabel(item.label);
|
|
107
|
-
setSubStep('trading');
|
|
108
|
-
}, []);
|
|
109
|
-
const handleVoiceCustom = useCallback((value) => {
|
|
110
|
-
setTone(value);
|
|
111
|
-
setVoiceStyle(value);
|
|
112
|
-
setVoiceLabel(value);
|
|
113
|
-
setSubStep('trading');
|
|
114
|
-
}, []);
|
|
115
|
-
const handleTradingStyleSelect = useCallback((item) => {
|
|
116
|
-
if (item.value === '__custom__') {
|
|
117
|
-
setSubStep('trading-custom');
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
setTradingStyle(item.value);
|
|
121
|
-
setTradingStyleLabel(item.label);
|
|
122
|
-
setSubStep('sectors');
|
|
123
|
-
}, []);
|
|
124
|
-
const handleTradingStyleCustom = useCallback((value) => {
|
|
125
|
-
setTradingStyle(value);
|
|
126
|
-
setTradingStyleLabel(value);
|
|
127
|
-
setSubStep('sectors');
|
|
128
|
-
}, []);
|
|
129
|
-
const handleSectors = useCallback((selected) => {
|
|
130
|
-
const values = selected.map((s) => s.value);
|
|
131
|
-
const labels = selected.map((s) => s.label);
|
|
132
|
-
setSectors(values);
|
|
133
|
-
const displayLabel = values.length === categoryItems.length ? 'All' : labels.join(', ');
|
|
134
|
-
setSectorsLabel(displayLabel);
|
|
135
|
-
setSubStep('sentiment');
|
|
136
|
-
}, []);
|
|
137
|
-
const handleSentimentSelect = useCallback((item) => {
|
|
138
|
-
setSentiment(item.value);
|
|
139
|
-
setSentimentLabel(item.label);
|
|
140
|
-
setSubStep('timeframe');
|
|
141
|
-
}, []);
|
|
142
|
-
const handleTimeframes = useCallback((selected) => {
|
|
143
|
-
const values = selected.map((s) => s.value);
|
|
144
|
-
const labels = selected.map((s) => s.label);
|
|
145
|
-
setTimeframes(values);
|
|
146
|
-
const displayLabel = values.length === timeframeItems.length ? 'All' : labels.join(', ');
|
|
147
|
-
setTimeframesLabel(displayLabel);
|
|
148
|
-
setSubStep('bio');
|
|
149
|
-
}, []);
|
|
150
|
-
const handleBio = useCallback((value) => {
|
|
151
|
-
const result = { personality, tone, voiceStyle, tradingStyle, sectors, sentiment, timeframes, bio: value };
|
|
152
|
-
onComplete(result);
|
|
153
|
-
}, [personality, tone, voiceStyle, tradingStyle, sectors, sentiment, timeframes, onComplete]);
|
|
154
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(CharacterSummaryCard, { name: agentName, personality: personalityLabel || undefined, voice: voiceLabel || undefined, tradingStyle: tradingStyleLabel || undefined, sectors: sectorsLabel || undefined, sentiment: sentimentLabel || undefined, timeframe: timeframesLabel || undefined }), subStep === 'personality' && (_jsx(SelectPrompt, { label: "Choose a personality", items: personalityItems, onSelect: handlePersonalitySelect })), subStep === 'personality-custom' && (_jsx(TextPrompt, { label: "Describe your agent's personality", placeholder: "e.g. stoic realist with a dry wit", onSubmit: handlePersonalityCustom, validate: (val) => (!val ? 'Personality is required' : true) })), subStep === 'voice' && (_jsx(SelectPrompt, { label: "Choose a voice", items: voiceItems, onSelect: handleVoiceSelect })), subStep === 'voice-custom' && (_jsx(TextPrompt, { label: "Describe your agent's voice", placeholder: "e.g. writes like a bloomberg terminal on acid", onSubmit: handleVoiceCustom, validate: (val) => (!val ? 'Voice is required' : true) })), subStep === 'trading' && (_jsx(SelectPrompt, { label: "How does your agent evaluate signals?", items: tradingStyleItems, onSelect: handleTradingStyleSelect })), subStep === 'trading-custom' && (_jsx(TextPrompt, { label: "Describe your agent's trading style", placeholder: "e.g. combines on-chain data with sentiment analysis", onSubmit: handleTradingStyleCustom, validate: (val) => (!val ? 'Trading style is required' : true) })), subStep === 'sectors' && (_jsx(MultiSelectPrompt, { label: "Which categories should your agent trade?", items: categoryItems, onSubmit: handleSectors })), subStep === 'sentiment' && (_jsx(SelectPrompt, { label: "What's your agent's market sentiment?", items: sentimentItems, onSelect: handleSentimentSelect })), subStep === 'timeframe' && (_jsx(MultiSelectPrompt, { label: "Which timeframes should your agent participate in?", items: timeframeItems, onSubmit: handleTimeframes })), subStep === 'bio' && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: colors.grayDim, italic: true, children: [symbols.arrow, " Examples:"] }), BIO_EXAMPLES.map((example, i) => (_jsx(Box, { marginLeft: 2, marginTop: i > 0 ? 1 : 0, children: _jsxs(Text, { color: colors.grayDim, italic: true, children: [symbols.diamond, " ", `"${example}"`] }) }, i)))] }), _jsx(TextPrompt, { label: "Write your agent's bio", placeholder: `short bio for your ${personalityLabel} agent`, onSubmit: handleBio, maxLength: 1000, validate: (val) => {
|
|
155
|
-
if (!val) {
|
|
156
|
-
return 'Bio is required';
|
|
157
|
-
}
|
|
158
|
-
if (val.length > 1000) {
|
|
159
|
-
return `Bio must be 1000 characters or less (currently ${val.length})`;
|
|
160
|
-
}
|
|
161
|
-
return true;
|
|
162
|
-
} })] }))] }));
|
|
163
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useCallback, useMemo } from 'react';
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
4
|
-
import axios from 'axios';
|
|
5
|
-
import { TextPrompt } from '../../components/TextPrompt.js';
|
|
6
|
-
import { Spinner } from '../../components/Spinner.js';
|
|
7
|
-
import { colors, symbols } from '../../theme.js';
|
|
8
|
-
import { HIVE_API_URL } from '../../config.js';
|
|
9
|
-
const ADJECTIVES = [
|
|
10
|
-
'royal', 'golden', 'buzzy', 'honey', 'sweet', 'stung', 'waxed', 'bold',
|
|
11
|
-
'swift', 'wild', 'keen', 'warm', 'hazy', 'calm', 'busy', 'amber',
|
|
12
|
-
'pollen', 'nectar', 'floral', 'sunny', 'misty', 'fuzzy', 'striped',
|
|
13
|
-
'waggle', 'silent', 'fierce', 'humble', 'lunar', 'solar', 'bloomed',
|
|
14
|
-
'bullish', 'bearish', 'staked', 'minted', 'forked', 'based', 'degen',
|
|
15
|
-
'pumped', 'longed', 'shorted', 'bridged', 'pegged', 'hodl', 'mega',
|
|
16
|
-
'alpha', 'sigma', 'hyper', 'ultra', 'rapid', 'atomic',
|
|
17
|
-
];
|
|
18
|
-
const NOUNS = [
|
|
19
|
-
'bee', 'drone', 'queen', 'swarm', 'hive', 'comb', 'larva', 'pupa',
|
|
20
|
-
'sting', 'apiary', 'keeper', 'mead', 'pollen', 'nectar', 'propolis',
|
|
21
|
-
'colony', 'brood', 'waggle', 'cell', 'wax', 'bloom', 'blossom',
|
|
22
|
-
'hornet', 'bumble', 'worker', 'forager', 'scout', 'smoker',
|
|
23
|
-
'whale', 'bull', 'bear', 'shard', 'block', 'node', 'vault',
|
|
24
|
-
'ledger', 'oracle', 'miner', 'staker', 'bridge', 'token', 'chain',
|
|
25
|
-
'wick', 'candle', 'pump', 'moon', 'floor', 'whale',
|
|
26
|
-
];
|
|
27
|
-
function generateRandomName() {
|
|
28
|
-
const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
|
|
29
|
-
const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
|
|
30
|
-
const number = Math.floor(Math.random() * 900) + 100;
|
|
31
|
-
const name = `${adjective}-${noun}-${number}`;
|
|
32
|
-
return name;
|
|
33
|
-
}
|
|
34
|
-
export function NameStep({ onComplete }) {
|
|
35
|
-
const [checking, setChecking] = useState(false);
|
|
36
|
-
const [error, setError] = useState('');
|
|
37
|
-
const placeholder = useMemo(() => generateRandomName(), []);
|
|
38
|
-
const handleSubmit = useCallback(async (name) => {
|
|
39
|
-
setChecking(true);
|
|
40
|
-
setError('');
|
|
41
|
-
try {
|
|
42
|
-
const apiUrl = HIVE_API_URL;
|
|
43
|
-
const response = await axios.get(`${apiUrl}/agent/check-name`, {
|
|
44
|
-
params: { name },
|
|
45
|
-
timeout: 3000,
|
|
46
|
-
});
|
|
47
|
-
if (!response.data.available) {
|
|
48
|
-
setError(`Name "${name}" is already taken`);
|
|
49
|
-
setChecking(false);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// best-effort: silently proceed if backend is unavailable
|
|
55
|
-
}
|
|
56
|
-
setChecking(false);
|
|
57
|
-
onComplete(name);
|
|
58
|
-
}, [onComplete]);
|
|
59
|
-
if (checking) {
|
|
60
|
-
return (_jsx(Box, { flexDirection: "column", children: _jsx(Spinner, { label: "Checking name availability..." }) }));
|
|
61
|
-
}
|
|
62
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(TextPrompt, { label: "Enter your agent name", placeholder: placeholder, onSubmit: handleSubmit, validate: (val) => {
|
|
63
|
-
if (!val) {
|
|
64
|
-
return 'Agent name is required';
|
|
65
|
-
}
|
|
66
|
-
if (!/^[a-zA-Z0-9-_]+$/.test(val)) {
|
|
67
|
-
return 'Only letters, numbers, hyphens, and underscores allowed';
|
|
68
|
-
}
|
|
69
|
-
return true;
|
|
70
|
-
} }), error !== '' && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: colors.red, children: [symbols.cross, " ", error] }) }))] }));
|
|
71
|
-
}
|