openclacky 1.2.15 → 1.2.16

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84e7378b08b627bad34d327d1bd82cc7efbfe980a690d64e678242917be8125d
4
- data.tar.gz: 87e4c1b8e99f2195c98c85124816503fd11436b3bdb38465bb7289fab1204fa3
3
+ metadata.gz: b0bb463f7cc0c691496a5dcb6e0d0d7e2e5f20eab12dddee17049f35588755e8
4
+ data.tar.gz: c046bc3d50ebb6624e15b19bbf727a763930d9182ac2b650ea8bc4f4a9b8ea6e
5
5
  SHA512:
6
- metadata.gz: 36cb343f4a81222b3a2861dcd80529f0d3216a341e19ea7ebfd1ea6dbebded9c0d31a212a645cffb7268e9faf844be9f257d739677a17d0387bf033970dc7675
7
- data.tar.gz: 16d08fc33223c56024a27072de5a0f435ac969c1ac55fffa39738e0e8d9bfe77f1ebbe4c4cafb8793c2dac1367744daeb417b97db9a854ba7e04ffd4d2b17042
6
+ metadata.gz: 10f9b0c800eb9756f138fc3ed583aceadd89ee7b0bee2d47a3b909b5136e171abb1d8e69c7a8569e2c0642c6839d13d082f956300b56070d7617674d05a73ef5
7
+ data.tar.gz: f730ed4911745afebf9fe8b30d3084b5b8922f26322dc4b55049e2700774fd2a5a5a6200c74b6fc03f3bfc20db689fe9cfa9c78476c0f8809b59aa23b1f9340f
data/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.2.16] - 2026-06-10
9
+
10
+ ### Added
11
+ - Claude Fable 5 model support
12
+
13
+ ### Fixed
14
+ - Model test failed when using saved API key
15
+ - Windows WSL install success rate
16
+
8
17
  ## [1.2.15] - 2026-06-10
9
18
 
10
19
  ### Added
@@ -31,6 +31,7 @@ module Clacky
31
31
  "api" => "bedrock",
32
32
  "default_model" => "abs-claude-sonnet-4-6",
