claw-dashboard 1.9.0 → 2.1.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 (135) hide show
  1. package/.c8rc.json +38 -0
  2. package/.dockerignore +68 -0
  3. package/.github/dependabot.yml +45 -0
  4. package/.github/pull_request_template.md +39 -0
  5. package/.github/workflows/ci.yml +147 -0
  6. package/.github/workflows/release.yml +40 -0
  7. package/.github/workflows/security.yml +84 -0
  8. package/.husky/pre-commit +1 -0
  9. package/.planning/codebase/ARCHITECTURE.md +206 -0
  10. package/.planning/codebase/INTEGRATIONS.md +150 -0
  11. package/.planning/codebase/STACK.md +122 -0
  12. package/.planning/codebase/STRUCTURE.md +201 -0
  13. package/CHANGELOG.md +293 -0
  14. package/CONTRIBUTING.md +378 -0
  15. package/Dockerfile +54 -0
  16. package/FEATURES.md +195 -21
  17. package/README.md +83 -6
  18. package/TODO.md +28 -0
  19. package/build-cjs.js +127 -0
  20. package/cjs-shim.js +13 -0
  21. package/dist/clawdash +1729 -0
  22. package/dist/clawdash.meta.json +2236 -0
  23. package/dist/widgets.cjs +6511 -0
  24. package/docker-compose.yml +67 -0
  25. package/docs/API.md +1050 -0
  26. package/docs/PLUGINS.md +1504 -0
  27. package/esbuild.config.js +158 -0
  28. package/eslint.config.js +56 -0
  29. package/examples/plugins/README.md +122 -0
  30. package/examples/plugins/api-status/index.js +294 -0
  31. package/examples/plugins/api-status/plugin.json +19 -0
  32. package/examples/plugins/hello-world/index.js +104 -0
  33. package/examples/plugins/hello-world/plugin.json +15 -0
  34. package/examples/plugins/system-metrics-chart/index.js +339 -0
  35. package/examples/plugins/system-metrics-chart/plugin.json +18 -0
  36. package/examples/plugins/weather-widget/index.js +136 -0
  37. package/examples/plugins/weather-widget/plugin.json +19 -0
  38. package/index.cjs +23005 -0
  39. package/index.js +5285 -566
  40. package/jest.config.js +11 -0
  41. package/man/clawdash.1 +276 -0
  42. package/package.json +54 -6
  43. package/schemas/plugin-manifest.json +128 -0
  44. package/scripts/release.js +595 -0
  45. package/src/alerts.js +693 -0
  46. package/src/auto-save.js +584 -0
  47. package/src/cache.js +390 -0
  48. package/src/checksum.js +146 -0
  49. package/src/cli/args.js +110 -0
  50. package/src/cli/export-schedule.js +423 -0
  51. package/src/cli/export-snapshot.js +138 -0
  52. package/src/cli/help.js +69 -0
  53. package/src/cli/import-snapshot.js +230 -0
  54. package/src/cli/index.js +14 -0
  55. package/src/cli/list-templates.js +69 -0
  56. package/src/cli/validate-config.js +76 -0
  57. package/src/cli/validate-plugin.js +187 -0
  58. package/src/cli/version.js +15 -0
  59. package/src/config-validator.js +586 -0
  60. package/src/config-watcher.js +401 -0
  61. package/src/config.js +684 -0
  62. package/src/container-detector.js +499 -0
  63. package/src/database.js +734 -0
  64. package/src/differential-render.js +327 -0
  65. package/src/errors.js +169 -0
  66. package/src/export-scheduler.js +581 -0
  67. package/src/gateway-manager.js +685 -0
  68. package/src/hints.js +371 -0
  69. package/src/loading-states.js +509 -0
  70. package/src/logger.js +185 -0
  71. package/src/memory-pressure.js +467 -0
  72. package/src/performance-monitor.js +374 -0
  73. package/src/plugin-errors.js +586 -0
  74. package/src/plugin-manifest-validator.js +219 -0
  75. package/src/plugin-reload.js +481 -0
  76. package/src/plugin-scaffold.js +1934 -0
  77. package/src/plugin-validator.js +284 -0
  78. package/src/retry.js +218 -0
  79. package/src/security.js +860 -0
  80. package/src/snapshot.js +372 -0
  81. package/src/splash.js +115 -0
  82. package/src/theme-selector.js +411 -0
  83. package/src/themes.js +874 -0
  84. package/src/transitions.js +534 -0
  85. package/src/types.d.ts +174 -0
  86. package/src/validation.js +926 -0
  87. package/src/web-server.js +885 -0
  88. package/src/widgets/builtin-widgets.js +1056 -0
  89. package/src/widgets/config-processor.js +401 -0
  90. package/src/widgets/dependency-resolver.js +596 -0
  91. package/src/widgets/index.js +79 -0
  92. package/src/widgets/plugin-api.js +845 -0
  93. package/src/widgets/widget-error-boundary.js +773 -0
  94. package/src/widgets/widget-error-isolation.js +551 -0
  95. package/src/widgets/widget-loader.js +1437 -0
  96. package/src/workers/system-worker.js +130 -0
  97. package/src/workers/worker-pool.js +633 -0
  98. package/tests/alerts.test.js +437 -0
  99. package/tests/auto-save.test.js +529 -0
  100. package/tests/cache.test.js +317 -0
  101. package/tests/cli.test.js +351 -0
  102. package/tests/command-palette.test.js +320 -0
  103. package/tests/config-processor.test.js +452 -0
  104. package/tests/config-validator.test.js +710 -0
  105. package/tests/config-watcher.test.js +594 -0
  106. package/tests/config.test.js +755 -0
  107. package/tests/database.test.js +438 -0
  108. package/tests/errors.test.js +189 -0
  109. package/tests/example-plugins.test.js +624 -0
  110. package/tests/integration.test.js +1321 -0
  111. package/tests/loading-states.test.js +300 -0
  112. package/tests/manifest-validation-on-load.test.js +671 -0
  113. package/tests/performance-monitor.test.js +190 -0
  114. package/tests/plugin-api-rate-limit.test.js +302 -0
  115. package/tests/plugin-errors.test.js +311 -0
  116. package/tests/plugin-lifecycle-e2e.test.js +1036 -0
  117. package/tests/plugin-reload.test.js +764 -0
  118. package/tests/rate-limiter.test.js +539 -0
  119. package/tests/retry.test.js +308 -0
  120. package/tests/security.test.js +411 -0
  121. package/tests/settings-modal.test.js +226 -0
  122. package/tests/theme-selector.test.js +57 -0
  123. package/tests/utils.js +242 -0
  124. package/tests/utils.test.js +317 -0
  125. package/tests/validate-plugin-cli.test.js +359 -0
  126. package/tests/validation.test.js +837 -0
  127. package/tests/web-server.test.js +646 -0
  128. package/tests/widget-arrange-mode.test.js +183 -0
  129. package/tests/widget-config-hot-reload.test.js +465 -0
  130. package/tests/widget-dependency.test.js +591 -0
  131. package/tests/widget-error-boundary.test.js +749 -0
  132. package/tests/widget-error-isolation.test.js +469 -0
  133. package/tests/widget-integration.test.js +1147 -0
  134. package/tests/widget-refresh-intervals.test.js +284 -0
  135. package/tests/worker-pool.test.js +522 -0
