openclacky 1.4.0 → 1.5.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/lib/clacky/agent/llm_caller.rb +3 -3
  4. data/lib/clacky/agent/session_serializer.rb +50 -9
  5. data/lib/clacky/agent.rb +4 -5
  6. data/lib/clacky/brand_config.rb +102 -10
  7. data/lib/clacky/client.rb +22 -16
  8. data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +135 -84
  9. data/lib/clacky/default_extensions/ext-studio/api/handler.rb +144 -1
  10. data/lib/clacky/default_extensions/ext-studio/ext.yml +2 -4
  11. data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +412 -173
  12. data/lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md +522 -0
  13. data/lib/clacky/extension/api_extension.rb +5 -6
  14. data/lib/clacky/extension/loader.rb +16 -2
  15. data/lib/clacky/extension/packager.rb +10 -1
  16. data/lib/clacky/extension/scaffold/templates/full/api/handler.rb.erb +16 -0
  17. data/lib/clacky/mcp/http_transport.rb +1 -1
  18. data/lib/clacky/platform_http_client.rb +9 -7
  19. data/lib/clacky/providers.rb +113 -5
  20. data/lib/clacky/server/http_server.rb +131 -38
  21. data/lib/clacky/server/session_registry.rb +9 -0
  22. data/lib/clacky/tools/grep.rb +6 -1
  23. data/lib/clacky/utils/environment_detector.rb +16 -9
  24. data/lib/clacky/utils/file_processor.rb +21 -24
  25. data/lib/clacky/utils/model_pricing.rb +85 -0
  26. data/lib/clacky/version.rb +1 -1
  27. data/lib/clacky/web/app.css +441 -48
  28. data/lib/clacky/web/app.js +29 -6
  29. data/lib/clacky/web/components/onboard.js +21 -7
  30. data/lib/clacky/web/features/backup/view.js +4 -3
  31. data/lib/clacky/web/features/extensions/store.js +56 -4
  32. data/lib/clacky/web/features/extensions/view.js +54 -24
  33. data/lib/clacky/web/features/mcp/view.js +5 -2
  34. data/lib/clacky/web/features/new-session/store.js +8 -4
  35. data/lib/clacky/web/features/version/view.js +5 -1
  36. data/lib/clacky/web/features/workspace/store.js +11 -0
  37. data/lib/clacky/web/features/workspace/view.js +10 -4
  38. data/lib/clacky/web/i18n.js +58 -36
  39. data/lib/clacky/web/index.html +74 -7
  40. data/lib/clacky/web/sessions.js +72 -15
  41. data/lib/clacky/web/settings.js +22 -3
  42. data/lib/clacky/web/theme.js +27 -58
  43. data/scripts/uninstall.sh +1 -1
  44. metadata +2 -5
  45. data/lib/clacky/default_extensions/ext-studio/skills/ext-debug/SKILL.md +0 -71
  46. data/lib/clacky/default_extensions/ext-studio/skills/ext-publish/SKILL.md +0 -73
  47. data/lib/clacky/default_extensions/ext-studio/skills/ext-scaffold/SKILL.md +0 -65
  48. data/lib/clacky/default_skills/extend-openclacky/SKILL.md +0 -106
@@ -6,6 +6,7 @@ require "securerandom"
6
6
  require "stringio"
7
7
 
8
8
  require_relative "parser_manager"
9
+ require_relative "environment_detector"
9
10
  require "zip"
10
11
 
11
12
  module Clacky
@@ -595,67 +596,63 @@ module Clacky
595
596
  return content if content.nil? || content.empty?
596
597
 
597
598
  # Rewrite markdown image syntax ![alt](file:///path) → proxy URL
598
- content = content.gsub(/!\[([^\]]*)\]\(((?:file:\/\/)?\/[^)]+)\)/) do |_match|
599
+ content = content.gsub(/!\[([^\]]*)\]\(((?:file:\/\/)?(?:~\/|\/)[^)]+)\)/) do |_match|
599
600
  alt = Regexp.last_match(1)
600
601
  href = Regexp.last_match(2)
601
602
 
