openclacky 1.3.1 → 1.3.3
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 +44 -0
- data/Dockerfile +3 -0
- data/README.md +1 -1
- data/README_JA.md +237 -0
- data/lib/clacky/agent/session_serializer.rb +65 -11
- data/lib/clacky/agent/time_machine.rb +247 -26
- data/lib/clacky/agent.rb +12 -1
- data/lib/clacky/agent_config.rb +14 -2
- data/lib/clacky/brand_config.rb +1 -1
- 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 +30 -6
- data/lib/clacky/media/openai_compat.rb +64 -1
- data/lib/clacky/media/output_dir.rb +43 -0
- data/lib/clacky/message_history.rb +9 -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 +521 -13
- data/lib/clacky/server/server_master.rb +6 -4
- data/lib/clacky/utils/environment_detector.rb +16 -0
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +512 -60
- data/lib/clacky/web/app.js +30 -7
- 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/core/aside.js +112 -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} +134 -242
- 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 +303 -0
- data/lib/clacky/web/features/skills/view.js +550 -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 +60 -6
- data/lib/clacky/web/index.html +117 -57
- data/lib/clacky/web/sessions.js +221 -25
- data/lib/clacky/web/settings.js +121 -25
- data/lib/clacky/web/skills.js +3 -821
- data/lib/clacky/web/vendor/codemirror/codemirror.min.js +29 -0
- data/lib/clacky.rb +1 -0
- metadata +45 -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 -365
- data/lib/clacky/web/version.js +0 -449
- data/lib/clacky/web/workspace.js +0 -212
- /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/{onboard.js → components/onboard.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
data/lib/clacky.rb
CHANGED
|
@@ -128,6 +128,7 @@ require_relative "clacky/mcp/registry"
|
|
|
128
128
|
require_relative "clacky/mcp/skill_provider"
|
|
129
129
|
require_relative "clacky/media/base"
|
|
130
130
|
require_relative "clacky/media/openai_compat"
|
|
131
|
+
require_relative "clacky/media/output_dir"
|
|
131
132
|
require_relative "clacky/media/generator"
|
|
132
133
|
require_relative "clacky/vision/resolver"
|
|
133
134
|
require_relative "clacky/telemetry"
|
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.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- windy
|
|
@@ -288,6 +288,7 @@ files:
|
|
|
288
288
|
- LICENSE.txt
|
|
289
289
|
- README.md
|
|
290
290
|
- README_CN.md
|
|
291
|
+
- README_JA.md
|
|
291
292
|
- ROADMAP.md
|
|
292
293
|
- Rakefile
|
|
293
294
|
- benchmark/fixtures/sample_project/Gemfile
|
|
@@ -348,9 +349,12 @@ files:
|
|
|
348
349
|
- lib/clacky/client.rb
|
|
349
350
|
- lib/clacky/default_agents/SOUL.md
|
|
350
351
|
- lib/clacky/default_agents/USER.md
|
|
352
|
+
- lib/clacky/default_agents/_panels/git/panel.js
|
|
353
|
+
- lib/clacky/default_agents/_panels/time_machine/panel.js
|
|
351
354
|
- lib/clacky/default_agents/base_prompt.md
|
|
352
355
|
- lib/clacky/default_agents/coding/profile.yml
|
|
353
356
|
- lib/clacky/default_agents/coding/system_prompt.md
|
|
357
|
+
- lib/clacky/default_agents/coding/webui/.gitkeep
|
|
354
358
|
- lib/clacky/default_agents/general/profile.yml
|
|
355
359
|
- lib/clacky/default_agents/general/system_prompt.md
|
|
356
360
|
- lib/clacky/default_parsers/doc_parser.rb
|
|
@@ -420,6 +424,7 @@ files:
|
|
|
420
424
|
- lib/clacky/media/gemini.rb
|
|
421
425
|
- lib/clacky/media/generator.rb
|
|
422
426
|
- lib/clacky/media/openai_compat.rb
|
|
427
|
+
- lib/clacky/media/output_dir.rb
|
|
423
428
|
- lib/clacky/message_format/anthropic.rb
|
|
424
429
|
- lib/clacky/message_format/bedrock.rb
|
|
425
430
|
- lib/clacky/message_format/open_ai.rb
|
|
@@ -459,6 +464,7 @@ files:
|
|
|
459
464
|
- lib/clacky/server/channel/user_adapter_loader.rb
|
|
460
465
|
- lib/clacky/server/discover.rb
|
|
461
466
|
- lib/clacky/server/epipe_safe_io.rb
|
|
467
|
+
- lib/clacky/server/git_panel.rb
|
|
462
468
|
- lib/clacky/server/http_server.rb
|
|
463
469
|
- lib/clacky/server/scheduler.rb
|
|
464
470
|
- lib/clacky/server/server_master.rb
|
|
@@ -538,38 +544,58 @@ files:
|
|
|
538
544
|
- lib/clacky/web/app.css
|
|
539
545
|
- lib/clacky/web/app.js
|
|
540
546
|
- lib/clacky/web/apple-touch-icon-180.png
|
|
547
|
+
- lib/clacky/web/assets/notify.mp3
|
|
541
548
|
- 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/
|
|
549
|
+
- lib/clacky/web/components/code-editor.js
|
|
550
|
+
- lib/clacky/web/components/datepicker.js
|
|
551
|
+
- lib/clacky/web/components/notify.js
|
|
552
|
+
- lib/clacky/web/components/onboard.js
|
|
553
|
+
- lib/clacky/web/components/sidebar.js
|
|
554
|
+
- lib/clacky/web/core/aside.js
|
|
555
|
+
- lib/clacky/web/core/ext.js
|
|
548
556
|
- lib/clacky/web/design-sample.css
|
|
549
557
|
- lib/clacky/web/design-sample.html
|
|
550
558
|
- lib/clacky/web/favicon.ico
|
|
551
559
|
- lib/clacky/web/favicon.svg
|
|
560
|
+
- lib/clacky/web/features/backup/store.js
|
|
561
|
+
- lib/clacky/web/features/backup/view.js
|
|
562
|
+
- lib/clacky/web/features/billing/store.js
|
|
563
|
+
- lib/clacky/web/features/billing/view.js
|
|
564
|
+
- lib/clacky/web/features/brand/store.js
|
|
565
|
+
- lib/clacky/web/features/brand/view.js
|
|
566
|
+
- lib/clacky/web/features/channels/store.js
|
|
567
|
+
- lib/clacky/web/features/channels/view.js
|
|
568
|
+
- lib/clacky/web/features/creator/store.js
|
|
569
|
+
- lib/clacky/web/features/creator/view.js
|
|
570
|
+
- lib/clacky/web/features/mcp/store.js
|
|
571
|
+
- lib/clacky/web/features/mcp/view.js
|
|
572
|
+
- lib/clacky/web/features/model-tester/store.js
|
|
573
|
+
- lib/clacky/web/features/model-tester/view.js
|
|
574
|
+
- lib/clacky/web/features/profile/store.js
|
|
575
|
+
- lib/clacky/web/features/profile/view.js
|
|
576
|
+
- lib/clacky/web/features/share/store.js
|
|
577
|
+
- lib/clacky/web/features/share/view.js
|
|
578
|
+
- lib/clacky/web/features/skills/store.js
|
|
579
|
+
- lib/clacky/web/features/skills/view.js
|
|
580
|
+
- lib/clacky/web/features/tasks/store.js
|
|
581
|
+
- lib/clacky/web/features/tasks/view.js
|
|
582
|
+
- lib/clacky/web/features/trash/store.js
|
|
583
|
+
- lib/clacky/web/features/trash/view.js
|
|
584
|
+
- lib/clacky/web/features/version/store.js
|
|
585
|
+
- lib/clacky/web/features/version/view.js
|
|
586
|
+
- lib/clacky/web/features/workspace/store.js
|
|
587
|
+
- lib/clacky/web/features/workspace/view.js
|
|
552
588
|
- lib/clacky/web/i18n.js
|
|
553
589
|
- lib/clacky/web/icon-dark.svg
|
|
554
590
|
- lib/clacky/web/icon.svg
|
|
555
591
|
- lib/clacky/web/index.html
|
|
556
592
|
- 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
593
|
- lib/clacky/web/sessions.js
|
|
565
594
|
- lib/clacky/web/settings.js
|
|
566
|
-
- lib/clacky/web/share.js
|
|
567
|
-
- lib/clacky/web/sidebar.js
|
|
568
595
|
- lib/clacky/web/skills.js
|
|
569
|
-
- lib/clacky/web/tasks.js
|
|
570
596
|
- lib/clacky/web/theme.js
|
|
571
|
-
- lib/clacky/web/trash.js
|
|
572
597
|
- lib/clacky/web/utils.js
|
|
598
|
+
- lib/clacky/web/vendor/codemirror/codemirror.min.js
|
|
573
599
|
- lib/clacky/web/vendor/hljs/highlight.min.js
|
|
574
600
|
- lib/clacky/web/vendor/hljs/hljs-theme.css
|
|
575
601
|
- lib/clacky/web/vendor/katex/auto-render.min.js
|
|
@@ -595,10 +621,9 @@ files:
|
|
|
595
621
|
- lib/clacky/web/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2
|
|
596
622
|
- lib/clacky/web/vendor/katex/katex.min.css
|
|
597
623
|
- lib/clacky/web/vendor/katex/katex.min.js
|
|
624
|
+
- lib/clacky/web/vendor/marked/marked.min.js
|
|
598
625
|
- lib/clacky/web/vendor/qrcode/qrcode.min.js
|
|
599
|
-
- lib/clacky/web/version.js
|
|
600
626
|
- lib/clacky/web/weixin-qr.html
|
|
601
|
-
- lib/clacky/web/workspace.js
|
|
602
627
|
- lib/clacky/web/ws-dispatcher.js
|
|
603
628
|
- lib/clacky/web/ws.js
|
|
604
629
|
- 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
|
-
})();
|