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,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Import Snapshot Module
|
|
3
|
+
* Imports dashboard configuration from a snapshot file
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import { join, resolve } from 'path';
|
|
9
|
+
import {
|
|
10
|
+
importSnapshotFromFile,
|
|
11
|
+
validateSnapshot,
|
|
12
|
+
mergeSnapshotSettings,
|
|
13
|
+
getSnapshotSummary,
|
|
14
|
+
getSnapshotsDirectory,
|
|
15
|
+
listSnapshots,
|
|
16
|
+
} from '../snapshot.js';
|
|
17
|
+
import { DEFAULT_SETTINGS, PATHS } from '../config.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Save settings to file
|
|
21
|
+
* @param {Object} settings - Settings to save
|
|
22
|
+
*/
|
|
23
|
+
function saveSettings(settings) {
|
|
24
|
+
try {
|
|
25
|
+
const settingsPath = PATHS.SETTINGS;
|
|
26
|
+
const dir = PATHS.OPENCLAW_DIR;
|
|
27
|
+
if (!fs.existsSync(dir)) {
|
|
28
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
31
|
+
} catch (err) {
|
|
32
|
+
throw new Error(`Failed to save settings: ${err.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run the import-snapshot CLI command
|
|
38
|
+
* @param {string[]} args - CLI arguments
|
|
39
|
+
* @returns {number} Exit code
|
|
40
|
+
*/
|
|
41
|
+
export async function runImportSnapshotCli(args) {
|
|
42
|
+
const jsonOutput = args.includes('--json') || args.includes('-j');
|
|
43
|
+
const dryRun = args.includes('--dry-run') || args.includes('-d');
|
|
44
|
+
const force = args.includes('--force') || args.includes('-f');
|
|
45
|
+
const showHelp = args.includes('--help') || args.includes('-h');
|
|
46
|
+
const listMode = args.includes('--list') || args.includes('-l');
|
|
47
|
+
|
|
48
|
+
// Get file path (first non-flag argument)
|
|
49
|
+
const filePath = args.find(a => !a.startsWith('-'));
|
|
50
|
+
|
|
51
|
+
if (showHelp) {
|
|
52
|
+
console.log(`
|
|
53
|
+
Import Dashboard Snapshot for Claw Dashboard
|
|
54
|
+
|
|
55
|
+
Usage: clawdash import-snapshot [path] [options]
|
|
56
|
+
|
|
57
|
+
Arguments:
|
|
58
|
+
path Path to snapshot file (optional with --list)
|
|
59
|
+
|
|
60
|
+
Options:
|
|
61
|
+
-l, --list List available snapshots
|
|
62
|
+
-d, --dry-run Validate without applying
|
|
63
|
+
-f, --force Skip confirmation
|
|
64
|
+
-j, --json Output results as JSON
|
|
65
|
+
-h, --help Show this help message
|
|
66
|
+
|
|
67
|
+
Examples:
|
|
68
|
+
clawdash import-snapshot --list
|
|
69
|
+
clawdash import-snapshot ~/my-layout.json
|
|
70
|
+
clawdash import-snapshot ~/.openclaw/snapshots/claw-snapshot-*.json --dry-run
|
|
71
|
+
clawdash import-snapshot ~/backup.json --force
|
|
72
|
+
`);
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// List mode
|
|
77
|
+
if (listMode) {
|
|
78
|
+
const snapshots = listSnapshots();
|
|
79
|
+
if (jsonOutput) {
|
|
80
|
+
console.log(JSON.stringify({ snapshots }, null, 2));
|
|
81
|
+
} else {
|
|
82
|
+
if (snapshots.length === 0) {
|
|
83
|
+
console.log('No snapshots found in ~/.openclaw/snapshots/');
|
|
84
|
+
} else {
|
|
85
|
+
console.log('Available snapshots:');
|
|
86
|
+
console.log('');
|
|
87
|
+
snapshots.forEach((s, i) => {
|
|
88
|
+
const date = new Date(s.createdAt).toLocaleDateString();
|
|
89
|
+
console.log(` ${i + 1}. ${s.name}`);
|
|
90
|
+
console.log(` Created: ${date}`);
|
|
91
|
+
console.log(` Path: ${s.path}`);
|
|
92
|
+
if (s.metadata) {
|
|
93
|
+
console.log(` Widgets: ${s.metadata.widgetCount}, Plugins: ${s.metadata.pluginCount}`);
|
|
94
|
+
}
|
|
95
|
+
console.log('');
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Require file path for import
|
|
103
|
+
if (!filePath) {
|
|
104
|
+
if (jsonOutput) {
|
|
105
|
+
console.log(JSON.stringify({
|
|
106
|
+
success: false,
|
|
107
|
+
error: 'File path is required (use --list to see available snapshots)',
|
|
108
|
+
}, null, 2));
|
|
109
|
+
} else {
|
|
110
|
+
console.error('Error: File path is required');
|
|
111
|
+
console.error('Run with --list to see available snapshots');
|
|
112
|
+
console.error('Run with --help for usage information');
|
|
113
|
+
}
|
|
114
|
+
return 1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
// Resolve the path
|
|
119
|
+
let resolvedPath = filePath;
|
|
120
|
+
if (filePath.startsWith('~')) {
|
|
121
|
+
resolvedPath = join(os.homedir(), filePath.slice(1));
|
|
122
|
+
}
|
|
123
|
+
resolvedPath = resolve(resolvedPath);
|
|
124
|
+
|
|
125
|
+
// Import and validate
|
|
126
|
+
const result = importSnapshotFromFile(resolvedPath);
|
|
127
|
+
|
|
128
|
+
if (!result.success) {
|
|
129
|
+
if (jsonOutput) {
|
|
130
|
+
console.log(JSON.stringify({
|
|
131
|
+
success: false,
|
|
132
|
+
error: result.error,
|
|
133
|
+
path: resolvedPath,
|
|
134
|
+
}, null, 2));
|
|
135
|
+
} else {
|
|
136
|
+
console.error(`✗ Import failed: ${result.error}`);
|
|
137
|
+
}
|
|
138
|
+
return 1;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const { snapshot } = result;
|
|
142
|
+
|
|
143
|
+
// Dry run mode - just validate and show info
|
|
144
|
+
if (dryRun) {
|
|
145
|
+
const summary = getSnapshotSummary(snapshot);
|
|
146
|
+
if (jsonOutput) {
|
|
147
|
+
console.log(JSON.stringify({
|
|
148
|
+
success: true,
|
|
149
|
+
dryRun: true,
|
|
150
|
+
path: resolvedPath,
|
|
151
|
+
snapshot: {
|
|
152
|
+
name: snapshot.name,
|
|
153
|
+
description: snapshot.description,
|
|
154
|
+
version: snapshot.dashboardVersion,
|
|
155
|
+
schemaVersion: snapshot.schemaVersion,
|
|
156
|
+
createdAt: snapshot.createdAt,
|
|
157
|
+
metadata: snapshot.metadata,
|
|
158
|
+
},
|
|
159
|
+
summary: summary.split('\n'),
|
|
160
|
+
}, null, 2));
|
|
161
|
+
} else {
|
|
162
|
+
console.log('✓ Snapshot is valid (dry run, no changes applied)');
|
|
163
|
+
console.log('');
|
|
164
|
+
console.log(summary);
|
|
165
|
+
}
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Show summary before applying (unless --force)
|
|
170
|
+
if (!jsonOutput && !force) {
|
|
171
|
+
const summary = getSnapshotSummary(snapshot);
|
|
172
|
+
console.log('Snapshot to import:');
|
|
173
|
+
console.log('');
|
|
174
|
+
console.log(summary);
|
|
175
|
+
console.log('');
|
|
176
|
+
console.log('Run with --force to apply, or --dry-run to preview');
|
|
177
|
+
return 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Load current settings and merge
|
|
181
|
+
const currentSettings = { ...DEFAULT_SETTINGS };
|
|
182
|
+
try {
|
|
183
|
+
if (fs.existsSync(PATHS.SETTINGS)) {
|
|
184
|
+
const data = fs.readFileSync(PATHS.SETTINGS, 'utf8');
|
|
185
|
+
Object.assign(currentSettings, JSON.parse(data));
|
|
186
|
+
}
|
|
187
|
+
} catch (err) {
|
|
188
|
+
// Continue with defaults
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const mergedSettings = mergeSnapshotSettings(currentSettings, snapshot.settings);
|
|
192
|
+
|
|
193
|
+
// Save merged settings
|
|
194
|
+
saveSettings(mergedSettings);
|
|
195
|
+
|
|
196
|
+
if (jsonOutput) {
|
|
197
|
+
console.log(JSON.stringify({
|
|
198
|
+
success: true,
|
|
199
|
+
path: resolvedPath,
|
|
200
|
+
snapshot: {
|
|
201
|
+
name: snapshot.name,
|
|
202
|
+
version: snapshot.dashboardVersion,
|
|
203
|
+
createdAt: snapshot.createdAt,
|
|
204
|
+
},
|
|
205
|
+
applied: Object.keys(snapshot.settings),
|
|
206
|
+
}, null, 2));
|
|
207
|
+
} else {
|
|
208
|
+
console.log('✓ Snapshot imported successfully');
|
|
209
|
+
console.log(` Name: ${snapshot.name}`);
|
|
210
|
+
console.log(` Version: ${snapshot.dashboardVersion}`);
|
|
211
|
+
console.log('');
|
|
212
|
+
console.log('Settings have been merged and saved.');
|
|
213
|
+
console.log('Restart Claw Dashboard to apply all changes.');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return 0;
|
|
217
|
+
} catch (err) {
|
|
218
|
+
if (jsonOutput) {
|
|
219
|
+
console.log(JSON.stringify({
|
|
220
|
+
success: false,
|
|
221
|
+
error: err.message,
|
|
222
|
+
}, null, 2));
|
|
223
|
+
} else {
|
|
224
|
+
console.error(`✗ Import error: ${err.message}`);
|
|
225
|
+
}
|
|
226
|
+
return 1;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export default { runImportSnapshotCli };
|
package/src/cli/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Module Index
|
|
3
|
+
* Centralized export for all CLI command handlers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { parseCliArgs } from './args.js';
|
|
7
|
+
export { showHelp } from './help.js';
|
|
8
|
+
export { showVersion } from './version.js';
|
|
9
|
+
export { runValidatePluginCli } from './validate-plugin.js';
|
|
10
|
+
export { runValidateConfigCli } from './validate-config.js';
|
|
11
|
+
export { runExportSnapshotCli } from './export-snapshot.js';
|
|
12
|
+
export { runImportSnapshotCli } from './import-snapshot.js';
|
|
13
|
+
export { runListTemplatesCli } from './list-templates.js';
|
|
14
|
+
export { runExportScheduleCli } from './export-schedule.js';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI List Templates Module
|
|
3
|
+
* Lists available widget templates for scaffolding
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { listTemplates } from '../plugin-scaffold.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Run the list-templates CLI command
|
|
10
|
+
* @param {string[]} args - CLI arguments
|
|
11
|
+
* @returns {number} Exit code
|
|
12
|
+
*/
|
|
13
|
+
export async function runListTemplatesCli(args) {
|
|
14
|
+
const showHelp = args.includes('--help') || args.includes('-h');
|
|
15
|
+
|
|
16
|
+
if (showHelp) {
|
|
17
|
+
console.log(`
|
|
18
|
+
List Available Widget Templates
|
|
19
|
+
|
|
20
|
+
Usage: clawdash list-templates [options]
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
-j, --json Output as JSON
|
|
24
|
+
-h, --help Show this help message
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
clawdash list-templates
|
|
28
|
+
clawdash list-templates --json
|
|
29
|
+
`);
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const jsonOutput = args.includes('--json') || args.includes('-j');
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const templates = listTemplates();
|
|
37
|
+
|
|
38
|
+
if (jsonOutput) {
|
|
39
|
+
console.log(JSON.stringify(templates, null, 2));
|
|
40
|
+
} else {
|
|
41
|
+
console.log('');
|
|
42
|
+
console.log('╔══════════════════════════════════════════════════════════════╗');
|
|
43
|
+
console.log('║ Available Widget Templates ║');
|
|
44
|
+
console.log('╚══════════════════════════════════════════════════════════════╝');
|
|
45
|
+
console.log('');
|
|
46
|
+
|
|
47
|
+
templates.forEach((template, index) => {
|
|
48
|
+
console.log(` ${index + 1}. ${template.name}`);
|
|
49
|
+
console.log(` ID: ${template.id}`);
|
|
50
|
+
console.log(` ${template.description}`);
|
|
51
|
+
console.log('');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
console.log('Usage:');
|
|
55
|
+
console.log(' clawdash create-plugin <name> --template <id>');
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log('Example:');
|
|
58
|
+
console.log(' clawdash create-plugin my-widget --template api');
|
|
59
|
+
console.log('');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return 0;
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error(`Error: ${err.message}`);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default { runListTemplatesCli };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Validate Config Module
|
|
3
|
+
* Validates dashboard configuration files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import { resolve, join } from 'path';
|
|
8
|
+
import { validateConfigFile, formatConfigValidationResult, getDefaultConfigPath } from '../config-validator.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Run the validate-config CLI command
|
|
12
|
+
* @param {string[]} args - CLI arguments
|
|
13
|
+
* @returns {number} Exit code
|
|
14
|
+
*/
|
|
15
|
+
export async function runValidateConfigCli(args) {
|
|
16
|
+
const configPath = args[0];
|
|
17
|
+
const jsonOutput = args.includes('--json') || args.includes('-j');
|
|
18
|
+
const showHelp = args.includes('--help') || args.includes('-h');
|
|
19
|
+
const strict = args.includes('--strict') || args.includes('-s');
|
|
20
|
+
|
|
21
|
+
if (showHelp) {
|
|
22
|
+
console.log(`
|
|
23
|
+
Validate Dashboard Configuration for Claw Dashboard
|
|
24
|
+
|
|
25
|
+
Usage: clawdash validate-config [path] [options]
|
|
26
|
+
|
|
27
|
+
Arguments:
|
|
28
|
+
path Path to configuration file (optional)
|
|
29
|
+
Defaults to: ~/.openclaw/dashboard-settings.json
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
-j, --json Output results as JSON
|
|
33
|
+
-s, --strict Fail on unknown properties
|
|
34
|
+
-h, --help Show this help message
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
clawdash validate-config
|
|
38
|
+
clawdash validate-config ~/.openclaw/dashboard-settings.json
|
|
39
|
+
clawdash validate-config ./my-config.json --json
|
|
40
|
+
clawdash validate-config --strict
|
|
41
|
+
`);
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Determine the config file path
|
|
46
|
+
const targetPath = configPath || getDefaultConfigPath();
|
|
47
|
+
|
|
48
|
+
// Resolve the path (handle ~ expansion)
|
|
49
|
+
let resolvedPath = targetPath;
|
|
50
|
+
if (targetPath.startsWith('~')) {
|
|
51
|
+
resolvedPath = join(os.homedir(), targetPath.slice(1));
|
|
52
|
+
}
|
|
53
|
+
resolvedPath = resolve(resolvedPath);
|
|
54
|
+
|
|
55
|
+
// Validate the configuration
|
|
56
|
+
const result = validateConfigFile(resolvedPath, { strict });
|
|
57
|
+
|
|
58
|
+
// Output results
|
|
59
|
+
if (jsonOutput) {
|
|
60
|
+
const output = {
|
|
61
|
+
valid: result.valid,
|
|
62
|
+
path: resolvedPath,
|
|
63
|
+
errors: result.errors,
|
|
64
|
+
warnings: result.warnings,
|
|
65
|
+
info: result.info,
|
|
66
|
+
stats: result.stats,
|
|
67
|
+
};
|
|
68
|
+
console.log(JSON.stringify(output, null, 2));
|
|
69
|
+
} else {
|
|
70
|
+
console.log(formatConfigValidationResult(result, resolvedPath));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return result.valid ? 0 : 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default { runValidateConfigCli };
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Validate Plugin Module
|
|
3
|
+
* Validates plugin.json manifest files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import { dirname, join, resolve } from 'path';
|
|
9
|
+
import { validateManifest, validatePluginIdFormat } from '../plugin-manifest-validator.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Run the validate-plugin CLI command
|
|
13
|
+
* @param {string[]} args - CLI arguments
|
|
14
|
+
* @returns {number} Exit code
|
|
15
|
+
*/
|
|
16
|
+
export async function runValidatePluginCli(args) {
|
|
17
|
+
const pluginPath = args[0];
|
|
18
|
+
const jsonOutput = args.includes('--json') || args.includes('-j');
|
|
19
|
+
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
20
|
+
const showHelp = args.includes('--help') || args.includes('-h');
|
|
21
|
+
|
|
22
|
+
if (showHelp) {
|
|
23
|
+
console.log(`
|
|
24
|
+
Validate Plugin Manifest for Claw Dashboard
|
|
25
|
+
|
|
26
|
+
Usage: clawdash validate-plugin <path> [options]
|
|
27
|
+
|
|
28
|
+
Arguments:
|
|
29
|
+
path Path to plugin.json file or plugin directory
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
-j, --json Output results as JSON
|
|
33
|
+
-v, --verbose Show detailed output including code analysis
|
|
34
|
+
-h, --help Show this help message
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
clawdash validate-plugin ./my-widget/plugin.json
|
|
38
|
+
clawdash validate-plugin ~/.openclaw/plugins/my-widget
|
|
39
|
+
clawdash validate-plugin ./my-widget --json
|
|
40
|
+
clawdash validate-plugin ./my-widget --verbose
|
|
41
|
+
`);
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!pluginPath) {
|
|
46
|
+
console.error('Error: Path is required');
|
|
47
|
+
console.error('Run with --help for usage information');
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Resolve the path
|
|
52
|
+
let resolvedPath = pluginPath;
|
|
53
|
+
if (pluginPath.startsWith('~')) {
|
|
54
|
+
resolvedPath = join(os.homedir(), pluginPath.slice(1));
|
|
55
|
+
}
|
|
56
|
+
resolvedPath = resolve(resolvedPath);
|
|
57
|
+
|
|
58
|
+
// Check if path exists
|
|
59
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
60
|
+
const result = {
|
|
61
|
+
valid: false,
|
|
62
|
+
error: `Path does not exist: ${pluginPath}`,
|
|
63
|
+
};
|
|
64
|
+
if (jsonOutput) {
|
|
65
|
+
console.log(JSON.stringify(result, null, 2));
|
|
66
|
+
} else {
|
|
67
|
+
console.error(`Error: ${result.error}`);
|
|
68
|
+
}
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Determine the manifest file path
|
|
73
|
+
let manifestPath = resolvedPath;
|
|
74
|
+
const stats = fs.statSync(resolvedPath);
|
|
75
|
+
if (stats.isDirectory()) {
|
|
76
|
+
manifestPath = join(resolvedPath, 'plugin.json');
|
|
77
|
+
if (!fs.existsSync(manifestPath)) {
|
|
78
|
+
const result = {
|
|
79
|
+
valid: false,
|
|
80
|
+
path: resolvedPath,
|
|
81
|
+
error: `No plugin.json found in directory: ${pluginPath}`,
|
|
82
|
+
};
|
|
83
|
+
if (jsonOutput) {
|
|
84
|
+
console.log(JSON.stringify(result, null, 2));
|
|
85
|
+
} else {
|
|
86
|
+
console.error(`Error: ${result.error}`);
|
|
87
|
+
}
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Read and parse the manifest
|
|
93
|
+
let manifest;
|
|
94
|
+
try {
|
|
95
|
+
const content = fs.readFileSync(manifestPath, 'utf8');
|
|
96
|
+
manifest = JSON.parse(content);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
const result = {
|
|
99
|
+
valid: false,
|
|
100
|
+
path: manifestPath,
|
|
101
|
+
error: `Failed to read/parse plugin.json: ${err.message}`,
|
|
102
|
+
};
|
|
103
|
+
if (jsonOutput) {
|
|
104
|
+
console.log(JSON.stringify(result, null, 2));
|
|
105
|
+
} else {
|
|
106
|
+
console.error(`Error: ${result.error}`);
|
|
107
|
+
}
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Validate the manifest
|
|
112
|
+
const validation = validateManifest(manifest);
|
|
113
|
+
|
|
114
|
+
// Also validate the plugin ID if present
|
|
115
|
+
let idValidation = { valid: true };
|
|
116
|
+
if (manifest.id) {
|
|
117
|
+
idValidation = validatePluginIdFormat(manifest.id);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const result = {
|
|
121
|
+
valid: validation.valid && idValidation.valid,
|
|
122
|
+
path: manifestPath,
|
|
123
|
+
errors: validation.errors,
|
|
124
|
+
id: manifest.id || null,
|
|
125
|
+
name: manifest.name || null,
|
|
126
|
+
version: manifest.version || null,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if (!idValidation.valid) {
|
|
130
|
+
result.errors.push(`Invalid plugin ID: ${idValidation.error}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Enhanced validation for verbose mode
|
|
134
|
+
let warnings = [];
|
|
135
|
+
if (verbose && result.valid) {
|
|
136
|
+
// Check for recommended fields
|
|
137
|
+
if (!manifest.description || manifest.description === 'A custom widget plugin for Claw Dashboard') {
|
|
138
|
+
warnings.push('Add a meaningful description to your plugin');
|
|
139
|
+
}
|
|
140
|
+
if (!manifest.author) {
|
|
141
|
+
warnings.push('Missing author - recommended for plugin distribution');
|
|
142
|
+
}
|
|
143
|
+
if (!manifest.config || Object.keys(manifest.config).length === 0) {
|
|
144
|
+
warnings.push('Consider adding configurable options to your plugin');
|
|
145
|
+
}
|
|
146
|
+
// Check for index.js if it's a widget type
|
|
147
|
+
if (manifest.type === 'widget') {
|
|
148
|
+
const indexPath = stats.isDirectory() ? join(resolvedPath, 'index.js') : join(dirname(resolvedPath), 'index.js');
|
|
149
|
+
if (!fs.existsSync(indexPath)) {
|
|
150
|
+
result.valid = false;
|
|
151
|
+
result.errors.push('Widget plugins must have an index.js file');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Output results
|
|
157
|
+
if (jsonOutput) {
|
|
158
|
+
if (verbose) {
|
|
159
|
+
result.warnings = warnings;
|
|
160
|
+
}
|
|
161
|
+
console.log(JSON.stringify(result, null, 2));
|
|
162
|
+
} else {
|
|
163
|
+
if (result.valid) {
|
|
164
|
+
console.log(`✓ Valid plugin manifest: ${manifestPath}`);
|
|
165
|
+
console.log(` ID: ${result.id}`);
|
|
166
|
+
console.log(` Name: ${result.name}`);
|
|
167
|
+
console.log(` Version: ${result.version}`);
|
|
168
|
+
if (verbose && warnings.length > 0) {
|
|
169
|
+
console.log('');
|
|
170
|
+
console.log('Warnings:');
|
|
171
|
+
warnings.forEach(warning => {
|
|
172
|
+
console.log(` ⚠ ${warning}`);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
console.error(`✗ Invalid plugin manifest: ${manifestPath}`);
|
|
177
|
+
console.error(' Errors:');
|
|
178
|
+
result.errors.forEach(error => {
|
|
179
|
+
console.error(` - ${error}`);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return result.valid ? 0 : 1;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export default { runValidatePluginCli };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Version Module
|
|
3
|
+
* Displays version information for Claw Dashboard
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DASHBOARD_VERSION } from '../config.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Display version information
|
|
10
|
+
*/
|
|
11
|
+
export function showVersion() {
|
|
12
|
+
console.log(`clawdash ${DASHBOARD_VERSION}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default { showVersion };
|