snow-ai 0.2.23 → 0.2.25

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.
Files changed (43) hide show
  1. package/dist/agents/compactAgent.d.ts +55 -0
  2. package/dist/agents/compactAgent.js +301 -0
  3. package/dist/api/chat.d.ts +0 -8
  4. package/dist/api/chat.js +1 -144
  5. package/dist/api/responses.d.ts +0 -11
  6. package/dist/api/responses.js +1 -189
  7. package/dist/api/systemPrompt.d.ts +1 -1
  8. package/dist/api/systemPrompt.js +80 -206
  9. package/dist/app.d.ts +2 -1
  10. package/dist/app.js +11 -13
  11. package/dist/cli.js +23 -3
  12. package/dist/hooks/useConversation.js +51 -7
  13. package/dist/hooks/useGlobalNavigation.d.ts +1 -1
  14. package/dist/hooks/useKeyboardInput.js +14 -8
  15. package/dist/mcp/filesystem.d.ts +49 -6
  16. package/dist/mcp/filesystem.js +243 -86
  17. package/dist/mcp/websearch.d.ts +118 -0
  18. package/dist/mcp/websearch.js +451 -0
  19. package/dist/ui/components/ToolResultPreview.js +60 -1
  20. package/dist/ui/pages/ChatScreen.d.ts +4 -2
  21. package/dist/ui/pages/ChatScreen.js +62 -14
  22. package/dist/ui/pages/{ApiConfigScreen.d.ts → ConfigScreen.d.ts} +1 -1
  23. package/dist/ui/pages/ConfigScreen.js +549 -0
  24. package/dist/ui/pages/{ModelConfigScreen.d.ts → ProxyConfigScreen.d.ts} +1 -1
  25. package/dist/ui/pages/ProxyConfigScreen.js +143 -0
  26. package/dist/ui/pages/WelcomeScreen.js +15 -15
  27. package/dist/utils/apiConfig.d.ts +8 -2
  28. package/dist/utils/apiConfig.js +21 -0
  29. package/dist/utils/commandExecutor.d.ts +1 -1
  30. package/dist/utils/contextCompressor.js +363 -49
  31. package/dist/utils/mcpToolsManager.d.ts +1 -1
  32. package/dist/utils/mcpToolsManager.js +106 -6
  33. package/dist/utils/resourceMonitor.d.ts +65 -0
  34. package/dist/utils/resourceMonitor.js +175 -0
  35. package/dist/utils/retryUtils.js +6 -0
  36. package/dist/utils/sessionManager.d.ts +1 -0
  37. package/dist/utils/sessionManager.js +10 -0
  38. package/dist/utils/textBuffer.js +7 -2
  39. package/dist/utils/toolExecutor.d.ts +2 -2
  40. package/dist/utils/toolExecutor.js +4 -4
  41. package/package.json +5 -1
  42. package/dist/ui/pages/ApiConfigScreen.js +0 -161
  43. package/dist/ui/pages/ModelConfigScreen.js +0 -467
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Resource Monitor - Track memory usage and potential leaks
3
+ */
4
+ interface ResourceStats {
5
+ timestamp: number;
6
+ memoryUsage: NodeJS.MemoryUsage;
7
+ activeEncoders: number;
8
+ activeMCPConnections: number;
9
+ }
10
+ declare class ResourceMonitor {
11
+ private stats;
12
+ private readonly maxStatsHistory;
13
+ private activeEncoders;
14
+ private activeMCPConnections;
15
+ private monitoringInterval;
16
+ /**
17
+ * Start monitoring resources
18
+ */
19
+ startMonitoring(intervalMs?: number): void;
20
+ /**
21
+ * Stop monitoring resources
22
+ */
23
+ stopMonitoring(): void;
24
+ /**
25
+ * Collect current resource stats
26
+ */
27
+ private collectStats;
28
+ /**
29
+ * Track encoder creation
30
+ */
31
+ trackEncoderCreated(): void;
32
+ /**
33
+ * Track encoder freed
34
+ */
35
+ trackEncoderFreed(): void;
36
+ /**
37
+ * Track MCP connection opened
38
+ */
39
+ trackMCPConnectionOpened(serviceName: string): void;
40
+ /**
41
+ * Track MCP connection closed
42
+ */
43
+ trackMCPConnectionClosed(serviceName: string): void;
44
+ /**
45
+ * Get current stats
46
+ */
47
+ getCurrentStats(): ResourceStats | null;
48
+ /**
49
+ * Get stats history
50
+ */
51
+ getStatsHistory(): ResourceStats[];
52
+ /**
53
+ * Check if there are potential memory leaks
54
+ */
55
+ checkForLeaks(): {
56
+ hasLeak: boolean;
57
+ reasons: string[];
58
+ };
59
+ /**
60
+ * Force garbage collection if available
61
+ */
62
+ forceGC(): void;
63
+ }
64
+ export declare const resourceMonitor: ResourceMonitor;
65
+ export {};
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Resource Monitor - Track memory usage and potential leaks
3
+ */
4
+ import { logger } from './logger.js';
5
+ class ResourceMonitor {
6
+ constructor() {
7
+ Object.defineProperty(this, "stats", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: []
12
+ });
13
+ Object.defineProperty(this, "maxStatsHistory", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: 100
18
+ });
19
+ Object.defineProperty(this, "activeEncoders", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: 0
24
+ });
25
+ Object.defineProperty(this, "activeMCPConnections", {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value: 0
30
+ });
31
+ Object.defineProperty(this, "monitoringInterval", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: null
36
+ });
37
+ }
38
+ /**
39
+ * Start monitoring resources
40
+ */
41
+ startMonitoring(intervalMs = 30000) {
42
+ if (this.monitoringInterval) {
43
+ return; // Already monitoring
44
+ }
45
+ this.monitoringInterval = setInterval(() => {
46
+ this.collectStats();
47
+ }, intervalMs);
48
+ logger.info('Resource monitoring started');
49
+ }
50
+ /**
51
+ * Stop monitoring resources
52
+ */
53
+ stopMonitoring() {
54
+ if (this.monitoringInterval) {
55
+ clearInterval(this.monitoringInterval);
56
+ this.monitoringInterval = null;
57
+ logger.info('Resource monitoring stopped');
58
+ }
59
+ }
60
+ /**
61
+ * Collect current resource stats
62
+ */
63
+ collectStats() {
64
+ const stats = {
65
+ timestamp: Date.now(),
66
+ memoryUsage: process.memoryUsage(),
67
+ activeEncoders: this.activeEncoders,
68
+ activeMCPConnections: this.activeMCPConnections,
69
+ };
70
+ this.stats.push(stats);
71
+ // Keep only recent history
72
+ if (this.stats.length > this.maxStatsHistory) {
73
+ this.stats.shift();
74
+ }
75
+ // Log warning if memory usage is high
76
+ const heapUsedMB = stats.memoryUsage.heapUsed / 1024 / 1024;
77
+ if (heapUsedMB > 500) {
78
+ logger.warn(`High memory usage detected: ${heapUsedMB.toFixed(2)} MB heap used`);
79
+ }
80
+ // Log debug info periodically (every 5 minutes)
81
+ if (this.stats.length % 10 === 0) {
82
+ logger.info(`Resource stats: Heap ${heapUsedMB.toFixed(2)} MB, Encoders: ${this.activeEncoders}, MCP: ${this.activeMCPConnections}`);
83
+ }
84
+ }
85
+ /**
86
+ * Track encoder creation
87
+ */
88
+ trackEncoderCreated() {
89
+ this.activeEncoders++;
90
+ logger.info(`Encoder created (total: ${this.activeEncoders})`);
91
+ }
92
+ /**
93
+ * Track encoder freed
94
+ */
95
+ trackEncoderFreed() {
96
+ this.activeEncoders--;
97
+ if (this.activeEncoders < 0) {
98
+ logger.warn('Encoder count went negative - possible double-free');
99
+ this.activeEncoders = 0;
100
+ }
101
+ logger.info(`Encoder freed (remaining: ${this.activeEncoders})`);
102
+ }
103
+ /**
104
+ * Track MCP connection opened
105
+ */
106
+ trackMCPConnectionOpened(serviceName) {
107
+ this.activeMCPConnections++;
108
+ logger.info(`MCP connection opened: ${serviceName} (total: ${this.activeMCPConnections})`);
109
+ }
110
+ /**
111
+ * Track MCP connection closed
112
+ */
113
+ trackMCPConnectionClosed(serviceName) {
114
+ this.activeMCPConnections--;
115
+ if (this.activeMCPConnections < 0) {
116
+ logger.warn('MCP connection count went negative - possible double-close');
117
+ this.activeMCPConnections = 0;
118
+ }
119
+ logger.info(`MCP connection closed: ${serviceName} (remaining: ${this.activeMCPConnections})`);
120
+ }
121
+ /**
122
+ * Get current stats
123
+ */
124
+ getCurrentStats() {
125
+ return this.stats.length > 0 ? this.stats[this.stats.length - 1] : null;
126
+ }
127
+ /**
128
+ * Get stats history
129
+ */
130
+ getStatsHistory() {
131
+ return [...this.stats];
132
+ }
133
+ /**
134
+ * Check if there are potential memory leaks
135
+ */
136
+ checkForLeaks() {
137
+ const reasons = [];
138
+ // Check encoder leak
139
+ if (this.activeEncoders > 3) {
140
+ reasons.push(`High encoder count: ${this.activeEncoders} (expected <= 3)`);
141
+ }
142
+ // Check MCP connection leak
143
+ if (this.activeMCPConnections > 5) {
144
+ reasons.push(`High MCP connection count: ${this.activeMCPConnections} (expected <= 5)`);
145
+ }
146
+ // Check memory growth
147
+ if (this.stats.length >= 10) {
148
+ const recent = this.stats.slice(-10);
149
+ const first = recent[0];
150
+ const last = recent[recent.length - 1];
151
+ const growthMB = (last.memoryUsage.heapUsed - first.memoryUsage.heapUsed) / 1024 / 1024;
152
+ if (growthMB > 100) {
153
+ reasons.push(`Memory grew by ${growthMB.toFixed(2)} MB in last 10 samples`);
154
+ }
155
+ }
156
+ return {
157
+ hasLeak: reasons.length > 0,
158
+ reasons,
159
+ };
160
+ }
161
+ /**
162
+ * Force garbage collection if available
163
+ */
164
+ forceGC() {
165
+ if (global.gc) {
166
+ logger.info('Forcing garbage collection');
167
+ global.gc();
168
+ }
169
+ else {
170
+ logger.warn('GC not available - run with --expose-gc flag for manual GC');
171
+ }
172
+ }
173
+ }
174
+ // Singleton instance
175
+ export const resourceMonitor = new ResourceMonitor();
@@ -64,6 +64,12 @@ function isRetriableError(error) {
64
64
  errorMessage.includes('unavailable')) {
65
65
  return true;
66
66
  }
