codeshift 0.3.6__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- codeshift/__init__.py +2 -2
- codeshift/cli/__init__.py +1 -1
- codeshift/cli/commands/__init__.py +1 -1
- codeshift/cli/commands/auth.py +5 -5
- codeshift/cli/commands/scan.py +2 -5
- codeshift/cli/commands/upgrade.py +2 -7
- codeshift/cli/commands/upgrade_all.py +1 -1
- codeshift/cli/main.py +2 -2
- codeshift/migrator/llm_migrator.py +8 -12
- codeshift/utils/__init__.py +1 -1
- codeshift/utils/api_client.py +11 -11
- codeshift/utils/cache.py +1 -1
- {codeshift-0.3.6.dist-info → codeshift-0.4.0.dist-info}/METADATA +2 -18
- {codeshift-0.3.6.dist-info → codeshift-0.4.0.dist-info}/RECORD +18 -34
- {codeshift-0.3.6.dist-info → codeshift-0.4.0.dist-info}/licenses/LICENSE +1 -1
- codeshift/api/__init__.py +0 -1
- codeshift/api/auth.py +0 -182
- codeshift/api/config.py +0 -73
- codeshift/api/database.py +0 -215
- codeshift/api/main.py +0 -103
- codeshift/api/models/__init__.py +0 -55
- codeshift/api/models/auth.py +0 -108
- codeshift/api/models/billing.py +0 -92
- codeshift/api/models/migrate.py +0 -42
- codeshift/api/models/usage.py +0 -116
- codeshift/api/routers/__init__.py +0 -5
- codeshift/api/routers/auth.py +0 -440
- codeshift/api/routers/billing.py +0 -395
- codeshift/api/routers/migrate.py +0 -304
- codeshift/api/routers/usage.py +0 -291
- codeshift/api/routers/webhooks.py +0 -289
- {codeshift-0.3.6.dist-info → codeshift-0.4.0.dist-info}/WHEEL +0 -0
- {codeshift-0.3.6.dist-info → codeshift-0.4.0.dist-info}/entry_points.txt +0 -0
- {codeshift-0.3.6.dist-info → codeshift-0.4.0.dist-info}/top_level.txt +0 -0
codeshift/__init__.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Codeshift - AI-powered CLI tool for migrating Python code to handle breaking dependency changes.
|
|
3
3
|
|
|
4
4
|
Don't just flag the update. Fix the break.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
__version__ = "0.2.0"
|
|
8
|
-
__author__ = "
|
|
8
|
+
__author__ = "Codeshift Team"
|
codeshift/cli/__init__.py
CHANGED
codeshift/cli/commands/auth.py
CHANGED
|
@@ -55,7 +55,7 @@ def delete_credentials() -> None:
|
|
|
55
55
|
def get_api_key() -> str | None:
|
|
56
56
|
"""Get API key from environment or saved credentials."""
|
|
57
57
|
# Check environment first
|
|
58
|
-
api_key = os.environ.get("
|
|
58
|
+
api_key = os.environ.get("CODESHIFT_API_KEY")
|
|
59
59
|
if api_key:
|
|
60
60
|
return api_key
|
|
61
61
|
|
|
@@ -99,7 +99,7 @@ def login(
|
|
|
99
99
|
api_key: str | None,
|
|
100
100
|
device: bool,
|
|
101
101
|
) -> None:
|
|
102
|
-
"""Login to
|
|
102
|
+
"""Login to Codeshift to enable cloud features.
|
|
103
103
|
|
|
104
104
|
\b
|
|
105
105
|
Authentication methods:
|
|
@@ -148,7 +148,7 @@ def register(
|
|
|
148
148
|
password: str | None,
|
|
149
149
|
name: str | None,
|
|
150
150
|
) -> None:
|
|
151
|
-
"""Create a new
|
|
151
|
+
"""Create a new Codeshift account.
|
|
152
152
|
|
|
153
153
|
\b
|
|
154
154
|
Example:
|
|
@@ -228,7 +228,7 @@ def _register_account(email: str, password: str, full_name: str | None) -> None:
|
|
|
228
228
|
f"[green]Account created successfully![/]\n\n"
|
|
229
229
|
f"Email: [cyan]{data['user']['email']}[/]\n"
|
|
230
230
|
f"Tier: [cyan]{data['user'].get('tier', 'free')}[/]\n\n"
|
|
231
|
-
f"[dim]You are now logged in and ready to use
|
|
231
|
+
f"[dim]You are now logged in and ready to use Codeshift.[/]",
|
|
232
232
|
title="Registration Successful",
|
|
233
233
|
)
|
|
234
234
|
)
|
|
@@ -466,7 +466,7 @@ def _login_with_device_code() -> None:
|
|
|
466
466
|
|
|
467
467
|
@click.command()
|
|
468
468
|
def logout() -> None:
|
|
469
|
-
"""Logout from
|
|
469
|
+
"""Logout from Codeshift and remove saved credentials."""
|
|
470
470
|
creds = load_credentials()
|
|
471
471
|
|
|
472
472
|
if not creds:
|
codeshift/cli/commands/scan.py
CHANGED
|
@@ -11,10 +11,7 @@ from rich.panel import Panel
|
|
|
11
11
|
from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
|
|
12
12
|
from rich.table import Table
|
|
13
13
|
|
|
14
|
-
from codeshift.knowledge import
|
|
15
|
-
generate_knowledge_base_sync,
|
|
16
|
-
is_tier_1_library,
|
|
17
|
-
)
|
|
14
|
+
from codeshift.knowledge import generate_knowledge_base_sync, is_tier_1_library
|
|
18
15
|
from codeshift.scanner import DependencyParser
|
|
19
16
|
from codeshift.utils.config import ProjectConfig
|
|
20
17
|
|
|
@@ -148,7 +145,7 @@ def scan(
|
|
|
148
145
|
console.print(
|
|
149
146
|
Panel(
|
|
150
147
|
"[bold]Scanning project for possible migrations[/]\n\n" f"Path: {project_path}",
|
|
151
|
-
title="
|
|
148
|
+
title="Codeshift Scan",
|
|
152
149
|
)
|
|
153
150
|
)
|
|
154
151
|
|
|
@@ -10,12 +10,7 @@ from rich.panel import Panel
|
|
|
10
10
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
11
11
|
from rich.table import Table
|
|
12
12
|
|
|
13
|
-
from codeshift.cli.quota import
|
|
14
|
-
QuotaError,
|
|
15
|
-
check_quota,
|
|
16
|
-
record_usage,
|
|
17
|
-
show_quota_exceeded_message,
|
|
18
|
-
)
|
|
13
|
+
from codeshift.cli.quota import QuotaError, check_quota, record_usage, show_quota_exceeded_message
|
|
19
14
|
from codeshift.knowledge import (
|
|
20
15
|
Confidence,
|
|
21
16
|
GeneratedKnowledgeBase,
|
|
@@ -130,7 +125,7 @@ def upgrade(
|
|
|
130
125
|
f"[bold]Upgrading {knowledge.display_name}[/] to version [cyan]{target}[/]\n\n"
|
|
131
126
|
f"{knowledge.description}\n"
|
|
132
127
|
f"Migration guide: {knowledge.migration_guide_url or 'N/A'}",
|
|
133
|
-
title="
|
|
128
|
+
title="Codeshift Migration",
|
|
134
129
|
)
|
|
135
130
|
)
|
|
136
131
|
|
codeshift/cli/main.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Main CLI entry point for
|
|
1
|
+
"""Main CLI entry point for Codeshift."""
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
4
|
from rich.console import Console
|
|
@@ -26,7 +26,7 @@ console = Console()
|
|
|
26
26
|
@click.version_option(version=__version__, prog_name="codeshift")
|
|
27
27
|
@click.pass_context
|
|
28
28
|
def cli(ctx: click.Context) -> None:
|
|
29
|
-
"""
|
|
29
|
+
"""Codeshift - AI-powered Python dependency migration tool.
|
|
30
30
|
|
|
31
31
|
Don't just flag the update. Fix the break.
|
|
32
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""LLM-based migration for complex cases.
|
|
2
2
|
|
|
3
|
-
LLM migrations (Tier 2/3) are routed through the
|
|
3
|
+
LLM migrations (Tier 2/3) are routed through the Codeshift API,
|
|
4
4
|
which handles authentication, quota checking, and billing.
|
|
5
5
|
Users must have a Pro or Unlimited subscription to use LLM features.
|
|
6
6
|
|
|
@@ -11,12 +11,8 @@ import re
|
|
|
11
11
|
from dataclasses import dataclass
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
|
|
14
|
-
from codeshift.migrator.ast_transforms import
|
|
15
|
-
|
|
16
|
-
TransformResult,
|
|
17
|
-
TransformStatus,
|
|
18
|
-
)
|
|
19
|
-
from codeshift.utils.api_client import PyResolveAPIClient, get_api_client
|
|
14
|
+
from codeshift.migrator.ast_transforms import TransformChange, TransformResult, TransformStatus
|
|
15
|
+
from codeshift.utils.api_client import CodeshiftAPIClient, get_api_client
|
|
20
16
|
from codeshift.utils.cache import LLMCache, get_llm_cache
|
|
21
17
|
from codeshift.validator.syntax_checker import quick_syntax_check
|
|
22
18
|
|
|
@@ -35,10 +31,10 @@ class LLMMigrationResult:
|
|
|
35
31
|
|
|
36
32
|
|
|
37
33
|
class LLMMigrator:
|
|
38
|
-
"""Handles complex migrations using LLM via the
|
|
34
|
+
"""Handles complex migrations using LLM via the Codeshift API.
|
|
39
35
|
|
|
40
36
|
LLM migrations require authentication and a Pro or Unlimited subscription.
|
|
41
|
-
All LLM calls are routed through the
|
|
37
|
+
All LLM calls are routed through the Codeshift API, which handles:
|
|
42
38
|
- Authentication and authorization
|
|
43
39
|
- Quota checking and billing
|
|
44
40
|
- Server-side Anthropic API calls
|
|
@@ -49,7 +45,7 @@ class LLMMigrator:
|
|
|
49
45
|
|
|
50
46
|
def __init__(
|
|
51
47
|
self,
|
|
52
|
-
client:
|
|
48
|
+
client: CodeshiftAPIClient | None = None,
|
|
53
49
|
cache: LLMCache | None = None,
|
|
54
50
|
use_cache: bool = True,
|
|
55
51
|
validate_output: bool = True,
|
|
@@ -80,7 +76,7 @@ class LLMMigrator:
|
|
|
80
76
|
to_version: str,
|
|
81
77
|
context: str | None = None,
|
|
82
78
|
) -> LLMMigrationResult:
|
|
83
|
-
"""Migrate code using the LLM via
|
|
79
|
+
"""Migrate code using the LLM via Codeshift API.
|
|
84
80
|
|
|
85
81
|
Args:
|
|
86
82
|
code: Source code to migrate
|
|
@@ -114,7 +110,7 @@ class LLMMigrator:
|
|
|
114
110
|
used_cache=True,
|
|
115
111
|
)
|
|
116
112
|
|
|
117
|
-
# Call
|
|
113
|
+
# Call Codeshift API (which calls Anthropic server-side)
|
|
118
114
|
response = self.client.migrate_code(
|
|
119
115
|
code=code,
|
|
120
116
|
library=library,
|
codeshift/utils/__init__.py
CHANGED
codeshift/utils/api_client.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Codeshift API client for LLM-powered migrations.
|
|
2
2
|
|
|
3
|
-
This client calls the
|
|
3
|
+
This client calls the Codeshift API instead of Anthropic directly,
|
|
4
4
|
ensuring that LLM features are gated behind the subscription model.
|
|
5
5
|
"""
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ from codeshift.cli.commands.auth import get_api_key, get_api_url
|
|
|
13
13
|
|
|
14
14
|
@dataclass
|
|
15
15
|
class APIResponse:
|
|
16
|
-
"""Response from the
|
|
16
|
+
"""Response from the Codeshift API."""
|
|
17
17
|
|
|
18
18
|
success: bool
|
|
19
19
|
content: str
|
|
@@ -22,10 +22,10 @@ class APIResponse:
|
|
|
22
22
|
cached: bool = False
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class
|
|
26
|
-
"""Client for interacting with the
|
|
25
|
+
class CodeshiftAPIClient:
|
|
26
|
+
"""Client for interacting with the Codeshift API for LLM migrations.
|
|
27
27
|
|
|
28
|
-
This client routes all LLM calls through the
|
|
28
|
+
This client routes all LLM calls through the Codeshift API,
|
|
29
29
|
which handles:
|
|
30
30
|
- Authentication and authorization
|
|
31
31
|
- Quota checking and billing
|
|
@@ -41,7 +41,7 @@ class PyResolveAPIClient:
|
|
|
41
41
|
"""Initialize the API client.
|
|
42
42
|
|
|
43
43
|
Args:
|
|
44
|
-
api_key:
|
|
44
|
+
api_key: Codeshift API key. Defaults to stored credentials.
|
|
45
45
|
api_url: API base URL. Defaults to stored URL.
|
|
46
46
|
timeout: Request timeout in seconds.
|
|
47
47
|
"""
|
|
@@ -89,7 +89,7 @@ class PyResolveAPIClient:
|
|
|
89
89
|
to_version: str,
|
|
90
90
|
context: str | None = None,
|
|
91
91
|
) -> APIResponse:
|
|
92
|
-
"""Migrate code using the
|
|
92
|
+
"""Migrate code using the Codeshift API.
|
|
93
93
|
|
|
94
94
|
Args:
|
|
95
95
|
code: Source code to migrate
|
|
@@ -249,14 +249,14 @@ class PyResolveAPIClient:
|
|
|
249
249
|
|
|
250
250
|
|
|
251
251
|
# Singleton instance
|
|
252
|
-
_default_client:
|
|
252
|
+
_default_client: CodeshiftAPIClient | None = None
|
|
253
253
|
|
|
254
254
|
|
|
255
|
-
def get_api_client() ->
|
|
255
|
+
def get_api_client() -> CodeshiftAPIClient:
|
|
256
256
|
"""Get the default API client instance."""
|
|
257
257
|
global _default_client
|
|
258
258
|
if _default_client is None:
|
|
259
|
-
_default_client =
|
|
259
|
+
_default_client = CodeshiftAPIClient()
|
|
260
260
|
return _default_client
|
|
261
261
|
|
|
262
262
|
|
codeshift/utils/cache.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeshift
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: AI-powered CLI tool that migrates Python code to handle breaking dependency changes
|
|
5
|
-
Author:
|
|
5
|
+
Author: Ragab Technologies
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/Ragab-Technologies/Codeshift
|
|
8
8
|
Project-URL: Repository, https://github.com/Ragab-Technologies/Codeshift
|
|
@@ -27,19 +27,6 @@ Requires-Dist: rich>=13.0
|
|
|
27
27
|
Requires-Dist: toml>=0.10
|
|
28
28
|
Requires-Dist: packaging>=23.0
|
|
29
29
|
Requires-Dist: httpx>=0.25
|
|
30
|
-
Requires-Dist: pytest>=8.4.2
|
|
31
|
-
Requires-Dist: nox>=2025.11.12
|
|
32
|
-
Requires-Dist: black>=24.10.0
|
|
33
|
-
Requires-Dist: mypy>=1.19.1
|
|
34
|
-
Requires-Dist: supabase>=2.27.2
|
|
35
|
-
Requires-Dist: pre-commit>=4.5.1
|
|
36
|
-
Provides-Extra: api
|
|
37
|
-
Requires-Dist: fastapi>=0.109.0; extra == "api"
|
|
38
|
-
Requires-Dist: uvicorn>=0.27.0; extra == "api"
|
|
39
|
-
Requires-Dist: supabase>=2.3.0; extra == "api"
|
|
40
|
-
Requires-Dist: stripe>=7.0.0; extra == "api"
|
|
41
|
-
Requires-Dist: pydantic-settings>=2.1.0; extra == "api"
|
|
42
|
-
Requires-Dist: email-validator>=2.0.0; extra == "api"
|
|
43
30
|
Provides-Extra: dev
|
|
44
31
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
45
32
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
@@ -50,13 +37,10 @@ Requires-Dist: nox>=2024.0; extra == "dev"
|
|
|
50
37
|
Requires-Dist: pre-commit>=3.6.0; extra == "dev"
|
|
51
38
|
Requires-Dist: types-toml>=0.10; extra == "dev"
|
|
52
39
|
Requires-Dist: types-PyYAML>=6.0; extra == "dev"
|
|
53
|
-
Provides-Extra: all
|
|
54
|
-
Requires-Dist: codeshift[api,dev]; extra == "all"
|
|
55
40
|
Dynamic: license-file
|
|
56
41
|
|
|
57
42
|
# Codeshift
|
|
58
43
|
|
|
59
|
-
[](https://github.com/Ragab-Technologies/Codeshift/actions/workflows/ci.yml)
|
|
60
44
|
[](https://pypi.org/project/codeshift/)
|
|
61
45
|
[](https://www.python.org/downloads/)
|
|
62
46
|
[](LICENSE)
|
|
@@ -1,33 +1,17 @@
|
|
|
1
|
-
codeshift/__init__.py,sha256=
|
|
1
|
+
codeshift/__init__.py,sha256=L-ImO-zi7CvCuKCl_PYICUgTDppPXgYDaUoIQWukRzU,202
|
|
2
2
|
codeshift/analyzer/__init__.py,sha256=m61k8rtOyHQNoPLDeoe0S9WSy9syvCyMgLZ07ojpoNU,188
|
|
3
3
|
codeshift/analyzer/risk_assessor.py,sha256=nKplyymbsqcbsZyDLVZ2zukpQlPD5KIvO-FBJ4AZFYc,13422
|
|
4
|
-
codeshift/
|
|
5
|
-
codeshift/
|
|
6
|
-
codeshift/api/config.py,sha256=QYCSKT9t4D_mSQHIsEmaFXvQhy7yZW7EJNUqmRB8T5E,1989
|
|
7
|
-
codeshift/api/database.py,sha256=VIsau8ccQ1ZRQAeQWF85GsdkqaDabTOnMlpgiJcLMLU,7171
|
|
8
|
-
codeshift/api/main.py,sha256=Q6rVpUhEeX-4KBbUXliUgQ5RGTjHKPyiPggVfZVHmvg,2877
|
|
9
|
-
codeshift/api/models/__init__.py,sha256=IXaCQUxh0hF03nh_3-fP0vpYdRab-RsI5LtKv9okELA,1143
|
|
10
|
-
codeshift/api/models/auth.py,sha256=vbSspwn1UR0km_nC4dD08JCib56IDFQwAQqXiHZ6_t4,2435
|
|
11
|
-
codeshift/api/models/billing.py,sha256=6Q0RMiYYqOAAvOPzJqnJ6FgV0mylPD0_GiguocO-Uo0,2107
|
|
12
|
-
codeshift/api/models/migrate.py,sha256=XkFQ3bZWlXngnkbE3im3eZo5WnoPt1BCHpKSp38PIMY,1353
|
|
13
|
-
codeshift/api/models/usage.py,sha256=YuP3XM9UsHE6OWHcXHY1jTWDirtln5Sx-0-VaDo6p8M,2911
|
|
14
|
-
codeshift/api/routers/__init__.py,sha256=kh9jmNdH0v_A70KN2GbAb4zhHBnl992pK3m_aEDYtEw,171
|
|
15
|
-
codeshift/api/routers/auth.py,sha256=1mJ3Hx7ARuZGA9iuglOUDXE8khScQfjMcL5naLE6MfM,13106
|
|
16
|
-
codeshift/api/routers/billing.py,sha256=imG1W89cv204IojrGePd9-wXEIavsGZBa1_Obzuu9Qc,12322
|
|
17
|
-
codeshift/api/routers/migrate.py,sha256=2FddO-XAHOVIoGGh7nGwVCd17jYl_FY-x7YnjKlYnxc,8945
|
|
18
|
-
codeshift/api/routers/usage.py,sha256=sLaefM9T-3Kw-OGKOIERS_Z3btZTDx8Mxu-6zAuvF-w,9197
|
|
19
|
-
codeshift/api/routers/webhooks.py,sha256=oHn-Fie4KxbPnfP7hdSe8yfp8JHNDkdyBBj2kGnbnfA,8733
|
|
20
|
-
codeshift/cli/__init__.py,sha256=khf471pmz_J7-MGnarVWvmM0mCKFvdXpaPSJgx-KvD4,87
|
|
21
|
-
codeshift/cli/main.py,sha256=iHNfLciYfq2F_QF-7hBFQvT7ezbIFg0zPJesWoKPT3Y,6983
|
|
4
|
+
codeshift/cli/__init__.py,sha256=rf0c8oMqzYktdLH8ga2sA7rS5GsJse8dGfIbfW4DDXo,87
|
|
5
|
+
codeshift/cli/main.py,sha256=iLXy2QMLXoiM88sn-XEHk9vJncB-WeKfEq0eps3ZHig,6983
|
|
22
6
|
codeshift/cli/package_manager.py,sha256=K7spYHSQ6VoHlrzM3L9B2JoxBTe_9gLd57P6ME0PsSI,2807
|
|
23
7
|
codeshift/cli/quota.py,sha256=zBiY3zqCGEyxbS3vnoQdgBld1emMQzLc0_5cUOWy9U8,5989
|
|
24
|
-
codeshift/cli/commands/__init__.py,sha256=
|
|
8
|
+
codeshift/cli/commands/__init__.py,sha256=Kbs7DFUWOXkw5-9jiiR03cuUJeo5byusLr_Y3cKFE3E,218
|
|
25
9
|
codeshift/cli/commands/apply.py,sha256=JqUiu6I5WD25677SXpOejKxLWNIUQUKrbOQ_JbpoEak,11213
|
|
26
|
-
codeshift/cli/commands/auth.py,sha256=
|
|
10
|
+
codeshift/cli/commands/auth.py,sha256=FO60p545yxYySw6v6CcJPUh-Q5byM_AUz1pxypccrUA,28066
|
|
27
11
|
codeshift/cli/commands/diff.py,sha256=4LjrVRu4lhj-aOBvClOnEnN0l2nZEU5S1_qzYoXL4dQ,6357
|
|
28
|
-
codeshift/cli/commands/scan.py,sha256=
|
|
29
|
-
codeshift/cli/commands/upgrade.py,sha256=
|
|
30
|
-
codeshift/cli/commands/upgrade_all.py,sha256=
|
|
12
|
+
codeshift/cli/commands/scan.py,sha256=Eia3xW6sVZSiKtxd7JXyjIcOUsxduLGOuFjBhhp1ut0,11373
|
|
13
|
+
codeshift/cli/commands/upgrade.py,sha256=JjPKbjMNDzBDZuz39JYas48Ch4vgrrtyc6AEoPq6Q6Q,15853
|
|
14
|
+
codeshift/cli/commands/upgrade_all.py,sha256=FimS7SYJeYkQvhrWACLQG4QiyyynCgi9SapUb8Ax0Ik,18964
|
|
31
15
|
codeshift/knowledge/__init__.py,sha256=_YwrLgjvsJQuYajfnIhUQqFeircF0MfkI9zJBPZTupc,1221
|
|
32
16
|
codeshift/knowledge/cache.py,sha256=aEx9aNDfrzCYMzlPRBzBOYiFIAGDcZJfQvcXroa5vsA,4837
|
|
33
17
|
codeshift/knowledge/generator.py,sha256=WVCF0ULYmVQRHBiK6BTyS6FuArSCj-UDWzPJLujVbxU,7304
|
|
@@ -55,7 +39,7 @@ codeshift/knowledge_base/libraries/sqlalchemy.yaml,sha256=qEk1Nc2ovgCiBLPLsBrcsW
|
|
|
55
39
|
codeshift/migrator/__init__.py,sha256=V5ATKxw4hV6Ec3g78IeHkP92-VM4l6OzH0gi-lnU09w,467
|
|
56
40
|
codeshift/migrator/ast_transforms.py,sha256=fyySqSIFVHS8jTFledkbGWQ-_zcC5-S_rsv4sPu9jEA,7190
|
|
57
41
|
codeshift/migrator/engine.py,sha256=QkxkAFIewmSOwFW041BtPBqEfv7x8nVH8zsS-bSB1Do,14572
|
|
58
|
-
codeshift/migrator/llm_migrator.py,sha256=
|
|
42
|
+
codeshift/migrator/llm_migrator.py,sha256=qWqb3Gzj4khDhlTmlHbpFplQ8zTq2c_ev52qL9Yeg90,10356
|
|
59
43
|
codeshift/migrator/transforms/__init__.py,sha256=WWeJSr4dOKXwl8XmoCBhkYTxd4e0ENEwfMoLNwkGYsc,807
|
|
60
44
|
codeshift/migrator/transforms/aiohttp_transformer.py,sha256=-7N9xYeoDSOy4iUujKU_0CtTfQS2Mx4P9lKEvriN6YQ,23297
|
|
61
45
|
codeshift/migrator/transforms/attrs_transformer.py,sha256=_Bgg-oLUzvTENZnH1yIORH2rV9F2vJinnyRwQpBalXM,24128
|
|
@@ -75,17 +59,17 @@ codeshift/migrator/transforms/sqlalchemy_transformer.py,sha256=rgoIk4_iDCuydZhy9
|
|
|
75
59
|
codeshift/scanner/__init__.py,sha256=GFx9yMPZVuxBC8mGOPZoINsCsJgHV4TSjiV4KSF3fPU,300
|
|
76
60
|
codeshift/scanner/code_scanner.py,sha256=YGuHVI1FN0h8cGSARFlF5duFd8WBCJUSVMcqCbsjqEQ,12859
|
|
77
61
|
codeshift/scanner/dependency_parser.py,sha256=Vd-wbgcG2trgLN7wntbNrGwuXvamn3u7B5SGvORdPiY,15372
|
|
78
|
-
codeshift/utils/__init__.py,sha256=
|
|
79
|
-
codeshift/utils/api_client.py,sha256=
|
|
80
|
-
codeshift/utils/cache.py,sha256=
|
|
62
|
+
codeshift/utils/__init__.py,sha256=8G28m1UBDdEqF_G8GN6qRFWhpjDhiXJmFd9gSgIvkQc,148
|
|
63
|
+
codeshift/utils/api_client.py,sha256=W6Us-eSqk3jl9l29lnEVa0QfuddZiFwA3D6FPmShRQw,8010
|
|
64
|
+
codeshift/utils/cache.py,sha256=9vPjU54S48iKy4CDvcjgz0u9eeIx7aUAHCdk3coRF6w,8793
|
|
81
65
|
codeshift/utils/config.py,sha256=8x-rEh4q99K0HvT4ZQHDQAeUT8Tc_HATkZOomBGVyIA,2454
|
|
82
66
|
codeshift/utils/llm_client.py,sha256=WkT3KftJi7rsj8MXH4MVJvznugicb2XpkKqnqRET1eo,6369
|
|
83
67
|
codeshift/validator/__init__.py,sha256=WRQSfJ7eLJdjR2_f_dXSaBtfawkvu1Dlu20Gh76D12c,280
|
|
84
68
|
codeshift/validator/syntax_checker.py,sha256=FJeLIqhNhV7_Xj2RskHScJZks6A9fybaqv5Z1-MGDfo,5343
|
|
85
69
|
codeshift/validator/test_runner.py,sha256=VX0OqkuI3AJxOUzRW2_BEjdDsMw1N4a0od-pPbSF6O8,6760
|
|
86
|
-
codeshift-0.
|
|
87
|
-
codeshift-0.
|
|
88
|
-
codeshift-0.
|
|
89
|
-
codeshift-0.
|
|
90
|
-
codeshift-0.
|
|
91
|
-
codeshift-0.
|
|
70
|
+
codeshift-0.4.0.dist-info/licenses/LICENSE,sha256=mHKnse9JK19WRK76lYEwKB9nJWyzMzRpG4gkUtbTyac,1066
|
|
71
|
+
codeshift-0.4.0.dist-info/METADATA,sha256=UDI4FyXLTM0c8C03CCOZQ4SSC3_F_kyrNpkrE8ePrac,16746
|
|
72
|
+
codeshift-0.4.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
73
|
+
codeshift-0.4.0.dist-info/entry_points.txt,sha256=AlJ8V7a2pNyu-9UiRKUWiTMIJtaYAUnlg53Y-wFHiK0,53
|
|
74
|
+
codeshift-0.4.0.dist-info/top_level.txt,sha256=Ct42mtGs5foZ4MyYSksd5rXP0qFhWSZz8Y8mON0EEds,10
|
|
75
|
+
codeshift-0.4.0.dist-info/RECORD,,
|
codeshift/api/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""PyResolve Billing API."""
|
codeshift/api/auth.py
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
"""Authentication utilities and dependencies for the PyResolve API."""
|
|
2
|
-
|
|
3
|
-
import hashlib
|
|
4
|
-
import secrets
|
|
5
|
-
from collections.abc import Awaitable, Callable
|
|
6
|
-
from typing import Annotated
|
|
7
|
-
|
|
8
|
-
from fastapi import Depends, HTTPException, Security, status
|
|
9
|
-
from fastapi.security import APIKeyHeader
|
|
10
|
-
|
|
11
|
-
from codeshift.api.config import get_settings
|
|
12
|
-
from codeshift.api.database import get_database
|
|
13
|
-
|
|
14
|
-
# API Key header scheme
|
|
15
|
-
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def generate_api_key() -> tuple[str, str, str]:
|
|
19
|
-
"""Generate a new API key.
|
|
20
|
-
|
|
21
|
-
Returns:
|
|
22
|
-
Tuple of (full_key, key_prefix, key_hash)
|
|
23
|
-
"""
|
|
24
|
-
settings = get_settings()
|
|
25
|
-
|
|
26
|
-
# Generate 32 random bytes (256 bits of entropy)
|
|
27
|
-
key_suffix = secrets.token_urlsafe(32)
|
|
28
|
-
|
|
29
|
-
# Create the full key with prefix
|
|
30
|
-
full_key = f"{settings.api_key_prefix}{key_suffix}"
|
|
31
|
-
|
|
32
|
-
# Get prefix for identification (first 12 chars including prefix)
|
|
33
|
-
key_prefix = full_key[:12]
|
|
34
|
-
|
|
35
|
-
# Hash the full key for storage
|
|
36
|
-
key_hash = hash_api_key(full_key)
|
|
37
|
-
|
|
38
|
-
return full_key, key_prefix, key_hash
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def hash_api_key(api_key: str) -> str:
|
|
42
|
-
"""Hash an API key using SHA-256."""
|
|
43
|
-
return hashlib.sha256(api_key.encode()).hexdigest()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class AuthenticatedUser:
|
|
47
|
-
"""Authenticated user context."""
|
|
48
|
-
|
|
49
|
-
def __init__(
|
|
50
|
-
self,
|
|
51
|
-
user_id: str,
|
|
52
|
-
email: str,
|
|
53
|
-
tier: str,
|
|
54
|
-
api_key_id: str | None = None,
|
|
55
|
-
scopes: list[str] | None = None,
|
|
56
|
-
):
|
|
57
|
-
self.user_id = user_id
|
|
58
|
-
self.email = email
|
|
59
|
-
self.tier = tier
|
|
60
|
-
self.api_key_id = api_key_id
|
|
61
|
-
self.scopes = scopes or []
|
|
62
|
-
|
|
63
|
-
def has_scope(self, scope: str) -> bool:
|
|
64
|
-
"""Check if user has a specific scope."""
|
|
65
|
-
return scope in self.scopes or "admin" in self.scopes
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
async def get_current_user(
|
|
69
|
-
api_key: Annotated[str | None, Security(api_key_header)] = None,
|
|
70
|
-
) -> AuthenticatedUser:
|
|
71
|
-
"""Validate API key and return the authenticated user.
|
|
72
|
-
|
|
73
|
-
Raises:
|
|
74
|
-
HTTPException: If API key is invalid or missing
|
|
75
|
-
"""
|
|
76
|
-
if not api_key:
|
|
77
|
-
raise HTTPException(
|
|
78
|
-
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
79
|
-
detail="Missing API key",
|
|
80
|
-
headers={"WWW-Authenticate": "ApiKey"},
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
# Hash the provided key
|
|
84
|
-
key_hash = hash_api_key(api_key)
|
|
85
|
-
|
|
86
|
-
# Look up the key in the database
|
|
87
|
-
db = get_database()
|
|
88
|
-
api_key_record = db.get_api_key_by_hash(key_hash)
|
|
89
|
-
|
|
90
|
-
if not api_key_record:
|
|
91
|
-
raise HTTPException(
|
|
92
|
-
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
93
|
-
detail="Invalid API key",
|
|
94
|
-
headers={"WWW-Authenticate": "ApiKey"},
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
# Check if key is expired
|
|
98
|
-
if api_key_record.get("expires_at"):
|
|
99
|
-
from datetime import datetime, timezone
|
|
100
|
-
|
|
101
|
-
expires_at = api_key_record["expires_at"]
|
|
102
|
-
if isinstance(expires_at, str):
|
|
103
|
-
expires_at = datetime.fromisoformat(expires_at.replace("Z", "+00:00"))
|
|
104
|
-
if expires_at < datetime.now(timezone.utc):
|
|
105
|
-
raise HTTPException(
|
|
106
|
-
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
107
|
-
detail="API key has expired",
|
|
108
|
-
headers={"WWW-Authenticate": "ApiKey"},
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
# Update last used timestamp
|
|
112
|
-
db.update_api_key_last_used(api_key_record["id"])
|
|
113
|
-
|
|
114
|
-
# Get profile data
|
|
115
|
-
profile = api_key_record.get("profiles", {})
|
|
116
|
-
|
|
117
|
-
return AuthenticatedUser(
|
|
118
|
-
user_id=api_key_record["user_id"],
|
|
119
|
-
email=profile.get("email", ""),
|
|
120
|
-
tier=profile.get("tier", "free"),
|
|
121
|
-
api_key_id=api_key_record["id"],
|
|
122
|
-
scopes=api_key_record.get("scopes", []),
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
async def get_optional_user(
|
|
127
|
-
api_key: Annotated[str | None, Security(api_key_header)] = None,
|
|
128
|
-
) -> AuthenticatedUser | None:
|
|
129
|
-
"""Get the current user if authenticated, otherwise return None.
|
|
130
|
-
|
|
131
|
-
This is useful for endpoints that work both authenticated and unauthenticated.
|
|
132
|
-
"""
|
|
133
|
-
if not api_key:
|
|
134
|
-
return None
|
|
135
|
-
|
|
136
|
-
try:
|
|
137
|
-
return await get_current_user(api_key)
|
|
138
|
-
except HTTPException:
|
|
139
|
-
return None
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def require_scope(scope: str) -> Callable[..., Awaitable[AuthenticatedUser]]:
|
|
143
|
-
"""Dependency that requires a specific scope."""
|
|
144
|
-
|
|
145
|
-
async def check_scope(
|
|
146
|
-
user: Annotated[AuthenticatedUser, Depends(get_current_user)],
|
|
147
|
-
) -> AuthenticatedUser:
|
|
148
|
-
if not user.has_scope(scope):
|
|
149
|
-
raise HTTPException(
|
|
150
|
-
status_code=status.HTTP_403_FORBIDDEN,
|
|
151
|
-
detail=f"Scope '{scope}' required",
|
|
152
|
-
)
|
|
153
|
-
return user
|
|
154
|
-
|
|
155
|
-
return check_scope
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def require_tier(minimum_tier: str) -> Callable[..., Awaitable[AuthenticatedUser]]:
|
|
159
|
-
"""Dependency that requires a minimum tier."""
|
|
160
|
-
tier_levels = {"free": 0, "pro": 1, "unlimited": 2, "enterprise": 3}
|
|
161
|
-
|
|
162
|
-
async def check_tier(
|
|
163
|
-
user: Annotated[AuthenticatedUser, Depends(get_current_user)],
|
|
164
|
-
) -> AuthenticatedUser:
|
|
165
|
-
user_level = tier_levels.get(user.tier, 0)
|
|
166
|
-
required_level = tier_levels.get(minimum_tier, 0)
|
|
167
|
-
|
|
168
|
-
if user_level < required_level:
|
|
169
|
-
raise HTTPException(
|
|
170
|
-
status_code=status.HTTP_403_FORBIDDEN,
|
|
171
|
-
detail=f"This feature requires {minimum_tier} tier or higher",
|
|
172
|
-
)
|
|
173
|
-
return user
|
|
174
|
-
|
|
175
|
-
return check_tier
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
# Type aliases for dependency injection
|
|
179
|
-
CurrentUser = Annotated[AuthenticatedUser, Depends(get_current_user)]
|
|
180
|
-
OptionalUser = Annotated[AuthenticatedUser | None, Depends(get_optional_user)]
|
|
181
|
-
ProUser = Annotated[AuthenticatedUser, Depends(require_tier("pro"))]
|
|
182
|
-
UnlimitedUser = Annotated[AuthenticatedUser, Depends(require_tier("unlimited"))]
|