commons-proxy 2.0.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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +757 -0
  3. package/bin/cli.js +146 -0
  4. package/package.json +97 -0
  5. package/public/Complaint Details.pdf +0 -0
  6. package/public/Cyber Crime Portal.pdf +0 -0
  7. package/public/app.js +229 -0
  8. package/public/css/src/input.css +523 -0
  9. package/public/css/style.css +1 -0
  10. package/public/favicon.png +0 -0
  11. package/public/index.html +549 -0
  12. package/public/js/components/account-manager.js +356 -0
  13. package/public/js/components/add-account-modal.js +414 -0
  14. package/public/js/components/claude-config.js +420 -0
  15. package/public/js/components/dashboard/charts.js +605 -0
  16. package/public/js/components/dashboard/filters.js +362 -0
  17. package/public/js/components/dashboard/stats.js +110 -0
  18. package/public/js/components/dashboard.js +236 -0
  19. package/public/js/components/logs-viewer.js +100 -0
  20. package/public/js/components/models.js +36 -0
  21. package/public/js/components/server-config.js +349 -0
  22. package/public/js/config/constants.js +102 -0
  23. package/public/js/data-store.js +375 -0
  24. package/public/js/settings-store.js +58 -0
  25. package/public/js/store.js +99 -0
  26. package/public/js/translations/en.js +367 -0
  27. package/public/js/translations/id.js +412 -0
  28. package/public/js/translations/pt.js +308 -0
  29. package/public/js/translations/tr.js +358 -0
  30. package/public/js/translations/zh.js +373 -0
  31. package/public/js/utils/account-actions.js +189 -0
  32. package/public/js/utils/error-handler.js +96 -0
  33. package/public/js/utils/model-config.js +42 -0
  34. package/public/js/utils/ui-logger.js +143 -0
  35. package/public/js/utils/validators.js +77 -0
  36. package/public/js/utils.js +69 -0
  37. package/public/proxy-server-64.png +0 -0
  38. package/public/views/accounts.html +361 -0
  39. package/public/views/dashboard.html +484 -0
  40. package/public/views/logs.html +97 -0
  41. package/public/views/models.html +331 -0
  42. package/public/views/settings.html +1327 -0
  43. package/src/account-manager/credentials.js +378 -0
  44. package/src/account-manager/index.js +462 -0
  45. package/src/account-manager/onboarding.js +112 -0
  46. package/src/account-manager/rate-limits.js +369 -0
  47. package/src/account-manager/storage.js +160 -0
  48. package/src/account-manager/strategies/base-strategy.js +109 -0
  49. package/src/account-manager/strategies/hybrid-strategy.js +339 -0
  50. package/src/account-manager/strategies/index.js +79 -0
  51. package/src/account-manager/strategies/round-robin-strategy.js +76 -0
  52. package/src/account-manager/strategies/sticky-strategy.js +138 -0
  53. package/src/account-manager/strategies/trackers/health-tracker.js +162 -0
  54. package/src/account-manager/strategies/trackers/index.js +9 -0
  55. package/src/account-manager/strategies/trackers/quota-tracker.js +120 -0
  56. package/src/account-manager/strategies/trackers/token-bucket-tracker.js +155 -0
  57. package/src/auth/database.js +169 -0
  58. package/src/auth/oauth.js +548 -0
  59. package/src/auth/token-extractor.js +117 -0
  60. package/src/cli/accounts.js +648 -0
  61. package/src/cloudcode/index.js +29 -0
  62. package/src/cloudcode/message-handler.js +510 -0
  63. package/src/cloudcode/model-api.js +248 -0
  64. package/src/cloudcode/rate-limit-parser.js +235 -0
  65. package/src/cloudcode/request-builder.js +93 -0
  66. package/src/cloudcode/session-manager.js +47 -0
  67. package/src/cloudcode/sse-parser.js +121 -0
  68. package/src/cloudcode/sse-streamer.js +293 -0
  69. package/src/cloudcode/streaming-handler.js +615 -0
  70. package/src/config.js +125 -0
  71. package/src/constants.js +407 -0
  72. package/src/errors.js +242 -0
  73. package/src/fallback-config.js +29 -0
  74. package/src/format/content-converter.js +193 -0
  75. package/src/format/index.js +20 -0
  76. package/src/format/request-converter.js +255 -0
  77. package/src/format/response-converter.js +120 -0
  78. package/src/format/schema-sanitizer.js +673 -0
  79. package/src/format/signature-cache.js +88 -0
  80. package/src/format/thinking-utils.js +648 -0
  81. package/src/index.js +148 -0
  82. package/src/modules/usage-stats.js +205 -0
  83. package/src/providers/anthropic-provider.js +258 -0
  84. package/src/providers/base-provider.js +157 -0
  85. package/src/providers/cloudcode.js +94 -0
  86. package/src/providers/copilot.js +399 -0
  87. package/src/providers/github-provider.js +287 -0
  88. package/src/providers/google-provider.js +192 -0
  89. package/src/providers/index.js +211 -0
  90. package/src/providers/openai-compatible.js +265 -0
  91. package/src/providers/openai-provider.js +271 -0
  92. package/src/providers/openrouter-provider.js +325 -0
  93. package/src/providers/setup.js +83 -0
  94. package/src/server.js +870 -0
  95. package/src/utils/claude-config.js +245 -0
  96. package/src/utils/helpers.js +51 -0
  97. package/src/utils/logger.js +142 -0
  98. package/src/utils/native-module-helper.js +162 -0
  99. package/src/webui/index.js +1134 -0
