@thelapyae/geniclaw 1.1.11 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -77
- package/bin/geniclaw.js +234 -26
- package/dist/config-manager.d.ts +17 -0
- package/dist/config-manager.d.ts.map +1 -1
- package/dist/config-manager.js +13 -1
- package/dist/config-manager.js.map +1 -1
- package/dist/providers/anthropic.d.ts +19 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +60 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/gemini.d.ts +21 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +110 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/openai.d.ts +21 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +94 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +15 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +3 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/queue-processor.js +78 -221
- package/dist/queue-processor.js.map +1 -1
- package/dist/tools/web-search.d.ts +7 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +40 -0
- package/dist/tools/web-search.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
# 🦁 GeniClaw - Your AI Terminal Assistant
|
|
2
2
|
|
|
3
|
-
**GeniClaw** is a powerful, local-first AI assistant manager that connects
|
|
4
|
-
|
|
5
|
-
## 🚀 Features
|
|
6
|
-
|
|
7
|
-
- **🧠 Multi-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
3
|
+
**GeniClaw** is a powerful, local-first AI assistant manager that connects multiple AI providers (Gemini, OpenAI, Claude, Groq, DeepSeek, Grok) to your workflow. It runs as a background service (daemon) on your machine, managing conversation history, project sessions, and token usage efficiently.
|
|
4
|
+
|
|
5
|
+
## 🚀 Key Features
|
|
6
|
+
|
|
7
|
+
- **🧠 Multi-Provider Support**:
|
|
8
|
+
- **Google Gemini** (1.5 Flash/Pro, 2.0 Flash)
|
|
9
|
+
- **OpenAI** (GPT-4o, GPT-3.5)
|
|
10
|
+
- **Anthropic** (Claude 3.5 Sonnet, Opus)
|
|
11
|
+
- **Groq** (Llama 3, Mixtral - Ultra Fast)
|
|
12
|
+
- **DeepSeek** (Coder, Chat)
|
|
13
|
+
- **xAI** (Grok Beta)
|
|
14
|
+
- **🔎 Web Search**: Integrated **Brave Search** allows the AI to Google things for you in real-time.
|
|
15
|
+
- **🔀 Task Routing**: Assign different models to different tasks! (e.g., Use Groq for fast Chat, Gemini for heartbeat/cron jobs).
|
|
16
|
+
- **📂 Session Management**: Create separate "folders" (sessions) for different projects.
|
|
17
|
+
- **💾 Smart Memory**: Automatically manages conversation context and prunes old messages to stay within token limits.
|
|
18
|
+
- **🖥️ Terminal UI (TUI)**: A beautiful interactive interface to manage keys, models, and sessions.
|
|
19
|
+
- **⚡ Queue-Based**: Robust message processing ensures no data is lost.
|
|
12
20
|
|
|
13
21
|
---
|
|
14
22
|
|
|
@@ -17,20 +25,23 @@
|
|
|
17
25
|
### Prerequisites
|
|
18
26
|
|
|
19
27
|
- **Node.js** (v18 or higher)
|
|
20
|
-
- **
|
|
21
|
-
-
|
|
28
|
+
- **API Keys**: You need at least one API key from a supported provider:
|
|
29
|
+
- [Google AI Studio](https://aistudio.google.com/)
|
|
30
|
+
- [OpenAI](https://platform.openai.com/)
|
|
31
|
+
- [Anthropic](https://console.anthropic.com/)
|
|
32
|
+
- [Groq](https://console.groq.com/)
|
|
33
|
+
- [DeepSeek](https://platform.deepseek.com/)
|
|
34
|
+
- [Brave Search](https://brave.com/search/api/) (Optional, for Web Search)
|
|
35
|
+
- **Telegram Bot Token** (Optional, for chatting via Telegram).
|
|
22
36
|
|
|
23
37
|
### Installation
|
|
24
38
|
|
|
25
39
|
1. **Install via NPM**:
|
|
26
|
-
```bash
|
|
27
40
|
```bash
|
|
28
41
|
npm install -g @thelapyae/geniclaw
|
|
29
42
|
```
|
|
30
|
-
*Note: The `-g` flag installs it globally so you can run `geniclaw` from anywhere.*
|
|
31
43
|
|
|
32
44
|
2. **Run Setup**:
|
|
33
|
-
This will guide you through entering your API keys.
|
|
34
45
|
```bash
|
|
35
46
|
geniclaw setup
|
|
36
47
|
```
|
|
@@ -42,87 +53,52 @@
|
|
|
42
53
|
GeniClaw is managed entirely through its **Interactive TUI**.
|
|
43
54
|
|
|
44
55
|
### 1. Start the TUI
|
|
45
|
-
Run the following command in your terminal:
|
|
46
56
|
```bash
|
|
47
|
-
./bin/geniclaw.js
|
|
48
|
-
# Or if you linked it:
|
|
49
57
|
geniclaw
|
|
50
58
|
```
|
|
51
59
|
|
|
52
60
|
### 2. Main Menu Options
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
- **View Logs**: Inspect what's happening under the hood.
|
|
61
|
+
- **🤖 Select AI Provider & Model**: Choose which brain you want to use (Gemini, GPT-4, Claude, etc.).
|
|
62
|
+
- **🔑 Manage API Keys**: Enter keys for different providers.
|
|
63
|
+
- **🔀 Task Routing**: Configure which provider handles Chat vs. Background Tasks.
|
|
64
|
+
- **🔎 Web Search Config**: Set your Brave Search API key to enable internet access.
|
|
65
|
+
- **Start/Stop Daemon**: Control the background process.
|
|
59
66
|
|
|
60
67
|
### 3. Chatting
|
|
61
|
-
|
|
62
|
-
- **
|
|
63
|
-
- **CLI (Manual)**: You can send a one-off message from the terminal:
|
|
64
|
-
```bash
|
|
65
|
-
./geniclaw.sh send "Hello Gemini, how are you?"
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## 📂 Session Management (Folders)
|
|
71
|
-
|
|
72
|
-
GeniClaw allows you to organize multiple conversations.
|
|
73
|
-
|
|
74
|
-
1. Open the TUI: `geniclaw`
|
|
75
|
-
2. Select **Manage Sessions**.
|
|
76
|
-
3. **Create New Session**: Name it (e.g., `coding-project`, `personal-notes`).
|
|
77
|
-
4. **Switch Session**: Select it to make it active.
|
|
78
|
-
|
|
79
|
-
**How it works**:
|
|
80
|
-
- When you switch to `coding-project`, GeniClaw creates a folder at `~/.geniclaw/sessions/coding-project/`.
|
|
81
|
-
- All history and memory for that conversation are stored there.
|
|
82
|
-
- Switching back to `default` restores your previous context instantly.
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## ⚙️ Memory & Token Management
|
|
87
|
-
|
|
88
|
-
To support **Long Term Usage**, GeniClaw monitors your token usage.
|
|
68
|
+
- **Telegram**: Chat with your bot.
|
|
69
|
+
- **CLI**: `geniclaw send "Hello!"`
|
|
89
70
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
-
|
|
94
|
-
|
|
71
|
+
### web Search
|
|
72
|
+
Just ask:
|
|
73
|
+
- "Search for the latest heavy metal news"
|
|
74
|
+
- "Google the price of Bitcoin"
|
|
75
|
+
- "/search Who won the super bowl?"
|
|
95
76
|
|
|
96
77
|
---
|
|
97
78
|
|
|
98
|
-
##
|
|
79
|
+
## 🔒 Safety & Technology
|
|
99
80
|
|
|
100
|
-
|
|
101
|
-
By default, GeniClaw stores everything locally in your home directory:
|
|
102
|
-
`~/.geniclaw/`
|
|
81
|
+
GeniClaw creates a secure, local-first environment for your AI interactions.
|
|
103
82
|
|
|
104
|
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
83
|
+
### Technology Stack
|
|
84
|
+
- **Runtime**: Node.js & TypeScript.
|
|
85
|
+
- **State Management**: Local JSON files (No external database required).
|
|
86
|
+
- **API Integration**: Uses official SDKs (Google Generative AI) and standard REST APIs (OpenAI-compatible) via `node-fetch`.
|
|
87
|
+
- **Background Service**: A lightweight daemon process manages the message queue.
|
|
108
88
|
|
|
109
|
-
###
|
|
110
|
-
1. **
|
|
111
|
-
2. **
|
|
112
|
-
3. **
|
|
113
|
-
|
|
114
|
-
- It checks **Token Limits**.
|
|
115
|
-
- It sends context + message to **Gemini API**.
|
|
116
|
-
4. **Response**: Gemini's reply is saved to history and written to `queue/outgoing/`.
|
|
117
|
-
5. **Output**: The interface (Telegram/CLI) sends the reply back to you.
|
|
89
|
+
### Why is it Safe?
|
|
90
|
+
1. **Local Data**: All conversation history, sessions, and logs are stored **locally on your machine** in `~/.geniclaw/`. Your data never leaves your computer except to go directly to the AI provider.
|
|
91
|
+
2. **Direct Connection**: GeniClaw connects **directly** to the AI APIs (Google, OpenAI, etc.). There is no "middleman" server collecting your data.
|
|
92
|
+
3. **Open Source**: The code is transparent. You can inspect exactly how your keys and data are used.
|
|
93
|
+
4. **Key Security**: API keys are stored in a local config file (`config.json`) on your machine.
|
|
118
94
|
|
|
119
95
|
---
|
|
120
96
|
|
|
121
|
-
## ⚠️
|
|
97
|
+
## ⚠️ Important Notes
|
|
122
98
|
|
|
123
|
-
1. **API Costs**:
|
|
124
|
-
2. **
|
|
125
|
-
3. **
|
|
99
|
+
1. **API Costs**: Using commercial APIs (OpenAI, Anthropic) is not free. Costs depend on your usage. Groq and Gemini (Free Tier) offer free options.
|
|
100
|
+
2. **Privacy**: While GeniClaw is local, the AI Providers (Google, OpenAI, etc.) have their own data policies.
|
|
101
|
+
3. **Daemon**: The background process must be running for GeniClaw to reply.
|
|
126
102
|
|
|
127
103
|
---
|
|
128
104
|
|
package/bin/geniclaw.js
CHANGED
|
@@ -111,11 +111,13 @@ async function showMenu() {
|
|
|
111
111
|
|
|
112
112
|
// Show current status summary
|
|
113
113
|
try {
|
|
114
|
-
console.log(`
|
|
115
|
-
console.log(`Active Session:
|
|
116
|
-
console.log(`Active Skill:
|
|
117
|
-
if (config.
|
|
118
|
-
|
|
114
|
+
console.log(`Active Provider: ${config.activeProvider || 'gemini'} (${config.activeProvider === 'gemini' ? config.geminiModel : (config[config.activeProvider + 'Model'] || 'Default')})`);
|
|
115
|
+
console.log(`Active Session: 📂 ${activeSession}`);
|
|
116
|
+
console.log(`Active Skill: 🧠 ${activeSkill}`);
|
|
117
|
+
if (config.providerRouting && (config.providerRouting.chat !== config.activeProvider)) {
|
|
118
|
+
console.log(`Routing: 🔀 Custom`);
|
|
119
|
+
}
|
|
120
|
+
if (config.geminiApiUrl) console.log(`API URL: 🔌 ${config.geminiApiUrl}`);
|
|
119
121
|
} catch (e) {}
|
|
120
122
|
console.log('');
|
|
121
123
|
|
|
@@ -128,13 +130,15 @@ async function showMenu() {
|
|
|
128
130
|
{ name: 'Start Daemon', value: 'start' },
|
|
129
131
|
{ name: 'Stop Daemon', value: 'stop' },
|
|
130
132
|
{ name: 'Restart Daemon', value: 'restart' },
|
|
131
|
-
{ name: 'Check Status', value: 'status' },
|
|
132
133
|
{ name: 'View Logs', value: 'logs' },
|
|
133
134
|
new inquirer.Separator(),
|
|
134
|
-
{ name: '
|
|
135
|
+
{ name: '🤖 Select AI Provider & Model', value: 'provider' },
|
|
136
|
+
{ name: '🔑 Manage API Keys', value: 'keys' },
|
|
137
|
+
{ name: '🔀 Task Routing (Chat/Heartbeat/Cron)', value: 'routing' },
|
|
138
|
+
{ name: '🔎 Web Search Config', value: 'search' },
|
|
135
139
|
{ name: '🧠 Manage Skills', value: 'skills' },
|
|
136
|
-
{ name: '
|
|
137
|
-
{ name: '🔌 Change API Base URL', value: 'apiUrl' },
|
|
140
|
+
{ name: '📂 Manage Sessions', value: 'sessions' },
|
|
141
|
+
{ name: '🔌 Change API Base URL (Gemini/Proxy)', value: 'apiUrl' },
|
|
138
142
|
{ name: '🔑 Set Gateway/Proxy Key', value: 'gatewayKey' },
|
|
139
143
|
new inquirer.Separator(),
|
|
140
144
|
{ name: '✨ Update GeniClaw', value: 'update' },
|
|
@@ -159,12 +163,27 @@ async function showMenu() {
|
|
|
159
163
|
return showMenu();
|
|
160
164
|
}
|
|
161
165
|
|
|
162
|
-
if (action === '
|
|
163
|
-
await
|
|
166
|
+
if (action === 'provider') {
|
|
167
|
+
await selectProviderAndModel();
|
|
164
168
|
await promptContinue();
|
|
165
169
|
return showMenu();
|
|
166
170
|
}
|
|
167
171
|
|
|
172
|
+
if (action === 'keys') {
|
|
173
|
+
await manageApiKeys();
|
|
174
|
+
return showMenu();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (action === 'routing') {
|
|
178
|
+
await manageTaskRouting();
|
|
179
|
+
return showMenu();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (action === 'search') {
|
|
183
|
+
await configureWebSearch();
|
|
184
|
+
return showMenu();
|
|
185
|
+
}
|
|
186
|
+
|
|
168
187
|
if (action === 'apiUrl') {
|
|
169
188
|
await changeApiUrl();
|
|
170
189
|
await promptContinue();
|
|
@@ -208,25 +227,72 @@ async function promptContinue() {
|
|
|
208
227
|
]);
|
|
209
228
|
}
|
|
210
229
|
|
|
211
|
-
async function
|
|
230
|
+
async function selectProviderAndModel() {
|
|
212
231
|
const config = loadConfig();
|
|
213
|
-
const
|
|
232
|
+
const currentProvider = config.activeProvider || 'gemini';
|
|
233
|
+
|
|
234
|
+
const { provider } = await inquirer.prompt([
|
|
235
|
+
{
|
|
236
|
+
type: 'list',
|
|
237
|
+
name: 'provider',
|
|
238
|
+
message: 'Select Active AI Provider:',
|
|
239
|
+
default: currentProvider,
|
|
240
|
+
choices: [
|
|
241
|
+
{ name: 'Google Gemini', value: 'gemini' },
|
|
242
|
+
{ name: 'OpenAI (GPT-4)', value: 'openai' },
|
|
243
|
+
{ name: 'Anthropic (Claude)', value: 'anthropic' },
|
|
244
|
+
{ name: 'Groq (Llama 3)', value: 'groq' },
|
|
245
|
+
{ name: 'Deepseek', value: 'deepseek' },
|
|
246
|
+
{ name: 'xAI (Grok)', value: 'grok' }
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
]);
|
|
250
|
+
|
|
251
|
+
config.activeProvider = provider;
|
|
252
|
+
saveConfig(config);
|
|
253
|
+
console.log(`✅ Active Provider set to: ${provider}`);
|
|
254
|
+
|
|
255
|
+
// Now configure model for this provider
|
|
256
|
+
let currentModel = 'default';
|
|
257
|
+
let choices = [];
|
|
258
|
+
|
|
259
|
+
switch (provider) {
|
|
260
|
+
case 'gemini':
|
|
261
|
+
currentModel = config.geminiModel || 'gemini-2.5-flash';
|
|
262
|
+
choices = ['gemini-2.5-flash', 'gemini-flash-lite-latest', 'gemini-2.5-pro', 'gemini-3-flash-preview', 'gemini-3-pro-preview'];
|
|
263
|
+
break;
|
|
264
|
+
case 'openai':
|
|
265
|
+
currentModel = config.openaiModel || 'gpt-4o';
|
|
266
|
+
choices = ['gpt-4o', 'gpt-4-turbo', 'gpt-3.5-turbo'];
|
|
267
|
+
break;
|
|
268
|
+
case 'anthropic':
|
|
269
|
+
currentModel = config.anthropicModel || 'claude-3-5-sonnet-20240620';
|
|
270
|
+
choices = ['claude-3-5-sonnet-20240620', 'claude-3-opus-20240229', 'claude-3-haiku-20240307'];
|
|
271
|
+
break;
|
|
272
|
+
case 'groq':
|
|
273
|
+
currentModel = config.groqModel || 'llama3-70b-8192';
|
|
274
|
+
choices = ['llama3-70b-8192', 'llama3-8b-8192', 'mixtral-8x7b-32768', 'gemma-7b-it'];
|
|
275
|
+
break;
|
|
276
|
+
case 'deepseek':
|
|
277
|
+
currentModel = config.deepseekModel || 'deepseek-chat';
|
|
278
|
+
choices = ['deepseek-chat', 'deepseek-coder'];
|
|
279
|
+
break;
|
|
280
|
+
case 'grok':
|
|
281
|
+
currentModel = config.grokModel || 'grok-beta';
|
|
282
|
+
choices = ['grok-beta'];
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
choices.push(new inquirer.Separator());
|
|
287
|
+
choices.push({ name: 'Custom (Enter manually)', value: 'custom' });
|
|
214
288
|
|
|
215
289
|
const { model } = await inquirer.prompt([
|
|
216
290
|
{
|
|
217
291
|
type: 'list',
|
|
218
292
|
name: 'model',
|
|
219
|
-
message:
|
|
293
|
+
message: `Select Model for ${provider}:`,
|
|
220
294
|
default: currentModel,
|
|
221
|
-
choices:
|
|
222
|
-
'gemini-2.5-flash',
|
|
223
|
-
'gemini-flash-lite-latest',
|
|
224
|
-
'gemini-2.5-pro',
|
|
225
|
-
'gemini-3-flash-preview',
|
|
226
|
-
'gemini-3-pro-preview',
|
|
227
|
-
new inquirer.Separator(),
|
|
228
|
-
{ name: 'Custom (Enter manually)', value: 'custom' }
|
|
229
|
-
]
|
|
295
|
+
choices: choices
|
|
230
296
|
}
|
|
231
297
|
]);
|
|
232
298
|
|
|
@@ -243,10 +309,152 @@ async function changeModel() {
|
|
|
243
309
|
selectedModel = customModel;
|
|
244
310
|
}
|
|
245
311
|
|
|
246
|
-
config
|
|
312
|
+
// Save model to correct config field
|
|
313
|
+
config[`${provider}Model`] = selectedModel;
|
|
314
|
+
saveConfig(config);
|
|
315
|
+
console.log(`✅ Model for ${provider} updated to: ${selectedModel}`);
|
|
316
|
+
|
|
317
|
+
// Check if key is set
|
|
318
|
+
const keyField = `${provider}ApiKey`;
|
|
319
|
+
if (!config[keyField] && !process.env[keyField.toUpperCase().replace(/([A-Z])/g, '_$1').toUpperCase()]) {
|
|
320
|
+
console.log(`⚠️ Warning: API Key for ${provider} is not set.`);
|
|
321
|
+
const { setKey } = await inquirer.prompt([{ type: 'confirm', name: 'setKey', message: 'Set API Key now?', default: true }]);
|
|
322
|
+
if (setKey) await manageApiKeys(provider);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function manageApiKeys(targetProvider = null) {
|
|
327
|
+
const config = loadConfig();
|
|
328
|
+
|
|
329
|
+
let provider = targetProvider;
|
|
330
|
+
if (!provider) {
|
|
331
|
+
const result = await inquirer.prompt([
|
|
332
|
+
{
|
|
333
|
+
type: 'list',
|
|
334
|
+
name: 'provider',
|
|
335
|
+
message: 'Select Provider to manage key:',
|
|
336
|
+
choices: [
|
|
337
|
+
{ name: 'Google Gemini', value: 'gemini' },
|
|
338
|
+
{ name: 'OpenAI', value: 'openai' },
|
|
339
|
+
{ name: 'Anthropic', value: 'anthropic' },
|
|
340
|
+
{ name: 'Groq', value: 'groq' },
|
|
341
|
+
{ name: 'Deepseek', value: 'deepseek' },
|
|
342
|
+
{ name: 'xAI (Grok)', value: 'grok' },
|
|
343
|
+
{ name: 'Brave Search', value: 'brave' },
|
|
344
|
+
{ name: 'Back', value: 'back' }
|
|
345
|
+
]
|
|
346
|
+
}
|
|
347
|
+
]);
|
|
348
|
+
if (result.provider === 'back') return;
|
|
349
|
+
provider = result.provider;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const keyField = `${provider}ApiKey`;
|
|
353
|
+
const currentKey = config[keyField] ? '********' : 'Not Set';
|
|
354
|
+
|
|
355
|
+
const { key } = await inquirer.prompt([
|
|
356
|
+
{
|
|
357
|
+
type: 'password',
|
|
358
|
+
name: 'key',
|
|
359
|
+
message: `Enter API Key for ${provider} (Current: ${currentKey}):`,
|
|
360
|
+
suffix: '\n(Leave empty to keep current)',
|
|
361
|
+
mask: '*'
|
|
362
|
+
}
|
|
363
|
+
]);
|
|
364
|
+
|
|
365
|
+
if (key && key.trim() !== '') {
|
|
366
|
+
config[keyField] = key.trim();
|
|
367
|
+
saveConfig(config);
|
|
368
|
+
console.log(`✅ API Key for ${provider} updated.`);
|
|
369
|
+
} else {
|
|
370
|
+
console.log('No change.');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (!targetProvider) await promptContinue();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async function manageTaskRouting() {
|
|
377
|
+
const config = loadConfig();
|
|
378
|
+
const routing = config.providerRouting || { chat: 'gemini', heartbeat: 'gemini', cron: 'gemini' };
|
|
379
|
+
|
|
380
|
+
const { task } = await inquirer.prompt([
|
|
381
|
+
{
|
|
382
|
+
type: 'list',
|
|
383
|
+
name: 'task',
|
|
384
|
+
message: 'Select Task to Route:',
|
|
385
|
+
choices: [
|
|
386
|
+
{ name: `Chat (Current: ${routing.chat || 'default'})`, value: 'chat' },
|
|
387
|
+
{ name: `Heartbeat (Current: ${routing.heartbeat || 'default'})`, value: 'heartbeat' },
|
|
388
|
+
{ name: `Cron Jobs (Current: ${routing.cron || 'default'})`, value: 'cron' },
|
|
389
|
+
{ name: 'Back', value: 'back' }
|
|
390
|
+
]
|
|
391
|
+
}
|
|
392
|
+
]);
|
|
393
|
+
|
|
394
|
+
if (task === 'back') return;
|
|
395
|
+
|
|
396
|
+
const { provider } = await inquirer.prompt([
|
|
397
|
+
{
|
|
398
|
+
type: 'list',
|
|
399
|
+
name: 'provider',
|
|
400
|
+
message: `Select Provider for ${task}:`,
|
|
401
|
+
choices: [
|
|
402
|
+
{ name: 'Use Active Provider (Default)', value: null },
|
|
403
|
+
new inquirer.Separator(),
|
|
404
|
+
{ name: 'Google Gemini', value: 'gemini' },
|
|
405
|
+
{ name: 'OpenAI', value: 'openai' },
|
|
406
|
+
{ name: 'Anthropic', value: 'anthropic' },
|
|
407
|
+
{ name: 'Groq', value: 'groq' },
|
|
408
|
+
{ name: 'Deepseek', value: 'deepseek' },
|
|
409
|
+
{ name: 'xAI (Grok)', value: 'grok' }
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
]);
|
|
413
|
+
|
|
414
|
+
if (provider) {
|
|
415
|
+
routing[task] = provider;
|
|
416
|
+
} else {
|
|
417
|
+
delete routing[task];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
config.providerRouting = routing;
|
|
247
421
|
saveConfig(config);
|
|
248
|
-
console.log(`✅
|
|
249
|
-
|
|
422
|
+
console.log(`✅ Routing updated.`);
|
|
423
|
+
|
|
424
|
+
return manageTaskRouting();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function configureWebSearch() {
|
|
428
|
+
const config = loadConfig();
|
|
429
|
+
const currentKey = config.braveApiKey ? '********' : 'Not Set';
|
|
430
|
+
|
|
431
|
+
console.log('configured Web Search uses Brave Search API.');
|
|
432
|
+
console.log('Get a free key at: https://brave.com/search/api/');
|
|
433
|
+
|
|
434
|
+
const { key } = await inquirer.prompt([
|
|
435
|
+
{
|
|
436
|
+
type: 'password',
|
|
437
|
+
name: 'key',
|
|
438
|
+
message: `Enter Brave Search API Key (Current: ${currentKey}):`,
|
|
439
|
+
suffix: '\n(Leave empty to remove/keep)',
|
|
440
|
+
mask: '*'
|
|
441
|
+
}
|
|
442
|
+
]);
|
|
443
|
+
|
|
444
|
+
if (key && key.trim() !== '') {
|
|
445
|
+
config.braveApiKey = key.trim();
|
|
446
|
+
saveConfig(config);
|
|
447
|
+
console.log('✅ Brave API Key set.');
|
|
448
|
+
} else if (config.braveApiKey && key === '') {
|
|
449
|
+
const { remove } = await inquirer.prompt([{ type: 'confirm', name: 'remove', message: 'Remove existing key?', default: false}]);
|
|
450
|
+
if (remove) {
|
|
451
|
+
delete config.braveApiKey;
|
|
452
|
+
saveConfig(config);
|
|
453
|
+
console.log('✅ Brave API Key removed.');
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
await promptContinue();
|
|
250
458
|
}
|
|
251
459
|
|
|
252
460
|
async function changeApiUrl() {
|
package/dist/config-manager.d.ts
CHANGED
|
@@ -12,6 +12,23 @@ export interface AppConfig {
|
|
|
12
12
|
userName?: string;
|
|
13
13
|
botName?: string;
|
|
14
14
|
botPurpose?: string;
|
|
15
|
+
activeProvider?: 'gemini' | 'openai' | 'anthropic' | 'groq' | 'deepseek' | 'grok';
|
|
16
|
+
providerRouting?: {
|
|
17
|
+
chat?: string;
|
|
18
|
+
heartbeat?: string;
|
|
19
|
+
cron?: string;
|
|
20
|
+
};
|
|
21
|
+
openaiModel?: string;
|
|
22
|
+
anthropicModel?: string;
|
|
23
|
+
groqModel?: string;
|
|
24
|
+
deepseekModel?: string;
|
|
25
|
+
grokModel?: string;
|
|
26
|
+
openaiApiKey?: string;
|
|
27
|
+
anthropicApiKey?: string;
|
|
28
|
+
groqApiKey?: string;
|
|
29
|
+
deepseekApiKey?: string;
|
|
30
|
+
grokApiKey?: string;
|
|
31
|
+
braveApiKey?: string;
|
|
15
32
|
onboardingState: 'INIT' | 'AWAITING_NAME' | 'AWAITING_BOT_NAME' | 'AWAITING_PURPOSE' | 'READY';
|
|
16
33
|
}
|
|
17
34
|
export declare class ConfigManager {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../src/config-manager.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,iBAAiB,QAAiH,CAAC;AAChJ,eAAO,MAAM,WAAW,QAA8C,CAAC;AAGvE,MAAM,WAAW,SAAS;IAEtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,GAAG,eAAe,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,OAAO,CAAC;CAClG;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,MAAM,CAAC,YAAY,CAA0B;IAErD,MAAM,CAAC,IAAI,IAAI,SAAS;
|
|
1
|
+
{"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../src/config-manager.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,iBAAiB,QAAiH,CAAC;AAChJ,eAAO,MAAM,WAAW,QAA8C,CAAC;AAGvE,MAAM,WAAW,SAAS;IAEtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IAClF,eAAe,CAAC,EAAE;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAGF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,eAAe,EAAE,MAAM,GAAG,eAAe,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,OAAO,CAAC;CAClG;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,MAAM,CAAC,YAAY,CAA0B;IAErD,MAAM,CAAC,IAAI,IAAI,SAAS;IAmCxB,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAcpC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI;CAKnD"}
|
package/dist/config-manager.js
CHANGED
|
@@ -32,7 +32,19 @@ class ConfigManager {
|
|
|
32
32
|
geminiModel: process.env.GEMINI_MODEL || 'gemini-1.5-flash',
|
|
33
33
|
geminiApiUrl: process.env.GEMINI_API_URL || undefined,
|
|
34
34
|
geminiGatewayKey: process.env.GEMINI_GATEWAY_KEY || undefined,
|
|
35
|
-
activeSession: 'default'
|
|
35
|
+
activeSession: 'default',
|
|
36
|
+
activeProvider: 'gemini',
|
|
37
|
+
providerRouting: {
|
|
38
|
+
chat: 'gemini',
|
|
39
|
+
heartbeat: 'gemini',
|
|
40
|
+
cron: 'gemini'
|
|
41
|
+
},
|
|
42
|
+
// Default Models
|
|
43
|
+
openaiModel: 'gpt-4o',
|
|
44
|
+
anthropicModel: 'claude-3-5-sonnet-20240620',
|
|
45
|
+
groqModel: 'llama3-70b-8192',
|
|
46
|
+
deepseekModel: 'deepseek-chat',
|
|
47
|
+
grokModel: 'grok-beta'
|
|
36
48
|
};
|
|
37
49
|
}
|
|
38
50
|
static save(config) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../src/config-manager.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AAEpB,eAAe;AACf,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;AAC3C,QAAA,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;AACnI,QAAA,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,yBAAiB,EAAE,aAAa,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../src/config-manager.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AAEpB,eAAe;AACf,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;AAC3C,QAAA,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;AACnI,QAAA,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,yBAAiB,EAAE,aAAa,CAAC,CAAC;AA0CvE,MAAa,aAAa;IACd,MAAM,CAAC,YAAY,GAAqB,IAAI,CAAC;IAErD,MAAM,CAAC,IAAI;QACP,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC;QAEhD,IAAI,YAAE,CAAC,UAAU,CAAC,mBAAW,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACD,MAAM,IAAI,GAAG,YAAE,CAAC,YAAY,CAAC,mBAAW,EAAE,MAAM,CAAC,CAAC;gBAClD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACrC,OAAO,IAAI,CAAC,YAAa,CAAC;YAC9B,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACT,OAAO,CAAC,KAAK,CAAC,0BAA2B,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YACpE,CAAC;QACL,CAAC;QAED,iBAAiB;QACjB,OAAO;YACH,eAAe,EAAE,MAAM;YACvB,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,kBAAkB;YAC3D,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,SAAS;YACrD,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,SAAS;YAC7D,aAAa,EAAE,SAAS;YACxB,cAAc,EAAE,QAAQ;YACxB,eAAe,EAAE;gBACb,IAAI,EAAE,QAAQ;gBACd,SAAS,EAAE,QAAQ;gBACnB,IAAI,EAAE,QAAQ;aACjB;YACD,iBAAiB;YACjB,WAAW,EAAE,QAAQ;YACrB,cAAc,EAAE,4BAA4B;YAC5C,SAAS,EAAE,iBAAiB;YAC5B,aAAa,EAAE,eAAe;YAC9B,SAAS,EAAE,WAAW;SACzB,CAAC;IACN,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,MAAiB;QACzB,IAAI,CAAC;YACD,0BAA0B;YAC1B,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,yBAAiB,CAAC,EAAE,CAAC;gBACpC,YAAE,CAAC,SAAS,CAAC,yBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,CAAC;YAED,YAAE,CAAC,aAAa,CAAC,mBAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/D,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;QAC/B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,0BAA2B,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;IACL,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,OAA2B;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;;AAxDL,sCAyDC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LLMProvider } from './types';
|
|
2
|
+
import { AppConfig } from '../config-manager';
|
|
3
|
+
export declare class AnthropicProvider implements LLMProvider {
|
|
4
|
+
private config;
|
|
5
|
+
constructor(config: AppConfig);
|
|
6
|
+
generateResponse(message: string, history: {
|
|
7
|
+
role: 'user' | 'model';
|
|
8
|
+
parts: {
|
|
9
|
+
text: string;
|
|
10
|
+
}[];
|
|
11
|
+
}[], systemInstruction?: string): Promise<string>;
|
|
12
|
+
countTokens(message: string, history: {
|
|
13
|
+
role: 'user' | 'model';
|
|
14
|
+
parts: {
|
|
15
|
+
text: string;
|
|
16
|
+
}[];
|
|
17
|
+
}[]): Promise<number>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,qBAAa,iBAAkB,YAAW,WAAW;IACjD,OAAO,CAAC,MAAM,CAAY;gBAEd,MAAM,EAAE,SAAS;IAIvB,gBAAgB,CAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,EAAE,EAChE,iBAAiB,CAAC,EAAE,MAAM,GAC3B,OAAO,CAAC,MAAM,CAAC;IA+CZ,WAAW,CACb,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,EAAE,GACjE,OAAO,CAAC,MAAM,CAAC;CAQrB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AnthropicProvider = void 0;
|
|
4
|
+
class AnthropicProvider {
|
|
5
|
+
config;
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
}
|
|
9
|
+
async generateResponse(message, history, systemInstruction) {
|
|
10
|
+
const apiKey = this.config.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
throw new Error("Anthropic API Key not found.");
|
|
13
|
+
}
|
|
14
|
+
const messages = [];
|
|
15
|
+
// History
|
|
16
|
+
for (const item of history) {
|
|
17
|
+
messages.push({
|
|
18
|
+
role: item.role === 'model' ? 'assistant' : 'user',
|
|
19
|
+
content: item.parts[0].text
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
// Current Message
|
|
23
|
+
messages.push({ role: 'user', content: message });
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: {
|
|
28
|
+
'Content-Type': 'application/json',
|
|
29
|
+
'x-api-key': apiKey,
|
|
30
|
+
'anthropic-version': '2023-06-01'
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
model: this.config.anthropicModel || 'claude-3-5-sonnet-20240620',
|
|
34
|
+
max_tokens: 4096,
|
|
35
|
+
system: systemInstruction,
|
|
36
|
+
messages: messages
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
const errorText = await response.text();
|
|
41
|
+
throw new Error(`Anthropic Error (${response.status}): ${errorText}`);
|
|
42
|
+
}
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
return data.content[0].text;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw new Error(`Anthropic Error: ${error.message}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async countTokens(message, history) {
|
|
51
|
+
// Rough estimation: 4 chars = 1 token
|
|
52
|
+
let text = message;
|
|
53
|
+
for (const item of history) {
|
|
54
|
+
text += item.parts[0].text;
|
|
55
|
+
}
|
|
56
|
+
return Math.ceil(text.length / 4);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.AnthropicProvider = AnthropicProvider;
|
|
60
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":";;;AAIA,MAAa,iBAAiB;IAClB,MAAM,CAAY;IAE1B,YAAY,MAAiB;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,gBAAgB,CAClB,OAAe,EACf,OAAgE,EAChE,iBAA0B;QAE1B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC5E,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,QAAQ,GAAG,EAAE,CAAC;QAEpB,UAAU;QACV,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YACzB,QAAQ,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM;gBAClD,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI;aAC9B,CAAC,CAAC;QACP,CAAC;QAED,kBAAkB;QAClB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAElD,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,uCAAuC,EAAE;gBAClE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACL,cAAc,EAAE,kBAAkB;oBAClC,WAAW,EAAE,MAAM;oBACnB,mBAAmB,EAAE,YAAY;iBACpC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACjB,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,4BAA4B;oBACjE,UAAU,EAAE,IAAI;oBAChB,MAAM,EAAE,iBAAiB;oBACzB,QAAQ,EAAE,QAAQ;iBACrB,CAAC;aACL,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACxC,MAAM,IAAI,KAAK,CAAC,oBAAoB,QAAQ,CAAC,MAAM,MAAM,SAAS,EAAE,CAAC,CAAC;YAC1E,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAS,CAAC;YAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,oBAAqB,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW,CACb,OAAe,EACf,OAAgE;QAEhE,sCAAsC;QACtC,IAAI,IAAI,GAAG,OAAO,CAAC;QACnB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YACzB,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/B,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACtC,CAAC;CACJ;AArED,8CAqEC"}
|