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,595 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Automated Release Script for claw-dashboard
5
+ *
6
+ * This script automates the release process:
7
+ * 1. Validates the working directory is clean
8
+ * 2. Bumps version in package.json (patch/minor/major)
9
+ * 3. Updates CHANGELOG.md with new version entry
10
+ * 4. Builds the project with ESBuild
11
+ * 5. Creates GPG-signed git tag
12
+ * 6. Optionally creates GitHub release
13
+ *
14
+ * Usage:
15
+ * node scripts/release.js [patch|minor|major] [--sign] [--github]
16
+ *
17
+ * Options:
18
+ * patch, minor, major - Version bump type (default: patch)
19
+ * --sign, -s - GPG sign the release tag and artifacts
20
+ * --github, -g - Create GitHub release (requires gh CLI)
21
+ * --dry-run, -d - Preview changes without executing
22
+ * --help, -h - Show help
23
+ */
24
+
25
+ import { execSync } from 'child_process';
26
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
27
+ import { join, dirname } from 'path';
28
+ import { fileURLToPath } from 'url';
29
+
30
+ const __filename = fileURLToPath(import.meta.url);
31
+ const __dirname = dirname(__filename);
32
+ const ROOT = join(__dirname, '..');
33
+
34
+ // Colors for output
35
+ const colors = {
36
+ reset: '\x1b[0m',
37
+ bright: '\x1b[1m',
38
+ green: '\x1b[32m',
39
+ yellow: '\x1b[33m',
40
+ red: '\x1b[31m',
41
+ cyan: '\x1b[36m',
42
+ gray: '\x1b[90m',
43
+ };
44
+
45
+ /**
46
+ * Print styled message
47
+ */
48
+ function log(message, type = 'info') {
49
+ const prefix = {
50
+ info: `${colors.cyan}ℹ${colors.reset}`,
51
+ success: `${colors.green}✓${colors.reset}`,
52
+ warning: `${colors.yellow}⚠${colors.reset}`,
53
+ error: `${colors.red}✗${colors.reset}`,
54
+ step: `${colors.bright}→${colors.reset}`,
55
+ }[type] || 'ℹ';
56
+
57
+ console.log(`${prefix} ${message}`);
58
+ }
59
+
60
+ /**
61
+ * Execute command with error handling
62
+ */
63
+ function exec(command, options = {}) {
64
+ try {
65
+ return execSync(command, {
66
+ encoding: 'utf8',
67
+ cwd: ROOT,
68
+ stdio: options.silent ? 'pipe' : 'inherit',
69
+ ...options,
70
+ });
71
+ } catch (error) {
72
+ if (!options.ignoreErrors) {
73
+ throw new Error(`Command failed: ${command}\n${error.message}`);
74
+ }
75
+ return null;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Parse command line arguments
81
+ */
82
+ function parseArgs() {
83
+ const args = process.argv.slice(2);
84
+ const result = {
85
+ bumpType: 'patch',
86
+ sign: false,
87
+ github: false,
88
+ dryRun: false,
89
+ help: false,
90
+ };
91
+
92
+ for (const arg of args) {
93
+ switch (arg) {
94
+ case 'patch':
95
+ case 'minor':
96
+ case 'major':
97
+ result.bumpType = arg;
98
+ break;
99
+ case '--sign':
100
+ case '-s':
101
+ result.sign = true;
102
+ break;
103
+ case '--github':
104
+ case '-g':
105
+ result.github = true;
106
+ break;
107
+ case '--dry-run':
108
+ case '-d':
109
+ result.dryRun = true;
110
+ break;
111
+ case '--help':
112
+ case '-h':
113
+ result.help = true;
114
+ break;
115
+ }
116
+ }
117
+
118
+ return result;
119
+ }
120
+
121
+ /**
122
+ * Show help message
123
+ */
124
+ function showHelp() {
125
+ console.log(`
126
+ ${colors.bright}Release Script for claw-dashboard${colors.reset}
127
+
128
+ Automates the release process with version bumping, building, and signing.
129
+
130
+ ${colors.bright}Usage:${colors.reset}
131
+ node scripts/release.js [patch|minor|major] [options]
132
+
133
+ ${colors.bright}Version Types:${colors.reset}
134
+ patch Increment patch version (1.0.0 → 1.0.1) [default]
135
+ minor Increment minor version (1.0.0 → 1.1.0)
136
+ major Increment major version (1.0.0 → 2.0.0)
137
+
138
+ ${colors.bright}Options:${colors.reset}
139
+ --sign, -s GPG sign the release tag and artifacts
140
+ --github, -g Create GitHub release (requires gh CLI)
141
+ --dry-run, -d Preview changes without executing
142
+ --help, -h Show this help message
143
+
144
+ ${colors.bright}Examples:${colors.reset}
145
+ node scripts/release.js patch # Patch release
146
+ node scripts/release.js minor --sign # Minor release with GPG signing
147
+ node scripts/release.js major --github # Major release with GitHub release
148
+ node scripts/release.js --dry-run # Preview changes
149
+
150
+ ${colors.bright}Requirements for signing:${colors.reset}
151
+ - GPG key configured: git config --global user.signingkey YOUR_KEY_ID
152
+ - GPG key added to GitHub: https://github.com/settings/gpg/new
153
+ `);
154
+ }
155
+
156
+ /**
157
+ * Check if working directory is clean
158
+ */
159
+ function checkWorkingDirectory() {
160
+ log('Checking working directory status...', 'step');
161
+
162
+ try {
163
+ const status = exec('git status --porcelain', { silent: true });
164
+ if (status && status.trim()) {
165
+ throw new Error('Working directory is not clean. Please commit or stash changes first.');
166
+ }
167
+ log('Working directory is clean', 'success');
168
+ } catch (error) {
169
+ if (error.message.includes('not a git repository')) {
170
+ throw new Error('Not a git repository');
171
+ }
172
+ throw error;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Get current version from package.json
178
+ */
179
+ function getCurrentVersion() {
180
+ const pkgPath = join(ROOT, 'package.json');
181
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
182
+ return pkg.version;
183
+ }
184
+
185
+ /**
186
+ * Bump version based on type
187
+ */
188
+ function bumpVersion(currentVersion, bumpType) {
189
+ const parts = currentVersion.split('.').map(Number);
190
+ const [major, minor, patch] = parts;
191
+
192
+ switch (bumpType) {
193
+ case 'major':
194
+ return `${major + 1}.0.0`;
195
+ case 'minor':
196
+ return `${major}.${minor + 1}.0`;
197
+ case 'patch':
198
+ default:
199
+ return `${major}.${minor}.${patch + 1}`;
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Update version in package.json
205
+ */
206
+ function updatePackageVersion(newVersion, dryRun) {
207
+ log(`Updating package.json to v${newVersion}...`, 'step');
208
+
209
+ const pkgPath = join(ROOT, 'package.json');
210
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
211
+ const oldVersion = pkg.version;
212
+ pkg.version = newVersion;
213
+
214
+ if (dryRun) {
215
+ log(`[DRY-RUN] Would update version: ${oldVersion} → ${newVersion}`, 'warning');
216
+ return;
217
+ }
218
+
219
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
220
+ log(`Version updated: ${oldVersion} → ${newVersion}`, 'success');
221
+ }
222
+
223
+ /**
224
+ * Get latest changelog entry
225
+ */
226
+ function getLatestChangelogEntry() {
227
+ const changelogPath = join(ROOT, 'CHANGELOG.md');
228
+ if (!existsSync(changelogPath)) {
229
+ return null;
230
+ }
231
+
232
+ const content = readFileSync(changelogPath, 'utf8');
233
+ const unreleasedMatch = content.match(/## \[Unreleased\]([\s\S]*?)(?=## \[|$)/);
234
+
235
+ if (unreleasedMatch) {
236
+ return unreleasedMatch[1].trim();
237
+ }
238
+
239
+ return null;
240
+ }
241
+
242
+ /**
243
+ * Update CHANGELOG.md
244
+ */
245
+ function updateChangelog(newVersion, dryRun) {
246
+ log('Updating CHANGELOG.md...', 'step');
247
+
248
+ const changelogPath = join(ROOT, 'CHANGELOG.md');
249
+ if (!existsSync(changelogPath)) {
250
+ log('CHANGELOG.md not found, skipping', 'warning');
251
+ return null;
252
+ }
253
+
254
+ const today = new Date().toISOString().split('T')[0];
255
+ const unreleasedSection = getLatestChangelogEntry();
256
+
257
+ let content = readFileSync(changelogPath, 'utf8');
258
+
259
+ // Replace [Unreleased] link with new version
260
+ const unreleasedLink = `[Unreleased]: https://github.com/spleck/claw-dashboard/compare/v${newVersion}...HEAD`;
261
+ const newVersionLink = `[${newVersion}]: https://github.com/spleck/claw-dashboard/compare/v${getCurrentVersion()}...v${newVersion}`;
262
+
263
+ content = content.replace(
264
+ /## \[Unreleased\]/,
265
+ `## [Unreleased]\n\n## [${newVersion}] - ${today}`
266
+ );
267
+
268
+ // Update comparison links at the bottom
269
+ content = content.replace(
270
+ /\[Unreleased\]: .*/,
271
+ `${unreleasedLink}\n${newVersionLink}`
272
+ );
273
+
274
+ if (dryRun) {
275
+ log('[DRY-RUN] Would update CHANGELOG.md', 'warning');
276
+ return unreleasedSection;
277
+ }
278
+
279
+ writeFileSync(changelogPath, content);
280
+ log('CHANGELOG.md updated', 'success');
281
+
282
+ return unreleasedSection;
283
+ }
284
+
285
+ /**
286
+ * Check if ESBuild is available
287
+ */
288
+ function checkEsbuild() {
289
+ log('Checking ESBuild availability...', 'step');
290
+
291
+ try {
292
+ exec('npx esbuild --version', { silent: true });
293
+ log('ESBuild is available', 'success');
294
+ return true;
295
+ } catch {
296
+ log('ESBuild not found, installing...', 'warning');
297
+ try {
298
+ exec('npm install --save-dev esbuild');
299
+ log('ESBuild installed', 'success');
300
+ return true;
301
+ } catch (error) {
302
+ log('Failed to install ESBuild', 'error');
303
+ return false;
304
+ }
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Build the project
310
+ */
311
+ function buildProject(dryRun) {
312
+ log('Building project...', 'step');
313
+
314
+ if (dryRun) {
315
+ log('[DRY-RUN] Would run: node esbuild.config.js', 'warning');
316
+ return;
317
+ }
318
+
319
+ // Ensure dist directory exists
320
+ const distPath = join(ROOT, 'dist');
321
+ if (!existsSync(distPath)) {
322
+ mkdirSync(distPath, { recursive: true });
323
+ }
324
+
325
+ try {
326
+ exec('node esbuild.config.js');
327
+ log('Build successful', 'success');
328
+ } catch (error) {
329
+ throw new Error(`Build failed: ${error.message}`);
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Check if GPG is configured
335
+ */
336
+ function checkGpgConfig() {
337
+ try {
338
+ const signingKey = exec('git config --get user.signingkey', {
339
+ silent: true,
340
+ ignoreErrors: true,
341
+ });
342
+ return signingKey && signingKey.trim();
343
+ } catch {
344
+ return null;
345
+ }
346
+ }
347
+
348
+ /**
349
+ * Check GPG availability
350
+ */
351
+ function checkGpg(sign) {
352
+ if (!sign) return true;
353
+
354
+ log('Checking GPG configuration...', 'step');
355
+
356
+ const signingKey = checkGpgConfig();
357
+ if (!signingKey) {
358
+ throw new Error(
359
+ 'GPG signing requested but no signing key configured.\n' +
360
+ 'Run: git config --global user.signingkey YOUR_KEY_ID'
361
+ );
362
+ }
363
+
364
+ try {
365
+ exec(`gpg --list-keys ${signingKey.trim()}`, { silent: true });
366
+ log(`GPG key found: ${signingKey.trim().substring(0, 16)}...`, 'success');
367
+ return true;
368
+ } catch {
369
+ throw new Error(`GPG key ${signingKey.trim()} not found in keyring`);
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Create GPG signature for artifact
375
+ */
376
+ function createGpgSignature(filePath, dryRun) {
377
+ if (dryRun) {
378
+ log(`[DRY-RUN] Would sign: ${filePath}`, 'warning');
379
+ return;
380
+ }
381
+
382
+ try {
383
+ exec(`gpg --armor --detach-sign --output ${filePath}.asc ${filePath}`, {
384
+ silent: true,
385
+ });
386
+ log(`GPG signature created: ${filePath}.asc`, 'success');
387
+ } catch (error) {
388
+ log(`Failed to sign ${filePath}: ${error.message}`, 'warning');
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Sign release artifacts
394
+ */
395
+ function signArtifacts(version, dryRun) {
396
+ log('Signing release artifacts...', 'step');
397
+
398
+ const artifacts = [
399
+ join(ROOT, 'dist', 'clawdash'),
400
+ join(ROOT, 'package.json'),
401
+ ];
402
+
403
+ for (const artifact of artifacts) {
404
+ if (existsSync(artifact)) {
405
+ createGpgSignature(artifact, dryRun);
406
+ }
407
+ }
408
+ }
409
+
410
+ /**
411
+ * Create git commit and tag
412
+ */
413
+ function createGitTag(version, sign, releaseNotes, dryRun) {
414
+ log('Creating git commit and tag...', 'step');
415
+
416
+ const tagMessage = releaseNotes
417
+ ? `Release v${version}\n\n${releaseNotes}`
418
+ : `Release v${version}`;
419
+
420
+ if (dryRun) {
421
+ log('[DRY-RUN] Would create commit: chore: bump version to v' + version, 'warning');
422
+ log(`[DRY-RUN] Would create tag: v${version}${sign ? ' (GPG signed)' : ''}`, 'warning');
423
+ return;
424
+ }
425
+
426
+ // Stage changes
427
+ exec('git add package.json CHANGELOG.md');
428
+
429
+ // Create commit
430
+ exec(`git commit -m "chore: bump version to v${version}"`);
431
+ log(`Created commit: chore: bump version to v${version}`, 'success');
432
+
433
+ // Create tag
434
+ const tagCmd = sign
435
+ ? `git tag -s v${version} -m "${tagMessage}"`
436
+ : `git tag -a v${version} -m "${tagMessage}"`;
437
+
438
+ exec(tagCmd);
439
+ log(`Created tag: v${version}${sign ? ' (GPG signed)' : ''}`, 'success');
440
+ }
441
+
442
+ /**
443
+ * Check if GitHub CLI is available
444
+ */
445
+ function checkGhCli() {
446
+ try {
447
+ exec('gh --version', { silent: true });
448
+ return true;
449
+ } catch {
450
+ return false;
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Create GitHub release
456
+ */
457
+ function createGithubRelease(version, releaseNotes, sign, dryRun) {
458
+ log('Creating GitHub release...', 'step');
459
+
460
+ if (!checkGhCli()) {
461
+ log('GitHub CLI (gh) not found, skipping GitHub release', 'warning');
462
+ log('Install from: https://cli.github.com/', 'info');
463
+ return;
464
+ }
465
+
466
+ const title = `v${version}`;
467
+ const notes = releaseNotes || `Release ${title}`;
468
+
469
+ const cmd = [
470
+ 'gh release create',
471
+ `v${version}`,
472
+ '--title', `"${title}"`,
473
+ '--notes', `"${notes}"`,
474
+ sign ? '--verify-tag' : '',
475
+ join(ROOT, 'dist', 'clawdash'),
476
+ ].filter(Boolean).join(' ');
477
+
478
+ if (dryRun) {
479
+ log('[DRY-RUN] Would run: ' + cmd, 'warning');
480
+ return;
481
+ }
482
+
483
+ try {
484
+ exec(cmd);
485
+ log(`GitHub release created: ${title}`, 'success');
486
+ } catch (error) {
487
+ log(`Failed to create GitHub release: ${error.message}`, 'warning');
488
+ }
489
+ }
490
+
491
+ /**
492
+ * Print summary
493
+ */
494
+ function printSummary(version, bumpType, sign, github, dryRun) {
495
+ console.log(`
496
+ ${colors.bright}╔══════════════════════════════════════════╗${colors.reset}
497
+ ${colors.bright}║${colors.reset} ${colors.green}Release Summary${colors.reset} ${colors.bright}║${colors.reset}
498
+ ${colors.bright}╠══════════════════════════════════════════╣${colors.reset}
499
+ Version: ${colors.cyan}v${version}${colors.reset}
500
+ Type: ${colors.yellow}${bumpType}${colors.reset}
501
+ GPG Sign: ${sign ? colors.green + 'Yes' : colors.gray + 'No'}${colors.reset}
502
+ GitHub: ${github ? colors.green + 'Yes' : colors.gray + 'No'}${colors.reset}
503
+ Mode: ${dryRun ? colors.yellow + 'DRY-RUN' : colors.green + 'LIVE'}${colors.reset}
504
+ ${colors.bright}╚══════════════════════════════════════════╝${colors.reset}
505
+ `);
506
+
507
+ if (!dryRun) {
508
+ console.log(`
509
+ Next steps:
510
+ 1. Review the changes: git show HEAD
511
+ 2. Review the tag: git show v${version}
512
+ 3. Push to remote: git push && git push origin v${version}
513
+ `);
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Main execution
519
+ */
520
+ async function main() {
521
+ const args = parseArgs();
522
+
523
+ if (args.help) {
524
+ showHelp();
525
+ process.exit(0);
526
+ }
527
+
528
+ console.log(`\n${colors.bright}🚀 Starting Release Process${colors.reset}\n`);
529
+
530
+ try {
531
+ // Pre-flight checks
532
+ checkWorkingDirectory();
533
+
534
+ const currentVersion = getCurrentVersion();
535
+ log(`Current version: ${currentVersion}`, 'info');
536
+
537
+ const newVersion = bumpVersion(currentVersion, args.bumpType);
538
+ log(`New version will be: ${newVersion}`, 'info');
539
+
540
+ // Check dependencies
541
+ if (!checkEsbuild()) {
542
+ process.exit(1);
543
+ }
544
+
545
+ if (args.sign && !checkGpg(args.sign)) {
546
+ process.exit(1);
547
+ }
548
+
549
+ if (args.github && !checkGhCli()) {
550
+ log('GitHub CLI not found, but continuing...', 'warning');
551
+ }
552
+
553
+ // Confirmation prompt in non-dry-run mode
554
+ if (!args.dryRun) {
555
+ console.log(`\n${colors.yellow}This will create a new release.${colors.reset}`);
556
+ console.log(`${colors.gray}Use --dry-run to preview changes.${colors.reset}\n`);
557
+ }
558
+
559
+ // Execute release steps
560
+ updatePackageVersion(newVersion, args.dryRun);
561
+ const releaseNotes = updateChangelog(newVersion, args.dryRun);
562
+ buildProject(args.dryRun);
563
+
564
+ if (args.sign) {
565
+ signArtifacts(newVersion, args.dryRun);
566
+ }
567
+
568
+ createGitTag(newVersion, args.sign, releaseNotes, args.dryRun);
569
+
570
+ if (args.github) {
571
+ createGithubRelease(newVersion, releaseNotes, args.sign, args.dryRun);
572
+ }
573
+
574
+ // Summary
575
+ printSummary(newVersion, args.bumpType, args.sign, args.github, args.dryRun);
576
+
577
+ process.exit(0);
578
+ } catch (error) {
579
+ console.error(`\n${colors.red}✗ Release failed:${colors.reset}`, error.message);
580
+ process.exit(1);
581
+ }
582
+ }
583
+
584
+ // Run if called directly
585
+ if (import.meta.url === `file://${process.argv[1]}`) {
586
+ main();
587
+ }
588
+
589
+ export {
590
+ bumpVersion,
591
+ checkWorkingDirectory,
592
+ getCurrentVersion,
593
+ updatePackageVersion,
594
+ updateChangelog,
595
+ };