snow-ai 0.2.25 → 0.2.27

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.
@@ -3,8 +3,10 @@ import { Box, Text, useInput } from 'ink';
3
3
  import Gradient from 'ink-gradient';
4
4
  import { Select, Alert, Spinner } from '@inkjs/ui';
5
5
  import TextInput from 'ink-text-input';
6
+ import chalk from 'chalk';
6
7
  import { getOpenAiConfig, updateOpenAiConfig, validateApiConfig, } from '../../utils/apiConfig.js';
7
8
  import { fetchAvailableModels, filterModels, } from '../../api/models.js';
9
+ import { getActiveProfileName, getAllProfiles, switchProfile, createProfile, deleteProfile, saveProfile, } from '../../utils/configManager.js';
8
10
  const focusEventTokenRegex = /(?:\x1b)?\[[0-9;]*[IO]/g;
9
11
  const isFocusEventInput = (value) => {
10
12
  if (!value) {
@@ -38,6 +40,11 @@ const stripFocusArtifacts = (value) => {
38
40
  .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
39
41
  };
40
42
  export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
43
+ // Profile management
44
+ const [profiles, setProfiles] = useState([]);
45
+ const [activeProfile, setActiveProfile] = useState('');
46
+ const [profileMode, setProfileMode] = useState('normal');
47
+ const [newProfileName, setNewProfileName] = useState('');
41
48
  // API settings
42
49
  const [baseUrl, setBaseUrl] = useState('');
43
50
  const [apiKey, setApiKey] = useState('');
@@ -50,7 +57,7 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
50
57
  const [maxTokens, setMaxTokens] = useState(4096);
51
58
  const [compactModelName, setCompactModelName] = useState('');
52
59
  // UI state
53
- const [currentField, setCurrentField] = useState('baseUrl');
60
+ const [currentField, setCurrentField] = useState('profile');
54
61
  const [errors, setErrors] = useState([]);
55
62
  const [isEditing, setIsEditing] = useState(false);
56
63
  const [models, setModels] = useState([]);
@@ -78,6 +85,13 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
78
85
  },
79
86
  ];
80
87
  useEffect(() => {
88
+ loadProfilesAndConfig();
89
+ }, []);
90
+ const loadProfilesAndConfig = () => {
91
+ // Load profiles
92
+ const loadedProfiles = getAllProfiles();
93
+ setProfiles(loadedProfiles);
94
+ // Load current config
81
95
  const config = getOpenAiConfig();
82
96
  setBaseUrl(config.baseUrl);
83
97
  setApiKey(config.apiKey);
@@ -88,7 +102,8 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
88
102
  setMaxContextTokens(config.maxContextTokens || 4000);
89
103
  setMaxTokens(config.maxTokens || 4096);
90
104
  setCompactModelName(config.compactModel?.modelName || '');
91
- }, []);
105
+ setActiveProfile(getActiveProfileName());
106
+ };
92
107
  const loadModels = async () => {
93
108
  setLoading(true);
94
109
  setLoadError('');
@@ -124,6 +139,8 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
124
139
  ];
125
140
  };
126
141
  const getCurrentValue = () => {
142
+ if (currentField === 'profile')
143
+ return activeProfile;
127
144
  if (currentField === 'baseUrl')
128
145
  return baseUrl;
129
146
  if (currentField === 'apiKey')
@@ -140,6 +157,83 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
140
157
  return compactModelName;
141
158
  return '';
142
159
  };