33
33
  "models" => [
34
+ "abs-claude-fable-5",
34
35
  "abs-claude-opus-4-8",
35
36
  "abs-claude-opus-4-7",
36
37
  "abs-claude-opus-4-6",
@@ -80,6 +81,7 @@ module Clacky
80
81
  # themselves, so they're intentionally not listed here as keys —
81
82
  # no injection happens when the default model is already lite-class.
82
83
  "lite_models" => {
84
+ "abs-claude-fable-5" => "abs-claude-haiku-4-5",
83
85
  "abs-claude-opus-4-8" => "abs-claude-haiku-4-5",
84
86
  "abs-claude-opus-4-7" => "abs-claude-haiku-4-5",
85
87
  "abs-claude-opus-4-6" => "abs-claude-haiku-4-5",
@@ -91,6 +93,7 @@ module Clacky
91
93
  # Fallback chain: if a model is unavailable, try the next one in order.
92
94
  # Keys are primary model names; values are the fallback model to use instead.
93
95
  "fallback_models" => {
96
+ "abs-claude-fable-5" => "abs-claude-opus-4-8",
94
97
  "abs-claude-sonnet-4-6" => "abs-claude-sonnet-4-5"
95
98
  },
96
99
  "website_url" => "https://www.openclacky.com/ai-keys"
@@ -4141,8 +4141,16 @@ module Clacky
4141
4141
 
4142
4142
  api_key = body["api_key"].to_s
4143
4143
  if api_key.include?("****")
4144
- idx = body["index"]&.to_i || @agent_config.current_model_index
4145
- api_key = @agent_config.models.dig(idx, "api_key").to_s
4144
+ model_id = body["id"].to_s
4145
+ entry = nil
4146
+ if !model_id.empty?
4147
+ entry = @agent_config.models.find { |m| m["id"] == model_id }
4148
+ end
4149
+ if entry.nil? && body.key?("index")
4150
+ entry = @agent_config.models[body["index"].to_i]
4151
+ end
4152
+ entry ||= @agent_config.models[@agent_config.current_model_index]
4153
+ api_key = entry ? entry["api_key"].to_s : ""
4146
4154
  end
4147
4155
 
4148
4156
  model = body["model"].to_s
@@ -8,6 +8,21 @@ module Clacky
8
8
  # All pricing is based on official API documentation
9
9
  PRICING_TABLE = {
10
10
  # Claude 4.5 models - tiered pricing based on prompt length
11
+ "claude-fable-5" => {
12
+ input: {
13
+ default: 10.00, # $10/MTok for prompts ≤ 200K tokens
14
+ over_200k: 10.00 # same for all tiers
15
+ },
16
+ output: {
17
+ default: 50.00, # $50/MTok for prompts ≤ 200K tokens
18
+ over_200k: 50.00 # same for all tiers
19
+ },
20
+ cache: {
21
+ write: 12.50, # $12.50/MTok cache write (5-min tier)
22
+ read: 1.00 # $1.00/MTok cache read
23
+ }
24
+ },
25
+
11
26
  "claude-opus-4.5" => {
12
27
  input: {
13
28
  default: 5.00, # $5/MTok for prompts ≤ 200K tokens
@@ -633,6 +648,8 @@ module Clacky
633
648
  # Support both dot and dash separators (e.g., "4.5", "4-5", "4-6")
634
649
  # Also handles Bedrock cross-region prefixes (e.g. "jp.anthropic.claude-sonnet-4-6")
635
650
  case model
651
+ when /claude.*fable.*5/i
652
+ "claude-fable-5"
636
653
  when /claude.*opus.*4[.-]?[5-9]/i
637
654
  "claude-opus-4.5"
638
655
  when /claude.*sonnet.*4[.-]?[5-9]/i
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Clacky
4
- VERSION = "1.2.15"
4
+ VERSION = "1.2.16"
5
5
  end
@@ -599,7 +599,7 @@ const I18n = (() => {
599
599
  "settings.browser.btn.reconfigure": "🌐 Reconfigure Browser",
600
600
  "settings.browser.btn.starting": "Starting…",
601
601
  "settings.network.title": "Network",
602
- "settings.network.desc": "Clacky always ignores HTTP_PROXY / HTTPS_PROXY from your shell. Set an explicit proxy URL below to route Clacky's outbound traffic through a proxy.",
602
+ "settings.network.desc": "Proxy is disabled by default. To enable, enter an HTTP/HTTPS proxy URL below (SOCKS5 is not supported).",
603
603
  "settings.network.proxyUrl": "Proxy URL",
604
604
  "settings.network.save": "Save",
605
605
  "settings.network.saved": "Saved",
@@ -1350,7 +1350,7 @@ const I18n = (() => {
1350
1350
  "settings.browser.btn.reconfigure": "🌐 重新配置浏览器",
1351
1351
  "settings.browser.btn.starting": "启动中…",
1352
1352
  "settings.network.title": "网络",
1353
- "settings.network.desc": "Clacky 始终忽略系统的 HTTP_PROXY / HTTPS_PROXY。如需让 Clacky 走代理,请在下方填入显式代理地址。",
1353
+ "settings.network.desc": "默认不启用代理,如需启用,请填写 HTTP/HTTPS 代理地址(不支持 SOCKS5)。",
1354
1354
  "settings.network.proxyUrl": "代理地址",
1355
1355
  "settings.network.save": "保存",
1356
1356
  "settings.network.saved": "已保存",
@@ -6,8 +6,9 @@ window.ModelTester = (function () {
6
6
  // { ok: true, base_url, message } — connected, no rewrite
7
7
  // { ok: true, base_url, message, rewrote: true } — connected, base_url auto-corrected (/v1 appended)
8
8
  // { ok: false, message } — failed (server-reported or network)
9
- async function testConnection({ model, base_url, api_key, anthropic_format, index } = {}) {
9
+ async function testConnection({ model, base_url, api_key, anthropic_format, index, id } = {}) {
10
10
  const body = { model, base_url, api_key };
11
+ if (typeof id === "string" && id) body.id = id;
11
12
  if (typeof index === "number") body.index = index;
12
13
  if (anthropic_format) body.anthropic_format = true;
13
14
 
@@ -292,7 +292,8 @@ const Settings = (() => {
292
292
  model: model.model,
293
293
  base_url: model.base_url,
294
294
  api_key: model.api_key_masked,
295
- index,
295
+ id: model.id,
296
+ index: typeof model.index === "number" ? model.index : index,
296
297
  anthropic_format: model.anthropic_format
297
298
  });
298
299
  _showTestResult(index, result.ok, result.message);
@@ -318,12 +319,16 @@ const Settings = (() => {
318
319
  // routing for Claude sub-models.
319
320
  const anthropic_format = _modalSelectedProviderId === "anthropic";
320
321
 
322
+ const isNew = index < 0;
323
+ const existing = isNew ? {} : (_models[index] || {});
324
+ const existingId = existing.id || null;
325
+
321
326
  // Step 1: Test first
322
327
  saveBtn.textContent = I18n.t("settings.models.btn.testing");
323
328
  _showModalTestResult(null, "");
324
329
 
325
330
  const result = await ModelTester.testConnection({
326
- model, base_url, api_key, index, anthropic_format
331
+ model, base_url, api_key, index, id: existingId, anthropic_format
327
332
  });
328
333
 
329
334
  if (result.rewrote) {
@@ -343,9 +348,7 @@ const Settings = (() => {
343
348
  // Step 2: Save
344
349
  saveBtn.textContent = I18n.t("settings.models.btn.saving");
345
350
 
346
- const isNew = index < 0;
347
- const existing = isNew ? {} : (_models[index] || {});
348
- const hasId = !!existing.id;
351
+ const hasId = !!existingId;
349
352
 
350
353
  const payload = { model, base_url, anthropic_format };
351
354
  const setDefault = document.getElementById("model-modal-set-default").checked;
@@ -367,7 +370,7 @@ const Settings = (() => {
367
370
  return;
368
371
  }
369
372
 
370
- const saveResult = await ModelTester.saveModel(payload, { existingId: hasId ? existing.id : null });
373
+ const saveResult = await ModelTester.saveModel(payload, { existingId: hasId ? existingId : null });
371
374
 
372
375
  if (saveResult.ok) {
373
376
  saveBtn.textContent = I18n.t("settings.models.btn.saved");
data/scripts/install.ps1 CHANGED
@@ -66,6 +66,15 @@ function Write-Warn { param($msg) Write-Host " [!] $msg" -ForegroundColor Ye
66
66
  function Write-Fail { param($msg) Write-Host " [x] $msg" -ForegroundColor Red }
67
67
  function Write-Step { param($msg) Write-Host "`n==> $msg" -ForegroundColor Blue }
68
68
 
69
+ # Exit codes (consumed by GUI installer to map to localized error messages).
70
+ $EXIT_OK = 0
71
+ $EXIT_GENERIC_ERROR = 1
72
+ $EXIT_NETWORK_ERROR = 2
73
+ $EXIT_DISK_ERROR = 3
74
+ $EXIT_UNSUPPORTED_OS = 4
75
+ $EXIT_NOT_ADMIN = 5
76
+ $EXIT_REBOOT_REQUIRED = 6
77
+
69
78
  function Test-IsAdmin {
70
79
  return ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
71
80
  [Security.Principal.WindowsBuiltInRole]::Administrator)
@@ -194,7 +203,7 @@ function Prompt-Reboot {
194
203
  Write-Host " $INSTALL_PS1_COMMAND" -ForegroundColor Yellow
195
204
  Write-Host ""
196
205
  Read-Host "Press Enter to exit"
197
- exit 0
206
+ exit $EXIT_REBOOT_REQUIRED
198
207
  }
199
208
 
200
209
  # Download Ubuntu rootfs and verify checksum. Returns local tar path.
@@ -221,7 +230,7 @@ function Get-UbuntuRootfs {
221
230
  Write-Fail "Not enough disk space on $drive."
222
231
  Write-Fail " Available : $([math]::Round($freeBytes / 1GB, 1)) GB"
223
232
  Write-Fail " Required : ~4 GB"
224
- exit 3
233
+ exit $EXIT_DISK_ERROR
225
234
  }
226
235
 
227
236
  # Check if a valid cached tarball exists (skip download if checksum passes)
@@ -242,14 +251,14 @@ function Get-UbuntuRootfs {
242
251
  Write-Step "Downloading Ubuntu rootfs (~350 MB)..."
243
252
  if (-not (Invoke-Download -Url $wslUrl -OutFile $tarPath)) {
244
253
  Write-Fail "Failed to download Ubuntu rootfs. Check your network and try again."
245
- exit 1
254
+ exit $EXIT_NETWORK_ERROR
246
255
  }
247
256
  Write-Success "Download complete."
248
257
 
249
258
  Write-Step "Verifying checksum..."
250
259
  if (-not (Test-Sha256 -FilePath $tarPath -Sha256Url $sha256Url)) {
251
260
  Write-Fail "The downloaded file is corrupted. Please try again."
252
- exit 1
261
+ exit $EXIT_GENERIC_ERROR
253
262
  }
254
263
  }
255
264
 
@@ -278,7 +287,7 @@ function Install-UbuntuRootfs {
278
287
  Write-Fail "wsl --import failed (exit $LASTEXITCODE)."
279
288
  if ($wslOutput) { Write-Fail "$wslOutput" }
280
289
  Write-Fail "Try removing $UBUNTU_WSL_DIR and running the script again."
281
- exit 1
290
+ exit $EXIT_GENERIC_ERROR
282
291
  }
283
292
  Write-Success "Ubuntu (WSL$WslVersion) imported successfully."
284
293
  }
@@ -289,7 +298,7 @@ function Test-WslNetwork {
289
298
  if ($LASTEXITCODE -ne 0) {
290
299
  Write-Fail "WSL cannot reach $CLACKY_CDN_PRIMARY_HOST (curl exit $LASTEXITCODE)."
291
300
  Write-Fail "Please fix the network inside WSL and re-run this installer."
292
- exit 2
301
+ exit $EXIT_NETWORK_ERROR
293
302
  }
294
303
  Write-Success "WSL network OK."
295
304
  }
@@ -304,7 +313,7 @@ function Run-InstallInWsl {
304
313
  $localScript = Join-Path $scriptDir "install.sh"
305
314
  if (-not (Test-Path $localScript)) {
306
315
  Write-Fail "Local mode: install.sh not found at $localScript"
307
- exit 1
316
+ exit $EXIT_GENERIC_ERROR
308
317
  }
309
318
  $wslPath = ($localScript -replace '\', '/') -replace '^([A-Za-z]):', { '/mnt/' + $args[0].Groups[1].Value.ToLower() }
310
319
  Write-Info "Local mode: using $wslPath"
@@ -315,13 +324,13 @@ function Run-InstallInWsl {
315
324
  }
316
325
 
317
326
  if ($LASTEXITCODE -eq 2) {
318
- exit 2
327
+ exit $EXIT_NETWORK_ERROR
319
328
  }
320
329
  if ($LASTEXITCODE -ne 0) {
321
330
  Write-Fail "Installation failed inside WSL (exit $LASTEXITCODE)."
322
331
  Write-Fail "You can retry manually:"
323
332
  Write-Host " wsl -d Ubuntu -u root -- bash -c `"curl -fsSL $INSTALL_SCRIPT_URL | bash`"" -ForegroundColor Yellow
324
- exit 1
333
+ exit $EXIT_GENERIC_ERROR
325
334
  }
326
335
  }
327
336
 
@@ -418,7 +427,8 @@ function Test-VirtualisationSupported {
418
427
  New-Item -ItemType Directory -Force -Path $probeDir | Out-Null
419
428
 
420
429
  Write-Info "[probe] Running: wsl --import $probeName $probeDir $TarPath --version 2"
421
- wsl.exe --import $probeName $probeDir $TarPath --version 2 >$null 2>$null
430
+ $probeOutput = wsl.exe --import $probeName $probeDir $TarPath --version 2 2>&1
431
+ if ($probeOutput) { Write-Info "WSL2 probe: $probeOutput" }
422
432
  $importExit = $LASTEXITCODE
423
433
  Write-Info "[probe] wsl --import exit code: $importExit"
424
434
  $ok = ($importExit -eq 0)
@@ -442,6 +452,8 @@ function Test-VirtualisationSupported {
442
452
 
443
453
  # Download and install the WSL2 kernel MSI from our CDN.
444
454
  function Install-WslKernel {
455
+ param([switch]$Repair)
456
+
445
457
  $cpuArch = Get-CpuArch
446
458
 
447
459
  # Select the correct MSI for this CPU architecture.
@@ -456,7 +468,18 @@ function Install-WslKernel {
456
468
  Write-Step "Downloading WSL kernel update ($cpuArch)..."
457
469
  if (-not (Invoke-Download -Url $url -OutFile $msiPath)) {
458
470
  Write-Fail "Failed to download WSL kernel update. Check your network and try again."
459
- exit 1
471
+ exit $EXIT_NETWORK_ERROR
472
+ }
473
+ if ($Repair) {
474
+ # /fa = force repair all files; handles corrupt/partial installs where /i silently no-ops.
475
+ $proc = Start-Process msiexec -Wait -PassThru -ArgumentList "/fa", $msiPath, "/quiet", "/norestart"
476
+ Remove-Item -Force -ErrorAction SilentlyContinue $msiPath
477
+ if ($proc.ExitCode -ne 0) {
478
+ Write-Warn "WSL kernel repair failed (exit $($proc.ExitCode))."
479
+ return $false
480
+ }
481
+ Write-Success "WSL kernel repaired."
482
+ return $true
460
483
  }
461
484
  Write-Info "Installing WSL kernel..."
462
485
  Start-Process msiexec -Wait -ArgumentList "/i", $msiPath, "/quiet", "/norestart"
@@ -488,7 +511,7 @@ if (-not (Test-IsAdmin)) {
488
511
  Write-Host ""
489
512
  Write-Host " Right-click PowerShell -> 'Run as administrator', then:" -ForegroundColor Yellow
490
513
  Write-Host " $INSTALL_PS1_COMMAND" -ForegroundColor Yellow
491
- exit 1
514
+ exit $EXIT_NOT_ADMIN
492
515
  }
493
516
 
494
517
  # Check minimum Windows version: WSL1 requires Build 16215 (Win10 1709).
@@ -497,7 +520,7 @@ if ($osBuild -lt 16215) {
497
520
  Write-Fail "Unsupported Windows version (Build $osBuild)."
498
521
  Write-Fail "WSL requires Windows 10 Build 16215 (version 1709) or later."
499
522
  Write-Fail "Please update Windows and try again."
500
- exit 1
523
+ exit $EXIT_UNSUPPORTED_OS
501
524
  }
502
525
  Write-Info "Windows Build $osBuild — OK."
503
526
 
@@ -516,12 +539,15 @@ if ($installPhase -eq "" -and $wslCode -ne 0) {
516
539
  # Always exits (prompts reboot)
517
540
  }
518
541
 
519
- # phase == wsl-pending + code 1: reboot happened but WSL still not ready.
542
+ # phase == wsl-pending + code 1: reboot happened but WSL still not ready; try repair.
520
543
  if ($installPhase -eq "wsl-pending" -and $wslCode -eq 1) {
521
- Write-Warn "WSL features were enabled but WSL is still not ready."
522
- Write-Warn "Please reboot your computer and run the installer again."
523
- Write-Warn "If this keeps happening, please contact our support team."
524
- exit 1
544
+ Write-Warn "WSL features were enabled but WSL is still not ready. Attempting repair..."
545
+ $repairOk = Install-WslKernel -Repair
546
+ if (-not $repairOk) {
547
+ Write-Warn "Please reboot your computer and run the installer again."
548
+ Write-Warn "If this keeps happening, please contact our support team."
549
+ exit $EXIT_GENERIC_ERROR
550
+ }
525
551
  }
526
552
 
527
553
  # wslCode != 1 (0, -1, -444, 50, etc.): WSL is functional, continue.
@@ -568,7 +594,7 @@ if (Test-UbuntuInstalled) {
568
594
  }
569
595
  Write-Fail "Failed to import Ubuntu into both WSL1 and WSL2."
570
596
  Write-Fail "Please ensure Windows Subsystem for Linux is enabled and try again."
571
- exit 1
597
+ exit $EXIT_GENERIC_ERROR
572
598
  }
573
599
  }
574
600
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openclacky
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.15
4
+ version: 1.2.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - windy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-09 00:00:00.000000000 Z
11
+ date: 2026-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday