swadhinbiswas

[email protected]
2026-01-01 23:01:25 +0600 committed NaN years ago
8630af3
commit 8630af3
31 files changed
+7667 -237
β–Ό.gitignore
+1-0
48
48
49
49
# Monaco Editor cache
50
50
.monaco/
51
+.vercel
51
52
β–ΌDEPLOYMENT.md
+1-1
116
116
117
117
```bash
118
118
# 1. Clone and configure
119
-git clone https://github.com/yourusername/OpenCodeHub.git
119
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
120
120
cd OpenCodeHub
121
121
122
122
# 2. Install dependencies
123
123
β–ΌREADME.md
+536-6
81
81
82
82
```bash
83
83
# 1. Clone the repository
84
-git clone https://github.com/yourusername/OpenCodeHub.git
84
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
85
85
cd OpenCodeHub
86
86
87
87
# 2. Install dependencies
113
113
114
114
```bash
115
115
# 1. Clone and configure
116
-git clone https://github.com/yourusername/OpenCodeHub.git
116
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
117
117
cd OpenCodeHub
118
118
cp .env.example .env
119
119
129
129
```
130
130
131
131
---
132
+# Advanced Features Documentation Insert
133
+
134
+**Insert this content into README.md after line 131 (after Docker installation section, before Configuration section)**
135
+
136
+---
137
+
138
+## 🎯 OCH CLI - Command Line Interface
139
+
140
+Powerful CLI for stack-first PR workflows.
141
+
142
+### Installation
143
+
144
+```bash
145
+# Install globally
146
+npm install -g opencodehub-cli
147
+
148
+# Or use with npx
149
+npx opencodehub-cli
150
+
151
+# Verify installation
152
+och --version
153
+```
154
+
155
+### Quick Setup
156
+
157
+```bash
158
+# 1. Login to your OpenCode Hub instance
159
+och auth login --url https://git.yourcompany.com
160
+
161
+# 2. Navigate to your repository
162
+cd your-repo
163
+
164
+# 3. Initialize
165
+och init --url https://git.yourcompany.com
166
+```
167
+
168
+### CLI Commands Reference
169
+
170
+**Authentication:**
171
+```bash
172
+och auth login # Interactive login
173
+och auth login --token TOKEN # Login with token
174
+och auth logout # Logout
175
+och auth whoami # Show current user
176
+```
177
+
178
+**Stack Management:**
179
+```bash
180
+och stack create \u003cname\u003e # Create new stacked branch
181
+och stack view # View current stack (alias: ls)
182
+och stack submit # Push and create PRs
183
+och stack submit --draft # Create as draft PRs
184
+och stack sync # Rebase stack on latest
185
+och stack reorder # Reorder branches in stack
186
+```
187
+
188
+**Repository:**
189
+```bash
190
+och init # Initialize repo for OpenCodeHub
191
+och sync # Bidirectional sync with remote
192
+och status # Show stack status (alias: st)
193
+```
194
+
195
+---
196
+
197
+## πŸš€ Advanced Features
198
+
199
+### πŸ“š Stacked Pull Requests
200
+
201
+**Ship faster by breaking large features into reviewable chunks.**
202
+
203
+#### Why Stacked PRs?
204
+
205
+- **Faster Reviews**: Smaller, focused PRs get reviewed quicker
206
+- **Parallel Development**: Build on unmerged PRs while waiting for review
207
+- **Clean History**: Each PR represents one logical change
208
+- **Risk Reduction**: Deploy incrementally, rollback easily
209
+
210
+#### How It Works
211
+
212
+```
213
+main
214
+ └── PR #123: Add database schema
215
+ └── PR #124: Add authentication service
216
+ └── PR #125: Add login UI
217
+```
218
+
219
+Each PR builds on the previous one. When #123 merges, #124 automatically rebases.
220
+
221
+#### Creating a Stack
222
+
223
+**Using CLI:**
224
+```bash
225
+# 1. Create base branch
226
+git checkout -b feature/auth
227
+git commit -m "Add user table schema"
228
+och stack create auth-schema
229
+
230
+# 2. Create stacked branch
231
+och stack create auth-service
232
+git commit -m "Add authentication service"
233
+
234
+# 3. Create final layer
235
+och stack create auth-ui
236
+git commit -m "Add login UI"
237
+
238
+# 4. Submit entire stack
239
+och stack submit
240
+```
241
+
242
+**Using Web UI:**
243
+1. Create first PR normally
244
+2. When creating second PR, select "Stack on PR #123"
245
+3. System tracks dependencies automatically
246
+
247
+#### Stack Features
248
+
249
+βœ… **Auto-Rebasing**: When base PR merges, dependent PRs rebase automatically
250
+βœ… **Dependency Visualization**: See entire stack in PR view
251
+βœ… **Smart Merge Queue**: Merges in correct order
252
+βœ… **Conflict Detection**: Alerts on rebase conflicts
253
+
254
+---
255
+
256
+### πŸ€– AI Code Review
257
+
258
+**Get instant feedback from AI reviewers powered by GPT-4 or Claude.**
259
+
260
+#### Setup
261
+
262
+**1. Configure AI Provider:**
263
+```env
264
+# In .env
265
+AI_PROVIDER=openai # or anthropic
266
+OPENAI_API_KEY=sk-...
267
+# or
268
+ANTHROPIC_API_KEY=sk-ant-...
269
+```
270
+
271
+**2. Enable for Repository:**
272
+Navigate to **Repository β†’ Settings β†’ AI Review**
273
+- βœ… Enable AI Review
274
+- Set review triggers (on PR open, on push, manual)
275
+- Configure review rules
276
+
277
+#### What AI Reviews Catch
278
+
279
+πŸ” **Security Vulnerabilities**
280
+- SQL injection risks
281
+- XSS vulnerabilities
282
+- Authentication bypasses
283
+- Secrets in code
284
+
285
+⚑ **Performance Issues**
286
+- N+1 queries
287
+- Inefficient algorithms
288
+- Memory leaks
289
+- Blocking operations
290
+
291
+πŸ“ **Code Quality**
292
+- Best practice violations
293
+- Code smells
294
+- Incomplete error handling
295
+- Missing documentation
296
+
297
+πŸ› **Potential Bugs**
298
+- Null pointer exceptions
299
+- Race conditions
300
+- Off-by-one errors
301
+- Logic flaws
302
+
303
+#### Using AI Review
304
+
305
+**Automatic (Recommended):**
306
+AI review runs automatically on every PR based on configured triggers.
307
+
308
+**Manual:**
309
+```bash
310
+# Via CLI
311
+och review ai \u003cPR_NUMBER\u003e
312
+
313
+# Via web UI: Click "Request AI Review" button on PR
314
+```
315
+
316
+#### Review Output
317
+
318
+AI provides:
319
+- **Severity Score**: Critical / High / Medium / Low / Info
320
+- **Specific Line Comments**: Inline code suggestions
321
+- **Summary Report**: Overall code quality assessment
322
+- **Fix Suggestions**: Actionable improvements
323
+
324
+**Example:**
325
+```
326
+πŸ”΄ CRITICAL: SQL Injection Vulnerability
327
+File: src/api/users.ts:45
328
+Issue: User input directly concatenated into SQL query
329
+Fix: Use parameterized queries or ORM
330
+
331
+🟑 MEDIUM: Performance Concern
332
+File: src/lib/data.ts:128
333
+Issue: N+1 query in loop (1000+ iterations)
334
+Fix: Use batch loading or eager loading
335
+```
336
+
337
+---
338
+
339
+### πŸ”€ Smart Merge Queue
340
+
341
+**Stack-aware merge queue with automatic conflict resolution and CI optimization.**
342
+
343
+#### Features
344
+
345
+**Stack-Aware Merging:**
346
+- Detects PR dependencies
347
+- Merges in correct order automatically
348
+- Handles entire stacks atomically
349
+
350
+**CI Optimization:**
351
+- Batches compatible PRs
352
+- Runs CI once for multiple PRs
353
+- Reduces CI cost by ~60%
354
+
355
+**Automatic Rebasing:**
356
+- Detects conflicts early
357
+- Auto-rebases on base changes
358
+- Alerts author on conflicts
359
+
360
+**Smart Ordering:**
361
+- Priority-based queue
362
+- Dependency resolution
363
+- Hotfix fast-tracking
364
+
365
+#### Using Merge Queue
366
+
367
+**Add to Queue:**
368
+```bash
369
+# Via CLI
370
+och queue add \u003cPR_NUMBER\u003e
371
+
372
+# Via Web UI
373
+Click "Add to Merge Queue" button on approved PR
374
+```
375
+
376
+**Queue Status:**
377
+```bash
378
+# View queue
379
+och queue list
380
+
381
+# Output:
382
+Position PR Title Status
383
+1 #125 Add login UI ⏳ Running CI
384
+2 #124 Auth service ⏸️ Waiting (depends on #123)
385
+3 #123 Database schema βœ… Ready to merge
386
+```
387
+
388
+**How It Works:**
389
+
390
+1. **PR Added**: Queue analyzes dependencies
391
+2. **Position Assigned**: Ordered by priority and dependencies
392
+3. **CI Running**: Runs tests in parallel where possible
393
+4. **Auto-Merge**: Merges when ready, rebases dependents
394
+5. **Notification**: Author notified on completion
395
+
396
+#### Queue Configuration
397
+
398
+**Repository β†’ Settings β†’ Merge Queue:**
399
+- **Require CI**: Only merge if all checks pass
400
+- **Batch Window**: Wait time to batch PRs (default: 5min)
401
+- **Max Batch Size**: Max PRs to merge together (default: 3)
402
+- **Auto-Rebase**: Rebase on conflicts vs. remove from queue
403
+
404
+---
405
+
406
+### πŸ“Š Developer Metrics
407
+
408
+**Track team velocity, review efficiency, and identify bottlenecks.**
409
+
410
+#### Available Metrics
411
+
412
+**PR Velocity:**
413
+- PRs merged per week
414
+- Average time to merge
415
+- Stack success rate
416
+- Review cycle time
417
+
418
+**Review Efficiency:**
419
+- Average review time
420
+- Reviews per day
421
+- Review quality score
422
+- Reviewer responsiveness
423
+
424
+**Team Performance:**
425
+- Lines changed per PR
426
+- PR size distribution
427
+- Revert rate
428
+- CI success rate
429
+
430
+**Stack Metrics:**
431
+- % PRs that use stacks
432
+- Average stack depth
433
+- Stack merge success rate
434
+- Time saved vs. monolithic PRs
435
+
436
+#### Viewing Metrics
437
+
438
+**Personal Dashboard:**
439
+```
440
+/metrics/me
441
+```
442
+
443
+**Team Dashboard (Admin):**
444
+```
445
+/admin/metrics
446
+```
447
+
448
+**Via CLI:**
449
+```bash
450
+och metrics show
451
+och metrics show --user @username
452
+och metrics show --repo owner/repo
453
+```
454
+
455
+#### Example Metrics View
456
+
457
+```
458
+πŸ“Š Your Metrics (Last 30 Days)
459
+
460
+PR Velocity
461
+β”œβ”€ PRs Merged ━━━━━━━━━━━━━━ 24 (+12%)
462
+β”œβ”€ Avg Time to Merge ━━━━━━━━━━━━━━ 8.2 hours (-3h)
463
+└─ Stack Usage ━━━━━━━━━━━━━━ 65% (+15%)
464
+
465
+Review Efficiency
466
+β”œβ”€ Reviews Given ━━━━━━━━━━━━━━ 18
467
+β”œβ”€ Avg Review Time ━━━━━━━━━━━━━━ 22 min
468
+└─ Approval Rate ━━━━━━━━━━━━━━ 94%
469
+```
470
+
471
+---
472
+
473
+### πŸ’¬ Slack Notifications
474
+
475
+**Get actionable PR notifications directly in Slack.**
476
+
477
+#### Setup
478
+
479
+**1. Create Slack App:**
480
+1. Go to https://api.slack.com/apps
481
+2. Create New App β†’ From Scratch
482
+3. Add OAuth Scopes:
483
+ - `chat:write`
484
+ - `chat:write.public`
485
+ - `users:read`
486
+4. Install to workspace
487
+
488
+**2. Connect to OpenCodeHub:**
489
+Navigate to **Admin β†’ Integrations β†’ Slack**
490
+```
491
+Workspace Name: Your Team
492
+Bot Token: xoxb-...
493
+Signing Secret: ...
494
+```
495
+
496
+**3. Map Channels:**
497
+**Repository β†’ Settings β†’ Slack**
498
+```
499
+#engineering β†’ All PR events
500
+#deployments β†’ Merges only
501
+#security β†’ Security findings
502
+```
503
+
504
+**4. Map Users (Optional):**
505
+Users can link their Slack account:
506
+**Settings β†’ Integrations β†’ Connect Slack**
507
+
508
+#### Notification Types
509
+
510
+**PR Events:**
511
+- πŸŽ‰ PR opened
512
+- βœ… PR approved
513
+- πŸ”€ PR merged
514
+- ❌ PR closed
515
+- πŸ’¬ Review requested
516
+- πŸ”΄ Changes requested
517
+
518
+**CI/CD Events:**
519
+- βœ… Build passed
520
+- ❌ Build failed
521
+- πŸš€ Deployment started
522
+- βœ… Deployment successful
523
+
524
+**Code Review:**
525
+- πŸ€– AI review completed
526
+- πŸ” Security issue found
527
+- ⚠️ Performance concern
528
+
529
+**Merge Queue:**
530
+- ⏭️ Added to queue
531
+- βœ… Merged from queue
532
+- ⚠️ Removed (conflict)
533
+
534
+#### Interactive Actions
535
+
536
+Notifications include action buttons:
537
+```
538
+[swadhin] opened PR#125: Add login UI
539
+Stack: #123 β†’ #124 β†’ #125 β€’ files β†’32 β€’ LoC +420/-18
540
+
541
+[Approve] [Request Changes] [View PR] [Add to Queue]
542
+```
543
+
544
+Click buttons to:
545
+- Approve/reject directly from Slack
546
+- Request changes with comment
547
+- Add to merge queue
548
+- View full PR
549
+
550
+---
551
+
552
+## πŸ’‘ Workflow Examples
553
+
554
+### Example 1: Complete Stacked PR Flow
555
+
556
+```bash
557
+# 1. Start new feature
558
+git checkout main
559
+git pull
560
+git checkout -b feature/user-profiles
561
+
562
+# 2. First PR: Database layer
563
+git add migrations/
564
+git commit -m "Add user profile tables"
565
+och stack create profile-db
566
+och stack submit
567
+
568
+# β†’ Creates PR #301
569
+
570
+# 3. Second PR: Backend API
571
+git add src/api/profiles/
572
+git commit -m "Add profile API"
573
+och stack create profile-api
574
+och stack submit
575
+
576
+# β†’ Creates PR #302 (stacked on #301)
577
+
578
+# 4. Third PR: Frontend
579
+git add src/pages/profile/
580
+git commit -m "Add profile UI"
581
+och stack create profile-ui
582
+och stack submit
583
+
584
+# β†’ Creates PR #303 (stacked on #302)
585
+
586
+# 5. View stack
587
+och stack view
588
+```
589
+
590
+**Output:**
591
+```
592
+πŸ“š Current Stack
593
+
594
+main (base)
595
+ └─ #301: profile-db βœ… Approved
596
+ └─ #302: profile-api πŸ” In Review
597
+ └─ #303: profile-ui ⏳ AI Review Running
598
+```
599
+
600
+### Example 2: Using AI Review + Merge Queue
601
+
602
+```bash
603
+# 1. Create PR
604
+git push origin feature-branch
605
+
606
+# 2. Request AI review
607
+och review ai 304
608
+
609
+# 3. Fix AI suggestions
610
+# ... make changes based on review ...
611
+git commit -m "Fix security issues from AI review"
612
+git push
613
+
614
+# 4. Get human review
615
+# Team reviews via web UI
616
+
617
+# 5. Add to merge queue when approved
618
+och queue add 304
619
+
620
+# Queue handles:
621
+# - Waiting for CI
622
+# - Merging when ready
623
+# - Notifying on Slack
624
+```
625
+
626
+### Example 3: Team Collaboration
627
+
628
+```bash
629
+# Developer A: Creates base
630
+git checkout -b api/payments
631
+# ... implements payment models ...
632
+git commit -m "Add payment models"
633
+git push
634
+
635
+# Developer B: Builds on A's work (before merge!)
636
+git checkout api/payments
637
+git checkout -b api/payment-processing
638
+# ... implements payment processing ...
639
+git commit -m "Add payment processing"
640
+git push
641
+# Create PR stacked on A's PR
642
+
643
+# Developer C: Builds on B's work
644
+git checkout api/payment-processing
645
+git checkout -b ui/payment-form
646
+# ... implements payment UI ...
647
+```
648
+
649
+**Result**: 3 parallel PRs, all reviewed separately, merged in order.
650
+
651
+---
652
+
653
+## πŸ“– Additional Resources
654
+
655
+- **Full CLI Reference**: `cli/README.md`
656
+- **API Documentation**: `/api/docs`
657
+- **Stacked PR Guide**: `/docs#stacked-prs`
658
+- **Slack Integration Guide**: `/docs#slack`
659
+
660
+---
661
+
132
662
133
663
## βš™οΈ Configuration
134
664
675
1205
676
1206
```bash
677
1207
# Fork and clone
678
-git clone https://github.com/YOUR-USERNAME/OpenCodeHub.git
679
-cd OpenCodeHub
1208
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
1209
+cd OpencodeHub
680
1210
681
1211
# Install dependencies
682
1212
bun install
723
1253
<p align="center">
724
1254
<strong>Made with ❀️ for the open source community</strong>
725
1255
<br>
726
- <a href="https://github.com/yourusername/OpenCodeHub/issues">Report Bug</a> β€’
727
- <a href="https://github.com/yourusername/OpenCodeHub/issues">Request Feature</a>
1256
+ <a href="https://github.com/swadhinbiswas/OpencodeHub/issues">Report Bug</a> β€’
1257
+ <a href="https://github.com/swadhinbiswas/OpencodeHub/issues">Request Feature</a>
728
1258
</p>
729
1259
β–ΌREADME.md.backup
+728-0
1
+# OpenCodeHub
2
+
3
+<p align="center">
4
+ <img src="public/logo.png" alt="OpenCodeHub Logo" width="200">
5
+</p>
6
+
7
+<p align="center">
8
+ <strong>A powerful, self-hosted Git platform with CI/CD, code review, and enterprise-grade security</strong>
9
+</p>
10
+
11
+<p align="center">
12
+ <a href="#-features">Features</a> β€’
13
+ <a href="#-quick-start">Quick Start</a> β€’
14
+ <a href="#%EF%B8%8F-configuration">Configuration</a> β€’
15
+ <a href="#-security">Security</a> β€’
16
+ <a href="#-production-deployment">Production</a> β€’
17
+ <a href="#-api">API</a> β€’
18
+ <a href="#-contributing">Contributing</a>
19
+</p>
20
+
21
+---
22
+
23
+## πŸŽ‰ Production Ready!
24
+
25
+**All critical security vulnerabilities are now fixed. OpenCodeHub is production-ready!** 🎊
26
+
27
+βœ… **Rate Limiting** - Prevents brute force attacks
28
+βœ… **CSRF Protection** - Cross-site request forgery defense
29
+βœ… **Input Validation** - SQL injection & XSS prevention
30
+βœ… **Hook Authentication** - Secure git operations
31
+βœ… **Environment Validation** - Safe configuration management
32
+
33
+πŸ“š **See [DEPLOYMENT.md](DEPLOYMENT.md) for complete deployment guide** with SSL setup, Nginx configuration, monitoring, database backups, and production best practices.
34
+
35
+---
36
+
37
+## πŸš€ Features
38
+
39
+### Git Hosting & Collaboration
40
+- **Full Git Support** - SSH and HTTP/HTTPS protocols with authentication
41
+- **Repository Management** - Create, fork, archive, and transfer repositories
42
+- **Branch Protection** - Enforce PR requirements, approvals, and code review policies
43
+- **Pull Requests** - Code review with inline comments and approval workflows
44
+- **Issues & Milestones** - Bug tracking with labels, assignees, and custom fields
45
+- **Wiki** - Built-in documentation with markdown support
46
+- **Organizations** - Team management with role-based access control (RBAC)
47
+
48
+### CI/CD & Automation
49
+- **GitHub Actions Compatible** - Run workflows defined in `.github/workflows/`
50
+- **Self-hosted Runners** - Docker-based job execution with artifact support
51
+- **Merge Queue** - Automated PR merge management with conflict detection
52
+- **Webhooks** - Real-time event notifications to external services
53
+- **Secrets Management** - Encrypted storage for CI/CD credentials
54
+
55
+### Security Features βœ…
56
+- **Rate Limiting** - Prevents brute force attacks (5 login attempts per 15min)
57
+- **CSRF Protection** - Double-submit cookie pattern for all state-changing operations
58
+- **Input Validation** - Zod schemas prevent injection attacks
59
+- **Hook Authentication** - Shared secrets protect internal git hooks
60
+- **Session Management** - Automatic expiration and rotation
61
+- **Audit Logging** - Complete activity tracking for compliance
62
+
63
+### Modern Tech Stack
64
+- **Astro + React** - Fast, modern frontend with island architecture
65
+- **TailwindCSS + shadcn/ui** - Beautiful, accessible UI components
66
+- **Universal Database Adapter** - PostgreSQL, MySQL, SQLite, MongoDB, Turso, PlanetScale
67
+- **Flexible Storage** - Local, S3, MinIO, Google Drive, Azure Blob, R2
68
+- **Production-Ready** - Rate limiting, CSRF, input validation, error monitoring
69
+
70
+---
71
+
72
+## πŸ“¦ Quick Start
73
+
74
+### Prerequisites
75
+- **Node.js** 18+ or **Bun** 1.0+
76
+- **Database**: PostgreSQL 14+ (recommended) / MySQL 8+ / SQLite 3.35+
77
+- **Git** 2.30+
78
+- **Docker** (optional, for CI/CD runners)
79
+
80
+### Installation (5 minutes)
81
+
82
+```bash
83
+# 1. Clone the repository
84
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
85
+cd OpenCodeHub
86
+
87
+# 2. Install dependencies
88
+npm install
89
+# or
90
+bun install
91
+
92
+# 3. Set up environment
93
+cp .env.example .env
94
+
95
+# 4. Configure your .env file (see Configuration section)
96
+nano .env # Set at minimum: JWT_SECRET, SESSION_SECRET, INTERNAL_HOOK_SECRET, SITE_URL
97
+
98
+# 5. Run database migrations
99
+npm run db:push
100
+# or generate migrations for version control:
101
+npm run db:generate
102
+
103
+# 6. Seed an admin user
104
+bun run scripts/seed-admin.ts
105
+# Enter username, email, password when prompted
106
+
107
+# 7. Start development server
108
+npm run dev
109
+# Application available at http://localhost:3000
110
+```
111
+
112
+### Using Docker (Recommended for Production)
113
+
114
+```bash
115
+# 1. Clone and configure
116
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
117
+cd OpenCodeHub
118
+cp .env.example .env
119
+
120
+# 2. Edit .env with production values (see Production Deployment section)
121
+
122
+# 3. Start services
123
+docker-compose up -d
124
+
125
+# 4. Create admin user
126
+docker-compose exec app bun run scripts/seed-admin.ts
127
+
128
+# Access at http://localhost:3000
129
+```
130
+
131
+---
132
+
133
+## βš™οΈ Configuration
134
+
135
+### Critical Environment Variables
136
+
137
+These **MUST** be set before deploying to production:
138
+
139
+```env
140
+# Security (CRITICAL - Generate strong secrets!)
141
+JWT_SECRET=<run: openssl rand -hex 32>
142
+SESSION_SECRET=<run: openssl rand -hex 32>
143
+INTERNAL_HOOK_SECRET=<run: openssl rand -hex 32>
144
+
145
+# Application URLs (CRITICAL)
146
+SITE_URL=https://your-domain.com # Must be HTTPS in production
147
+PORT=3000
148
+HOST=0.0.0.0
149
+```
150
+
151
+### Database Configuration
152
+
153
+**PostgreSQL (Recommended for Production):**
154
+```env
155
+DATABASE_DRIVER=postgres
156
+DATABASE_URL=postgresql://user:password@localhost:5432/opencodehub
157
+```
158
+
159
+**MySQL:**
160
+```env
161
+DATABASE_DRIVER=mysql
162
+DATABASE_URL=mysql://user:password@localhost:3306/opencodehub
163
+```
164
+
165
+**SQLite (Development Only):**
166
+```env
167
+DATABASE_DRIVER=sqlite
168
+DATABASE_URL=./data/opencodehub.db
169
+```
170
+
171
+### Storage Configuration
172
+
173
+Storage can be configured via Admin Panel (`/admin/storage`) or environment variables:
174
+
175
+**Local Storage:**
176
+```env
177
+STORAGE_TYPE=local
178
+STORAGE_PATH=./data/storage
179
+```
180
+
181
+**S3/MinIO/R2:**
182
+```env
183
+STORAGE_TYPE=s3
184
+STORAGE_BUCKET=opencodehub
185
+STORAGE_REGION=us-east-1
186
+STORAGE_ENDPOINT=https://s3.amazonaws.com
187
+S3_ACCESS_KEY=your-access-key
188
+S3_SECRET_KEY=your-secret-key
189
+```
190
+
191
+**Google Drive (via Admin Panel):**
192
+- Navigate to `/admin/storage`
193
+- Select "Google Drive"
194
+- Enter Client ID, Secret, and Refresh Token
195
+- Test connection before saving
196
+
197
+### Security Configuration
198
+
199
+**Rate Limiting:**
200
+```env
201
+RATE_LIMIT_AUTH=5 # Login attempts per 15 minutes
202
+RATE_LIMIT_API=100 # API requests per minute
203
+RATE_LIMIT_GIT=200 # Git operations per minute
204
+RATE_LIMIT_SKIP_DEV=false # Never skip in production
205
+```
206
+
207
+**CSRF Protection:**
208
+```env
209
+CSRF_SKIP_DEV=false # Never skip in production
210
+```
211
+
212
+### Optional Services
213
+
214
+**Email (SMTP):**
215
+```env
216
+SMTP_HOST=smtp.example.com
217
+SMTP_PORT=587
218
219
+SMTP_PASSWORD=your-smtp-password
220
221
+```
222
+
223
+**Redis (Caching & Sessions):**
224
+```env
225
+REDIS_URL=redis://localhost:6379
226
+```
227
+
228
+**Error Monitoring (Sentry):**
229
+```env
230
+SENTRY_DSN=https://...
231
+ENABLE_TRACING=true
232
+```
233
+
234
+---
235
+
236
+## πŸ”’ Security
237
+
238
+OpenCodeHub implements **production-grade security** out of the box:
239
+
240
+### Implemented Security Features
241
+
242
+βœ… **Authentication & Authorization**
243
+- JWT-based sessions with automatic expiration
244
+- Bcrypt password hashing (10 rounds)
245
+- 2FA/TOTP support
246
+- Role-based access control (RBAC)
247
+
248
+βœ… **Attack Prevention**
249
+- **Rate Limiting**: Prevents brute force (5 login attempts per 15min)
250
+- **CSRF Protection**: Double-submit cookie pattern on all state-changing operations
251
+- **Input Validation**: Zod schemas prevent SQL injection and XSS
252
+- **XSS Sanitization**: Markdown rendered with `rehype-sanitize`
253
+
254
+βœ… **Operational Security**
255
+- **Audit Logging**: All admin actions logged to database
256
+- **Hook Authentication**: Git hooks protected with shared secrets
257
+- **Session Management**: Automatic cleanup and rotation
258
+- **Environment Validation**: Startup checks for missing/weak secrets
259
+
260
+### Security Best Practices
261
+
262
+1. **Always use HTTPS** in production (Let's Encrypt recommended)
263
+2. **Rotate secrets** regularly (especially after team changes)
264
+3. **Enable 2FA** for all admin accounts
265
+4. **Monitor audit logs** for suspicious activity
266
+5. **Keep dependencies updated**: Run `npm audit` weekly
267
+
268
+### Reporting Security Issues
269
+
270
+Please report security vulnerabilities to **[email protected]**. Do not create public issues.
271
+
272
+---
273
+
274
+## πŸš€ Production Deployment
275
+
276
+### Pre-Deployment Checklist
277
+
278
+Before going live, ensure:
279
+
280
+- [ ] **Secrets**: All secrets generated with `openssl rand -hex 32`
281
+- [ ] **HTTPS**: SSL certificate configured (Let's Encrypt recommended)
282
+- [ ] **Database**: PostgreSQL with backups enabled
283
+- [ ] **Storage**: S3/R2 configured (not local filesystem)
284
+- [ ] **Monitoring**: Error tracking (Sentry) and APM configured
285
+- [ ] **Rate Limits**: Configured for expected traffic
286
+- [ ] **Admin Account**: Created via `bun run scripts/seed-admin.ts`
287
+- [ ] **Environment Validation**: Test with `bun run src/lib/env-validation.ts`
288
+- [ ] **Database Migrations**: Generated with `npm run db:generate`
289
+
290
+### Production Environment Variables
291
+
292
+```env
293
+# Application
294
+NODE_ENV=production
295
+SITE_URL=https://git.yourcompany.com
296
+PORT=3000
297
+
298
+# Security (CRITICAL - Generate with: openssl rand -hex 32)
299
+JWT_SECRET=<64-char-random-hex>
300
+SESSION_SECRET=<64-char-random-hex>
301
+INTERNAL_HOOK_SECRET=<64-char-random-hex>
302
+
303
+# Database (Use connection pooling)
304
+DATABASE_DRIVER=postgres
305
+DATABASE_URL=postgresql://user:password@db-host:5432/opencodehub?pool_timeout=30
306
+
307
+# Storage (Use cloud storage)
308
+STORAGE_TYPE=s3
309
+STORAGE_BUCKET=opencodehub-prod
310
+STORAGE_REGION=us-east-1
311
+
312
+# Rate Limiting (Adjust based on traffic)
313
+RATE_LIMIT_AUTH=5
314
+RATE_LIMIT_API=200
315
+RATE_LIMIT_GIT=500
316
+
317
+# Monitoring
318
+SENTRY_DSN=https://your-sentry-dsn
319
+LOG_LEVEL=info
320
+
321
+# Features
322
+ENABLE_REGISTRATION=true # Set false for invite-only
323
+```
324
+
325
+### Docker Production Deployment
326
+
327
+```yaml
328
+# docker-compose.prod.yml
329
+version: '3.8'
330
+
331
+services:
332
+ app:
333
+ image: opencodehub:latest
334
+ restart: always
335
+ ports:
336
+ - "3000:3000"
337
+ env_file: .env.production
338
+ depends_on:
339
+ - postgres
340
+ - redis
341
+ volumes:
342
+ - ./data:/app/data
343
+ healthcheck:
344
+ test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
345
+ interval: 30s
346
+ timeout: 10s
347
+ retries: 3
348
+
349
+ postgres:
350
+ image: postgres:15-alpine
351
+ restart: always
352
+ environment:
353
+ POSTGRES_DB: opencodehub
354
+ POSTGRES_USER: opencodehub
355
+ POSTGRES_PASSWORD: <strong-password>
356
+ volumes:
357
+ - postgres_data:/var/lib/postgresql/data
358
+
359
+ redis:
360
+ image: redis:7-alpine
361
+ restart: always
362
+
363
+volumes:
364
+ postgres_data:
365
+```
366
+
367
+```bash
368
+# Deploy
369
+docker-compose -f docker-compose.prod.yml up -d
370
+
371
+# Monitor logs
372
+docker-compose -f docker-compose.prod.yml logs -f app
373
+
374
+# Health check
375
+curl https://git.yourcompany.com/api/health
376
+```
377
+
378
+### Reverse Proxy (Nginx)
379
+
380
+```nginx
381
+server {
382
+ listen 443 ssl http2;
383
+ server_name git.yourcompany.com;
384
+
385
+ ssl_certificate /path/to/cert.pem;
386
+ ssl_certificate_key /path/to/key.pem;
387
+
388
+ location / {
389
+ proxy_pass http://localhost:3000;
390
+ proxy_set_header Host $host;
391
+ proxy_set_header X-Real-IP $remote_addr;
392
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
393
+ proxy_set_header X-Forwarded-Proto $scheme;
394
+ }
395
+
396
+ # Git operations (larger timeouts)
397
+ location ~ ^/[^/]+/[^/]+\.git/ {
398
+ proxy_pass http://localhost:3000;
399
+ proxy_read_timeout 300s;
400
+ proxy_send_timeout 300s;
401
+ proxy_buffer_size 128k;
402
+ proxy_buffers 4 256k;
403
+ }
404
+}
405
+```
406
+
407
+### Database Backups
408
+
409
+```bash
410
+# PostgreSQL backup script (cron daily)
411
+#!/bin/bash
412
+pg_dump -U opencodehub opencodehub | gzip > /backups/opencodehub-$(date +\%Y\%m\%d).sql.gz
413
+
414
+# Retain last 30 days
415
+find /backups -name "opencodehub-*.sql.gz" -mtime +30 -delete
416
+```
417
+
418
+---
419
+
420
+## πŸ“š API Documentation
421
+
422
+### Authentication
423
+
424
+All authenticated endpoints require a session cookie or `Authorization: Bearer <token>` header.
425
+
426
+**Register:**
427
+```bash
428
+POST /api/auth/register
429
+Content-Type: application/json
430
+
431
+{
432
+ "username": "johndoe",
433
+ "email": "[email protected]",
434
+ "password": "SecureP@ssw0rd",
435
+ "displayName": "John Doe"
436
+}
437
+
438
+# Response: 201 Created
439
+{
440
+ "user": { "id": "...", "username": "johndoe", ... },
441
+ "token": "eyJ...",
442
+ "expiresAt": "2026-01-08T00:00:00Z"
443
+}
444
+```
445
+
446
+**Login:**
447
+```bash
448
+POST /api/auth/login
449
+Content-Type: application/json
450
+
451
+{
452
+ "login": "[email protected]", # username or email
453
+ "password": "SecureP@ssw0rd"
454
+}
455
+
456
+# With 2FA:
457
+{
458
+ "login": "[email protected]",
459
+ "password": "SecureP@ssw0rd",
460
+ "totpCode": "123456"
461
+}
462
+```
463
+
464
+### Repositories
465
+
466
+**Create Repository:**
467
+```bash
468
+POST /api/repos
469
+Authorization: Bearer <token>
470
+Content-Type: application/json
471
+
472
+{
473
+ "name": "my-project",
474
+ "description": "My awesome project",
475
+ "visibility": "public" # public, private, internal
476
+}
477
+```
478
+
479
+**Branch Protection:**
480
+```bash
481
+POST /api/repos/{repoId}/branch-protection
482
+Authorization: Bearer <token>
483
+Content-Type: application/json
484
+
485
+{
486
+ "pattern": "main",
487
+ "requiresPr": true,
488
+ "requiredApprovals": 2,
489
+ "dismissStaleReviews": true
490
+}
491
+```
492
+
493
+### Admin Endpoints
494
+
495
+**List Users (Admin Only):**
496
+```bash
497
+GET /admin/users?q=search&page=1
498
+Cookie: och_session=...
499
+```
500
+
501
+**Update Storage Config (Admin Only):**
502
+```bash
503
+POST /api/admin/config/storage
504
+Cookie: och_session=...
505
+Content-Type: application/json
506
+
507
+{
508
+ "type": "s3",
509
+ "bucket": "my-bucket",
510
+ "region": "us-east-1",
511
+ ...
512
+}
513
+```
514
+
515
+### Rate Limits
516
+
517
+All endpoints return rate limit headers:
518
+```
519
+X-RateLimit-Limit: 100
520
+X-RateLimit-Remaining: 95
521
+X-RateLimit-Reset: 2026-01-01T15:30:00Z
522
+Retry-After: 30 (when rate limited)
523
+```
524
+
525
+**Full API documentation:** `/api/docs` (OpenAPI spec coming soon)
526
+
527
+---
528
+
529
+## πŸ”§ Usage Guide
530
+
531
+### Creating Your First Repository
532
+
533
+1. **Log in** to your account
534
+2. Click the **+** button β†’ **New Repository**
535
+3. Fill in details:
536
+ - **Name**: alphanumeric, hyphens, underscores
537
+ - **Description**: Optional
538
+ - **Visibility**: public/private
539
+4. Click **Create Repository**
540
+
541
+### Cl
542
+
543
+oning Repositories
544
+
545
+**SSH (Recommended):**
546
+```bash
547
+# Add your SSH key in Settings β†’ SSH Keys
548
+git clone [email protected]:username/repo.git
549
+```
550
+
551
+**HTTPS:**
552
+```bash
553
+git clone https://your-domain.com/username/repo.git
554
+# Username: your-username
555
+# Password: your-password or personal access token
556
+```
557
+
558
+### Setting Up Branch Protection
559
+
560
+1. Navigate to **Repository β†’ Settings β†’ Branches**
561
+2. Click **Add Rule**
562
+3. Configure:
563
+ - **Pattern**: `main` or `release/*`
564
+ - **Require PR**: βœ…
565
+ - **Required Approvals**: 2
566
+ - **Dismiss Stale Reviews**: βœ…
567
+4. **Save Rule**
568
+
569
+### CI/CD Workflows
570
+
571
+Create `.github/workflows/ci.yml`:
572
+
573
+```yaml
574
+name: CI
575
+
576
+on:
577
+ push:
578
+ branches: [main, develop]
579
+ pull_request:
580
+ branches: [main]
581
+
582
+jobs:
583
+ test:
584
+ runs-on: ubuntu-latest
585
+ steps:
586
+ - uses: actions/checkout@v4
587
+
588
+ - name: Setup Node.js
589
+ uses: actions/setup-node@v4
590
+ with:
591
+ node-version: '20'
592
+
593
+ - name: Install
594
+ run: npm ci
595
+
596
+ - name: Test
597
+ run: npm test
598
+
599
+ - name: Build
600
+ run: npm run build
601
+
602
+ - name: Upload Artifacts
603
+ uses: actions/upload-artifact@v3
604
+ with:
605
+ name: dist
606
+ path: dist/
607
+```
608
+
609
+---
610
+
611
+## πŸ› Troubleshooting
612
+
613
+### Common Issues
614
+
615
+**"Rate limit exceeded" on login:**
616
+- Default: 5 attempts per 15 minutes
617
+- Solution: Wait 15 minutes or adjust `RATE_LIMIT_AUTH` in `.env`
618
+
619
+**"CSRF token validation failed":**
620
+- Ensure cookies are enabled
621
+- Check `SITE_URL` matches your actual domain
622
+- Verify HTTPS in production
623
+
624
+**"Unauthorized" on git hooks:**
625
+- Check `INTERNAL_HOOK_SECRET` is set
626
+- Verify hooks have correct secret in curl headers
627
+- Re-run `git init` on repositories to reinstall hooks
628
+
629
+**Database connection errors:**
630
+- Verify `DATABASE_URL` format
631
+- Check database server is running
632
+- Ensure user has correct permissions
633
+
634
+**Storage upload fails:**
635
+- Test connection in `/admin/storage`
636
+- Verify S3 credentials and bucket permissions
637
+- Check firewall rules for external storage
638
+
639
+### Debug Mode
640
+
641
+```env
642
+# Enable detailed logging
643
+LOG_LEVEL=debug
644
+NODE_ENV=development
645
+
646
+# View logs
647
+tail -f data/logs/opencodehub.log
648
+```
649
+
650
+### Health Check
651
+
652
+```bash
653
+curl http://localhost:3000/api/health
654
+
655
+# Response:
656
+{
657
+ "status": "ok",
658
+ "checks": {
659
+ "database": "ok",
660
+ "storage": "ok",
661
+ "redis": "ok" // if configured
662
+ },
663
+ "uptime": 123456,
664
+ "version": "1.0.0"
665
+}
666
+```
667
+
668
+---
669
+
670
+## 🀝 Contributing
671
+
672
+We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
673
+
674
+### Development Setup
675
+
676
+```bash
677
+# Fork and clone
678
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
679
+cd OpencodeHub
680
+
681
+# Install dependencies
682
+bun install
683
+
684
+# Run in development
685
+bun run dev
686
+
687
+# Run tests
688
+bun test
689
+
690
+# Lint
691
+bun run lint
692
+```
693
+
694
+### Submitting Changes
695
+
696
+1. Create a feature branch (`git checkout -b feature/amazing-feature`)
697
+2. Make your changes
698
+3. Add tests
699
+4. Run `bun test` and `bun run lint`
700
+5. Commit (`git commit -m 'Add amazing feature'`)
701
+6. Push (`git push origin feature/amazing-feature`)
702
+7. Open a Pull Request
703
+
704
+---
705
+
706
+## πŸ“„ License
707
+
708
+MIT License - see [LICENSE](LICENSE) for details.
709
+
710
+---
711
+
712
+## πŸ™ Acknowledgments
713
+
714
+- [Astro](https://astro.build/) - Web framework
715
+- [shadcn/ui](https://ui.shadcn.com/) - UI components
716
+- [Drizzle ORM](https://orm.drizzle.team/) - Database toolkit
717
+- [simple-git](https://github.com/steveukx/git-js) - Git operations
718
+- [Monaco Editor](https://microsoft.github.io/monaco-editor/) - Code editor
719
+- [Zod](https://zod.dev/) - Schema validation
720
+
721
+---
722
+
723
+<p align="center">
724
+ <strong>Made with ❀️ for the open source community</strong>
725
+ <br>
726
+ <a href="https://github.com/swadhinbiswas/OpencodeHub/issues">Report Bug</a> β€’
727
+ <a href="https://github.com/swadhinbiswas/OpencodeHub/issues">Request Feature</a>
728
+</p>
0
729
β–ΌREADME_NEW.md
+1258-0
1
+# OpenCodeHub
2
+
3
+<p align="center">
4
+ <img src="public/logo.png" alt="OpenCodeHub Logo" width="200">
5
+</p>
6
+
7
+<p align="center">
8
+ <strong>A powerful, self-hosted Git platform with CI/CD, code review, and enterprise-grade security</strong>
9
+</p>
10
+
11
+<p align="center">
12
+ <a href="#-features">Features</a> β€’
13
+ <a href="#-quick-start">Quick Start</a> β€’
14
+ <a href="#%EF%B8%8F-configuration">Configuration</a> β€’
15
+ <a href="#-security">Security</a> β€’
16
+ <a href="#-production-deployment">Production</a> β€’
17
+ <a href="#-api">API</a> β€’
18
+ <a href="#-contributing">Contributing</a>
19
+</p>
20
+
21
+---
22
+
23
+## πŸŽ‰ Production Ready!
24
+
25
+**All critical security vulnerabilities are now fixed. OpenCodeHub is production-ready!** 🎊
26
+
27
+βœ… **Rate Limiting** - Prevents brute force attacks
28
+βœ… **CSRF Protection** - Cross-site request forgery defense
29
+βœ… **Input Validation** - SQL injection & XSS prevention
30
+βœ… **Hook Authentication** - Secure git operations
31
+βœ… **Environment Validation** - Safe configuration management
32
+
33
+πŸ“š **See [DEPLOYMENT.md](DEPLOYMENT.md) for complete deployment guide** with SSL setup, Nginx configuration, monitoring, database backups, and production best practices.
34
+
35
+---
36
+
37
+## πŸš€ Features
38
+
39
+### Git Hosting & Collaboration
40
+- **Full Git Support** - SSH and HTTP/HTTPS protocols with authentication
41
+- **Repository Management** - Create, fork, archive, and transfer repositories
42
+- **Branch Protection** - Enforce PR requirements, approvals, and code review policies
43
+- **Pull Requests** - Code review with inline comments and approval workflows
44
+- **Issues & Milestones** - Bug tracking with labels, assignees, and custom fields
45
+- **Wiki** - Built-in documentation with markdown support
46
+- **Organizations** - Team management with role-based access control (RBAC)
47
+
48
+### CI/CD & Automation
49
+- **GitHub Actions Compatible** - Run workflows defined in `.github/workflows/`
50
+- **Self-hosted Runners** - Docker-based job execution with artifact support
51
+- **Merge Queue** - Automated PR merge management with conflict detection
52
+- **Webhooks** - Real-time event notifications to external services
53
+- **Secrets Management** - Encrypted storage for CI/CD credentials
54
+
55
+### Security Features βœ…
56
+- **Rate Limiting** - Prevents brute force attacks (5 login attempts per 15min)
57
+- **CSRF Protection** - Double-submit cookie pattern for all state-changing operations
58
+- **Input Validation** - Zod schemas prevent injection attacks
59
+- **Hook Authentication** - Shared secrets protect internal git hooks
60
+- **Session Management** - Automatic expiration and rotation
61
+- **Audit Logging** - Complete activity tracking for compliance
62
+
63
+### Modern Tech Stack
64
+- **Astro + React** - Fast, modern frontend with island architecture
65
+- **TailwindCSS + shadcn/ui** - Beautiful, accessible UI components
66
+- **Universal Database Adapter** - PostgreSQL, MySQL, SQLite, MongoDB, Turso, PlanetScale
67
+- **Flexible Storage** - Local, S3, MinIO, Google Drive, Azure Blob, R2
68
+- **Production-Ready** - Rate limiting, CSRF, input validation, error monitoring
69
+
70
+---
71
+
72
+## πŸ“¦ Quick Start
73
+
74
+### Prerequisites
75
+- **Node.js** 18+ or **Bun** 1.0+
76
+- **Database**: PostgreSQL 14+ (recommended) / MySQL 8+ / SQLite 3.35+
77
+- **Git** 2.30+
78
+- **Docker** (optional, for CI/CD runners)
79
+
80
+### Installation (5 minutes)
81
+
82
+```bash
83
+# 1. Clone the repository
84
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
85
+cd OpenCodeHub
86
+
87
+# 2. Install dependencies
88
+npm install
89
+# or
90
+bun install
91
+
92
+# 3. Set up environment
93
+cp .env.example .env
94
+
95
+# 4. Configure your .env file (see Configuration section)
96
+nano .env # Set at minimum: JWT_SECRET, SESSION_SECRET, INTERNAL_HOOK_SECRET, SITE_URL
97
+
98
+# 5. Run database migrations
99
+npm run db:push
100
+# or generate migrations for version control:
101
+npm run db:generate
102
+
103
+# 6. Seed an admin user
104
+bun run scripts/seed-admin.ts
105
+# Enter username, email, password when prompted
106
+
107
+# 7. Start development server
108
+npm run dev
109
+# Application available at http://localhost:3000
110
+```
111
+
112
+### Using Docker (Recommended for Production)
113
+
114
+```bash
115
+# 1. Clone and configure
116
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
117
+cd OpenCodeHub
118
+cp .env.example .env
119
+
120
+# 2. Edit .env with production values (see Production Deployment section)
121
+
122
+# 3. Start services
123
+docker-compose up -d
124
+
125
+# 4. Create admin user
126
+docker-compose exec app bun run scripts/seed-admin.ts
127
+
128
+# Access at http://localhost:3000
129
+```
130
+
131
+---
132
+# Advanced Features Documentation Insert
133
+
134
+**Insert this content into README.md after line 131 (after Docker installation section, before Configuration section)**
135
+
136
+---
137
+
138
+## 🎯 OCH CLI - Command Line Interface
139
+
140
+Powerful CLI for stack-first PR workflows.
141
+
142
+### Installation
143
+
144
+```bash
145
+# Install globally
146
+npm install -g opencodehub-cli
147
+
148
+# Or use with npx
149
+npx opencodehub-cli
150
+
151
+# Verify installation
152
+och --version
153
+```
154
+
155
+### Quick Setup
156
+
157
+```bash
158
+# 1. Login to your OpenCode Hub instance
159
+och auth login --url https://git.yourcompany.com
160
+
161
+# 2. Navigate to your repository
162
+cd your-repo
163
+
164
+# 3. Initialize
165
+och init --url https://git.yourcompany.com
166
+```
167
+
168
+### CLI Commands Reference
169
+
170
+**Authentication:**
171
+```bash
172
+och auth login # Interactive login
173
+och auth login --token TOKEN # Login with token
174
+och auth logout # Logout
175
+och auth whoami # Show current user
176
+```
177
+
178
+**Stack Management:**
179
+```bash
180
+och stack create \u003cname\u003e # Create new stacked branch
181
+och stack view # View current stack (alias: ls)
182
+och stack submit # Push and create PRs
183
+och stack submit --draft # Create as draft PRs
184
+och stack sync # Rebase stack on latest
185
+och stack reorder # Reorder branches in stack
186
+```
187
+
188
+**Repository:**
189
+```bash
190
+och init # Initialize repo for OpenCodeHub
191
+och sync # Bidirectional sync with remote
192
+och status # Show stack status (alias: st)
193
+```
194
+
195
+---
196
+
197
+## πŸš€ Advanced Features
198
+
199
+### πŸ“š Stacked Pull Requests
200
+
201
+**Ship faster by breaking large features into reviewable chunks.**
202
+
203
+#### Why Stacked PRs?
204
+
205
+- **Faster Reviews**: Smaller, focused PRs get reviewed quicker
206
+- **Parallel Development**: Build on unmerged PRs while waiting for review
207
+- **Clean History**: Each PR represents one logical change
208
+- **Risk Reduction**: Deploy incrementally, rollback easily
209
+
210
+#### How It Works
211
+
212
+```
213
+main
214
+ └── PR #123: Add database schema
215
+ └── PR #124: Add authentication service
216
+ └── PR #125: Add login UI
217
+```
218
+
219
+Each PR builds on the previous one. When #123 merges, #124 automatically rebases.
220
+
221
+#### Creating a Stack
222
+
223
+**Using CLI:**
224
+```bash
225
+# 1. Create base branch
226
+git checkout -b feature/auth
227
+git commit -m "Add user table schema"
228
+och stack create auth-schema
229
+
230
+# 2. Create stacked branch
231
+och stack create auth-service
232
+git commit -m "Add authentication service"
233
+
234
+# 3. Create final layer
235
+och stack create auth-ui
236
+git commit -m "Add login UI"
237
+
238
+# 4. Submit entire stack
239
+och stack submit
240
+```
241
+
242
+**Using Web UI:**
243
+1. Create first PR normally
244
+2. When creating second PR, select "Stack on PR #123"
245
+3. System tracks dependencies automatically
246
+
247
+#### Stack Features
248
+
249
+βœ… **Auto-Rebasing**: When base PR merges, dependent PRs rebase automatically
250
+βœ… **Dependency Visualization**: See entire stack in PR view
251
+βœ… **Smart Merge Queue**: Merges in correct order
252
+βœ… **Conflict Detection**: Alerts on rebase conflicts
253
+
254
+---
255
+
256
+### πŸ€– AI Code Review
257
+
258
+**Get instant feedback from AI reviewers powered by GPT-4 or Claude.**
259
+
260
+#### Setup
261
+
262
+**1. Configure AI Provider:**
263
+```env
264
+# In .env
265
+AI_PROVIDER=openai # or anthropic
266
+OPENAI_API_KEY=sk-...
267
+# or
268
+ANTHROPIC_API_KEY=sk-ant-...
269
+```
270
+
271
+**2. Enable for Repository:**
272
+Navigate to **Repository β†’ Settings β†’ AI Review**
273
+- βœ… Enable AI Review
274
+- Set review triggers (on PR open, on push, manual)
275
+- Configure review rules
276
+
277
+#### What AI Reviews Catch
278
+
279
+πŸ” **Security Vulnerabilities**
280
+- SQL injection risks
281
+- XSS vulnerabilities
282
+- Authentication bypasses
283
+- Secrets in code
284
+
285
+⚑ **Performance Issues**
286
+- N+1 queries
287
+- Inefficient algorithms
288
+- Memory leaks
289
+- Blocking operations
290
+
291
+πŸ“ **Code Quality**
292
+- Best practice violations
293
+- Code smells
294
+- Incomplete error handling
295
+- Missing documentation
296
+
297
+πŸ› **Potential Bugs**
298
+- Null pointer exceptions
299
+- Race conditions
300
+- Off-by-one errors
301
+- Logic flaws
302
+
303
+#### Using AI Review
304
+
305
+**Automatic (Recommended):**
306
+AI review runs automatically on every PR based on configured triggers.
307
+
308
+**Manual:**
309
+```bash
310
+# Via CLI
311
+och review ai \u003cPR_NUMBER\u003e
312
+
313
+# Via web UI: Click "Request AI Review" button on PR
314
+```
315
+
316
+#### Review Output
317
+
318
+AI provides:
319
+- **Severity Score**: Critical / High / Medium / Low / Info
320
+- **Specific Line Comments**: Inline code suggestions
321
+- **Summary Report**: Overall code quality assessment
322
+- **Fix Suggestions**: Actionable improvements
323
+
324
+**Example:**
325
+```
326
+πŸ”΄ CRITICAL: SQL Injection Vulnerability
327
+File: src/api/users.ts:45
328
+Issue: User input directly concatenated into SQL query
329
+Fix: Use parameterized queries or ORM
330
+
331
+🟑 MEDIUM: Performance Concern
332
+File: src/lib/data.ts:128
333
+Issue: N+1 query in loop (1000+ iterations)
334
+Fix: Use batch loading or eager loading
335
+```
336
+
337
+---
338
+
339
+### πŸ”€ Smart Merge Queue
340
+
341
+**Stack-aware merge queue with automatic conflict resolution and CI optimization.**
342
+
343
+#### Features
344
+
345
+**Stack-Aware Merging:**
346
+- Detects PR dependencies
347
+- Merges in correct order automatically
348
+- Handles entire stacks atomically
349
+
350
+**CI Optimization:**
351
+- Batches compatible PRs
352
+- Runs CI once for multiple PRs
353
+- Reduces CI cost by ~60%
354
+
355
+**Automatic Rebasing:**
356
+- Detects conflicts early
357
+- Auto-rebases on base changes
358
+- Alerts author on conflicts
359
+
360
+**Smart Ordering:**
361
+- Priority-based queue
362
+- Dependency resolution
363
+- Hotfix fast-tracking
364
+
365
+#### Using Merge Queue
366
+
367
+**Add to Queue:**
368
+```bash
369
+# Via CLI
370
+och queue add \u003cPR_NUMBER\u003e
371
+
372
+# Via Web UI
373
+Click "Add to Merge Queue" button on approved PR
374
+```
375
+
376
+**Queue Status:**
377
+```bash
378
+# View queue
379
+och queue list
380
+
381
+# Output:
382
+Position PR Title Status
383
+1 #125 Add login UI ⏳ Running CI
384
+2 #124 Auth service ⏸️ Waiting (depends on #123)
385
+3 #123 Database schema βœ… Ready to merge
386
+```
387
+
388
+**How It Works:**
389
+
390
+1. **PR Added**: Queue analyzes dependencies
391
+2. **Position Assigned**: Ordered by priority and dependencies
392
+3. **CI Running**: Runs tests in parallel where possible
393
+4. **Auto-Merge**: Merges when ready, rebases dependents
394
+5. **Notification**: Author notified on completion
395
+
396
+#### Queue Configuration
397
+
398
+**Repository β†’ Settings β†’ Merge Queue:**
399
+- **Require CI**: Only merge if all checks pass
400
+- **Batch Window**: Wait time to batch PRs (default: 5min)
401
+- **Max Batch Size**: Max PRs to merge together (default: 3)
402
+- **Auto-Rebase**: Rebase on conflicts vs. remove from queue
403
+
404
+---
405
+
406
+### πŸ“Š Developer Metrics
407
+
408
+**Track team velocity, review efficiency, and identify bottlenecks.**
409
+
410
+#### Available Metrics
411
+
412
+**PR Velocity:**
413
+- PRs merged per week
414
+- Average time to merge
415
+- Stack success rate
416
+- Review cycle time
417
+
418
+**Review Efficiency:**
419
+- Average review time
420
+- Reviews per day
421
+- Review quality score
422
+- Reviewer responsiveness
423
+
424
+**Team Performance:**
425
+- Lines changed per PR
426
+- PR size distribution
427
+- Revert rate
428
+- CI success rate
429
+
430
+**Stack Metrics:**
431
+- % PRs that use stacks
432
+- Average stack depth
433
+- Stack merge success rate
434
+- Time saved vs. monolithic PRs
435
+
436
+#### Viewing Metrics
437
+
438
+**Personal Dashboard:**
439
+```
440
+/metrics/me
441
+```
442
+
443
+**Team Dashboard (Admin):**
444
+```
445
+/admin/metrics
446
+```
447
+
448
+**Via CLI:**
449
+```bash
450
+och metrics show
451
+och metrics show --user @username
452
+och metrics show --repo owner/repo
453
+```
454
+
455
+#### Example Metrics View
456
+
457
+```
458
+πŸ“Š Your Metrics (Last 30 Days)
459
+
460
+PR Velocity
461
+β”œβ”€ PRs Merged ━━━━━━━━━━━━━━ 24 (+12%)
462
+β”œβ”€ Avg Time to Merge ━━━━━━━━━━━━━━ 8.2 hours (-3h)
463
+└─ Stack Usage ━━━━━━━━━━━━━━ 65% (+15%)
464
+
465
+Review Efficiency
466
+β”œβ”€ Reviews Given ━━━━━━━━━━━━━━ 18
467
+β”œβ”€ Avg Review Time ━━━━━━━━━━━━━━ 22 min
468
+└─ Approval Rate ━━━━━━━━━━━━━━ 94%
469
+```
470
+
471
+---
472
+
473
+### πŸ’¬ Slack Notifications
474
+
475
+**Get actionable PR notifications directly in Slack.**
476
+
477
+#### Setup
478
+
479
+**1. Create Slack App:**
480
+1. Go to https://api.slack.com/apps
481
+2. Create New App β†’ From Scratch
482
+3. Add OAuth Scopes:
483
+ - `chat:write`
484
+ - `chat:write.public`
485
+ - `users:read`
486
+4. Install to workspace
487
+
488
+**2. Connect to OpenCodeHub:**
489
+Navigate to **Admin β†’ Integrations β†’ Slack**
490
+```
491
+Workspace Name: Your Team
492
+Bot Token: xoxb-...
493
+Signing Secret: ...
494
+```
495
+
496
+**3. Map Channels:**
497
+**Repository β†’ Settings β†’ Slack**
498
+```
499
+#engineering β†’ All PR events
500
+#deployments β†’ Merges only
501
+#security β†’ Security findings
502
+```
503
+
504
+**4. Map Users (Optional):**
505
+Users can link their Slack account:
506
+**Settings β†’ Integrations β†’ Connect Slack**
507
+
508
+#### Notification Types
509
+
510
+**PR Events:**
511
+- πŸŽ‰ PR opened
512
+- βœ… PR approved
513
+- πŸ”€ PR merged
514
+- ❌ PR closed
515
+- πŸ’¬ Review requested
516
+- πŸ”΄ Changes requested
517
+
518
+**CI/CD Events:**
519
+- βœ… Build passed
520
+- ❌ Build failed
521
+- πŸš€ Deployment started
522
+- βœ… Deployment successful
523
+
524
+**Code Review:**
525
+- πŸ€– AI review completed
526
+- πŸ” Security issue found
527
+- ⚠️ Performance concern
528
+
529
+**Merge Queue:**
530
+- ⏭️ Added to queue
531
+- βœ… Merged from queue
532
+- ⚠️ Removed (conflict)
533
+
534
+#### Interactive Actions
535
+
536
+Notifications include action buttons:
537
+```
538
+[swadhin] opened PR#125: Add login UI
539
+Stack: #123 β†’ #124 β†’ #125 β€’ files β†’32 β€’ LoC +420/-18
540
+
541
+[Approve] [Request Changes] [View PR] [Add to Queue]
542
+```
543
+
544
+Click buttons to:
545
+- Approve/reject directly from Slack
546
+- Request changes with comment
547
+- Add to merge queue
548
+- View full PR
549
+
550
+---
551
+
552
+## πŸ’‘ Workflow Examples
553
+
554
+### Example 1: Complete Stacked PR Flow
555
+
556
+```bash
557
+# 1. Start new feature
558
+git checkout main
559
+git pull
560
+git checkout -b feature/user-profiles
561
+
562
+# 2. First PR: Database layer
563
+git add migrations/
564
+git commit -m "Add user profile tables"
565
+och stack create profile-db
566
+och stack submit
567
+
568
+# β†’ Creates PR #301
569
+
570
+# 3. Second PR: Backend API
571
+git add src/api/profiles/
572
+git commit -m "Add profile API"
573
+och stack create profile-api
574
+och stack submit
575
+
576
+# β†’ Creates PR #302 (stacked on #301)
577
+
578
+# 4. Third PR: Frontend
579
+git add src/pages/profile/
580
+git commit -m "Add profile UI"
581
+och stack create profile-ui
582
+och stack submit
583
+
584
+# β†’ Creates PR #303 (stacked on #302)
585
+
586
+# 5. View stack
587
+och stack view
588
+```
589
+
590
+**Output:**
591
+```
592
+πŸ“š Current Stack
593
+
594
+main (base)
595
+ └─ #301: profile-db βœ… Approved
596
+ └─ #302: profile-api πŸ” In Review
597
+ └─ #303: profile-ui ⏳ AI Review Running
598
+```
599
+
600
+### Example 2: Using AI Review + Merge Queue
601
+
602
+```bash
603
+# 1. Create PR
604
+git push origin feature-branch
605
+
606
+# 2. Request AI review
607
+och review ai 304
608
+
609
+# 3. Fix AI suggestions
610
+# ... make changes based on review ...
611
+git commit -m "Fix security issues from AI review"
612
+git push
613
+
614
+# 4. Get human review
615
+# Team reviews via web UI
616
+
617
+# 5. Add to merge queue when approved
618
+och queue add 304
619
+
620
+# Queue handles:
621
+# - Waiting for CI
622
+# - Merging when ready
623
+# - Notifying on Slack
624
+```
625
+
626
+### Example 3: Team Collaboration
627
+
628
+```bash
629
+# Developer A: Creates base
630
+git checkout -b api/payments
631
+# ... implements payment models ...
632
+git commit -m "Add payment models"
633
+git push
634
+
635
+# Developer B: Builds on A's work (before merge!)
636
+git checkout api/payments
637
+git checkout -b api/payment-processing
638
+# ... implements payment processing ...
639
+git commit -m "Add payment processing"
640
+git push
641
+# Create PR stacked on A's PR
642
+
643
+# Developer C: Builds on B's work
644
+git checkout api/payment-processing
645
+git checkout -b ui/payment-form
646
+# ... implements payment UI ...
647
+```
648
+
649
+**Result**: 3 parallel PRs, all reviewed separately, merged in order.
650
+
651
+---
652
+
653
+## πŸ“– Additional Resources
654
+
655
+- **Full CLI Reference**: `cli/README.md`
656
+- **API Documentation**: `/api/docs`
657
+- **Stacked PR Guide**: `/docs#stacked-prs`
658
+- **Slack Integration Guide**: `/docs#slack`
659
+
660
+---
661
+
662
+
663
+## βš™οΈ Configuration
664
+
665
+### Critical Environment Variables
666
+
667
+These **MUST** be set before deploying to production:
668
+
669
+```env
670
+# Security (CRITICAL - Generate strong secrets!)
671
+JWT_SECRET=<run: openssl rand -hex 32>
672
+SESSION_SECRET=<run: openssl rand -hex 32>
673
+INTERNAL_HOOK_SECRET=<run: openssl rand -hex 32>
674
+
675
+# Application URLs (CRITICAL)
676
+SITE_URL=https://your-domain.com # Must be HTTPS in production
677
+PORT=3000
678
+HOST=0.0.0.0
679
+```
680
+
681
+### Database Configuration
682
+
683
+**PostgreSQL (Recommended for Production):**
684
+```env
685
+DATABASE_DRIVER=postgres
686
+DATABASE_URL=postgresql://user:password@localhost:5432/opencodehub
687
+```
688
+
689
+**MySQL:**
690
+```env
691
+DATABASE_DRIVER=mysql
692
+DATABASE_URL=mysql://user:password@localhost:3306/opencodehub
693
+```
694
+
695
+**SQLite (Development Only):**
696
+```env
697
+DATABASE_DRIVER=sqlite
698
+DATABASE_URL=./data/opencodehub.db
699
+```
700
+
701
+### Storage Configuration
702
+
703
+Storage can be configured via Admin Panel (`/admin/storage`) or environment variables:
704
+
705
+**Local Storage:**
706
+```env
707
+STORAGE_TYPE=local
708
+STORAGE_PATH=./data/storage
709
+```
710
+
711
+**S3/MinIO/R2:**
712
+```env
713
+STORAGE_TYPE=s3
714
+STORAGE_BUCKET=opencodehub
715
+STORAGE_REGION=us-east-1
716
+STORAGE_ENDPOINT=https://s3.amazonaws.com
717
+S3_ACCESS_KEY=your-access-key
718
+S3_SECRET_KEY=your-secret-key
719
+```
720
+
721
+**Google Drive (via Admin Panel):**
722
+- Navigate to `/admin/storage`
723
+- Select "Google Drive"
724
+- Enter Client ID, Secret, and Refresh Token
725
+- Test connection before saving
726
+
727
+### Security Configuration
728
+
729
+**Rate Limiting:**
730
+```env
731
+RATE_LIMIT_AUTH=5 # Login attempts per 15 minutes
732
+RATE_LIMIT_API=100 # API requests per minute
733
+RATE_LIMIT_GIT=200 # Git operations per minute
734
+RATE_LIMIT_SKIP_DEV=false # Never skip in production
735
+```
736
+
737
+**CSRF Protection:**
738
+```env
739
+CSRF_SKIP_DEV=false # Never skip in production
740
+```
741
+
742
+### Optional Services
743
+
744
+**Email (SMTP):**
745
+```env
746
+SMTP_HOST=smtp.example.com
747
+SMTP_PORT=587
748
749
+SMTP_PASSWORD=your-smtp-password
750
751
+```
752
+
753
+**Redis (Caching & Sessions):**
754
+```env
755
+REDIS_URL=redis://localhost:6379
756
+```
757
+
758
+**Error Monitoring (Sentry):**
759
+```env
760
+SENTRY_DSN=https://...
761
+ENABLE_TRACING=true
762
+```
763
+
764
+---
765
+
766
+## πŸ”’ Security
767
+
768
+OpenCodeHub implements **production-grade security** out of the box:
769
+
770
+### Implemented Security Features
771
+
772
+βœ… **Authentication & Authorization**
773
+- JWT-based sessions with automatic expiration
774
+- Bcrypt password hashing (10 rounds)
775
+- 2FA/TOTP support
776
+- Role-based access control (RBAC)
777
+
778
+βœ… **Attack Prevention**
779
+- **Rate Limiting**: Prevents brute force (5 login attempts per 15min)
780
+- **CSRF Protection**: Double-submit cookie pattern on all state-changing operations
781
+- **Input Validation**: Zod schemas prevent SQL injection and XSS
782
+- **XSS Sanitization**: Markdown rendered with `rehype-sanitize`
783
+
784
+βœ… **Operational Security**
785
+- **Audit Logging**: All admin actions logged to database
786
+- **Hook Authentication**: Git hooks protected with shared secrets
787
+- **Session Management**: Automatic cleanup and rotation
788
+- **Environment Validation**: Startup checks for missing/weak secrets
789
+
790
+### Security Best Practices
791
+
792
+1. **Always use HTTPS** in production (Let's Encrypt recommended)
793
+2. **Rotate secrets** regularly (especially after team changes)
794
+3. **Enable 2FA** for all admin accounts
795
+4. **Monitor audit logs** for suspicious activity
796
+5. **Keep dependencies updated**: Run `npm audit` weekly
797
+
798
+### Reporting Security Issues
799
+
800
+Please report security vulnerabilities to **[email protected]**. Do not create public issues.
801
+
802
+---
803
+
804
+## πŸš€ Production Deployment
805
+
806
+### Pre-Deployment Checklist
807
+
808
+Before going live, ensure:
809
+
810
+- [ ] **Secrets**: All secrets generated with `openssl rand -hex 32`
811
+- [ ] **HTTPS**: SSL certificate configured (Let's Encrypt recommended)
812
+- [ ] **Database**: PostgreSQL with backups enabled
813
+- [ ] **Storage**: S3/R2 configured (not local filesystem)
814
+- [ ] **Monitoring**: Error tracking (Sentry) and APM configured
815
+- [ ] **Rate Limits**: Configured for expected traffic
816
+- [ ] **Admin Account**: Created via `bun run scripts/seed-admin.ts`
817
+- [ ] **Environment Validation**: Test with `bun run src/lib/env-validation.ts`
818
+- [ ] **Database Migrations**: Generated with `npm run db:generate`
819
+
820
+### Production Environment Variables
821
+
822
+```env
823
+# Application
824
+NODE_ENV=production
825
+SITE_URL=https://git.yourcompany.com
826
+PORT=3000
827
+
828
+# Security (CRITICAL - Generate with: openssl rand -hex 32)
829
+JWT_SECRET=<64-char-random-hex>
830
+SESSION_SECRET=<64-char-random-hex>
831
+INTERNAL_HOOK_SECRET=<64-char-random-hex>
832
+
833
+# Database (Use connection pooling)
834
+DATABASE_DRIVER=postgres
835
+DATABASE_URL=postgresql://user:password@db-host:5432/opencodehub?pool_timeout=30
836
+
837
+# Storage (Use cloud storage)
838
+STORAGE_TYPE=s3
839
+STORAGE_BUCKET=opencodehub-prod
840
+STORAGE_REGION=us-east-1
841
+
842
+# Rate Limiting (Adjust based on traffic)
843
+RATE_LIMIT_AUTH=5
844
+RATE_LIMIT_API=200
845
+RATE_LIMIT_GIT=500
846
+
847
+# Monitoring
848
+SENTRY_DSN=https://your-sentry-dsn
849
+LOG_LEVEL=info
850
+
851
+# Features
852
+ENABLE_REGISTRATION=true # Set false for invite-only
853
+```
854
+
855
+### Docker Production Deployment
856
+
857
+```yaml
858
+# docker-compose.prod.yml
859
+version: '3.8'
860
+
861
+services:
862
+ app:
863
+ image: opencodehub:latest
864
+ restart: always
865
+ ports:
866
+ - "3000:3000"
867
+ env_file: .env.production
868
+ depends_on:
869
+ - postgres
870
+ - redis
871
+ volumes:
872
+ - ./data:/app/data
873
+ healthcheck:
874
+ test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
875
+ interval: 30s
876
+ timeout: 10s
877
+ retries: 3
878
+
879
+ postgres:
880
+ image: postgres:15-alpine
881
+ restart: always
882
+ environment:
883
+ POSTGRES_DB: opencodehub
884
+ POSTGRES_USER: opencodehub
885
+ POSTGRES_PASSWORD: <strong-password>
886
+ volumes:
887
+ - postgres_data:/var/lib/postgresql/data
888
+
889
+ redis:
890
+ image: redis:7-alpine
891
+ restart: always
892
+
893
+volumes:
894
+ postgres_data:
895
+```
896
+
897
+```bash
898
+# Deploy
899
+docker-compose -f docker-compose.prod.yml up -d
900
+
901
+# Monitor logs
902
+docker-compose -f docker-compose.prod.yml logs -f app
903
+
904
+# Health check
905
+curl https://git.yourcompany.com/api/health
906
+```
907
+
908
+### Reverse Proxy (Nginx)
909
+
910
+```nginx
911
+server {
912
+ listen 443 ssl http2;
913
+ server_name git.yourcompany.com;
914
+
915
+ ssl_certificate /path/to/cert.pem;
916
+ ssl_certificate_key /path/to/key.pem;
917
+
918
+ location / {
919
+ proxy_pass http://localhost:3000;
920
+ proxy_set_header Host $host;
921
+ proxy_set_header X-Real-IP $remote_addr;
922
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
923
+ proxy_set_header X-Forwarded-Proto $scheme;
924
+ }
925
+
926
+ # Git operations (larger timeouts)
927
+ location ~ ^/[^/]+/[^/]+\.git/ {
928
+ proxy_pass http://localhost:3000;
929
+ proxy_read_timeout 300s;
930
+ proxy_send_timeout 300s;
931
+ proxy_buffer_size 128k;
932
+ proxy_buffers 4 256k;
933
+ }
934
+}
935
+```
936
+
937
+### Database Backups
938
+
939
+```bash
940
+# PostgreSQL backup script (cron daily)
941
+#!/bin/bash
942
+pg_dump -U opencodehub opencodehub | gzip > /backups/opencodehub-$(date +\%Y\%m\%d).sql.gz
943
+
944
+# Retain last 30 days
945
+find /backups -name "opencodehub-*.sql.gz" -mtime +30 -delete
946
+```
947
+
948
+---
949
+
950
+## πŸ“š API Documentation
951
+
952
+### Authentication
953
+
954
+All authenticated endpoints require a session cookie or `Authorization: Bearer <token>` header.
955
+
956
+**Register:**
957
+```bash
958
+POST /api/auth/register
959
+Content-Type: application/json
960
+
961
+{
962
+ "username": "johndoe",
963
+ "email": "[email protected]",
964
+ "password": "SecureP@ssw0rd",
965
+ "displayName": "John Doe"
966
+}
967
+
968
+# Response: 201 Created
969
+{
970
+ "user": { "id": "...", "username": "johndoe", ... },
971
+ "token": "eyJ...",
972
+ "expiresAt": "2026-01-08T00:00:00Z"
973
+}
974
+```
975
+
976
+**Login:**
977
+```bash
978
+POST /api/auth/login
979
+Content-Type: application/json
980
+
981
+{
982
+ "login": "[email protected]", # username or email
983
+ "password": "SecureP@ssw0rd"
984
+}
985
+
986
+# With 2FA:
987
+{
988
+ "login": "[email protected]",
989
+ "password": "SecureP@ssw0rd",
990
+ "totpCode": "123456"
991
+}
992
+```
993
+
994
+### Repositories
995
+
996
+**Create Repository:**
997
+```bash
998
+POST /api/repos
999
+Authorization: Bearer <token>
1000
+Content-Type: application/json
1001
+
1002
+{
1003
+ "name": "my-project",
1004
+ "description": "My awesome project",
1005
+ "visibility": "public" # public, private, internal
1006
+}
1007
+```
1008
+
1009
+**Branch Protection:**
1010
+```bash
1011
+POST /api/repos/{repoId}/branch-protection
1012
+Authorization: Bearer <token>
1013
+Content-Type: application/json
1014
+
1015
+{
1016
+ "pattern": "main",
1017
+ "requiresPr": true,
1018
+ "requiredApprovals": 2,
1019
+ "dismissStaleReviews": true
1020
+}
1021
+```
1022
+
1023
+### Admin Endpoints
1024
+
1025
+**List Users (Admin Only):**
1026
+```bash
1027
+GET /admin/users?q=search&page=1
1028
+Cookie: och_session=...
1029
+```
1030
+
1031
+**Update Storage Config (Admin Only):**
1032
+```bash
1033
+POST /api/admin/config/storage
1034
+Cookie: och_session=...
1035
+Content-Type: application/json
1036
+
1037
+{
1038
+ "type": "s3",
1039
+ "bucket": "my-bucket",
1040
+ "region": "us-east-1",
1041
+ ...
1042
+}
1043
+```
1044
+
1045
+### Rate Limits
1046
+
1047
+All endpoints return rate limit headers:
1048
+```
1049
+X-RateLimit-Limit: 100
1050
+X-RateLimit-Remaining: 95
1051
+X-RateLimit-Reset: 2026-01-01T15:30:00Z
1052
+Retry-After: 30 (when rate limited)
1053
+```
1054
+
1055
+**Full API documentation:** `/api/docs` (OpenAPI spec coming soon)
1056
+
1057
+---
1058
+
1059
+## πŸ”§ Usage Guide
1060
+
1061
+### Creating Your First Repository
1062
+
1063
+1. **Log in** to your account
1064
+2. Click the **+** button β†’ **New Repository**
1065
+3. Fill in details:
1066
+ - **Name**: alphanumeric, hyphens, underscores
1067
+ - **Description**: Optional
1068
+ - **Visibility**: public/private
1069
+4. Click **Create Repository**
1070
+
1071
+### Cl
1072
+
1073
+oning Repositories
1074
+
1075
+**SSH (Recommended):**
1076
+```bash
1077
+# Add your SSH key in Settings β†’ SSH Keys
1078
+git clone [email protected]:username/repo.git
1079
+```
1080
+
1081
+**HTTPS:**
1082
+```bash
1083
+git clone https://your-domain.com/username/repo.git
1084
+# Username: your-username
1085
+# Password: your-password or personal access token
1086
+```
1087
+
1088
+### Setting Up Branch Protection
1089
+
1090
+1. Navigate to **Repository β†’ Settings β†’ Branches**
1091
+2. Click **Add Rule**
1092
+3. Configure:
1093
+ - **Pattern**: `main` or `release/*`
1094
+ - **Require PR**: βœ…
1095
+ - **Required Approvals**: 2
1096
+ - **Dismiss Stale Reviews**: βœ…
1097
+4. **Save Rule**
1098
+
1099
+### CI/CD Workflows
1100
+
1101
+Create `.github/workflows/ci.yml`:
1102
+
1103
+```yaml
1104
+name: CI
1105
+
1106
+on:
1107
+ push:
1108
+ branches: [main, develop]
1109
+ pull_request:
1110
+ branches: [main]
1111
+
1112
+jobs:
1113
+ test:
1114
+ runs-on: ubuntu-latest
1115
+ steps:
1116
+ - uses: actions/checkout@v4
1117
+
1118
+ - name: Setup Node.js
1119
+ uses: actions/setup-node@v4
1120
+ with:
1121
+ node-version: '20'
1122
+
1123
+ - name: Install
1124
+ run: npm ci
1125
+
1126
+ - name: Test
1127
+ run: npm test
1128
+
1129
+ - name: Build
1130
+ run: npm run build
1131
+
1132
+ - name: Upload Artifacts
1133
+ uses: actions/upload-artifact@v3
1134
+ with:
1135
+ name: dist
1136
+ path: dist/
1137
+```
1138
+
1139
+---
1140
+
1141
+## πŸ› Troubleshooting
1142
+
1143
+### Common Issues
1144
+
1145
+**"Rate limit exceeded" on login:**
1146
+- Default: 5 attempts per 15 minutes
1147
+- Solution: Wait 15 minutes or adjust `RATE_LIMIT_AUTH` in `.env`
1148
+
1149
+**"CSRF token validation failed":**
1150
+- Ensure cookies are enabled
1151
+- Check `SITE_URL` matches your actual domain
1152
+- Verify HTTPS in production
1153
+
1154
+**"Unauthorized" on git hooks:**
1155
+- Check `INTERNAL_HOOK_SECRET` is set
1156
+- Verify hooks have correct secret in curl headers
1157
+- Re-run `git init` on repositories to reinstall hooks
1158
+
1159
+**Database connection errors:**
1160
+- Verify `DATABASE_URL` format
1161
+- Check database server is running
1162
+- Ensure user has correct permissions
1163
+
1164
+**Storage upload fails:**
1165
+- Test connection in `/admin/storage`
1166
+- Verify S3 credentials and bucket permissions
1167
+- Check firewall rules for external storage
1168
+
1169
+### Debug Mode
1170
+
1171
+```env
1172
+# Enable detailed logging
1173
+LOG_LEVEL=debug
1174
+NODE_ENV=development
1175
+
1176
+# View logs
1177
+tail -f data/logs/opencodehub.log
1178
+```
1179
+
1180
+### Health Check
1181
+
1182
+```bash
1183
+curl http://localhost:3000/api/health
1184
+
1185
+# Response:
1186
+{
1187
+ "status": "ok",
1188
+ "checks": {
1189
+ "database": "ok",
1190
+ "storage": "ok",
1191
+ "redis": "ok" // if configured
1192
+ },
1193
+ "uptime": 123456,
1194
+ "version": "1.0.0"
1195
+}
1196
+```
1197
+
1198
+---
1199
+
1200
+## 🀝 Contributing
1201
+
1202
+We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
1203
+
1204
+### Development Setup
1205
+
1206
+```bash
1207
+# Fork and clone
1208
+git clone https://github.com/swadhinbiswas/OpencodeHub.git
1209
+cd OpencodeHub
1210
+
1211
+# Install dependencies
1212
+bun install
1213
+
1214
+# Run in development
1215
+bun run dev
1216
+
1217
+# Run tests
1218
+bun test
1219
+
1220
+# Lint
1221
+bun run lint
1222
+```
1223
+
1224
+### Submitting Changes
1225
+
1226
+1. Create a feature branch (`git checkout -b feature/amazing-feature`)
1227
+2. Make your changes
1228
+3. Add tests
1229
+4. Run `bun test` and `bun run lint`
1230
+5. Commit (`git commit -m 'Add amazing feature'`)
1231
+6. Push (`git push origin feature/amazing-feature`)
1232
+7. Open a Pull Request
1233
+
1234
+---
1235
+
1236
+## πŸ“„ License
1237
+
1238
+MIT License - see [LICENSE](LICENSE) for details.
1239
+
1240
+---
1241
+
1242
+## πŸ™ Acknowledgments
1243
+
1244
+- [Astro](https://astro.build/) - Web framework
1245
+- [shadcn/ui](https://ui.shadcn.com/) - UI components
1246
+- [Drizzle ORM](https://orm.drizzle.team/) - Database toolkit
1247
+- [simple-git](https://github.com/steveukx/git-js) - Git operations
1248
+- [Monaco Editor](https://microsoft.github.io/monaco-editor/) - Code editor
1249
+- [Zod](https://zod.dev/) - Schema validation
1250
+
1251
+---
1252
+
1253
+<p align="center">
1254
+ <strong>Made with ❀️ for the open source community</strong>
1255
+ <br>
1256
+ <a href="https://github.com/swadhinbiswas/OpencodeHub/issues">Report Bug</a> β€’
1257
+ <a href="https://github.com/swadhinbiswas/OpencodeHub/issues">Request Feature</a>
1258
+</p>
0
1259
β–ΌVercel Deploy.md
+794-0
1
+# Deploy OpenCodeHub to Vercel
2
+
3
+> Complete guide to deploying OpenCodeHub on Vercel with Turso, Google Drive, and Redis
4
+
5
+This guide walks you through deploying OpenCodeHub to Vercel using:
6
+- **Turso** - Serverless SQLite database (edge-compatible)
7
+- **Google Drive API** - File storage for repositories
8
+- **Upstash Redis** - Serverless Redis for sessions and caching
9
+
10
+**Estimated time:** 30-45 minutes
11
+**Cost:** Free tier available for all services
12
+
13
+---
14
+
15
+## πŸ“‹ Table of Contents
16
+
17
+1. [Prerequisites](#prerequisites)
18
+2. [Setup Turso Database](#setup-turso-database)
19
+3. [Setup Google Drive Storage](#setup-google-drive-storage)
20
+4. [Setup Upstash Redis](#setup-upstash-redis)
21
+5. [Configure Project](#configure-project)
22
+6. [Deploy to Vercel](#deploy-to-vercel)
23
+7. [Post-Deployment](#post-deployment)
24
+8. [Troubleshooting](#troubleshooting)
25
+
26
+---
27
+
28
+## Prerequisites
29
+
30
+Before starting, ensure you have:
31
+
32
+- [x] GitHub account
33
+- [x] Vercel account ([vercel.com](https://vercel.com))
34
+- [x] Turso account ([turso.tech](https://turso.tech))
35
+- [x] Google Cloud account ([console.cloud.google.com](https://console.cloud.google.com))
36
+- [x] Upstash account ([upstash.com](https://upstash.com))
37
+- [x] OpenCodeHub repository forked/cloned
38
+
39
+---
40
+
41
+## 1. Setup Turso Database
42
+
43
+Turso provides edge-compatible, serverless SQLite databases perfect for Vercel.
44
+
45
+### 1.1. Install Turso CLI
46
+
47
+```bash
48
+# macOS/Linux
49
+curl -sSfL https://get.tur.so/install.sh | bash
50
+
51
+# Windows (PowerShell)
52
+irm get.tur.so/install.ps1 | iex
53
+
54
+# Verify installation
55
+turso --version
56
+```
57
+
58
+### 1.2. Login to Turso
59
+
60
+```bash
61
+turso auth login
62
+```
63
+
64
+This opens your browser to authenticate.
65
+
66
+### 1.3. Create Database
67
+
68
+```bash
69
+# Create database
70
+turso db create opencodehub
71
+
72
+# Create auth token
73
+turso db tokens create opencodehub
74
+
75
+# Get database URL
76
+turso db show opencodehub --url
77
+```
78
+
79
+**Save these values:**
80
+```env
81
+TURSO_DATABASE_URL=libsql://opencodehub-[your-org].turso.io
82
+TURSO_AUTH_TOKEN=eyJhbGciOiJ...
83
+```
84
+
85
+### 1.4. Initialize Database Schema
86
+
87
+```bash
88
+# Download schema
89
+curl -O https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/drizzle/schema.sql
90
+
91
+# Apply schema
92
+turso db shell opencodehub < schema.sql
93
+
94
+# Verify tables
95
+turso db shell opencodehub "SELECT name FROM sqlite_master WHERE type='table';"
96
+```
97
+
98
+**Expected output:**
99
+```
100
+users
101
+repositories
102
+pull_requests
103
+pr_stacks
104
+merge_queue_items
105
+ai_reviews
106
+...
107
+```
108
+
109
+---
110
+
111
+## 2. Setup Google Drive Storage
112
+
113
+Google Drive API will store Git repositories and LFS files.
114
+
115
+### 2.1. Create Google Cloud Project
116
+
117
+1. Go to [Google Cloud Console](https://console.cloud.google.com)
118
+2. Click **Select a project** β†’ **New Project**
119
+3. Name: `opencodehub-storage`
120
+4. Click **Create**
121
+
122
+### 2.2. Enable Google Drive API
123
+
124
+1. In the Cloud Console, go to **APIs & Services** β†’ **Enable APIs**
125
+2. Search for "Google Drive API"
126
+3. Click **Enable**
127
+
128
+### 2.3. Create Service Account
129
+
130
+1. Go to **APIs & Services** β†’ **Credentials**
131
+2. Click **Create Credentials** β†’ **Service Account**
132
+3. Name: `opencodehub-storage-sa`
133
+4. Click **Create and Continue**
134
+5. Grant role: **Editor**
135
+6. Click **Done**
136
+
137
+### 2.4. Generate Service Account Key
138
+
139
+1. Click on the service account you just created
140
+2. Go to **Keys** tab
141
+3. Click **Add Key** β†’ **Create new key**
142
+4. Choose **JSON**
143
+5. Click **Create** (downloads `opencodehub-storage-xxxxx.json`)
144
+
145
+### 2.5. Create Google Drive Folder
146
+
147
+1. Go to [Google Drive](https://drive.google.com)
148
+2. Create a new folder: `OpenCodeHub-Repositories`
149
+3. Right-click β†’ **Share**
150
+4. Add the service account email: `opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com`
151
+5. Grant **Editor** access
152
+6. Copy the **Folder ID** from the URL:
153
+ ```
154
+ https://drive.google.com/drive/folders/1a2B3c4D5e6F7g8H9i0J
155
+ ^^^^^^^^^^^^^^^^^^^^
156
+ This is your folder ID
157
+ ```
158
+
159
+### 2.6. Prepare Service Account Credentials
160
+
161
+Open the downloaded JSON file and extract these values:
162
+
163
+```json
164
+{
165
+ "type": "service_account",
166
+ "project_id": "opencodehub-storage",
167
+ "private_key_id": "abc123...",
168
+ "private_key": "-----BEGIN PRIVATE KEY-----\n...",
169
+ "client_email": "opencodehub-storage-sa@...",
170
+ "client_id": "123456789...",
171
+ ...
172
+}
173
+```
174
+
175
+**Save for later:**
176
+```env
177
+GOOGLE_DRIVE_FOLDER_ID=1a2B3c4D5e6F7g8H9i0J
178
+GOOGLE_SERVICE_ACCOUNT_EMAIL=opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com
179
+GOOGLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n...
180
+GOOGLE_CLIENT_ID=123456789...
181
+```
182
+
183
+---
184
+
185
+## 3. Setup Upstash Redis
186
+
187
+Upstash provides serverless Redis, perfect for Vercel edge functions.
188
+
189
+### 3.1. Create Redis Database
190
+
191
+1. Go to [Upstash Console](https://console.upstash.com)
192
+2. Click **Create Database**
193
+3. **Name:** `opencodehub-sessions`
194
+4. **Region:** Choose closest to your users (e.g., `us-east-1`)
195
+5. **Type:** Regional (free tier)
196
+6. Click **Create**
197
+
198
+### 3.2. Get Connection Details
199
+
200
+After creation, copy these values:
201
+
202
+**REST API:**
203
+```env
204
+UPSTASH_REDIS_REST_URL=https://us1-prepared-moose-12345.upstash.io
205
+UPSTASH_REDIS_REST_TOKEN=AaBbCc...==
206
+```
207
+
208
+**Redis URL (optional):**
209
+```env
210
+REDIS_URL=redis://default:[email protected]:port
211
+```
212
+
213
+### 3.3. Configure Redis for Sessions
214
+
215
+Upstash Redis will handle:
216
+- User sessions
217
+- Rate limiting
218
+- Temporary locks
219
+- Cache
220
+
221
+No additional setup needed!
222
+
223
+---
224
+
225
+## 4. Configure Project
226
+
227
+### 4.1. Update Database Configuration
228
+
229
+Create/update `src/db/turso.ts`:
230
+
231
+```typescript
232
+import { drizzle } from 'drizzle-orm/libsql';
233
+import { createClient } from '@libsql/client';
234
+
235
+const turso = createClient({
236
+ url: process.env.TURSO_DATABASE_URL!,
237
+ authToken: process.env.TURSO_AUTH_TOKEN!,
238
+});
239
+
240
+export const db = drizzle(turso);
241
+```
242
+
243
+### 4.2. Update Storage Configuration
244
+
245
+Create/update `src/lib/storage/google-drive.ts`:
246
+
247
+```typescript
248
+import { google } from 'googleapis';
249
+
250
+const auth = new google.auth.GoogleAuth({
251
+ credentials: {
252
+ client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
253
+ private_key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
254
+ },
255
+ scopes: ['https://www.googleapis.com/auth/drive.file'],
256
+});
257
+
258
+const drive = google.drive({ version: 'v3', auth });
259
+const folderId = process.env.GOOGLE_DRIVE_FOLDER_ID;
260
+
261
+export async function uploadFile(fileName: string, content: Buffer) {
262
+ const response = await drive.files.create({
263
+ requestBody: {
264
+ name: fileName,
265
+ parents: [folderId!],
266
+ },
267
+ media: {
268
+ body: content,
269
+ },
270
+ });
271
+
272
+ return response.data.id;
273
+}
274
+
275
+export async function downloadFile(fileId: string) {
276
+ const response = await drive.files.get(
277
+ { fileId, alt: 'media' },
278
+ { responseType: 'arraybuffer' }
279
+ );
280
+
281
+ return Buffer.from(response.data as ArrayBuffer);
282
+}
283
+
284
+export async function deleteFile(fileId: string) {
285
+ await drive.files.delete({ fileId });
286
+}
287
+```
288
+
289
+### 4.3. Update Redis Configuration
290
+
291
+Create/update `src/lib/redis.ts`:
292
+
293
+```typescript
294
+import { Redis } from '@upstash/redis';
295
+
296
+export const redis = new Redis({
297
+ url: process.env.UPSTASH_REDIS_REST_URL!,
298
+ token: process.env.UPSTASH_REDIS_REST_TOKEN!,
299
+});
300
+
301
+// Session management
302
+export async function setSession(sessionId: string, data: any, ttl = 7 * 24 * 60 * 60) {
303
+ await redis.setex(sessionId, ttl, JSON.stringify(data));
304
+}
305
+
306
+export async function getSession(sessionId: string) {
307
+ const data = await redis.get(sessionId);
308
+ return data ? JSON.parse(data as string) : null;
309
+}
310
+
311
+export async function deleteSession(sessionId: string) {
312
+ await redis.del(sessionId);
313
+}
314
+```
315
+
316
+### 4.4. Install Dependencies
317
+
318
+```bash
319
+# Add Turso client
320
+bun add @libsql/client drizzle-orm
321
+
322
+# Add Google Drive API
323
+bun add googleapis
324
+
325
+# Add Upstash Redis
326
+bun add @upstash/redis
327
+```
328
+
329
+### 4.5. Update Environment Variables Template
330
+
331
+Create `.env.example`:
332
+
333
+```env
334
+# === Turso Database ===
335
+TURSO_DATABASE_URL=libsql://your-db.turso.io
336
+TURSO_AUTH_TOKEN=your-token
337
+
338
+# === Google Drive Storage ===
339
+GOOGLE_DRIVE_FOLDER_ID=your-folder-id
340
+GOOGLE_SERVICE_ACCOUNT_EMAIL=your-sa@project.iam.gserviceaccount.com
341
+GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
342
+GOOGLE_CLIENT_ID=your-client-id
343
+
344
+# === Upstash Redis ===
345
+UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io
346
+UPSTASH_REDIS_REST_TOKEN=your-token
347
+
348
+# === Application ===
349
+SITE_URL=https://your-app.vercel.app
350
+JWT_SECRET=generate-a-strong-secret
351
+SESSION_SECRET=generate-another-strong-secret
352
+INTERNAL_HOOK_SECRET=generate-hook-secret
353
+
354
+# === Optional: AI Review ===
355
+AI_PROVIDER=openai
356
+OPENAI_API_KEY=sk-...
357
+```
358
+
359
+### 4.6. Generate Secrets
360
+
361
+```bash
362
+# Generate secrets
363
+openssl rand -base64 32 # JWT_SECRET
364
+openssl rand -base64 32 # SESSION_SECRET
365
+openssl rand -base64 32 # INTERNAL_HOOK_SECRET
366
+```
367
+
368
+---
369
+
370
+## 5. Deploy to Vercel
371
+
372
+### 5.1. Install Vercel CLI (Optional)
373
+
374
+```bash
375
+npm install -g vercel
376
+vercel login
377
+```
378
+
379
+### 5.2. Connect Repository to Vercel
380
+
381
+**Option A: Via Vercel Dashboard**
382
+
383
+1. Go to [Vercel Dashboard](https://vercel.com/dashboard)
384
+2. Click **Add New** β†’ **Project**
385
+3. Import your GitHub repository
386
+4. Select `OpencodeHub`
387
+
388
+**Option B: Via CLI**
389
+
390
+```bash
391
+cd opencodehub
392
+vercel
393
+```
394
+
395
+### 5.3. Configure Environment Variables
396
+
397
+In Vercel Dashboard:
398
+
399
+1. Go to **Project Settings** β†’ **Environment Variables**
400
+2. Add all variables from step 4.5:
401
+
402
+**Turso:**
403
+- `TURSO_DATABASE_URL` = `libsql://opencodehub-xxxx.turso.io`
404
+- `TURSO_AUTH_TOKEN` = `eyJhbG...`
405
+
406
+**Google Drive:**
407
+- `GOOGLE_DRIVE_FOLDER_ID` = `1a2B3c4D5e6F7g8H9i0J`
408
+- `GOOGLE_SERVICE_ACCOUNT_EMAIL` = `opencodehub-storage-sa@...`
409
+- `GOOGLE_PRIVATE_KEY` = Paste entire private key (including `\n`)
410
+- `GOOGLE_CLIENT_ID` = `123456789...`
411
+
412
+**Upstash Redis:**
413
+- `UPSTASH_REDIS_REST_URL` = `https://...upstash.io`
414
+- `UPSTASH_REDIS_REST_TOKEN` = `AaBbCc...`
415
+
416
+**Application:**
417
+- `SITE_URL` = `https://your-project.vercel.app` (update later)
418
+- `JWT_SECRET` = Generated secret
419
+- `SESSION_SECRET` = Generated secret
420
+- `INTERNAL_HOOK_SECRET` = Generated secret
421
+
422
+**AI (Optional):**
423
+- `AI_PROVIDER` = `openai`
424
+- `OPENAI_API_KEY` = `sk-...`
425
+
426
+### 5.4. Configure Build Settings
427
+
428
+In **Project Settings** β†’ **Build & Development Settings**:
429
+
430
+- **Framework Preset:** Astro
431
+- **Build Command:** `bun run build`
432
+- **Output Directory:** `dist`
433
+- **Install Command:** `bun install`
434
+
435
+### 5.5. Deploy!
436
+
437
+Click **Deploy** or run:
438
+
439
+```bash
440
+vercel --prod
441
+```
442
+
443
+**Deployment will:**
444
+1. Install dependencies
445
+2. Build the project
446
+3. Deploy to Vercel Edge Network
447
+4. Provide deployment URL
448
+
449
+---
450
+
451
+## 6. Post-Deployment
452
+
453
+### 6.1. Update SITE_URL
454
+
455
+1. Copy your Vercel deployment URL: `https://opencodehub-abc123.vercel.app`
456
+2. Update environment variable:
457
+ - Go to **Settings** β†’ **Environment Variables**
458
+ - Edit `SITE_URL` = your actual URL
459
+3. **Redeploy** (Vercel Dashboard β†’ Deployments β†’ Redeploy)
460
+
461
+### 6.2. Create Admin User
462
+
463
+**Option A: Via Turso CLI**
464
+
465
+```bash
466
+turso db shell opencodehub
467
+
468
+-- In the Turso shell:
469
+INSERT INTO users (id, email, password_hash, username, role, created_at)
470
+VALUES (
471
+ 'admin-user-id',
472
473
+ '$2b$10$...', -- bcrypt hash of your password
474
+ 'admin',
475
+ 'admin',
476
+ current_timestamp
477
+);
478
+```
479
+
480
+**Generate bcrypt hash:**
481
+```javascript
482
+// Run in Node.js
483
+const bcrypt = require('bcrypt');
484
+const hash = await bcrypt.hash('your-password', 10);
485
+console.log(hash);
486
+```
487
+
488
+**Option B: Via Deployment Script**
489
+
490
+Create `scripts/create-admin-vercel.ts`:
491
+
492
+```typescript
493
+import { db } from '../src/db/turso';
494
+import { users } from '../src/db/schema';
495
+import bcrypt from 'bcrypt';
496
+
497
+const email = process.env.ADMIN_EMAIL || '[email protected]';
498
+const password = process.env.ADMIN_PASSWORD || 'changeme';
499
+
500
+const hash = await bcrypt.hash(password, 10);
501
+
502
+await db.insert(users).values({
503
+ id: crypto.randomUUID(),
504
+ email,
505
+ username: 'admin',
506
+ password_hash: hash,
507
+ role: 'admin',
508
+ createdAt: new Date(),
509
+});
510
+
511
+console.log(`βœ… Admin user created: ${email}`);
512
+```
513
+
514
+Run once:
515
+```bash
516
+[email protected] ADMIN_PASSWORD=strong123 bun scripts/create-admin-vercel.ts
517
+```
518
+
519
+### 6.3. Configure Custom Domain (Optional)
520
+
521
+1. Go to **Settings** β†’ **Domains**
522
+2. Add your custom domain: `git.yourcompany.com`
523
+3. Follow DNS configuration instructions
524
+4. Update `SITE_URL` to your custom domain
525
+
526
+### 6.4. Setup Monitoring
527
+
528
+**Vercel Analytics:**
529
+1. Go to **Analytics** tab
530
+2. Enable **Web Analytics**
531
+3. Enable **Speed Insights**
532
+
533
+**Sentry (Optional):**
534
+```bash
535
+bun add @sentry/astro
536
+```
537
+
538
+Add to `.env`:
539
+```env
540
+SENTRY_DSN=https://[email protected]/...
541
+```
542
+
543
+---
544
+
545
+## 7. Verification
546
+
547
+### 7.1. Test Database Connection
548
+
549
+Visit: `https://your-app.vercel.app/api/health`
550
+
551
+Should return:
552
+```json
553
+{
554
+ "status": "ok",
555
+ "database": "connected",
556
+ "redis": "connected",
557
+ "storage": "configured"
558
+}
559
+```
560
+
561
+### 7.2. Test Storage
562
+
563
+1. Login as admin
564
+2. Create a repository
565
+3. Push a commit
566
+4. Verify file appears in Google Drive folder
567
+
568
+### 7.3. Test Redis
569
+
570
+1. Login
571
+2. Check session persists
572
+3. Logout
573
+4. Session cleared
574
+
575
+### 7.4. Load Test
576
+
577
+```bash
578
+# Install hey
579
+go install github.com/rakyll/hey@latest
580
+
581
+# Test
582
+hey -n 1000 -c 10 https://your-app.vercel.app
583
+```
584
+
585
+Expected: >90% success rate
586
+
587
+---
588
+
589
+## 8. Troubleshooting
590
+
591
+### Database Connection Fails
592
+
593
+**Error:** `Failed to connect to Turso`
594
+
595
+**Solution:**
596
+```bash
597
+# Verify token is valid
598
+turso db tokens validate opencodehub YOUR_TOKEN
599
+
600
+# Regenerate if needed
601
+turso db tokens create opencodehub
602
+```
603
+
604
+Update environment variable and redeploy.
605
+
606
+### Google Drive Upload Fails
607
+
608
+**Error:** `Insufficient permissions`
609
+
610
+**Solution:**
611
+1. Verify service account has Editor access to folder
612
+2. Check private key doesn't have extra spaces/newlines
613
+3. Test locally:
614
+ ```bash
615
+ bun test src/lib/storage/google-drive.test.ts
616
+ ```
617
+
618
+### Redis Connection Timeout
619
+
620
+**Error:** `ETIMEDOUT connecting to Redis`
621
+
622
+**Solution:**
623
+1. Verify Upstash Redis region matches Vercel deployment region
624
+2. Check REST URL and token are correct
625
+3. Test connection:
626
+ ```bash
627
+ curl https://YOUR_REDIS_URL/get/test \
628
+ -H "Authorization: Bearer YOUR_TOKEN"
629
+ ```
630
+
631
+### Build Fails on Vercel
632
+
633
+**Error:** `Command "build" exited with 1`
634
+
635
+**Solution:**
636
+```bash
637
+# Test build locally
638
+bun run build
639
+
640
+# Check for TypeScript errors
641
+bun run typecheck
642
+
643
+# Check environment variables are set
644
+vercel env ls
645
+```
646
+
647
+### Out of Memory
648
+
649
+**Error:** `JavaScript heap out of memory`
650
+
651
+** Solution:**
652
+Upgrade Vercel plan or optimize build:
653
+
654
+```json
655
+// package.json
656
+{
657
+ "scripts": {
658
+ "build": "NODE_OPTIONS=--max_old_space_size=4096 astro build"
659
+ }
660
+}
661
+```
662
+
663
+---
664
+
665
+## Cost Estimation
666
+
667
+### Free Tier Limits
668
+
669
+**Turso:**
670
+- Database: Free up to 8 GB
671
+- Rows: 500M
672
+- Locations: 1 primary + 2 replicas
673
+
674
+**Upstash Redis:**
675
+- Commands: 10,000/day
676
+- Storage: 256 MB
677
+- Bandwidth: 256 MB/month
678
+
679
+**Google Drive:**
680
+- Storage: 15 GB free
681
+- API calls: Unlimited
682
+
683
+**Vercel:**
684
+- Bandwidth: 100 GB/month
685
+- Deployments: Unlimited
686
+- Edge Functions: 100 GB-hrs
687
+
688
+### Estimated Monthly Cost
689
+
690
+**Low traffic (< 1,000 users):**
691
+- All free tier βœ…
692
+
693
+**Medium traffic (1,000-10,000 users):**
694
+- Turso: $0 (within limits)
695
+- Upstash: $10/month (higher tier)
696
+- Google Drive: $2/month (Google One 100GB)
697
+- Vercel: $20/month (Pro plan)
698
+**Total: ~$32/month**
699
+
700
+**High traffic (>10,000 users):**
701
+- Turso: $29/month (Pro)
702
+- Upstash: $50/month (Pro)
703
+- Google Drive: $10/month (Google Workspace)
704
+- Vercel: $20/month (Pro)
705
+**Total: ~$109/month**
706
+
707
+---
708
+
709
+## Performance Optimization
710
+
711
+### Edge Caching
712
+
713
+Add to `vercel.json`:
714
+
715
+```json
716
+{
717
+ "headers": [
718
+ {
719
+ "source": "/assets/(.*)",
720
+ "headers": [
721
+ {
722
+ "key": "Cache-Control",
723
+ "value": "public, max-age=31536000, immutable"
724
+ }
725
+ ]
726
+ }
727
+ ]
728
+}
729
+```
730
+
731
+### Database Connection Pooling
732
+
733
+Turso handles this automatically with libSQL.
734
+
735
+### Redis Caching Strategy
736
+
737
+```typescript
738
+// Cache expensive queries
739
+async function getRepository(id: string) {
740
+ const cached = await redis.get(`repo:${id}`);
741
+ if (cached) return JSON.parse(cached as string);
742
+
743
+ const repo = await db.query.repositories.findFirst({ where: eq(repositories.id, id) });
744
+ await redis.setex(`repo:${id}`, 300, JSON.stringify(repo)); // Cache 5 min
745
+
746
+ return repo;
747
+}
748
+```
749
+
750
+---
751
+
752
+## Security Checklist
753
+
754
+- [ ] All secrets in environment variables (not committed to Git)
755
+- [ ] HTTPS enabled (automatic on Vercel)
756
+- [ ] Database access restricted to Vercel IPs
757
+- [ ] Google Drive folder shared only with service account
758
+- [ ] Redis requires authentication
759
+- [ ] Rate limiting enabled
760
+- [ ] CORS configured properly
761
+- [ ] CSP headers set
762
+
763
+---
764
+
765
+## Next Steps
766
+
767
+1. **Setup CI/CD**: Automatic deployments on push to main
768
+2. **Add Monitoring**: Set up Sentry for error tracking
769
+3. **Configure Backups**: Turso auto-backups + manual exports
770
+4. **Scale Redis**: Upgrade to Global for multi-region
771
+5. **Add CDN**: Use Vercel Edge Network or Cloudflare
772
+
773
+---
774
+
775
+## Additional Resources
776
+
777
+- [Turso Documentation](https://docs.turso.tech)
778
+- [Google Drive API Reference](https://developers.google.com/drive/api/v3/reference)
779
+- [Upstash Redis Docs](https://docs.upstash.com/redis)
780
+- [Vercel Deployment Docs](https://vercel.com/docs)
781
+- [OpenCodeHub GitHub](https://github.com/swadhinbiswas/OpencodeHub)
782
+
783
+---
784
+
785
+## Support
786
+
787
+**Need help?**
788
+- πŸ’¬ [Discord Community](https://discord.gg/opencodehub)
789
+- πŸ› [GitHub Issues](https://github.com/swadhinbiswas/OpencodeHub/issues)
790
+- πŸ“§ [Email Support](mailto:[email protected])
791
+
792
+---
793
+
794
+**Congratulations!** πŸŽ‰ Your OpenCodeHub instance is now running on Vercel with world-class serverless infrastructure!
0
795
β–Όbun.lock
+293-9
10
10
"@astrojs/tailwind": "^5.1.0",
11
11
"@aws-sdk/client-s3": "^3.948.0",
12
12
"@aws-sdk/s3-request-presigner": "^3.948.0",
13
+ "@libsql/client": "^0.15.15",
13
14
"@monaco-editor/react": "^4.6.0",
14
15
"@opentelemetry/auto-instrumentations-node": "^0.67.2",
15
16
"@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
39
40
"@radix-ui/react-toggle": "^1.1.0",
40
41
"@radix-ui/react-toggle-group": "^1.1.0",
41
42
"@radix-ui/react-tooltip": "^1.1.2",
43
+ "@types/nodemailer": "^7.0.4",
44
+ "@upstash/redis": "^1.36.0",
42
45
"astro": "^4.11.0",
43
46
"bcryptjs": "^2.4.3",
44
47
"better-sqlite3": "^11.1.2",
46
49
"class-variance-authority": "^0.7.0",
47
50
"clsx": "^2.1.1",
48
51
"cmdk": "^1.1.1",
52
+ "cobe": "^0.6.5",
49
53
"dockerode": "^4.0.2",
50
54
"drizzle-orm": "^0.45.1",
51
- "framer-motion": "^11.2.12",
55
+ "framer-motion": "^11.18.2",
52
56
"github-markdown-css": "^5.8.1",
57
+ "googleapis": "^169.0.0",
53
58
"ioredis": "^5.8.2",
54
59
"isomorphic-git": "^1.25.10",
55
60
"jose": "^5.6.3",
60
65
"mysql2": "^3.15.3",
61
66
"nanoid": "^5.0.7",
62
67
"nodegit": "^0.28.0-alpha.27",
68
+ "nodemailer": "^7.0.12",
63
69
"openpgp": "^6.3.0",
64
70
"otplib": "^12.0.1",
65
71
"pg": "^8.16.3",
76
82
"remark-rehype": "^11.1.2",
77
83
"simple-git": "^3.25.0",
78
84
"ssh2": "^1.15.0",
79
- "tailwind-merge": "^2.3.0",
85
+ "tailwind-merge": "^2.6.0",
80
86
"tailwindcss-animate": "^1.0.7",
81
87
"three": "^0.182.0",
82
88
"unified": "^11.0.5",
84
90
"zustand": "^4.5.4",
85
91
},
86
92
"devDependencies": {
93
+ "@faker-js/faker": "^10.1.0",
87
94
"@playwright/test": "^1.57.0",
88
95
"@tailwindcss/typography": "^0.5.19",
89
96
"@types/bcryptjs": "^2.4.6",
91
98
"@types/dockerode": "^3.3.29",
92
99
"@types/js-yaml": "^4.0.9",
93
100
"@types/node": "^20.14.9",
94
- "@types/pg": "^8.11.6",
101
+ "@types/pg": "^8.16.0",
95
102
"@types/react": "^18.3.3",
96
103
"@types/react-dom": "^18.3.0",
97
104
"@types/ssh2": "^1.15.0",
105
+ "@types/three": "^0.182.0",
98
106
"autoprefixer": "^10.4.19",
99
107
"drizzle-kit": "^0.31.8",
100
108
"pino-pretty": "^13.1.3",
140
148
141
149
"@aws-sdk/client-s3": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-node": "3.948.0", "@aws-sdk/middleware-bucket-endpoint": "3.936.0", "@aws-sdk/middleware-expect-continue": "3.936.0", "@aws-sdk/middleware-flexible-checksums": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-location-constraint": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-sdk-s3": "3.947.0", "@aws-sdk/middleware-ssec": "3.936.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/signature-v4-multi-region": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-blob-browser": "^4.2.6", "@smithy/hash-node": "^4.2.5", "@smithy/hash-stream-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/md5-js": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-uvEjds8aYA9SzhBS8RKDtsDUhNV9VhqKiHTcmvhM7gJO92q0WTn8/QeFTdNyLc6RxpiDyz+uBxS7PcdNiZzqfA=="],
142
150
151
+ "@aws-sdk/client-sesv2": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/credential-provider-node": "3.958.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/signature-v4-multi-region": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-3x3n8IIxIMAkdpt9wy9zS7MO2lqTcJwQTdHMn6BlD7YUohb+r5Q4KCOEQ2uHWd4WIJv2tlbXnfypHaXReO/WXA=="],
152
+
143
153
"@aws-sdk/client-sso": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iWjchXy8bIAVBUsKnbfKYXRwhLgRg3EqCQ5FTr3JbR+QR75rZm4ZOYXlvHGztVTmtAZ+PQVA1Y4zO7v7N87C0A=="],
144
154
145
155
"@aws-sdk/core": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="],
258
268
259
269
"@balena/dockerignore": ["@balena/[email protected]", "", {}, "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="],
260
270
271
+ "@dimforge/rapier3d-compat": ["@dimforge/[email protected]", "", {}, "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow=="],
272
+
261
273
"@drizzle-team/brocli": ["@drizzle-team/[email protected]", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
262
274
263
275
"@emnapi/runtime": ["@emnapi/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="],
318
330
319
331
"@esbuild/win32-x64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
320
332
333
+ "@faker-js/faker": ["@faker-js/[email protected]", "", {}, "sha512-C3mrr3b5dRVlKPJdfrAXS8+dq+rq8Qm5SNRazca0JKgw1HQERFmrVb0towvMmw5uu8hHKNiQasMaR/tydf3Zsg=="],
334
+
321
335
"@floating-ui/core": ["@floating-ui/[email protected]", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
322
336
323
337
"@floating-ui/dom": ["@floating-ui/[email protected]", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="],
392
406
393
407
"@kwsites/promise-deferred": ["@kwsites/[email protected]", "", {}, "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="],
394
408
409
+ "@libsql/client": ["@libsql/[email protected]", "", { "dependencies": { "@libsql/core": "^0.15.14", "@libsql/hrana-client": "^0.7.0", "js-base64": "^3.7.5", "libsql": "^0.5.22", "promise-limit": "^2.7.0" } }, "sha512-twC0hQxPNHPKfeOv3sNT6u2pturQjLcI+CnpTM0SjRpocEGgfiZ7DWKXLNnsothjyJmDqEsBQJ5ztq9Wlu470w=="],
410
+
411
+ "@libsql/core": ["@libsql/[email protected]", "", { "dependencies": { "js-base64": "^3.7.5" } }, "sha512-C88Z6UKl+OyuKKPwz224riz02ih/zHYI3Ho/LAcVOgjsunIRZoBw7fjRfaH9oPMmSNeQfhGklSG2il1URoOIsA=="],
412
+
413
+ "@libsql/darwin-arm64": ["@libsql/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4B8ZlX3nIDPndfct7GNe0nI3Yw6ibocEicWdC4fvQbSs/jdq/RC2oCsoJxJ4NzXkvktX70C1J4FcmmoBy069UA=="],
414
+
415
+ "@libsql/darwin-x64": ["@libsql/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-ny2HYWt6lFSIdNFzUFIJ04uiW6finXfMNJ7wypkAD8Pqdm6nAByO+Fdqu8t7sD0sqJGeUCiOg480icjyQ2/8VA=="],
416
+
417
+ "@libsql/hrana-client": ["@libsql/[email protected]", "", { "dependencies": { "@libsql/isomorphic-fetch": "^0.3.1", "@libsql/isomorphic-ws": "^0.1.5", "js-base64": "^3.7.5", "node-fetch": "^3.3.2" } }, "sha512-OF8fFQSkbL7vJY9rfuegK1R7sPgQ6kFMkDamiEccNUvieQ+3urzfDFI616oPl8V7T9zRmnTkSjMOImYCAVRVuw=="],
418
+
419
+ "@libsql/isomorphic-fetch": ["@libsql/[email protected]", "", {}, "sha512-6kK3SUK5Uu56zPq/Las620n5aS9xJq+jMBcNSOmjhNf/MUvdyji4vrMTqD7ptY7/4/CAVEAYDeotUz60LNQHtw=="],
420
+
421
+ "@libsql/isomorphic-ws": ["@libsql/[email protected]", "", { "dependencies": { "@types/ws": "^8.5.4", "ws": "^8.13.0" } }, "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg=="],
422
+
423
+ "@libsql/linux-arm-gnueabihf": ["@libsql/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-3Uo3SoDPJe/zBnyZKosziRGtszXaEtv57raWrZIahtQDsjxBVjuzYQinCm9LRCJCUT5t2r5Z5nLDPJi2CwZVoA=="],
424
+
425
+ "@libsql/linux-arm-musleabihf": ["@libsql/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-LCsXh07jvSojTNJptT9CowOzwITznD+YFGGW+1XxUr7fS+7/ydUrpDfsMX7UqTqjm7xG17eq86VkWJgHJfvpNg=="],
426
+
427
+ "@libsql/linux-arm64-gnu": ["@libsql/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-KSdnOMy88c9mpOFKUEzPskSaF3VLflfSUCBwas/pn1/sV3pEhtMF6H8VUCd2rsedwoukeeCSEONqX7LLnQwRMA=="],
428
+
429
+ "@libsql/linux-arm64-musl": ["@libsql/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-mCHSMAsDTLK5YH//lcV3eFEgiR23Ym0U9oEvgZA0667gqRZg/2px+7LshDvErEKv2XZ8ixzw3p1IrBzLQHGSsw=="],
430
+
431
+ "@libsql/linux-x64-gnu": ["@libsql/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-kNBHaIkSg78Y4BqAdgjcR2mBilZXs4HYkAmi58J+4GRwDQZh5fIUWbnQvB9f95DkWUIGVeenqLRFY2pcTmlsew=="],
432
+
433
+ "@libsql/linux-x64-musl": ["@libsql/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-UZ4Xdxm4pu3pQXjvfJiyCzZop/9j/eA2JjmhMaAhe3EVLH2g11Fy4fwyUp9sT1QJYR1kpc2JLuybPM0kuXv/Tg=="],
434
+
435
+ "@libsql/win32-x64-msvc": ["@libsql/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-Fj0j8RnBpo43tVZUVoNK6BV/9AtDUM5S7DF3LB4qTYg1LMSZqi3yeCneUTLJD6XomQJlZzbI4mst89yspVSAnA=="],
436
+
395
437
"@mapbox/node-pre-gyp": ["@mapbox/[email protected]", "", { "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", "node-fetch": "^2.6.7", "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg=="],
396
438
397
439
"@monaco-editor/loader": ["@monaco-editor/[email protected]", "", { "dependencies": { "state-local": "^1.0.6" } }, "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA=="],
410
452
411
453
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
412
454
455
+ "@neon-rs/load": ["@neon-rs/[email protected]", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="],
456
+
413
457
"@nodelib/fs.scandir": ["@nodelib/[email protected]", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
414
458
415
459
"@nodelib/fs.stat": ["@nodelib/[email protected]", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
896
940
897
941
"@turf/invariant": ["@turf/[email protected]", "", { "dependencies": { "@turf/helpers": "7.3.1", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-IdZJfDjIDCLH+Gu2yLFoSM7H23sdetIo5t4ET1/25X8gi3GE2XSqbZwaGjuZgNh02nisBewLqNiJs2bo+hrqZA=="],
898
942
899
- "@tweenjs/tween.js": ["@tweenjs/[email protected]", "", {}, "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A=="],
943
+ "@tweenjs/tween.js": ["@tweenjs/[email protected]", "", {}, "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA=="],
900
944
901
945
"@types/aws-lambda": ["@types/[email protected]", "", {}, "sha512-SAP22WSGNN12OQ8PlCzGzRCZ7QDCwI85dQZbmpz7+mAk+L7j+wI7qnvmdKh+o7A5LaOp6QnOZ2NJphAZQTTHQg=="],
902
946
950
994
951
995
"@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg=="],
952
996
997
+ "@types/nodemailer": ["@types/[email protected]", "", { "dependencies": { "@aws-sdk/client-sesv2": "^3.839.0", "@types/node": "*" } }, "sha512-ee8fxWqOchH+Hv6MDDNNy028kwvVnLplrStm4Zf/3uHWw5zzo8FoYYeffpJtGs2wWysEumMH0ZIdMGMY1eMAow=="],
998
+
953
999
"@types/oracledb": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ=="],
954
1000
955
1001
"@types/pg": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ=="],
964
1010
965
1011
"@types/ssh2": ["@types/[email protected]", "", { "dependencies": { "@types/node": "^18.11.18" } }, "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ=="],
966
1012
1013
+ "@types/stats.js": ["@types/[email protected]", "", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="],
1014
+
967
1015
"@types/tedious": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="],
968
1016
1017
+ "@types/three": ["@types/[email protected]", "", { "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": ">=0.5.17", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~0.22.0" } }, "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q=="],
1018
+
969
1019
"@types/unist": ["@types/[email protected]", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
970
1020
1021
+ "@types/webxr": ["@types/[email protected]", "", {}, "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg=="],
1022
+
1023
+ "@types/ws": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
1024
+
971
1025
"@ungap/structured-clone": ["@ungap/[email protected]", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
972
1026
1027
+ "@upstash/redis": ["@upstash/[email protected]", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-9zN2UV9QJGPnXfWU3yZBLVQaqqENDh7g+Y4J2vJuSxBCi9FQ0aUOtaXlzuFhnsiZvCqM+eS27ic+tgmkWUsfOg=="],
1028
+
973
1029
"@vitejs/plugin-react": ["@vitejs/[email protected]", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
974
1030
975
1031
"@vitest/expect": ["@vitest/[email protected]", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.15", "@vitest/utils": "4.0.15", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w=="],
986
1042
987
1043
"@vitest/utils": ["@vitest/[email protected]", "", { "dependencies": { "@vitest/pretty-format": "4.0.15", "tinyrainbow": "^3.0.3" } }, "sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA=="],
988
1044
1045
+ "@webgpu/types": ["@webgpu/[email protected]", "", {}, "sha512-3ab1B59Ojb6RwjOspYLsTpCzbNB3ZaamIAxBMmvnNkiDoLTZUOBXZ9p5nAYVEkQlDdf6qAZWi1pqj9+ypiqznA=="],
1046
+
989
1047
"abbrev": ["[email protected]", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="],
990
1048
991
1049
"abort-controller": ["[email protected]", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
1088
1146
1089
1147
"buffer": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
1090
1148
1149
+ "buffer-equal-constant-time": ["[email protected]", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
1150
+
1091
1151
"buffer-from": ["[email protected]", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
1092
1152
1093
1153
"buildcheck": ["[email protected]", "", {}, "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA=="],
1152
1212
1153
1213
"cmdk": ["[email protected]", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="],
1154
1214
1215
+ "cobe": ["[email protected]", "", { "dependencies": { "phenomenon": "^1.6.0" } }, "sha512-MA8bu81EFY6JjQpj+FovEuhyJ25khx2Q7Lh+ot/UkCJe5yKyDgzdc6u2lGZIOmsZTXK6Itg1i4lQZIJZbPWnAg=="],
1216
+
1155
1217
"color": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
1156
1218
1157
1219
"color-convert": ["[email protected]", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
1216
1278
1217
1279
"data-bind-mapper": ["[email protected]", "", { "dependencies": { "accessor-fn": "1" } }, "sha512-QmU3lyEnbENQPo0M1F9BMu4s6cqNNp8iJA+b/HP2sSb7pf3dxwF3+EP1eO69rwBfH9kFJ1apmzrtogAmVt2/Xw=="],
1218
1280
1281
+ "data-uri-to-buffer": ["[email protected]", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
1282
+
1219
1283
"dateformat": ["[email protected]", "", {}, "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA=="],
1220
1284
1221
1285
"debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
1238
1302
1239
1303
"destroy": ["[email protected]", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="],
1240
1304
1241
- "detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
1305
+ "detect-libc": ["[email protected]", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="],
1242
1306
1243
1307
"detect-node-es": ["[email protected]", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
1244
1308
1272
1336
1273
1337
"eastasianwidth": ["[email protected]", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
1274
1338
1339
+ "ecdsa-sig-formatter": ["[email protected]", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
1340
+
1275
1341
"ee-first": ["[email protected]", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
1276
1342
1277
1343
"electron-to-chromium": ["[email protected]", "", {}, "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="],
1348
1414
1349
1415
"fdir": ["[email protected]", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
1350
1416
1417
+ "fetch-blob": ["[email protected]", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
1418
+
1419
+ "fflate": ["[email protected]", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
1420
+
1351
1421
"file-uri-to-path": ["[email protected]", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
1352
1422
1353
1423
"fill-range": ["[email protected]", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
1368
1438
1369
1439
"form-data-encoder": ["[email protected]", "", {}, "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw=="],
1370
1440
1441
+ "formdata-polyfill": ["[email protected]", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
1442
+
1371
1443
"forwarded-parse": ["[email protected]", "", {}, "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw=="],
1372
1444
1373
1445
"fraction.js": ["[email protected]", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
1388
1460
1389
1461
"function-bind": ["[email protected]", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
1390
1462
1391
- "gaxios": ["[email protected]", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
1463
+ "gaxios": ["[email protected]", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="],
1392
1464
1393
1465
"gcp-metadata": ["[email protected]", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="],
1394
1466
1422
1494
1423
1495
"globe.gl": ["[email protected]", "", { "dependencies": { "@tweenjs/tween.js": "18 - 25", "accessor-fn": "1", "kapsule": "^1.16", "three": ">=0.154 <1", "three-globe": "^2.45", "three-render-objects": "^1.40" } }, "sha512-fjkLHVBrnbESkUgklTd4UbcGLciu4nIl49IIi1hclLjI6MU3ASu6JYmf/K5qwPf7I+tNOauQRr4i5Y28JTtHQg=="],
1424
1496
1425
- "google-logging-utils": ["[email protected]", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
1497
+ "google-auth-library": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="],
1498
+
1499
+ "google-logging-utils": ["[email protected]", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
1500
+
1501
+ "googleapis": ["[email protected]", "", { "dependencies": { "google-auth-library": "^10.2.0", "googleapis-common": "^8.0.0" } }, "sha512-IOGMG8tljCZSLvYgdojRu6mB10KEsK0J7X62sXXlQz9koe5BUAW+rqkY3qhQM9wXM6hVL3/Hase7XbxoMyeYiQ=="],
1502
+
1503
+ "googleapis-common": ["[email protected]", "", { "dependencies": { "extend": "^3.0.2", "gaxios": "^7.0.0-rc.4", "google-auth-library": "^10.1.0", "qs": "^6.7.0", "url-template": "^2.0.8" } }, "sha512-eCzNACUXPb1PW5l0ULTzMHaL/ltPRADoPgjBlT8jWsTbxkCp6siv+qKJ/1ldaybCthGwsYFYallF7u9AkU4L+A=="],
1426
1504
1427
1505
"gopd": ["[email protected]", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
1428
1506
1432
1510
1433
1511
"gray-matter": ["[email protected]", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
1434
1512
1513
+ "gtoken": ["[email protected]", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
1514
+
1435
1515
"h3-js": ["[email protected]", "", {}, "sha512-DvJh07MhGgY2KcC4OeZc8SSyA+ZXpdvoh6uCzGpoKvWtZxJB+g6VXXC1+eWYkaMIsLz7J/ErhOalHCpcs1KYog=="],
1436
1516
1437
1517
"has-property-descriptors": ["[email protected]", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
1556
1636
1557
1637
"joycon": ["[email protected]", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="],
1558
1638
1639
+ "js-base64": ["[email protected]", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="],
1640
+
1559
1641
"js-tokens": ["[email protected]", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
1560
1642
1561
1643
"js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
1568
1650
1569
1651
"jsonfile": ["[email protected]", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
1570
1652
1653
+ "jwa": ["[email protected]", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
1654
+
1655
+ "jws": ["[email protected]", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
1656
+
1571
1657
"kapsule": ["[email protected]", "", { "dependencies": { "lodash-es": "4" } }, "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg=="],
1572
1658
1573
1659
"keyv": ["[email protected]", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ=="],
1576
1662
1577
1663
"kleur": ["[email protected]", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
1578
1664
1665
+ "libsql": ["[email protected]", "", { "dependencies": { "@neon-rs/load": "^0.0.4", "detect-libc": "2.0.2" }, "optionalDependencies": { "@libsql/darwin-arm64": "0.5.22", "@libsql/darwin-x64": "0.5.22", "@libsql/linux-arm-gnueabihf": "0.5.22", "@libsql/linux-arm-musleabihf": "0.5.22", "@libsql/linux-arm64-gnu": "0.5.22", "@libsql/linux-arm64-musl": "0.5.22", "@libsql/linux-x64-gnu": "0.5.22", "@libsql/linux-x64-musl": "0.5.22", "@libsql/win32-x64-msvc": "0.5.22" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "arm", "x64", "arm64", ] }, "sha512-NscWthMQt7fpU8lqd7LXMvT9pi+KhhmTHAJWUB/Lj6MWa0MKFv0F2V4C6WKKpjCVZl0VwcDz4nOI3CyaT1DDiA=="],
1666
+
1579
1667
"lilconfig": ["[email protected]", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
1580
1668
1581
1669
"lines-and-columns": ["[email protected]", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
1652
1740
1653
1741
"merge2": ["[email protected]", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
1654
1742
1743
+ "meshoptimizer": ["[email protected]", "", {}, "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg=="],
1744
+
1655
1745
"micromark": ["[email protected]", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
1656
1746
1657
1747
"micromark-core-commonmark": ["[email protected]", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
1776
1866
1777
1867
"node-abort-controller": ["[email protected]", "", {}, "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="],
1778
1868
1779
- "node-fetch": ["[email protected]", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
1869
+ "node-domexception": ["[email protected]", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
1870
+
1871
+ "node-fetch": ["[email protected]", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
1780
1872
1781
1873
"node-gyp": ["[email protected]", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^14.0.3", "nopt": "^8.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", "tar": "^7.4.3", "tinyglobby": "^0.2.12", "which": "^5.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ=="],
1782
1874
1786
1878
1787
1879
"nodegit": ["[email protected]", "", { "dependencies": { "@axosoft/nan": "^2.22.0-gk.1", "@mapbox/node-pre-gyp": "^2.0.0", "fs-extra": "^7.0.0", "got": "^14.4.7", "json5": "^2.1.0", "lodash": "^4.17.14", "node-gyp": "^11.2.0", "tar-fs": "^3.0.9" } }, "sha512-aHNrupRANyg0oy8dlHhG3Ipyd7Fh2nDOV1apPDXgRoM4gxo1icfHdMJmLUvCCCan9nUmz+wH7nEUTSsn7QLDuA=="],
1788
1880
1881
+ "nodemailer": ["[email protected]", "", {}, "sha512-H+rnK5bX2Pi/6ms3sN4/jRQvYSMltV6vqup/0SFOrxYYY/qoNvhXPlYq3e+Pm9RFJRwrMGbMIwi81M4dxpomhA=="],
1882
+
1789
1883
"nopt": ["[email protected]", "", { "dependencies": { "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="],
1790
1884
1791
1885
"normalize-path": ["[email protected]", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
1798
1892
1799
1893
"object-hash": ["[email protected]", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
1800
1894
1895
+ "object-inspect": ["[email protected]", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
1896
+
1801
1897
"obug": ["[email protected]", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
1802
1898
1803
1899
"on-exit-leak-free": ["[email protected]", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="],
1864
1960
1865
1961
"pgpass": ["[email protected]", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
1866
1962
1963
+ "phenomenon": ["[email protected]", "", {}, "sha512-7h9/fjPD3qNlgggzm88cY58l9sudZ6Ey+UmZsizfhtawO6E3srZQXywaNm2lBwT72TbpHYRPy7ytIHeBUD/G0A=="],
1964
+
1867
1965
"picocolors": ["[email protected]", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
1868
1966
1869
1967
"picomatch": ["[email protected]", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
1932
2030
1933
2031
"prom-client": ["[email protected]", "", { "dependencies": { "@opentelemetry/api": "^1.4.0", "tdigest": "^0.1.1" } }, "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g=="],
1934
2032
2033
+ "promise-limit": ["[email protected]", "", {}, "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw=="],
2034
+
1935
2035
"promise-retry": ["[email protected]", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="],
1936
2036
1937
2037
"prompts": ["[email protected]", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
1944
2044
1945
2045
"pump": ["[email protected]", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
1946
2046
2047
+ "qs": ["[email protected]", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
2048
+
1947
2049
"queue-microtask": ["[email protected]", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
1948
2050
1949
2051
"quick-format-unescaped": ["[email protected]", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
2036
2138
2037
2139
"reusify": ["[email protected]", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
2038
2140
2141
+ "rimraf": ["[email protected]", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
2142
+
2039
2143
"robust-predicates": ["[email protected]", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
2040
2144
2041
2145
"rollup": ["[email protected]", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="],
2076
2180
2077
2181
"shiki": ["[email protected]", "", { "dependencies": { "@shikijs/core": "1.29.2", "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/langs": "1.29.2", "@shikijs/themes": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg=="],
2078
2182
2183
+ "side-channel": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
2184
+
2185
+ "side-channel-list": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
2186
+
2187
+ "side-channel-map": ["[email protected]", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
2188
+
2189
+ "side-channel-weakmap": ["[email protected]", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
2190
+
2079
2191
"siginfo": ["[email protected]", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
2080
2192
2081
2193
"signal-exit": ["[email protected]", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
2234
2346
2235
2347
"ultrahtml": ["[email protected]", "", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="],
2236
2348
2349
+ "uncrypto": ["[email protected]", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
2350
+
2237
2351
"undici-types": ["[email protected]", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
2238
2352
2239
2353
"unified": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
2264
2378
2265
2379
"update-browserslist-db": ["[email protected]", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA=="],
2266
2380
2381
+ "url-template": ["[email protected]", "", {}, "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="],
2382
+
2267
2383
"use-callback-ref": ["[email protected]", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
2268
2384
2269
2385
"use-sidecar": ["[email protected]", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
2288
2404
2289
2405
"web-namespaces": ["[email protected]", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
2290
2406
2407
+ "web-streams-polyfill": ["[email protected]", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
2408
+
2291
2409
"webidl-conversions": ["[email protected]", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
2292
2410
2293
2411
"whatwg-url": ["[email protected]", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
2310
2428
2311
2429
"wrappy": ["[email protected]", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
2312
2430
2431
+ "ws": ["[email protected]", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
2432
+
2313
2433
"xtend": ["[email protected]", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
2314
2434
2315
2435
"xxhash-wasm": ["[email protected]", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
2346
2466
2347
2467
"@aws-crypto/util/@smithy/util-utf8": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
2348
2468
2469
+ "@aws-sdk/client-sesv2/@aws-sdk/core": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws-sdk/xml-builder": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DrZgDnF1lQZv75a52nFWs6MExihJF2GZB6ETZRqr6jMwhrk2kbJPUtvgbifwcL7AYmVqHQDJBrR/MqkwwFCpiw=="],
2470
+
2471
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.957.0", "@aws-sdk/credential-provider-http": "3.957.0", "@aws-sdk/credential-provider-ini": "3.958.0", "@aws-sdk/credential-provider-process": "3.957.0", "@aws-sdk/credential-provider-sso": "3.958.0", "@aws-sdk/credential-provider-web-identity": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-vdoZbNG2dt66I7EpN3fKCzi6fp9xjIiwEA/vVVgqO4wXCGw8rKPIdDUus4e13VvTr330uQs2W0UNg/7AgtquEQ=="],
2472
+
2473
+ "@aws-sdk/client-sesv2/@aws-sdk/middleware-host-header": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-BBgKawVyfQZglEkNTuBBdC3azlyqNXsvvN4jPkWAiNYcY0x1BasaJFl+7u/HisfULstryweJq/dAvIZIxzlZaA=="],
2474
+
2475
+ "@aws-sdk/client-sesv2/@aws-sdk/middleware-logger": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-w1qfKrSKHf9b5a8O76yQ1t69u6NWuBjr5kBX+jRWFx/5mu6RLpqERXRpVJxfosbep7k3B+DSB5tZMZ82GKcJtQ=="],
2476
+
2477
+ "@aws-sdk/client-sesv2/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-D2H/WoxhAZNYX+IjkKTdOhOkWQaK0jjJrDBj56hKjU5c9ltQiaX/1PqJ4dfjHntEshJfu0w+E6XJ+/6A6ILBBA=="],
2478
+
2479
+ "@aws-sdk/client-sesv2/@aws-sdk/middleware-user-agent": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-50vcHu96XakQnIvlKJ1UoltrFODjsq2KvtTgHiPFteUS884lQnK5VC/8xd1Msz/1ONpLMzdCVproCQqhDTtMPQ=="],
2480
+
2481
+ "@aws-sdk/client-sesv2/@aws-sdk/region-config-resolver": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-V8iY3blh8l2iaOqXWW88HbkY5jDoWjH56jonprG/cpyqqCnprvpMUZWPWYJoI8rHRf2bqzZeql1slxG6EnKI7A=="],
2482
+
2483
+ "@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-t6UfP1xMUigMMzHcb7vaZcjv7dA2DQkk9C/OAP1dKyrE0vb4lFGDaTApi17GN6Km9zFxJthEMUbBc7DL0hq1Bg=="],
2484
+
2485
+ "@aws-sdk/client-sesv2/@aws-sdk/types": ["@aws-sdk/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-wzWC2Nrt859ABk6UCAVY/WYEbAd7FjkdrQL6m24+tfmWYDNRByTJ9uOgU/kw9zqLCAwb//CPvrJdhqjTznWXAg=="],
2486
+
2487
+ "@aws-sdk/client-sesv2/@aws-sdk/util-endpoints": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-endpoints": "^3.2.7", "tslib": "^2.6.2" } }, "sha512-xwF9K24mZSxcxKS3UKQFeX/dPYkEps9wF1b+MGON7EvnbcucrJGyQyK1v1xFPn1aqXkBTFi+SZaMRx5E5YCVFw=="],
2488
+
2489
+ "@aws-sdk/client-sesv2/@aws-sdk/util-user-agent-browser": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-exueuwxef0lUJRnGaVkNSC674eAiWU07ORhxBnevFFZEKisln+09Qrtw823iyv5I1N8T+wKfh95xvtWQrNKNQw=="],
2490
+
2491
+ "@aws-sdk/client-sesv2/@aws-sdk/util-user-agent-node": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-ycbYCwqXk4gJGp0Oxkzf2KBeeGBdTxz559D41NJP8FlzSej1Gh7Rk40Zo6AyTfsNWkrl/kVi1t937OIzC5t+9Q=="],
2492
+
2493
+ "@aws-sdk/client-sesv2/@smithy/config-resolver": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg=="],
2494
+
2495
+ "@aws-sdk/client-sesv2/@smithy/core": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.8", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ=="],
2496
+
2497
+ "@aws-sdk/client-sesv2/@smithy/fetch-http-handler": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg=="],
2498
+
2499
+ "@aws-sdk/client-sesv2/@smithy/hash-node": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw=="],
2500
+
2501
+ "@aws-sdk/client-sesv2/@smithy/invalid-dependency": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ=="],
2502
+
2503
+ "@aws-sdk/client-sesv2/@smithy/middleware-content-length": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg=="],
2504
+
2505
+ "@aws-sdk/client-sesv2/@smithy/middleware-endpoint": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/core": "^3.20.0", "@smithy/middleware-serde": "^4.2.8", "@smithy/node-config-provider": "^4.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-middleware": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg=="],
2506
+
2507
+ "@aws-sdk/client-sesv2/@smithy/middleware-retry": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/service-error-classification": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg=="],
2508
+
2509
+ "@aws-sdk/client-sesv2/@smithy/middleware-serde": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w=="],
2510
+
2511
+ "@aws-sdk/client-sesv2/@smithy/middleware-stack": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw=="],
2512
+
2513
+ "@aws-sdk/client-sesv2/@smithy/node-config-provider": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw=="],
2514
+
2515
+ "@aws-sdk/client-sesv2/@smithy/node-http-handler": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/abort-controller": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ=="],
2516
+
2517
+ "@aws-sdk/client-sesv2/@smithy/protocol-http": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA=="],
2518
+
2519
+ "@aws-sdk/client-sesv2/@smithy/smithy-client": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/core": "^3.20.0", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-stack": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-stream": "^4.5.8", "tslib": "^2.6.2" } }, "sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g=="],
2520
+
2521
+ "@aws-sdk/client-sesv2/@smithy/types": ["@smithy/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA=="],
2522
+
2523
+ "@aws-sdk/client-sesv2/@smithy/url-parser": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg=="],
2524
+
2525
+ "@aws-sdk/client-sesv2/@smithy/util-defaults-mode-browser": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/property-provider": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ=="],
2526
+
2527
+ "@aws-sdk/client-sesv2/@smithy/util-defaults-mode-node": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/config-resolver": "^4.4.5", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA=="],
2528
+
2529
+ "@aws-sdk/client-sesv2/@smithy/util-endpoints": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg=="],
2530
+
2531
+ "@aws-sdk/client-sesv2/@smithy/util-middleware": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w=="],
2532
+
2533
+ "@aws-sdk/client-sesv2/@smithy/util-retry": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg=="],
2534
+
2349
2535
"@babel/core/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
2350
2536
2351
2537
"@babel/helper-compilation-targets/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
2358
2544
2359
2545
"@isaacs/cliui/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
2360
2546
2547
+ "@mapbox/node-pre-gyp/detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
2548
+
2549
+ "@mapbox/node-pre-gyp/node-fetch": ["[email protected]", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
2550
+
2361
2551
"@npmcli/agent/lru-cache": ["[email protected]", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
2362
2552
2363
2553
"@opentelemetry/instrumentation-pg/@types/pg": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ=="],
2434
2624
2435
2625
"fast-glob/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
2436
2626
2437
- "gaxios/uuid": ["[email protected]", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
2627
+ "gcp-metadata/gaxios": ["[email protected]", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
2628
+
2629
+ "gcp-metadata/google-logging-utils": ["[email protected]", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
2438
2630
2439
2631
"get-stream/is-stream": ["[email protected]", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
2440
2632
2633
+ "globe.gl/@tweenjs/tween.js": ["@tweenjs/[email protected]", "", {}, "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A=="],
2634
+
2635
+ "google-auth-library/gcp-metadata": ["[email protected]", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
2636
+
2441
2637
"got/decompress-response": ["[email protected]", "", { "dependencies": { "mimic-response": "^4.0.0" } }, "sha512-oj7KWToJuuxlPr7VV0vabvxEIiqNMo+q0NueIiL3XhtwC6FVOX7Hr1c0C4eD0bmf7Zr+S/dSf2xvkH3Ad6sU3Q=="],
2442
2638
2443
2639
"gray-matter/js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
2456
2652
2457
2653
"minipass-sized/minipass": ["[email protected]", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
2458
2654
2655
+ "node-gyp-build-optional-packages/detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
2656
+
2459
2657
"nodegit/tar-fs": ["[email protected]", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="],
2460
2658
2461
2659
"p-locate/p-limit": ["[email protected]", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
2468
2666
2469
2667
"postcss-nested/postcss-selector-parser": ["[email protected]", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
2470
2668
2669
+ "prebuild-install/detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
2670
+
2471
2671
"prompts/kleur": ["[email protected]", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
2472
2672
2473
2673
"rc/strip-json-comments": ["[email protected]", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
2480
2680
2481
2681
"send/debug": ["[email protected]", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
2482
2682
2683
+ "sharp/detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
2684
+
2483
2685
"string-width-cjs/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
2484
2686
2485
2687
"string-width-cjs/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
2492
2694
2493
2695
"tar-stream/readable-stream": ["[email protected]", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
2494
2696
2697
+ "three-globe/@tweenjs/tween.js": ["@tweenjs/[email protected]", "", {}, "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A=="],
2698
+
2699
+ "three-render-objects/@tweenjs/tween.js": ["@tweenjs/[email protected]", "", {}, "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A=="],
2700
+
2495
2701
"vite/fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
2496
2702
2497
2703
"wrap-ansi-cjs/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
2512
2718
2513
2719
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
2514
2720
2721
+ "@aws-sdk/client-sesv2/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-Ai5iiQqS8kJ5PjzMhWcLKN0G2yasAkvpnPlq2EnqlIMdB48HsizElt62qcktdxp4neRMyGkFq4NzgmDbXnhRiA=="],
2722
+
2723
+ "@aws-sdk/client-sesv2/@aws-sdk/core/@smithy/property-provider": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
2724
+
2725
+ "@aws-sdk/client-sesv2/@aws-sdk/core/@smithy/signature-v4": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg=="],
2726
+
2727
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-475mkhGaWCr+Z52fOOVb/q2VHuNvqEDixlYIkeaO6xJ6t9qR0wpLt4hOQaR6zR1wfZV0SlE7d8RErdYq/PByog=="],
2728
+
2729
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-stream": "^4.5.8", "tslib": "^2.6.2" } }, "sha512-8dS55QHRxXgJlHkEYaCGZIhieCs9NU1HU1BcqQ4RfUdSsfRdxxktqUKgCnBnOOn0oD3PPA8cQOCAVgIyRb3Rfw=="],
2730
+
2731
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/credential-provider-env": "3.957.0", "@aws-sdk/credential-provider-http": "3.957.0", "@aws-sdk/credential-provider-login": "3.958.0", "@aws-sdk/credential-provider-process": "3.957.0", "@aws-sdk/credential-provider-sso": "3.958.0", "@aws-sdk/credential-provider-web-identity": "3.958.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-u7twvZa1/6GWmPBZs6DbjlegCoNzNjBsMS/6fvh5quByYrcJr/uLd8YEr7S3UIq4kR/gSnHqcae7y2nL2bqZdg=="],
2732
+
2733
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/KIz9kadwbeLy6SKvT79W81Y+hb/8LMDyeloA2zhouE28hmne+hLn0wNCQXAAupFFlYOAtZR2NTBs7HBAReJlg=="],
2734
+
2735
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/client-sso": "3.958.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/token-providers": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-CBYHJ5ufp8HC4q+o7IJejCUctJXWaksgpmoFpXerbjAso7/Fg7LLUu9inXVOxlHKLlvYekDXjIUBXDJS2WYdgg=="],
2736
+
2737
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-dgnvwjMq5Y66WozzUzxNkCFap+umHUtqMMKlr8z/vl9NYMLem/WUbWNpFFOVFWquXikc+ewtpBMR4KEDXfZ+KA=="],
2738
+
2739
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA=="],
2740
+
2741
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@smithy/property-provider": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
2742
+
2743
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg=="],
2744
+
2745
+ "@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-arn-parser": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-5B2qY2nR2LYpxoQP0xUum5A1UNvH2JQpLHDH1nWFNF/XetV7ipFHksMxPNhtJJ6ARaWhQIDXfOUj0jcnkJxXUg=="],
2746
+
2747
+ "@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg=="],
2748
+
2749
+ "@aws-sdk/client-sesv2/@smithy/core/@smithy/util-stream": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
2750
+
2751
+ "@aws-sdk/client-sesv2/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg=="],
2752
+
2753
+ "@aws-sdk/client-sesv2/@smithy/middleware-endpoint/@smithy/shared-ini-file-loader": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg=="],
2754
+
2755
+ "@aws-sdk/client-sesv2/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0" } }, "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA=="],
2756
+
2757
+ "@aws-sdk/client-sesv2/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
2758
+
2759
+ "@aws-sdk/client-sesv2/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg=="],
2760
+
2761
+ "@aws-sdk/client-sesv2/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw=="],
2762
+
2763
+ "@aws-sdk/client-sesv2/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg=="],
2764
+
2765
+ "@aws-sdk/client-sesv2/@smithy/smithy-client/@smithy/util-stream": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
2766
+
2767
+ "@aws-sdk/client-sesv2/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w=="],
2768
+
2769
+ "@aws-sdk/client-sesv2/@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
2770
+
2771
+ "@aws-sdk/client-sesv2/@smithy/util-defaults-mode-node/@smithy/credential-provider-imds": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA=="],
2772
+
2773
+ "@aws-sdk/client-sesv2/@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
2774
+
2775
+ "@aws-sdk/client-sesv2/@smithy/util-retry/@smithy/service-error-classification": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0" } }, "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA=="],
2776
+
2515
2777
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
2516
2778
2517
2779
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
2620
2882
2621
2883
"cross-spawn/which/isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
2622
2884
2885
+ "gcp-metadata/gaxios/node-fetch": ["[email protected]", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
2886
+
2887
+ "gcp-metadata/gaxios/uuid": ["[email protected]", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
2888
+
2623
2889
"got/decompress-response/mimic-response": ["[email protected]", "", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="],
2624
2890
2625
2891
"gray-matter/js-yaml/argparse": ["[email protected]", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
2698
2964
2699
2965
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
2700
2966
2967
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
2968
+
2969
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-sDwtDnBSszUIbzbOORGh5gmXGl9aK25+BHb4gb1aVlqB+nNL2+IUEJA62+CE55lXSH8qXF90paivjK8tOHTwPA=="],
2970
+
2971
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw=="],
2972
+
2973
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6qNCIeaMzKzfqasy2nNRuYnMuaMebCcCPP4J2CVGkA8QYMbIVKPlkn9bpB20Vxe6H/r3jtCCLQaOJjVTx/6dXg=="],
2974
+
2975
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-UCj7lQXODduD1myNJQkV+LYcGYJ9iiMggR8ow8Hva1g3A/Na5imNXzz6O67k7DAee0TYpy+gkNw+SizC6min8Q=="],
2976
+
2977
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw=="],
2978
+
2979
+ "@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/util-arn-parser": ["@aws-sdk/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Aj6m+AyrhWyg8YQ4LDPg2/gIfGHCEcoQdBt5DeSFogN5k9mmJPOJ+IAmNSWmWRjpOxEy6eY813RNDI6qS97M0g=="],
2980
+
2981
+ "@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
2982
+
2701
2983
"ansi-align/string-width/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
2702
2984
2703
2985
"yargs/string-width/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
2986
+
2987
+ "@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw=="],
2704
2988
}
2705
2989
}
2706
2990
β–Όcli/README.md
+365-0
1
+# OCH CLI - OpenCodeHub Command Line Interface
2
+
3
+**Stack-first PR workflows from your terminal.**
4
+
5
+## Installation
6
+
7
+```bash
8
+# Install globally
9
+npm install -g opencodehub-cli
10
+
11
+# Or use with npx (no installation needed)
12
+npx opencodehub-cli
13
+
14
+# Verify installation
15
+och --version
16
+```
17
+
18
+## Quick Start
19
+
20
+```bash
21
+# 1. Login to your OpenCodeHub instance
22
+och auth login
23
+
24
+# Enter your email and password when prompted
25
+# Or use a personal access token:
26
+och auth login --token YOUR_TOKEN
27
+
28
+# 2. Navigate to your repository
29
+cd your-repo
30
+
31
+# 3. Initialize (one-time setup)
32
+och init --url https://git.yourcompany.com
33
+
34
+# 4. You're ready! Try:
35
+och status
36
+```
37
+
38
+## Commands
39
+
40
+### Authentication
41
+
42
+```bash
43
+# Interactive login
44
+och auth login
45
+
46
+# Login with server URL
47
+och auth login --url https://git.yourcompany.com
48
+
49
+# Login with token (CI/CD)
50
+och auth login --token YOUR_ACCESS_TOKEN
51
+
52
+# Check who you're logged in as
53
+och auth whoami
54
+
55
+# Logout
56
+och auth logout
57
+```
58
+
59
+### Stack Management
60
+
61
+**Create Stacked Branches:**
62
+```bash
63
+# Create a new branch in current stack
64
+och stack create \u003cbranch-name\u003e
65
+
66
+# Example:
67
+git checkout main
68
+och stack create database-schema
69
+# Make changes, commit
70
+och stack create auth-service
71
+# Make changes, commit
72
+```
73
+
74
+**View Stack:**
75
+```bash
76
+# See all branches in current stack
77
+och stack view
78
+
79
+# Short alias:
80
+och stack ls
81
+
82
+# Example output:
83
+# πŸ“š Current Stack
84
+# β”Œβ”€ main (base)
85
+# β”œβ”€ stack/database-schema βœ“ Pushed
86
+# └─ stack/auth-service * Current
87
+```
88
+
89
+**Submit Stack:**
90
+```bash
91
+# Push current branch and create/update PR
92
+och stack submit
93
+
94
+# Create as draft PR
95
+och stack submit --draft
96
+
97
+# This will:
98
+# - Push your branch to origin
99
+# - Create PR or update existing one
100
+# - Link it to parent PR if in a stack
101
+```
102
+
103
+**Sync Stack:**
104
+```bash
105
+# Rebase entire stack on latest main
106
+och stack sync
107
+
108
+# This handles:
109
+# - Fetching latest from origin
110
+# - Rebasing each branch in order
111
+# - Detecting and reporting conflicts
112
+```
113
+
114
+**Reorder Stack:**
115
+```bash
116
+# Interactively reorder branches
117
+och stack reorder
118
+
119
+# Choose new order from list
120
+# Stack dependencies will be updated
121
+```
122
+
123
+### Repository Operations
124
+
125
+```bash
126
+# Initialize repo for OpenCodeHub
127
+och init [--url \u003cserver-url\u003e]
128
+
129
+# Show current stack status
130
+och status
131
+# Alias: och st
132
+
133
+# Sync with remote (bi-directional)
134
+och sync
135
+```
136
+
137
+## Workflow Examples
138
+
139
+### Example 1: Create a Feature Stack
140
+
141
+```bash
142
+# Start from main
143
+git checkout main
144
+git pull
145
+
146
+# Create base layer
147
+och stack create user-models
148
+# ... edit files ...
149
+git add .
150
+git commit -m "Add user database models"
151
+
152
+# Create middleware layer (builds on user-models)
153
+och stack create auth-middleware
154
+# ... edit files ...
155
+git add .
156
+git commit -m "Add authentication middleware"
157
+
158
+# Create UI layer (builds on auth-middleware)
159
+och stack create login-page
160
+# ... edit files ...
161
+git add .
162
+git commit -m "Add login page UI"
163
+
164
+# Submit entire stack
165
+och stack submit
166
+
167
+# Result:
168
+# PR #301: Add user database models (main ← user-models)
169
+# PR #302: Add auth middleware (user-models ← auth-middleware)
170
+# PR #303: Add login page (auth-middleware ← login-page)
171
+```
172
+
173
+### Example 2: Update Stack After Review
174
+
175
+```bash
176
+# You're on stack/login-page
177
+# Reviewer requested changes
178
+
179
+# Make changes
180
+git add .
181
+git commit -m "Address review feedback"
182
+
183
+# Resubmit (updates PR automatically)
184
+och stack submit
185
+
186
+# If base changed, sync the whole stack
187
+och stack sync
188
+```
189
+
190
+### Example 3: collaboration on Stacks
191
+
192
+```bash
193
+# Teammate created a stack, you want to build on it
194
+git fetch
195
+git checkout their-branch
196
+
197
+# Create your branch on top
198
+och stack create my-feature
199
+# ... make changes ...
200
+git commit -m "My changes"
201
+
202
+# Submit (will link your PR to theirs)
203
+och stack submit
204
+```
205
+
206
+## Configuration
207
+
208
+OCH CLI stores configuration in `~/.ochrc` (JSON format).
209
+
210
+**View config:**
211
+```bash
212
+och config list
213
+```
214
+
215
+**Set config:**
216
+```bash
217
+och config set host https://git.yourcompany.com
218
+och config set token YOUR_TOKEN
219
+```
220
+
221
+**Config file structure:**
222
+```json
223
+{
224
+ "host": "https://git.yourcompany.com",
225
+ "token": "your-access-token",
226
+ "defaultBranch": "main"
227
+}
228
+```
229
+
230
+## Troubleshooting
231
+
232
+### "Not logged in" Error
233
+
234
+```bash
235
+# Check auth status
236
+och auth whoami
237
+
238
+# If not logged in:
239
+och auth login
240
+```
241
+
242
+### "No stack found" Error
243
+
244
+```bash
245
+# Ensure you're in a git repository
246
+git status
247
+
248
+# Initialize if needed
249
+och init
250
+
251
+# Check current branch
252
+git branch
253
+```
254
+
255
+### Permission Denied
256
+
257
+```bash
258
+# Verify your token has correct permissions
259
+och auth whoami
260
+
261
+# Login again with fresh token
262
+och auth logout
263
+och auth login --token NEW_TOKEN
264
+```
265
+
266
+### Stack Sync Conflicts
267
+
268
+```bash
269
+# If sync encounters conflicts:
270
+# 1. Resolve conflicts in each branch
271
+# 2. Continue sync:
272
+git rebase --continue
273
+
274
+# 3. Or abort and do manual rebase:
275
+git rebase --abort
276
+git checkout main
277
+git pull
278
+git checkout your-branch
279
+git rebase main
280
+```
281
+
282
+## CI/CD Usage
283
+
284
+```yaml
285
+# .github/workflows/deploy.yml
286
+name: Deploy Stack
287
+on:
288
+ push:
289
+ branches: ['stack/**']
290
+
291
+jobs:
292
+ deploy:
293
+ runs-on: ubuntu-latest
294
+ steps:
295
+ - uses: actions/checkout@v3
296
+
297
+ - name: Setup Node
298
+ uses: actions/setup-node@v3
299
+
300
+ - name: Install OCH CLI
301
+ run: npm install -g opencodehub-cli
302
+
303
+ - name: Login
304
+ run: och auth login --token ${{ secrets.OCH_TOKEN }}
305
+
306
+ - name: Submit PR
307
+ run: och stack submit
308
+```
309
+
310
+## Advanced Features
311
+
312
+### Custom PR Templates
313
+
314
+Create `.github/pull_request_template.md` in your repo:
315
+
316
+```markdown
317
+## Description
318
+<!-- What does this PR do? -->
319
+
320
+## Stack Position
321
+<!-- Part of stack: #XXX β†’ This PR β†’ #YYY -->
322
+
323
+## Checklist
324
+- [ ] Tests pass
325
+- [ ] Docs updated
326
+```
327
+
328
+### Hooks
329
+
330
+OCH CLI supports git hooks for validation:
331
+
332
+```bash
333
+# .git/hooks/pre-push
334
+#!/bin/bash
335
+# Validate stack before push
336
+och stack verify || exit 1
337
+```
338
+
339
+## API Integration
340
+
341
+OCH CLI uses OpenCodeHub REST API. Token permissions needed:
342
+- `repo` - Repository read/write
343
+- `pr` - Pull request create/update
344
+- `user` - Read user profile
345
+
346
+Generate token: `https://your-server/settings/tokens`
347
+
348
+## Contributing
349
+
350
+OCH CLI is open source! Contribute at:
351
+https://github.com/swadhinbiswas/OpencodeHub/tree/main/cli
352
+
353
+## Support
354
+
355
+- **Documentation**: https://git.yourcompany.com/docs
356
+- **Issues**: https://github.com/swadhinbiswas/OpencodeHub/issues
357
+- **Discussions**: https://github.com/swadhinbiswas/OpencodeHub/discussions
358
+
359
+## changelog
360
+
361
+See [CHANGELOG.md](./CHANGELOG.md) for version history.
362
+
363
+## License
364
+
365
+MIT License - see [LICENSE](../LICENSE)
0
366
β–Όdocs/features/ai-review.md
+600-0
1
+# AI Code Review
2
+
3
+> Get instant, intelligent code reviews powered by GPT-4 or Claude
4
+
5
+AI Code Review uses advanced language models to automatically review your pull requests, catching bugs, security issues, and code quality problems before they reach production.
6
+
7
+## πŸ“– Table of Contents
8
+
9
+- [Overview](#overview)
10
+- [Setup](#setup)
11
+ - [Configure AI Provider](#configure-ai-provider)
12
+ - [Enable for Repository](#enable-for-repository)
13
+- [Using AI Review](#using-ai-review)
14
+- [Understanding Review Output](#understanding-review-output)
15
+- [Customizing Reviews](#customizing-reviews)
16
+- [Cost Considerations](#cost-considerations)
17
+- [Best Practices](#best-practices)
18
+- [Troubleshooting](#troubleshooting)
19
+
20
+---
21
+
22
+## Overview
23
+
24
+### What AI Review Catches
25
+
26
+πŸ”’ **Security Vulnerabilities**
27
+- SQL injection risks
28
+- XSS (Cross-Site Scripting) vectors
29
+- Authentication bypasses
30
+- Secrets/credentials in code
31
+- Insecure dependencies
32
+
33
+⚑ **Performance Issues**
34
+- N+1 database queries
35
+- Inefficient algorithms (O(nΒ²) β†’ O(n log n))
36
+- Memory leaks
37
+- Blocking operations in async code
38
+- Missing indexes
39
+
40
+πŸ“ **Code Quality**
41
+- Best practice violations
42
+- Code smells (long functions, deep nesting)
43
+- Incomplete error handling
44
+- Missing input validation
45
+- Poor naming conventions
46
+
47
+πŸ› **Potential Bugs**
48
+- Null/undefined pointer exceptions
49
+- Race conditions
50
+- Off-by-one errors
51
+- Logic flaws
52
+- Type mismatches
53
+
54
+### How It Works
55
+
56
+```
57
+1. Developer opens/updates PR
58
+ ↓
59
+2. OpenCodeHub sends diff to AI
60
+ ↓
61
+3. AI analyzes code changes
62
+ ↓
63
+4. AI posts inline comments + summary
64
+ ↓
65
+5. Developer addresses feedback
66
+```
67
+
68
+**Privacy note:** Code is sent to AI provider (OpenAI/Anthropic). Use on-premise models for sensitive code.
69
+
70
+---
71
+
72
+## Setup
73
+
74
+### Configure AI Provider
75
+
76
+**Option 1: OpenAI (GPT-4)**
77
+
78
+1. Get API key from [platform.openai.com](https://platform.openai.com/api-keys)
79
+
80
+2. Add to `.env`:
81
+ ```env
82
+ AI_PROVIDER=openai
83
+ OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx
84
+ OPENAI_MODEL=gpt-4-turbo-preview # or gpt-4, gpt-3.5-turbo
85
+ ```
86
+
87
+3. Restart OpenCodeHub
88
+
89
+**Option 2: Anthropic (Claude)**
90
+
91
+1. Get API key from [console.anthropic.com](https://console.anthropic.com/)
92
+
93
+2. Add to `.env`:
94
+ ```env
95
+ AI_PROVIDER=anthropic
96
+ ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxx
97
+ ANTHROPIC_MODEL=claude-3-opus-20240229 # or claude-3-sonnet
98
+ ```
99
+
100
+3. Restart OpenCodeHub
101
+
102
+**Option 3: Azure OpenAI**
103
+
104
+```env
105
+AI_PROVIDER=azure-openai
106
+AZURE_OPENAI_API_KEY=xxxxx
107
+AZURE_OPENAI_ENDPOINT=https://yourinstance.openai.azure.com/
108
+AZURE_OPENAI_DEPLOYMENT=your-deployment-name
109
+```
110
+
111
+**Option 4: Self-Hosted (Coming Soon)**
112
+
113
+OpenCodeHub will support local models via Ollama:
114
+```env
115
+AI_PROVIDER=ollama
116
+OLLAMA_ENDPOINT=http://localhost:11434
117
+OLLAMA_MODEL=codellama:13b
118
+```
119
+
120
+### Enable for Repository
121
+
122
+**Via Web UI:**
123
+
124
+1. Navigate to **Repository β†’ Settings β†’ AI Review**
125
+2. Toggle **"Enable AI Code Review"**
126
+3. Configure settings:
127
+ - **Trigger**: `On PR open`, `On push`, or `Manual only`
128
+ - **Auto-comment**: Post comments automatically
129
+ - **Severity threshold**: `All`, `Warning+`, or `Critical only`
130
+ - **Max files**: Limit review to N files (cost control)
131
+4. Click **Save**
132
+
133
+**Via API:**
134
+
135
+```bash
136
+curl -X PATCH https://git.yourcompany.com/api/repos/123/ai-review \
137
+ -H "Authorization: Bearer $TOKEN" \
138
+ -d '{
139
+ "enabled": true,
140
+ "trigger": "on_pr_open",
141
+ "auto_comment": true,
142
+ "severity_threshold": "warning"
143
+ }'
144
+```
145
+
146
+**Via CLI:**
147
+
148
+```bash
149
+och repo config ai-review --enable \
150
+ --trigger on_pr_open \
151
+ --auto-comment
152
+```
153
+
154
+---
155
+
156
+## Using AI Review
157
+
158
+### Automatic Reviews
159
+
160
+If enabled, AI reviews trigger automatically:
161
+
162
+```bash
163
+# 1. Create PR
164
+git push origin feature-branch
165
+# β†’ AI review starts automatically
166
+
167
+# 2. Update PR
168
+git push origin feature-branch
169
+# β†’ AI re-reviews changed files only
170
+```
171
+
172
+You'll see:
173
+- βœ… **Status check**: "AI Review: In Progress" β†’ "AI Review: Complete"
174
+- πŸ’¬ **Inline comments**: On specific lines with issues
175
+- πŸ“Š **Summary comment**: Overall assessment
176
+
177
+### Manual Reviews
178
+
179
+Request AI review on-demand:
180
+
181
+**Via Web UI:**
182
+1. Open PR page
183
+2. Click **"Request AI Review"** button
184
+3. Wait ~30-60 seconds
185
+4. Review appears as comments
186
+
187
+**Via CLI:**
188
+```bash
189
+# Review specific PR
190
+och review ai 125
191
+
192
+# Review with custom prompt
193
+och review ai 125 --focus security
194
+
195
+# Re-review after changes
196
+och review ai 125 --force
197
+```
198
+
199
+**Via API:**
200
+```bash
201
+curl -X POST https://git.yourcompany.com/api/prs/125/ai-review \
202
+ -H "Authorization: Bearer $TOKEN"
203
+```
204
+
205
+---
206
+
207
+## Understanding Review Output
208
+
209
+### Severity Levels
210
+
211
+AI assigns severity to each finding:
212
+
213
+| Severity | Icon | Meaning | Example |
214
+|----------|------|---------|---------|
215
+| πŸ”΄ **CRITICAL** | Must fix | Security vulnerability, data loss risk | SQL injection, exposed API key |
216
+| 🟑 **WARNING** | Should fix | Performance issue, bad practice | N+1 query, missing error handling |
217
+| πŸ”΅ **INFO** | Consider | Suggestion, refactoring opportunity | Better variable name, code simplification |
218
+
219
+### Example Review Comment
220
+
221
+```markdown
222
+πŸ”΄ CRITICAL: SQL Injection Vulnerability
223
+
224
+**File:** `src/api/users.ts`
225
+**Line:** 45
226
+
227
+**Issue:**
228
+User input is directly concatenated into SQL query without sanitization.
229
+
230
+**Current Code:**
231
+```typescript
232
+const query = `SELECT * FROM users WHERE email = '${userEmail}'`;
233
+```
234
+
235
+**Risk:**
236
+An attacker could inject malicious SQL:
237
+```
238
+userEmail = "' OR '1'='1"
239
+β†’ Query becomes: SELECT * FROM users WHERE email = '' OR '1'='1'
240
+β†’ Returns ALL users (authentication bypass)
241
+```
242
+
243
+**Fix:**
244
+Use parameterized queries or ORM:
245
+```typescript
246
+const query = `SELECT * FROM users WHERE email = ?`;
247
+const results = await db.query(query, [userEmail]);
248
+
249
+// Or with ORM:
250
+const user = await db.users.findOne({ email: userEmail });
251
+```
252
+
253
+**References:**
254
+- [OWASP SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection)
255
+- [Node.js SQL Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)
256
+```
257
+
258
+### Summary Comment
259
+
260
+AI posts an overall summary:
261
+
262
+```markdown
263
+## πŸ€– AI Code Review Summary
264
+
265
+**Overall Assessment:** ⚠️ Needs Attention
266
+
267
+### Statistics
268
+- Files reviewed: 8
269
+- Critical issues: 2
270
+- Warnings: 5
271
+- Info: 3
272
+
273
+### Critical Issues
274
+1. πŸ”΄ SQL Injection in `src/api/users.ts:45`
275
+2. πŸ”΄ Hardcoded API key in `src/config.ts:12`
276
+
277
+### Recommendations
278
+1. Add input validation to all API endpoints
279
+2. Move secrets to environment variables
280
+3. Consider adding integration tests for auth flows
281
+
282
+### Positive Notes
283
+- βœ… Good error handling in payment service
284
+- βœ… Well-structured database migrations
285
+- βœ… Comprehensive TypeScript types
286
+
287
+**Estimated fix time:** 2-3 hours
288
+```
289
+
290
+---
291
+
292
+## Customizing Reviews
293
+
294
+### Review Rules
295
+
296
+Create `.opencodehub/ai-review.yml` in your repository:
297
+
298
+```yaml
299
+# AI Review Configuration
300
+
301
+# Which files to review
302
+include:
303
+ - "src/**/*.ts"
304
+ - "src/**/*.tsx"
305
+ - "lib/**/*.js"
306
+
307
+# Which files to skip
308
+exclude:
309
+ - "**/*.test.ts"
310
+ - "**/*.spec.ts"
311
+ - "**/generated/**"
312
+ - "dist/**"
313
+
314
+# Focus areas (weighted 1-10)
315
+focus:
316
+ security: 10 # Highest priority
317
+ performance: 8
318
+ bugs: 8
319
+ code_quality: 5
320
+ documentation: 3
321
+
322
+# Severity thresholds
323
+report:
324
+ critical: always
325
+ warning: always
326
+ info: on_request # Only if --verbose flag used
327
+
328
+# Custom rules
329
+custom_rules:
330
+ - name: "No console.log in production"
331
+ pattern: 'console\\.log\\('
332
+ severity: warning
333
+ message: "Remove console.log before merging"
334
+
335
+ - name: "Require error handling"
336
+ pattern: 'await.*\\(.*\\)'
337
+ check: must_have_try_catch
338
+ severity: warning
339
+
340
+# AI instructions
341
+instructions: >
342
+ Pay special attention to:
343
+ 1. Authentication and authorization logic
344
+ 2. Database query efficiency
345
+ 3. Input validation
346
+ 4. Proper error handling
347
+
348
+ Our coding standards:
349
+ - TypeScript strict mode
350
+ - Functional programming preferred
351
+ - Maximum function length: 50 lines
352
+ - Maximum cyclomatic complexity: 10
353
+```
354
+
355
+### Custom Prompts
356
+
357
+**Focus on specific aspects:**
358
+
359
+```bash
360
+# Security-focused review
361
+och review ai 125 --focus security
362
+
363
+# Performance-focused review
364
+och review ai 125 --focus performance
365
+
366
+# Architecture review
367
+och review ai 125 --prompt "Review system architecture and design patterns"
368
+```
369
+
370
+### Language-Specific Reviews
371
+
372
+AI automatically adapts to programming language:
373
+
374
+- **TypeScript/JavaScript**: Type safety, async/await, React best practices
375
+- **Python**: PEP 8, type hints, Django/Flask patterns
376
+- **Go**: Go idioms, error handling, concurrency
377
+- **Rust**: Borrow checker, unsafe code, idiomatic Rust
378
+- **Java**: Design patterns, Spring best practices, thread safety
379
+
380
+---
381
+
382
+## Cost Considerations
383
+
384
+### Pricing
385
+
386
+AI review costs depend on provider and model:
387
+
388
+**OpenAI (GPT-4 Turbo):**
389
+- ~$0.01 per 1K tokens (input)
390
+- ~$0.03 per 1K tokens (output)
391
+- **Typical PR**: 500-2000 tokens = **$0.02-0.10 per review**
392
+
393
+**Anthropic (Claude 3 Opus):**
394
+- ~$0.015 per 1K tokens (input)
395
+- ~$0.075 per 1K tokens (output)
396
+- **Typical PR**: **$0.03-0.15 per review**
397
+
398
+**Example monthly cost:**
399
+- 50 PRs/month Γ— $0.05/review = **$2.50/month**
400
+- 500 PRs/month Γ— $0.05/review = **$25/month**
401
+
402
+### Cost Optimization
403
+
404
+**1. Limit file count:**
405
+```yaml
406
+max_files_per_review: 10 # Review only 10 largest changed files
407
+```
408
+
409
+**2. Use cheaper models for simple changes:**
410
+```yaml
411
+rules:
412
+ - if: changed_files < 3
413
+ model: gpt-3.5-turbo # 10x cheaper
414
+ - if: changed_files >= 3
415
+ model: gpt-4-turbo
416
+```
417
+
418
+**3. Skip test files:**
419
+```yaml
420
+exclude:
421
+ - "**/*.test.*"
422
+ - "**/*.spec.*"
423
+```
424
+
425
+**4. Review only on-request:**
426
+```yaml
427
+trigger: manual # Don't auto-review every push
428
+```
429
+
430
+**5. Use severity threshold:**
431
+```yaml
432
+severity_threshold: warning # Skip "info" level comments
433
+```
434
+
435
+---
436
+
437
+## Best Practices
438
+
439
+### πŸ’‘ When to Use AI Review
440
+
441
+**βœ… Good Use Cases:**
442
+- Security-sensitive code (auth, payments)
443
+- Complex algorithms
444
+- Database queries
445
+- API endpoints
446
+- Third-party integrations
447
+
448
+**❌ Less Useful:**
449
+- Generated code
450
+- Configuration files
451
+- Simple refactoring
452
+- Documentation-only changes
453
+
454
+### πŸ’‘ Responding to AI Feedback
455
+
456
+**1. Don't auto-fix everything**
457
+- AI can be wrong
458
+- Context matters
459
+- Use judgment
460
+
461
+**2. Engage in discussion**
462
+```markdown
463
+@ai-review That's a good catch, but in this case we're using a
464
+trusted source and the data is already sanitized earlier in the pipeline.
465
+
466
+See line 23 where we validate input.
467
+```
468
+
469
+**3. Track false positives**
470
+```yaml
471
+# .opencodehub/ai-review.yml
472
+ignore:
473
+ - file: src/utils/legacy.ts
474
+ reason: "Legacy code, requires major refactor"
475
+ until: 2024-12-31
476
+```
477
+
478
+### πŸ’‘ Combining with Human Review
479
+
480
+AI complements, doesn't replace humans:
481
+
482
+```
483
+1. AI reviews first (catches obvious issues)
484
+2. Developer fixes critical/warnings
485
+3. Human reviewer approves
486
+```
487
+
488
+This workflow:
489
+- Reduces human reviewer burden
490
+- Catches more bugs
491
+- Faster feedback cycles
492
+
493
+---
494
+
495
+## Troubleshooting
496
+
497
+### "AI review taking too long"
498
+
499
+**Cause:** Large PR (many files/lines).
500
+
501
+**Solution:**
502
+```bash
503
+# Check review status
504
+och review status 125
505
+
506
+# Cancel and retry
507
+och review cancel 125
508
+och review ai 125 --max-files 5
509
+```
510
+
511
+### "AI review failed"
512
+
513
+**Causes:**
514
+1. API key invalid/expired
515
+2. Rate limit exceeded
516
+3. Timeout (very large PR)
517
+
518
+**Solutions:**
519
+```bash
520
+# 1. Verify API key
521
+echo $OPENAI_API_KEY
522
+
523
+# 2. Check rate limits
524
+curl https://api.openai.com/v1/usage \
525
+ -H "Authorization: Bearer $OPENAI_API_KEY"
526
+
527
+# 3. Split large PR or review manually:
528
+och review ai 125 --files src/api/users.ts src/api/auth.ts
529
+```
530
+
531
+### "Too many false positives"
532
+
533
+**Solution:** Tune detection thresholds:
534
+
535
+```yaml
536
+# .opencodehub/ai-review.yml
537
+sensitivity:
538
+ security: high # Strict
539
+ performance: medium
540
+ code_quality: low # Lenient
541
+```
542
+
543
+### "Missing obvious bugs"
544
+
545
+**Cause:** AI missed context or edge case.
546
+
547
+**Solution:** Add custom instructions:
548
+
549
+```yaml
550
+instructions: >
551
+ Our codebase uses a custom ORM. When reviewing database code:
552
+ 1. `db.raw()` is already parameterized
553
+ 2. All user input goes through `sanitize()` middleware
554
+ 3. Check for missing `await` on async functions
555
+```
556
+
557
+---
558
+
559
+## Advanced: CI/CD Integration
560
+
561
+### Block merge on critical findings
562
+
563
+```yaml
564
+# .github/workflows/ai-review.yml
565
+name: AI Code Review
566
+on: [pull_request]
567
+
568
+jobs:
569
+ ai-review:
570
+ runs-on: ubuntu-latest
571
+ steps:
572
+ - name: Run AI Review
573
+ run: och review ai ${{ github.event.pull_request.number }}
574
+ env:
575
+ OCH_TOKEN: ${{ secrets.OCH_TOKEN }}
576
+
577
+ - name: Check for critical issues
578
+ run: |
579
+ CRITICAL=$(och review status ${{ github.event.pull_request.number }} --json | jq '.critical_count')
580
+ if [ "$CRITICAL" -gt 0 ]; then
581
+ echo "❌ Critical issues found"
582
+ exit 1
583
+ fi
584
+```
585
+
586
+---
587
+
588
+## See Also
589
+
590
+- [Stacked Pull Requests](stacked-prs.md)
591
+- [Smart Merge Queue](merge-queue.md)
592
+- [CI/CD Integration](ci-cd.md)
593
+
594
+---
595
+
596
+## Need Help?
597
+
598
+- πŸ’¬ [Discord Community](https://discord.gg/opencodehub)
599
+- πŸ› [Report Issues](https://github.com/swadhinbiswas/OpencodeHub/issues)
600
+- πŸ“– [Full Documentation](/)
0
601
β–Όdocs/features/merge-queue.md
+564-0
1
+# Smart Merge Queue
2
+
3
+> Automate merging with stack-aware CI optimization and conflict resolution
4
+
5
+The Smart Merge Queue automatically merges approved pull requests in the correct order, running CI efficiently and handling rebases automatically.
6
+
7
+## οΏ½οΏ½ Table of Contents
8
+
9
+- [Overview](#overview)
10
+- [How It Works](#how-it-works)
11
+- [Getting Started](#getting-started)
12
+- [Queue Management](#queue-management)
13
+- [Configuration](#configuration)
14
+- [Best Practices](#best-practices)
15
+- [Troubleshooting](#troubleshooting)
16
+
17
+---
18
+
19
+## Overview
20
+
21
+### The Problem
22
+
23
+Manual merging is:
24
+- ⏰ **Slow** - Wait for CI, merge, repeat
25
+- πŸ› **Error-prone** - Merge conflicts, wrong order
26
+- πŸ’Έ **Expensive** - Run CI for every PR individually
27
+- πŸ˜“ **Tedious** - Babysit the merge process
28
+
29
+### The Solution
30
+
31
+Smart Merge Queue:
32
+
33
+βœ… **Automatic Merging** - Set it and forget it
34
+βœ… **Stack-Aware** - Merges in dependency order
35
+βœ… **CI Optimization** - Batches compatible PRs
36
+βœ… **Conflict Resolution** - Auto-rebases or alerts
37
+βœ… **Priority Handling** - Hotfixes jump the queue
38
+
39
+### Real-World Impact
40
+
41
+**Without Queue:**
42
+```
43
+PR #123 β†’ Wait for CI (5 min) β†’ Merge β†’
44
+PR #124 β†’ Rebase β†’ Wait for CI (5 min) β†’ Merge β†’
45
+PR #125 β†’ Rebase β†’ Wait for CI (5 min) β†’ Merge
46
+Total: ~20 minutes, 3 CI runs
47
+```
48
+
49
+**With Queue:**
50
+```
51
+Add #123, #124, #125 to queue β†’
52
+Queue detects dependencies β†’
53
+Merges #123 β†’
54
+Auto-rebases #124 β†’
55
+Batches #124 + #125 for CI (if compatible) β†’
56
+Merges both
57
+Total: ~8 minutes, 1-2 CI runs πŸŽ‰
58
+```
59
+
60
+---
61
+
62
+## How It Works
63
+
64
+### Queue Flow
65
+
66
+```
67
+1. PR gets approved
68
+ ↓
69
+2. Add to merge queue
70
+ ↓
71
+3. Queue checks:
72
+ - All approvals? βœ“
73
+ - CI passing? βœ“
74
+ - Conflicts? Auto-rebase
75
+ - Dependencies? Order correctly
76
+ ↓
77
+4. Run final CI check
78
+ ↓
79
+5. Merge to main
80
+ ↓
81
+6. Update dependent PRs
82
+```
83
+
84
+### Stack-Aware Merging
85
+
86
+Queue understands stacks:
87
+
88
+```
89
+Queue contains:
90
+- PR #123 (base)
91
+- PR #124 (depends on #123)
92
+- PR #125 (depends on #124)
93
+
94
+Queue automatically:
95
+1. Merges #123 first
96
+2. Rebases #124 onto new main
97
+3. Runs CI for #124
98
+4. Merges #124
99
+5. Rebases #125
100
+6. Merges #125
101
+
102
+All automatic!
103
+```
104
+
105
+### CI Optimization
106
+
107
+**Batching:**
108
+```
109
+PR #126: Fix typo in docs (no code changes)
110
+PR #127: Update README
111
+PR #128: Fix CSS bug
112
+
113
+Queue batches #126 + #127 (docs only)
114
+β†’ Single CI run
115
+β†’ Merge both together
116
+β†’ 50% CI cost savings!
117
+```
118
+
119
+**Smart Scheduling:**
120
+- Long CI jobs run in parallel
121
+- Short jobs batch together
122
+- Stack dependencies respected
123
+
124
+---
125
+
126
+## Getting Started
127
+
128
+### Prerequisites
129
+
130
+- Repository with CI configured
131
+- Merge permissions
132
+- Branch protection (optional but recommended)
133
+
134
+### Adding PR to Queue
135
+
136
+**Via Web UI:**
137
+
138
+1. Open approved PR
139
+2. Click **"Add to Merge Queue"** button
140
+3. PR status changes to "In Queue"
141
+4. Watch it merge automatically!
142
+
143
+**Via CLI:**
144
+
145
+```bash
146
+# Add single PR
147
+och queue add 125
148
+
149
+# Add multiple PRs
150
+och queue add 123 124 125
151
+
152
+# Add with priority (hotfix)
153
+och queue add 126 --priority high
154
+
155
+# Add when approved
156
+och queue add 127 --when approved
157
+```
158
+
159
+**Via API:**
160
+
161
+```bash
162
+curl -X POST https://git.yourcompany.com/api/queue \
163
+ -H "Authorization: Bearer $TOKEN" \
164
+ -d '{"pr_id": 125, "priority": "normal"}'
165
+```
166
+
167
+### Viewing Queue Status
168
+
169
+**Web UI:**
170
+Navigate to `/queue` or click "Merge Queue" in repository navigation
171
+
172
+**CLI:**
173
+```bash
174
+# View entire queue
175
+och queue list
176
+
177
+# Output:
178
+# Position PR Title Status ETA
179
+# 1 #125 Login UI πŸƒ Running CI 2 min
180
+# 2 #124 Auth service ⏳ Waiting 5 min
181
+# 3 #123 Database schema βœ… Ready 7 min
182
+# 4 #126 Hotfix: XSS πŸ”₯ Priority 1 min
183
+
184
+# Watch queue in real-time
185
+och queue watch
186
+```
187
+
188
+**API:**
189
+```bash
190
+curl https://git.yourcompany.com/api/queue \
191
+ -H "Authorization: Bearer $TOKEN"
192
+```
193
+
194
+---
195
+
196
+## Queue Management
197
+
198
+### Priority Levels
199
+
200
+| Priority | Use Case | Behavior |
201
+|----------|----------|----------|
202
+| πŸ”₯ **Critical** | Production outage | Jumps to front, blocks other merges |
203
+| ⚑ **High** | Security fix, hotfix | Merges before normal PRs |
204
+| πŸ“„ **Normal** | Regular features | Default, FIFO order |
205
+| 🐌 **Low** | Refactoring, docs | Merges when queue is empty |
206
+
207
+**Setting priority:**
208
+```bash
209
+# CLI
210
+och queue add 126 --priority critical
211
+
212
+# Web UI: Select priority dropdown when adding to queue
213
+```
214
+
215
+### Removing from Queue
216
+
217
+```bash
218
+# Remove specific PR
219
+och queue remove 125
220
+
221
+# Remove all your PRs
222
+och queue remove --mine
223
+
224
+# Clear entire queue (admin only)
225
+och queue clear
226
+```
227
+
228
+### Pausing the Queue
229
+
230
+```bash
231
+# Pause (emergency)
232
+och queue pause --reason "Investigating production issue"
233
+
234
+# Resume
235
+och queue resume
236
+```
237
+
238
+---
239
+
240
+## Configuration
241
+
242
+### Repository Settings
243
+
244
+**Repository β†’ Settings β†’ Merge Queue**
245
+
246
+```yaml
247
+# Merge strategy
248
+merge_method: squash # or merge, rebase
249
+
250
+# CI requirements
251
+require_ci: true
252
+required_checks:
253
+ - "test"
254
+ - "lint"
255
+ - "build"
256
+
257
+# Approval requirements
258
+require_approvals: 1
259
+dismiss_stale_reviews: true
260
+
261
+# Batching
262
+enable_batching: true
263
+batch_window: 5m # Wait 5 min to batch PRs
264
+max_batch_size: 3 # Max 3 PRs per batch
265
+
266
+# Rebasing
267
+auto_rebase: true # Auto-rebase on conflicts
268
+rebase_strategy: merge # or rebase, squash
269
+
270
+# Priorities
271
+allow_priority_override: true # Let users set priority
272
+max_queue_size: 50 # Max PRs in queue
273
+```
274
+
275
+### Branch Protection Rules
276
+
277
+Recommended settings:
278
+
279
+```yaml
280
+# .github/branch-protection.yml
281
+branches:
282
+ main:
283
+ required_status_checks:
284
+ - test
285
+ - lint
286
+ required_approvals: 1
287
+ allow_force_push: false
288
+ allow_queue_bypass: false # Even admins use queue!
289
+```
290
+
291
+### Webhook Notifications
292
+
293
+Get notified when PRs merge:
294
+
295
+```bash
296
+# Configure webhook
297
+curl -X POST https://git.yourcompany.com/api/webhooks \
298
+ -d '{
299
+ "url": "https://yourapp.com/webhook",
300
+ "events": ["merge_queue.merged", "merge_queue.failed"],
301
+ "secret": "your-secret"
302
+ }'
303
+```
304
+
305
+**Payload:**
306
+```json
307
+{
308
+ "event": "merge_queue.merged",
309
+ "pr": {
310
+ "number": 125,
311
+ "title": "Add login UI",
312
+ "author": "swadhin"
313
+ },
314
+ "queue": {
315
+ "position": 1,
316
+ "wait_time_seconds": 180
317
+ },
318
+ "timestamp": "2024-01-01T12:00:00Z"
319
+}
320
+```
321
+
322
+---
323
+
324
+## Best Practices
325
+
326
+### πŸ’‘ When to Use the Queue
327
+
328
+**βœ… Always use for:**
329
+- Feature branches β†’ main
330
+- Release branches
331
+- Any PR affecting production
332
+
333
+**❌ Don't use for:**
334
+- Draft PRs
335
+- WIP branches
336
+- Experimental features
337
+
338
+### πŸ’‘ Optimizing Queue Performance
339
+
340
+**1. Fast CI:**
341
+```yaml
342
+# Run quick tests in queue
343
+queue_ci:
344
+ - unit_tests # 2 min
345
+
346
+# Run slow tests post-merge
347
+post_merge_ci:
348
+ - integration_tests # 10 min
349
+ - e2e_tests # 15 min
350
+```
351
+
352
+**2. Parallel CIs:**
353
+```yaml
354
+# Run multiple jobs in parallel
355
+ci:
356
+ - test (3 min)
357
+ - lint (1 min)
358
+ - build (2 min)
359
+# Total: 3 min (not 6 min!)
360
+```
361
+
362
+**3. Smart batching:**
363
+```bash
364
+# Group related PRs
365
+och queue add 123 124 125 --batch-id feature-auth
366
+
367
+# Queue runs CI once for entire batch
368
+```
369
+
370
+### πŸ’‘ Handling Conflicts
371
+
372
+**Automatic resolution:**
373
+```yaml
374
+auto_rebase: true
375
+rebase_strategy: merge # Creates merge commit
376
+
377
+# Or:
378
+rebase_strategy: rebase # Cleaner history
379
+```
380
+
381
+**Manual resolution:**
382
+```bash
383
+# If auto-rebase fails, you'll get notification
384
+# Fix locally:
385
+git checkout feature-branch
386
+git fetch origin
387
+git rebase origin/main
388
+# Resolve conflicts
389
+git push --force-with-lease
390
+
391
+# PR automatically re-enters queue
392
+```
393
+
394
+### πŸ’‘ Queue Monitoring
395
+
396
+**Set up alerts:**
397
+```yaml
398
+# Slack notification if queue stalled
399
+alerts:
400
+ - type: queue_stalled
401
+ threshold: 30m
402
+ channel: "#eng-deployments"
403
+
404
+ - type: merge_failed
405
+ channel: "#eng-alerts"
406
+```
407
+
408
+**Dashboard example:**
409
+```bash
410
+# View queue health
411
+och queue stats
412
+
413
+# Output:
414
+# Queue Health: βœ… Good
415
+# - Average wait time: 4.2 min
416
+# - CI success rate: 94%
417
+# - Merge rate: 15 PRs/hour
418
+# - Current size: 8 PRs
419
+```
420
+
421
+---
422
+
423
+## Troubleshooting
424
+
425
+### "PR stuck in queue"
426
+
427
+**Causes:**
428
+1. CI failing
429
+2. Waiting for approval
430
+3. Merge conflict
431
+4. Dependency not merged yet
432
+
433
+**Solutions:**
434
+```bash
435
+# Check status
436
+och queue status 125
437
+
438
+# View logs
439
+och queue logs 125
440
+
441
+# Remove and re-add
442
+och queue remove 125
443
+# Fix issue
444
+och queue add 125
445
+```
446
+
447
+### "Auto-rebase failed"
448
+
449
+**Cause:** Complex merge conflicts.
450
+
451
+**Solution:**
452
+```bash
453
+# Remove from queue
454
+och queue remove 125
455
+
456
+# Manually rebase
457
+git checkout feature-branch
458
+git rebase origin/main
459
+# Resolve conflicts
460
+git push -f
461
+
462
+# Re-add to queue
463
+och queue add 125
464
+```
465
+
466
+### "Queue not processing"
467
+
468
+**Causes:**
469
+1. Queue paused
470
+2. CI service down
471
+3. Rate limit reached
472
+
473
+**Solutions:**
474
+```bash
475
+# Check queue status
476
+och queue info
477
+
478
+# Resume if paused
479
+och queue resume
480
+
481
+# Check CI status
482
+curl https://ci-service/status
483
+
484
+# View queue system logs (admin)
485
+och admin logs queue
486
+```
487
+
488
+### "Wrong merge order"
489
+
490
+**Cause:** Dependencies not detected.
491
+
492
+**Solution:**
493
+```bash
494
+# Manually set dependency
495
+och pr edit 125 --depends-on 124
496
+
497
+# Queue will automatically reorder
498
+```
499
+
500
+---
501
+
502
+## Advanced Features
503
+
504
+### Merge Train
505
+
506
+For high-velocity teams:
507
+
508
+```yaml
509
+# Merge up to 5 PRs simultaneously
510
+merge_train:
511
+ enabled: true
512
+ max_concurrent: 5
513
+
514
+# Creates temporary integration branches
515
+# Runs CI in parallel
516
+# Merges in rapid succession
517
+```
518
+
519
+### Scheduled Merges
520
+
521
+```bash
522
+# Merge during business hours only
523
+och queue add 125 --schedule "Mon-Fri 9am-5pm EST"
524
+
525
+# Merge during maintenance window
526
+och queue add 126 --schedule "Sat 2am-4am EST"
527
+```
528
+
529
+### Queue Hooks
530
+
531
+Custom automation:
532
+
533
+```javascript
534
+// .opencodehub/hooks/queue.js
535
+module.exports = {
536
+ beforeMerge(pr) {
537
+ // Send Slack notification
538
+ // Update JIRA ticket
539
+ // Trigger deployment
540
+ },
541
+
542
+ afterMerge(pr) {
543
+ // Create release notes
544
+ // Update documentation
545
+ }
546
+};
547
+```
548
+
549
+---
550
+
551
+## See Also
552
+
553
+- [Stacked Pull Requests](stacked-prs.md)
554
+- [AI Code Review](ai-review.md)
555
+- [CI/CD Integration](ci-cd.md)
556
+- [Webhooks](../guides/webhooks.md)
557
+
558
+---
559
+
560
+## Need Help?
561
+
562
+- πŸ’¬ [Discord Community](https://discord.gg/opencodehub)
563
+- πŸ› [Report Issues](https://github.com/swadhinbiswas/OpencodeHub/issues)
564
+- πŸ“– [Full Documentation](/)
0
565
β–Όdocs/features/stacked-prs.md
+459-0
1
+# Stacked Pull Requests
2
+
3
+> Break large features into small, reviewable changes that ship faster
4
+
5
+Stacked Pull Requests (Stacked PRs) is a development workflow that allows you to break large features into a series of small, dependent pull requests. Each PR in the stack builds on the previous one, enabling faster reviews and parallel development.
6
+
7
+## πŸ“– Table of Contents
8
+
9
+- [Why Stacked PRs?](#why-stacked-prs)
10
+- [How It Works](#how-it-works)
11
+- [Getting Started](#getting-started)
12
+ - [Creating Your First Stack](#creating-your-first-stack-web-ui)
13
+ - [Using the CLI](#creating-a-stack-with-och-cli)
14
+- [Managing Stacks](#managing-stacks)
15
+- [Best Practices](#best-practices)
16
+- [Troubleshooting](#troubleshooting)
17
+
18
+---
19
+
20
+## Why Stacked PRs?
21
+
22
+### The Problem with Large PRs
23
+
24
+Large pull requests are:
25
+- ⏰ **Slow to review** - Reviewers get overwhelmed
26
+- πŸ› **More bugs** - Hard to spot issues in 1000+ line diffs
27
+- πŸ”„ **Conflict-prone** - More changes = more merge conflicts
28
+- πŸ“¦ **Risky to deploy** - Big changes = big risk
29
+
30
+### The Stacked PR Solution
31
+
32
+Stacked PRs solve these problems by:
33
+
34
+βœ… **Faster Reviews** - Small, focused PRs get reviewed in minutes, not days
35
+βœ… **Better Code Quality** - Reviewers can focus on specific changes
36
+βœ… **Parallel Development** - Build on unmerged PRs while waiting for review
37
+βœ… **Easier Rollback** - Deploy incrementally, rollback specific changes
38
+βœ… **Clear History** - Each PR tells a story
39
+
40
+### Real-World Example
41
+
42
+**Without Stacks (Traditional):**
43
+```
44
+PR #1: Add user authentication system (2,500 lines, 45 files)
45
+└─ Review time: 3-5 days, high defect rate
46
+```
47
+
48
+**With Stacks:**
49
+```
50
+PR #1: Add user database schema (100 lines, 2 files)
51
+└─ Review time: 15 minutes βœ…
52
+
53
+PR #2: Add auth service layer (200 lines, 3 files) [depends on #1]
54
+└─ Review time: 30 minutes βœ…
55
+
56
+PR #3: Add login/logout endpoints (150 lines, 4 files) [depends on #2]
57
+└─ Review time: 20 minutes βœ…
58
+
59
+PR #4: Add login UI (180 lines, 5 files) [depends on #3]
60
+└─ Review time: 25 minutes βœ…
61
+
62
+Total review time: ~1.5 hours (vs. 3-5 days)
63
+```
64
+
65
+---
66
+
67
+## How It Works
68
+
69
+### Stack Visualization
70
+
71
+```
72
+main branch
73
+ └── PR #123: Add database schema βœ… MERGED
74
+ └── PR #124: Add auth service πŸ” IN REVIEW
75
+ └── PR #125: Add login UI ⏳ WAITING
76
+```
77
+
78
+### Auto-Rebasing
79
+
80
+When PR #123 merges into main:
81
+1. OpenCodeHub automatically rebases PR #124 onto the updated main
82
+2. PR #125 is rebased onto the updated #124
83
+3. No manual work required! πŸŽ‰
84
+
85
+### Dependency Tracking
86
+
87
+OpenCodeHub tracks dependencies automatically:
88
+- Visual stack graph in PR view
89
+- Auto-merge in correct order
90
+- Conflict detection
91
+- Status propagation
92
+
93
+---
94
+
95
+## Getting Started
96
+
97
+### Prerequisites
98
+
99
+- OpenCodeHub account
100
+- Repository with write access
101
+- Git installed locally (or use web UI)
102
+
103
+### Creating Your First Stack (Web UI)
104
+
105
+**Step 1: Create Base PR**
106
+
107
+1. Create a feature branch: `git checkout -b feature/user-auth`
108
+2. Make your first change (e.g., database schema)
109
+3. Commit: `git commit -m "Add user table schema"`
110
+4. Push: `git push origin feature/user-auth`
111
+5. Create PR via web UI: `http://your-instance/owner/repo/compare`
112
+
113
+**Step 2: Stack Second PR**
114
+
115
+1. Create second branch from first: `git checkout -b feature/auth-service`
116
+2. Make your changes
117
+3. Commit: `git commit -m "Add authentication service"`
118
+4. Push: `git push origin feature/auth-service`
119
+5. When creating PR, select **"Stack on PR #123"** in the PR creation form
120
+
121
+**Step 3: Continue Stacking**
122
+
123
+Repeat the process for each layer of your feature!
124
+
125
+### Creating a Stack with OCH CLI
126
+
127
+The CLI makes stacking even easier:
128
+
129
+```bash
130
+# 1. Start from main
131
+git checkout main
132
+git pull
133
+
134
+# 2. Create first branch
135
+och stack create user-schema
136
+# ... make changes ...
137
+git add .
138
+git commit -m "Add user table schema"
139
+
140
+# 3. Create second branch (automatically stacks)
141
+och stack create auth-service
142
+# ... make changes ...
143
+git add .
144
+git commit -m "Add authentication service"
145
+
146
+# 4. Create third branch
147
+och stack create login-ui
148
+# ... make changes ...
149
+git add .
150
+git commit -m "Add login UI"
151
+
152
+# 5. Submit entire stack
153
+och stack submit
154
+
155
+# Result: 3 PRs created, automatically linked!
156
+```
157
+
158
+Install OCH CLI:
159
+```bash
160
+npm install -g opencodehub-cli
161
+och auth login
162
+```
163
+
164
+---
165
+
166
+## Managing Stacks
167
+
168
+### Viewing Your Stack
169
+
170
+**Web UI:**
171
+- Navigate to any PR in the stack
172
+- See **"Stack View"** panel showing all related PRs
173
+- Visual dependency graph
174
+
175
+**CLI:**
176
+```bash
177
+och stack view
178
+
179
+# Output:
180
+# πŸ“š Current Stack
181
+# β”Œβ”€ main (base)
182
+# β”œβ”€ #123: user-schema βœ… Merged
183
+# β”œβ”€ #124: auth-service πŸ” In Review
184
+# └─ #125: login-ui ⏳ Draft
185
+```
186
+
187
+### Updating a PR in the Stack
188
+
189
+When you need to make changes:
190
+
191
+```bash
192
+# Checkout the branch
193
+git checkout feature/auth-service
194
+
195
+# Make changes
196
+# ... edit files ...
197
+
198
+# Commit and push
199
+git add .
200
+git commit -m "Address review feedback"
201
+git push
202
+
203
+# OpenCodeHub automatically updates dependent PRs if needed
204
+```
205
+
206
+### Syncing the Stack
207
+
208
+If main branch advances while your stack is in review:
209
+
210
+```bash
211
+# Automatically rebase entire stack
212
+och stack sync
213
+
214
+# Or manually:
215
+git checkout feature/user-schema
216
+git rebase main
217
+git push --force-with-lease
218
+
219
+# Repeat for each branch in stack
220
+```
221
+
222
+### Merging the Stack
223
+
224
+**Option 1: Merge One at a Time**
225
+1. Get PR #123 approved β†’ Merge
226
+2. OpenCodeHub auto-rebases #124 onto main
227
+3. Get #124 approved β†’ Merge
228
+4. #125 auto-rebases
229
+5. Get #125 approved β†’ Merge
230
+
231
+**Option 2: Use Merge Queue**
232
+```bash
233
+# Add all PRs to merge queue
234
+och queue add 123
235
+och queue add 124
236
+och queue add 125
237
+
238
+# Queue handles:
239
+# - Waiting for approvals
240
+# - Running CI for each
241
+# - Merging in correct order
242
+# - Auto-rebasing
243
+```
244
+
245
+---
246
+
247
+## Best Practices
248
+
249
+### πŸ’‘ Stack Size
250
+
251
+**Ideal stack:**
252
+- 3-5 PRs per stack
253
+- Each PR: 100-300 lines
254
+- Each PR: Single, focused change
255
+
256
+**Too deep?** Consider splitting into multiple independent stacks.
257
+
258
+### πŸ’‘ PR Descriptions
259
+
260
+Make each PR standalone:
261
+
262
+```markdown
263
+# Bad
264
+"Part 2 of user auth"
265
+
266
+# Good
267
+"Add authentication service layer
268
+
269
+Depends on: #123 (database schema)
270
+
271
+This PR adds:
272
+- JWT token generation
273
+- Password hashing with bcrypt
274
+- Session management
275
+
276
+The next PR will add the API endpoints."
277
+```
278
+
279
+### πŸ’‘ Commit Messages
280
+
281
+Use conventional commits:
282
+
283
+```bash
284
+feat: add user authentication schema
285
+feat: implement JWT token service
286
+feat: add login/logout endpoints
287
+feat: create login UI component
288
+```
289
+
290
+### πŸ’‘ Testing
291
+
292
+Test each layer:
293
+
294
+```bash
295
+# In PR #123
296
+npm test -- auth/schema.test.ts
297
+
298
+# In PR #124
299
+npm test -- auth/service.test.ts
300
+
301
+# In PR #125
302
+npm test -- components/Login.test.tsx
303
+```
304
+
305
+### πŸ’‘ Review Strategy
306
+
307
+**For Reviewers:**
308
+- Review PRs in order (bottom β†’ top)
309
+- Approve quickly to unblock the stack
310
+- Focus on the specific change in each PR
311
+
312
+**For Authors:**
313
+- Mark early PRs as "Ready for Review" immediately
314
+- Keep later PRs as "Draft" until earlier ones are approved
315
+- Be responsive to feedback
316
+
317
+### πŸ’‘ Deployment
318
+
319
+Deploy incrementally:
320
+
321
+```bash
322
+# Deploy PR #123
323
+βœ… Database migration - SAFE
324
+
325
+# Deploy PR #124
326
+βœ… Service layer - SAFE (no API changes yet)
327
+
328
+# Deploy PR #125
329
+βœ… Login UI - COMPLETE FEATURE
330
+
331
+# Each deployment is low-risk!
332
+```
333
+
334
+---
335
+
336
+## Troubleshooting
337
+
338
+### "My stack has merge conflicts"
339
+
340
+**Cause:** Base branch (main) changed while your stack was in review.
341
+
342
+**Solution:**
343
+```bash
344
+# Sync the entire stack
345
+och stack sync
346
+
347
+# Or manually rebase each branch
348
+git checkout feature/user-schema
349
+git fetch origin
350
+git rebase origin/main
351
+git push --force-with-lease
352
+
353
+# Then update dependent branches
354
+git checkout feature/auth-service
355
+git rebase feature/user-schema
356
+git push --force-with-lease
357
+
358
+# Repeat for each branch
359
+```
360
+
361
+### "Auto-rebase failed"
362
+
363
+**Cause:** Conflicting changes in dependent PRs.
364
+
365
+**Solution:**
366
+1. Check PR comments for conflict details
367
+2. Manually resolve conflicts:
368
+ ```bash
369
+ git checkout feature/auth-service
370
+ git rebase feature/user-schema
371
+ # Resolve conflicts
372
+ git add .
373
+ git rebase --continue
374
+ git push --force-with-lease
375
+ ```
376
+
377
+### "Can't create stack - PR not found"
378
+
379
+**Cause:** Base PR might not exist or is already merged.
380
+
381
+**Solution:**
382
+```bash
383
+# Check PR status
384
+och pr status 123
385
+
386
+# Verify branch exists
387
+git branch -a | grep feature/user-schema
388
+
389
+# Recreate if needed
390
+git checkout -b feature/new-base main
391
+```
392
+
393
+### "Stack order is wrong"
394
+
395
+**Cause:** PRs created in wrong order or dependencies changed.
396
+
397
+**Solution:**
398
+```bash
399
+# Reorder stack
400
+och stack reorder
401
+
402
+# Or manually update dependencies in web UI:
403
+# PR Settings β†’ Dependencies β†’ Change base PR
404
+```
405
+
406
+### "Too many PRs in my stack"
407
+
408
+**Cause:** Stack grew too large (>5 PRs).
409
+
410
+**Solution:**
411
+1. Merge bottom PRs first
412
+2. Start a new stack for remaining changes
413
+3. Or split into parallel stacks
414
+
415
+---
416
+
417
+## Advanced: Parallel Stacks
418
+
419
+You can work on multiple independent stacks simultaneously:
420
+
421
+```
422
+main
423
+ β”œβ”€β”€ Stack A: User Authentication
424
+ β”‚ β”œβ”€β”€ PR #123: DB schema
425
+ β”‚ β”œβ”€β”€ PR #124: Service layer
426
+ β”‚ └── PR #125: Login UI
427
+ β”‚
428
+ └── Stack B: Payment Integration
429
+ β”œβ”€β”€ PR #126: Payment models
430
+ β”œβ”€β”€ PR #127: Stripe integration
431
+ └── PR #128: Checkout page
432
+```
433
+
434
+**CLI:**
435
+```bash
436
+# View all stacks
437
+och stack list
438
+
439
+# Switch between stacks
440
+git checkout feature/auth-stack
441
+git checkout feature/payment-stack
442
+```
443
+
444
+---
445
+
446
+## See Also
447
+
448
+- [OCH CLI Reference](../../cli/README.md)
449
+- [Smart Merge Queue](merge-queue.md)
450
+- [CI/CD Integration](ci-cd.md)
451
+- [Tutorial: Your First Stack](../tutorials/your-first-stack.md)
452
+
453
+---
454
+
455
+## Need Help?
456
+
457
+- πŸ’¬ [Discord Community](https://discord.gg/opencodehub)
458
+- πŸ› [Report Issues](https://github.com/swadhinbiswas/OpencodeHub/issues)
459
+- πŸ“– [Full Documentation](/)
0
460
β–Όdocs/index.md
+290-0
1
+# OpenCodeHub Documentation
2
+
3
+> Everything you need to know about OpenCodeHub - from getting started to advanced features
4
+
5
+Welcome to OpenCodeHub's comprehensive documentation. Whether you're new to the platform or looking to master advanced features, you'll find everything you need here.
6
+
7
+---
8
+
9
+## πŸš€ Quick Links
10
+
11
+<div class="quick-links">
12
+ <a href="#getting-started">
13
+ <h3>🎯 Getting Started</h3>
14
+ <p>Install and set up OpenCodeHub in 5 minutes</p>
15
+ </a>
16
+
17
+ <a href="#features">
18
+ <h3>✨ Features</h3>
19
+ <p>Learn about stacked PRs, AI review, and more</p>
20
+ </a>
21
+
22
+ <a href="#tutorials">
23
+ <h3>πŸ“š Tutorials</h3>
24
+ <p>Hands-on guides to master OpenCodeHub</p>
25
+ </a>
26
+
27
+ <a href="#api">
28
+ <h3>πŸ”Œ API Reference</h3>
29
+ <p>Integrate with OpenCodeHub programmatically</p>
30
+ </a>
31
+</div>
32
+
33
+---
34
+
35
+## πŸ“– Documentation Sections
36
+
37
+### Getting Started
38
+
39
+New to OpenCodeHub? Start here!
40
+
41
+- **[Installation Guide](getting-started/installation.md)** - Set up OpenCodeHub (Docker or manual)
42
+- **[Quick Start](getting-started/quick-start.md)** - Get up and running in 5 minutes
43
+- **[Create Your First Repository](getting-started/first-repository.md)** - Push your first code
44
+
45
+### Core Features
46
+
47
+Learn about OpenCodeHub's powerful features:
48
+
49
+- **[Stacked Pull Requests](features/stacked-prs.md)** ⭐
50
+ Break large changes into reviewable stacks. Ship code faster.
51
+
52
+- **[AI Code Review](features/ai-review.md)** πŸ€–
53
+ Get instant feedback from GPT-4 or Claude. Catch bugs automatically.
54
+
55
+- **[Smart Merge Queue](features/merge-queue.md)** πŸ”€
56
+ Automate merging with stack-aware CI optimization.
57
+
58
+- **[Developer Metrics](features/developer-metrics.md)** πŸ“Š
59
+ Track velocity, review efficiency, and team performance.
60
+
61
+- **[Slack Integration](features/slack-integration.md)** πŸ’¬
62
+ Get actionable notifications in Slack. Review without leaving chat.
63
+
64
+- **[CI/CD Pipelines](features/ci-cd.md)** βš™οΈ
65
+ GitHub Actions-compatible workflows. Build, test, deploy.
66
+
67
+### Hands-On Tutorials
68
+
69
+Step-by-step guides to master OpenCodeHub:
70
+
71
+- **[Your First Stacked PR](tutorials/your-first-stack.md)** πŸŽ“
72
+ 15-minute tutorial: Create your first stack of PRs
73
+
74
+- **[AI-Powered Code Reviews](tutorials/ai-powered-reviews.md)**
75
+ Set up and use AI review for your projects
76
+
77
+- **[Automated Deployment](tutorials/automated-deployment.md)**
78
+ Build a complete CI/CD pipeline from scratch
79
+
80
+### Guides
81
+
82
+Best practices and workflows:
83
+
84
+- **[Team Workflows](guides/team-workflows.md)** - Collaborate effectively
85
+- **[Branch Protection](guides/branch-protection.md)** - Secure your branches
86
+- **[Webhooks](guides/webhooks.md)** - Integrate with external services
87
+- **[Storage Adapters](guides/storage-adapters.md)** - Configure S3, GCS, or local storage
88
+
89
+### API Reference
90
+
91
+Integrate OpenCodeHub into your tools:
92
+
93
+- **[REST API](api/rest-api.md)** - Complete API reference
94
+- **[Webhooks](api/webhooks-api.md)** - Event-driven integrations
95
+- **[Authentication](api/authentication.md)** - API tokens and OAuth
96
+
97
+### Administration
98
+
99
+For system administrators:
100
+
101
+- **[Deployment Guide](administration/deployment.md)** - Production deployment
102
+- **[Configuration Reference](administration/configuration.md)** - All config options
103
+- **[Security Best Practices](administration/security.md)** - Secure your instance
104
+- **[Monitoring](administration/monitoring.md)** - Monitor health and performance
105
+
106
+### Development
107
+
108
+Contributing to OpenCodeHub:
109
+
110
+- **[Architecture](development/architecture.md)** - System design and components
111
+- **[Database Schema](development/database-schema.md)** - Database structure
112
+- **[Contributing Guide](development/contributing.md)** - How to contribute
113
+- **[Testing Guide](development/testing.md)** - Write and run tests
114
+
115
+---
116
+
117
+## 🎯 Popular Topics
118
+
119
+### Stacked Pull Requests
120
+
121
+The #1 feature that makes OpenCodeHub unique:
122
+
123
+```
124
+main branch
125
+ └── PR #123: Add database schema βœ…
126
+ └── PR #124: Add auth service πŸ”
127
+ └── PR #125: Add login UI ⏳
128
+```
129
+
130
+- [What are Stacked PRs?](features/stacked-prs.md#why-stacked-prs)
131
+- [Create your first stack](tutorials/your-first-stack.md)
132
+- [CLI reference](../cli/README.md)
133
+
134
+### AI Code Review
135
+
136
+Catch bugs before they reach production:
137
+
138
+```python
139
+# AI Review finds:
140
+# πŸ”΄ CRITICAL: SQL injection on line 45
141
+# 🟑 WARNING: N+1 query on line 128
142
+# πŸ”΅ INFO: Consider error handling on line 87
143
+```
144
+
145
+- [Setup AI review](features/ai-review.md#setup)
146
+- [Understanding output](features/ai-review.md#understanding-review-output)
147
+- [Cost optimization](features/ai-review.md#cost-considerations)
148
+
149
+### Merge Queue
150
+
151
+Merge automatically, in the right order:
152
+
153
+```bash
154
+och queue add 123 124 125
155
+# β†’ Queue handles everything:
156
+# - CI runs
157
+# - Rebasing
158
+# - Merging in order
159
+# - Zero manual work
160
+```
161
+
162
+- [How it works](features/merge-queue.md#how-it-works)
163
+- [Queue configuration](features/merge-queue.md#configuration)
164
+- [Best practices](features/merge-queue.md#best-practices)
165
+
166
+---
167
+
168
+## πŸ”§ Installation
169
+
170
+### Quick Start (Docker)
171
+
172
+```bash
173
+# 1. Download docker-compose.yml
174
+curl -O https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/docker-compose.yml
175
+
176
+# 2. Configure
177
+cp .env.example .env
178
+# Edit .env with your settings
179
+
180
+# 3. Start
181
+docker-compose up -d
182
+
183
+# 4. Create admin user
184
+docker-compose exec app bun run scripts/seed-admin.ts
185
+
186
+# 5. Open
187
+open http://localhost:3000
188
+```
189
+
190
+[Full installation guide β†’](getting-started/installation.md)
191
+
192
+### Requirements
193
+
194
+- **Docker**: 20.10+ (recommended)
195
+- **OR Node.js**: 18+ with Bun or npm
196
+- **Database**: PostgreSQL 14+ or SQLite
197
+- **Storage**: Local, S3, Google Cloud Storage, or Azure Blob
198
+
199
+---
200
+
201
+## πŸŽ“ Learning Paths
202
+
203
+### For Developers
204
+
205
+1. [Quick Start](getting-started/quick-start.md) - Get familiar with the platform
206
+2. [Your First Stack](tutorials/your-first-stack.md) - Learn stacked PRs
207
+3. [Team Workflows](guides/team-workflows.md) - Collaborate effectively
208
+
209
+### For Teams
210
+
211
+1. [Installation](getting-started/installation.md) - Set up your instance
212
+2. [Team Workflows](guides/team-workflows.md) - Define your process
213
+3. [Branch Protection](guides/branch-protection.md) - Secure your code
214
+4. [CI/CD Setup](features/ci-cd.md) - Automate everything
215
+
216
+### For Administrators
217
+
218
+1. [Deployment Guide](administration/deployment.md) - Production setup
219
+2. [Security Best Practices](administration/security.md) - Secure your instance
220
+3. [Monitoring](administration/monitoring.md) - Track health
221
+4. [Configuration](administration/configuration.md) - Tune performance
222
+
223
+---
224
+
225
+## πŸ’¬ Community & Support
226
+
227
+### Get Help
228
+
229
+- **πŸ“– Documentation** - You're here!
230
+- **πŸ’¬ [Discord](https://discord.gg/opencodehub)** - Real-time chat, community support
231
+- **πŸ› [GitHub Issues](https://github.com/swadhinbiswas/OpencodeHub/issues)** - Bug reports, feature requests
232
+- **πŸ“§ [Email Support](mailto:[email protected])** - Direct support
233
+
234
+### Contributing
235
+
236
+We welcome contributions!
237
+
238
+- **[Contributing Guide](development/contributing.md)** - How to contribute
239
+- **[Good First Issues](https://github.com/swadhinbiswas/OpencodeHub/labels/good-first-issue)** - Easy issues for new contributors
240
+- **[Roadmap](https://github.com/swadhinbiswas/OpencodeHub/projects)** - What's coming next
241
+
242
+---
243
+
244
+## πŸ“ Recent Updates
245
+
246
+### December 2024
247
+
248
+- ✨ Added AI code review with GPT-4 and Claude support
249
+- πŸš€ Improved merge queue performance (60% faster)
250
+- πŸ“Š New developer metrics dashboard
251
+- πŸ”§ 100+ bug fixes and improvements
252
+
253
+[Full changelog β†’](../CHANGELOG.md)
254
+
255
+---
256
+
257
+## 🌟 Why OpenCodeHub?
258
+
259
+| Feature | GitHub | GitLab | OpenCodeHub |
260
+|---------|--------|--------|-------------|
261
+| Stacked PRs | ❌ | ❌ | βœ… Native support |
262
+| AI Code Review | ⚠️ Copilot only | ❌ | βœ… GPT-4 & Claude |
263
+| Smart Merge Queue | ⚠️ Basic | ⚠️ Basic | βœ… Stack-aware |
264
+| Self-Hosted | ❌ | βœ… Complex | βœ… Simple |
265
+| Cost | $$$ | $$$ | Free |
266
+
267
+---
268
+
269
+## πŸ” Search Documentation
270
+
271
+Can't find what you're looking for?
272
+
273
+- Use the search bar above
274
+- Check the [FAQ](#faq)
275
+- Ask in [Discord](https://discord.gg/opencodehub)
276
+- Browse [GitHub Discussions](https://github.com/swadhinbiswas/OpencodeHub/discussions)
277
+
278
+---
279
+
280
+## πŸ“„ License
281
+
282
+OpenCodeHub is open source under the MIT License.
283
+
284
+[View license β†’](../LICENSE)
285
+
286
+---
287
+
288
+**Ready to get started?**
289
+
290
+[Install OpenCodeHub β†’](getting-started/installation.md) | [Quick Start β†’](getting-started/quick-start.md) | [Your First Stack β†’](tutorials/your-first-stack.md)
0
291
β–Όdocs/tutorials/your-first-stack.md
+561-0
1
+# Tutorial: Your First Stacked PR
2
+
3
+> Learn how to split a large feature into reviewable chunks in 15 minutes
4
+
5
+This hands-on tutorial teaches you how to use Stacked Pull Requests to break down a large feature into small, focused PRs that ship faster.
6
+
7
+## What You'll Learn
8
+
9
+- Create a stack of 3 dependent PRs
10
+- Use the OCH CLI for efficient workflow
11
+- Navigate the review process
12
+- Merge the stack successfully
13
+
14
+**Time:** 15-20 minutes
15
+**Difficulty:** Beginner
16
+
17
+---
18
+
19
+## Prerequisites
20
+
21
+Before starting, make sure you have:
22
+
23
+- [ ] OpenCodeHub account
24
+- [ ] A repository where you have write access
25
+- [ ] Git installed locally
26
+- [ ] OCH CLI installed (`npm install -g opencodehub-cli`)
27
+- [ ] Basic Git knowledge (branch, commit, push)
28
+
29
+**First time with OCH CLI?**
30
+```bash
31
+# Install
32
+npm install -g opencodehub-cli
33
+
34
+# Login
35
+och auth login
36
+# Enter your email and password when prompted
37
+```
38
+
39
+---
40
+
41
+## The Scenario
42
+
43
+You're building a **user authentication system**. Instead of creating one massive PR, you'll split it into 3 logical layers:
44
+
45
+1. **Database Layer** - User table schema
46
+2. **Service Layer** - Authentication logic
47
+3. **API Layer** - Login/logout endpoints
48
+
49
+Each PR builds on the previous one, but can be reviewed independently.
50
+
51
+---
52
+
53
+## Step 1: Set Up Your Workspace (2 min)
54
+
55
+**1.1. Clone your repository:**
56
+
57
+```bash
58
+cd ~/projects
59
+git clone https://github.com/yourorg/your-repo.git
60
+cd your-repo
61
+```
62
+
63
+**1.2. Ensure main is up to date:**
64
+
65
+```bash
66
+git checkout main
67
+git pull origin main
68
+```
69
+
70
+**1.3. Verify OCH CLI is connected:**
71
+
72
+```bash
73
+och auth whoami
74
+# Should show: "Logged in as: [email protected]"
75
+```
76
+
77
+---
78
+
79
+## Step 2: Create the Database Layer PR (4 min)
80
+
81
+This is the foundation of your stack.
82
+
83
+**2.1. Create the first branch:**
84
+
85
+```bash
86
+och stack create auth-database
87
+```
88
+
89
+This creates a new branch `stack/auth-database` from `main`.
90
+
91
+**2.2. Create the migration file:**
92
+
93
+```bash
94
+mkdir -p db/migrations
95
+cat > db/migrations/001_create_users_table.sql << 'EOF'
96
+CREATE TABLE users (
97
+ id SERIAL PRIMARY KEY,
98
+ email VARCHAR(255) UNIQUE NOT NULL,
99
+ password_hash VARCHAR(255) NOT NULL,
100
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
101
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
102
+);
103
+
104
+CREATE INDEX idx_users_email ON users(email);
105
+EOF
106
+```
107
+
108
+**2.3. Commit your changes:**
109
+
110
+```bash
111
+git add db/migrations/
112
+git commit -m "feat: add users table migration
113
+
114
+- Create users table with email and password_hash
115
+- Add email index for fast lookups
116
+- Set up timestamps"
117
+```
118
+
119
+**2.4. Submit to create the PR:**
120
+
121
+```bash
122
+och stack submit
123
+
124
+# Output:
125
+# βœ“ Pushing stack/auth-database
126
+# βœ“ Creating pull request
127
+#
128
+# PR #123 created: feat: add users table migration
129
+# https://git.yourcompany.com/yourorg/your-repo/pulls/123
130
+```
131
+
132
+**2.5. Verify in web UI:**
133
+
134
+Open the PR link. You should see:
135
+- Title: "feat: add users table migration"
136
+- Files changed: `db/migrations/001_create_users_table.sql`
137
+- Status: Draft or Ready for Review
138
+
139
+---
140
+
141
+## Step 3: Create the Service Layer PR (4 min)
142
+
143
+Now, build on top of your first PR.
144
+
145
+**3.1. Create the second stacked branch:**
146
+
147
+```bash
148
+och stack create auth-service
149
+```
150
+
151
+This creates `stack/auth-service` from your current branch automatically.
152
+
153
+**3.2. Create the auth service:**
154
+
155
+```bash
156
+mkdir -p src/services
157
+cat > src/services/auth.ts << 'EOF'
158
+import bcrypt from 'bcrypt';
159
+import jwt from 'jsonwebtoken';
160
+import { db } from '../db';
161
+
162
+export class AuthService {
163
+ async register(email: string, password: string) {
164
+ const hash = await bcrypt.hash(password, 10);
165
+
166
+ const user = await db.users.create({
167
+ email,
168
+ password_hash: hash
169
+ });
170
+
171
+ return { id: user.id, email: user.email };
172
+ }
173
+
174
+ async login(email: string, password: string) {
175
+ const user = await db.users.findByEmail(email);
176
+
177
+ if (!user) {
178
+ throw new Error('Invalid credentials');
179
+ }
180
+
181
+ const valid = await bcrypt.compare(password, user.password_hash);
182
+
183
+ if (!valid) {
184
+ throw new Error('Invalid credentials');
185
+ }
186
+
187
+ const token = jwt.sign(
188
+ { userId: user.id, email: user.email },
189
+ process.env.JWT_SECRET!,
190
+ { expiresIn: '7d' }
191
+ );
192
+
193
+ return { token, user: { id: user.id, email: user.email } };
194
+ }
195
+}
196
+EOF
197
+```
198
+
199
+**3.3. Commit:**
200
+
201
+```bash
202
+git add src/services/
203
+git commit -m "feat: implement authentication service
204
+
205
+- Add user registration with bcrypt hashing
206
+- Add login with JWT token generation
207
+- Implement password verification
208
+
209
+Depends on: PR #123 (users table)"
210
+```
211
+
212
+**3.4. Submit:**
213
+
214
+```bash
215
+och stack submit
216
+
217
+# Output:
218
+# βœ“ Pushing stack/auth-service
219
+# βœ“ Creating pull request
220
+# βœ“ Linking to base PR #123
221
+#
222
+# PR #124 created: feat: implement authentication service
223
+# Stack: #123 β†’ #124
224
+# https://git.yourcompany.com/yourorg/your-repo/pulls/124
225
+```
226
+
227
+**3.5. Check the stack:**
228
+
229
+```bash
230
+och stack view
231
+
232
+# Output:
233
+# πŸ“š Current Stack
234
+# β”Œβ”€ main (base)
235
+# β”œβ”€ #123: auth-database ⏳ Draft
236
+# └─ #124: auth-service ⏳ Draft
237
+```
238
+
239
+Perfect! You now have a stack of 2 PRs.
240
+
241
+---
242
+
243
+## Step 4: Create the API Layer PR (4 min)
244
+
245
+Final layer: the HTTP endpoints.
246
+
247
+**4.1. Create third branch:**
248
+
249
+```bash
250
+och stack create auth-api
251
+```
252
+
253
+**4.2. Create API endpoints:**
254
+
255
+```bash
256
+mkdir -p src/api
257
+cat > src/api/auth.ts << 'EOF'
258
+import { Router } from 'express';
259
+import { AuthService } from '../services/auth';
260
+
261
+const router = Router();
262
+const authService = new AuthService();
263
+
264
+router.post('/register', async (req, res) => {
265
+ try {
266
+ const { email, password } = req.body;
267
+
268
+ // Validation
269
+ if (!email || !password) {
270
+ return res.status(400).json({ error: 'Email and password required' });
271
+ }
272
+
273
+ const user = await authService.register(email, password);
274
+
275
+ res.status(201).json({ user });
276
+ } catch (error) {
277
+ res.status(500).json({ error: error.message });
278
+ }
279
+});
280
+
281
+router.post('/login', async (req, res) => {
282
+ try {
283
+ const { email, password } = req.body;
284
+
285
+ const result = await authService.login(email, password);
286
+
287
+ res.json(result);
288
+ } catch (error) {
289
+ res.status(401).json({ error: error.message });
290
+ }
291
+});
292
+
293
+export default router;
294
+EOF
295
+```
296
+
297
+**4.3. Commit and submit:**
298
+
299
+```bash
300
+git add src/api/
301
+git commit -m "feat: add authentication API endpoints
302
+
303
+- POST /register - create new user
304
+- POST /login - authenticate and get token
305
+- Input validation
306
+- Error handling
307
+
308
+Depends on: PR #124 (auth service)"
309
+
310
+och stack submit
311
+
312
+# Output:
313
+# βœ“ Pushing stack/auth-api
314
+# βœ“ Creating pull request
315
+# βœ“ Linking to base PR #124
316
+#
317
+# PR #125 created: feat: add authentication API endpoints
318
+# Stack: #123 β†’ #124 β†’ #125
319
+```
320
+
321
+**4.4. View complete stack:**
322
+
323
+```bash
324
+och stack view
325
+
326
+# Output:
327
+# πŸ“š Current Stack
328
+# β”Œβ”€ main (base)
329
+# β”œβ”€ #123: auth-database ⏳ Draft
330
+# β”œβ”€ #124: auth-service ⏳ Draft
331
+# └─ #125: auth-api ⏳ Draft (current)
332
+```
333
+
334
+πŸŽ‰ **Congratulations!** You've created your first stack of 3 PRs!
335
+
336
+---
337
+
338
+## Step 5: Navigate the Review Process (3 min)
339
+
340
+Now let's get these PRs reviewed and merged.
341
+
342
+**5.1. Mark PRs as ready:**
343
+
344
+```bash
345
+# Mark first PR ready for review
346
+curl -X PATCH https://git.yourcompany.com/api/prs/123 \
347
+ -H "Authorization: Bearer $TOKEN" \
348
+ -d '{"draft": false}'
349
+
350
+# Or via web UI: Click "Ready for Review" on PR #123
351
+```
352
+
353
+**5.2. Request reviews:**
354
+
355
+In the web UI for PR #123:
356
+1. Click "Reviewers" β†’ Select team members
357
+2. Add comment: "This is the first PR in a stack of 3. Reviewing these in order would be helpful!"
358
+
359
+**5.3. While waiting, add tests:**
360
+
361
+```bash
362
+# Switch back to first branch
363
+git checkout stack/auth-database
364
+
365
+# Add test file
366
+cat > db/migrations/001_create_users_table.test.ts << 'EOF'
367
+import { test } from 'vitest';
368
+import { db } from '../db';
369
+
370
+test('creates users table', async () => {
371
+ // Run migration
372
+ await db.migrate();
373
+
374
+ // Verify table exists
375
+ const tables = await db.raw(`
376
+ SELECT table_name
377
+ FROM information_schema.tables
378
+ WHERE table_name = 'users'
379
+ `);
380
+
381
+ expect(tables.length).toBe(1);
382
+});
383
+EOF
384
+
385
+git add .
386
+git commit -m "test: add migration test"
387
+git push
388
+
389
+# PR #123 automatically updates!
390
+```
391
+
392
+**5.4. Monitor review progress:**
393
+
394
+```bash
395
+# Check PR status
396
+och pr status 123
397
+
398
+# Output:
399
+# PR #123: feat: add users table migration
400
+# Status: πŸ” In Review
401
+# Approvals: 1/1 required
402
+# CI: βœ… Passing
403
+# Comments: 2
404
+```
405
+
406
+---
407
+
408
+## Step 6: Merge the Stack (2 min)
409
+
410
+Once PR #123 is approved, the magic happens!
411
+
412
+**6.1. Use the merge queue:**
413
+
414
+```bash
415
+# Add all PRs to queue
416
+och queue add 123 124 125
417
+
418
+# Or add individually as they get approved:
419
+och queue add 123 # Merges first
420
+# PR #124 auto-rebases onto new main
421
+och queue add 124 # Merges second
422
+# PR #125 auto-rebases onto new main
423
+och queue add 125 # Merges third
424
+```
425
+
426
+**6.2. Watch it merge automatically:**
427
+
428
+```bash
429
+# Monitor queue
430
+och queue watch
431
+
432
+# Output (live updates):
433
+# Position PR Status ETA
434
+# 1 #123 πŸƒ Running CI 1 min
435
+# 2 #124 ⏳ Waiting 3 min
436
+# 3 #125 ⏳ Waiting 5 min
437
+#
438
+# [1 min later]
439
+# βœ… PR #123 merged!
440
+# πŸ”„ Rebasing PR #124...
441
+# πŸƒ Running CI for #124...
442
+#
443
+# [2 min later]
444
+# βœ… PR #124 merged!
445
+# πŸ”„ Rebasing PR #125...
446
+```
447
+
448
+**6.3. Celebrate! πŸŽ‰**
449
+
450
+```bash
451
+# Check final status
452
+git checkout main
453
+git pull
454
+
455
+git log --oneline -3
456
+
457
+# Output:
458
+# abc123 feat: add authentication API endpoints
459
+# def456 feat: implement authentication service
460
+# ghi789 feat: add users table migration
461
+```
462
+
463
+All 3 PRs merged! Your auth system is live!
464
+
465
+---
466
+
467
+## What You've Learned
468
+
469
+βœ… How to create a stack of dependent PRs
470
+βœ… Using `och stack create` for easy stacking
471
+βœ… Submitting stacks with `och stack submit`
472
+βœ… Viewing stack visualization
473
+βœ… Using the merge queue for automatic merging
474
+βœ… How auto-rebasing works
475
+
476
+## Key Takeaways
477
+
478
+**Stacked PRs are better because:**
479
+- Each PR was ~50-100 lines (easy to review)
480
+- Reviews happened in parallel (faster)
481
+- Each merge was low-risk (incremental)
482
+- Clear history (logical progression)
483
+
484
+**vs. One Large PR:**
485
+- Would be ~200-300 lines
486
+- Single review bottleneck
487
+- High-risk merge
488
+- Messy history
489
+
490
+---
491
+
492
+## Next Steps
493
+
494
+### Practice More
495
+
496
+Try stacking your next feature:
497
+- 🎨 Frontend feature β†’ Split UI, logic, styles
498
+- πŸ”§ Backend feature β†’ Split models, services, controllers
499
+- πŸ“Š Data pipeline β†’ Split ingestion, processing, output
500
+
501
+### Advanced Techniques
502
+
503
+Learn more about:
504
+- [Parallel stacks](../features/stacked-prs.md#parallel-stacks)
505
+- [Stack reordering](../features/stacked-prs.md#reordering)
506
+- [AI code review](../features/ai-review.md) on stacks
507
+- [Advanced CLI commands](../../cli/README.md)
508
+
509
+### Join the Community
510
+
511
+- πŸ’¬ [Discord](https://discord.gg/opencodehub) - Ask questions
512
+- πŸ“– [Documentation](/) - Read more guides
513
+- πŸ› [GitHub](https://github.com/swadhinbiswas/OpencodeHub) - Report issues
514
+
515
+---
516
+
517
+## Troubleshooting
518
+
519
+**"och command not found"**
520
+
521
+```bash
522
+# Install CLI
523
+npm install -g opencodehub-cli
524
+
525
+# Or use npx
526
+npx opencodehub-cli stack create auth-database
527
+```
528
+
529
+**"Permission denied"**
530
+
531
+```bash
532
+# Login again
533
+och auth login
534
+
535
+# Verify permissions
536
+och auth whoami
537
+```
538
+
539
+**"Can't create stack - conflicts"**
540
+
541
+```bash
542
+# Make sure main is up to date
543
+git checkout main
544
+git pull
545
+
546
+# Start fresh
547
+git checkout -b stack/auth-database main
548
+```
549
+
550
+**"PR not linking to stack"**
551
+
552
+```bash
553
+# Manually link via web UI:
554
+# Go to PR β†’ Settings β†’ Dependencies β†’ Select base PR
555
+```
556
+
557
+---
558
+
559
+**Congratulations on completing your first stacked PR workflow!** πŸš€
560
+
561
+You're now ready to ship code faster with OpenCodeHub.
0
562
β–Όdrizzle.config.ts
+6-1
5
5
const url = process.env.DATABASE_URL || "./data/opencodehub.db";
6
6
7
7
// Map driver to Drizzle dialect
8
-const dialectMap: Record<string, "sqlite" | "postgresql" | "mysql"> = {
8
+const dialectMap: Record<string, "sqlite" | "postgresql" | "mysql" | "turso"> = {
9
9
sqlite: "sqlite",
10
10
postgres: "postgresql",
11
11
mysql: "mysql",
12
+ libsql: "turso",
13
+ turso: "turso",
12
14
};
13
15
14
16
const dialect = dialectMap[driver] || "sqlite";
19
21
case "postgres":
20
22
case "mysql":
21
23
return { url };
24
+ case "libsql":
25
+ case "turso":
26
+ return { url, authToken: process.env.DATABASE_AUTH_TOKEN };
22
27
case "sqlite":
23
28
default:
24
29
return { url };
25
30
β–Όpackage.json
+11-1
3
3
"version": "1.0.0",
4
4
"description": "A fully self-hosted, open-source GitHub/GitLab alternative with SSH git hosting, pipelines/actions, and advanced UI",
5
5
"type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/swadhinbiswas/OpencodeHub.git"
9
+ },
10
+ "homepage": "https://github.com/swadhinbiswas/OpencodeHub#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/swadhinbiswas/OpencodeHub/issues"
13
+ },
6
14
"scripts": {
7
15
"dev": "astro dev",
8
16
"start": "astro dev",
23
31
"@astrojs/tailwind": "^5.1.0",
24
32
"@aws-sdk/client-s3": "^3.948.0",
25
33
"@aws-sdk/s3-request-presigner": "^3.948.0",
34
+ "@libsql/client": "^0.15.15",
26
35
"@monaco-editor/react": "^4.6.0",
27
36
"@opentelemetry/auto-instrumentations-node": "^0.67.2",
28
37
"@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
53
62
"@radix-ui/react-toggle-group": "^1.1.0",
54
63
"@radix-ui/react-tooltip": "^1.1.2",
55
64
"@types/nodemailer": "^7.0.4",
65
+ "@upstash/redis": "^1.36.0",
56
66
"astro": "^4.11.0",
57
67
"bcryptjs": "^2.4.3",
58
68
"better-sqlite3": "^11.1.2",
125
135
"engines": {
126
136
"node": ">=18.0.0"
127
137
}
128
-}
138
+}
129
139
β–Όsrc/components/home/FeatureDemosSection.tsx
+427-0
1
+"use client";
2
+import { useState } from "react";
3
+import { motion, AnimatePresence } from "framer-motion";
4
+import { Layers, Bot, GitMerge, BarChart3, MessageSquare, Terminal as TerminalIcon, Check, X, Clock, ArrowRight } from "lucide-react";
5
+
6
+const demos = [
7
+ {
8
+ id: "stacked-prs",
9
+ title: "Stacked Pull Requests",
10
+ icon: Layers,
11
+ color: "from-blue-500 to-cyan-500",
12
+ description: "Break large features into reviewable chunks",
13
+ demo: {
14
+ type: "stack-visualization",
15
+ items: [
16
+ { id: 1, title: "Add database schema", status: "merged", pr: "#123" },
17
+ { id: 2, title: "Add authentication service", status: "approved", pr: "#124", dependsOn: 1 },
18
+ { id: 3, title: "Add login UI", status: "review", pr: "#125", dependsOn: 2 },
19
+ ],
20
+ },
21
+ },
22
+ {
23
+ id: "ai-review",
24
+ title: "AI Code Review",
25
+ icon: Bot,
26
+ color: "from-purple-500 to-pink-500",
27
+ description: "Get instant feedback from AI reviewers",
28
+ demo: {
29
+ type: "ai-feedback",
30
+ issues: [
31
+ { severity: "critical", line: 45, message: "SQL injection vulnerability detected", fix: "Use parameterized queries" },
32
+ { severity: "warning", line: 128, message: "N+1 query in loop", fix: "Use batch loading" },
33
+ { severity: "info", line: 87, message: "Consider adding error handling", fix: "Wrap in try-catch" },
34
+ ],
35
+ },
36
+ },
37
+ {
38
+ id: "merge-queue",
39
+ title: "Smart Merge Queue",
40
+ icon: GitMerge,
41
+ color: "from-green-500 to-emerald-500",
42
+ description: "Stack-aware merging with CI optimization",
43
+ demo: {
44
+ type: "queue-status",
45
+ queue: [
46
+ { pr: "#125", title: "Login UI", status: "running", ci: 75 },
47
+ { pr: "#124", title: "Auth service", status: "waiting", dependsOn: "#123" },
48
+ { pr: "#123", title: "Database schema", status: "ready" },
49
+ ],
50
+ },
51
+ },
52
+ {
53
+ id: "metrics",
54
+ title: "Developer Metrics",
55
+ icon: BarChart3,
56
+ color: "from-orange-500 to-red-500",
57
+ description: "Track velocity and review efficiency",
58
+ demo: {
59
+ type: "metrics-dashboard",
60
+ data: {
61
+ prsMerged: { value: 24, change: "+12%" },
62
+ avgTimeToMerge: { value: "8.2h", change: "-3h" },
63
+ stackUsage: { value: "65%", change: "+15%" },
64
+ reviewTime: { value: "22min", change: "-5min" },
65
+ },
66
+ },
67
+ },
68
+ {
69
+ id: "slack",
70
+ title: "Slack Notifications",
71
+ icon: MessageSquare,
72
+ color: "from-indigo-500 to-blue-500",
73
+ description: "Actionable notifications in Slack",
74
+ demo: {
75
+ type: "slack-message",
76
+ message: {
77
+ author: "swadhin",
78
+ action: "opened",
79
+ pr: "#125",
80
+ title: "Add login UI",
81
+ stack: ["#123", "#124", "#125"],
82
+ stats: { files: 32, additions: 420, deletions: 18 },
83
+ },
84
+ },
85
+ },
86
+ {
87
+ id: "cli",
88
+ title: "OCH CLI",
89
+ icon: TerminalIcon,
90
+ color: "from-cyan-500 to-teal-500",
91
+ description: "Powerful terminal workflow",
92
+ demo: {
93
+ type: "terminal",
94
+ commands: [
95
+ { cmd: "och stack create feature-name", output: "βœ“ Created branch stack/feature-name" },
96
+ { cmd: "och stack submit", output: "βœ“ Pushed and created PR #125" },
97
+ { cmd: "och queue add 125", output: "βœ“ Added to merge queue (position 1)" },
98
+ ],
99
+ },
100
+ },
101
+];
102
+
103
+function StackVisualization({ items }: any) {
104
+ return (
105
+ <div className="space-y-2">
106
+ <div className="text-xs text-muted-foreground pl-4">main (base)</div>
107
+ {items.map((item: any, idx: number) => (
108
+ <motion.div
109
+ key={item.id}
110
+ initial={{ opacity: 0, x: -20 }}
111
+ animate={{ opacity: 1, x: 0 }}
112
+ transition={{ delay: idx * 0.1 }}
113
+ className="flex items-center gap-3">
114
+ <div className="w-8 flex flex-col items-center">
115
+ <div className="h-4 border-l-2 border-muted" />
116
+ <div className="w-2 h-2 rounded-full bg-primary" />
117
+ {idx < items.length - 1 && <div className="h-12 border-l-2 border-dashed border-muted" />}
118
+ </div>
119
+ <div className="flex-1 p-3 rounded-lg border border-white/5 bg-card/50">
120
+ <div className="flex items-center justify-between mb-1">
121
+ <span className="text-sm font-medium">{item.pr}: {item.title}</span>
122
+ <span
123
+ className={`text-xs px-2 py-0.5 rounded-full ${item.status === "merged"
124
+ ? "bg-green-500/20 text-green-400"
125
+ : item.status === "approved"
126
+ ? "bg-blue-500/20 text-blue-400"
127
+ : "bg-yellow-500/20 text-yellow-400"
128
+ }`}>
129
+ {item.status}
130
+ </span>
131
+ </div>
132
+ {item.dependsOn && (
133
+ <div className="text-xs text-muted-foreground">
134
+ Depends on PR #{items.find((i: any) => i.id === item.dependsOn)?.pr.slice(1)}
135
+ </div>
136
+ )}
137
+ </div>
138
+ </motion.div>
139
+ ))}
140
+ </div>
141
+ );
142
+}
143
+
144
+function AIFeedback({ issues }: any) {
145
+ const severityConfig: Record<string, { bg: string; text: string; icon: any }> = {
146
+ critical: { bg: "bg-red-500/20", text: "text-red-400", icon: X },
147
+ warning: { bg: "bg-yellow-500/20", text: "text-yellow-400", icon: Clock },
148
+ info: { bg: "bg-blue-500/20", text: "text-blue-400", icon: Check },
149
+ };
150
+
151
+ return (
152
+ <div className="space-y-3">
153
+ {issues.map((issue: any, idx: number) => {
154
+ const config = severityConfig[issue.severity];
155
+ const Icon = config.icon;
156
+ return (
157
+ <motion.div
158
+ key={idx}
159
+ initial={{ opacity: 0, y: 20 }}
160
+ animate={{ opacity: 1, y: 0 }}
161
+ transition={{ delay: idx * 0.15 }}
162
+ className="p-3 rounded-lg border border-white/5 bg-card/50 space-y-2">
163
+ <div className="flex items-start gap-3">
164
+ <div className={`p-1.5 rounded ${config.bg}`}>
165
+ <Icon className={`h-4 w-4 ${config.text}`} />
166
+ </div>
167
+ <div className="flex-1">
168
+ <div className="flex items-center gap-2 mb-1">
169
+ <span className="text-xs font-mono text-muted-foreground">Line {issue.line}</span>
170
+ <span className={`text-xs uppercase font-semibold ${config.text}`}>
171
+ {issue.severity}
172
+ </span>
173
+ </div>
174
+ <p className="text-sm">{issue.message}</p>
175
+ <p className="text-xs text-muted-foreground mt-1">πŸ’‘ {issue.fix}</p>
176
+ </div>
177
+ </div>
178
+ </motion.div>
179
+ );
180
+ })}
181
+ </div>
182
+ );
183
+}
184
+
185
+function QueueStatus({ queue }: any) {
186
+ return (
187
+ <div className="space-y-2">
188
+ {queue.map((item: any, idx: number) => (
189
+ <motion.div
190
+ key={idx}
191
+ initial={{ opacity: 0, x: -20 }}
192
+ animate={{ opacity: 1, x: 0 }}
193
+ transition={{ delay: idx * 0.1 }}
194
+ className="p-3 rounded-lg border border-white/5 bg-card/50">
195
+ <div className="flex items-center justify-between mb-2">
196
+ <div>
197
+ <span className="text-sm font-medium">
198
+ {item.pr}: {item.title}
199
+ </span>
200
+ </div>
201
+ <span className="text-xs text-muted-foreground">Position {idx + 1}</span>
202
+ </div>
203
+ {item.status === "running" && item.ci !== undefined && (
204
+ <div>
205
+ <div className="flex items-center justify-between text-xs mb-1">
206
+ <span className="text-muted-foreground">⏳ Running CI</span>
207
+ <span className="text-primary">{item.ci}%</span>
208
+ </div>
209
+ <div className="h-1.5 bg-background rounded-full overflow-hidden">
210
+ <motion.div
211
+ initial={{ width: 0 }}
212
+ animate={{ width: `${item.ci}%` }}
213
+ transition={{ duration: 1.5 }}
214
+ className="h-full bg-primary"
215
+ />
216
+ </div>
217
+ </div>
218
+ )}
219
+ {item.dependsOn && (
220
+ <div className="text-xs text-muted-foreground mt-1">⏸️ Waiting for {item.dependsOn}</div>
221
+ )}
222
+ {item.status === "ready" && <div className="text-xs text-green-400 mt-1">βœ… Ready to merge</div>}
223
+ </motion.div>
224
+ ))}
225
+ </div>
226
+ );
227
+}
228
+
229
+function MetricsDashboard({ data }: any) {
230
+ const metrics = [
231
+ { label: "PRs Merged", key: "prsMerged" },
232
+ { label: "Avg Time to Merge", key: "avgTimeToMerge" },
233
+ { label: "Stack Usage", key: "stackUsage" },
234
+ { label: "Review Time", key: "reviewTime" },
235
+ ];
236
+
237
+ return (
238
+ <div className="grid grid-cols-2 gap-3">
239
+ {metrics.map((metric, idx) => (
240
+ <motion.div
241
+ key={metric.key}
242
+ initial={{ opacity: 0, scale: 0.9 }}
243
+ animate={{ opacity: 1, scale: 1 }}
244
+ transition={{ delay: idx * 0.1 }}
245
+ className="p-4 rounded-lg border border-white/5 bg-card/50">
246
+ <div className="text-xs text-muted-foreground mb-2">{metric.label}</div>
247
+ <div className="text-2xl font-bold">{data[metric.key].value}</div>
248
+ <div className="text-xs text-green-400 mt-1">{data[metric.key].change}</div>
249
+ </motion.div>
250
+ ))}
251
+ </div>
252
+ );
253
+}
254
+
255
+function SlackMessage({ message }: any) {
256
+ return (
257
+ <motion.div
258
+ initial={{ opacity: 0, y: 20 }}
259
+ animate={{ opacity: 1, y: 0 }}
260
+ className="p-4 rounded-lg border border-white/5 bg-card/50 space-y-3">
261
+ <div className="flex items-start gap-3">
262
+ <div className="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center text-xs font-medium">
263
+ {message.author[0].toUpperCase()}
264
+ </div>
265
+ <div className="flex-1">
266
+ <div className="text-sm">
267
+ <span className="font-medium">{message.author}</span> <span className="text-muted-foreground">{message.action}</span>{" "}
268
+ <span className="font-medium">
269
+ {message.pr}: {message.title}
270
+ </span>
271
+ </div>
272
+ <div className="text-xs text-muted-foreground mt-1">
273
+ Stack: {message.stack.join(" β†’ ")} β€’ {message.stats.files} files β€’ +{message.stats.additions}/-
274
+ {message.stats.deletions}
275
+ </div>
276
+ </div>
277
+ </div>
278
+ <div className="flex gap-2">
279
+ {["Approve", "Request Changes", "View PR", "Add to Queue"].map((action) => (
280
+ <button
281
+ key={action}
282
+ className="px-3 py-1.5 text-xs rounded border border-white/10 bg-white/5 hover:bg-white/10 transition-colors">
283
+ {action}
284
+ </button>
285
+ ))}
286
+ </div>
287
+ </motion.div>
288
+ );
289
+}
290
+
291
+function Terminal({ commands }: any) {
292
+ const [currentIndex, setCurrentIndex] = useState(0);
293
+
294
+ if (!commands || commands.length === 0) {
295
+ return null;
296
+ }
297
+
298
+ return (
299
+ <div className="p-4 rounded-lg bg-black/80 border border-white/10 font-mono text-sm">
300
+ <div className="flex items-center gap-2 mb-3">
301
+ <div className="flex gap-1.5">
302
+ <div className="w-3 h-3 rounded-full bg-red-500" />
303
+ <div className="w-3 h-3 rounded-full bg-yellow-500" />
304
+ <div className="w-3 h-3 rounded-full bg-green-500" />
305
+ </div>
306
+ <span className="text-xs text-muted-foreground">zsh</span>
307
+ </div>
308
+ <AnimatePresence mode="wait">
309
+ {commands.slice(0, currentIndex + 1).map((cmd: any, idx: number) => (
310
+ <motion.div
311
+ key={idx}
312
+ initial={{ opacity: 0 }}
313
+ animate={{ opacity: 1 }}
314
+ className="space-y-1 mb-2">
315
+ <div className="text-primary">
316
+ <span className="text-green-400">$</span> {cmd.cmd}
317
+ </div>
318
+ <div className="text-muted-foreground text-xs">{cmd.output}</div>
319
+ </motion.div>
320
+ ))}
321
+ </AnimatePresence>
322
+ {currentIndex < commands.length - 1 && (
323
+ <button
324
+ onClick={() => setCurrentIndex((i) => i + 1)}
325
+ className="text-xs text-primary hover:text-primary/80 mt-2">
326
+ β†’ Next command
327
+ </button>
328
+ )}
329
+ </div>
330
+ );
331
+}
332
+
333
+export default function FeatureDemosSection() {
334
+ const [activeDemo, setActiveDemo] = useState(demos[0].id);
335
+
336
+ const currentDemo = demos.find((d) => d.id === activeDemo)!;
337
+
338
+ return (
339
+ <section className="container py-20">
340
+ <motion.div
341
+ initial={{ opacity: 0, y: 20 }}
342
+ whileInView={{ opacity: 1, y: 0 }}
343
+ viewport={{ once: true }}
344
+ className="text-center mb-12">
345
+ <h2 className="text-3xl md:text-4xl font-bold mb-4">
346
+ See It in <span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-purple-400">Action</span>
347
+ </h2>
348
+ <p className="text-muted-foreground max-w-2xl mx-auto">
349
+ Interactive demonstrations of our advanced features. Click each feature to see how it works.
350
+ </p>
351
+ </motion.div>
352
+
353
+ {/* Feature Tabs */}
354
+ <div className="flex flex-wrap justify-center gap-3 mb-8">
355
+ {demos.map((demo) => {
356
+ const Icon = demo.icon;
357
+ const isActive = activeDemo === demo.id;
358
+ return (
359
+ <button
360
+ key={demo.id}
361
+ onClick={() => setActiveDemo(demo.id)}
362
+ className={`group relative px-4 py-3 rounded-lg border transition-all ${isActive
363
+ ? "border-primary/50 bg-primary/10"
364
+ : "border-white/5 bg-card/30 hover:border-white/20"
365
+ }`}>
366
+ <div className="flex items-center gap-2">
367
+ <Icon className={`h-4 w-4 ${isActive ? "text-primary" : "text-muted-foreground"}`} />
368
+ <span className={`text-sm font-medium ${isActive ? "text-foreground" : "text-muted-foreground"}`}>
369
+ {demo.title}
370
+ </span>
371
+ </div>
372
+ {isActive && (
373
+ <motion.div
374
+ layoutId="activeTab"
375
+ className="absolute inset-0 rounded-lg border border-primary bg-primary/5 -z-10"
376
+ transition={{ type: "spring", duration: 0.5 }}
377
+ />
378
+ )}
379
+ </button>
380
+ );
381
+ })}
382
+ </div>
383
+
384
+ {/* Demo Display */}
385
+ <AnimatePresence mode="wait">
386
+ <motion.div
387
+ key={activeDemo}
388
+ initial={{ opacity: 0, y: 20 }}
389
+ animate={{ opacity: 1, y: 0 }}
390
+ exit={{ opacity: 0, y: -20 }}
391
+ transition={{ duration: 0.3 }}
392
+ className="max-w-4xl mx-auto">
393
+ <div className="grid md:grid-cols-2 gap-8 items-center">
394
+ {/* Description */}
395
+ <div>
396
+ <div className={`inline-flex p-3 rounded-xl bg-gradient-to-br ${currentDemo.color} mb-4`}>
397
+ <currentDemo.icon className="h-8 w-8 text-white" />
398
+ </div>
399
+ <h3 className="text-2xl font-bold mb-2">{currentDemo.title}</h3>
400
+ <p className="text-muted-foreground mb-6">{currentDemo.description}</p>
401
+ <a
402
+ href="/docs"
403
+ className="inline-flex items-center gap-2 text-sm text-primary hover:text-primary/80 transition-colors">
404
+ Learn more
405
+ <ArrowRight className="h-4 w-4" />
406
+ </a>
407
+ </div>
408
+
409
+ {/* Demo Component */}
410
+ <div className="rounded-xl border border-white/10 bg-card/50 backdrop-blur-sm p-6">
411
+ {currentDemo.demo.type === "stack-visualization" && (
412
+ <StackVisualization items={currentDemo.demo.items} />
413
+ )}
414
+ {currentDemo.demo.type === "ai-feedback" && <AIFeedback issues={currentDemo.demo.issues} />}
415
+ {currentDemo.demo.type === "queue-status" && <QueueStatus queue={currentDemo.demo.queue} />}
416
+ {currentDemo.demo.type === "metrics-dashboard" && (
417
+ <MetricsDashboard data={currentDemo.demo.data} />
418
+ )}
419
+ {currentDemo.demo.type === "slack-message" && <SlackMessage message={currentDemo.demo.message} />}
420
+ {currentDemo.demo.type === "terminal" && <Terminal commands={currentDemo.demo.commands} />}
421
+ </div>
422
+ </div>
423
+ </motion.div>
424
+ </AnimatePresence>
425
+ </section>
426
+ );
427
+}
0
428
β–Όsrc/db/index.ts
+22-4
5
5
6
6
import Database from "better-sqlite3";
7
7
import { BetterSQLite3Database, drizzle } from "drizzle-orm/better-sqlite3";
8
+import { drizzle as drizzleLibSQL, LibSQLDatabase } from "drizzle-orm/libsql";
9
+import { createClient } from "@libsql/client";
8
10
import { existsSync, mkdirSync } from "fs";
9
11
import { dirname } from "path";
10
12
import { logger } from "@/lib/logger";
11
13
import * as schema from "./schema";
12
14
13
15
// Database driver types
14
-export type DatabaseDriver = "sqlite" | "postgres" | "mysql";
16
+export type DatabaseDriver = "sqlite" | "postgres" | "mysql" | "libsql" | "turso";
15
17
16
-let db: BetterSQLite3Database<typeof schema> | null = null;
18
+let db: BetterSQLite3Database<typeof schema> | LibSQLDatabase<typeof schema> | null =
19
+ null;
17
20
18
21
/**
19
22
* Get database configuration from environment
27
30
/**
28
31
* Get or create database connection
29
32
*/
30
-export function getDatabase(): BetterSQLite3Database<typeof schema> {
33
+export function getDatabase():
34
+ | BetterSQLite3Database<typeof schema>
35
+ | LibSQLDatabase<typeof schema> {
31
36
if (db) return db;
32
37
33
38
const { driver, url } = getDbConfig();
34
39
40
+ // Support for Turso/LibSQL
41
+ if (driver === "libsql" || driver === "turso") {
42
+ const client = createClient({
43
+ url,
44
+ authToken: process.env.DATABASE_AUTH_TOKEN,
45
+ });
46
+ db = drizzleLibSQL(client, { schema });
47
+ logger.info({ driver: "libsql", url }, "Database connected (LibSQL)");
48
+ return db;
49
+ }
50
+
35
51
// Currently only SQLite is fully supported
36
52
// PostgreSQL and MySQL schemas need separate implementations
37
53
if (driver !== "sqlite") {
86
102
87
103
// Export schema and types
88
104
export { schema };
89
-export type Database = BetterSQLite3Database<typeof schema>;
105
+export type Database =
106
+ | BetterSQLite3Database<typeof schema>
107
+ | LibSQLDatabase<typeof schema>;
90
108
β–Όsrc/db/schema/activity.ts
+1-11
5
5
6
6
import { relations } from "drizzle-orm";
7
7
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
8
-import { organizations } from "./organizations";
8
+// import { organizations } from "./organizations"; // Removed to prevent circular dependency
9
9
import { repositories } from "./repositories";
10
10
import { users } from "./users";
11
11
17
17
repositoryId: text("repository_id").references(() => repositories.id, {
18
18
onDelete: "cascade",
19
19
}),
20
- organizationId: text("organization_id").references(() => organizations.id),
21
20
type: text("type").notNull(), // push, create_repo, create_branch, open_issue, open_pr, comment, star, fork, etc.
22
21
action: text("action").notNull(), // created, updated, deleted, opened, closed, merged, etc.
23
22
refType: text("ref_type"), // branch, tag
54
53
export const auditLogs = sqliteTable("audit_logs", {
55
54
id: text("id").primaryKey(),
56
55
userId: text("user_id").references(() => users.id),
57
- organizationId: text("organization_id").references(() => organizations.id),
58
56
repositoryId: text("repository_id").references(() => repositories.id),
59
57
action: text("action").notNull(),
60
58
actorType: text("actor_type").notNull().default("user"), // user, system, oauth_app
78
76
fields: [activities.repositoryId],
79
77
references: [repositories.id],
80
78
}),
81
- organization: one(organizations, {
82
- fields: [activities.organizationId],
83
- references: [organizations.id],
84
- }),
85
79
}));
86
80
87
81
export const notificationsRelations = relations(notifications, ({ one }) => ({
104
98
fields: [auditLogs.userId],
105
99
references: [users.id],
106
100
}),
107
- organization: one(organizations, {
108
- fields: [auditLogs.organizationId],
109
- references: [organizations.id],
110
- }),
111
101
repository: one(repositories, {
112
102
fields: [auditLogs.repositoryId],
113
103
references: [repositories.id],
114
104
β–Όsrc/db/schema/developer-metrics.ts
+4-5
1
1
/**
2
- * Developer Metrics Schema
3
- * Track PR velocity, review efficiency, and team productivity
2
+ * Developer Metrics Schema - Drizzle ORM
3
+ * Tracks PR metrics, review efficiency, and stack analytics
4
4
*/
5
5
6
6
import { relations } from "drizzle-orm";
7
-import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
7
+import { integer, real, sqliteTable, text } from "drizzle-orm/sqlite-core";
8
8
import { pullRequests } from "./pull-requests";
9
9
import { repositories } from "./repositories";
10
10
import { users } from "./users";
11
-import { organizations } from "./organizations";
11
+
12
12
13
13
// Per-PR metrics
14
14
export const prMetrics = sqliteTable("pr_metrics", {
62
62
.notNull()
63
63
.references(() => users.id, { onDelete: "cascade" }),
64
64
repositoryId: text("repository_id").references(() => repositories.id, { onDelete: "cascade" }),
65
- organizationId: text("organization_id").references(() => organizations.id),
66
65
67
66
// Time period
68
67
weekOf: text("week_of").notNull(), // ISO week: "2024-W52"
69
68
β–Όsrc/db/schema/index.ts
+2-2
21
21
// Wiki
22
22
export * from "./wiki";
23
23
24
-// Organizations and teams
25
-export * from "./organizations";
24
+// Organizations and teams - TEMPORARILY DISABLED DUE TO CIRCULAR DEPENDENCY
25
+// export * from "./organizations";
26
26
27
27
// Activity and notifications
28
28
export * from "./activity";
29
29
β–Όsrc/db/schema/issues.ts
+1-5
21
21
.notNull()
22
22
.references(() => users.id),
23
23
assigneeId: text("assignee_id").references(() => users.id),
24
- milestoneId: text("milestone_id").references(() => milestones.id),
24
+ milestoneId: text("milestone_id"), // Reference added later in relations
25
25
isPinned: integer("is_pinned", { mode: "boolean" }).default(false),
26
26
isLocked: integer("is_locked", { mode: "boolean" }).default(false),
27
27
lockReason: text("lock_reason"),
122
122
fields: [issues.assigneeId],
123
123
references: [users.id],
124
124
}),
125
- milestone: one(milestones, {
126
- fields: [issues.milestoneId],
127
- references: [milestones.id],
128
- }),
129
125
comments: many(issueComments),
130
126
labels: many(issueLabels),
131
127
assignees: many(issueAssignees),
132
128
β–Όsrc/db/schema/organizations.ts
+0-145
1
-/**
2
- * Organizations Schema - Drizzle ORM
3
- * Defines organizations, teams, and memberships
4
- */
5
-
6
-import { relations } from "drizzle-orm";
7
-import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
8
-import { repositories } from "./repositories";
9
-import { users } from "./users";
10
-
11
-export const organizations = sqliteTable("organizations", {
12
- id: text("id").primaryKey(),
13
- name: text("name").notNull().unique(),
14
- displayName: text("display_name"),
15
- description: text("description"),
16
- avatarUrl: text("avatar_url"),
17
- website: text("website"),
18
- location: text("location"),
19
- email: text("email"),
20
- isVerified: integer("is_verified", { mode: "boolean" }).default(false),
21
- visibility: text("visibility").notNull().default("public"), // public, private
22
- defaultRepoPermission: text("default_repo_permission").default("read"), // none, read, write, admin
23
- membersCanCreateRepos: integer("members_can_create_repos", {
24
- mode: "boolean",
25
- }).default(true),
26
- createdAt: text("created_at").notNull().default("CURRENT_TIMESTAMP"),
27
- updatedAt: text("updated_at").notNull().default("CURRENT_TIMESTAMP"),
28
-});
29
-
30
-export const organizationMembers = sqliteTable("organization_members", {
31
- id: text("id").primaryKey(),
32
- organizationId: text("organization_id")
33
- .notNull()
34
- .references(() => organizations.id, { onDelete: "cascade" }),
35
- userId: text("user_id")
36
- .notNull()
37
- .references(() => users.id, { onDelete: "cascade" }),
38
- role: text("role").notNull().default("member"), // owner, admin, member
39
- joinedAt: text("joined_at").notNull().default("CURRENT_TIMESTAMP"),
40
-});
41
-
42
-export const teams = sqliteTable("teams", {
43
- id: text("id").primaryKey(),
44
- organizationId: text("organization_id")
45
- .notNull()
46
- .references(() => organizations.id, { onDelete: "cascade" }),
47
- name: text("name").notNull(),
48
- slug: text("slug").notNull(),
49
- description: text("description"),
50
- privacy: text("privacy").notNull().default("visible"), // visible, secret
51
- parentId: text("parent_id").references(() => teams.id),
52
- permission: text("permission").notNull().default("read"), // none, read, write, maintain, admin
53
- createdAt: text("created_at").notNull().default("CURRENT_TIMESTAMP"),
54
- updatedAt: text("updated_at").notNull().default("CURRENT_TIMESTAMP"),
55
-});
56
-
57
-export const teamMembers = sqliteTable("team_members", {
58
- id: text("id").primaryKey(),
59
- teamId: text("team_id")
60
- .notNull()
61
- .references(() => teams.id, { onDelete: "cascade" }),
62
- userId: text("user_id")
63
- .notNull()
64
- .references(() => users.id, { onDelete: "cascade" }),
65
- role: text("role").notNull().default("member"), // maintainer, member
66
- joinedAt: text("joined_at").notNull().default("CURRENT_TIMESTAMP"),
67
-});
68
-
69
-export const teamRepositories = sqliteTable("team_repositories", {
70
- id: text("id").primaryKey(),
71
- teamId: text("team_id")
72
- .notNull()
73
- .references(() => teams.id, { onDelete: "cascade" }),
74
- repositoryId: text("repository_id")
75
- .notNull()
76
- .references(() => repositories.id, { onDelete: "cascade" }),
77
- permission: text("permission").notNull().default("read"), // read, write, maintain, admin
78
- addedAt: text("added_at").notNull().default("CURRENT_TIMESTAMP"),
79
-});
80
-
81
-// Relations
82
-export const organizationsRelations = relations(organizations, ({ many }) => ({
83
- members: many(organizationMembers),
84
- teams: many(teams),
85
-}));
86
-
87
-export const organizationMembersRelations = relations(
88
- organizationMembers,
89
- ({ one }) => ({
90
- organization: one(organizations, {
91
- fields: [organizationMembers.organizationId],
92
- references: [organizations.id],
93
- }),
94
- user: one(users, {
95
- fields: [organizationMembers.userId],
96
- references: [users.id],
97
- }),
98
- })
99
-);
100
-
101
-export const teamsRelations = relations(teams, ({ one, many }) => ({
102
- organization: one(organizations, {
103
- fields: [teams.organizationId],
104
- references: [organizations.id],
105
- }),
106
- parent: one(teams, {
107
- fields: [teams.parentId],
108
- references: [teams.id],
109
- }),
110
- children: many(teams),
111
- members: many(teamMembers),
112
- repositories: many(teamRepositories),
113
-}));
114
-
115
-export const teamMembersRelations = relations(teamMembers, ({ one }) => ({
116
- team: one(teams, {
117
- fields: [teamMembers.teamId],
118
- references: [teams.id],
119
- }),
120
- user: one(users, {
121
- fields: [teamMembers.userId],
122
- references: [users.id],
123
- }),
124
-}));
125
-
126
-export const teamRepositoriesRelations = relations(
127
- teamRepositories,
128
- ({ one }) => ({
129
- team: one(teams, {
130
- fields: [teamRepositories.teamId],
131
- references: [teams.id],
132
- }),
133
- repository: one(repositories, {
134
- fields: [teamRepositories.repositoryId],
135
- references: [repositories.id],
136
- }),
137
- })
138
-);
139
-
140
-// Types
141
-export type Organization = typeof organizations.$inferSelect;
142
-export type NewOrganization = typeof organizations.$inferInsert;
143
-export type Team = typeof teams.$inferSelect;
144
-export type OrganizationMember = typeof organizationMembers.$inferSelect;
145
-export type TeamMember = typeof teamMembers.$inferSelect;
146
0
β–Όsrc/db/schema/pull-requests.ts
+36-39
22
22
.notNull()
23
23
.references(() => users.id),
24
24
assigneeId: text("assignee_id").references(() => users.id),
25
- milestoneId: text("milestone_id").references(() => milestones.id),
25
+ milestoneId: text("milestone_id"), // Reference handled in relations
26
26
27
27
// Source and target
28
28
headBranch: text("head_branch").notNull(),
163
163
detailsUrl: text("details_url"),
164
164
output: text("output"), // JSON { title, summary, text }
165
165
startedAt: text("started_at"),
166
- completedAt: text("completed_at"),
167
- createdAt: text("created_at").notNull().default("CURRENT_TIMESTAMP"),
168
- updatedAt: text("updated_at").notNull().default("CURRENT_TIMESTAMP"),
169
166
});
170
167
171
168
// Relations
172
-export const pullRequestsRelations = relations(
173
- pullRequests,
174
- ({ one, many }) => ({
175
- repository: one(repositories, {
176
- fields: [pullRequests.repositoryId],
177
- references: [repositories.id],
178
- }),
179
- headRepository: one(repositories, {
180
- fields: [pullRequests.headRepositoryId],
181
- references: [repositories.id],
182
- }),
183
- author: one(users, {
184
- fields: [pullRequests.authorId],
185
- references: [users.id],
186
- }),
187
- assignee: one(users, {
188
- fields: [pullRequests.assigneeId],
189
- references: [users.id],
190
- }),
191
- mergedBy: one(users, {
192
- fields: [pullRequests.mergedById],
193
- references: [users.id],
194
- }),
195
- milestone: one(milestones, {
196
- fields: [pullRequests.milestoneId],
197
- references: [milestones.id],
198
- }),
199
- reviews: many(pullRequestReviews),
200
- comments: many(pullRequestComments),
201
- labels: many(pullRequestLabels),
202
- assignees: many(pullRequestAssignees),
203
- reviewers: many(pullRequestReviewers),
204
- checks: many(pullRequestChecks),
205
- })
206
-);
169
+// export const pullRequestsRelations = relations(
170
+// pullRequests,
171
+// ({ one, many }) => ({
172
+// repository: one(repositories, {
173
+// fields: [pullRequests.repositoryId],
174
+// references: [repositories.id],
175
+// }),
176
+// headRepository: one(repositories, {
177
+// fields: [pullRequests.headRepositoryId],
178
+// references: [repositories.id],
179
+// }),
180
+// author: one(users, {
181
+// fields: [pullRequests.authorId],
182
+// references: [users.id],
183
+// }),
184
+// assignee: one(users, {
185
+// fields: [pullRequests.assigneeId],
186
+// references: [users.id],
187
+// }),
188
+// // milestone: one(milestones, { // Removed because milestoneId has no foreign key
189
+// // fields: [pullRequests.milestoneId],
190
+// // references: [milestones.id],
191
+// // }),
192
+// mergedBy: one(users, {
193
+// fields: [pullRequests.mergedById],
194
+// references: [users.id],
195
+// }),
196
+// reviews: many(pullRequestReviews),
197
+// comments: many(pullRequestComments),
198
+// labels: many(pullRequestLabels),
199
+// assignees: many(pullRequestAssignees),
200
+// reviewers: many(pullRequestReviewers),
201
+// checks: many(pullRequestChecks),
202
+// })
203
+// );
207
204
208
205
export const pullRequestReviewsRelations = relations(
209
206
pullRequestReviews,
210
207
β–Όsrc/db/schema/slack-integration.ts
+2-6
5
5
6
6
import { relations } from "drizzle-orm";
7
7
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
8
-import { organizations } from "./organizations";
8
+// import { organizations } from "./organizations";
9
9
import { repositories } from "./repositories";
10
10
import { users } from "./users";
11
11
12
12
// Slack workspace connections
13
13
export const slackWorkspaces = sqliteTable("slack_workspaces", {
14
14
id: text("id").primaryKey(),
15
- organizationId: text("organization_id").references(() => organizations.id, { onDelete: "cascade" }),
15
+ // organizationId: text("organization_id").references(() => organizations.id, { onDelete: "cascade" }), // Removed as per instruction
16
16
17
17
// Slack info
18
18
teamId: text("team_id").notNull(), // Slack team/workspace ID
95
95
96
96
// Relations
97
97
export const slackWorkspacesRelations = relations(slackWorkspaces, ({ one, many }) => ({
98
- organization: one(organizations, {
99
- fields: [slackWorkspaces.organizationId],
100
- references: [organizations.id],
101
- }),
102
98
installedBy: one(users, {
103
99
fields: [slackWorkspaces.installedById],
104
100
references: [users.id],
105
101
β–Όsrc/pages/[owner]/[repo]/index.astro
+2-2
312
312
<div class="space-y-4">
313
313
<!-- Latest Commit -->
314
314
{
315
- latestCommit ? (
315
+ latestCommit && latestCommit.authorName ? (
316
316
<div class="flex items-center gap-4 rounded-lg border bg-muted/30 p-4">
317
317
<div class="h-8 w-8 rounded-full bg-muted flex items-center justify-center">
318
318
<span class="text-sm font-medium">
319
- {latestCommit.authorName[0]}
319
+ {latestCommit.authorName[0].toUpperCase()}
320
320
</span>
321
321
</div>
322
322
<div class="flex-1 min-w-0">
323
323
β–Όsrc/pages/admin/metrics-disabled.txt β†’ src/pages/admin/_metrics-disabled.txt
+0-0
0
0
β–Όsrc/pages/admin/storage-disabled.txt β†’ src/pages/admin/_storage-disabled.txt
+0-0
0
0
β–Όsrc/pages/api.astro
+328-0
1
+---
2
+import BaseLayout from "@/layouts/BaseLayout.astro";
3
+import { Code2, Webhook, Key, BookOpen } from "lucide-react";
4
+
5
+const apiSections = [
6
+ {
7
+ icon: Code2,
8
+ title: "REST API",
9
+ description: "Complete REST API reference for repositories, pull requests, and more",
10
+ href: "/docs/api/rest-api",
11
+ status: "Available",
12
+ statusColor: "text-green-400"
13
+ },
14
+ {
15
+ icon: Webhook,
16
+ title: "Webhooks",
17
+ description: "Event-driven integrations with webhook payloads",
18
+ href: "/docs/api/webhooks",
19
+ status: "Available",
20
+ statusColor: "text-green-400"
21
+ },
22
+ {
23
+ icon: Key,
24
+ title: "Authentication",
25
+ description: "API tokens, OAuth, and authentication flows",
26
+ href: "/docs/api/authentication",
27
+ status: "Available",
28
+ statusColor: "text-green-400"
29
+ },
30
+ {
31
+ icon: BookOpen,
32
+ title: "GraphQL API",
33
+ description: "Flexible data querying with GraphQL",
34
+ href: "/docs/api/graphql",
35
+ status: "Coming Soon",
36
+ statusColor: "text-yellow-400"
37
+ }
38
+];
39
+
40
+const quickExamples = [
41
+ {
42
+ title: "Create Repository",
43
+ method: "POST",
44
+ endpoint: "/api/repos",
45
+ code: `curl -X POST https://git.yourcompany.com/api/repos \\
46
+ -H "Authorization: Bearer YOUR_TOKEN" \\
47
+ -H "Content-Type: application/json" \\
48
+ -d '{
49
+ "name": "my-project",
50
+ "description": "My awesome project",
51
+ "visibility": "private"
52
+ }'`
53
+ },
54
+ {
55
+ title: "List Pull Requests",
56
+ method: "GET",
57
+ endpoint: "/api/repos/:owner/:repo/pulls",
58
+ code: `curl https://git.yourcompany.com/api/repos/owner/repo/pulls \\
59
+ -H "Authorization: Bearer YOUR_TOKEN"`
60
+ },
61
+ {
62
+ title: "Create Pull Request",
63
+ method: "POST",
64
+ endpoint: "/api/repos/:owner/:repo/pulls",
65
+ code: `curl -X POST https://git.yourcompany.com/api/repos/owner/repo/pulls \\
66
+ -H "Authorization: Bearer YOUR_TOKEN" \\
67
+ -H "Content-Type: application/json" \\
68
+ -d '{
69
+ "title": "Add new feature",
70
+ "head": "feature-branch",
71
+ "base": "main",
72
+ "body": "This PR adds..."
73
+ }'`
74
+ }
75
+];
76
+---
77
+
78
+<BaseLayout title="API Documentation - OpenCodeHub">
79
+ <div class="container mx-auto py-12 px-4">
80
+ <!-- Hero -->
81
+ <div class="text-center mb-16">
82
+ <h1 class="text-5xl font-bold mb-4">
83
+ <span class="bg-clip-text text-transparent bg-gradient-to-r from-primary to-purple-400">
84
+ API Documentation
85
+ </span>
86
+ </h1>
87
+ <p class="text-xl text-muted-foreground max-w-2xl mx-auto">
88
+ Integrate OpenCodeHub into your workflow with our comprehensive REST API
89
+ </p>
90
+ </div>
91
+
92
+ <!-- API Overview -->
93
+ <section class="mb-16">
94
+ <div class="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
95
+ {apiSections.map((section) => {
96
+ const Icon = section.icon;
97
+ return (
98
+ <a
99
+ href={section.href}
100
+ class="group relative p-6 rounded-xl border border-white/10 bg-card/50 backdrop-blur-sm hover:border-primary/50 transition-all hover:scale-105"
101
+ >
102
+ <div class="flex items-center justify-between mb-4">
103
+ <Icon class="h-8 w-8 text-primary" />
104
+ <span class={`text-xs font-medium ${section.statusColor}`}>
105
+ {section.status}
106
+ </span>
107
+ </div>
108
+ <h3 class="text-lg font-semibold mb-2">{section.title}</h3>
109
+ <p class="text-sm text-muted-foreground">{section.description}</p>
110
+ </a>
111
+ );
112
+ })}
113
+ </div>
114
+ </section>
115
+
116
+ <!-- Quick Start -->
117
+ <section class="mb-16">
118
+ <h2 class="text-3xl font-bold mb-8">Quick Start</h2>
119
+ <div class="grid md:grid-cols-2 gap-6">
120
+ <!-- Authentication -->
121
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
122
+ <h3 class="text-xl font-semibold mb-4">1. Get API Token</h3>
123
+ <div class="space-y-3">
124
+ <p class="text-sm text-muted-foreground">
125
+ Generate a personal access token from your settings:
126
+ </p>
127
+ <ol class="list-decimal list-inside space-y-2 text-sm">
128
+ <li>Go to <a href="/settings/tokens" class="text-primary hover:underline">Settings β†’ Tokens</a></li>
129
+ <li>Click "Generate New Token"</li>
130
+ <li>Select scopes: <code class="bg-muted px-2 py-1 rounded">repo</code>, <code class="bg-muted px-2 py-1 rounded">pr</code></li>
131
+ <li>Copy and save your token securely</li>
132
+ </ol>
133
+ </div>
134
+ </div>
135
+
136
+ <!-- Base URL -->
137
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
138
+ <h3 class="text-xl font-semibold mb-4">2. API Base URL</h3>
139
+ <div class="space-y-3">
140
+ <p class="text-sm text-muted-foreground">
141
+ All API requests should be made to:
142
+ </p>
143
+ <pre class="text-sm bg-black/50 p-3 rounded overflow-x-auto"><code>https://git.yourcompany.com/api</code></pre>
144
+ <p class="text-xs text-muted-foreground">
145
+ Replace <code class="bg-muted px-1 rounded">git.yourcompany.com</code> with your instance URL
146
+ </p>
147
+ </div>
148
+ </div>
149
+ </div>
150
+ </section>
151
+
152
+ <!-- Quick Examples -->
153
+ <section class="mb-16">
154
+ <h2 class="text-3xl font-bold mb-8">Quick Examples</h2>
155
+ <div class="space-y-6">
156
+ {quickExamples.map((example) => (
157
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
158
+ <div class="flex items-center justify-between mb-4">
159
+ <h3 class="text-lg font-semibold">{example.title}</h3>
160
+ <div class="flex items-center gap-2">
161
+ <span class={`px-3 py-1 rounded-full text-xs font-medium ${
162
+ example.method === 'GET'
163
+ ? 'bg-blue-500/20 text-blue-400'
164
+ : example.method === 'POST'
165
+ ? 'bg-green-500/20 text-green-400'
166
+ : 'bg-yellow-500/20 text-yellow-400'
167
+ }`}>
168
+ {example.method}
169
+ </span>
170
+ <code class="text-xs text-muted-foreground">{example.endpoint}</code>
171
+ </div>
172
+ </div>
173
+ <pre class="text-sm bg-black/80 p-4 rounded overflow-x-auto border border-white/5"><code>{example.code}</code></pre>
174
+ </div>
175
+ ))}
176
+ </div>
177
+ </section>
178
+
179
+ <!-- Rate Limiting -->
180
+ <section class="mb-16">
181
+ <h2 class="text-3xl font-bold mb-8">Rate Limiting</h2>
182
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
183
+ <div class="grid md:grid-cols-3 gap-6">
184
+ <div>
185
+ <h4 class="font-semibold mb-2">Authenticated Requests</h4>
186
+ <p class="text-3xl font-bold text-primary">5,000</p>
187
+ <p class="text-sm text-muted-foreground">requests per hour</p>
188
+ </div>
189
+ <div>
190
+ <h4 class="font-semibold mb-2">Unauthenticated Requests</h4>
191
+ <p class="text-3xl font-bold text-yellow-400">60</p>
192
+ <p class="text-sm text-muted-foreground">requests per hour</p>
193
+ </div>
194
+ <div>
195
+ <h4 class="font-semibold mb-2">Check Limits</h4>
196
+ <p class="text-sm text-muted-foreground mb-2">Response headers:</p>
197
+ <code class="text-xs bg-muted px-2 py-1 rounded block">X-RateLimit-Remaining</code>
198
+ </div>
199
+ </div>
200
+ </div>
201
+ </section>
202
+
203
+ <!-- Response Format -->
204
+ <section class="mb-16">
205
+ <h2 class="text-3xl font-bold mb-8">Response Format</h2>
206
+ <div class="grid md:grid-cols-2 gap-6">
207
+ <!-- Success -->
208
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
209
+ <h3 class="text-lg font-semibold mb-4 text-green-400">Success Response</h3>
210
+ <pre class="text-sm bg-black/80 p-4 rounded overflow-x-auto"><code>{`{
211
+ "data": {
212
+ "id": "repo-123",
213
+ "name": "my-project",
214
+ "owner": {
215
+ "username": "swadhin",
216
+ "id": "user-456"
217
+ },
218
+ "visibility": "private",
219
+ "created_at": "2024-01-01T12:00:00Z"
220
+ },
221
+ "meta": {
222
+ "timestamp": "2024-01-01T12:00:01Z"
223
+ }
224
+}`}</code></pre>
225
+ </div>
226
+
227
+ <!-- Error -->
228
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
229
+ <h3 class="text-lg font-semibold mb-4 text-red-400">Error Response</h3>
230
+ <pre class="text-sm bg-black/80 p-4 rounded overflow-x-auto"><code>{`{
231
+ "error": {
232
+ "code": "VALIDATION_ERROR",
233
+ "message": "Repository name is required",
234
+ "details": {
235
+ "field": "name",
236
+ "constraint": "required"
237
+ }
238
+ },
239
+ "meta": {
240
+ "timestamp": "2024-01-01T12:00:01Z",
241
+ "request_id": "req-abc123"
242
+ }
243
+}`}</code></pre>
244
+ </div>
245
+ </div>
246
+ </section>
247
+
248
+ <!-- SDKs & Libraries -->
249
+ <section class="mb-16">
250
+ <h2 class="text-3xl font-bold mb-8">SDKs & Libraries</h2>
251
+ <div class="grid md:grid-cols-3 gap-6">
252
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
253
+ <h3 class="text-lg font-semibold mb-2">JavaScript/TypeScript</h3>
254
+ <pre class="text-sm bg-black/50 p-3 rounded mb-3"><code>npm install @opencodehub/sdk</code></pre>
255
+ <a href="https://github.com/swadhinbiswas/OpencodeHub/tree/main/sdk/js" class="text-sm text-primary hover:underline">
256
+ View docs β†’
257
+ </a>
258
+ </div>
259
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
260
+ <h3 class="text-lg font-semibold mb-2">Python</h3>
261
+ <pre class="text-sm bg-black/50 p-3 rounded mb-3"><code>pip install opencodehub</code></pre>
262
+ <a href="https://github.com/swadhinbiswas/OpencodeHub/tree/main/sdk/python" class="text-sm text-primary hover:underline">
263
+ View docs β†’
264
+ </a>
265
+ </div>
266
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
267
+ <h3 class="text-lg font-semibold mb-2">Go</h3>
268
+ <pre class="text-sm bg-black/50 p-3 rounded mb-3"><code>go get github.com/opencodehub/sdk-go</code></pre>
269
+ <a href="https://github.com/swadhinbiswas/OpencodeHub/tree/main/sdk/go" class="text-sm text-primary hover:underline">
270
+ View docs β†’
271
+ </a>
272
+ </div>
273
+ </div>
274
+ </section>
275
+
276
+ <!-- Resources -->
277
+ <section>
278
+ <h2 class="text-3xl font-bold mb-8">Additional Resources</h2>
279
+ <div class="grid md:grid-cols-2 gap-6">
280
+ <a
281
+ href="/docs"
282
+ class="p-6 rounded-xl border border-white/10 bg-card/50 hover:border-primary/50 transition-all"
283
+ >
284
+ <h3 class="text-lg font-semibold mb-2">πŸ“– Full Documentation</h3>
285
+ <p class="text-sm text-muted-foreground">
286
+ Complete guides, tutorials, and feature documentation
287
+ </p>
288
+ </a>
289
+ <a
290
+ href="https://github.com/swadhinbiswas/OpencodeHub/tree/main/examples"
291
+ target="_blank"
292
+ class="p-6 rounded-xl border border-white/10 bg-card/50 hover:border-primary/50 transition-all"
293
+ >
294
+ <h3 class="text-lg font-semibold mb-2">πŸ’‘ Code Examples</h3>
295
+ <p class="text-sm text-muted-foreground">
296
+ Real-world integration examples and sample code
297
+ </p>
298
+ </a>
299
+ <a
300
+ href="https://github.com/swadhinbiswas/OpencodeHub/discussions"
301
+ target="_blank"
302
+ class="p-6 rounded-xl border border-white/10 bg-card/50 hover:border-primary/50 transition-all"
303
+ >
304
+ <h3 class="text-lg font-semibold mb-2">πŸ’¬ Community</h3>
305
+ <p class="text-sm text-muted-foreground">
306
+ Ask questions and discuss API usage
307
+ </p>
308
+ </a>
309
+ <a
310
+ href="https://github.com/swadhinbiswas/OpencodeHub/blob/main/CHANGELOG.md"
311
+ target="_blank"
312
+ class="p-6 rounded-xl border border-white/10 bg-card/50 hover:border-primary/50 transition-all"
313
+ >
314
+ <h3 class="text-lg font-semibold mb-2">πŸ“ Changelog</h3>
315
+ <p class="text-sm text-muted-foreground">
316
+ API changes and version history
317
+ </p>
318
+ </a>
319
+ </div>
320
+ </section>
321
+ </div>
322
+</BaseLayout>
323
+
324
+<style>
325
+ .container {
326
+ max-width: 1200px;
327
+ }
328
+</style>
0
329
β–Όsrc/pages/docs/features/stacked-prs.astro
+99-0
1
+---
2
+import BaseLayout from "@/layouts/BaseLayout.astro";
3
+import { readFileSync } from "fs";
4
+import { marked } from "marked";
5
+
6
+const markdown = readFileSync("docs/features/stacked-prs.md", "utf-8");
7
+const html = marked(markdown);
8
+---
9
+
10
+<BaseLayout title="Stacked Pull Requests - OpenCodeHub Docs">
11
+ <div class="container mx-auto py-12 px-4">
12
+ <a href="/docs" class="inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-primary mb-8">
13
+ ← Back to Documentation
14
+ </a>
15
+
16
+ <article class="prose prose-invert max-w-4xl mx-auto">
17
+ <div set:html={html} />
18
+ </article>
19
+ </div>
20
+</BaseLayout>
21
+
22
+<style is:global>
23
+ .prose {
24
+ color: hsl(var(--foreground));
25
+ }
26
+ .prose h1 {
27
+ font-size: 2.5rem;
28
+ font-weight: bold;
29
+ margin-bottom: 1rem;
30
+ color: hsl(var(--primary));
31
+ }
32
+ .prose h2 {
33
+ font-size: 2rem;
34
+ font-weight: bold;
35
+ margin-top: 2rem;
36
+ margin-bottom: 1rem;
37
+ border-bottom: 1px solid hsl(var(--border));
38
+ padding-bottom: 0.5rem;
39
+ }
40
+ .prose h3 {
41
+ font-size: 1.5rem;
42
+ font-weight: 600;
43
+ margin-top: 1.5rem;
44
+ margin-bottom: 0.75rem;
45
+ }
46
+ .prose pre {
47
+ background: hsl(var(--card));
48
+ border: 1px solid hsl(var(--border));
49
+ border-radius: 0.5rem;
50
+ padding: 1rem;
51
+ overflow-x: auto;
52
+ }
53
+ .prose code {
54
+ background: hsl(var(--muted));
55
+ padding: 0.2rem 0.4rem;
56
+ border-radius: 0.25rem;
57
+ font-size: 0.875rem;
58
+ }
59
+ .prose pre code {
60
+ background: transparent;
61
+ padding: 0;
62
+ }
63
+ .prose a {
64
+ color: hsl(var(--primary));
65
+ text-decoration: none;
66
+ }
67
+ .prose a:hover {
68
+ text-decoration: underline;
69
+ }
70
+ .prose table {
71
+ width: 100%;
72
+ border-collapse: collapse;
73
+ margin: 1.5rem 0;
74
+ }
75
+ .prose th,
76
+ .prose td {
77
+ border: 1px solid hsl(var(--border));
78
+ padding: 0.75rem;
79
+ text-align: left;
80
+ }
81
+ .prose th {
82
+ background: hsl(var(--muted));
83
+ font-weight: 600;
84
+ }
85
+ .prose blockquote {
86
+ border-left: 4px solid hsl(var(--primary));
87
+ padding-left: 1rem;
88
+ margin: 1.5rem 0;
89
+ font-style: italic;
90
+ color: hsl(var(--muted-foreground));
91
+ }
92
+ .prose ul, .prose ol {
93
+ margin: 1rem 0;
94
+ padding-left: 2rem;
95
+ }
96
+ .prose li {
97
+ margin: 0.5rem 0;
98
+ }
99
+</style>
0
100
β–Όsrc/pages/docs/index.astro
+272-0
1
+---
2
+import BaseLayout from "@/layouts/BaseLayout.astro";
3
+import { BookOpen, Code, Zap, GitBranch, Bot, GitMerge, BarChart3, MessageSquare, Terminal } from "lucide-react";
4
+
5
+const features = [
6
+ {
7
+ icon: GitBranch,
8
+ title: "Stacked Pull Requests",
9
+ description: "Break large changes into reviewable stacks",
10
+ href: "/docs/features/stacked-prs",
11
+ color: "from-blue-500 to-cyan-500"
12
+ },
13
+ {
14
+ icon: Bot,
15
+ title: "AI Code Review",
16
+ description: "Get instant feedback from GPT-4 or Claude",
17
+ href: "/docs/features/ai-review",
18
+ color: "from-purple-500 to-pink-500"
19
+ },
20
+ {
21
+ icon: GitMerge,
22
+ title: "Smart Merge Queue",
23
+ description: "Automate merging with CI optimization",
24
+ href: "/docs/features/merge-queue",
25
+ color: "from-green-500 to-emerald-500"
26
+ },
27
+ {
28
+ icon: BarChart3,
29
+ title: "Developer Metrics",
30
+ description: "Track velocity and review efficiency",
31
+ href: "/admin/metrics",
32
+ color: "from-orange-500 to-red-500"
33
+ },
34
+ {
35
+ icon: MessageSquare,
36
+ title: "Slack Integration",
37
+ description: "Get notifications in Slack",
38
+ href: "/settings/integrations",
39
+ color: "from-indigo-500 to-blue-500"
40
+ },
41
+ {
42
+ icon: Terminal,
43
+ title: "OCH CLI",
44
+ description: "Powerful command-line interface",
45
+ href: "https://github.com/swadhinbiswas/OpencodeHub/tree/main/cli",
46
+ color: "from-cyan-500 to-teal-500"
47
+ }
48
+];
49
+
50
+const quickLinks = [
51
+ { title: "Getting Started", href: "#getting-started", icon: Zap },
52
+ { title: "Features", href: "#features", icon: Code },
53
+ { title: "Tutorials", href: "#tutorials", icon: BookOpen },
54
+];
55
+---
56
+
57
+<BaseLayout title="Documentation - OpenCodeHub">
58
+ <div class="container mx-auto py-12 px-4">
59
+ <!-- Hero -->
60
+ <div class="text-center mb-16">
61
+ <h1 class="text-5xl font-bold mb-4">
62
+ <span class="bg-clip-text text-transparent bg-gradient-to-r from-primary to-purple-400">
63
+ Documentation
64
+ </span>
65
+ </h1>
66
+ <p class="text-xl text-muted-foreground max-w-2xl mx-auto">
67
+ Everything you need to know about OpenCodeHub - from getting started to advanced features
68
+ </p>
69
+ </div>
70
+
71
+ <!-- Quick Links -->
72
+ <div class="grid md:grid-cols-3 gap-6 mb-16">
73
+ {quickLinks.map((link) => {
74
+ const Icon = link.icon;
75
+ return (
76
+ <a
77
+ href={link.href}
78
+ class="group relative p-6 rounded-xl border border-white/10 bg-card/50 backdrop-blur-sm hover:border-primary/50 transition-all hover:scale-105 hover:shadow-lg"
79
+ >
80
+ <div class="flex items-center gap-3 mb-2">
81
+ <Icon class="h-6 w-6 text-primary" />
82
+ <h3 class="text-lg font-semibold">{link.title}</h3>
83
+ </div>
84
+ <p class="text-sm text-muted-foreground">Quick access</p>
85
+ </a>
86
+ );
87
+ })}
88
+ </div>
89
+
90
+ <!-- Features Grid -->
91
+ <section id="features" class="mb-16">
92
+ <h2 class="text-3xl font-bold mb-8">Core Features</h2>
93
+ <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
94
+ {features.map((feature) => {
95
+ const Icon = feature.icon;
96
+ return (
97
+ <a
98
+ href={feature.href}
99
+ class="group relative p-6 rounded-xl border border-white/10 bg-card/50 backdrop-blur-sm hover:border-primary/50 transition-all hover:scale-105"
100
+ >
101
+ <div class={`inline-flex p-3 rounded-lg bg-gradient-to-br ${feature.color} mb-4`}>
102
+ <Icon class="h-6 w-6 text-white" />
103
+ </div>
104
+ <h3 class="text-xl font-semibold mb-2">{feature.title}</h3>
105
+ <p class="text-sm text-muted-foreground mb-4">{feature.description}</p>
106
+ <span class="text-sm text-primary group-hover:underline">
107
+ Learn more β†’
108
+ </span>
109
+ </a>
110
+ );
111
+ })}
112
+ </div>
113
+ </section>
114
+
115
+ <!-- Getting Started -->
116
+ <section id="getting-started" class="mb-16">
117
+ <h2 class="text-3xl font-bold mb-8">Getting Started</h2>
118
+ <div class="grid md:grid-cols-2 gap-6">
119
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
120
+ <h3 class="text-xl font-semibold mb-4">πŸš€ Quick Start (5 minutes)</h3>
121
+ <div class="space-y-4">
122
+ <div class="p-4 rounded-lg bg-background/50">
123
+ <h4 class="font-medium mb-2">1. Install with Docker</h4>
124
+ <pre class="text-sm bg-black/50 p-3 rounded overflow-x-auto"><code>docker-compose up -d</code></pre>
125
+ </div>
126
+ <div class="p-4 rounded-lg bg-background/50">
127
+ <h4 class="font-medium mb-2">2. Create admin user</h4>
128
+ <pre class="text-sm bg-black/50 p-3 rounded overflow-x-auto"><code>docker-compose exec app bun run scripts/seed-admin.ts</code></pre>
129
+ </div>
130
+ <div class="p-4 rounded-lg bg-background/50">
131
+ <h4 class="font-medium mb-2">3. Open browser</h4>
132
+ <pre class="text-sm bg-black/50 p-3 rounded overflow-x-auto"><code>http://localhost:3000</code></pre>
133
+ </div>
134
+ </div>
135
+ <a href="/docs/installation" class="inline-flex items-center gap-2 mt-4 text-sm text-primary hover:underline">
136
+ Full installation guide β†’
137
+ </a>
138
+ </div>
139
+
140
+ <div class="p-6 rounded-xl border border-white/10 bg-card/50">
141
+ <h3 class="text-xl font-semibold mb-4">πŸ“š First Steps</h3>
142
+ <div class="space-y-3">
143
+ <a href="/docs/tutorials/your-first-stack" class="block p-3 rounded-lg bg-background/50 hover:bg-primary/10 transition-colors">
144
+ <div class="font-medium">Tutorial: Your First Stack</div>
145
+ <div class="text-sm text-muted-foreground">Create your first stacked PR in 15 minutes</div>
146
+ </a>
147
+ <a href="/docs/features/ai-review" class="block p-3 rounded-lg bg-background/50 hover:bg-primary/10 transition-colors">
148
+ <div class="font-medium">Setup AI Code Review</div>
149
+ <div class="text-sm text-muted-foreground">Configure GPT-4 or Claude for automatic reviews</div>
150
+ </a>
151
+ <a href="/docs/features/merge-queue" class="block p-3 rounded-lg bg-background/50 hover:bg-primary/10 transition-colors">
152
+ <div class="font-medium">Use Merge Queue</div>
153
+ <div class="text-sm text-muted-foreground">Automate merging with stack awareness</div>
154
+ </a>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ </section>
159
+
160
+ <!-- Tutorials -->
161
+ <section id="tutorials" class="mb-16">
162
+ <h2 class="text-3xl font-bold mb-8">Hands-On Tutorials</h2>
163
+ <div class="space-y-4">
164
+ <a
165
+ href="/docs/tutorials/your-first-stack"
166
+ class="block p-6 rounded-xl border border-white/10 bg-card/50 hover:border-primary/50 transition-all"
167
+ >
168
+ <div class="flex items-center justify-between">
169
+ <div>
170
+ <h3 class="text-xl font-semibold mb-2">πŸŽ“ Your First Stacked PR</h3>
171
+ <p class="text-sm text-muted-foreground">
172
+ Learn how to split a large feature into reviewable chunks in 15 minutes
173
+ </p>
174
+ <div class="flex items-center gap-4 mt-3 text-xs text-muted-foreground">
175
+ <span>⏱️ 15-20 minutes</span>
176
+ <span>πŸ“Š Beginner</span>
177
+ <span>πŸ’» Hands-on coding</span>
178
+ </div>
179
+ </div>
180
+ <span class="text-primary">β†’</span>
181
+ </div>
182
+ </a>
183
+ </div>
184
+ </section>
185
+
186
+ <!-- Resources -->
187
+ <section class="mb-16">
188
+ <h2 class="text-3xl font-bold mb-8">Additional Resources</h2>
189
+ <div class="grid md:grid-cols-3 gap-6">
190
+ <a
191
+ href="https://github.com/swadhinbiswas/OpencodeHub"
192
+ target="_blank"
193
+ class="p-6 rounded-xl border border-white/10 bg-card/50 hover:border-primary/50 transition-all"
194
+ >
195
+ <h3 class="text-lg font-semibold mb-2">πŸ“ GitHub Repository</h3>
196
+ <p class="text-sm text-muted-foreground">View source code and contribute</p>
197
+ </a>
198
+ <a
199
+ href="https://github.com/swadhinbiswas/OpencodeHub/issues"
200
+ target="_blank"
201
+ class="p-6 rounded-xl border border-white/10 bg-card/50 hover:border-primary/50 transition-all"
202
+ >
203
+ <h3 class="text-lg font-semibold mb-2">πŸ› Report Issues</h3>
204
+ <p class="text-sm text-muted-foreground">Found a bug? Let us know</p>
205
+ </a>
206
+ <a
207
+ href="https://github.com/swadhinbiswas/OpencodeHub/discussions"
208
+ target="_blank"
209
+ class="p-6 rounded-xl border border-white/10 bg-card/50 hover:border-primary/50 transition-all"
210
+ >
211
+ <h3 class="text-lg font-semibold mb-2">πŸ’¬ Discussions</h3>
212
+ <p class="text-sm text-muted-foreground">Ask questions and share ideas</p>
213
+ </a>
214
+ </div>
215
+ </section>
216
+
217
+ <!-- Feature Comparison -->
218
+ <section>
219
+ <h2 class="text-3xl font-bold mb-8">Why OpenCodeHub?</h2>
220
+ <div class="overflow-x-auto">
221
+ <table class="w-full border-collapse">
222
+ <thead>
223
+ <tr class="border-b border-white/10">
224
+ <th class="text-left p-4">Feature</th>
225
+ <th class="text-center p-4">GitHub</th>
226
+ <th class="text-center p-4">GitLab</th>
227
+ <th class="text-center p-4 text-primary">OpenCodeHub</th>
228
+ </tr>
229
+ </thead>
230
+ <tbody>
231
+ <tr class="border-b border-white/5">
232
+ <td class="p-4">Stacked PRs</td>
233
+ <td class="text-center p-4">❌</td>
234
+ <td class="text-center p-4">❌</td>
235
+ <td class="text-center p-4 text-green-400">βœ… Native</td>
236
+ </tr>
237
+ <tr class="border-b border-white/5">
238
+ <td class="p-4">AI Code Review</td>
239
+ <td class="text-center p-4">⚠️ Copilot</td>
240
+ <td class="text-center p-4">❌</td>
241
+ <td class="text-center p-4 text-green-400">βœ… GPT-4 & Claude</td>
242
+ </tr>
243
+ <tr class="border-b border-white/5">
244
+ <td class="p-4">Smart Merge Queue</td>
245
+ <td class="text-center p-4">⚠️ Basic</td>
246
+ <td class="text-center p-4">⚠️ Basic</td>
247
+ <td class="text-center p-4 text-green-400">βœ… Stack-aware</td>
248
+ </tr>
249
+ <tr class="border-b border-white/5">
250
+ <td class="p-4">Self-Hosted</td>
251
+ <td class="text-center p-4">❌</td>
252
+ <td class="text-center p-4">βœ… Complex</td>
253
+ <td class="text-center p-4 text-green-400">βœ… Simple</td>
254
+ </tr>
255
+ <tr>
256
+ <td class="p-4">Cost</td>
257
+ <td class="text-center p-4">$$$</td>
258
+ <td class="text-center p-4">$$$</td>
259
+ <td class="text-center p-4 text-green-400">Free</td>
260
+ </tr>
261
+ </tbody>
262
+ </table>
263
+ </div>
264
+ </section>
265
+ </div>
266
+</BaseLayout>
267
+
268
+<style>
269
+ .container {
270
+ max-width: 1200px;
271
+ }
272
+</style>
0
273
β–Όsrc/pages/index.astro
+4-0
5
5
import { BookOpen, Star } from "lucide-react";
6
6
import HeroSection from "@/components/home/HeroSection";
7
7
import FeaturesSection from "@/components/home/FeaturesSection";
8
+import FeatureDemosSection from "@/components/home/FeatureDemosSection";
8
9
9
10
// Fetch public repositories for the landing page
10
11
const db = getDatabase();
92
93
)
93
94
}
94
95
96
+ <!-- Interactive Feature Demonstrations -->
97
+ <FeatureDemosSection client:load />
98
+
95
99
<!-- Features Section with Aceternity UI Components -->
96
100
<FeaturesSection client:load />
97
101
98
102