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.
- package/CHANGELOG.md +29 -1
- package/docs/API.md +1 -2
- package/index.js +32 -42
- package/package.json +23 -12
- package/src/auto-save.js +11 -5
- package/src/cli/export-snapshot.js +3 -1
- package/src/cli/import-snapshot.js +3 -1
- package/src/config.js +9 -15
- package/src/errors.js +0 -9
- package/src/security.js +6 -2
- package/src/snapshot.js +73 -26
- package/src/web-server.js +7 -10
- package/src/widgets/builtin-widgets.js +0 -94
- package/src/widgets/index.js +0 -1
- package/.c8rc.json +0 -38
- package/.dockerignore +0 -68
- package/.github/dependabot.yml +0 -45
- package/.github/pull_request_template.md +0 -39
- package/.github/workflows/ci.yml +0 -147
- package/.github/workflows/release.yml +0 -40
- package/.github/workflows/security.yml +0 -84
- package/.husky/pre-commit +0 -1
- package/.planning/codebase/ARCHITECTURE.md +0 -206
- package/.planning/codebase/INTEGRATIONS.md +0 -150
- package/.planning/codebase/STACK.md +0 -122
- package/.planning/codebase/STRUCTURE.md +0 -201
- package/CONTRIBUTING.md +0 -378
- package/Dockerfile +0 -54
- package/FEATURES.md +0 -307
- package/TODO.md +0 -28
- package/ai.openclaw.dashboard.plist +0 -35
- package/build-cjs.js +0 -127
- package/cjs-shim.js +0 -13
- package/dist/clawdash +0 -1729
- package/dist/clawdash.meta.json +0 -2236
- package/dist/widgets.cjs +0 -6511
- package/docker-compose.yml +0 -67
- package/esbuild.config.js +0 -158
- package/eslint.config.js +0 -56
- package/examples/plugins/README.md +0 -122
- package/examples/plugins/api-status/index.js +0 -294
- package/examples/plugins/api-status/plugin.json +0 -19
- package/examples/plugins/hello-world/index.js +0 -104
- package/examples/plugins/hello-world/plugin.json +0 -15
- package/examples/plugins/system-metrics-chart/index.js +0 -339
- package/examples/plugins/system-metrics-chart/plugin.json +0 -18
- package/examples/plugins/weather-widget/index.js +0 -136
- package/examples/plugins/weather-widget/plugin.json +0 -19
- package/index.cjs +0 -23005
- package/jest.config.js +0 -11
- package/scripts/release.js +0 -595
- package/src/database.js +0 -734
- package/tests/alerts.test.js +0 -437
- package/tests/auto-save.test.js +0 -529
- package/tests/cache.test.js +0 -317
- package/tests/cli.test.js +0 -351
- package/tests/command-palette.test.js +0 -320
- package/tests/config-processor.test.js +0 -452
- package/tests/config-validator.test.js +0 -710
- package/tests/config-watcher.test.js +0 -594
- package/tests/config.test.js +0 -755
- package/tests/database.test.js +0 -438
- package/tests/errors.test.js +0 -189
- package/tests/example-plugins.test.js +0 -624
- package/tests/integration.test.js +0 -1321
- package/tests/loading-states.test.js +0 -300
- package/tests/manifest-validation-on-load.test.js +0 -671
- package/tests/performance-monitor.test.js +0 -190
- package/tests/plugin-api-rate-limit.test.js +0 -302
- package/tests/plugin-errors.test.js +0 -311
- package/tests/plugin-lifecycle-e2e.test.js +0 -1036
- package/tests/plugin-reload.test.js +0 -764
- package/tests/rate-limiter.test.js +0 -539
- package/tests/retry.test.js +0 -308
- package/tests/security.test.js +0 -411
- package/tests/settings-modal.test.js +0 -226
- package/tests/theme-selector.test.js +0 -57
- package/tests/utils.js +0 -242
- package/tests/utils.test.js +0 -317
- package/tests/validate-plugin-cli.test.js +0 -359
- package/tests/validation.test.js +0 -837
- package/tests/web-server.test.js +0 -646
- package/tests/widget-arrange-mode.test.js +0 -183
- package/tests/widget-config-hot-reload.test.js +0 -465
- package/tests/widget-dependency.test.js +0 -591
- package/tests/widget-error-boundary.test.js +0 -749
- package/tests/widget-error-isolation.test.js +0 -469
- package/tests/widget-integration.test.js +0 -1147
- package/tests/widget-refresh-intervals.test.js +0 -284
- 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
|
-
});
|