claw-dashboard 2.1.0 → 2.2.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 (90) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/docs/API.md +1 -2
  3. package/index.js +32 -42
  4. package/package.json +23 -12
  5. package/src/auto-save.js +11 -5
  6. package/src/cli/export-snapshot.js +3 -1
  7. package/src/cli/import-snapshot.js +3 -1
  8. package/src/config.js +9 -15
  9. package/src/errors.js +0 -9
  10. package/src/security.js +6 -2
  11. package/src/snapshot.js +73 -26
  12. package/src/web-server.js +7 -10
  13. package/src/widgets/builtin-widgets.js +0 -94
  14. package/src/widgets/index.js +0 -1
  15. package/.c8rc.json +0 -38
  16. package/.dockerignore +0 -68
  17. package/.github/dependabot.yml +0 -45
  18. package/.github/pull_request_template.md +0 -39
  19. package/.github/workflows/ci.yml +0 -147
  20. package/.github/workflows/release.yml +0 -40
  21. package/.github/workflows/security.yml +0 -84
  22. package/.husky/pre-commit +0 -1
  23. package/.planning/codebase/ARCHITECTURE.md +0 -206
  24. package/.planning/codebase/INTEGRATIONS.md +0 -150
  25. package/.planning/codebase/STACK.md +0 -122
  26. package/.planning/codebase/STRUCTURE.md +0 -201
  27. package/CONTRIBUTING.md +0 -378
  28. package/Dockerfile +0 -54
  29. package/FEATURES.md +0 -307
  30. package/TODO.md +0 -28
  31. package/ai.openclaw.dashboard.plist +0 -35
  32. package/build-cjs.js +0 -127
  33. package/cjs-shim.js +0 -13
  34. package/dist/clawdash +0 -1729
  35. package/dist/clawdash.meta.json +0 -2236
  36. package/dist/widgets.cjs +0 -6511
  37. package/docker-compose.yml +0 -67
  38. package/esbuild.config.js +0 -158
  39. package/eslint.config.js +0 -56
  40. package/examples/plugins/README.md +0 -122
  41. package/examples/plugins/api-status/index.js +0 -294
  42. package/examples/plugins/api-status/plugin.json +0 -19
  43. package/examples/plugins/hello-world/index.js +0 -104
  44. package/examples/plugins/hello-world/plugin.json +0 -15
  45. package/examples/plugins/system-metrics-chart/index.js +0 -339
  46. package/examples/plugins/system-metrics-chart/plugin.json +0 -18
  47. package/examples/plugins/weather-widget/index.js +0 -136
  48. package/examples/plugins/weather-widget/plugin.json +0 -19
  49. package/index.cjs +0 -23005
  50. package/jest.config.js +0 -11
  51. package/scripts/release.js +0 -595
  52. package/src/database.js +0 -734
  53. package/tests/alerts.test.js +0 -437
  54. package/tests/auto-save.test.js +0 -529
  55. package/tests/cache.test.js +0 -317
  56. package/tests/cli.test.js +0 -351
  57. package/tests/command-palette.test.js +0 -320
  58. package/tests/config-processor.test.js +0 -452
  59. package/tests/config-validator.test.js +0 -710
  60. package/tests/config-watcher.test.js +0 -594
  61. package/tests/config.test.js +0 -755
  62. package/tests/database.test.js +0 -438
  63. package/tests/errors.test.js +0 -189
  64. package/tests/example-plugins.test.js +0 -624
  65. package/tests/integration.test.js +0 -1321
  66. package/tests/loading-states.test.js +0 -300
  67. package/tests/manifest-validation-on-load.test.js +0 -671
  68. package/tests/performance-monitor.test.js +0 -190
  69. package/tests/plugin-api-rate-limit.test.js +0 -302
  70. package/tests/plugin-errors.test.js +0 -311
  71. package/tests/plugin-lifecycle-e2e.test.js +0 -1036
  72. package/tests/plugin-reload.test.js +0 -764
  73. package/tests/rate-limiter.test.js +0 -539
  74. package/tests/retry.test.js +0 -308
  75. package/tests/security.test.js +0 -411
  76. package/tests/settings-modal.test.js +0 -226
  77. package/tests/theme-selector.test.js +0 -57
  78. package/tests/utils.js +0 -242
  79. package/tests/utils.test.js +0 -317
  80. package/tests/validate-plugin-cli.test.js +0 -359
  81. package/tests/validation.test.js +0 -837
  82. package/tests/web-server.test.js +0 -646
  83. package/tests/widget-arrange-mode.test.js +0 -183
  84. package/tests/widget-config-hot-reload.test.js +0 -465
  85. package/tests/widget-dependency.test.js +0 -591
  86. package/tests/widget-error-boundary.test.js +0 -749
  87. package/tests/widget-error-isolation.test.js +0 -469
  88. package/tests/widget-integration.test.js +0 -1147
  89. package/tests/widget-refresh-intervals.test.js +0 -284
  90. package/tests/worker-pool.test.js +0 -522
