codeshift 0.3.7__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.
Files changed (34) hide show
  1. codeshift/__init__.py +2 -2
  2. codeshift/cli/__init__.py +1 -1
  3. codeshift/cli/commands/__init__.py +1 -1
  4. codeshift/cli/commands/auth.py +5 -5
  5. codeshift/cli/commands/scan.py +2 -5
  6. codeshift/cli/commands/upgrade.py +2 -7
  7. codeshift/cli/commands/upgrade_all.py +1 -1
  8. codeshift/cli/main.py +2 -2
  9. codeshift/migrator/llm_migrator.py +8 -12
  10. codeshift/utils/__init__.py +1 -1
  11. codeshift/utils/api_client.py +11 -11
  12. codeshift/utils/cache.py +1 -1
  13. {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/METADATA +2 -17
  14. {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/RECORD +18 -34
  15. {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/licenses/LICENSE +1 -1
  16. codeshift/api/__init__.py +0 -1
  17. codeshift/api/auth.py +0 -182
  18. codeshift/api/config.py +0 -73
  19. codeshift/api/database.py +0 -215
  20. codeshift/api/main.py +0 -103
  21. codeshift/api/models/__init__.py +0 -55
  22. codeshift/api/models/auth.py +0 -108
  23. codeshift/api/models/billing.py +0 -92
  24. codeshift/api/models/migrate.py +0 -42
  25. codeshift/api/models/usage.py +0 -116
  26. codeshift/api/routers/__init__.py +0 -5
  27. codeshift/api/routers/auth.py +0 -440
  28. codeshift/api/routers/billing.py +0 -395
  29. codeshift/api/routers/migrate.py +0 -304
  30. codeshift/api/routers/usage.py +0 -291
  31. codeshift/api/routers/webhooks.py +0 -289
  32. {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/WHEEL +0 -0
  33. {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/entry_points.txt +0 -0
  34. {codeshift-0.3.7.dist-info → codeshift-0.4.0.dist-info}/top_level.txt +0 -0
codeshift/__init__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """
2
- PyResolve - AI-powered CLI tool for migrating Python code to handle breaking dependency changes.
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__ = "PyResolve Team"
8
+ __author__ = "Codeshift Team"
codeshift/cli/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- """CLI module for PyResolve."""
1
+ """CLI module for Codeshift."""
2
2
 
3
3
  from codeshift.cli.main import cli
4
4
 
@@ -1,4 +1,4 @@
1
- """CLI commands for PyResolve."""
1
+ """CLI commands for Codeshift."""
2
2
 
3
3
  from codeshift.cli.commands.apply import apply
4
4
  from codeshift.cli.commands.diff import diff
@@ -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("PYRESOLVE_API_KEY")
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 PyResolve to enable cloud features.
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 PyResolve account.
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 PyResolve.[/]",
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 PyResolve and remove saved credentials."""
469
+ """Logout from Codeshift and remove saved credentials."""
470
470
  creds = load_credentials()
471
471
 
472
472
  if not creds:
@@ -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="PyResolve Scan",
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="PyResolve Migration",
128
+ title="Codeshift Migration",
134
129
  )
135
130
  )
136
131
 
@@ -234,7 +234,7 @@ def upgrade_all(
234
234
  console.print(
235
235
  Panel(
236
236
  "[bold]Scanning project for upgradeable dependencies[/]\n\n" f"Path: {project_path}",
237
- title="PyResolve Upgrade All",
237
+ title="Codeshift Upgrade All",
238
238
  )
239
239
  )
240
240
 
codeshift/cli/main.py CHANGED
@@ -1,4 +1,4 @@
1
- """Main CLI entry point for PyResolve."""
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
- """PyResolve - AI-powered Python dependency migration tool.
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 PyResolve API,
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
- TransformChange,
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 PyResolve API.
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 PyResolve API, which handles:
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: PyResolveAPIClient | None = None,
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 PyResolve API.
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 PyResolve API (which calls Anthropic server-side)
113
+ # Call Codeshift API (which calls Anthropic server-side)
118
114
  response = self.client.migrate_code(
119
115
  code=code,
120
116
  library=library,
@@ -1,4 +1,4 @@
1
- """Utility functions and classes for PyResolve."""
1
+ """Utility functions and classes for Codeshift."""
2
2
 
3
3
  from codeshift.utils.config import Config, ProjectConfig
4
4
 
@@ -1,6 +1,6 @@
1
- """PyResolve API client for LLM-powered migrations.
1
+ """Codeshift API client for LLM-powered migrations.
2
2
 
3
- This client calls the PyResolve API instead of Anthropic directly,
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 PyResolve API."""
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 PyResolveAPIClient:
26
- """Client for interacting with the PyResolve API for LLM migrations.
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 PyResolve API,
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: PyResolve API key. Defaults to stored credentials.
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 PyResolve API.
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: PyResolveAPIClient | None = None
252
+ _default_client: CodeshiftAPIClient | None = None
253
253
 
254
254
 
255
- def get_api_client() -> PyResolveAPIClient:
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 = PyResolveAPIClient()
259
+ _default_client = CodeshiftAPIClient()
260
260
  return _default_client
261
261
 
262
262
 
codeshift/utils/cache.py CHANGED
@@ -27,7 +27,7 @@ class CacheEntry:
27
27
 
28
28
 
29
29
  class Cache:
30
- """Simple file-based cache for PyResolve."""
30
+ """Simple file-based cache for Codeshift."""
31
31
 
32
32
  def __init__(
33
33
  self,
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeshift
3
- Version: 0.3.7
3
+ Version: 0.4.0
4
4
  Summary: AI-powered CLI tool that migrates Python code to handle breaking dependency changes
5
- Author: PyResolve Team
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,8 +37,6 @@ 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
@@ -1,33 +1,17 @@
1
- codeshift/__init__.py,sha256=rDp65Hj8u9ubLT3_gkhrKfdSBIlXKBQays3n_eLXl0s,202
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/api/__init__.py,sha256=jlBP8FeFfOkITfWFAK9Wqc0UHt6kyYAqVXzfvT_EQMk,29
5
- codeshift/api/auth.py,sha256=37wWEr4rvGbEB4HQIxI-m-uWa6HgjHQdCWCaaDwOpOA,5565
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=phw462By4_CzFoJQUdON73qF5-5Mv45GwUEOX_5SbLc,218
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=uXUtNEtAH17vhSlANfcFR5pCP8NQmFJ7Uw0bN30rYlM,28066
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=JXR3MMKWOh1dlAEJ-OXh-EjCDBrBMxqzI6snZcBirqU,11386
29
- codeshift/cli/commands/upgrade.py,sha256=_V1BP15BJR6OZHg7jdq90kfdUpjquf_FoWNNEWNzrnI,15874
30
- codeshift/cli/commands/upgrade_all.py,sha256=v-fz9jWCzJWWW6d0QXTdOfd2M54okI9AA6qv7qxLPiE,18964
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=dyxVNu8Rf0FPM7Q_4TXt8f5nbbMqOHOIc_zbMsRm3RU,10373
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=dNtKUzFwoMxuEl41d_V0Zggv5lprZnOowB8Qt_THwbg,148
79
- codeshift/utils/api_client.py,sha256=RUbKiox9dc_xc4z7IcrZ7plObYkkaKFa7Dw4qi75KGw,8010
80
- codeshift/utils/cache.py,sha256=WHwNToQVmzhLgmHvt9nWUlzDFmVLBp_Sax7R4rrQfOg,8793
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.3.7.dist-info/licenses/LICENSE,sha256=SrjIFBfm7GQh90Giv7mEiPnYdgp1wsp0RnOTT6HkeVw,1066
87
- codeshift-0.3.7.dist-info/METADATA,sha256=CwpDXX4P62C0Cgq5cR5cVneNXQeEkJCyoNyj8P0TOY0,17312
88
- codeshift-0.3.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
89
- codeshift-0.3.7.dist-info/entry_points.txt,sha256=AlJ8V7a2pNyu-9UiRKUWiTMIJtaYAUnlg53Y-wFHiK0,53
90
- codeshift-0.3.7.dist-info/top_level.txt,sha256=Ct42mtGs5foZ4MyYSksd5rXP0qFhWSZz8Y8mON0EEds,10
91
- codeshift-0.3.7.dist-info/RECORD,,
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,,
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 PyResolve
3
+ Copyright (c) 2024 Codeshift
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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"))]