@@ -0,0 +1,423 @@
1
+ /**
2
+ * CLI Commands for Export Schedule Management
3
+ * Allows users to configure, enable/disable, and manage scheduled exports
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ import logger from '../logger.js';
10
+ import config from '../config.js';
11
+ import validation from '../validation.js';
12
+ import { ExportScheduler, CRON_PRESETS, DEFAULT_SCHEDULE_CONFIG } from '../export-scheduler.js';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+
17
+ const SETTINGS_PATH = config.PATHS.SETTINGS;
18
+
19
+ /**
20
+ * Load current settings
21
+ * @returns {Object} Settings object
22
+ */
23
+ function loadSettings() {
24
+ try {
25
+ if (!fs.existsSync(SETTINGS_PATH)) {
26
+ return config.DEFAULT_SETTINGS;
27
+ }
28
+ const data = fs.readFileSync(SETTINGS_PATH, 'utf8');
29
+ const loaded = JSON.parse(data);
30
+ const result = validation.validateSettings(loaded);
31
+ return result.valid ? result.value : config.DEFAULT_SETTINGS;
32
+ } catch (err) {
33
+ logger.error(`Failed to load settings: ${err.message}`);
34
+ return config.DEFAULT_SETTINGS;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Save settings to file
40
+ * @param {Object} settings - Settings to save
41
+ */
42
+ function saveSettings(settings) {
43
+ try {
44
+ const dir = path.dirname(SETTINGS_PATH);
45
+ if (!fs.existsSync(dir)) {
46
+ fs.mkdirSync(dir, { recursive: true });
47
+ }
48
+ fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
49
+ logger.info('Settings saved successfully');
50
+ } catch (err) {
51
+ logger.error(`Failed to save settings: ${err.message}`);
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Show current export schedule configuration
57
+ */
58
+ export function showScheduleStatus() {
59
+ const settings = loadSettings();
60
+ const schedule = settings.exportSchedule || DEFAULT_SCHEDULE_CONFIG;
61
+
62
+ console.log('\n=== Export Schedule Status ===\n');
63
+ console.log(`Enabled: ${schedule.enabled ? 'Yes' : 'No'}`);
64
+ console.log(`Format: ${schedule.format}`);
65
+ console.log(`Schedule: ${schedule.schedule}`);
66
+ console.log(`Retention: ${schedule.retentionDays} days${schedule.retentionDays === 0 ? ' (forever)' : ''}`);
67
+ console.log(`Directory: ${schedule.directory || '(default)'}`);
68
+ console.log(`Include Metrics: ${schedule.includeMetrics ? 'Yes' : 'No'}`);
69
+
70
+ // Show preset name if matches
71
+ const presetName = Object.entries(CRON_PRESETS).find(([_, value]) => value === schedule.schedule);
72
+ if (presetName) {
73
+ console.log(`Preset: ${presetName[0]}`);
74
+ }
75
+
76
+ console.log('\n=== Available Cron Presets ===\n');
77
+ for (const [name, expression] of Object.entries(CRON_PRESETS)) {
78
+ console.log(` ${name.padEnd(20)} ${expression}`);
79
+ }
80
+ console.log('');
81
+ }
82
+
83
+ /**
84
+ * Enable or disable export schedule
85
+ * @param {boolean} enabled - Whether to enable or disable
86
+ */
87
+ export function setScheduleEnabled(enabled) {
88
+ const settings = loadSettings();
89
+
90
+ if (!settings.exportSchedule) {
91
+ settings.exportSchedule = { ...DEFAULT_SCHEDULE_CONFIG };
92
+ }
93
+
94
+ settings.exportSchedule.enabled = Boolean(enabled);
95
+ saveSettings(settings);
96
+
97
+ console.log(`Export schedule ${enabled ? 'enabled' : 'disabled'}`);
98
+ }
99
+
100
+ /**
101
+ * Set the export schedule cron expression
102
+ * @param {string} expression - Cron expression or preset name
103
+ */
104
+ export function setSchedule(expression) {
105
+ const settings = loadSettings();
106
+
107
+ // Check if it's a preset name
108
+ let cronExpression = expression;
109
+ if (CRON_PRESETS[expression]) {
110
+ cronExpression = CRON_PRESETS[expression];
111
+ console.log(`Using preset: ${expression} (${cronExpression})`);
112
+ }
113
+
114
+ // Validate the cron expression
115
+ try {
116
+ const { CronParser } = ExportScheduler;
117
+ CronParser.parse(cronExpression);
118
+ } catch (err) {
119
+ console.error(`Invalid cron expression: ${err.message}`);
120
+ console.error('\nAvailable presets:');
121
+ for (const [name, expr] of Object.entries(CRON_PRESETS)) {
122
+ console.error(` ${name}: ${expr}`);
123
+ }
124
+ process.exit(1);
125
+ }
126
+
127
+ if (!settings.exportSchedule) {
128
+ settings.exportSchedule = { ...DEFAULT_SCHEDULE_CONFIG };
129
+ }
130
+
131
+ settings.exportSchedule.schedule = cronExpression;
132
+ saveSettings(settings);
133
+
134
+ console.log(`Export schedule set to: ${cronExpression}`);
135
+ }
136
+
137
+ /**
138
+ * Set the export format
139
+ * @param {string} format - Export format ('json' or 'csv')
140
+ */
141
+ export function setScheduleFormat(format) {
142
+ const normalizedFormat = format.toLowerCase();
143
+
144
+ if (!['json', 'csv'].includes(normalizedFormat)) {
145
+ console.error(`Invalid format: ${format}`);
146
+ console.error('Valid formats: json, csv');
147
+ process.exit(1);
148
+ }
149
+
150
+ const settings = loadSettings();
151
+
152
+ if (!settings.exportSchedule) {
153
+ settings.exportSchedule = { ...DEFAULT_SCHEDULE_CONFIG };
154
+ }
155
+
156
+ settings.exportSchedule.format = normalizedFormat;
157
+ saveSettings(settings);
158
+
159
+ console.log(`Export format set to: ${normalizedFormat}`);
160
+ }
161
+
162
+ /**
163
+ * Set the retention period for exports
164
+ * @param {number} days - Number of days to retain (0 = forever)
165
+ */
166
+ export function setScheduleRetention(days) {
167
+ const daysNum = parseInt(days, 10);
168
+
169
+ if (isNaN(daysNum) || daysNum < 0 || daysNum > 365) {
170
+ console.error('Invalid retention days: must be 0-365');
171
+ console.error(' 0 = keep forever');
172
+ console.error(' 1-365 = keep for N days');
173
+ process.exit(1);
174
+ }
175
+
176
+ const settings = loadSettings();
177
+
178
+ if (!settings.exportSchedule) {
179
+ settings.exportSchedule = { ...DEFAULT_SCHEDULE_CONFIG };
180
+ }
181
+
182
+ settings.exportSchedule.retentionDays = daysNum;
183
+ saveSettings(settings);
184
+
185
+ console.log(`Export retention set to: ${daysNum === 0 ? 'forever' : `${daysNum} days`}`);
186
+ }
187
+
188
+ /**
189
+ * Set the export directory
190
+ * @param {string} directory - Directory path
191
+ */
192
+ export function setScheduleDirectory(directory) {
193
+ const settings = loadSettings();
194
+
195
+ if (!settings.exportSchedule) {
196
+ settings.exportSchedule = { ...DEFAULT_SCHEDULE_CONFIG };
197
+ }
198
+
199
+ settings.exportSchedule.directory = directory || null;
200
+ saveSettings(settings);
201
+
202
+ console.log(`Export directory set to: ${directory || '(default)'}`);
203
+ }
204
+
205
+ /**
206
+ * Trigger an immediate export
207
+ */
208
+ export async function triggerExport() {
209
+ const settings = loadSettings();
210
+ const schedule = settings.exportSchedule || DEFAULT_SCHEDULE_CONFIG;
211
+
212
+ console.log('\nTriggering immediate export...\n');
213
+
214
+ const scheduler = new ExportScheduler(schedule);
215
+
216
+ // Mock metrics callback for CLI
217
+ scheduler.setMetricsCallback(async () => {
218
+ // Return placeholder metrics for CLI export
219
+ return {
220
+ timestamp: new Date().toISOString(),
221
+ source: 'cli-manual-export',
222
+ };
223
+ });
224
+
225
+ const result = await scheduler.triggerExport();
226
+
227
+ if (result.success) {
228
+ console.log(`Export completed successfully: ${result.path}`);
229
+ } else {
230
+ console.error(`Export failed: ${result.error}`);
231
+ process.exit(1);
232
+ }
233
+ }
234
+
235
+ /**
236
+ * List recent exports
237
+ */
238
+ export function listExports() {
239
+ const settings = loadSettings();
240
+ const schedule = settings.exportSchedule || DEFAULT_SCHEDULE_CONFIG;
241
+ const exportDir = schedule.directory || path.join(config.PATHS.OPENCLAW_DIR, 'snapshots');
242
+
243
+ console.log(`\n=== Recent Exports in ${exportDir} ===\n`);
244
+
245
+ if (!fs.existsSync(exportDir)) {
246
+ console.log('No exports found (directory does not exist)');
247
+ return;
248
+ }
249
+
250
+ try {
251
+ const files = fs.readdirSync(exportDir)
252
+ .filter(f => f.startsWith('claw-export-') || f.startsWith('claw-snapshot-'))
253
+ .map(f => {
254
+ const filePath = path.join(exportDir, f);
255
+ const stats = fs.statSync(filePath);
256
+ return {
257
+ name: f,
258
+ size: stats.size,
259
+ mtime: stats.mtime,
260
+ };
261
+ })
262
+ .sort((a, b) => b.mtime - a.mtime)
263
+ .slice(0, 20); // Show last 20 exports
264
+
265
+ if (files.length === 0) {
266
+ console.log('No exports found');
267
+ return;
268
+ }
269
+
270
+ console.log('Filename'.padEnd(50) + 'Size'.padEnd(15) + 'Modified');
271
+ console.log('-'.repeat(80));
272
+
273
+ for (const file of files) {
274
+ const sizeStr = formatFileSize(file.size);
275
+ const dateStr = file.mtime.toISOString().replace('T', ' ').slice(0, 19);
276
+ console.log(file.name.padEnd(50) + sizeStr.padEnd(15) + dateStr);
277
+ }
278
+
279
+ console.log('');
280
+ } catch (err) {
281
+ console.error(`Failed to list exports: ${err.message}`);
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Format file size in human-readable form
287
+ * @param {number} bytes - Size in bytes
288
+ * @returns {string} Formatted size
289
+ */
290
+ function formatFileSize(bytes) {
291
+ const units = ['B', 'KB', 'MB', 'GB'];
292
+ let unitIndex = 0;
293
+ let size = bytes;
294
+
295
+ while (size >= 1024 && unitIndex < units.length - 1) {
296
+ size /= 1024;
297
+ unitIndex++;
298
+ }
299
+
300
+ return `${size.toFixed(1)} ${units[unitIndex]}`;
301
+ }
302
+
303
+ /**
304
+ * Show help for export-schedule commands
305
+ */
306
+ export function showExportScheduleHelp() {
307
+ console.log(`
308
+ Export Schedule Management Commands
309
+
310
+ Usage: clawdash export-schedule <command> [options]
311
+
312
+ Commands:
313
+ status Show current schedule configuration
314
+ enable Enable scheduled exports
315
+ disable Disable scheduled exports
316
+ set <expression> Set cron schedule (or preset name)
317
+ format <format> Set export format (json or csv)
318
+ retention <days> Set retention period (0-365 days, 0=forever)
319
+ directory <path> Set export directory
320
+ export Trigger immediate export
321
+ list List recent exports
322
+
323
+ Cron Presets:
324
+ everyMinute * * * * *
325
+ every5Minutes */5 * * * *
326
+ every15Minutes */15 * * * *
327
+ hourly 0 * * * *
328
+ every6Hours 0 */6 * * *
329
+ daily 0 0 * * *
330
+ weekly 0 0 * * 0
331
+ monthly 0 0 1 * *
332
+
333
+ Examples:
334
+ clawdash export-schedule status
335
+ clawdash export-schedule enable
336
+ clawdash export-schedule set hourly
337
+ clawdash export-schedule set "*/30 * * * *"
338
+ clawdash export-schedule format csv
339
+ clawdash export-schedule retention 7
340
+ clawdash export-schedule export
341
+ `);
342
+ }
343
+
344
+ /**
345
+ * Main CLI handler for export-schedule commands
346
+ * @param {string[]} args - Command line arguments
347
+ * @returns {Promise<number>} Exit code
348
+ */
349
+ export async function runExportScheduleCli(args = []) {
350
+ const command = args[0];
351
+ const arg = args[1];
352
+
353
+ switch (command) {
354
+ case 'status':
355
+ showScheduleStatus();
356
+ break;
357
+
358
+ case 'enable':
359
+ setScheduleEnabled(true);
360
+ break;
361
+
362
+ case 'disable':
363
+ setScheduleEnabled(false);
364
+ break;
365
+
366
+ case 'set':
367
+ if (!arg) {
368
+ console.error('Error: cron expression required');
369
+ console.error('Usage: clawdash export-schedule set <expression>');
370
+ console.error('Example: clawdash export-schedule set "0 * * * *"');
371
+ return 1;
372
+ }
373
+ setSchedule(arg);
374
+ break;
375
+
376
+ case 'format':
377
+ if (!arg) {
378
+ console.error('Error: format required');
379
+ console.error('Usage: clawdash export-schedule format <json|csv>');
380
+ return 1;
381
+ }
382
+ setScheduleFormat(arg);
383
+ break;
384
+
385
+ case 'retention':
386
+ if (arg === undefined) {
387
+ console.error('Error: retention days required');
388
+ console.error('Usage: clawdash export-schedule retention <days>');
389
+ return 1;
390
+ }
391
+ setScheduleRetention(arg);
392
+ break;
393
+
394
+ case 'directory':
395
+ setScheduleDirectory(arg || '');
396
+ break;
397
+
398
+ case 'export':
399
+ await triggerExport();
400
+ break;
401
+
402
+ case 'list':
403
+ listExports();
404
+ break;
405
+
406
+ case 'help':
407
+ case '--help':
408
+ case '-h':
409
+ showExportScheduleHelp();
410
+ break;
411
+
412
+ default:
413
+ if (!command) {
414
+ showScheduleStatus();
415
+ } else {
416
+ console.error(`Unknown command: ${command}`);
417
+ console.error('Run with --help for usage');
418
+ return 1;
419
+ }
420
+ }
421
+
422
+ return 0;
423
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * CLI Export Snapshot Module
3
+ * Exports dashboard configuration to a shareable JSON file
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import os from 'os';
8
+ import { join, resolve } from 'path';
9
+ import {
10
+ createSnapshot,
11
+ exportSnapshotToFile,
12
+ generateSnapshotFilename,
13
+ getSnapshotsDirectory,
14
+ } from '../snapshot.js';
15
+ import { DEFAULT_SETTINGS, DASHBOARD_VERSION, PATHS } from '../config.js';
16
+
17
+ /**
18
+ * Load current settings from file
19
+ * @returns {Object} Current settings or defaults
20
+ */
21
+ function loadCurrentSettings() {
22
+ try {
23
+ const settingsPath = PATHS.SETTINGS;
24
+ if (fs.existsSync(settingsPath)) {
25
+ const data = fs.readFileSync(settingsPath, 'utf8');
26
+ return { ...DEFAULT_SETTINGS, ...JSON.parse(data) };
27
+ }
28
+ } catch (err) {
29
+ // Fall through to defaults
30
+ }
31
+ return { ...DEFAULT_SETTINGS };
32
+ }
33
+
34
+ /**
35
+ * Run the export-snapshot CLI command
36
+ * @param {string[]} args - CLI arguments
37
+ * @returns {number} Exit code
38
+ */
39
+ export async function runExportSnapshotCli(args) {
40
+ const jsonOutput = args.includes('--json') || args.includes('-j');
41
+ const showHelp = args.includes('--help') || args.includes('-h');
42
+ const nameFlag = args.findIndex(a => a === '--name' || a === '-n');
43
+ const snapshotName = nameFlag !== -1 && args[nameFlag + 1] ? args[nameFlag + 1] : 'Dashboard Configuration';
44
+
45
+ // Get output path (first non-flag argument)
46
+ const outputPath = args.find(a => !a.startsWith('-') && !args[args.indexOf(a) - 1]?.startsWith('-'));
47
+
48
+ if (showHelp) {
49
+ console.log(`
50
+ Export Dashboard Snapshot for Claw Dashboard
51
+
52
+ Usage: clawdash export-snapshot [path] [options]
53
+
54
+ Arguments:
55
+ path Output file path (optional, defaults to ~/.openclaw/snapshots/)
56
+
57
+ Options:
58
+ -n, --name Snapshot name (default: "Dashboard Configuration")
59
+ -j, --json Output results as JSON
60
+ -h, --help Show this help message
61
+
62
+ Examples:
63
+ clawdash export-snapshot
64
+ clawdash export-snapshot ~/my-layout.json
65
+ clawdash export-snapshot --name "Production Setup"
66
+ clawdash export-snapshot ~/backup.json --json
67
+ `);
68
+ return 0;
69
+ }
70
+
71
+ try {
72
+ // Load current settings
73
+ const settings = loadCurrentSettings();
74
+
75
+ // Create snapshot
76
+ const snapshot = createSnapshot(settings, {
77
+ name: snapshotName,
78
+ description: `Claw Dashboard v${DASHBOARD_VERSION} - Exported via CLI`,
79
+ });
80
+
81
+ // Determine output path
82
+ let filePath;
83
+ if (outputPath) {
84
+ let resolvedPath = outputPath;
85
+ if (outputPath.startsWith('~')) {
86
+ resolvedPath = join(os.homedir(), outputPath.slice(1));
87
+ }
88
+ filePath = resolve(resolvedPath);
89
+ } else {
90
+ const snapshotDir = getSnapshotsDirectory();
91
+ const filename = generateSnapshotFilename(snapshotName);
92
+ filePath = join(snapshotDir, filename);
93
+ }
94
+
95
+ // Export to file
96
+ const result = exportSnapshotToFile(snapshot, filePath);
97
+
98
+ if (jsonOutput) {
99
+ console.log(JSON.stringify({
100
+ success: result.success,
101
+ path: result.path,
102
+ error: result.error,
103
+ snapshot: {
104
+ name: snapshot.name,
105
+ version: snapshot.dashboardVersion,
106
+ schemaVersion: snapshot.schemaVersion,
107
+ createdAt: snapshot.createdAt,
108
+ metadata: snapshot.metadata,
109
+ },
110
+ }, null, 2));
111
+ } else {
112
+ if (result.success) {
113
+ console.log('✓ Snapshot exported successfully');
114
+ console.log(` Name: ${snapshot.name}`);
115
+ console.log(` Path: ${result.path}`);
116
+ console.log(` Version: ${snapshot.dashboardVersion}`);
117
+ console.log(` Widgets: ${snapshot.metadata?.widgetCount || 'N/A'}`);
118
+ console.log(` Plugins: ${snapshot.metadata?.pluginCount || 'N/A'}`);
119
+ } else {
120
+ console.error(`✗ Export failed: ${result.error}`);
121
+ }
122
+ }
123
+
124
+ return result.success ? 0 : 1;
125
+ } catch (err) {
126
+ if (jsonOutput) {
127
+ console.log(JSON.stringify({
128
+ success: false,
129
+ error: err.message,
130
+ }, null, 2));
131
+ } else {
132
+ console.error(`✗ Export error: ${err.message}`);
133
+ }
134
+ return 1;
135
+ }
136
+ }
137
+
138
+ export default { runExportSnapshotCli };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * CLI Help Module
3
+ * Displays help information for Claw Dashboard CLI
4
+ */
5
+
6
+ /**
7
+ * Display CLI help message
8
+ */
9
+ export function showHelp() {
10
+ console.log(`
11
+ Claw Dashboard - A beautiful terminal dashboard for monitoring OpenClaw instances
12
+
13
+ Usage: clawdash [OPTIONS] [COMMAND]
14
+
15
+ Commands:
16
+ create-plugin <id> Create a new widget plugin scaffold
17
+ Use -h with this command for options
18
+ validate-plugin <path> Validate a plugin.json manifest file
19
+ Use -h with this command for options
20
+ validate-config [path] Validate dashboard configuration file
21
+ Uses ~/.openclaw/dashboard-settings.json by default
22
+ export-snapshot [path] Export dashboard configuration snapshot
23
+ Shareable JSON format for backups and sharing
24
+ Use -h with this command for options
25
+ import-snapshot [path] Import dashboard configuration snapshot
26
+ Use --list to see available snapshots
27
+ Use -h with this command for options
28
+ list-templates List available widget templates
29
+ Shows all templates for create-plugin command
30
+ export-schedule Manage scheduled metric exports
31
+ Configure cron-style auto-exports to CSV/JSON
32
+ Use -h with this command for options
33
+
34
+ Options:
35
+ -h, --help Display this help message
36
+ -v, --version Display version information
37
+ -d, --debug Run in debug mode with additional logging
38
+ -w, --web Run web server mode (no TUI, HTTP API only)
39
+ -p, --web-port Set web server port (default: 18790, requires --web)
40
+ --web-host Set web server host (default: 0.0.0.0, requires --web)
41
+ -W, --watch Enable plugin hot-reload (watches ~/.openclaw/plugins/)
42
+
43
+ Developer Mode:
44
+ --watch Automatically reload plugins when files change
45
+ Watches plugin.json and index.js files in plugin directories
46
+ Shows notifications in dashboard when plugins reload
47
+
48
+ Web Server Endpoints (when --web is enabled):
49
+ GET /health Health check
50
+ GET /metrics System metrics (CPU, memory, GPU, etc.)
51
+ GET /sessions Active OpenClaw sessions
52
+ GET /agents Available OpenClaw agents
53
+ GET /logs Recent OpenClaw logs
54
+ GET /status Full dashboard status (all data)
55
+
56
+ Controls:
57
+ q, Q, Ctrl+C Quit the dashboard
58
+ r, R Force refresh data
59
+ p, Space Pause/resume auto-refresh
60
+ o Cycle session sort (time/tokens/idle/name)
61
+ ? Toggle help panel
62
+ s, S Open settings panel
63
+ 1-8 Toggle widgets
64
+
65
+ For full documentation, see: man clawdash
66
+ `);
67
+ }
68
+
69
+ export default { showHelp };