@@ -1,284 +0,0 @@
1
- /**
2
- * Tests for Widget Configurable Refresh Intervals and Graceful Degradation
3
- */
4
-
5
- import { jest } from '@jest/globals';
6
- import { BaseWidget } from '../src/widgets/plugin-api.js';
7
- import config from '../src/config.js';
8
-
9
- // Mock logger
10
- jest.unstable_mockModule('../src/logger.js', () => ({
11
- default: {
12
- info: jest.fn(),
13
- warn: jest.fn(),
14
- error: jest.fn(),
15
- debug: jest.fn(),
16
- },
17
- }));
18
-
19
- describe('Widget Refresh Intervals', () => {
20
- describe('BaseWidget', () => {
21
- test('should initialize with null refresh interval by default', () => {
22
- const widget = new BaseWidget({ id: 'test-widget' });
23
- expect(widget.refreshInterval).toBeNull();
24
- });
25
-
26
- test('should use refresh interval from config', () => {
27
- const widget = new BaseWidget({ id: 'cpu' });
28
- // CPU should use WIDGET_REFRESH_INTERVALS.CPU from config
29
- expect(widget.refreshInterval).toBe(config.WIDGET_REFRESH_INTERVALS.CPU);
30
- });
31
-
32
- test('should allow custom refresh interval in constructor', () => {
33
- const widget = new BaseWidget({
34
- id: 'custom-widget',
35
- config: { refreshInterval: 5000 }
36
- });
37
- expect(widget.refreshInterval).toBe(5000);
38
- });
39
-
40
- test('should validate refresh interval range', () => {
41
- const widget = new BaseWidget({ id: 'test-widget' });
42
-
43
- // Valid intervals should work
44
- widget.updateRefreshInterval(1000);
45
- expect(widget.refreshInterval).toBe(1000);
46
-
47
- widget.updateRefreshInterval(30000);
48
- expect(widget.refreshInterval).toBe(30000);
49
-
50
- widget.updateRefreshInterval(null);
51
- expect(widget.refreshInterval).toBeNull();
52
- });
53
-
54
- test('should throw on invalid refresh interval', () => {
55
- const widget = new BaseWidget({ id: 'test-widget' });
56
-
57
- // Too low
58
- expect(() => widget.updateRefreshInterval(100)).toThrow(/Invalid refresh interval/);
59
-
60
- // Too high
61
- expect(() => widget.updateRefreshInterval(100000)).toThrow(/Invalid refresh interval/);
62
- });
63
- });
64
-
65
- describe('shouldUpdate', () => {
66
- test('should always return true when no refresh interval is set', () => {
67
- const widget = new BaseWidget({ id: 'test-widget' });
68
- widget.refreshInterval = null;
69
-
70
- expect(widget.shouldUpdate(1000)).toBe(true);
71
- expect(widget.shouldUpdate(0)).toBe(true);
72
- expect(widget.shouldUpdate(999999)).toBe(true);
73
- });
74
-
75
- test('should return true when interval has elapsed', () => {
76
- const widget = new BaseWidget({ id: 'test-widget' });
77
- widget.refreshInterval = 1000;
78
- widget.lastUpdateTime = 0;
79
-
80
- expect(widget.shouldUpdate(1000)).toBe(true);
81
- expect(widget.shouldUpdate(1500)).toBe(true);
82
- expect(widget.shouldUpdate(2000)).toBe(true);
83
- });
84
-
85
- test('should return false when interval has not elapsed', () => {
86
- const widget = new BaseWidget({ id: 'test-widget' });
87
- widget.refreshInterval = 1000;
88
- widget.lastUpdateTime = 1000;
89
-
90
- expect(widget.shouldUpdate(1000)).toBe(false);
91
- expect(widget.shouldUpdate(1500)).toBe(false);
92
- expect(widget.shouldUpdate(1999)).toBe(false);
93
- });
94
-
95
- test('should use current time when not provided', () => {
96
- const widget = new BaseWidget({ id: 'test-widget' });
97
- widget.refreshInterval = 50000; // Very long interval
98
- widget.lastUpdateTime = Date.now();
99
-
100
- expect(widget.shouldUpdate()).toBe(false);
101
- });
102
- });
103
-
104
- describe('recordUpdate', () => {
105
- test('should update lastUpdateTime and increment count', () => {
106
- const widget = new BaseWidget({ id: 'test-widget' });
107
- widget.recordUpdate(1000);
108
-
109
- expect(widget.lastUpdateTime).toBe(1000);
110
- expect(widget.updateCount).toBe(1);
111
-
112
- widget.recordUpdate(2000);
113
- expect(widget.lastUpdateTime).toBe(2000);
114
- expect(widget.updateCount).toBe(2);
115
- });
116
-
117
- test('should use current time by default', () => {
118
- const widget = new BaseWidget({ id: 'test-widget' });
119
- const before = Date.now();
120
- widget.recordUpdate();
121
- const after = Date.now();
122
-
123
- expect(widget.lastUpdateTime).toBeGreaterThanOrEqual(before);
124
- expect(widget.lastUpdateTime).toBeLessThanOrEqual(after);
125
- });
126
- });
127
-
128
- describe('getRefreshStats', () => {
129
- test('should return refresh statistics', () => {
130
- const widget = new BaseWidget({ id: 'test-widget' });
131
- widget.refreshInterval = 5000;
132
- widget.lastUpdateTime = 1000;
133
- widget.updateCount = 5;
134
- widget.skipCount = 2;
135
-
136
- const stats = widget.getRefreshStats();
137
-
138
- expect(stats.refreshInterval).toBe(5000);
139
- expect(stats.lastUpdateTime).toBe(1000);
140
- expect(stats.updateCount).toBe(5);
141
- expect(stats.skippedUpdates).toBe(2);
142
- });
143
- });
144
- });
145
-
146
- describe('Widget Graceful Degradation', () => {
147
- describe('shouldUpdateUnderDegradation', () => {
148
- test('should always allow critical widgets to update', () => {
149
- const widget = new BaseWidget({ id: 'cpu' });
150
- widget.refreshInterval = 1000;
151
- widget.lastUpdateTime = 0;
152
-
153
- // Critical widgets should update even in critical degradation
154
- const result = widget.shouldUpdateUnderDegradation('critical', 1000);
155
- expect(result.shouldUpdate).toBe(true);
156
- expect(result.reason).toBe('critical_widget');
157
- });
158
-
159
- test('should skip non-critical widgets in critical degradation', () => {
160
- const widget = new BaseWidget({ id: 'disk' });
161
- widget.priority = 100; // Non-critical priority
162
- widget.refreshInterval = 30000;
163
- widget.lastUpdateTime = 0;
164
-
165
- // Disk widget should be skipped in critical degradation
166
- const result = widget.shouldUpdateUnderDegradation('critical', 1000);
167
- expect(result.shouldUpdate).toBe(false);
168
- expect(result.reason).toBe('degradation_critical_skip');
169
- expect(widget.skipCount).toBe(1);
170
- });
171
-
172
- test('should extend intervals under warning degradation', () => {
173
- const widget = new BaseWidget({ id: 'disk' });
174
- widget.priority = 100;
175
- widget.refreshInterval = 10000;
176
- widget.lastUpdateTime = 0;
177
-
178
- // At time 5000, with 1.5x multiplier, need 15000ms - should not update
179
- const result1 = widget.shouldUpdateUnderDegradation('warning', 5000);
180
- expect(result1.shouldUpdate).toBe(false);
181
- expect(result1.reason).toBe('degradation_extended_interval');
182
-
183
- // At time 16000, with 1.5x multiplier on 10000ms = 15000ms - should update
184
- const result2 = widget.shouldUpdateUnderDegradation('warning', 16000);
185
- expect(result2.shouldUpdate).toBe(true);
186
- expect(result2.reason).toBe('ok');
187
- });
188
-
189
- test('should extend intervals more under critical degradation', () => {
190
- const widget = new BaseWidget({ id: 'network' });
191
- widget.priority = 10; // Low enough to not be skipped
192
- widget.refreshInterval = 10000;
193
- widget.lastUpdateTime = 0;
194
-
195
- // At time 15000, with 2.0x multiplier, need 20000ms - should not update
196
- const result1 = widget.shouldUpdateUnderDegradation('critical', 15000);
197
- expect(result1.shouldUpdate).toBe(false);
198
- expect(result1.reason).toBe('degradation_extended_interval');
199
-
200
- // At time 25000, with 2.0x multiplier on 10000ms = 20000ms - should update
201
- const result2 = widget.shouldUpdateUnderDegradation('critical', 25000);
202
- expect(result2.shouldUpdate).toBe(true);
203
- expect(result2.reason).toBe('ok');
204
- });
205
-
206
- test('should use standard intervals when no degradation', () => {
207
- const widget = new BaseWidget({ id: 'disk' });
208
- widget.refreshInterval = 30000;
209
- widget.lastUpdateTime = 0;
210
-
211
- // Should use normal interval when no degradation
212
- const result1 = widget.shouldUpdateUnderDegradation('none', 1000);
213
- expect(result1.shouldUpdate).toBe(false);
214
- expect(result1.reason).toBe('interval_not_elapsed');
215
-
216
- const result2 = widget.shouldUpdateUnderDegradation('none', 30000);
217
- expect(result2.shouldUpdate).toBe(true);
218
- expect(result2.reason).toBe('ok');
219
- });
220
- });
221
-
222
- describe('setDegradationLevel', () => {
223
- test('should update degradation state', () => {
224
- const widget = new BaseWidget({ id: 'test-widget' });
225
-
226
- widget.setDegradationLevel('warning');
227
- expect(widget.degradationLevel).toBe('warning');
228
- expect(widget.isDegraded).toBe(true);
229
-
230
- widget.setDegradationLevel('critical');
231
- expect(widget.degradationLevel).toBe('critical');
232
- expect(widget.isDegraded).toBe(true);
233
-
234
- widget.setDegradationLevel('none');
235
- expect(widget.degradationLevel).toBe('none');
236
- expect(widget.isDegraded).toBe(false);
237
- });
238
-
239
- test('should adjust currentRefreshInterval based on degradation', () => {
240
- const widget = new BaseWidget({ id: 'test-widget' });
241
- widget.refreshInterval = 10000;
242
-
243
- // Normal operation
244
- widget.setDegradationLevel('none');
245
- expect(widget.currentRefreshInterval).toBe(10000);
246
-
247
- // Warning - 1.5x multiplier
248
- widget.setDegradationLevel('warning');
249
- expect(widget.currentRefreshInterval).toBe(15000);
250
-
251
- // Critical - 2.0x multiplier
252
- widget.setDegradationLevel('critical');
253
- expect(widget.currentRefreshInterval).toBe(20000);
254
- });
255
- });
256
- });
257
-
258
- describe('Widget Config Integration', () => {
259
- test('WIDGET_REFRESH_INTERVALS should have expected values', () => {
260
- // Check that config has the expected default values
261
- expect(config.WIDGET_REFRESH_INTERVALS.CPU).toBe(1000);
262
- expect(config.WIDGET_REFRESH_INTERVALS.MEMORY).toBe(1000);
263
- expect(config.WIDGET_REFRESH_INTERVALS.GPU).toBe(5000);
264
- expect(config.WIDGET_REFRESH_INTERVALS.NETWORK).toBe(1000);
265
- expect(config.WIDGET_REFRESH_INTERVALS.DISK).toBe(30000);
266
- expect(config.WIDGET_REFRESH_INTERVALS.SYSTEM).toBe(5000);
267
- expect(config.WIDGET_REFRESH_INTERVALS.UPTIME).toBe(60000);
268
- });
269
-
270
- test('WIDGET_REFRESH_VALIDATION should have min/max values', () => {
271
- expect(config.WIDGET_REFRESH_VALIDATION.MIN_INTERVAL).toBe(500);
272
- expect(config.WIDGET_REFRESH_VALIDATION.MAX_INTERVAL).toBe(60000);
273
- });
274
-
275
- test('WIDGET_DEGRADATION should define critical widgets', () => {
276
- expect(config.WIDGET_DEGRADATION.CRITICAL_WIDGETS).toContain('cpu');
277
- expect(config.WIDGET_DEGRADATION.CRITICAL_WIDGETS).toContain('memory');
278
- });
279
-
280
- test('WIDGET_DEGRADATION should have multipliers', () => {
281
- expect(config.WIDGET_DEGRADATION.WARNING.EXTEND_INTERVAL_MULTIPLIER).toBe(1.5);
282
- expect(config.WIDGET_DEGRADATION.CRITICAL.EXTEND_INTERVAL_MULTIPLIER).toBe(2.0);
283
- });
284
- });