codedna 0.8.1__tar.gz → 0.8.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codedna-0.8.1 → codedna-0.8.3}/PKG-INFO +27 -18
- {codedna-0.8.1 → codedna-0.8.3}/README.md +26 -17
- {codedna-0.8.1 → codedna-0.8.3}/codedna/__init__.py +1 -1
- {codedna-0.8.1 → codedna-0.8.3}/codedna/cli.py +12 -10
- {codedna-0.8.1 → codedna-0.8.3}/codedna/db.py +19 -9
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/ai-compare/page.tsx +1 -2
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/api/billing/checkout/route.ts +1 -1
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/billing/page.tsx +3 -3
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/layout.tsx +1 -1
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/protected/page.tsx +1 -1
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/ErrorBanner.tsx +4 -5
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/NavClient.tsx +9 -9
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/api.ts +0 -4
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/auth.ts +0 -41
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/plan.ts +14 -9
- {codedna-0.8.1 → codedna-0.8.3}/pyproject.toml +1 -1
- {codedna-0.8.1 → codedna-0.8.3}/.gitignore +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/LICENSE +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/ai.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/ai_box.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/ai_fingerprint.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/analyzer.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/api.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/auth.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/bus_factor.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/demo.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/git_hook.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/integrations/__init__.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/integrations/github_bot.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/integrations/jira.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/integrations/lemonsqueezy.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/interview.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/onboarding.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/plan.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/protection.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/rate_limit.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/scorer.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/sprint_health.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/survey.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/codedna/tech_debt.py +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/.env.local.example +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/.gitignore +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/README.md +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/api/auth/login/route.ts +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/api/auth/logout/route.ts +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/api/auth/me/route.ts +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/api/auth/register/route.ts +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/bus-factor/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/commits/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/debt/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/favicon.ico +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/files/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/fonts/GeistMonoVF.woff +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/fonts/GeistVF.woff +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/globals.css +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/interview/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/login/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/onboarding/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/pricing/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/register/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/settings/integrations/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/app/sprints/page.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/AuthProvider.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/ClientProviders.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/CommitTable.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/CostInfoTooltip.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/FeatureGate.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/FileHeatmap.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/LanguageSwitcher.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/OverviewTitles.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/QuickInsights.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/RiskBadge.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/SummaryCards.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/UnderstandingChart.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/components/UpgradeBanner.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/i18n/en.ts +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/i18n/index.tsx +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/lib/i18n/tr.ts +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/next-env.d.ts +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/next.config.mjs +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/package-lock.json +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/package.json +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/postcss.config.mjs +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/tailwind.config.ts +0 -0
- {codedna-0.8.1 → codedna-0.8.3}/dashboard/tsconfig.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codedna
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.3
|
|
4
4
|
Summary: AI Code Transparency Tool - detect AI-written code and measure developer understanding
|
|
5
5
|
Project-URL: Homepage, https://codedna.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/natureco-official/codedna
|
|
@@ -179,7 +179,7 @@ pip install -e .
|
|
|
179
179
|
### Verify Installation
|
|
180
180
|
|
|
181
181
|
```bash
|
|
182
|
-
codedna --version # should show "codedna 0.
|
|
182
|
+
codedna --version # should show "codedna 0.8.x"
|
|
183
183
|
codedna doctor # full system health check
|
|
184
184
|
```
|
|
185
185
|
|
|
@@ -244,9 +244,9 @@ codedna interview-list # List interviews
|
|
|
244
244
|
codedna interview-score # Score interview
|
|
245
245
|
codedna bus-factor # Ownership analysis (Team+)
|
|
246
246
|
codedna debt # Technical debt (Team+)
|
|
247
|
-
codedna sprint-
|
|
248
|
-
codedna sprint-
|
|
249
|
-
codedna sprint-
|
|
247
|
+
codedna sprint-create # Create sprint
|
|
248
|
+
codedna sprint-health # Sprint health
|
|
249
|
+
codedna sprint-history # Sprint history
|
|
250
250
|
```
|
|
251
251
|
|
|
252
252
|
### 🌐 Infrastructure
|
|
@@ -539,23 +539,32 @@ MIT License - Copyright (c) 2026 NatureCo
|
|
|
539
539
|
|
|
540
540
|
## 🗺️ Roadmap
|
|
541
541
|
|
|
542
|
-
### v0.
|
|
543
|
-
- [
|
|
544
|
-
- [
|
|
545
|
-
- [
|
|
546
|
-
- [
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
- [
|
|
550
|
-
- [
|
|
551
|
-
- [
|
|
552
|
-
- [
|
|
542
|
+
### ✅ Completed (v0.8.0+)
|
|
543
|
+
- [x] Full English translation (CLI, API, Dashboard)
|
|
544
|
+
- [x] Web Dashboard shipped inside PyPI package (`pip install codedna` → `codedna dashboard`)
|
|
545
|
+
- [x] Demo mode for quick evaluation
|
|
546
|
+
- [x] VS Code Marketplace extension
|
|
547
|
+
- [x] Jira integration + webhook
|
|
548
|
+
- [x] Technical debt & bus factor analysis
|
|
549
|
+
- [x] Sprint health & onboarding tracking
|
|
550
|
+
- [x] AI tool comparison (Copilot vs Cursor vs Claude)
|
|
551
|
+
- [x] Interview tool for candidate assessment
|
|
552
|
+
- [x] Protected modules & understanding thresholds
|
|
553
|
+
- [x] Lemon Squeezy billing integration
|
|
554
|
+
|
|
555
|
+
### v0.9.0 (Next)
|
|
556
|
+
- [ ] Go/Rust/Java Tree-sitter support
|
|
557
|
+
- [ ] Slack/Discord webhook notifications
|
|
558
|
+
- [ ] AI coach — personalized learning suggestions
|
|
559
|
+
- [ ] Custom AI fingerprinting rules
|
|
560
|
+
- [ ] Enhanced multi-repo dashboard
|
|
553
561
|
|
|
554
562
|
### v1.0.0 (2027)
|
|
555
|
-
- [ ] Self-hosted option
|
|
563
|
+
- [ ] Self-hosted option (Docker)
|
|
556
564
|
- [ ] Enterprise SSO (SAML, OIDC)
|
|
557
565
|
- [ ] SOC 2 compliance
|
|
558
|
-
- [ ]
|
|
566
|
+
- [ ] Cloud sync (optional)
|
|
567
|
+
- [ ] Team analytics dashboard with role-based views
|
|
559
568
|
|
|
560
569
|
---
|
|
561
570
|
|
|
@@ -127,7 +127,7 @@ pip install -e .
|
|
|
127
127
|
### Verify Installation
|
|
128
128
|
|
|
129
129
|
```bash
|
|
130
|
-
codedna --version # should show "codedna 0.
|
|
130
|
+
codedna --version # should show "codedna 0.8.x"
|
|
131
131
|
codedna doctor # full system health check
|
|
132
132
|
```
|
|
133
133
|
|
|
@@ -192,9 +192,9 @@ codedna interview-list # List interviews
|
|
|
192
192
|
codedna interview-score # Score interview
|
|
193
193
|
codedna bus-factor # Ownership analysis (Team+)
|
|
194
194
|
codedna debt # Technical debt (Team+)
|
|
195
|
-
codedna sprint-
|
|
196
|
-
codedna sprint-
|
|
197
|
-
codedna sprint-
|
|
195
|
+
codedna sprint-create # Create sprint
|
|
196
|
+
codedna sprint-health # Sprint health
|
|
197
|
+
codedna sprint-history # Sprint history
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
### 🌐 Infrastructure
|
|
@@ -487,23 +487,32 @@ MIT License - Copyright (c) 2026 NatureCo
|
|
|
487
487
|
|
|
488
488
|
## 🗺️ Roadmap
|
|
489
489
|
|
|
490
|
-
### v0.
|
|
491
|
-
- [
|
|
492
|
-
- [
|
|
493
|
-
- [
|
|
494
|
-
- [
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
- [
|
|
498
|
-
- [
|
|
499
|
-
- [
|
|
500
|
-
- [
|
|
490
|
+
### ✅ Completed (v0.8.0+)
|
|
491
|
+
- [x] Full English translation (CLI, API, Dashboard)
|
|
492
|
+
- [x] Web Dashboard shipped inside PyPI package (`pip install codedna` → `codedna dashboard`)
|
|
493
|
+
- [x] Demo mode for quick evaluation
|
|
494
|
+
- [x] VS Code Marketplace extension
|
|
495
|
+
- [x] Jira integration + webhook
|
|
496
|
+
- [x] Technical debt & bus factor analysis
|
|
497
|
+
- [x] Sprint health & onboarding tracking
|
|
498
|
+
- [x] AI tool comparison (Copilot vs Cursor vs Claude)
|
|
499
|
+
- [x] Interview tool for candidate assessment
|
|
500
|
+
- [x] Protected modules & understanding thresholds
|
|
501
|
+
- [x] Lemon Squeezy billing integration
|
|
502
|
+
|
|
503
|
+
### v0.9.0 (Next)
|
|
504
|
+
- [ ] Go/Rust/Java Tree-sitter support
|
|
505
|
+
- [ ] Slack/Discord webhook notifications
|
|
506
|
+
- [ ] AI coach — personalized learning suggestions
|
|
507
|
+
- [ ] Custom AI fingerprinting rules
|
|
508
|
+
- [ ] Enhanced multi-repo dashboard
|
|
501
509
|
|
|
502
510
|
### v1.0.0 (2027)
|
|
503
|
-
- [ ] Self-hosted option
|
|
511
|
+
- [ ] Self-hosted option (Docker)
|
|
504
512
|
- [ ] Enterprise SSO (SAML, OIDC)
|
|
505
513
|
- [ ] SOC 2 compliance
|
|
506
|
-
- [ ]
|
|
514
|
+
- [ ] Cloud sync (optional)
|
|
515
|
+
- [ ] Team analytics dashboard with role-based views
|
|
507
516
|
|
|
508
517
|
---
|
|
509
518
|
|
|
@@ -316,8 +316,9 @@ def status(
|
|
|
316
316
|
console.print(f"[bold red]{warning}[/bold red]")
|
|
317
317
|
except Exception:
|
|
318
318
|
pass # Never breaks the hook
|
|
319
|
+
return
|
|
319
320
|
|
|
320
|
-
|
|
321
|
+
if not results:
|
|
321
322
|
console.print(
|
|
322
323
|
f"[bold]Commit:[/bold] [dim]{commit_hash[:8]}[/dim]\n"
|
|
323
324
|
"[dim]No supported code files found in this commit.[/dim]"
|
|
@@ -1093,8 +1094,7 @@ def bus_factor(
|
|
|
1093
1094
|
if not is_feature_available("bus_factor"):
|
|
1094
1095
|
console.print(
|
|
1095
1096
|
Panel(
|
|
1096
|
-
"[bold yellow]🔒 This feature is available on Team plan.[/bold yellow]\n"
|
|
1097
|
-
"[dim]This feature is available on Team plan.[/dim]\n\n"
|
|
1097
|
+
"[bold yellow]🔒 This feature is available on Team plan.[/bold yellow]\n\n"
|
|
1098
1098
|
"[dim]To upgrade:[/dim] [cyan]codedna plan activate <LICENSE_KEY>[/cyan]",
|
|
1099
1099
|
border_style="yellow",
|
|
1100
1100
|
padding=(1, 2),
|
|
@@ -1311,8 +1311,7 @@ def sprint_create(
|
|
|
1311
1311
|
if not is_feature_available("sprint_health"):
|
|
1312
1312
|
console.print(
|
|
1313
1313
|
Panel(
|
|
1314
|
-
"[bold yellow]🔒 This feature is available on Team plan.[/bold yellow]\n"
|
|
1315
|
-
"[dim]This feature is available on Team plan.[/dim]\n\n"
|
|
1314
|
+
"[bold yellow]🔒 This feature is available on Team plan.[/bold yellow]\n\n"
|
|
1316
1315
|
"[dim]To upgrade:[/dim] [cyan]codedna plan activate <LICENSE_KEY>[/cyan]",
|
|
1317
1316
|
border_style="yellow",
|
|
1318
1317
|
padding=(1, 2),
|
|
@@ -1719,7 +1718,7 @@ def uninstall(
|
|
|
1719
1718
|
"""Remove CodeDNA hook."""
|
|
1720
1719
|
root = repo or find_git_root()
|
|
1721
1720
|
if uninstall_hook(root):
|
|
1722
|
-
console.print("[green]✓[/green] CodeDNA
|
|
1721
|
+
console.print("[green]✓[/green] CodeDNA hook removed.")
|
|
1723
1722
|
else:
|
|
1724
1723
|
raise typer.Exit(1)
|
|
1725
1724
|
|
|
@@ -1850,11 +1849,11 @@ def doctor(
|
|
|
1850
1849
|
deps = [
|
|
1851
1850
|
("typer", "CLI framework"),
|
|
1852
1851
|
("rich", "Terminal UI"),
|
|
1853
|
-
("
|
|
1852
|
+
("git", "Git integration"),
|
|
1854
1853
|
("fastapi", "REST API"),
|
|
1855
1854
|
("uvicorn", "ASGI server"),
|
|
1856
1855
|
("pydantic", "Data validation"),
|
|
1857
|
-
("
|
|
1856
|
+
("jwt", "JWT auth"),
|
|
1858
1857
|
("bcrypt", "Password hashing"),
|
|
1859
1858
|
]
|
|
1860
1859
|
for mod_name, desc in deps:
|
|
@@ -2086,12 +2085,14 @@ def update(
|
|
|
2086
2085
|
def demo(
|
|
2087
2086
|
reset: bool = typer.Option(False, "--reset", help="Clear all demo data"),
|
|
2088
2087
|
data_only: bool = typer.Option(False, "--data-only", help="Seed data only, don't start dashboard"),
|
|
2088
|
+
repo: Optional[Path] = typer.Option(None, "--repo", "-r", help="Git repo directory"),
|
|
2089
2089
|
) -> None:
|
|
2090
2090
|
"""Load demo data into the database (idempotent) and open the dashboard."""
|
|
2091
2091
|
from codedna.demo import seed_demo_data, clear_demo_data, is_demo_active
|
|
2092
2092
|
from codedna.db import get_db_path
|
|
2093
2093
|
|
|
2094
|
-
|
|
2094
|
+
root = repo or find_git_root() or Path.cwd()
|
|
2095
|
+
db_path = get_db_path(root)
|
|
2095
2096
|
|
|
2096
2097
|
console.print()
|
|
2097
2098
|
if reset:
|
|
@@ -2175,6 +2176,7 @@ def demo(
|
|
|
2175
2176
|
@app.command(name="security-check")
|
|
2176
2177
|
def security_check(
|
|
2177
2178
|
path: Optional[Path] = typer.Option(None, "--path", "-p", help="Project root to scan (defaults to cwd)"),
|
|
2179
|
+
repo: Optional[Path] = typer.Option(None, "--repo", "-r", help="Git repo directory"),
|
|
2178
2180
|
strict: bool = typer.Option(False, "--strict", help="Exit 1 on any warning"),
|
|
2179
2181
|
) -> None:
|
|
2180
2182
|
"""Scan the project for personal paths, secrets, and missing .gitignore rules.
|
|
@@ -2188,7 +2190,7 @@ def security_check(
|
|
|
2188
2190
|
from codedna.git_hook import find_git_root
|
|
2189
2191
|
import re
|
|
2190
2192
|
|
|
2191
|
-
root = path or find_git_root() or Path.cwd()
|
|
2193
|
+
root = repo or path or find_git_root() or Path.cwd()
|
|
2192
2194
|
if not root.exists():
|
|
2193
2195
|
console.print(f" [red]✗[/red] Path not found: {root}")
|
|
2194
2196
|
raise typer.Exit(1)
|
|
@@ -119,15 +119,25 @@ def save_commit(
|
|
|
119
119
|
) -> None:
|
|
120
120
|
"""Save or update a commit record."""
|
|
121
121
|
with get_connection(db_path) as conn:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
122
|
+
if understanding_score is not None:
|
|
123
|
+
conn.execute(
|
|
124
|
+
"""
|
|
125
|
+
INSERT INTO commits (commit_hash, author, timestamp, files_changed, understanding_score)
|
|
126
|
+
VALUES (?, ?, ?, ?, ?)
|
|
127
|
+
ON CONFLICT(commit_hash) DO UPDATE SET
|
|
128
|
+
understanding_score = excluded.understanding_score
|
|
129
|
+
""",
|
|
130
|
+
(commit_hash, author, timestamp, files_changed, understanding_score),
|
|
131
|
+
)
|
|
132
|
+
else:
|
|
133
|
+
conn.execute(
|
|
134
|
+
"""
|
|
135
|
+
INSERT INTO commits (commit_hash, author, timestamp, files_changed, understanding_score)
|
|
136
|
+
VALUES (?, ?, ?, ?, NULL)
|
|
137
|
+
ON CONFLICT(commit_hash) DO NOTHING
|
|
138
|
+
""",
|
|
139
|
+
(commit_hash, author, timestamp, files_changed),
|
|
140
|
+
)
|
|
131
141
|
|
|
132
142
|
|
|
133
143
|
def save_file_score(
|
|
@@ -6,7 +6,7 @@ import { useTranslation } from "@/lib/i18n";
|
|
|
6
6
|
import { getCurrentPlan } from "@/lib/plan";
|
|
7
7
|
import { FeatureGate } from "@/components/FeatureGate";
|
|
8
8
|
import { ErrorBanner } from "@/components/ErrorBanner";
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
|
|
11
11
|
const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
|
|
12
12
|
|
|
@@ -77,7 +77,6 @@ export default function AIComparePage() {
|
|
|
77
77
|
<div className="flex items-start gap-2 bg-amber-500/10 border border-amber-500/20 rounded-xl px-4 py-3">
|
|
78
78
|
<span className="text-amber-400 mt-0.5">⚠️</span>
|
|
79
79
|
<p className="text-xs text-amber-400/80">{t("ai_compare_disclaimer")}</p>
|
|
80
|
-
<CostInfoTooltip />
|
|
81
80
|
</div>
|
|
82
81
|
|
|
83
82
|
{error && <ErrorBanner />}
|
|
@@ -107,7 +107,7 @@ export default function BillingPage() {
|
|
|
107
107
|
.then((d) => {
|
|
108
108
|
if (d) setSubscription({ plan: d.plan, subscription_status: d.subscription_status, lemonsqueezy_customer_id: null });
|
|
109
109
|
})
|
|
110
|
-
.catch(() =>
|
|
110
|
+
.catch(() => setCheckoutError("Could not load subscription data."));
|
|
111
111
|
}, [user]);
|
|
112
112
|
|
|
113
113
|
const handleUpgrade = async (plan: Plan) => {
|
|
@@ -145,8 +145,8 @@ export default function BillingPage() {
|
|
|
145
145
|
} else {
|
|
146
146
|
alert("Customer portal coming soon. For now, use: codedna plan demo");
|
|
147
147
|
}
|
|
148
|
-
} catch
|
|
149
|
-
alert("Error:
|
|
148
|
+
} catch {
|
|
149
|
+
alert("Error: Could not connect to customer portal.");
|
|
150
150
|
} finally {
|
|
151
151
|
setCancelLoading(false);
|
|
152
152
|
}
|
|
@@ -42,7 +42,7 @@ export default function RootLayout({
|
|
|
42
42
|
<div className="max-w-7xl mx-auto px-6 py-4 text-xs text-gray-700 flex items-center justify-between">
|
|
43
43
|
<span>🧬 CodeDNA</span>
|
|
44
44
|
<a
|
|
45
|
-
href="http://localhost:8000/docs
|
|
45
|
+
href={`${process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"}/docs`}
|
|
46
46
|
target="_blank"
|
|
47
47
|
rel="noopener noreferrer"
|
|
48
48
|
className="hover:text-gray-500 transition-colors"
|
|
@@ -114,7 +114,7 @@ export default function ProtectedPage() {
|
|
|
114
114
|
const violations = modules.filter((m) => m.status === "VIOLATION");
|
|
115
115
|
|
|
116
116
|
return (
|
|
117
|
-
<FeatureGate feature="
|
|
117
|
+
<FeatureGate feature="protect_modules" plan={plan}>
|
|
118
118
|
<div className="space-y-6">
|
|
119
119
|
<div>
|
|
120
120
|
<h1 className="text-2xl font-bold text-white">🛡️ {t("protect_title")}</h1>
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { useTranslation } from "@/lib/i18n";
|
|
4
4
|
|
|
5
|
-
/** Banner shown when API connection fails */
|
|
6
5
|
export function ErrorBanner({ message }: { message?: string }) {
|
|
7
6
|
const { t } = useTranslation();
|
|
8
7
|
|
|
@@ -11,11 +10,11 @@ export function ErrorBanner({ message }: { message?: string }) {
|
|
|
11
10
|
<span className="text-red-400 text-lg mt-0.5">⚠️</span>
|
|
12
11
|
<div>
|
|
13
12
|
<p className="text-red-400 font-semibold text-sm">
|
|
14
|
-
{t("error_api")
|
|
15
|
-
</p>
|
|
16
|
-
<p className="text-red-300/70 text-xs mt-0.5">
|
|
17
|
-
{message ?? t("error_api")}
|
|
13
|
+
{t("error_api")}
|
|
18
14
|
</p>
|
|
15
|
+
{message && (
|
|
16
|
+
<p className="text-red-300/70 text-xs mt-0.5">{message}</p>
|
|
17
|
+
)}
|
|
19
18
|
</div>
|
|
20
19
|
</div>
|
|
21
20
|
);
|
|
@@ -21,16 +21,16 @@ export function NavClient() {
|
|
|
21
21
|
<Link href="/" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">{t("nav_dashboard")}</Link>
|
|
22
22
|
<Link href="/files" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">{t("nav_files")}</Link>
|
|
23
23
|
<Link href="/commits" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">{t("nav_commits")}</Link>
|
|
24
|
-
<Link href="/bus-factor" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🚌</Link>
|
|
25
|
-
<Link href="/debt" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">💰</Link>
|
|
26
|
-
<Link href="/sprints" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🏃</Link>
|
|
27
|
-
<Link href="/ai-compare" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🤖</Link>
|
|
28
|
-
<Link href="/onboarding" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🚀</Link>
|
|
29
|
-
<Link href="/settings/integrations" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">⚙️</Link>
|
|
30
|
-
<Link href="/protected" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🛡️</Link>
|
|
31
|
-
<Link href="/interview" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">🎯</Link>
|
|
24
|
+
<Link href="/bus-factor" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Bus Factor">🚌</Link>
|
|
25
|
+
<Link href="/debt" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Technical Debt">💰</Link>
|
|
26
|
+
<Link href="/sprints" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Sprints">🏃</Link>
|
|
27
|
+
<Link href="/ai-compare" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="AI Comparison">🤖</Link>
|
|
28
|
+
<Link href="/onboarding" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Onboarding">🚀</Link>
|
|
29
|
+
<Link href="/settings/integrations" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Integrations">⚙️</Link>
|
|
30
|
+
<Link href="/protected" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Protected Modules">🛡️</Link>
|
|
31
|
+
<Link href="/interview" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium" aria-label="Interview">🎯</Link>
|
|
32
32
|
<Link href="/pricing" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm font-medium">{t("nav_pricing")}</Link>
|
|
33
|
-
<a href="http://localhost:8000/docs
|
|
33
|
+
<a href={`${process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"}/docs`} target="_blank" rel="noopener noreferrer" className="text-gray-600 hover:text-gray-400 transition-colors text-xs">API ↗</a>
|
|
34
34
|
|
|
35
35
|
{/* Auth state */}
|
|
36
36
|
{user ? (
|
|
@@ -95,10 +95,6 @@ async function apiFetch<T>(path: string): Promise<T> {
|
|
|
95
95
|
return res.json() as Promise<T>;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
export async function getHealth(): Promise<HealthResponse> {
|
|
99
|
-
return apiFetch<HealthResponse>("/health");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
98
|
export async function getRepoSummary(): Promise<RepoSummary> {
|
|
103
99
|
return apiFetch<RepoSummary>("/repo/summary");
|
|
104
100
|
}
|
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
* - The client side only knows whether the user is logged in
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
|
|
13
|
-
|
|
14
12
|
export interface UserData {
|
|
15
13
|
user_id: number;
|
|
16
14
|
email: string;
|
|
@@ -18,12 +16,6 @@ export interface UserData {
|
|
|
18
16
|
subscription_status: string;
|
|
19
17
|
}
|
|
20
18
|
|
|
21
|
-
export interface SubscriptionData {
|
|
22
|
-
plan: string;
|
|
23
|
-
subscription_status: string;
|
|
24
|
-
lemonsqueezy_customer_id: string | null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
19
|
/** Log in — via Next.js API route (sets httpOnly cookie) */
|
|
28
20
|
export async function login(
|
|
29
21
|
email: string,
|
|
@@ -95,37 +87,4 @@ export async function getCurrentUser(): Promise<UserData | null> {
|
|
|
95
87
|
}
|
|
96
88
|
}
|
|
97
89
|
|
|
98
|
-
/** Request checkout URL directly from FastAPI */
|
|
99
|
-
export async function getCheckoutUrl(
|
|
100
|
-
plan: string,
|
|
101
|
-
token: string
|
|
102
|
-
): Promise<string | null> {
|
|
103
|
-
try {
|
|
104
|
-
const res = await fetch(`${API_URL}/billing/checkout`, {
|
|
105
|
-
method: "POST",
|
|
106
|
-
headers: {
|
|
107
|
-
"Content-Type": "application/json",
|
|
108
|
-
Authorization: `Bearer ${token}`,
|
|
109
|
-
},
|
|
110
|
-
body: JSON.stringify({ plan }),
|
|
111
|
-
});
|
|
112
|
-
if (!res.ok) return null;
|
|
113
|
-
const data = await res.json();
|
|
114
|
-
return data.checkout_url ?? null;
|
|
115
|
-
} catch {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
90
|
|
|
120
|
-
/** Fetch subscription status */
|
|
121
|
-
export async function getSubscriptionStatus(token: string): Promise<SubscriptionData | null> {
|
|
122
|
-
try {
|
|
123
|
-
const res = await fetch(`${API_URL}/billing/subscription`, {
|
|
124
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
125
|
-
});
|
|
126
|
-
if (!res.ok) return null;
|
|
127
|
-
return res.json();
|
|
128
|
-
} catch {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
@@ -5,17 +5,18 @@
|
|
|
5
5
|
export type Plan = "free" | "pro" | "team" | "enterprise";
|
|
6
6
|
|
|
7
7
|
export interface PlanLimits {
|
|
8
|
-
max_repos: number;
|
|
9
|
-
max_files_scan: number;
|
|
10
|
-
history_days: number;
|
|
8
|
+
max_repos: number;
|
|
9
|
+
max_files_scan: number;
|
|
10
|
+
history_days: number;
|
|
11
11
|
dashboard_access: boolean;
|
|
12
12
|
github_actions: boolean;
|
|
13
13
|
slack_notify: boolean;
|
|
14
|
-
bus_factor: boolean;
|
|
15
|
-
sprint_health: boolean;
|
|
16
|
-
ai_comparison: boolean;
|
|
17
|
-
team_members: number;
|
|
18
|
-
interview_tool: boolean;
|
|
14
|
+
bus_factor: boolean;
|
|
15
|
+
sprint_health: boolean;
|
|
16
|
+
ai_comparison: boolean;
|
|
17
|
+
team_members: number;
|
|
18
|
+
interview_tool: boolean;
|
|
19
|
+
protect_modules: boolean;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
|
|
@@ -23,7 +24,7 @@ export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
|
|
|
23
24
|
max_repos: 1,
|
|
24
25
|
max_files_scan: 50,
|
|
25
26
|
history_days: 7,
|
|
26
|
-
dashboard_access:
|
|
27
|
+
dashboard_access: true,
|
|
27
28
|
github_actions: false,
|
|
28
29
|
slack_notify: false,
|
|
29
30
|
bus_factor: false,
|
|
@@ -31,6 +32,7 @@ export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
|
|
|
31
32
|
ai_comparison: false,
|
|
32
33
|
team_members: 1,
|
|
33
34
|
interview_tool: false,
|
|
35
|
+
protect_modules: false,
|
|
34
36
|
},
|
|
35
37
|
pro: {
|
|
36
38
|
max_repos: -1,
|
|
@@ -44,6 +46,7 @@ export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
|
|
|
44
46
|
ai_comparison: false,
|
|
45
47
|
team_members: 1,
|
|
46
48
|
interview_tool: false,
|
|
49
|
+
protect_modules: false,
|
|
47
50
|
},
|
|
48
51
|
team: {
|
|
49
52
|
max_repos: -1,
|
|
@@ -57,6 +60,7 @@ export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
|
|
|
57
60
|
ai_comparison: false,
|
|
58
61
|
team_members: 10,
|
|
59
62
|
interview_tool: false,
|
|
63
|
+
protect_modules: true,
|
|
60
64
|
},
|
|
61
65
|
enterprise: {
|
|
62
66
|
max_repos: -1,
|
|
@@ -70,6 +74,7 @@ export const PLAN_LIMITS: Record<Plan, PlanLimits> = {
|
|
|
70
74
|
ai_comparison: true,
|
|
71
75
|
team_members: -1,
|
|
72
76
|
interview_tool: true,
|
|
77
|
+
protect_modules: true,
|
|
73
78
|
},
|
|
74
79
|
};
|
|
75
80
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|