67
+ // Connection terminated by server
68
+ if (errorMessage.includes('terminated') ||
69
+ errorMessage.includes('connection reset') ||
70
+ errorMessage.includes('socket hang up')) {
71
+ return true;
72
+ }
67
73
  return false;
68
74
  }
69
75
  /**
@@ -34,6 +34,7 @@ declare class SessionManager {
34
34
  setCurrentSession(session: Session): void;
35
35
  clearCurrentSession(): void;
36
36
  deleteSession(sessionId: string): Promise<boolean>;
37
+ truncateMessages(messageCount: number): Promise<void>;
37
38
  }
38
39
  export declare const sessionManager: SessionManager;
39
40
  export {};
@@ -168,5 +168,15 @@ class SessionManager {
168
168
  return false;
169
169
  }
170
170
  }
171
+ async truncateMessages(messageCount) {
172
+ if (!this.currentSession) {
173
+ return;
174
+ }
175
+ // Truncate messages array to specified count
176
+ this.currentSession.messages = this.currentSession.messages.slice(0, messageCount);
177
+ this.currentSession.messageCount = this.currentSession.messages.length;
178
+ this.currentSession.updatedAt = Date.now();
179
+ await this.saveSession(this.currentSession);
180
+ }
171
181
  }
172
182
  export const sessionManager = new SessionManager();
@@ -8,10 +8,15 @@ function sanitizeInput(str) {
8
8
  .replace(/\r\n/g, '\n') // Normalize line endings
9
9
  .replace(/\r/g, '\n') // Convert remaining \r to \n
10
10
  .replace(/\t/g, ' ') // Convert tabs to spaces
11
- // Remove focus events - only the complete escape sequences
12
- // ESC[I and ESC[O are focus events, don't confuse with user input like "[I"
11
+ // Remove focus events - complete escape sequences and standalone patterns
12
+ // ESC[I and ESC[O are focus events from terminal focus in/out
13
13
  .replace(/\x1b\[I/g, '')
14
14
  .replace(/\x1b\[O/g, '')
15
+ // Also remove standalone [I and [O if they appear at word boundaries
16
+ // This catches cases where escape sequences arrive fragmented
17
+ // But we preserve legitimate text like "FOO[I]BAR" or "[Inside]"
18
+ .replace(/(?:^|\s)\[I(?:\s|$)/g, ' ')
19
+ .replace(/(?:^|\s)\[O(?:\s|$)/g, ' ')
15
20
  // Remove control characters except newlines
16
21
  .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
17
22
  }
@@ -14,8 +14,8 @@ export interface ToolResult {
14
14
  /**
15
15
  * Execute a single tool call and return the result
16
16
  */