@@ -0,0 +1,412 @@
1
+ /**
2
+ * Indonesian (Bahasa Indonesia) Translations
3
+ *
4
+ * Panduan Terminologi IT:
5
+ * - Istilah teknis umum tetap dalam bahasa Inggris (OAuth, API, token, proxy, debug, cache, dll.)
6
+ * - Istilah UI umum diterjemahkan (Simpan, Hapus, Batal, dll.)
7
+ * - Pesan error dan status menggunakan campuran yang konsisten
8
+ */
9
+ window.translations = window.translations || {};
10
+ window.translations.id = {
11
+ // Navigation
12
+ dashboard: "Dashboard",
13
+ models: "Model",
14
+ accounts: "Akun",
15
+ logs: "Log",
16
+ settings: "Pengaturan",
17
+
18
+ // Status
19
+ online: "ONLINE",
20
+ offline: "OFFLINE",
21
+ totalAccounts: "TOTAL AKUN",
22
+ active: "AKTIF",
23
+ operational: "Berjalan Normal",
24
+ rateLimited: "DIBATASI",
25
+ quotasDepleted: "{count}/{total} Kuota Habis",
26
+ quotasDepletedTitle: "KUOTA HABIS",
27
+ outOfTracked: "Dari {total} Terpantau",
28
+ cooldown: "Cooldown",
29
+
30
+ // Search & Filters
31
+ searchPlaceholder: "Cari model...",
32
+ allAccounts: "Semua Akun",
33
+ stat: "STATUS",
34
+ modelIdentity: "IDENTITAS MODEL",
35
+ globalQuota: "KUOTA GLOBAL",
36
+ nextReset: "RESET BERIKUTNYA",
37
+ distribution: "DISTRIBUSI AKUN",
38
+
39
+ // System Configuration
40
+ systemConfig: "Konfigurasi Sistem",
41
+ language: "Bahasa",
42
+ pollingInterval: "Interval Polling",
43
+ maxDisplayLogs: "Maks. Log Ditampilkan",
44
+ showExhausted: "Tampilkan Model Habis",
45
+ showExhaustedDesc: "Tampilkan model meskipun kuotanya sudah 0%.",
46
+ compactMode: "Mode Ringkas",
47
+ compactModeDesc: "Tampilan tabel lebih padat untuk informasi lebih banyak.",
48
+ saveChanges: "Simpan Perubahan",
49
+
50
+ // Logs
51
+ autoScroll: "Auto-scroll",
52
+ clearLogs: "Hapus Log",
53
+
54
+ // Account Management
55
+ accountManagement: "Kelola Akun",
56
+ manageTokens: "Kelola token akun Google dan status otorisasi",
57
+ addAccount: "Tambah Akun",
58
+ status: "STATUS",
59
+ enabled: "AKTIF",
60
+ health: "STATUS",
61
+ accountEmail: "AKUN (EMAIL)",
62
+ source: "SUMBER",
63
+ projectId: "PROJECT ID",
64
+ sessionState: "STATUS SESI",
65
+ operations: "AKSI",
66
+ delete: "Hapus",
67
+ confirmDelete: "Yakin ingin menghapus akun ini?",
68
+ cannotDeleteDatabase: "Tidak bisa dihapus: Akun ini dari database CommonsProxy (read-only)",
69
+ connectGoogle: "Hubungkan Akun Google",
70
+ reauthenticated: "diautentikasi ulang",
71
+ added: "ditambahkan",
72
+ successfully: "berhasil",
73
+ accountAddedSuccess: "Akun berhasil ditambahkan",
74
+ accountReauthSuccess: "Akun berhasil diautentikasi ulang",
75
+ failedToGetAuthUrl: "Gagal mendapatkan URL autentikasi",
76
+ failedToStartOAuth: "Gagal memulai proses OAuth",
77
+ oauthInProgress: "Proses OAuth sedang berjalan. Silakan selesaikan autentikasi di jendela popup...",
78
+ family: "Jenis",
79
+ model: "Model",
80
+ activeSuffix: "Aktif",
81
+ manualReload: "Muat Ulang Konfigurasi",
82
+
83
+ // Tabs
84
+ tabInterface: "Tampilan",
85
+ tabClaude: "Claude CLI",
86
+ tabModels: "Model",
87
+ tabServer: "Server",
88
+
89
+ // Dashboard
90
+ linkedAccounts: "Akun Terhubung",
91
+ noSignal: "TIDAK ADA SINYAL",
92
+ establishingUplink: "MENGHUBUNGKAN...",
93
+ goToAccounts: "Lihat Akun",
94
+
95
+ // Settings - Models
96
+ modelsDesc: "Atur visibilitas model, pin, dan routing request.",
97
+ modelsPageDesc: "Kuota dan status real-time untuk semua model yang tersedia.",
98
+ showHidden: "Tampilkan Model Tersembunyi",
99
+ hideHidden: "Sembunyikan Model Tersembunyi",
100
+ hiddenOn: "Tersembunyi: YA",
101
+ hiddenOff: "Tersembunyi: TIDAK",
102
+ modelId: "ID Model",
103
+ actions: "Aksi",
104
+ pinToTop: "Pin ke atas",
105
+ toggleVisibility: "Tampilkan/Sembunyikan",
106
+ noModels: "MODEL TIDAK DITEMUKAN",
107
+ modelMappingHint: "Routing model di sisi server. Pengguna Claude Code: lihat tab 'Claude CLI' untuk pengaturan di sisi klien.",
108
+ modelMapping: "Mapping (Target Model ID)",
109
+
110
+ // Settings - Claude
111
+ proxyConnection: "Koneksi Proxy",
112
+ modelSelection: "Pilihan Model",
113
+ defaultModelAliases: "ALIAS MODEL DEFAULT",
114
+ opusAlias: "Alias Opus",
115
+ sonnetAlias: "Alias Sonnet",
116
+ haikuAlias: "Alias Haiku",
117
+ claudeSettingsAlertPrefix: "Pengaturan di bawah langsung mengubah",
118
+ claudeSettingsAlertSuffix: "Restart Claude CLI untuk menerapkan.",
119
+ applyToClaude: "Simpan ke Claude CLI",
120
+
121
+ // Presets
122
+ configPresets: "Preset Konfigurasi",
123
+ saveAsPreset: "Simpan sebagai Preset",
124
+ deletePreset: "Hapus Preset",
125
+ loadPreset: "Muat preset",
126
+ load: "Muat",
127
+ presetHint: "Pilih preset untuk dimuat. Klik \"Simpan ke Claude CLI\" untuk menyimpan.",
128
+ presetLoaded: "Preset dimuat. Klik \"Simpan ke Claude CLI\" untuk menyimpan.",
129
+ presetSaved: "Preset tersimpan",
130
+ presetDeleted: "Preset terhapus",
131
+ unsavedChangesTitle: "Ada Perubahan Belum Disimpan",
132
+ unsavedChangesMessage: "Konfigurasi saat ini tidak cocok dengan preset tersimpan. Jika beralih, perubahan akan hilang.",
133
+ loadAnyway: "Tetap Muat",
134
+ savePresetTitle: "Simpan Preset",
135
+ savePresetDesc: "Simpan konfigurasi saat ini sebagai preset.",
136
+ presetName: "Nama Preset",
137
+ presetNamePlaceholder: "contoh: Setup Kantor",
138
+ savePreset: "Simpan Preset",
139
+
140
+ // Settings - Server
141
+ port: "Port",
142
+ uiVersion: "Versi UI",
143
+ debugMode: "Mode Debug",
144
+ environment: "Environment",
145
+ serverReadOnly: "Pengaturan dikelola melalui config.json. Restart server untuk menerapkan.",
146
+ advancedSettings: "Pengaturan Lanjutan",
147
+ reloadConfigTitle: "Muat Ulang Konfigurasi Akun",
148
+ reloadConfigDesc: "Paksa muat ulang accounts.json dari disk",
149
+ reload: "Muat Ulang",
150
+
151
+ // Config Specific
152
+ primaryModel: "Model Utama",
153
+ subAgentModel: "Model Sub-agent",
154
+ advancedOverrides: "Timpa Model Default",
155
+ opusModel: "Model Opus",
156
+ sonnetModel: "Model Sonnet",
157
+ haikuModel: "Model Haiku",
158
+ authToken: "Token Auth",
159
+ saveConfig: "Simpan ke Claude CLI",
160
+ envVar: "Env",
161
+
162
+ // System
163
+ systemName: "COMMONSPROXY",
164
+ systemDesc: "CLAUDE PROXY SYSTEM",
165
+ connectGoogleDesc: "Hubungkan akun Google Workspace untuk meningkatkan kuota API. Akun akan digunakan untuk meneruskan request Claude via CommonsProxy.",
166
+ useCliCommand: "Gunakan Perintah CLI",
167
+ close: "Tutup",
168
+ requestVolume: "Volume Request",
169
+ filter: "Filter",
170
+ all: "Semua",
171
+ none: "Tidak Ada",
172
+ noDataTracked: "Belum ada data tercatat",
173
+ selectFamilies: "Pilih jenis model untuk ditampilkan",
174
+ selectModels: "Pilih model untuk ditampilkan",
175
+ noLogsMatch: "Tidak ada log yang sesuai filter",
176
+ connecting: "MENGHUBUNGKAN",
177
+ main: "Utama",
178
+ system: "Sistem",
179
+ refreshData: "Refresh Data",
180
+ connectionLost: "Koneksi Terputus",
181
+ lastUpdated: "Terakhir Diperbarui",
182
+ grepLogs: "cari log...",
183
+ noMatchingModels: "Tidak ada model yang cocok",
184
+ typeToSearch: "Ketik untuk mencari...",
185
+ or: "ATAU",
186
+
187
+ // Account Operations
188
+ refreshingAccount: "Memperbarui {email}...",
189
+ refreshedAccount: "{email} berhasil diperbarui",
190
+ refreshFailed: "Gagal memperbarui",
191
+ accountToggled: "Akun {email} {status}",
192
+ toggleFailed: "Gagal mengubah status",
193
+ reauthenticating: "Autentikasi ulang {email}...",
194
+ authUrlFailed: "Gagal mendapatkan URL autentikasi",
195
+ deletedAccount: "{email} berhasil dihapus",
196
+ deleteFailed: "Gagal menghapus",
197
+ accountsReloaded: "Akun berhasil dimuat ulang",
198
+ reloadFailed: "Gagal memuat ulang",
199
+
200
+ // Claude Config
201
+ claudeConfigSaved: "Konfigurasi Claude tersimpan",
202
+ claudeConfigRestored: "Claude CLI dikembalikan ke default",
203
+ saveConfigFailed: "Gagal menyimpan konfigurasi",
204
+ restoreConfigFailed: "Gagal mengembalikan konfigurasi",
205
+ restoreDefault: "Reset ke Default",
206
+ confirmRestoreTitle: "Konfirmasi Reset",
207
+ confirmRestoreMessage: "Yakin ingin mengembalikan Claude CLI ke pengaturan default? Konfigurasi proxy akan dihapus.",
208
+ confirmRestore: "Ya, Reset",
209
+
210
+ // Status Labels
211
+ claudeActive: "Claude Aktif",
212
+ claudeEmpty: "Claude Kosong",
213
+ geminiActive: "Gemini Aktif",
214
+ geminiEmpty: "Gemini Kosong",
215
+ synced: "SINKRON",
216
+ syncing: "SINKRONISASI...",
217
+
218
+ // Time range labels
219
+ last1Hour: "1 Jam Terakhir",
220
+ last6Hours: "6 Jam Terakhir",
221
+ last24Hours: "24 Jam Terakhir",
222
+ last7Days: "7 Hari Terakhir",
223
+ allTime: "Semua Waktu",
224
+ groupBy: "Kelompokkan",
225
+
226
+ // Additional
227
+ reloading: "Memuat ulang...",
228
+ reloaded: "Dimuat ulang",
229
+ lines: "baris",
230
+ enabledSeeLogs: "Aktif (Lihat Log)",
231
+ production: "Produksi",
232
+ configSaved: "Konfigurasi Tersimpan",
233
+ enterPassword: "Masukkan Password Web UI:",
234
+ ready: "SIAP",
235
+ depleted: "Habis",
236
+ timeH: "j",
237
+ timeM: "m",
238
+ familyClaude: "Claude",
239
+ familyGemini: "Gemini",
240
+ familyOther: "Lainnya",
241
+ enabledStatus: "diaktifkan",
242
+ disabledStatus: "dinonaktifkan",
243
+ logLevelInfo: "INFO",
244
+ logLevelSuccess: "OK",
245
+ logLevelWarn: "WARN",
246
+ logLevelError: "ERROR",
247
+ totalColon: "Total:",
248
+ todayColon: "Hari Ini:",
249
+ hour1Colon: "1j:",
250
+ frequentModels: "Sering Dipakai",
251
+ smartTitle: "Otomatis pilih 5 model paling sering dipakai (24 jam)",
252
+ activeCount: "{count} Aktif",
253
+ allCaps: "SEMUA",
254
+ claudeCaps: "CLAUDE",
255
+ geminiCaps: "GEMINI",
256
+
257
+ // System Info
258
+ systemInfo: "Informasi Sistem",
259
+ refresh: "Refresh",
260
+ runtimeConfig: "Konfigurasi Runtime",
261
+ debugDesc: "Aktifkan log detail (Lihat tab Log)",
262
+ networkRetry: "Pengaturan Retry Jaringan",
263
+ maxRetries: "Maks. Retry",
264
+ retryBaseDelay: "Jeda Awal Retry (ms)",
265
+ retryMaxDelay: "Jeda Maks. Retry (ms)",
266
+ persistentSessions: "Sesi Persisten",
267
+ persistTokenDesc: "Simpan sesi OAuth ke disk agar startup lebih cepat",
268
+ rateLimiting: "Rate Limiting & Timeout Akun",
269
+ defaultCooldown: "Cooldown Default",
270
+ defaultCooldownDesc: "Cooldown bawaan jika API tidak memberikan waktu reset.",
271
+ maxWaitThreshold: "Batas Tunggu Maksimal",
272
+ maxWaitDesc: "Jika semua akun terkena rate limit lebih lama dari ini, langsung gagal.",
273
+ // Error Handling Tuning
274
+ errorHandlingTuning: "Penyetelan Penanganan Error",
275
+ rateLimitDedupWindow: "Jendela Deduplikasi Rate Limit",
276
+ rateLimitDedupWindowDesc: "Mencegah badai retry ketika beberapa permintaan terkena rate limit bersamaan.",
277
+ maxConsecutiveFailures: "Maks. Kegagalan Berturut-turut",
278
+ maxConsecutiveFailuresDesc: "Jumlah kegagalan berturut-turut sebelum menerapkan cooldown diperpanjang.",
279
+ extendedCooldown: "Cooldown Diperpanjang",
280
+ extendedCooldownDesc: "Durasi cooldown setelah mencapai maks. kegagalan berturut-turut.",
281
+ maxCapacityRetries: "Maks. Retry Kapasitas",
282
+ maxCapacityRetriesDesc: "Maksimum retry untuk kehabisan kapasitas sebelum ganti akun.",
283
+ saveConfigServer: "Simpan Konfigurasi",
284
+ serverRestartAlert: "Tersimpan ke {path}. Restart server untuk menerapkan.",
285
+
286
+ // Password
287
+ changePassword: "Ubah Password WebUI",
288
+ changePasswordDesc: "Ubah password untuk akses dashboard ini",
289
+ currentPassword: "Password Saat Ini",
290
+ newPassword: "Password Baru",
291
+ confirmNewPassword: "Konfirmasi Password Baru",
292
+ passwordEmptyDesc: "Kosongkan jika belum ada password",
293
+ passwordLengthDesc: "Minimal 6 karakter",
294
+ passwordConfirmDesc: "Masukkan ulang password baru",
295
+ cancel: "Batal",
296
+ passwordsNotMatch: "Password tidak cocok",
297
+ passwordTooShort: "Password minimal 6 karakter",
298
+
299
+ // Dashboard drill-down
300
+ clickToViewAllAccounts: "Klik untuk lihat semua akun",
301
+ clickToViewModels: "Klik untuk lihat halaman Model",
302
+ clickToViewLimitedAccounts: "Klik untuk lihat akun yang dibatasi",
303
+ clickToFilterClaude: "Klik untuk filter model Claude",
304
+ clickToFilterGemini: "Klik untuk filter model Gemini",
305
+
306
+ // Accounts page
307
+ searchAccounts: "Cari akun...",
308
+ noAccountsYet: "Belum Ada Akun",
309
+ noAccountsDesc: "Mulai dengan menambahkan akun Google via OAuth, atau gunakan perintah CLI untuk import kredensial.",
310
+ addFirstAccount: "Tambah Akun Pertama",
311
+ noSearchResults: "Tidak ada akun yang cocok",
312
+ clearSearch: "Reset Pencarian",
313
+ disabledAccountsNote: "<strong>Akun nonaktif</strong> tidak akan digunakan untuk routing tapi tetap tersimpan di konfigurasi. Statistik dashboard hanya mencakup akun aktif.",
314
+ dangerousOperation: "⚠️ Operasi Berbahaya",
315
+ confirmDeletePrompt: "Yakin ingin menghapus akun",
316
+ deleteWarning: "⚠️ Tindakan ini tidak bisa dibatalkan. Semua pengaturan dan riwayat akan dihapus permanen.",
317
+
318
+ // OAuth progress
319
+ oauthWaiting: "Menunggu otorisasi OAuth...",
320
+ oauthWaitingDesc: "Silakan selesaikan autentikasi di jendela popup. Proses ini bisa memakan waktu hingga 2 menit.",
321
+ oauthCancelled: "Otorisasi OAuth dibatalkan",
322
+ oauthTimeout: "⏱️ Waktu otorisasi OAuth habis. Silakan coba lagi.",
323
+ oauthWindowClosed: "Jendela OAuth ditutup. Otorisasi mungkin tidak lengkap.",
324
+ cancelOAuth: "Batal",
325
+
326
+ // MCP CLI & Gemini 1M
327
+ mcpCliExperimental: "MCP CLI (Eksperimental)",
328
+ mcpCliDesc: "Aktifkan integrasi MCP eksperimental untuk penggunaan tool yang lebih stabil dengan konsumsi konteks lebih rendah.",
329
+ gemini1mMode: "Mode Konteks Gemini 1M",
330
+ gemini1mDesc: "Tambahkan suffix [1m] ke model Gemini untuk context window 1M.",
331
+ gemini1mWarning: "⚠ Konteks besar dapat menurunkan performa Gemini-3-Pro.",
332
+ clickToSet: "Klik untuk atur...",
333
+
334
+ // Quota Distribution
335
+ quotaDistribution: "Distribusi Kuota",
336
+ resetsIn: "Reset dalam {time}",
337
+ noQuotaData: "Data kuota belum tersedia untuk akun ini.",
338
+ // Manual OAuth Mode
339
+ manualMode: "Mode Manual",
340
+ manualModeDesc: "(untuk lingkungan di mana callback tidak bisa dicapai)",
341
+ authLinkLabel: "Link Otorisasi:",
342
+ linkCopied: "Link disalin ke clipboard",
343
+ pasteCallbackLabel: "Tempel callback URL atau kode:",
344
+ pasteCallbackPlaceholder: "http://localhost:51121/oauth-callback?code=... atau 4/0xxx...",
345
+ completeAuth: "Selesaikan Otorisasi",
346
+ authFailed: "Otorisasi gagal",
347
+ // Import/Export
348
+ export: "Ekspor",
349
+ import: "Impor",
350
+ exportAccounts: "Ekspor Akun",
351
+ importAccounts: "Impor Akun",
352
+ exportSuccess: "Berhasil mengekspor {count} akun",
353
+ exportFailed: "Gagal mengekspor",
354
+ importSuccess: "Impor selesai:",
355
+ importFailed: "Gagal mengimpor",
356
+
357
+ // Completed TODOs
358
+ pageTitle: "CommonsProxy Console",
359
+ live: "Live",
360
+ tier: "Tier",
361
+ quota: "Kuota",
362
+ tierUltra: "Ultra",
363
+ tierPro: "Pro",
364
+ tierFree: "Gratis",
365
+ menu: "Menu",
366
+ github: "GitHub",
367
+ noData: "Tidak ada data",
368
+ fix: "Perbaiki",
369
+
370
+ // Error Messages
371
+ operationFailed: "Operasi gagal",
372
+ unknownError: "Error tidak diketahui",
373
+ presetNameRequired: "Nama preset wajib diisi",
374
+ saveFailed: "Gagal menyimpan",
375
+ failedToSavePreset: "Gagal menyimpan preset",
376
+ noPresetSelected: "Tidak ada preset dipilih",
377
+ deletePresetConfirm: "Hapus preset \"{name}\"?",
378
+ deleteFailed: "Gagal menghapus",
379
+ failedToDeletePreset: "Gagal menghapus preset",
380
+ failedToChangePassword: "Gagal mengubah password",
381
+ passwordChangedSuccess: "Password berhasil diubah",
382
+ debugModeToggled: "Mode debug {status}",
383
+ tokenCacheToggled: "Cache token {status}",
384
+ failedToUpdateTokenCache: "Gagal memperbarui cache token",
385
+ failedToUpdateDebugMode: "Gagal memperbarui mode debug",
386
+ failedToRefreshAccount: "Gagal memperbarui akun",
387
+ failedToDeleteAccount: "Gagal menghapus akun",
388
+ failedToReloadAccounts: "Gagal memuat ulang akun",
389
+ failedToUpdateModelConfig: "Gagal memperbarui konfigurasi model",
390
+ fieldUpdated: "{displayName} diubah menjadi {value}",
391
+ failedToUpdateField: "Gagal memperbarui {displayName}",
392
+
393
+ // Validation
394
+ mustBeValidNumber: "{fieldName} harus berupa angka valid",
395
+ mustBeAtLeast: "{fieldName} minimal {min}",
396
+ mustBeAtMost: "{fieldName} maksimal {max}",
397
+ cannotBeEmpty: "{fieldName} tidak boleh kosong",
398
+ mustBeTrueOrFalse: "Nilai harus true atau false",
399
+
400
+ // Account Selection Strategy translations
401
+ accountSelectionStrategy: "Strategi Pemilihan Akun",
402
+ selectionStrategy: "Strategi Pemilihan",
403
+ strategyStickyLabel: "Tetap (Optimisasi Cache)",
404
+ strategyRoundRobinLabel: "Bergilir (Load Balanced)",
405
+ strategyHybridLabel: "Hibrida (Distribusi Cerdas)",
406
+ strategyStickyDesc: "Tetap di akun yang sama hingga terkena rate limit. Terbaik untuk cache prompt.",
407
+ strategyRoundRobinDesc: "Berputar ke akun berikutnya setiap permintaan. Throughput maksimum.",
408
+ strategyHybridDesc: "Pemilihan cerdas berdasarkan kesehatan, token, dan kesegaran.",
409
+ strategyUpdated: "Strategi diubah ke: {strategy}",
410
+ failedToUpdateStrategy: "Gagal memperbarui strategi",
411
+ invalidStrategy: "Strategi tidak valid dipilih",
412
+ };