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.
- package/.c8rc.json +38 -0
- package/.dockerignore +68 -0
- package/.github/dependabot.yml +45 -0
- package/.github/pull_request_template.md +39 -0
- package/.github/workflows/ci.yml +147 -0
- package/.github/workflows/release.yml +40 -0
- package/.github/workflows/security.yml +84 -0
- package/.husky/pre-commit +1 -0
- package/.planning/codebase/ARCHITECTURE.md +206 -0
- package/.planning/codebase/INTEGRATIONS.md +150 -0
- package/.planning/codebase/STACK.md +122 -0
- package/.planning/codebase/STRUCTURE.md +201 -0
- package/CHANGELOG.md +293 -0
- package/CONTRIBUTING.md +378 -0
- package/Dockerfile +54 -0
- package/FEATURES.md +195 -21
- package/README.md +83 -6
- package/TODO.md +28 -0
- package/build-cjs.js +127 -0
- package/cjs-shim.js +13 -0
- package/dist/clawdash +1729 -0
- package/dist/clawdash.meta.json +2236 -0
- package/dist/widgets.cjs +6511 -0
- package/docker-compose.yml +67 -0
- package/docs/API.md +1050 -0
- package/docs/PLUGINS.md +1504 -0
- package/esbuild.config.js +158 -0
- package/eslint.config.js +56 -0
- package/examples/plugins/README.md +122 -0
- package/examples/plugins/api-status/index.js +294 -0
- package/examples/plugins/api-status/plugin.json +19 -0
- package/examples/plugins/hello-world/index.js +104 -0
- package/examples/plugins/hello-world/plugin.json +15 -0
- package/examples/plugins/system-metrics-chart/index.js +339 -0
- package/examples/plugins/system-metrics-chart/plugin.json +18 -0
- package/examples/plugins/weather-widget/index.js +136 -0
- package/examples/plugins/weather-widget/plugin.json +19 -0
- package/index.cjs +23005 -0
- package/index.js +5285 -566
- package/jest.config.js +11 -0
- package/man/clawdash.1 +276 -0
- package/package.json +54 -6
- package/schemas/plugin-manifest.json +128 -0
- package/scripts/release.js +595 -0
- package/src/alerts.js +693 -0
- package/src/auto-save.js +584 -0
- package/src/cache.js +390 -0
- package/src/checksum.js +146 -0
- package/src/cli/args.js +110 -0
- package/src/cli/export-schedule.js +423 -0
- package/src/cli/export-snapshot.js +138 -0
- package/src/cli/help.js +69 -0
- package/src/cli/import-snapshot.js +230 -0
- package/src/cli/index.js +14 -0
- package/src/cli/list-templates.js +69 -0
- package/src/cli/validate-config.js +76 -0
- package/src/cli/validate-plugin.js +187 -0
- package/src/cli/version.js +15 -0
- package/src/config-validator.js +586 -0
- package/src/config-watcher.js +401 -0
- package/src/config.js +684 -0
- package/src/container-detector.js +499 -0
- package/src/database.js +734 -0
- package/src/differential-render.js +327 -0
- package/src/errors.js +169 -0
- package/src/export-scheduler.js +581 -0
- package/src/gateway-manager.js +685 -0
- package/src/hints.js +371 -0
- package/src/loading-states.js +509 -0
- package/src/logger.js +185 -0
- package/src/memory-pressure.js +467 -0
- package/src/performance-monitor.js +374 -0
- package/src/plugin-errors.js +586 -0
- package/src/plugin-manifest-validator.js +219 -0
- package/src/plugin-reload.js +481 -0
- package/src/plugin-scaffold.js +1934 -0
- package/src/plugin-validator.js +284 -0
- package/src/retry.js +218 -0
- package/src/security.js +860 -0
- package/src/snapshot.js +372 -0
- package/src/splash.js +115 -0
- package/src/theme-selector.js +411 -0
- package/src/themes.js +874 -0
- package/src/transitions.js +534 -0
- package/src/types.d.ts +174 -0
- package/src/validation.js +926 -0
- package/src/web-server.js +885 -0
- package/src/widgets/builtin-widgets.js +1056 -0
- package/src/widgets/config-processor.js +401 -0
- package/src/widgets/dependency-resolver.js +596 -0
- package/src/widgets/index.js +79 -0
- package/src/widgets/plugin-api.js +845 -0
- package/src/widgets/widget-error-boundary.js +773 -0
- package/src/widgets/widget-error-isolation.js +551 -0
- package/src/widgets/widget-loader.js +1437 -0
- package/src/workers/system-worker.js +130 -0
- package/src/workers/worker-pool.js +633 -0
- package/tests/alerts.test.js +437 -0
- package/tests/auto-save.test.js +529 -0
- package/tests/cache.test.js +317 -0
- package/tests/cli.test.js +351 -0
- package/tests/command-palette.test.js +320 -0
- package/tests/config-processor.test.js +452 -0
- package/tests/config-validator.test.js +710 -0
- package/tests/config-watcher.test.js +594 -0
- package/tests/config.test.js +755 -0
- package/tests/database.test.js +438 -0
- package/tests/errors.test.js +189 -0
- package/tests/example-plugins.test.js +624 -0
- package/tests/integration.test.js +1321 -0
- package/tests/loading-states.test.js +300 -0
- package/tests/manifest-validation-on-load.test.js +671 -0
- package/tests/performance-monitor.test.js +190 -0
- package/tests/plugin-api-rate-limit.test.js +302 -0
- package/tests/plugin-errors.test.js +311 -0
- package/tests/plugin-lifecycle-e2e.test.js +1036 -0
- package/tests/plugin-reload.test.js +764 -0
- package/tests/rate-limiter.test.js +539 -0
- package/tests/retry.test.js +308 -0
- package/tests/security.test.js +411 -0
- package/tests/settings-modal.test.js +226 -0
- package/tests/theme-selector.test.js +57 -0
- package/tests/utils.js +242 -0
- package/tests/utils.test.js +317 -0
- package/tests/validate-plugin-cli.test.js +359 -0
- package/tests/validation.test.js +837 -0
- package/tests/web-server.test.js +646 -0
- package/tests/widget-arrange-mode.test.js +183 -0
- package/tests/widget-config-hot-reload.test.js +465 -0
- package/tests/widget-dependency.test.js +591 -0
- package/tests/widget-error-boundary.test.js +749 -0
- package/tests/widget-error-isolation.test.js +469 -0
- package/tests/widget-integration.test.js +1147 -0
- package/tests/widget-refresh-intervals.test.js +284 -0
- 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 };
|
package/src/cli/help.js
ADDED
|
@@ -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 };
|