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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/Dockerfile +3 -0
  4. data/README.md +1 -1
  5. data/README_JA.md +237 -0
  6. data/lib/clacky/agent/session_serializer.rb +65 -11
  7. data/lib/clacky/agent/time_machine.rb +247 -26
  8. data/lib/clacky/agent.rb +12 -1
  9. data/lib/clacky/agent_config.rb +14 -2
  10. data/lib/clacky/brand_config.rb +1 -1
  11. data/lib/clacky/default_agents/_panels/git/panel.js +201 -0
  12. data/lib/clacky/default_agents/_panels/time_machine/panel.js +640 -0
  13. data/lib/clacky/default_agents/coding/profile.yml +3 -0
  14. data/lib/clacky/default_agents/coding/webui/.gitkeep +0 -0
  15. data/lib/clacky/default_skills/cron-task-creator/SKILL.md +1 -1
  16. data/lib/clacky/default_skills/extend-openclacky/SKILL.md +6 -4
  17. data/lib/clacky/default_skills/media-gen/SKILL.md +30 -6
  18. data/lib/clacky/media/openai_compat.rb +64 -1
  19. data/lib/clacky/media/output_dir.rb +43 -0
  20. data/lib/clacky/message_history.rb +9 -0
  21. data/lib/clacky/server/channel/channel_manager.rb +26 -0
  22. data/lib/clacky/server/git_panel.rb +115 -0
  23. data/lib/clacky/server/http_server.rb +521 -13
  24. data/lib/clacky/server/server_master.rb +6 -4
  25. data/lib/clacky/utils/environment_detector.rb +16 -0
  26. data/lib/clacky/version.rb +1 -1
  27. data/lib/clacky/web/app.css +512 -60
  28. data/lib/clacky/web/app.js +30 -7
  29. data/lib/clacky/web/components/code-editor.js +197 -0
  30. data/lib/clacky/web/{notify.js → components/notify.js} +1 -1
  31. data/lib/clacky/web/core/aside.js +112 -0
  32. data/lib/clacky/web/core/ext.js +387 -0
  33. data/lib/clacky/web/features/backup/store.js +92 -0
  34. data/lib/clacky/web/features/backup/view.js +94 -0
  35. data/lib/clacky/web/features/billing/store.js +163 -0
  36. data/lib/clacky/web/{billing.js → features/billing/view.js} +134 -242
  37. data/lib/clacky/web/features/brand/store.js +110 -0
  38. data/lib/clacky/web/{brand.js → features/brand/view.js} +49 -199
  39. data/lib/clacky/web/features/channels/store.js +103 -0
  40. data/lib/clacky/web/{channels.js → features/channels/view.js} +50 -127
  41. data/lib/clacky/web/features/creator/store.js +81 -0
  42. data/lib/clacky/web/{creator.js → features/creator/view.js} +53 -102
  43. data/lib/clacky/web/features/mcp/store.js +158 -0
  44. data/lib/clacky/web/{mcp.js → features/mcp/view.js} +57 -134
  45. data/lib/clacky/web/features/model-tester/store.js +77 -0
  46. data/lib/clacky/web/features/model-tester/view.js +7 -0
  47. data/lib/clacky/web/features/profile/store.js +170 -0
  48. data/lib/clacky/web/{profile.js → features/profile/view.js} +94 -144
  49. data/lib/clacky/web/features/share/store.js +145 -0
  50. data/lib/clacky/web/{share.js → features/share/view.js} +66 -202
  51. data/lib/clacky/web/features/skills/store.js +303 -0
  52. data/lib/clacky/web/features/skills/view.js +550 -0
  53. data/lib/clacky/web/features/tasks/store.js +135 -0
  54. data/lib/clacky/web/features/tasks/view.js +241 -0
  55. data/lib/clacky/web/features/trash/store.js +242 -0
  56. data/lib/clacky/web/{trash.js → features/trash/view.js} +102 -293
  57. data/lib/clacky/web/features/version/store.js +165 -0
  58. data/lib/clacky/web/features/version/view.js +323 -0
  59. data/lib/clacky/web/features/workspace/store.js +99 -0
  60. data/lib/clacky/web/features/workspace/view.js +305 -0
  61. data/lib/clacky/web/i18n.js +60 -6
  62. data/lib/clacky/web/index.html +117 -57
  63. data/lib/clacky/web/sessions.js +221 -25
  64. data/lib/clacky/web/settings.js +121 -25
  65. data/lib/clacky/web/skills.js +3 -821
  66. data/lib/clacky/web/vendor/codemirror/codemirror.min.js +29 -0
  67. data/lib/clacky.rb +1 -0
  68. metadata +45 -20
  69. data/lib/clacky/web/backup.js +0 -119
  70. data/lib/clacky/web/model-tester.js +0 -66
  71. data/lib/clacky/web/tasks.js +0 -365
  72. data/lib/clacky/web/version.js +0 -449
  73. data/lib/clacky/web/workspace.js +0 -212
  74. /data/lib/clacky/web/{notify.mp3 → assets/notify.mp3} +0 -0
  75. /data/lib/clacky/web/{datepicker.js → components/datepicker.js} +0 -0
  76. /data/lib/clacky/web/{onboard.js → components/onboard.js} +0 -0
  77. /data/lib/clacky/web/{sidebar.js → components/sidebar.js} +0 -0
  78. /data/lib/clacky/web/{marked.min.js → vendor/marked/marked.min.js} +0 -0