160
+ const handleProfileChange = (value) => {
161
+ if (value === '__CREATE_NEW__') {
162
+ setProfileMode('creating');
163
+ setNewProfileName('');
164
+ return;
165
+ }
166
+ if (value === '__DELETE__') {
167
+ if (activeProfile === 'default') {
168
+ setErrors(['Cannot delete the default profile']);
169
+ return;
170
+ }
171
+ setProfileMode('deleting');
172
+ return;
173
+ }
174
+ // Switch profile
175
+ try {
176
+ switchProfile(value);
177
+ loadProfilesAndConfig();
178
+ setIsEditing(false);
179
+ setErrors([]);
180
+ }
181
+ catch (err) {
182
+ setErrors([
183
+ err instanceof Error ? err.message : 'Failed to switch profile',
184
+ ]);
185
+ }
186
+ };
187
+ const handleCreateProfile = () => {
188
+ const cleaned = stripFocusArtifacts(newProfileName).trim();
189
+ if (!cleaned) {
190
+ setErrors(['Profile name cannot be empty']);
191
+ return;
192
+ }
193
+ try {
194
+ // Create new profile with current config
195
+ const currentConfig = {
196
+ snowcfg: {
197
+ baseUrl,
198
+ apiKey,
199
+ requestMethod,
200
+ anthropicBeta,
201
+ advancedModel,
202
+ basicModel,
203
+ maxContextTokens,
204
+ maxTokens,
205
+ compactModel: compactModelName ? { modelName: compactModelName } : undefined,
206
+ },
207
+ };
208
+ createProfile(cleaned, currentConfig);
209
+ switchProfile(cleaned);
210
+ loadProfilesAndConfig();
211
+ setProfileMode('normal');
212
+ setNewProfileName('');
213
+ setIsEditing(false);
214
+ setErrors([]);
215
+ }
216
+ catch (err) {
217
+ setErrors([
218
+ err instanceof Error ? err.message : 'Failed to create profile',
219
+ ]);
220
+ }
221
+ };
222
+ const handleDeleteProfile = () => {
223
+ try {
224
+ deleteProfile(activeProfile);
225
+ loadProfilesAndConfig();
226
+ setProfileMode('normal');
227
+ setIsEditing(false);
228
+ setErrors([]);
229
+ }
230
+ catch (err) {
231
+ setErrors([
232
+ err instanceof Error ? err.message : 'Failed to delete profile',
233
+ ]);
234
+ setProfileMode('normal');
235
+ }
236
+ };
143
237
  const handleModelChange = (value) => {
144
238
  if (value === '__MANUAL_INPUT__') {
145
239
  setManualInputMode(true);
@@ -181,7 +275,30 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
181
275
  modelName: compactModelName,
182
276
  };
183
277
  }
278
+ // Save to main config
184
279
  updateOpenAiConfig(config);
280
+ // Also save to the current profile
281
+ try {
282
+ const fullConfig = {
283
+ snowcfg: {
284
+ baseUrl,
285
+ apiKey,
286
+ requestMethod,
287
+ anthropicBeta,
288
+ advancedModel,
289
+ basicModel,
290
+ maxContextTokens,
291
+ maxTokens,
292
+ compactModel: compactModelName
293
+ ? { modelName: compactModelName }
294
+ : undefined,
295
+ },
296
+ };
297
+ saveProfile(activeProfile, fullConfig);
298
+ }
299
+ catch (err) {
300
+ console.error('Failed to save profile:', err);
301
+ }
185
302
  setErrors([]);
186
303
  return true;
187
304
  }
@@ -198,6 +315,29 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
198
315
  if (isFocusEventInput(rawInput)) {
199
316
  return;
200
317
  }
318
+ // Handle profile creation mode
319
+ if (profileMode === 'creating') {
320
+ if (key.return) {
321
+ handleCreateProfile();
322
+ }
323
+ else if (key.escape) {
324
+ setProfileMode('normal');
325
+ setNewProfileName('');
326
+ setErrors([]);
327
+ }
328
+ return;
329
+ }
330
+ // Handle profile deletion confirmation
331
+ if (profileMode === 'deleting') {
332
+ if (input === 'y' || input === 'Y') {
333
+ handleDeleteProfile();
334
+ }
335
+ else if (input === 'n' || input === 'N' || key.escape) {
336
+ setProfileMode('normal');
337
+ setErrors([]);
338
+ }
339
+ return;
340
+ }
201
341
  // Handle loading state
