openclacky 1.3.2 → 1.3.4
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 +4 -4
- data/CHANGELOG.md +54 -0
- data/Dockerfile +3 -0
- data/README.md +1 -1
- data/README_JA.md +237 -0
- data/docs/rich_ui_guide.md +277 -0
- data/docs/rich_ui_refactor_plan.md +396 -0
- data/lib/clacky/agent/llm_caller.rb +10 -4
- data/lib/clacky/agent/session_serializer.rb +52 -7
- data/lib/clacky/agent/time_machine.rb +247 -26
- data/lib/clacky/agent.rb +15 -3
- data/lib/clacky/api_extension.rb +262 -0
- data/lib/clacky/api_extension_loader.rb +156 -0
- data/lib/clacky/cli.rb +93 -3
- data/lib/clacky/client.rb +38 -13
- data/lib/clacky/default_agents/_panels/git/panel.js +201 -0
- data/lib/clacky/default_agents/_panels/time_machine/panel.js +640 -0
- data/lib/clacky/default_agents/coding/profile.yml +3 -0
- data/lib/clacky/default_agents/coding/webui/.gitkeep +0 -0
- data/lib/clacky/default_skills/cron-task-creator/SKILL.md +1 -1
- data/lib/clacky/default_skills/extend-openclacky/SKILL.md +6 -4
- data/lib/clacky/default_skills/media-gen/SKILL.md +37 -10
- data/lib/clacky/idle_compression_timer.rb +3 -1
- data/lib/clacky/locales/en.rb +26 -0
- data/lib/clacky/locales/i18n.rb +26 -0
- data/lib/clacky/locales/zh.rb +26 -0
- data/lib/clacky/media/openai_compat.rb +64 -1
- data/lib/clacky/message_history.rb +9 -0
- data/lib/clacky/rich_ui/components/base_component.rb +50 -0
- data/lib/clacky/rich_ui/components/dialogs/approval_dialog.rb +142 -0
- data/lib/clacky/rich_ui/components/dialogs/config_menu_dialog.rb +106 -0
- data/lib/clacky/rich_ui/components/dialogs/form_dialog.rb +128 -0
- data/lib/clacky/rich_ui/components/sidebar.rb +119 -0
- data/lib/clacky/rich_ui/components/sidebar_panels.rb +134 -0
- data/lib/clacky/rich_ui/components/status_view.rb +58 -0
- data/lib/clacky/rich_ui/components/thinking_live_view.rb +79 -0
- data/lib/clacky/rich_ui/entry_tracker.rb +56 -0
- data/lib/clacky/rich_ui/layout_adapter.rb +16 -0
- data/lib/clacky/rich_ui/progress_handle_adapter.rb +24 -0
- data/lib/clacky/rich_ui/rich_ui_controller.rb +868 -0
- data/lib/clacky/rich_ui/shell/rich_agent_shell.rb +184 -0
- data/lib/clacky/rich_ui/view_renderer.rb +291 -0
- data/lib/clacky/rich_ui.rb +57 -0
- data/lib/clacky/rich_ui_controller.rb +3 -1549
- data/lib/clacky/server/api_extension_dispatcher.rb +120 -0
- data/lib/clacky/server/channel/channel_manager.rb +26 -0
- data/lib/clacky/server/git_panel.rb +115 -0
- data/lib/clacky/server/http_server.rb +547 -15
- data/lib/clacky/server/server_master.rb +6 -4
- data/lib/clacky/server/session_registry.rb +1 -1
- data/lib/clacky/shell_hook_loader.rb +1 -1
- data/lib/clacky/tools/edit.rb +14 -2
- data/lib/clacky/ui2/ui_controller.rb +7 -0
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +511 -101
- data/lib/clacky/web/app.js +95 -14
- data/lib/clacky/web/components/code-editor.js +197 -0
- data/lib/clacky/web/{notify.js → components/notify.js} +1 -1
- data/lib/clacky/web/{onboard.js → components/onboard.js} +18 -2
- data/lib/clacky/web/core/aside.js +117 -0
- data/lib/clacky/web/core/ext.js +387 -0
- data/lib/clacky/web/features/backup/store.js +92 -0
- data/lib/clacky/web/features/backup/view.js +94 -0
- data/lib/clacky/web/features/billing/store.js +163 -0
- data/lib/clacky/web/{billing.js → features/billing/view.js} +132 -240
- data/lib/clacky/web/features/brand/store.js +110 -0
- data/lib/clacky/web/{brand.js → features/brand/view.js} +49 -199
- data/lib/clacky/web/features/channels/store.js +103 -0
- data/lib/clacky/web/{channels.js → features/channels/view.js} +50 -127
- data/lib/clacky/web/features/creator/store.js +81 -0
- data/lib/clacky/web/{creator.js → features/creator/view.js} +53 -102
- data/lib/clacky/web/features/mcp/store.js +158 -0
- data/lib/clacky/web/{mcp.js → features/mcp/view.js} +57 -134
- data/lib/clacky/web/features/model-tester/store.js +77 -0
- data/lib/clacky/web/features/model-tester/view.js +7 -0
- data/lib/clacky/web/features/profile/store.js +170 -0
- data/lib/clacky/web/{profile.js → features/profile/view.js} +94 -144
- data/lib/clacky/web/features/share/store.js +145 -0
- data/lib/clacky/web/{share.js → features/share/view.js} +66 -202
- data/lib/clacky/web/features/skills/store.js +331 -0
- data/lib/clacky/web/features/skills/view.js +581 -0
- data/lib/clacky/web/features/tasks/store.js +135 -0
- data/lib/clacky/web/features/tasks/view.js +241 -0
- data/lib/clacky/web/features/trash/store.js +242 -0
- data/lib/clacky/web/{trash.js → features/trash/view.js} +102 -293
- data/lib/clacky/web/features/version/store.js +165 -0
- data/lib/clacky/web/features/version/view.js +323 -0
- data/lib/clacky/web/features/workspace/store.js +99 -0
- data/lib/clacky/web/features/workspace/view.js +305 -0
- data/lib/clacky/web/i18n.js +68 -6
- data/lib/clacky/web/index.html +113 -62
- data/lib/clacky/web/sessions.js +493 -39
- data/lib/clacky/web/settings.js +143 -49
- data/lib/clacky/web/skills.js +3 -863
- data/lib/clacky/web/vendor/codemirror/codemirror.min.js +29 -0
- data/lib/clacky/web/ws-dispatcher.js +7 -3
- data/lib/clacky.rb +17 -1
- metadata +81 -20
- data/lib/clacky/web/backup.js +0 -119
- data/lib/clacky/web/model-tester.js +0 -66
- data/lib/clacky/web/tasks.js +0 -373
- data/lib/clacky/web/version.js +0 -449
- data/lib/clacky/web/workspace.js +0 -316
- /data/lib/clacky/web/{notify.mp3 → assets/notify.mp3} +0 -0
- /data/lib/clacky/web/{datepicker.js → components/datepicker.js} +0 -0
- /data/lib/clacky/web/{sidebar.js → components/sidebar.js} +0 -0
- /data/lib/clacky/web/{marked.min.js → vendor/marked/marked.min.js} +0 -0
|
@@ -217,7 +217,7 @@ WS.onEvent(ev => {
|
|
|
217
217
|
const pendingMsg = Sessions.takePendingMessage();
|
|
218
218
|
if (pendingMsg && pendingMsg.session_id === ev.session_id) {
|
|
219
219
|
Sessions.appendMsg("user", escapeHtml(pendingMsg.content), { time: new Date() });
|
|
220
|
-
WS.send({ type: "message", session_id: pendingMsg.session_id, content: pendingMsg.content });
|
|
220
|
+
WS.send({ type: "message", session_id: pendingMsg.session_id, content: pendingMsg.content, lang: I18n.lang() });
|
|
221
221
|
}
|
|
222
222
|
break;
|
|
223
223
|
}
|
|
@@ -298,8 +298,12 @@ WS.onEvent(ev => {
|
|
|
298
298
|
|
|
299
299
|
// ── Chat messages ──────────────────────────────────────────────────
|
|
300
300
|
case "history_user_message":
|
|
301
|
-
// Emitted
|
|
302
|
-
//
|
|
301
|
+
// Emitted by show_user_message before agent.run; stamp the authoritative
|
|
302
|
+
// created_at onto the optimistically-rendered bubble and register it in
|
|
303
|
+
// the dedup set so the subsequent history fetch skips this round.
|
|
304
|
+
if (ev.session_id === Sessions.activeId && ev.created_at) {
|
|
305
|
+
Sessions.stampLastUserBubble(ev.created_at);
|
|
306
|
+
}
|
|
303
307
|
break;
|
|
304
308
|
|
|
305
309
|
case "assistant_message":
|
data/lib/clacky.rb
CHANGED
|
@@ -68,6 +68,7 @@ require_relative "clacky/message_format/bedrock"
|
|
|
68
68
|
require_relative "clacky/bedrock_stream_aggregator"
|
|
69
69
|
require_relative "clacky/openai_stream_aggregator"
|
|
70
70
|
require_relative "clacky/anthropic_stream_aggregator"
|
|
71
|
+
require_relative "clacky/locales/i18n"
|
|
71
72
|
require_relative "clacky/client"
|
|
72
73
|
require_relative "clacky/skill"
|
|
73
74
|
require_relative "clacky/skill_loader"
|
|
@@ -144,10 +145,25 @@ require_relative "clacky/cli"
|
|
|
144
145
|
require_relative "clacky/patch_loader"
|
|
145
146
|
Clacky::PatchLoader.load_all
|
|
146
147
|
|
|
148
|
+
# HTTP API extension layer: define the base class + loader + dispatcher.
|
|
149
|
+
# Loading of user extensions is deferred to HttpServer#start so the host
|
|
150
|
+
# process is fully initialized before extension handlers can resolve helpers
|
|
151
|
+
# like session_manager or agent_config.
|
|
152
|
+
require_relative "clacky/api_extension"
|
|
153
|
+
require_relative "clacky/api_extension_loader"
|
|
154
|
+
require_relative "clacky/server/api_extension_dispatcher"
|
|
155
|
+
|
|
147
156
|
module Clacky
|
|
148
157
|
class AgentInterrupted < Exception; end # Inherit from Exception to bypass rescue StandardError
|
|
149
158
|
class AgentError < StandardError; end
|
|
150
|
-
class BadRequestError < AgentError
|
|
159
|
+
class BadRequestError < AgentError
|
|
160
|
+
attr_reader :display_message
|
|
161
|
+
|
|
162
|
+
def initialize(message, display_message: nil)
|
|
163
|
+
super(message)
|
|
164
|
+
@display_message = display_message
|
|
165
|
+
end
|
|
166
|
+
end
|
|
151
167
|
class InsufficientCreditError < AgentError
|
|
152
168
|
attr_reader :error_code, :provider_id
|
|
153
169
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openclacky
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- windy
|
|
@@ -245,6 +245,20 @@ dependencies:
|
|
|
245
245
|
- - "<"
|
|
246
246
|
- !ruby/object:Gem::Version
|
|
247
247
|
version: '5.0'
|
|
248
|
+
- !ruby/object:Gem::Dependency
|
|
249
|
+
name: ruby_rich
|
|
250
|
+
requirement: !ruby/object:Gem::Requirement
|
|
251
|
+
requirements:
|
|
252
|
+
- - "~>"
|
|
253
|
+
- !ruby/object:Gem::Version
|
|
254
|
+
version: 0.5.2
|
|
255
|
+
type: :runtime
|
|
256
|
+
prerelease: false
|
|
257
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
258
|
+
requirements:
|
|
259
|
+
- - "~>"
|
|
260
|
+
- !ruby/object:Gem::Version
|
|
261
|
+
version: 0.5.2
|
|
248
262
|
- !ruby/object:Gem::Dependency
|
|
249
263
|
name: chunky_png
|
|
250
264
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -288,6 +302,7 @@ files:
|
|
|
288
302
|
- LICENSE.txt
|
|
289
303
|
- README.md
|
|
290
304
|
- README_CN.md
|
|
305
|
+
- README_JA.md
|
|
291
306
|
- ROADMAP.md
|
|
292
307
|
- Rakefile
|
|
293
308
|
- benchmark/fixtures/sample_project/Gemfile
|
|
@@ -312,6 +327,8 @@ files:
|
|
|
312
327
|
- docs/engineering-article.md
|
|
313
328
|
- docs/mcp-architecture.md
|
|
314
329
|
- docs/mcp.example.json
|
|
330
|
+
- docs/rich_ui_guide.md
|
|
331
|
+
- docs/rich_ui_refactor_plan.md
|
|
315
332
|
- docs/session-skill-invocation.md
|
|
316
333
|
- docs/time_machine_design.md
|
|
317
334
|
- docs/ui2-architecture.md
|
|
@@ -338,6 +355,8 @@ files:
|
|
|
338
355
|
- lib/clacky/agent_config.rb
|
|
339
356
|
- lib/clacky/agent_profile.rb
|
|
340
357
|
- lib/clacky/anthropic_stream_aggregator.rb
|
|
358
|
+
- lib/clacky/api_extension.rb
|
|
359
|
+
- lib/clacky/api_extension_loader.rb
|
|
341
360
|
- lib/clacky/banner.rb
|
|
342
361
|
- lib/clacky/bedrock_stream_aggregator.rb
|
|
343
362
|
- lib/clacky/billing/billing_record.rb
|
|
@@ -348,9 +367,12 @@ files:
|
|
|
348
367
|
- lib/clacky/client.rb
|
|
349
368
|
- lib/clacky/default_agents/SOUL.md
|
|
350
369
|
- lib/clacky/default_agents/USER.md
|
|
370
|
+
- lib/clacky/default_agents/_panels/git/panel.js
|
|
371
|
+
- lib/clacky/default_agents/_panels/time_machine/panel.js
|
|
351
372
|
- lib/clacky/default_agents/base_prompt.md
|
|
352
373
|
- lib/clacky/default_agents/coding/profile.yml
|
|
353
374
|
- lib/clacky/default_agents/coding/system_prompt.md
|
|
375
|
+
- lib/clacky/default_agents/coding/webui/.gitkeep
|
|
354
376
|
- lib/clacky/default_agents/general/profile.yml
|
|
355
377
|
- lib/clacky/default_agents/general/system_prompt.md
|
|
356
378
|
- lib/clacky/default_parsers/doc_parser.rb
|
|
@@ -408,6 +430,9 @@ files:
|
|
|
408
430
|
- lib/clacky/default_skills/skill-creator/scripts/validate_skill_frontmatter.rb
|
|
409
431
|
- lib/clacky/idle_compression_timer.rb
|
|
410
432
|
- lib/clacky/json_ui_controller.rb
|
|
433
|
+
- lib/clacky/locales/en.rb
|
|
434
|
+
- lib/clacky/locales/i18n.rb
|
|
435
|
+
- lib/clacky/locales/zh.rb
|
|
411
436
|
- lib/clacky/mcp/client.rb
|
|
412
437
|
- lib/clacky/mcp/http_transport.rb
|
|
413
438
|
- lib/clacky/mcp/registry.rb
|
|
@@ -430,7 +455,23 @@ files:
|
|
|
430
455
|
- lib/clacky/platform_http_client.rb
|
|
431
456
|
- lib/clacky/providers.rb
|
|
432
457
|
- lib/clacky/proxy_config.rb
|
|
458
|
+
- lib/clacky/rich_ui.rb
|
|
459
|
+
- lib/clacky/rich_ui/components/base_component.rb
|
|
460
|
+
- lib/clacky/rich_ui/components/dialogs/approval_dialog.rb
|
|
461
|
+
- lib/clacky/rich_ui/components/dialogs/config_menu_dialog.rb
|
|
462
|
+
- lib/clacky/rich_ui/components/dialogs/form_dialog.rb
|
|
463
|
+
- lib/clacky/rich_ui/components/sidebar.rb
|
|
464
|
+
- lib/clacky/rich_ui/components/sidebar_panels.rb
|
|
465
|
+
- lib/clacky/rich_ui/components/status_view.rb
|
|
466
|
+
- lib/clacky/rich_ui/components/thinking_live_view.rb
|
|
467
|
+
- lib/clacky/rich_ui/entry_tracker.rb
|
|
468
|
+
- lib/clacky/rich_ui/layout_adapter.rb
|
|
469
|
+
- lib/clacky/rich_ui/progress_handle_adapter.rb
|
|
470
|
+
- lib/clacky/rich_ui/rich_ui_controller.rb
|
|
471
|
+
- lib/clacky/rich_ui/shell/rich_agent_shell.rb
|
|
472
|
+
- lib/clacky/rich_ui/view_renderer.rb
|
|
433
473
|
- lib/clacky/rich_ui_controller.rb
|
|
474
|
+
- lib/clacky/server/api_extension_dispatcher.rb
|
|
434
475
|
- lib/clacky/server/backup_manager.rb
|
|
435
476
|
- lib/clacky/server/browser_manager.rb
|
|
436
477
|
- lib/clacky/server/channel.rb
|
|
@@ -459,6 +500,7 @@ files:
|
|
|
459
500
|
- lib/clacky/server/channel/user_adapter_loader.rb
|
|
460
501
|
- lib/clacky/server/discover.rb
|
|
461
502
|
- lib/clacky/server/epipe_safe_io.rb
|
|
503
|
+
- lib/clacky/server/git_panel.rb
|
|
462
504
|
- lib/clacky/server/http_server.rb
|
|
463
505
|
- lib/clacky/server/scheduler.rb
|
|
464
506
|
- lib/clacky/server/server_master.rb
|
|
@@ -538,38 +580,58 @@ files:
|
|
|
538
580
|
- lib/clacky/web/app.css
|
|
539
581
|
- lib/clacky/web/app.js
|
|
540
582
|
- lib/clacky/web/apple-touch-icon-180.png
|
|
583
|
+
- lib/clacky/web/assets/notify.mp3
|
|
541
584
|
- lib/clacky/web/auth.js
|
|
542
|
-
- lib/clacky/web/
|
|
543
|
-
- lib/clacky/web/
|
|
544
|
-
- lib/clacky/web/
|
|
545
|
-
- lib/clacky/web/
|
|
546
|
-
- lib/clacky/web/
|
|
547
|
-
- lib/clacky/web/
|
|
585
|
+
- lib/clacky/web/components/code-editor.js
|
|
586
|
+
- lib/clacky/web/components/datepicker.js
|
|
587
|
+
- lib/clacky/web/components/notify.js
|
|
588
|
+
- lib/clacky/web/components/onboard.js
|
|
589
|
+
- lib/clacky/web/components/sidebar.js
|
|
590
|
+
- lib/clacky/web/core/aside.js
|
|
591
|
+
- lib/clacky/web/core/ext.js
|
|
548
592
|
- lib/clacky/web/design-sample.css
|
|
549
593
|
- lib/clacky/web/design-sample.html
|
|
550
594
|
- lib/clacky/web/favicon.ico
|
|
551
595
|
- lib/clacky/web/favicon.svg
|
|
596
|
+
- lib/clacky/web/features/backup/store.js
|
|
597
|
+
- lib/clacky/web/features/backup/view.js
|
|
598
|
+
- lib/clacky/web/features/billing/store.js
|
|
599
|
+
- lib/clacky/web/features/billing/view.js
|
|
600
|
+
- lib/clacky/web/features/brand/store.js
|
|
601
|
+
- lib/clacky/web/features/brand/view.js
|
|
602
|
+
- lib/clacky/web/features/channels/store.js
|
|
603
|
+
- lib/clacky/web/features/channels/view.js
|
|
604
|
+
- lib/clacky/web/features/creator/store.js
|
|
605
|
+
- lib/clacky/web/features/creator/view.js
|
|
606
|
+
- lib/clacky/web/features/mcp/store.js
|
|
607
|
+
- lib/clacky/web/features/mcp/view.js
|
|
608
|
+
- lib/clacky/web/features/model-tester/store.js
|
|
609
|
+
- lib/clacky/web/features/model-tester/view.js
|
|
610
|
+
- lib/clacky/web/features/profile/store.js
|
|
611
|
+
- lib/clacky/web/features/profile/view.js
|
|
612
|
+
- lib/clacky/web/features/share/store.js
|
|
613
|
+
- lib/clacky/web/features/share/view.js
|
|
614
|
+
- lib/clacky/web/features/skills/store.js
|
|
615
|
+
- lib/clacky/web/features/skills/view.js
|
|
616
|
+
- lib/clacky/web/features/tasks/store.js
|
|
617
|
+
- lib/clacky/web/features/tasks/view.js
|
|
618
|
+
- lib/clacky/web/features/trash/store.js
|
|
619
|
+
- lib/clacky/web/features/trash/view.js
|
|
620
|
+
- lib/clacky/web/features/version/store.js
|
|
621
|
+
- lib/clacky/web/features/version/view.js
|
|
622
|
+
- lib/clacky/web/features/workspace/store.js
|
|
623
|
+
- lib/clacky/web/features/workspace/view.js
|
|
552
624
|
- lib/clacky/web/i18n.js
|
|
553
625
|
- lib/clacky/web/icon-dark.svg
|
|
554
626
|
- lib/clacky/web/icon.svg
|
|
555
627
|
- lib/clacky/web/index.html
|
|
556
628
|
- lib/clacky/web/logo_nav_dark.png
|
|
557
|
-
- lib/clacky/web/marked.min.js
|
|
558
|
-
- lib/clacky/web/mcp.js
|
|
559
|
-
- lib/clacky/web/model-tester.js
|
|
560
|
-
- lib/clacky/web/notify.js
|
|
561
|
-
- lib/clacky/web/notify.mp3
|
|
562
|
-
- lib/clacky/web/onboard.js
|
|
563
|
-
- lib/clacky/web/profile.js
|
|
564
629
|
- lib/clacky/web/sessions.js
|
|
565
630
|
- lib/clacky/web/settings.js
|
|
566
|
-
- lib/clacky/web/share.js
|
|
567
|
-
- lib/clacky/web/sidebar.js
|
|
568
631
|
- lib/clacky/web/skills.js
|
|
569
|
-
- lib/clacky/web/tasks.js
|
|
570
632
|
- lib/clacky/web/theme.js
|
|
571
|
-
- lib/clacky/web/trash.js
|
|
572
633
|
- lib/clacky/web/utils.js
|
|
634
|
+
- lib/clacky/web/vendor/codemirror/codemirror.min.js
|
|
573
635
|
- lib/clacky/web/vendor/hljs/highlight.min.js
|
|
574
636
|
- lib/clacky/web/vendor/hljs/hljs-theme.css
|
|
575
637
|
- lib/clacky/web/vendor/katex/auto-render.min.js
|
|
@@ -595,10 +657,9 @@ files:
|
|
|
595
657
|
- lib/clacky/web/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2
|
|
596
658
|
- lib/clacky/web/vendor/katex/katex.min.css
|
|
597
659
|
- lib/clacky/web/vendor/katex/katex.min.js
|
|
660
|
+
- lib/clacky/web/vendor/marked/marked.min.js
|
|
598
661
|
- lib/clacky/web/vendor/qrcode/qrcode.min.js
|
|
599
|
-
- lib/clacky/web/version.js
|
|
600
662
|
- lib/clacky/web/weixin-qr.html
|
|
601
|
-
- lib/clacky/web/workspace.js
|
|
602
663
|
- lib/clacky/web/ws-dispatcher.js
|
|
603
664
|
- lib/clacky/web/ws.js
|
|
604
665
|
- scripts/build/build.sh
|
data/lib/clacky/web/backup.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// backup.js — Backup settings panel (Settings → General → Backup).
|
|
2
|
-
//
|
|
3
|
-
// Talks to /api/backup/{status,config,download}. Lets the user toggle automatic
|
|
4
|
-
// backups (handled by the server-side Scheduler — fixed daily at 03:00, keeps 7)
|
|
5
|
-
// and download a one-off archive directly to the browser.
|
|
6
|
-
|
|
7
|
-
const Backup = (() => {
|
|
8
|
-
|
|
9
|
-
let _status = null;
|
|
10
|
-
let _saving = false;
|
|
11
|
-
|
|
12
|
-
async function load() {
|
|
13
|
-
try {
|
|
14
|
-
const res = await fetch("/api/backup/status");
|
|
15
|
-
_status = await res.json();
|
|
16
|
-
_render();
|
|
17
|
-
} catch (e) {
|
|
18
|
-
// Backup section is non-critical; fail quietly.
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function _render() {
|
|
23
|
-
if (!_status) return;
|
|
24
|
-
const cfg = _status.config || {};
|
|
25
|
-
|
|
26
|
-
const incl = $("backup-include-sessions");
|
|
27
|
-
if (incl) incl.checked = cfg.include_sessions !== false;
|
|
28
|
-
|
|
29
|
-
const autoToggle = $("backup-auto-toggle");
|
|
30
|
-
if (autoToggle) autoToggle.checked = !!cfg.enabled;
|
|
31
|
-
|
|
32
|
-
_renderLastRun(cfg);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function _renderLastRun(cfg) {
|
|
36
|
-
const el = $("backup-status");
|
|
37
|
-
if (!el) return;
|
|
38
|
-
if (!cfg.last_run_at) { el.textContent = ""; el.className = "model-test-result"; return; }
|
|
39
|
-
if (cfg.last_status === "error") {
|
|
40
|
-
el.textContent = I18n.t("settings.backup.lastError", { msg: cfg.last_error || "" });
|
|
41
|
-
el.className = "model-test-result error";
|
|
42
|
-
} else {
|
|
43
|
-
el.textContent = I18n.t("settings.backup.lastOk", { time: _fmtDate(cfg.last_run_at) });
|
|
44
|
-
el.className = "model-test-result success";
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function _downloadNow() {
|
|
49
|
-
const btn = $("btn-backup-now");
|
|
50
|
-
const el = $("backup-status");
|
|
51
|
-
if (btn) btn.disabled = true;
|
|
52
|
-
if (el) { el.textContent = I18n.t("settings.backup.running"); el.className = "model-test-result"; }
|
|
53
|
-
try {
|
|
54
|
-
const res = await fetch("/api/backup/download");
|
|
55
|
-
if (!res.ok) {
|
|
56
|
-
let msg = "failed";
|
|
57
|
-
try { msg = (await res.json()).error || msg; } catch (e) {}
|
|
58
|
-
throw new Error(msg);
|
|
59
|
-
}
|
|
60
|
-
const blob = await res.blob();
|
|
61
|
-
const cd = res.headers.get("Content-Disposition") || "";
|
|
62
|
-
const m = cd.match(/filename="?([^"]+)"?/);
|
|
63
|
-
const name = (m && m[1]) || "clacky-backup.tar.gz";
|
|
64
|
-
const url = URL.createObjectURL(blob);
|
|
65
|
-
const a = document.createElement("a");
|
|
66
|
-
a.href = url;
|
|
67
|
-
a.download = name;
|
|
68
|
-
document.body.appendChild(a);
|
|
69
|
-
a.click();
|
|
70
|
-
a.remove();
|
|
71
|
-
URL.revokeObjectURL(url);
|
|
72
|
-
if (el) { el.textContent = I18n.t("settings.backup.downloaded"); el.className = "model-test-result success"; }
|
|
73
|
-
} catch (e) {
|
|
74
|
-
if (el) { el.textContent = I18n.t("settings.backup.lastError", { msg: e.message }); el.className = "model-test-result error"; }
|
|
75
|
-
} finally {
|
|
76
|
-
if (btn) btn.disabled = false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async function _saveConfig(patch) {
|
|
81
|
-
if (_saving) return;
|
|
82
|
-
_saving = true;
|
|
83
|
-
try {
|
|
84
|
-
const res = await fetch("/api/backup/config", {
|
|
85
|
-
method: "PATCH",
|
|
86
|
-
headers: { "Content-Type": "application/json" },
|
|
87
|
-
body: JSON.stringify(patch)
|
|
88
|
-
});
|
|
89
|
-
const data = await res.json();
|
|
90
|
-
if (data.ok) { _status = data.status; _render(); }
|
|
91
|
-
} catch (e) {
|
|
92
|
-
// ignore — next load will resync
|
|
93
|
-
} finally {
|
|
94
|
-
_saving = false;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function _fmtDate(iso) {
|
|
99
|
-
if (!iso) return "";
|
|
100
|
-
try { return new Date(iso).toLocaleString(); } catch (e) { return iso; }
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function _bind() {
|
|
104
|
-
const btn = $("btn-backup-now");
|
|
105
|
-
if (btn) btn.addEventListener("click", _downloadNow);
|
|
106
|
-
|
|
107
|
-
const autoToggle = $("backup-auto-toggle");
|
|
108
|
-
if (autoToggle) autoToggle.addEventListener("change", () => _saveConfig({ enabled: autoToggle.checked }));
|
|
109
|
-
|
|
110
|
-
const incl = $("backup-include-sessions");
|
|
111
|
-
if (incl) incl.addEventListener("change", () => _saveConfig({ include_sessions: incl.checked }));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
document.addEventListener("DOMContentLoaded", _bind);
|
|
115
|
-
|
|
116
|
-
return { load };
|
|
117
|
-
})();
|
|
118
|
-
|
|
119
|
-
window.Backup = Backup;
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// Shared helpers for the model config UI flows.
|
|
2
|
-
// Used by both the onboarding wizard and the settings model modal.
|
|
3
|
-
window.ModelTester = (function () {
|
|
4
|
-
// Test a model connection.
|
|
5
|
-
// Returns one of:
|
|
6
|
-
// { ok: true, base_url, message } — connected, no rewrite
|
|
7
|
-
// { ok: true, base_url, message, rewrote: true } — connected, base_url auto-corrected (/v1 appended)
|
|
8
|
-
// { ok: false, message } — failed (server-reported or network)
|
|
9
|
-
async function testConnection({ model, base_url, api_key, anthropic_format, index, id } = {}) {
|
|
10
|
-
const body = { model, base_url, api_key };
|
|
11
|
-
if (typeof id === "string" && id) body.id = id;
|
|
12
|
-
if (typeof index === "number") body.index = index;
|
|
13
|
-
if (anthropic_format) body.anthropic_format = true;
|
|
14
|
-
|
|
15
|
-
let data;
|
|
16
|
-
try {
|
|
17
|
-
const res = await fetch("/api/config/test", {
|
|
18
|
-
method: "POST",
|
|
19
|
-
headers: { "Content-Type": "application/json" },
|
|
20
|
-
body: JSON.stringify(body)
|
|
21
|
-
});
|
|
22
|
-
data = await res.json();
|
|
23
|
-
} catch (e) {
|
|
24
|
-
return { ok: false, message: e.message };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!data.ok) {
|
|
28
|
-
const msg = data.message || "";
|
|
29
|
-
const code = data.error_code || "";
|
|
30
|
-
if (code === "insufficient_credit") {
|
|
31
|
-
return { ok: false, message: I18n.t("error.insufficient_credit"), error_code: code };
|
|
32
|
-
}
|
|
33
|
-
return { ok: false, message: msg, error_code: code };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (data.effective_base_url && data.effective_base_url !== base_url) {
|
|
37
|
-
return { ok: true, base_url: data.effective_base_url, message: data.message || "", rewrote: true };
|
|
38
|
-
}
|
|
39
|
-
return { ok: true, base_url, message: data.message || "" };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Persist a model config (create or update).
|
|
43
|
-
// existingId === null/undefined → POST /api/config/models (create).
|
|
44
|
-
// existingId === string → PATCH /api/config/models/:id (update).
|
|
45
|
-
// Returns { ok: bool, error? }.
|
|
46
|
-
async function saveModel(payload, { existingId } = {}) {
|
|
47
|
-
const url = existingId
|
|
48
|
-
? `/api/config/models/${encodeURIComponent(existingId)}`
|
|
49
|
-
: "/api/config/models";
|
|
50
|
-
const method = existingId ? "PATCH" : "POST";
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
const res = await fetch(url, {
|
|
54
|
-
method,
|
|
55
|
-
headers: { "Content-Type": "application/json" },
|
|
56
|
-
body: JSON.stringify(payload)
|
|
57
|
-
});
|
|
58
|
-
const data = await res.json();
|
|
59
|
-
return data.ok ? { ok: true } : { ok: false, error: data.error || "" };
|
|
60
|
-
} catch (e) {
|
|
61
|
-
return { ok: false, error: e.message };
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return { testConnection, saveModel };
|
|
66
|
-
})();
|