claude-ws 0.3.97 → 0.3.99

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 (86) hide show
  1. package/locales/de.json +374 -12
  2. package/locales/en.json +374 -12
  3. package/locales/es.json +398 -11
  4. package/locales/fr.json +398 -11
  5. package/locales/ja.json +398 -11
  6. package/locales/ko.json +398 -11
  7. package/locales/vi.json +374 -12
  8. package/locales/zh.json +398 -11
  9. package/package.json +1 -1
  10. package/server.ts +283 -6
  11. package/src/app/[locale]/not-found.tsx +6 -3
  12. package/src/app/[locale]/page.tsx +14 -4
  13. package/src/app/api/attempts/[id]/workflow/route.ts +76 -0
  14. package/src/app/api/questions/answer/route.ts +58 -0
  15. package/src/app/api/questions/route.ts +68 -0
  16. package/src/app/api/tasks/[id]/compact/route.ts +62 -0
  17. package/src/components/access-anywhere/api-access-key-setup-modal.tsx +2 -2
  18. package/src/components/access-anywhere/tunnel-settings-dialog.tsx +6 -6
  19. package/src/components/access-anywhere/wizard-step-ctunnel.tsx +8 -8
  20. package/src/components/agent-factory/dependency-tree.tsx +5 -3
  21. package/src/components/agent-factory/discovery-dialog.tsx +26 -22
  22. package/src/components/agent-factory/plugin-detail-dialog.tsx +41 -38
  23. package/src/components/agent-factory/plugin-form-dialog.tsx +23 -20
  24. package/src/components/agent-factory/plugin-list.tsx +20 -17
  25. package/src/components/agent-factory/upload-dialog.tsx +17 -14
  26. package/src/components/auth/agent-provider-dialog.tsx +67 -65
  27. package/src/components/auth/api-key-dialog.tsx +14 -11
  28. package/src/components/auth/auth-error-message.tsx +6 -3
  29. package/src/components/editor/code-editor-with-inline-edit.tsx +4 -2
  30. package/src/components/editor/file-diff-resolver-modal.tsx +31 -26
  31. package/src/components/editor/inline-edit-dialog.tsx +9 -6
  32. package/src/components/editor/selection-mention-popup.tsx +3 -1
  33. package/src/components/header/project-selector.tsx +7 -4
  34. package/src/components/header.tsx +70 -4
  35. package/src/components/kanban/column.tsx +11 -0
  36. package/src/components/kanban/task-card.tsx +70 -4
  37. package/src/components/project-settings/component-selector.tsx +3 -1
  38. package/src/components/project-settings/plugin-upload-dialog.tsx +7 -5
  39. package/src/components/project-settings/project-settings-dialog.tsx +5 -3
  40. package/src/components/questions/questions-panel.tsx +136 -0
  41. package/src/components/settings/folder-browser-dialog.tsx +29 -25
  42. package/src/components/settings/settings-page.tsx +64 -18
  43. package/src/components/settings/setup-dialog.tsx +26 -23
  44. package/src/components/setup/unified-setup-wizard.tsx +12 -9
  45. package/src/components/sidebar/file-browser/file-create-buttons.tsx +7 -3
  46. package/src/components/sidebar/file-browser/file-tab-content.tsx +19 -15
  47. package/src/components/sidebar/file-browser/file-tabs-panel.tsx +7 -4
  48. package/src/components/sidebar/file-browser/file-tree.tsx +3 -1
  49. package/src/components/sidebar/git-changes/branch-checkout-modal.tsx +6 -4
  50. package/src/components/sidebar/git-changes/commit-details-modal.tsx +5 -3
  51. package/src/components/sidebar/git-changes/diff-tabs-panel.tsx +3 -1
  52. package/src/components/sidebar/git-changes/git-file-item.tsx +8 -6
  53. package/src/components/sidebar/git-changes/git-graph.tsx +8 -5
  54. package/src/components/sidebar/git-changes/git-panel.tsx +28 -27
  55. package/src/components/sidebar/git-changes/git-section.tsx +5 -3
  56. package/src/components/sidebar/shells/shell-panel.tsx +3 -1
  57. package/src/components/task/attachment-bar.tsx +4 -1
  58. package/src/components/task/attempt-item.tsx +7 -5
  59. package/src/components/task/conversation-view.tsx +21 -13
  60. package/src/components/task/floating-chat-window.tsx +14 -5
  61. package/src/components/task/interactive-command/checkpoint-list.tsx +5 -3
  62. package/src/components/task/interactive-command/confirm-dialog.tsx +9 -4
  63. package/src/components/task/interactive-command/interactive-command-overlay.tsx +23 -9
  64. package/src/components/task/interactive-command/question-prompt.tsx +12 -8
  65. package/src/components/task/pending-question-indicator.tsx +5 -3
  66. package/src/components/task/prompt-input.tsx +1 -1
  67. package/src/components/task/shell-log-view.tsx +3 -1
  68. package/src/components/task/status-line.tsx +84 -23
  69. package/src/components/task/task-detail-panel.tsx +27 -27
  70. package/src/components/task/task-shell-indicator.tsx +10 -6
  71. package/src/components/terminal/terminal-context-menu.tsx +6 -4
  72. package/src/components/terminal/terminal-instance.tsx +11 -3
  73. package/src/components/terminal/terminal-panel.tsx +6 -3
  74. package/src/components/terminal/terminal-shortcut-bar.tsx +3 -1
  75. package/src/components/terminal/terminal-tab-bar.tsx +5 -3
  76. package/src/components/workflow/workflow-panel.tsx +181 -0
  77. package/src/hooks/use-attempt-stream.ts +96 -3
  78. package/src/lib/agent-manager.ts +89 -3
  79. package/src/lib/db/index.ts +18 -0
  80. package/src/lib/db/schema.ts +29 -0
  81. package/src/lib/process-manager.ts +28 -7
  82. package/src/lib/session-manager.ts +60 -0
  83. package/src/lib/usage-tracker.ts +19 -19
  84. package/src/lib/workflow-tracker.ts +118 -20
  85. package/src/stores/questions-store.ts +76 -0
  86. package/src/stores/workflow-store.ts +71 -0
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useRef } from 'react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import {
5
6
  Dialog,