202
342
  if (loading) {
203
343
  if (key.escape) {
@@ -239,7 +379,8 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
239
379
  }
240
380
  // Allow Escape key to exit Select component
241
381
  if (isEditing &&
242
- (currentField === 'requestMethod' ||
382
+ (currentField === 'profile' ||
383
+ currentField === 'requestMethod' ||
243
384
  currentField === 'advancedModel' ||
244
385
  currentField === 'basicModel' ||
245
386
  currentField === 'compactModelName') &&
@@ -360,15 +501,16 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
360
501
  }
361
502
  else if (!isEditing && key.upArrow) {
362
503
  const fields = [
504
+ 'profile',
363
505
  'baseUrl',
364
506
  'apiKey',
365
507
  'requestMethod',
366
508
  'anthropicBeta',
367
509
  'advancedModel',
368
510
  'basicModel',
511
+ 'compactModelName',
369
512
  'maxContextTokens',
370
513
  'maxTokens',
371
- 'compactModelName',
372
514
  ];
373
515
  const currentIndex = fields.indexOf(currentField);
374
516
  if (currentIndex > 0) {
@@ -377,15 +519,16 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
377
519
  }
378
520
  else if (!isEditing && key.downArrow) {
379
521
  const fields = [
522
+ 'profile',
380
523
  'baseUrl',
381
524
  'apiKey',
382
525
  'requestMethod',
383
526
  'anthropicBeta',
384
527
  'advancedModel',
385
528
  'basicModel',
529
+ 'compactModelName',
386
530
  'maxContextTokens',
387
531
  'maxTokens',
388
- 'compactModelName',
389
532
  ];
390
533
  const currentIndex = fields.indexOf(currentField);
391
534
  if (currentIndex < fields.length - 1) {
@@ -393,6 +536,40 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
393
536
  }
394
537
  }
395
538
  });
539
+ // Render profile creation mode
540
+ if (profileMode === 'creating') {
541
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
542
+ !inlineMode && (React.createElement(Box, { marginBottom: 1, borderStyle: "double", borderColor: 'cyan', paddingX: 2 },
543
+ React.createElement(Box, { flexDirection: "column" },
544
+ React.createElement(Gradient, { name: "rainbow" }, "Create New Profile"),
545
+ React.createElement(Text, { color: "gray", dimColor: true }, "Enter a name for the new configuration profile")))),
546
+ React.createElement(Box, { flexDirection: "column" },
547
+ React.createElement(Text, { color: "cyan" }, "Profile Name:"),
548
+ React.createElement(Box, { marginLeft: 2 },
549
+ React.createElement(TextInput, { value: newProfileName, onChange: value => setNewProfileName(stripFocusArtifacts(value)), placeholder: "e.g., work, personal, test" }))),
550
+ errors.length > 0 && (React.createElement(Box, { marginTop: 1 },
551
+ React.createElement(Text, { color: "red" }, errors[0]))),
552
+ React.createElement(Box, { marginTop: 1 },
553
+ React.createElement(Alert, { variant: "info" }, "Press Enter to create, Esc to cancel"))));
554
+ }
555
+ // Render profile deletion confirmation
556
+ if (profileMode === 'deleting') {
557
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
558
+ !inlineMode && (React.createElement(Box, { marginBottom: 1, borderStyle: "double", borderColor: 'cyan', paddingX: 2 },
559
+ React.createElement(Box, { flexDirection: "column" },
560
+ React.createElement(Gradient, { name: "rainbow" }, "Delete Profile"),
561
+ React.createElement(Text, { color: "gray", dimColor: true }, "Confirm profile deletion")))),
562
+ React.createElement(Box, { flexDirection: "column" },
563
+ React.createElement(Text, { color: "yellow" },
564
+ "Are you sure you want to delete the profile \"",
565
+ activeProfile,
566
+ "\"?"),
567
+ React.createElement(Text, { color: "gray", dimColor: true }, "This action cannot be undone. You will be switched to the default profile.")),
568
+ errors.length > 0 && (React.createElement(Box, { marginTop: 1 },
569
+ React.createElement(Text, { color: "red" }, errors[0]))),
570
+ React.createElement(Box, { marginTop: 1 },
571
+ React.createElement(Alert, { variant: "warning" }, "Press Y to confirm, N or Esc to cancel"))));
572
+ }
396
573
  if (loading) {
397
574
  return (React.createElement(Box, { flexDirection: "column", padding: 1 },
398
575
  !inlineMode && (React.createElement(Box, { marginBottom: 1, borderStyle: "double", borderColor: 'cyan', paddingX: 2 },
@@ -434,9 +611,33 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
434
611
  !inlineMode && (React.createElement(Box, { marginBottom: 1, borderStyle: "double", borderColor: 'cyan', paddingX: 2 },
435
612
  React.createElement(Box, { flexDirection: "column" },
436
613
  React.createElement(Gradient, { name: "rainbow" }, "API & Model Configuration"),
437
- React.createElement(Text, { color: "gray", dimColor: true }, "Configure your API settings and AI models")))),
614
+ React.createElement(Text, { color: "gray", dimColor: true }, "Configure your API settings and AI models"),
615
+ activeProfile && (React.createElement(Text, { color: "cyan", dimColor: true },
616
+ "Active Profile: ",
617
+ activeProfile))))),
438
618
  React.createElement(Box, { flexDirection: "column" },
439
- React.createElement(Text, { color: "cyan", bold: true }, "API Settings:"),
619
+ React.createElement(Box, { flexDirection: "column" },
620
+ React.createElement(Text, { color: currentField === 'profile' ? 'green' : 'white' },
621
+ currentField === 'profile' ? '❯ ' : ' ',
622
+ "Profile:"),
623
+ currentField === 'profile' && isEditing && (React.createElement(Box, { marginLeft: 3 },
624
+ React.createElement(Select, { options: [
625
+ ...profiles.map(p => ({
626
+ label: `${p.displayName}${p.isActive ? ' (Active)' : ''}`,
627
+ value: p.name,
628
+ })),
629
+ {
630
+ label: chalk.green('+ New Profile'),
631
+ value: '__CREATE_NEW__',
632
+ },
633
+ {
634
+ label: chalk.red('🆇 Delete Profile'),
635
+ value: '__DELETE__',
636
+ },
637
+ ], defaultValue: activeProfile, onChange: handleProfileChange }))),
638
+ (!isEditing || currentField !== 'profile') && (React.createElement(Box, { marginLeft: 3 },
639
+ React.createElement(Text, { color: "gray" }, profiles.find(p => p.name === activeProfile)?.displayName ||
640
+ activeProfile)))),
440
641
  React.createElement(Box, { flexDirection: "column" },
441
642
  React.createElement(Text, { color: currentField === 'baseUrl' ? 'green' : 'white' },
442
643
  currentField === 'baseUrl' ? '❯ ' : ' ',
@@ -465,19 +666,18 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
465
666
  (!isEditing || currentField !== 'requestMethod') && (React.createElement(Box, { marginLeft: 3 },
466
667
  React.createElement(Text, { color: "gray" }, requestMethodOptions.find(opt => opt.value === requestMethod)
467
668
  ?.label || 'Not set')))),
468
- React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
669
+ React.createElement(Box, { flexDirection: "column" },
469
670
  React.createElement(Text, { color: currentField === 'anthropicBeta' ? 'green' : 'white' },
470
671
  currentField === 'anthropicBeta' ? '❯ ' : ' ',
471
- "Anthropic Beta (for Claude API):"),
672
+ "Anthropic Beta:"),
472
673
  React.createElement(Box, { marginLeft: 3 },
473
674
  React.createElement(Text, { color: "gray" },
474
675
  anthropicBeta ? '☑ Enabled' : '☐ Disabled',
475
676
  " (Press Enter to toggle)"))),
476
- React.createElement(Text, { color: "cyan", bold: true }, "Model Settings:"),
477
677
  React.createElement(Box, { flexDirection: "column" },
478
678
  React.createElement(Text, { color: currentField === 'advancedModel' ? 'green' : 'white' },
479
679
  currentField === 'advancedModel' ? '❯ ' : ' ',
480
- "Advanced Model (Main Work):"),
680
+ "Advanced Model:"),
481
681
  currentField === 'advancedModel' && isEditing && (React.createElement(Box, { marginLeft: 3 },
482
682
  React.createElement(Box, { flexDirection: "column" },
483
683
  searchTerm && React.createElement(Text, { color: "cyan" },
@@ -489,7 +689,7 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
489
689
  React.createElement(Box, { flexDirection: "column" },
490
690
  React.createElement(Text, { color: currentField === 'basicModel' ? 'green' : 'white' },
491
691
  currentField === 'basicModel' ? '❯ ' : ' ',
492
- "Basic Model (Summary & Analysis):"),
692
+ "Basic Model:"),
493
693
  currentField === 'basicModel' && isEditing && (React.createElement(Box, { marginLeft: 3 },
494
694
  React.createElement(Box, { flexDirection: "column" },
495
695
  searchTerm && React.createElement(Text, { color: "cyan" },
@@ -498,39 +698,38 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
498
698
  React.createElement(Select, { options: getCurrentOptions(), defaultValue: getCurrentValue(), onChange: handleModelChange })))),
499
699
  (!isEditing || currentField !== 'basicModel') && (React.createElement(Box, { marginLeft: 3 },
500
700
  React.createElement(Text, { color: "gray" }, basicModel || 'Not set')))),
701
+ React.createElement(Box, { flexDirection: "column" },
702
+ React.createElement(Text, { color: currentField === 'compactModelName' ? 'green' : 'white' },
703
+ currentField === 'compactModelName' ? '❯ ' : ' ',
704
+ "Compact Model:"),
705
+ currentField === 'compactModelName' && isEditing && (React.createElement(Box, { marginLeft: 3 },
706
+ React.createElement(Box, { flexDirection: "column" },
707
+ searchTerm && React.createElement(Text, { color: "cyan" },
708
+ "Filter: ",
709
+ searchTerm),
710
+ React.createElement(Select, { options: getCurrentOptions(), defaultValue: getCurrentValue(), onChange: handleModelChange })))),
711
+ (!isEditing || currentField !== 'compactModelName') && (React.createElement(Box, { marginLeft: 3 },
712
+ React.createElement(Text, { color: "gray" }, compactModelName || 'Not set')))),
501
713
  React.createElement(Box, { flexDirection: "column" },
502
714
  React.createElement(Text, { color: currentField === 'maxContextTokens' ? 'green' : 'white' },
503
715
  currentField === 'maxContextTokens' ? '❯ ' : ' ',
504
- "Max Context Tokens (Auto-compress when reached):"),
716
+ "Max Context Tokens:"),
505
717
  currentField === 'maxContextTokens' && isEditing && (React.createElement(Box, { marginLeft: 3 },
506
718
  React.createElement(Text, { color: "cyan" },
507
719
  "Enter value: ",
508
720
  maxContextTokens))),
509
721
  (!isEditing || currentField !== 'maxContextTokens') && (React.createElement(Box, { marginLeft: 3 },
510
722
  React.createElement(Text, { color: "gray" }, maxContextTokens)))),
511
- React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
723
+ React.createElement(Box, { flexDirection: "column" },
512
724
  React.createElement(Text, { color: currentField === 'maxTokens' ? 'green' : 'white' },
513
725
  currentField === 'maxTokens' ? '❯ ' : ' ',
514
- "Max Tokens (Max tokens for single response):"),
726
+ "Max Tokens:"),
515
727
  currentField === 'maxTokens' && isEditing && (React.createElement(Box, { marginLeft: 3 },
516
728
  React.createElement(Text, { color: "cyan" },
517
729
  "Enter value: ",
518
730
  maxTokens))),
519
731
  (!isEditing || currentField !== 'maxTokens') && (React.createElement(Box, { marginLeft: 3 },
520
- React.createElement(Text, { color: "gray" }, maxTokens)))),
521
- React.createElement(Text, { color: "cyan", bold: true }, "Compact Model (Context Compression):"),
522
- React.createElement(Box, { flexDirection: "column" },
523
- React.createElement(Text, { color: currentField === 'compactModelName' ? 'green' : 'white' },
524
- currentField === 'compactModelName' ? '❯ ' : ' ',
525
- "Model Name (uses same API credentials):"),
526
- currentField === 'compactModelName' && isEditing && (React.createElement(Box, { marginLeft: 3 },
527
- React.createElement(Box, { flexDirection: "column" },
528
- searchTerm && React.createElement(Text, { color: "cyan" },
529
- "Filter: ",
530
- searchTerm),
531
- React.createElement(Select, { options: getCurrentOptions(), defaultValue: getCurrentValue(), onChange: handleModelChange })))),
532
- (!isEditing || currentField !== 'compactModelName') && (React.createElement(Box, { marginLeft: 3 },
533
- React.createElement(Text, { color: "gray" }, compactModelName || 'Not set'))))),
732
+ React.createElement(Text, { color: "gray" }, maxTokens))))),
534
733
  errors.length > 0 && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