17
- export declare function executeToolCall(toolCall: ToolCall): Promise<ToolResult>;
17
+ export declare function executeToolCall(toolCall: ToolCall, abortSignal?: AbortSignal, onTokenUpdate?: (tokenCount: number) => void): Promise<ToolResult>;
18
18
  /**
19
19
  * Execute multiple tool calls in parallel
20
20
  */
21
- export declare function executeToolCalls(toolCalls: ToolCall[]): Promise<ToolResult[]>;
21
+ export declare function executeToolCalls(toolCalls: ToolCall[], abortSignal?: AbortSignal, onTokenUpdate?: (tokenCount: number) => void): Promise<ToolResult[]>;
@@ -2,10 +2,10 @@ import { executeMCPTool } from './mcpToolsManager.js';
2
2
  /**
3
3
  * Execute a single tool call and return the result
4
4
  */
5
- export async function executeToolCall(toolCall) {
5
+ export async function executeToolCall(toolCall, abortSignal, onTokenUpdate) {
6
6
  try {
7
7
  const args = JSON.parse(toolCall.function.arguments);
8
- const result = await executeMCPTool(toolCall.function.name, args);
8
+ const result = await executeMCPTool(toolCall.function.name, args, abortSignal, onTokenUpdate);
9
9
  return {
10
10
  tool_call_id: toolCall.id,
11
11
  role: 'tool',
@@ -23,6 +23,6 @@ export async function executeToolCall(toolCall) {
23
23
  /**
24
24
  * Execute multiple tool calls in parallel
25
25
  */
26
- export async function executeToolCalls(toolCalls) {
27
- return Promise.all(toolCalls.map(tc => executeToolCall(tc)));
26
+ export async function executeToolCalls(toolCalls, abortSignal, onTokenUpdate) {
27
+ return Promise.all(toolCalls.map(tc => executeToolCall(tc, abortSignal, onTokenUpdate)));
28
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -47,6 +47,7 @@
47
47
  "cli-highlight": "^2.1.11",
48
48
  "diff": "^8.0.2",
49
49
  "figlet": "^1.8.2",
50
+ "https-proxy-agent": "^7.0.6",
50
51
  "ink": "^5.2.1",
51
52
  "ink-gradient": "^3.0.0",
52
53
  "ink-markdown": "^1.0.4",
@@ -54,8 +55,10 @@
54
55
  "ink-spinner": "^5.0.0",
55
56
  "ink-text-input": "^6.0.0",
56
57
  "ink-tree-select": "^2.3.1",
58
+ "jsdom": "^27.0.0",
57
59
  "meow": "^11.0.0",
58
60
  "openai": "^6.1.0",
61
+ "puppeteer-core": "^24.25.0",
59
62
  "react": "^18.2.0",
60
63
  "string-width": "^7.2.0",
61
64
  "tiktoken": "^1.0.22",
@@ -66,6 +69,7 @@
66
69
  "@types/diff": "^7.0.2",
67
70
  "@types/figlet": "^1.7.0",
68
71
  "@types/glob": "^8.1.0",
72
+ "@types/jsdom": "^27.0.0",
69
73
  "@types/react": "^18.0.32",
70
74
  "@types/ws": "^8.5.8",
71
75
  "@vdemedes/prettier-config": "^2.0.1",
@@ -1,161 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Box, Text, useInput } from 'ink';
3
- import Gradient from 'ink-gradient';
4
- import { Select, Alert } from '@inkjs/ui';
5
- import TextInput from 'ink-text-input';
6
- import { getOpenAiConfig, updateOpenAiConfig, validateApiConfig, } from '../../utils/apiConfig.js';
7
- export default function ApiConfigScreen({ onBack, onSave, inlineMode = false }) {
8
- const [baseUrl, setBaseUrl] = useState('');
9
- const [apiKey, setApiKey] = useState('');
10
- const [requestMethod, setRequestMethod] = useState('chat');
11
- const [anthropicBeta, setAnthropicBeta] = useState(false);
12
- const [currentField, setCurrentField] = useState('baseUrl');
13
- const [errors, setErrors] = useState([]);
14
- const [isEditing, setIsEditing] = useState(false);
15
- const requestMethodOptions = [
16
- {
17
- label: 'Chat Completions - Modern chat API (GPT-4, GPT-3.5-turbo)',
18
- value: 'chat',
19
- },
20
- {
21
- label: 'Responses - New responses API (2025, with built-in tools)',
22
- value: 'responses',
23
- },
24
- {
25
- label: 'Gemini - Google Gemini API',
26
- value: 'gemini',
27
- },
28
- {
29
- label: 'Anthropic - Claude API',
30
- value: 'anthropic',
31
- },
32
- ];
33
- useEffect(() => {
34
- const config = getOpenAiConfig();
35
- setBaseUrl(config.baseUrl);
36
- setApiKey(config.apiKey);
37
- setRequestMethod(config.requestMethod || 'chat');
38
- setAnthropicBeta(config.anthropicBeta || false);
39
- }, []);
40
- useInput((input, key) => {
41
- // Allow Escape key to exit Select component without changes
42
- if (isEditing && currentField === 'requestMethod' && key.escape) {
43
- setIsEditing(false);
44
- return;
45
- }
46
- // Don't handle other input when Select component is active
47
- if (isEditing && currentField === 'requestMethod') {
48
- return;
49
- }
50
- // Handle save/exit globally
51
- if (input === 's' && (key.ctrl || key.meta)) {
52
- const validationErrors = validateApiConfig({ baseUrl, apiKey, requestMethod });
53
- if (validationErrors.length === 0) {
54
- updateOpenAiConfig({ baseUrl, apiKey, requestMethod, anthropicBeta });
55
- setErrors([]);
56
- onSave();
57
- }
58
- else {
59
- setErrors(validationErrors);
60
- }
61
- }
62
- else if (key.escape) {
63
- const validationErrors = validateApiConfig({ baseUrl, apiKey, requestMethod });
64
- if (validationErrors.length === 0) {
65
- updateOpenAiConfig({ baseUrl, apiKey, requestMethod, anthropicBeta });
66
- setErrors([]);
67
- }
68
- onBack();
69
- }
70
- else if (key.return) {
71
- if (isEditing) {
72
- // Exit edit mode, return to navigation
73
- setIsEditing(false);
74
- }
75
- else {
76
- // Enter edit mode for current field (toggle for checkbox)
77
- if (currentField === 'anthropicBeta') {
78
- setAnthropicBeta(!anthropicBeta);
79
- }
80
- else {
81
- setIsEditing(true);
82
- }
83
- }
84
- }
85
- else if (!isEditing && key.upArrow) {
86
- if (currentField === 'apiKey') {
87
- setCurrentField('baseUrl');
88
- }
89
- else if (currentField === 'requestMethod') {
90
- setCurrentField('apiKey');
91
- }
92
- else if (currentField === 'anthropicBeta') {
93
- setCurrentField('requestMethod');
94
- }
95
- }
96
- else if (!isEditing && key.downArrow) {
97
- if (currentField === 'baseUrl') {
98
- setCurrentField('apiKey');
99
- }
100
- else if (currentField === 'apiKey') {
101
- setCurrentField('requestMethod');
102
- }
103
- else if (currentField === 'requestMethod') {
104
- setCurrentField('anthropicBeta');
105
- }
106
- }
107
- });
108
- return (React.createElement(Box, { flexDirection: "column", padding: 1 },
109
- !inlineMode && (React.createElement(Box, { marginBottom: 2, borderStyle: "double", borderColor: "cyan", paddingX: 2, paddingY: 1 },
110
- React.createElement(Box, { flexDirection: "column" },
111
- React.createElement(Gradient, { name: "rainbow" }, "OpenAI API Configuration"),
112
- React.createElement(Text, { color: "gray", dimColor: true }, "Configure your OpenAI API settings")))),
113
- React.createElement(Box, { flexDirection: "column", marginBottom: 2 },
114
- React.createElement(Box, { marginBottom: 1 },
115
- React.createElement(Box, { flexDirection: "column" },
116
- React.createElement(Text, { color: currentField === 'baseUrl' ? 'green' : 'white' },
117
- currentField === 'baseUrl' ? '❯ ' : ' ',
118
- "Base URL:"),
119
- currentField === 'baseUrl' && isEditing && (React.createElement(Box, { marginLeft: 3 },
120
- React.createElement(TextInput, { value: baseUrl, onChange: setBaseUrl, placeholder: "https://api.openai.com/v1" }))),
121
- (!isEditing || currentField !== 'baseUrl') && (React.createElement(Box, { marginLeft: 3 },
122
- React.createElement(Text, { color: "gray" }, baseUrl || 'Not set'))))),
123
- React.createElement(Box, { marginBottom: 1 },
124
- React.createElement(Box, { flexDirection: "column" },
125
- React.createElement(Text, { color: currentField === 'apiKey' ? 'green' : 'white' },
126
- currentField === 'apiKey' ? '❯ ' : ' ',
127
- "API Key:"),
128
- currentField === 'apiKey' && isEditing && (React.createElement(Box, { marginLeft: 3 },
129
- React.createElement(TextInput, { value: apiKey, onChange: setApiKey, placeholder: "sk-...", mask: "*" }))),
130
- (!isEditing || currentField !== 'apiKey') && (React.createElement(Box, { marginLeft: 3 },
131
- React.createElement(Text, { color: "gray" }, apiKey ? '*'.repeat(Math.min(apiKey.length, 20)) : 'Not set'))))),
132
- React.createElement(Box, { marginBottom: 1 },
133
- React.createElement(Box, { flexDirection: "column" },
134
- React.createElement(Text, { color: currentField === 'requestMethod' ? 'green' : 'white' },
135
- currentField === 'requestMethod' ? '❯ ' : ' ',
136
- "Request Method:"),
137
- currentField === 'requestMethod' && isEditing && (React.createElement(Box, { marginLeft: 3 },
138
- React.createElement(Select, { options: requestMethodOptions, defaultValue: requestMethod, onChange: (value) => {
139
- setRequestMethod(value);
140
- setIsEditing(false); // Auto exit edit mode after selection
141
- } }))),
142
- (!isEditing || currentField !== 'requestMethod') && (React.createElement(Box, { marginLeft: 3 },
143
- React.createElement(Text, { color: "gray" }, requestMethodOptions.find(opt => opt.value === requestMethod)?.label || 'Not set'))))),
144
- React.createElement(Box, { marginBottom: 1 },
145
- React.createElement(Box, { flexDirection: "column" },
146
- React.createElement(Text, { color: currentField === 'anthropicBeta' ? 'green' : 'white' },
147
- currentField === 'anthropicBeta' ? '❯ ' : ' ',
148
- "Anthropic Beta (for Claude API):"),
149
- React.createElement(Box, { marginLeft: 3 },
150
- React.createElement(Text, { color: "gray" },
151
- anthropicBeta ? '☑ Enabled' : '☐ Disabled',
152
- " (Press Enter to toggle)"))))),
153
- errors.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 2 },
154
- React.createElement(Text, { color: "red", bold: true }, "Errors:"),
155
- errors.map((error, index) => (React.createElement(Text, { key: index, color: "red" },
156
- "\u2022 ",
157
- error))))),
158
- React.createElement(Box, { flexDirection: "column" }, isEditing ? (React.createElement(React.Fragment, null,
159
- React.createElement(Alert, { variant: "info" }, "Editing mode: Press Enter to save and exit editing (Make your changes and press Enter when done)"))) : (React.createElement(React.Fragment, null,
160
- React.createElement(Alert, { variant: "info" }, "Use \u2191\u2193 to navigate between fields, press Enter to edit, and press Ctrl+S or Esc to save and return"))))));
161
- }