6
7
  DialogContent,
@@ -26,6 +27,8 @@ interface UploadDialogProps {
26
27
  }
27
28
 
28
29
  export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDialogProps) {
30
+ const t = useTranslations('agentFactory');
31
+ const tCommon = useTranslations('common');
29
32
  const [step, setStep] = useState<'upload' | 'preview' | 'importing'>('upload');
30
33
  const [uploading, setUploading] = useState(false);
31
34
  const [error, setError] = useState<string | null>(null);
@@ -72,7 +75,7 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
72
75
  const isValidExtension = validExtensions.some(ext => fileName.endsWith(ext));
73
76
 
74
77
  if (!isValidExtension) {
75
- setError('Invalid file type. Please upload a .zip, .tar, .gz, .gzip, or .tgz file.');
78
+ setError(t('invalidFileType'));
76
79
  return;
77
80
  }
78
81
 
@@ -209,7 +212,7 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
209
212
  const isValidExtension = validExtensions.some(ext => fileName.endsWith(ext));
210
213
 
211
214
  if (!isValidExtension) {
212
- setError('Invalid file type. Please upload a .zip, .tar, .gz, .gzip, or .tgz file.');
215
+ setError(t('invalidFileType'));
213
216
  return;
214
217
  }
215
218
 
@@ -296,12 +299,12 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
296
299
  <DialogHeader>
297
300
  <DialogTitle className="flex items-center gap-2">
298
301
  <FileArchive className="w-5 h-5" />
299
- {step === 'preview' ? 'Confirm Import' : 'Import Plugins from Archive'}
302
+ {step === 'preview' ? t('confirmImport') : t('importFromArchive')}
300
303
  </DialogTitle>
301
304
  <DialogDescription>
302
305
  {step === 'preview'
303
306
  ? `Review ${previewItems.length} plugin(s) found in ${uploadedFileName}`
304
- : 'Upload a .zip, .tar, .gz, .gzip, or .tgz file containing plugins to import.'
307
+ : t('supportedFormats')
305
308
  }
306
309
  </DialogDescription>
307
310
  </DialogHeader>
@@ -330,15 +333,15 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
330
333
  {uploading ? (
331
334
  <div className="flex flex-col items-center gap-3">
332
335
  <Loader2 className="w-12 h-12 animate-spin text-muted-foreground" />
333
- <p className="text-muted-foreground">Analyzing archive...</p>
336
+ <p className="text-muted-foreground">{t('analyzingArchive')}</p>
334
337
  </div>
335
338
  ) : (
336
339
  <div className="flex flex-col items-center gap-3">
337
340
  <Upload className="w-12 h-12 text-muted-foreground" />
338
341
  <div>
339
- <p className="font-medium">Click to upload or drag and drop</p>
342
+ <p className="font-medium">{t('clickToUploadOrDrag')}</p>
340
343
  <p className="text-sm text-muted-foreground mt-1">
341
- .zip, .tar, .gz, .gzip, or .tgz files
344
+ {t('supportedFormats')}
342
345
  </p>
343
346
  </div>
344
347
  </div>
@@ -355,7 +358,7 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
355
358
 
356
359
  {/* Info */}
357
360
  <div className="text-sm text-muted-foreground bg-muted/50 p-3 rounded-lg">
358
- <p className="font-medium mb-2">Automatic Organization:</p>
361
+ <p className="font-medium mb-2">{t('automaticOrganization')}</p>
359
362
  <p className="text-xs mb-2">
360
363
  Files will be automatically organized into the correct folders:
361
364
  </p>
@@ -376,7 +379,7 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
376
379
  {/* Preview List */}
377
380
  <div className="border rounded-lg overflow-hidden">
378
381
  <div className="bg-muted/50 px-3 py-2 text-sm font-medium border-b">
379
- Items to import ({previewItems.length})
382
+ {t('itemsToImport')} ({previewItems.length})
380
383
  </div>
381
384
  <div className="max-h-[300px] overflow-y-auto">
382
385
  {previewItems.map((item, index) => (
@@ -406,7 +409,7 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
406
409
  <div className="flex items-start gap-2 p-3 bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-200 rounded-lg text-sm">
407
410
  <Check className="w-4 h-4 mt-0.5 flex-shrink-0" />
408
411
  <span>
409
- Archive analyzed successfully. Click <strong>Import</strong> to add these plugins to your Agent Factory.
412
+ {t('archiveAnalyzed')}
410
413
  </span>
411
414
  </div>
412
415
  </>
@@ -415,7 +418,7 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
415
418
  {step === 'importing' && (
416
419
  <div className="flex flex-col items-center gap-4 py-8">
417
420
  <Loader2 className="w-12 h-12 animate-spin text-muted-foreground" />
418
- <p className="text-muted-foreground">Importing plugins...</p>
421
+ <p className="text-muted-foreground">{t('importingPlugins')}</p>
419
422
  </div>
420
423
  )}
421
424
  </div>
@@ -425,7 +428,7 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
425
428
  <>
426
429
  <Button variant="outline" onClick={handleCancel} disabled={uploading}>
427
430
  <X className="w-4 h-4 mr-1" />
428
- Cancel
431
+ {tCommon('cancel')}
429
432
  </Button>
430
433
  <Button
431
434
  variant="outline"
@@ -434,7 +437,7 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
434
437
  title="Import to ~/.claude (globally available)"
435
438
  >
436
439
  <Globe className="w-4 h-4 mr-1" />
437
- Import Globally
440
+ {t('importGlobally')}
438
441
  </Button>
439
442
  <Button onClick={() => handleConfirmImport(false)} disabled={uploading}>
440
443
  <Check className="w-4 h-4 mr-1" />
@@ -443,7 +446,7 @@ export function UploadDialog({ open, onOpenChange, onUploadSuccess }: UploadDial
443
446
  </>
444
447
  ) : (
445
448
  <Button variant="outline" onClick={() => onOpenChange(false)} disabled={uploading}>
446
- Cancel
449
+ {tCommon('cancel')}
447
450
  </Button>
448
451
  )}
449
452
  </div>
@@ -71,6 +71,7 @@ interface AgentProviderSetupFormProps {
71
71
 
72
72
  export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormProps) {
73
73
  const t = useTranslations('agentProvider');
74
+ const tCommon = useTranslations('common');
74
75
  const [selectedOption, setSelectedOption] = useState<ProviderOption | null>(null);
75
76
  const [config, setConfig] = useState<ProviderConfig>({
76
77
  ANTHROPIC_AUTH_TOKEN: '',
@@ -165,7 +166,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
165
166
 
166
167
  const handleOAuthLogin = () => {
167
168
  window.open('https://claude.ai/login', '_blank');
168
- setError('After logging in on claude.ai, run "claude login" in your terminal to authenticate.');
169
+ setError(t('afterLoginHint'));
169
170
  };
170
171
 
171
172
  const handleConsoleSetup = () => {
@@ -313,21 +314,21 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
313
314
  </div>
314
315
  <div className="flex-1">
315
316
  <div className="flex items-center gap-2">
316
- <span className="font-medium">Login with Claude Account</span>
317
+ <span className="font-medium">{t('loginWithClaude')}</span>
317
318
  {providers.oauth.configured && (
318
319
  <span className="inline-flex items-center gap-1 text-xs text-green-600 dark:text-green-400">
319
320
  <Check className="h-3 w-3" />
320
- Configured
321
+ {tCommon('configured')}
321
322
  </span>
322
323
  )}
323
324
  {providers.oauth.isDefault && (
324
325
  <span className="text-xs bg-primary/10 text-primary px-1.5 py-0.5 rounded">
325
- Default
326
+ {tCommon('default')}
326
327
  </span>
327
328
  )}
328
329
  </div>
329
330
  <div className="text-sm text-muted-foreground mt-1">
330
- For Claude Pro, Max, Team, or Enterprise subscribers
331
+ {t('forClaudeSubscribers')}
331
332
  </div>
332
333
  </div>
333
334
  </div>
@@ -349,21 +350,21 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
349
350
  </div>
350
351
  <div className="flex-1">
351
352
  <div className="flex items-center gap-2">
352
- <span className="font-medium">Anthropic Console Account</span>
353
+ <span className="font-medium">{t('anthropicConsole')}</span>
353
354
  {providers.console.configured && (
354
355
  <span className="inline-flex items-center gap-1 text-xs text-green-600 dark:text-green-400">
355
356
  <Check className="h-3 w-3" />
356
- Configured
357
+ {tCommon('configured')}
357
358
  </span>
358
359
  )}
359
360
  {providers.console.isDefault && (
360
361
  <span className="text-xs bg-primary/10 text-primary px-1.5 py-0.5 rounded">
361
- Default
362
+ {tCommon('default')}
362
363
  </span>
363
364
  )}
364
365
  </div>
365
366
  <div className="text-sm text-muted-foreground mt-1">
366
- Pay-as-you-go API usage billing
367
+ {t('payAsYouGo')}
367
368
  </div>
368
369
  </div>
369
370
  </div>
@@ -385,21 +386,21 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
385
386
  </div>
386
387
  <div className="flex-1">
387
388
  <div className="flex items-center gap-2">
388
- <span className="font-medium">Claude Code Settings</span>
389
+ <span className="font-medium">{t('claudeCodeSettings')}</span>
389
390
  {providers.settings.configured && (
390
391
  <span className="inline-flex items-center gap-1 text-xs text-green-600 dark:text-green-400">
391
392
  <Check className="h-3 w-3" />
392
- Configured
393
+ {tCommon('configured')}
393
394
  </span>
394
395
  )}
395
396
  {providers.settings.isDefault && (
396
397
  <span className="text-xs bg-primary/10 text-primary px-1.5 py-0.5 rounded">
397
- Default
398
+ {tCommon('default')}
398
399
  </span>
399
400
  )}
400
401
  </div>
401
402
  <div className="text-sm text-muted-foreground mt-1">
402
- Use settings.json configuration
403
+ {t('useSettingsJson')}
403
404
  </div>
404
405
  </div>
405
406
  </div>
@@ -421,21 +422,21 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
421
422
  </div>
422
423
  <div className="flex-1">
423
424
  <div className="flex items-center gap-2">
424
- <span className="font-medium">Custom API Key</span>
425
+ <span className="font-medium">{t('customApiKey')}</span>
425
426
  {providers.custom.configured && (
426
427
  <span className="inline-flex items-center gap-1 text-xs text-green-600 dark:text-green-400">
427
428
  <Check className="h-3 w-3" />
428
- Configured
429
+ {tCommon('configured')}
429
430
  </span>
430
431
  )}
431
432
  {providers.custom.isDefault && (
432
433
  <span className="text-xs bg-primary/10 text-primary px-1.5 py-0.5 rounded">
433
- Default
434
+ {tCommon('default')}
434
435
  </span>
435
436
  )}
436
437
  </div>
437
438
  <div className="text-sm text-muted-foreground mt-1">
438
- Use your own Anthropic API key
439
+ {t('useOwnApiKey')}
439
440
  </div>
440
441
  </div>
441
442
  </div>
@@ -503,14 +504,14 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
503
504
  // OAuth instructions view
504
505
  <div className="space-y-4 py-4">
505
506
  <div className="p-4 rounded-lg bg-muted/50">
506
- <h4 className="font-medium mb-2">How to login with OAuth:</h4>
507
+ <h4 className="font-medium mb-2">{t('howToLoginOAuth')}</h4>
507
508
  <ol className="text-sm text-muted-foreground space-y-2 list-decimal list-inside">
508
- <li>Make sure you have a Claude Pro, Max, Team, or Enterprise subscription</li>
509
- <li>Open a terminal on your server</li>
510
- <li>Run: <code className="px-1.5 py-0.5 rounded bg-muted font-mono text-xs">claude /login</code></li>
511
- <li>Choose <code className="px-1.5 py-0.5 rounded bg-muted font-mono text-xs">1. Claude account with subscription · Pro, Max, Team, or Enterprise</code></li>
512
- <li>Follow the browser authentication flow</li>
513
- <li>Restart the application after login</li>
509
+ <li>{t('ensureSubscription')}</li>
510
+ <li>{t('openTerminal')}</li>
511
+ <li>{t('runClaudeLogin')} <code className="px-1.5 py-0.5 rounded bg-muted font-mono text-xs">claude /login</code></li>
512
+ <li>{t('chooseOAuthOption')}</li>
513
+ <li>{t('followAuthFlow')}</li>
514
+ <li>{t('restartAfterLogin')}</li>
514
515
  </ol>
515
516
  </div>
516
517
  {error && (
@@ -522,10 +523,10 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
522
523
 
523
524
  <div className="flex gap-2">
524
525
  <Button variant="ghost" onClick={handleBack} className="flex-1">
525
- Back
526
+ {tCommon('back')}
526
527
  </Button>
527
528
  <Button onClick={handleReload} className="flex-1">
528
- I&apos;ve Logged In - Reload
529
+ {t('loggedInReload')}
529
530
  </Button>
530
531
  </div>
531
532
  </div>
@@ -533,22 +534,22 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
533
534
  // Console setup view
534
535
  <div className="space-y-4 py-4">
535
536
  <div className="p-4 rounded-lg bg-muted/50">
536
- <h4 className="font-medium mb-2">How to use Anthropic Console account:</h4>
537
+ <h4 className="font-medium mb-2">{t('howToUseConsole')}</h4>
537
538
  <ol className="text-sm text-muted-foreground space-y-2 list-decimal list-inside">
538
- <li>Make sure you have a Claude Pro, Max, Team, or Enterprise subscription</li>
539
- <li>Open a terminal on your server</li>
540
- <li>Run: <code className="px-1.5 py-0.5 rounded bg-muted font-mono text-xs">claude /login</code></li>
541
- <li>Choose <code className="px-1.5 py-0.5 rounded bg-muted font-mono text-xs">2. Anthropic Console account · API usage billing</code></li>
542
- <li>Follow the browser authentication flow</li>
543
- <li>Restart the application after login</li>
539
+ <li>{t('ensureSubscription')}</li>
540
+ <li>{t('openTerminal')}</li>
541
+ <li>{t('runClaudeLogin')} <code className="px-1.5 py-0.5 rounded bg-muted font-mono text-xs">claude /login</code></li>
542
+ <li>{t('chooseConsoleOption')}</li>
543
+ <li>{t('followAuthFlow')}</li>
544
+ <li>{t('restartAfterLogin')}</li>
544
545
  </ol>
545
546
  </div>
546
547
  <div className="flex gap-2">
547
548
  <Button variant="ghost" onClick={handleBack} className="flex-1">
548
- Back
549
+ {tCommon('back')}
549
550
  </Button>
550
551
  <Button onClick={handleReload} className="flex-1">
551
- I&apos;ve Logged In - Reload
552
+ {t('loggedInReload')}
552
553
  </Button>
553
554
  </div>
554
555
  </div>
@@ -556,18 +557,18 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
556
557
  // Settings.json info view
557
558
  <div className="space-y-4 py-4">
558
559
  <div className="p-4 rounded-lg bg-muted/50">
559
- <h4 className="font-medium mb-2">Using Claude Code Settings</h4>
560
+ <h4 className="font-medium mb-2">{t('usingClaudeCodeSettings')}</h4>
560
561
  <div className="text-sm text-muted-foreground space-y-2">
561
562
  <p>
562
- This method uses the configuration from your Claude Code settings file:
563
+ {t('settingsFileDescription')}
563
564
  </p>
564
565
  <ul className="list-disc list-inside pl-2 space-y-1 text-xs">
565
- <li><strong>Linux/macOS:</strong> <code className="px-1 rounded bg-muted font-mono">~/.claude/settings.json</code></li>
566
- <li><strong>Windows:</strong> <code className="px-1 rounded bg-muted font-mono">%USERPROFILE%\.claude\settings.json</code></li>
566
+ <li><strong>{t('linuxMacOs')}</strong> <code className="px-1 rounded bg-muted font-mono">~/.claude/settings.json</code></li>
567
+ <li><strong>{t('windows')}</strong> <code className="px-1 rounded bg-muted font-mono">%USERPROFILE%\.claude\settings.json</code></li>
567
568
  </ul>
568
- <p className="mt-2">The following environment variables are read from the <code className="px-1 rounded bg-muted font-mono text-xs">env</code> section:</p>
569
+ <p className="mt-2">{t('envVarsDescription')}</p>
569
570
  <ul className="list-disc list-inside pl-2 space-y-1 text-xs">
570
- <li><code className="px-1 rounded bg-muted font-mono">ANTHROPIC_AUTH_TOKEN</code> <span className="text-destructive">*required</span></li>
571
+ <li><code className="px-1 rounded bg-muted font-mono">ANTHROPIC_AUTH_TOKEN</code> <span className="text-destructive">{t('required')}</span></li>
571
572
  <li><code className="px-1 rounded bg-muted font-mono">ANTHROPIC_MODEL</code></li>
572
573
  <li><code className="px-1 rounded bg-muted font-mono">ANTHROPIC_BASE_URL</code></li>
573
574
  <li><code className="px-1 rounded bg-muted font-mono">ANTHROPIC_DEFAULT_HAIKU_MODEL</code></li>
@@ -577,21 +578,21 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
577
578
  </ul>
578
579
  {providers.settings.configured ? (
579
580
  <p className="text-green-600 dark:text-green-400 mt-2">
580
- Settings.json is configured and will be used for authentication.
581
+ {t('settingsConfigured')}
581
582
  </p>
582
583
  ) : (
583
584
  <p className="text-amber-600 dark:text-amber-400 mt-2">
584
- Settings.json does not contain ANTHROPIC_AUTH_TOKEN. Add it to use this method.
585
+ {t('settingsNotConfigured')}
585
586
  </p>
586
587
  )}
587
588
  </div>
588
589
  </div>
589
590
  <div className="flex gap-2">
590
591
  <Button variant="ghost" onClick={handleBack} className="flex-1">
591
- Back
592
+ {tCommon('back')}
592
593
  </Button>
593
594
  <Button onClick={handleReload} className="flex-1">
594
- Reload to Apply
595
+ {t('reloadToApply')}
595
596
  </Button>
596
597
  </div>
597
598
  </div>
@@ -601,7 +602,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
601
602
  {/* API Key - Required */}
602
603
  <div className="space-y-2">
603
604
  <Label htmlFor="api-key" className="text-sm font-medium">
604
- API Key {!hasExistingKey && <span className="text-destructive">*</span>}
605
+ {t('customApiKey')} {!hasExistingKey && <span className="text-destructive">*</span>}
605
606
  </Label>
606
607
  <div className="relative">
607
608
  <Key className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
@@ -610,7 +611,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
610
611
  type="password"
611
612
  value={config.ANTHROPIC_AUTH_TOKEN}
612
613
  onChange={(e) => handleConfigChange('ANTHROPIC_AUTH_TOKEN', e.target.value)}
613
- placeholder={hasExistingKey ? "Leave empty to keep existing key" : "Enter API key..."}
614
+ placeholder={hasExistingKey ? t('leaveEmptyToKeep') : "Enter API key..."}
614
615
  className="pl-8"
615
616
  disabled={loading}
616
617
  autoFocus
@@ -618,7 +619,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
618
619
  </div>
619
620
  {hasExistingKey && (
620
621
  <p className="text-xs text-muted-foreground">
621
- An API key is already configured. Leave empty to keep it, or enter a new one to replace it.
622
+ {t('existingKeyHint')}
622
623
  </p>
623
624
  )}
624
625
  </div>
@@ -633,13 +634,13 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
633
634
  disabled={loading}
634
635
  >
635
636
  <RotateCcw className="h-4 w-4 mr-2" />
636
- Fill Default Values
637
+ {t('fillDefaultValues')}
637
638
  </Button>
638
639
 
639
640
  {/* Base URL */}
640
641
  <div className="space-y-2">
641
642
  <Label htmlFor="base-url" className="text-sm font-medium">
642
- Base URL
643
+ {t('baseUrl')}
643
644
  </Label>
644
645
  <Input
645
646
  id="base-url"
@@ -654,7 +655,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
654
655
  {/* Model */}
655
656
  <div className="space-y-2">
656
657
  <Label htmlFor="model" className="text-sm font-medium">
657
- Default Model
658
+ {t('defaultModel')}
658
659
  </Label>
659
660
  <Input
660
661
  id="model"
@@ -670,7 +671,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
670
671
  <div className="grid grid-cols-3 gap-2">
671
672
  <div className="space-y-1">
672
673
  <Label htmlFor="haiku-model" className="text-xs font-medium">
673
- Haiku Model
674
+ {t('haikuModel')}
674
675
  </Label>
675
676
  <Input
676
677
  id="haiku-model"
@@ -684,7 +685,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
684
685
  </div>
685
686
  <div className="space-y-1">
686
687
  <Label htmlFor="sonnet-model" className="text-xs font-medium">
687
- Sonnet Model
688
+ {t('sonnetModel')}
688
689
  </Label>
689
690
  <Input
690
691
  id="sonnet-model"
@@ -698,7 +699,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
698
699
  </div>
699
700
  <div className="space-y-1">
700
701
  <Label htmlFor="opus-model" className="text-xs font-medium">
701
- Opus Model
702
+ {t('opusModel')}
702
703
  </Label>
703
704
  <Input
704
705
  id="opus-model"
@@ -715,7 +716,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
715
716
  {/* Timeout */}
716
717
  <div className="space-y-2">
717
718
  <Label htmlFor="timeout" className="text-sm font-medium">
718
- API Timeout (ms)
719
+ {t('apiTimeout')}
719
720
  </Label>
720
721
  <Input
721
722
  id="timeout"
@@ -728,7 +729,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
728
729
  </div>
729
730
 
730
731
  <p className="text-xs text-muted-foreground">
731
- Configuration will be saved to .env file in the app directory. Empty fields will use default values.
732
+ {t('configSavedToEnv')}
732
733
  </p>
733
734
 
734
735
  {error && (
@@ -740,7 +741,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
740
741
 
741
742
  <div className="flex gap-2">
742
743
  <Button variant="ghost" onClick={handleBack} disabled={loading}>
743
- Back
744
+ {tCommon('back')}
744
745
  </Button>
745
746
  {hasExistingKey && !showDismissConfirm && (
746
747
  <Button
@@ -748,19 +749,19 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
748
749
  onClick={() => setShowDismissConfirm(true)}
749
750
  disabled={loading}
750
751
  >
751
- Dismiss This Provider
752
+ {t('dismissProvider')}
752
753
  </Button>
753
754
  )}
754
755
  {showDismissConfirm && (
755
756
  <div className="flex items-center gap-2">
756
- <span className="text-sm text-destructive">Are you sure?</span>
757
+ <span className="text-sm text-destructive">{t('areYouSure')}</span>
757
758
  <Button
758
759
  variant="outline"
759
760
  size="sm"
760
761
  onClick={() => setShowDismissConfirm(false)}
761
762
  disabled={loading}
762
763
  >
763
- No
764
+ {tCommon('no')}
764
765
  </Button>
765
766
  <Button
766
767
  variant="destructive"
@@ -768,7 +769,7 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
768
769
  onClick={handleDismissMethod}
769
770
  disabled={loading}
770
771
  >
771
- Yes, Dismiss
772
+ {t('yesDismiss')}
772
773
  </Button>
773
774
  </div>
774
775
  )}
@@ -781,10 +782,10 @@ export function AgentProviderSetupForm({ onComplete }: AgentProviderSetupFormPro
781
782
  {loading ? (
782
783
  <>
783
784
  <Loader2 className="h-4 w-4 mr-2 animate-spin" />
784
- Saving...
785
+ {tCommon('saving')}
785
786
  </>
786
787
  ) : (
787
- 'Save Configuration'
788
+ t('saveConfiguration')
788
789
  )}
789
790
  </Button>
790
791
  )}
@@ -801,13 +802,14 @@ interface AgentProviderDialogProps {
801
802
  }
802
803
 
803
804
  export function AgentProviderDialog({ open, onOpenChange }: AgentProviderDialogProps) {
805
+ const t = useTranslations('agentProvider');
804
806
  return (
805
807
  <Dialog open={open} onOpenChange={onOpenChange}>
806
808
  <DialogContent className="sm:max-w-[500px] z-[9999] max-h-[90vh] overflow-y-auto !grid !grid-rows-[auto_1fr]">
807
809
  <DialogHeader>
808
- <DialogTitle>Configure Agent Provider</DialogTitle>
810
+ <DialogTitle>{t('configureTitle')}</DialogTitle>
809
811
  <DialogDescription>
810
- Choose how you want to authenticate with Claude API:
812
+ {t('configureDescription')}
811
813
  </DialogDescription>
812
814
  </DialogHeader>
813
815
 
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useEffect } from 'react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import { Key, AlertCircle, Check } from 'lucide-react';
5
6
  import {
6
7
  Dialog,
@@ -95,6 +96,8 @@ async function verifyApiKey(apiKey: string): Promise<boolean> {
95
96
  }
96
97
 
97
98
  export function ApiKeyDialog({ open, onOpenChange, onSuccess }: ApiKeyDialogProps) {
99
+ const t = useTranslations('auth');
100
+ const tCommon = useTranslations('common');
98
101
  const [apiKey, setApiKey] = useState('');
99
102
  const [loading, setLoading] = useState(false);
100
103
  const [error, setError] = useState('');
@@ -112,7 +115,7 @@ export function ApiKeyDialog({ open, onOpenChange, onSuccess }: ApiKeyDialogProp
112
115
  setError('');
113
116
 
114
117
  if (!apiKey.trim()) {
115
- setError('API key is required');
118
+ setError(t('apiKeyIsRequired'));
116
119
  return;
117
120
  }
118
121
 
@@ -125,10 +128,10 @@ export function ApiKeyDialog({ open, onOpenChange, onSuccess }: ApiKeyDialogProp
125
128
  onOpenChange(false);
126
129
  onSuccess();
127
130
  } else {
128
- setError('Invalid API key');
131
+ setError(t('invalidApiKey'));
129
132
  }
130
133
  } catch {
131
- setError('Failed to verify API key');
134
+ setError(t('failedToVerifyApiKey'));
132
135
  } finally {
133
136
  setLoading(false);
134
137
  }
@@ -138,9 +141,9 @@ export function ApiKeyDialog({ open, onOpenChange, onSuccess }: ApiKeyDialogProp
138
141
  <Dialog open={open} onOpenChange={onOpenChange}>
139
142
  <DialogContent className="sm:max-w-[450px] z-[9999]">
140
143
  <DialogHeader>
141
- <DialogTitle>API Key Required</DialogTitle>
144
+ <DialogTitle>{t('apiKeyRequired')}</DialogTitle>
142
145
  <DialogDescription>
143
- This server requires an API key for access. Enter your key to continue.
146
+ {t('serverRequiresApiKey')}
144
147
  </DialogDescription>
145
148
  </DialogHeader>
146
149
 
@@ -148,7 +151,7 @@ export function ApiKeyDialog({ open, onOpenChange, onSuccess }: ApiKeyDialogProp
148
151
  {/* API Key Input */}
149
152
  <div className="space-y-2">
150
153
  <label htmlFor="api-key" className="text-sm font-medium">
151
- API Key
154
+ {t('apiKey')}
152
155
  </label>
153
156
  <div className="relative">
154
157
  <Key className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
@@ -157,14 +160,14 @@ export function ApiKeyDialog({ open, onOpenChange, onSuccess }: ApiKeyDialogProp
157
160
  type="password"
158
161
  value={apiKey}
159
162
  onChange={(e) => setApiKey(e.target.value)}
160
- placeholder="Enter your API key"
163
+ placeholder={t('enterYourApiKey')}
161
164
  className="pl-8"
162
165
  disabled={loading}
163
166
  autoFocus
164
167
  />
165
168
  </div>
166
169
  <p className="text-xs text-muted-foreground">
167
- Your API key will be stored locally in your browser
170
+ {t('apiKeyStoredLocally')}
168
171
  </p>
169
172
  </div>
170
173
 
@@ -180,7 +183,7 @@ export function ApiKeyDialog({ open, onOpenChange, onSuccess }: ApiKeyDialogProp
180
183
  {!error && apiKey && (
181
184
  <div className="flex items-center gap-2 text-sm text-muted-foreground">
182
185
  <Check className="h-4 w-4 text-muted-foreground" />
183
- Press Enter or click Submit to verify
186
+ {t('pressEnterOrSubmit')}
184
187
  </div>
185
188
  )}
186
189
 
@@ -192,10 +195,10 @@ export function ApiKeyDialog({ open, onOpenChange, onSuccess }: ApiKeyDialogProp
192
195
  onClick={() => onOpenChange(false)}
193
196
  disabled={loading}
194
197
  >
195
- Cancel
198
+ {tCommon('cancel')}
196
199
  </Button>
197
200
  <Button type="submit" disabled={loading || !apiKey}>
198
- {loading ? 'Verifying...' : 'Submit'}
201
+ {loading ? tCommon('verifying') : tCommon('submit')}
199
202
  </Button>
200
203
  </div>
201
204
  </form>