claw-dashboard 2.1.1 → 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 (89) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/docs/API.md +1 -2
  3. package/index.js +31 -41
  4. package/package.json +22 -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/.c8rc.json +0 -38
  14. package/.dockerignore +0 -68
  15. package/.github/dependabot.yml +0 -45
  16. package/.github/pull_request_template.md +0 -39
  17. package/.github/workflows/ci.yml +0 -147
  18. package/.github/workflows/release.yml +0 -40
  19. package/.github/workflows/security.yml +0 -84
  20. package/.husky/pre-commit +0 -1
  21. package/.planning/codebase/ARCHITECTURE.md +0 -206
  22. package/.planning/codebase/INTEGRATIONS.md +0 -150
  23. package/.planning/codebase/STACK.md +0 -122
  24. package/.planning/codebase/STRUCTURE.md +0 -201
  25. package/CONTRIBUTING.md +0 -378
  26. package/Dockerfile +0 -54
  27. package/FEATURES.md +0 -307
  28. package/TODO.md +0 -28
  29. package/ai.openclaw.dashboard.plist +0 -35
  30. package/build-cjs.js +0 -127
  31. package/cjs-shim.js +0 -13
  32. package/dist/clawdash +0 -1729
  33. package/dist/clawdash.meta.json +0 -2236
  34. package/dist/widgets.cjs +0 -6511
  35. package/docker-compose.yml +0 -67
  36. package/esbuild.config.js +0 -158
  37. package/eslint.config.js +0 -56
  38. package/examples/plugins/README.md +0 -122
  39. package/examples/plugins/api-status/index.js +0 -294
  40. package/examples/plugins/api-status/plugin.json +0 -19
  41. package/examples/plugins/hello-world/index.js +0 -104
  42. package/examples/plugins/hello-world/plugin.json +0 -15
  43. package/examples/plugins/system-metrics-chart/index.js +0 -339
  44. package/examples/plugins/system-metrics-chart/plugin.json +0 -18
  45. package/examples/plugins/weather-widget/index.js +0 -136
  46. package/examples/plugins/weather-widget/plugin.json +0 -19
  47. package/index.cjs +0 -23005
  48. package/jest.config.js +0 -11
  49. package/scripts/release.js +0 -595
  50. package/src/database.js +0 -734
  51. package/tests/alerts.test.js +0 -437
  52. package/tests/auto-save.test.js +0 -529
  53. package/tests/cache.test.js +0 -317
  54. package/tests/cli.test.js +0 -351
  55. package/tests/command-palette.test.js +0 -320
  56. package/tests/config-processor.test.js +0 -452
  57. package/tests/config-validator.test.js +0 -710
  58. package/tests/config-watcher.test.js +0 -594
  59. package/tests/config.test.js +0 -755
  60. package/tests/database.test.js +0 -438
  61. package/tests/errors.test.js +0 -189
  62. package/tests/example-plugins.test.js +0 -624
  63. package/tests/integration.test.js +0 -1321
  64. package/tests/loading-states.test.js +0 -300
  65. package/tests/manifest-validation-on-load.test.js +0 -671
  66. package/tests/performance-monitor.test.js +0 -190
  67. package/tests/plugin-api-rate-limit.test.js +0 -302
  68. package/tests/plugin-errors.test.js +0 -311
  69. package/tests/plugin-lifecycle-e2e.test.js +0 -1036
  70. package/tests/plugin-reload.test.js +0 -764
  71. package/tests/rate-limiter.test.js +0 -539
  72. package/tests/removed-dependencies.test.js +0 -74
  73. package/tests/retry.test.js +0 -308
  74. package/tests/security.test.js +0 -411
  75. package/tests/settings-modal.test.js +0 -226
  76. package/tests/theme-selector.test.js +0 -57
  77. package/tests/utils.js +0 -242
  78. package/tests/utils.test.js +0 -317
  79. package/tests/validate-plugin-cli.test.js +0 -359
  80. package/tests/validation.test.js +0 -837
  81. package/tests/web-server.test.js +0 -646
  82. package/tests/widget-arrange-mode.test.js +0 -183
  83. package/tests/widget-config-hot-reload.test.js +0 -465
  84. package/tests/widget-dependency.test.js +0 -591
  85. package/tests/widget-error-boundary.test.js +0 -749
  86. package/tests/widget-error-isolation.test.js +0 -469
  87. package/tests/widget-integration.test.js +0 -1147
  88. package/tests/widget-refresh-intervals.test.js +0 -284
  89. 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
- });