535
734
  React.createElement(Text, { color: "red", bold: true }, "Errors:"),
536
735
  errors.map((error, index) => (React.createElement(Text, { key: index, color: "red" },
@@ -24,7 +24,7 @@ export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
24
24
  {
25
25
  label: 'API & Model Settings',
26
26
  value: 'config',
27
- infoText: 'Configure API settings and AI models',
27
+ infoText: 'Configure API settings, AI models, and manage profiles',
28
28
  },
29
29
  {
30
30
  label: 'Proxy & Browser Settings',
@@ -112,6 +112,18 @@ export function updateOpenAiConfig(apiConfig) {
112
112
  snowcfg: { ...currentConfig.snowcfg, ...apiConfig },
113
113
  };
114
114
  saveConfig(updatedConfig);
115
+ // Also save to the active profile if profiles system is initialized
116
+ try {
117
+ // Dynamic import to avoid circular dependencies
118
+ const { getActiveProfileName, saveProfile } = require('./configManager.js');
119
+ const activeProfileName = getActiveProfileName();
120
+ if (activeProfileName) {
121
+ saveProfile(activeProfileName, updatedConfig);
122
+ }
123
+ }
124
+ catch {
125
+ // Profiles system not available yet (during initialization), skip sync
126
+ }
115
127
  }
116
128
  export function getOpenAiConfig() {
117
129
  const config = loadConfig();
@@ -0,0 +1,45 @@
1
+ import { type AppConfig } from './apiConfig.js';
2
+ export interface ConfigProfile {
3
+ name: string;
4
+ displayName: string;
5
+ isActive: boolean;
6
+ config: AppConfig;
7
+ }
8
+ /**
9
+ * Get the current active profile name
10
+ */
11
+ export declare function getActiveProfileName(): string;
12
+ /**
13
+ * Load a specific profile
14
+ */
15
+ export declare function loadProfile(profileName: string): AppConfig | undefined;
16
+ /**
17
+ * Save a profile
18
+ */
19
+ export declare function saveProfile(profileName: string, config: AppConfig): void;
20
+ /**
21
+ * Get all available profiles
22
+ */
23
+ export declare function getAllProfiles(): ConfigProfile[];
24
+ /**
25
+ * Switch to a different profile
26
+ * This copies the profile config to config.json and updates the active profile
27
+ */
28
+ export declare function switchProfile(profileName: string): void;
29
+ /**
30
+ * Create a new profile
31
+ */
32
+ export declare function createProfile(profileName: string, config?: AppConfig): void;
33
+ /**
34
+ * Delete a profile
35
+ */
36
+ export declare function deleteProfile(profileName: string): void;
37
+ /**
38
+ * Rename a profile
39
+ */
40
+ export declare function renameProfile(oldName: string, newName: string): void;
41
+ /**
42
+ * Initialize profiles system
43
+ * This should be called on app startup to ensure profiles are set up
44
+ */
45
+ export declare function initializeProfiles(): void;