602
- path = href.sub(%r{\Afile://}, "")
603
- path = CGI.unescape(path)
603
+ real = Clacky::Utils::EnvironmentDetector.resolve_local_path(href)
604
604
 
605
- ext = File.extname(path).downcase
606
- if LOCAL_MEDIA_EXTENSIONS.include?(ext) && File.exist?(path)
607
- "![#{alt}](#{local_image_proxy_url(href, path)})"
605
+ ext = File.extname(real).downcase
606
+ if LOCAL_MEDIA_EXTENSIONS.include?(ext) && File.exist?(real)
607
+ "![#{alt}](#{local_image_proxy_url(href, real)})"
608
608
  else
609
609
  _match
610
610
  end
611
611
  end
612
612
 
613
613
  # Rewrite <video src="file:///path/vid.mp4" ...> → proxy URL
614
- content = content.gsub(/<video\b([^>]*)\bsrc="((?:file:\/\/)?\/[^"]+)"([^>]*)>/) do |_match|
614
+ content = content.gsub(/<video\b([^>]*)\bsrc="((?:file:\/\/)?(?:~\/|\/)[^"]+)"([^>]*)>/) do |_match|
615
615
  pre = Regexp.last_match(1) || ""
616
616
  href = Regexp.last_match(2)
617
617
  post = Regexp.last_match(3) || ""
618
618
 
619
- path = href.sub(%r{\Afile://}, "")
620
- path = CGI.unescape(path)
619
+ real = Clacky::Utils::EnvironmentDetector.resolve_local_path(href)
621
620
 
622
- ext = File.extname(path).downcase
623
- if LOCAL_VIDEO_EXTENSIONS.include?(ext) && File.exist?(path)
624
- "<video#{pre} src=\"#{local_image_proxy_url(href, path)}\"#{post}>"
621
+ ext = File.extname(real).downcase
622
+ if LOCAL_VIDEO_EXTENSIONS.include?(ext) && File.exist?(real)
623
+ "<video#{pre} src=\"#{local_image_proxy_url(href, real)}\"#{post}>"
625
624
  else
626
625
  _match
627
626
  end
628
627
  end
629
628
 
630
629
  # Rewrite <audio src="file:///path/audio.wav" ...> → proxy URL
631
- content = content.gsub(/<audio\b([^>]*)\bsrc="((?:file:\/\/)?\/[^"]+)"([^>]*)>/) do |_match|
630
+ content = content.gsub(/<audio\b([^>]*)\bsrc="((?:file:\/\/)?(?:~\/|\/)[^"]+)"([^>]*)>/) do |_match|
632
631
  pre = Regexp.last_match(1) || ""
633
632
  href = Regexp.last_match(2)
634
633
  post = Regexp.last_match(3) || ""
635
634
 
636
- path = href.sub(%r{\Afile://}, "")
637
- path = CGI.unescape(path)
635
+ real = Clacky::Utils::EnvironmentDetector.resolve_local_path(href)
638
636
 
639
- ext = File.extname(path).downcase
640
- if LOCAL_AUDIO_EXTENSIONS.include?(ext) && File.exist?(path)
641
- "<audio#{pre} src=\"#{local_image_proxy_url(href, path)}\"#{post}>"
637
+ ext = File.extname(real).downcase
638
+ if LOCAL_AUDIO_EXTENSIONS.include?(ext) && File.exist?(real)
639
+ "<audio#{pre} src=\"#{local_image_proxy_url(href, real)}\"#{post}>"
642
640
  else
643
641
  _match
644
642
  end
645
643
  end
646
644
 
647
645
  # Rewrite video/audio markdown links [text](file:///path) → proxy URL
648
- content = content.gsub(/(?<!!)\[([^\]]*)\]\(((?:file:\/\/)?\/[^)]+)\)/) do |_match|
646
+ content = content.gsub(/(?<!!)\[([^\]]*)\]\(((?:file:\/\/)?(?:~\/|\/)[^)]+)\)/) do |_match|
649
647
  text = Regexp.last_match(1)
650
648
  href = Regexp.last_match(2)
651
649
 
652
- path = href.sub(%r{\Afile://}, "")
653
- path = CGI.unescape(path)
650
+ real = Clacky::Utils::EnvironmentDetector.resolve_local_path(href)
654
651
 
655
- ext = File.extname(path).downcase
652
+ ext = File.extname(real).downcase
656
653
  if LOCAL_VIDEO_EXTENSIONS.include?(ext) || LOCAL_AUDIO_EXTENSIONS.include?(ext)
657
- if File.exist?(path)
658
- "[#{text}](#{local_image_proxy_url(href, path)})"
654
+ if File.exist?(real)
655
+ "[#{text}](#{local_image_proxy_url(href, real)})"
659
656
  else
660
657
  _match
661
658
  end
@@ -237,6 +237,91 @@ module Clacky
237
237
  }
238
238
  },