@@ -0,0 +1,145 @@
1
+ // ── Share · store — brand source, scorecard state, frequency cap, telemetry
2
+ //
3
+ // Owns the share feature's data: the brand identity (hydrated from
4
+ // /api/brand/status), the live scorecard stats + selected period, the
5
+ // success-count / cooldown frequency cap (localStorage), and the telemetry
6
+ // beacon. It never renders.
7
+ //
8
+ // White-label safety lives here: a branded build NEVER falls back to
9
+ // openclacky.com — the homepage/QR url is simply null when unset.
10
+ //
11
+ // `Share` stays the single public facade.
12
+ //
13
+ // Depends on: Clacky.ext.
14
+ // ───────────────────────────────────────────────────────────────────────────
15
+
16
+ const ShareStore = (() => {
17
+ const DEFAULT_NAME = "OpenClacky";
18
+ const DEFAULT_HOMEPAGE = "https://www.openclacky.com/";
19
+
20
+ const COUNT_KEY = "clacky-share-success-count";
21
+ const COOLDOWN_KEY = "clacky-share-cooldown-until";
22
+ const COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000;
23
+ const PROMPT_AT = [1, 5];
24
+
25
+ let _brand = { name: DEFAULT_NAME, homepageUrl: DEFAULT_HOMEPAGE, logoUrl: null };
26
+ let _scorecard = null;
27
+ let _scorePeriod = null;
28
+
29
+ const _listeners = {};
30
+
31
+ function _on(event, handler) {
32
+ (_listeners[event] ||= []).push(handler);
33
+ return () => {
34
+ const list = _listeners[event];
35
+ const i = list ? list.indexOf(handler) : -1;
36
+ if (i >= 0) list.splice(i, 1);
37
+ };
38
+ }
39
+
40
+ function _emit(event, payload) {
41
+ (_listeners[event] || []).forEach((h) => h(payload));
42
+ if (window.Clacky && Clacky.ext) Clacky.ext.emit(event, payload);
43
+ }
44
+
45
+ const state = {
46
+ get brand() { return _brand; },
47
+ get scorecard() { return _scorecard; },
48
+ get scorePeriod() { return _scorePeriod; },
49
+ set scorePeriod(p) { _scorePeriod = p; },
50
+ shareUrl() { return _brand.homepageUrl; },
51
+
52
+ /** Stats for the selected period, falling back to flat fields. */
53
+ curStats() {
54
+ if (_scorecard && _scorecard.periods && _scorePeriod && _scorecard.periods[_scorePeriod]) {
55
+ return _scorecard.periods[_scorePeriod];
56
+ }
57
+ return _scorecard;
58
+ },
59
+ };
60
+
61
+ async function _hydrateBrand() {
62
+ try {
63
+ const res = await fetch("/api/brand/status");
64
+ if (!res.ok) return;
65
+ const data = await res.json();
66
+ if (data && data.branded) {
67
+ _brand = {
68
+ name: (data.product_name || "").trim() || DEFAULT_NAME,
69
+ homepageUrl: (data.homepage_url || "").trim() || null,
70
+ logoUrl: (data.logo_url || "").trim() || null
71
+ };
72
+ _emit("share:brandChanged", _brand);
73
+ }
74
+ } catch (_e) { /* keep defaults */ }
75
+ }
76
+
77
+ function _successCount() {
78
+ return parseInt(localStorage.getItem(COUNT_KEY) || "0", 10) || 0;
79
+ }
80
+ function _bumpSuccessCount() {
81
+ const n = _successCount() + 1;
82
+ localStorage.setItem(COUNT_KEY, String(n));
83
+ return n;
84
+ }
85
+ function _inCooldown() {
86
+ const until = parseInt(localStorage.getItem(COOLDOWN_KEY) || "0", 10) || 0;
87
+ return Date.now() < until;
88
+ }
89
+ function _startCooldown() {
90
+ localStorage.setItem(COOLDOWN_KEY, String(Date.now() + COOLDOWN_MS));
91
+ }
92
+
93
+ const Share = {
94
+ on: _on,
95
+ state,
96
+ PROMPT_AT,
97
+
98
+ hydrateBrand: _hydrateBrand,
99
+
100
+ /** Enter scorecard mode with live stats; picks the default period. */
101
+ setScorecard(stats) {
102
+ _scorecard = stats || null;
103
+ _scorePeriod = _scorecard
104
+ ? (_scorecard.defaultPeriod && _scorecard.periods && _scorecard.periods[_scorecard.defaultPeriod]
105
+ ? _scorecard.defaultPeriod
106
+ : (_scorecard.periods ? Object.keys(_scorecard.periods)[0] : null))
107
+ : null;
108
+ _emit("share:scorecardChanged", { scorecard: _scorecard, period: _scorePeriod });
109
+ },
110
+
111
+ /** Merge a late-arriving period's stats and notify the view. */
112
+ addScorecardPeriod(key, stats) {
113
+ if (!_scorecard) return false;
114
+ if (!_scorecard.periods) _scorecard.periods = {};
115
+ _scorecard.periods[key] = stats;
116
+ _emit("share:scorecardPeriodAdded", { key, stats });
117
+ return true;
118
+ },
119
+
120
+ clearScorecard() {
121
+ _scorecard = null;
122
+ _scorePeriod = null;
123
+ _emit("share:scorecardChanged", { scorecard: null, period: null });
124
+ },
125
+
126
+ /** Bump success count and decide whether to auto-prompt; starts cooldown. */
127
+ consumeSuccess() {
128
+ const n = _bumpSuccessCount();
129
+ if (_inCooldown()) return { prompt: false };
130
+ if (!PROMPT_AT.includes(n)) return { prompt: false };
131
+ _startCooldown();
132
+ return { prompt: true, count: n };
133
+ },
134
+
135
+ telemetry(event, extra) {
136
+ if (typeof fetch === "undefined") return;
137
+ const body = JSON.stringify({ event, extra: extra || {} });
138
+ fetch("/api/telemetry", { method: "POST", headers: { "Content-Type": "application/json" }, body }).catch(() => {});
139
+ },
140
+ };
141
+
142
+ return Share;
143
+ })();
144
+
145
+ const Share = ShareStore;