239
239
 
240
+ # Kimi K3 flagship model (1M context, native vision, tool calling).
241
+ # Source: https://platform.moonshot.ai (USD / 1M tokens)
242
+ "kimi-k3" => {
243
+ input: {
244
+ default: 3.00, # $3.00/MTok cache miss
245
+ over_200k: 3.00
246
+ },
247
+ output: {
248
+ default: 15.00, # $15.00/MTok
249
+ over_200k: 15.00
250
+ },
251
+ cache: {
252
+ write: 3.00, # no separate write charge; bill at miss rate
253
+ read: 0.30 # $0.30/MTok cache hit
254
+ }
255
+ },
256
+
257
+ # Kimi K2.7 Code (256K context, multimodal coding model).
258
+ # Source: https://platform.moonshot.ai (USD / 1M tokens)
259
+ "kimi-k2.7-code" => {
260
+ input: {
261
+ default: 0.95, # $0.95/MTok cache miss
262
+ over_200k: 0.95
263
+ },
264
+ output: {
265
+ default: 4.00, # $4.00/MTok
266
+ over_200k: 4.00
267
+ },
268
+ cache: {
269
+ write: 0.95, # no separate write charge; bill at miss rate
270
+ read: 0.19 # $0.19/MTok cache hit
271
+ }
272
+ },
273
+
274
+ "kimi-k2.7-code-highspeed" => {
275
+ input: {
276
+ default: 1.90, # $1.90/MTok cache miss
277
+ over_200k: 1.90
278
+ },
279
+ output: {
280
+ default: 8.00, # $8.00/MTok
281
+ over_200k: 8.00
282
+ },
283
+ cache: {
284
+ write: 1.90, # no separate write charge; bill at miss rate
285
+ read: 0.38 # $0.38/MTok cache hit
286
+ }
287
+ },
288
+
289
+ # Volcengine Ark (Doubao) — priced in CNY, converted at 1 USD = 6.7730 CNY
290
+ # (2026-07-23) and rounded up to the cent. Ark tiers by input length in K
291
+ # tokens; we map (32,128] → default and (128,256] → over_200k, matching
292
+ # the coarse two-tier structure used elsewhere. Single-tier [0,256] models
293
+ # use the same value for both. cache.write bills at the miss (input) rate.
294
+ "doubao-seed-evolving" => {
295
+ input: { default: 0.89, over_200k: 0.89 },
296
+ output: { default: 4.43, over_200k: 4.43 },
297
+ cache: { write: 0.89, read: 0.18 }
298
+ },
299
+ "doubao-seed-2.1-pro" => {
300
+ input: { default: 0.89, over_200k: 0.89 },
301
+ output: { default: 4.43, over_200k: 4.43 },
302
+ cache: { write: 0.89, read: 0.18 }
303
+ },
304
+ "doubao-seed-2.1-turbo" => {
305
+ input: { default: 0.45, over_200k: 0.45 },
306
+ output: { default: 2.22, over_200k: 2.22 },
307
+ cache: { write: 0.45, read: 0.09 }
308
+ },
309
+ "doubao-seed-2.0-pro" => {
310
+ input: { default: 0.71, over_200k: 1.42 },
311
+ output: { default: 3.55, over_200k: 7.09 },
312
+ cache: { write: 0.71, read: 0.15 }
313
+ },
314
+ "doubao-seed-2.0-code" => {
315
+ input: { default: 0.71, over_200k: 1.42 },
316
+ output: { default: 3.55, over_200k: 7.09 },
317
+ cache: { write: 0.71, read: 0.15 }
318
+ },
319
+ "doubao-seed-2.0-lite" => {
320
+ input: { default: 0.14, over_200k: 0.27 },
321
+ output: { default: 0.80, over_200k: 1.60 },
322
+ cache: { write: 0.14, read: 0.03 }
323
+ },
324
+
240
325
  # Google Gemini 3 series (via Vertex AI). Tiered at 200K input tokens
241
326
  # for Pro; Flash has flat pricing.
242
327
  "gemini-3.1-pro" => {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Clacky
4
- VERSION = "1.4.0"
4
+ VERSION = "1.5.0"
5
5
  end