coding-client 1.21.2__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 (109) hide show
  1. coding_client/__init__.py +1 -0
  2. coding_client/api/__init__.py +19 -0
  3. coding_client/api/exception_handlers.py +158 -0
  4. coding_client/api/logging_setup.py +44 -0
  5. coding_client/api/metrics.py +40 -0
  6. coding_client/api/openapi_config.py +124 -0
  7. coding_client/api/request_logging.py +60 -0
  8. coding_client/api/validation.py +150 -0
  9. coding_client/auth/__init__.py +78 -0
  10. coding_client/auth/_default_tenant.py +132 -0
  11. coding_client/auth/_header_trust.py +98 -0
  12. coding_client/auth/_onbehalf.py +315 -0
  13. coding_client/auth/_trusted_networks.py +94 -0
  14. coding_client/auth/dependencies.py +61 -0
  15. coding_client/auth/middleware.py +445 -0
  16. coding_client/auth/models.py +83 -0
  17. coding_client/auth/outbound.py +137 -0
  18. coding_client/auth/rbac.py +21 -0
  19. coding_client/auth/rbac_coding.py +5 -0
  20. coding_client/auth/tenant.py +650 -0
  21. coding_client/auth/websocket.py +148 -0
  22. coding_client/clients/__init__.py +24 -0
  23. coding_client/clients/base.py +131 -0
  24. coding_client/clients/coding_ai.py +33 -0
  25. coding_client/clients/coding_analysis.py +36 -0
  26. coding_client/clients/coding_containers.py +45 -0
  27. coding_client/clients/coding_github.py +127 -0
  28. coding_client/clients/coding_realtime.py +127 -0
  29. coding_client/clients/coding_security.py +51 -0
  30. coding_client/clients/coding_testing.py +31 -0
  31. coding_client/clients/coding_workspaces.py +265 -0
  32. coding_client/config/__init__.py +12 -0
  33. coding_client/config/base_settings.py +61 -0
  34. coding_client/config/yaml_config_loader.py +172 -0
  35. coding_client/db/__init__.py +42 -0
  36. coding_client/db/db_types.py +122 -0
  37. coding_client/db/exceptions.py +59 -0
  38. coding_client/db/postgres_connection.py +572 -0
  39. coding_client/db/tenant_query_helper.py +417 -0
  40. coding_client/db_init.py +49 -0
  41. coding_client/exceptions/__init__.py +67 -0
  42. coding_client/exceptions/api_exceptions.py +395 -0
  43. coding_client/exceptions/pydantic_ai_exceptions.py +533 -0
  44. coding_client/interfaces/__init__.py +22 -0
  45. coding_client/interfaces/cache_interface.py +179 -0
  46. coding_client/interfaces/container_interface.py +49 -0
  47. coding_client/interfaces/docker_interface.py +283 -0
  48. coding_client/interfaces/graph_interface.py +28 -0
  49. coding_client/interfaces/security_interface.py +72 -0
  50. coding_client/interfaces/webhook_interface.py +67 -0
  51. coding_client/llm/__init__.py +54 -0
  52. coding_client/llm/base_agent.py +195 -0
  53. coding_client/llm/context.py +121 -0
  54. coding_client/llm/responses.py +102 -0
  55. coding_client/llm/routing.py +82 -0
  56. coding_client/llm/settings.py +102 -0
  57. coding_client/models/__init__.py +62 -0
  58. coding_client/models/agent.py +38 -0
  59. coding_client/models/ai_test_failure.py +745 -0
  60. coding_client/models/analysis.py +330 -0
  61. coding_client/models/architecture.py +685 -0
  62. coding_client/models/bug.py +128 -0
  63. coding_client/models/code_graph.py +317 -0
  64. coding_client/models/container.py +241 -0
  65. coding_client/models/core.py +878 -0
  66. coding_client/models/docker_container.py +104 -0
  67. coding_client/models/github.py +293 -0
  68. coding_client/models/logging.py +29 -0
  69. coding_client/models/package_manager.py +257 -0
  70. coding_client/models/pr_validation.py +440 -0
  71. coding_client/models/pydantic_ai.py +395 -0
  72. coding_client/models/repo_scanner.py +249 -0
  73. coding_client/models/responses.py +431 -0
  74. coding_client/models/security.py +185 -0
  75. coding_client/models/static_analysis.py +508 -0
  76. coding_client/models/system.py +48 -0
  77. coding_client/models/test_executor.py +204 -0
  78. coding_client/models/testing.py +673 -0
  79. coding_client/models/testing_config.py +200 -0
  80. coding_client/models/validation.py +127 -0
  81. coding_client/models_init.py +272 -0
  82. coding_client/telemetry.py +195 -0
  83. coding_client/testing.py +180 -0
  84. coding_client/types/__init__.py +130 -0
  85. coding_client/types/constraint_types.py +276 -0
  86. coding_client/types/core_types.py +112 -0
  87. coding_client/types/state_machine.py +443 -0
  88. coding_client/utils/__init__.py +86 -0
  89. coding_client/utils/archive_utils.py +387 -0
  90. coding_client/utils/ast_debug.py +45 -0
  91. coding_client/utils/async_timeout.py +364 -0
  92. coding_client/utils/constants.py +518 -0
  93. coding_client/utils/content_processing.py +326 -0
  94. coding_client/utils/database.py +916 -0
  95. coding_client/utils/encoding_helper.py +222 -0
  96. coding_client/utils/enum_helpers.py +166 -0
  97. coding_client/utils/file_operations.py +177 -0
  98. coding_client/utils/filesystem_ops.py +528 -0
  99. coding_client/utils/http_client.py +562 -0
  100. coding_client/utils/path_utils.py +471 -0
  101. coding_client/utils/profiling.py +421 -0
  102. coding_client/utils/response_builder.py +240 -0
  103. coding_client/utils/retry_helper.py +712 -0
  104. coding_client/utils/security_validation.py +423 -0
  105. coding_client-1.21.2.dist-info/METADATA +234 -0
  106. coding_client-1.21.2.dist-info/RECORD +109 -0
  107. coding_client-1.21.2.dist-info/WHEEL +5 -0
  108. coding_client-1.21.2.dist-info/licenses/LICENSE +201 -0
  109. coding_client-1.21.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,104 @@
1
+ """Docker-specific container models for internal container management.
2
+
3
+ This module provides Pydantic models that represent Docker containers and their
4
+ metadata within the code graph helper system. These models facilitate the management
5
+ of containerized development environments for code analysis.
6
+
7
+ The models in this module are specifically designed for internal use by the DockerService
8
+ and handle the conversion between Docker-specific representations and the public API models.
9
+
10
+ Classes:
11
+ DockerContainerDetails: Internal representation of a Docker container with all
12
+ Docker-specific metadata including ports, environment variables, and status.
13
+
14
+ Example:
15
+ ```python
16
+ from docker_container_models import DockerContainerDetails, ContainerStatus
17
+
18
+ # Create container details
19
+ container = DockerContainerDetails(
20
+ docker_id="abc123",
21
+ container_name="my-analysis-container",
22
+ image="code-graph-helper:latest",
23
+ ports={"8700/tcp": 8700},
24
+ workspace_path="/app/workspace",
25
+ status=ContainerStatus.RUNNING
26
+ )
27
+
28
+ # Convert to public API model
29
+ api_model = container.to_container_info(
30
+ code_graph_id="graph-123",
31
+ repo_url="https://github.com/owner/repo"
32
+ )
33
+ ```
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ from datetime import UTC, datetime
39
+
40
+ from pydantic import BaseModel, Field
41
+
42
+ from .container import ContainerInfo, ContainerStatus
43
+
44
+
45
+ class DockerContainerDetails(BaseModel):
46
+ """Docker-specific container details for internal use by DockerService."""
47
+
48
+ docker_id: str
49
+ container_name: str
50
+ image: str
51
+ ports: dict[str, int]
52
+ environment: dict[str, str] = Field(default_factory=dict)
53
+ workspace_path: str
54
+ last_activity: datetime = Field(default_factory=lambda: datetime.now(UTC))
55
+ status: ContainerStatus
56
+
57
+ def to_container_info(
58
+ self,
59
+ code_graph_id: str = "",
60
+ codebase_path: str = "/app/data/repo",
61
+ repo_url: str = "",
62
+ branch: str = "main",
63
+ description: str | None = None,
64
+ ) -> ContainerInfo:
65
+ """Convert Docker-specific details to public ContainerInfo model.
66
+
67
+ Transforms the internal Docker container representation into a public API model
68
+ that hides Docker-specific implementation details while providing essential
69
+ container information to API consumers.
70
+
71
+ Args:
72
+ code_graph_id (str, optional): Unique identifier for the associated code graph.
73
+ Defaults to empty string.
74
+ codebase_path (str, optional): Path to the codebase within the container.
75
+ Defaults to "/app/data/repo".
76
+ repo_url (str, optional): URL of the repository being analyzed.
77
+ Defaults to empty string.
78
+ branch (str, optional): Git branch being analyzed.
79
+ Defaults to "main".
80
+ description (Optional[str], optional): Human-readable description of the container.
81
+ Defaults to None.
82
+
83
+ Returns:
84
+ ContainerInfo: Public API model containing essential container information
85
+ including the container name, API endpoint URL, and status.
86
+
87
+ Note:
88
+ The method extracts the first available port mapping from the Docker container
89
+ to construct the API endpoint URL. If no ports are mapped, it defaults to 8700.
90
+ """
91
+
92
+ # Get the first port mapping (usually there's only one)
93
+ port = next(iter(self.ports.values()), 8700)
94
+
95
+ return ContainerInfo(
96
+ container_name=self.container_name,
97
+ api_endpoint=f"http://localhost:{port}",
98
+ code_graph_id=code_graph_id,
99
+ codebase_path=codebase_path,
100
+ repo_url=repo_url,
101
+ branch=branch,
102
+ status=self.status,
103
+ description=description,
104
+ )
@@ -0,0 +1,293 @@
1
+ """GitHub integration models for repository operations and webhook handling.
2
+
3
+ This module provides comprehensive data models for GitHub API integration, supporting
4
+ repository operations, pull request management, webhook processing, and code analysis
5
+ workflows. The models facilitate seamless integration between GitHub's API and the
6
+ code graph helper system.
7
+
8
+ The GitHub integration supports:
9
+ - Repository cloning and synchronization
10
+ - Pull request analysis and validation
11
+ - Webhook event processing
12
+ - Code snippet evaluation in repository context
13
+ - Automated PR creation and management
14
+ - User and organization data handling
15
+
16
+ Key Model Categories:
17
+ Core GitHub Models: GitHubUser, GitHubRepository, GitHubCommit for basic entities
18
+ Pull Request Models: PullRequestData, PullRequest for PR operations
19
+ Webhook Models: GitHubWebhookPayload for event processing
20
+ Workspace Models: RepoWorkspace, RepoSetupResult for repository management
21
+ Request Models: SnippetAnalysisRequest, PRCreationRequest for API operations
22
+
23
+ Example:
24
+ ```python
25
+ from github_models import GitHubRepository, PullRequestData, RepoSetupResult
26
+
27
+ # Repository information
28
+ repo = GitHubRepository(
29
+ id=123456,
30
+ name="my-project",
31
+ full_name="owner/my-project",
32
+ description="A sample project",
33
+ url="https://github.com/owner/my-project",
34
+ clone_url="https://github.com/owner/my-project.git",
35
+ default_branch="main",
36
+ language="Python",
37
+ size=1024,
38
+ stargazers_count=42,
39
+ forks_count=7,
40
+ open_issues_count=3,
41
+ private=False,
42
+ owner=GitHubUser(...)
43
+ )
44
+
45
+ # Pull request data
46
+ pr = PullRequestData(
47
+ number=123,
48
+ title="Add new feature",
49
+ state="open",
50
+ author="contributor",
51
+ created_at=datetime.now(timezone.utc),
52
+ updated_at=datetime.now(timezone.utc),
53
+ base_branch="main",
54
+ head_branch="feature/new-feature",
55
+ url="https://github.com/owner/my-project/pull/123"
56
+ )
57
+ ```
58
+ """
59
+
60
+ from __future__ import annotations
61
+
62
+ from datetime import datetime
63
+ from typing import Any
64
+
65
+ from pydantic import BaseModel, Field
66
+
67
+
68
+ class PullRequestData(BaseModel):
69
+ """Pull request data model."""
70
+
71
+ number: int = Field(..., description="Pull request number")
72
+ title: str = Field(..., description="Pull request title")
73
+ message: str | None = Field(None, description="Pull request message")
74
+ state: str = Field(..., description="Pull request state (open, closed, merged)")
75
+ author: str = Field(..., description="Pull request author")
76
+ created_at: datetime = Field(..., description="Creation timestamp")
77
+ updated_at: datetime = Field(..., description="Last update timestamp")
78
+ merged_at: datetime | None = Field(None, description="Merge timestamp")
79
+ base_branch: str = Field(..., description="Base branch")
80
+ head_branch: str = Field(..., description="Head branch")
81
+ url: str = Field(..., description="Pull request URL")
82
+ diff_url: str = Field(..., description="Diff URL")
83
+ patch_url: str = Field(..., description="Patch URL")
84
+
85
+
86
+ class GitHubUser(BaseModel):
87
+ """GitHub user model."""
88
+
89
+ login: str = Field(..., description="Username")
90
+ id: int = Field(..., description="User ID")
91
+ avatar_url: str = Field(..., description="Avatar URL")
92
+ url: str = Field(..., description="User URL")
93
+ type: str = Field(..., description="User type")
94
+
95
+
96
+ class GitHubRepository(BaseModel):
97
+ """GitHub repository model."""
98
+
99
+ id: int = Field(..., description="Repository ID")
100
+ name: str = Field(..., description="Repository name")
101
+ full_name: str = Field(..., description="Full repository name")
102
+ description: str | None = Field(None, description="Repository description")
103
+ url: str = Field(..., description="Repository URL")
104
+ clone_url: str = Field(..., description="Clone URL")
105
+ ssh_url: str = Field(..., description="SSH URL")
106
+ default_branch: str = Field(..., description="Default branch")
107
+ language: str | None = Field(None, description="Primary language")
108
+ size: int = Field(..., description="Repository size")
109
+ stargazers_count: int = Field(..., description="Number of stars")
110
+ forks_count: int = Field(..., description="Number of forks")
111
+ open_issues_count: int = Field(..., description="Number of open issues")
112
+ private: bool = Field(..., description="Whether repository is private")
113
+ owner: GitHubUser = Field(..., description="Repository owner")
114
+
115
+
116
+ class GitHubCommit(BaseModel):
117
+ """GitHub commit model."""
118
+
119
+ sha: str = Field(..., description="Commit SHA")
120
+ message: str = Field(..., description="Commit message")
121
+ author: GitHubUser = Field(..., description="Commit author")
122
+ committer: GitHubUser = Field(..., description="Commit committer")
123
+ timestamp: datetime = Field(..., description="Commit timestamp")
124
+ url: str = Field(..., description="Commit URL")
125
+
126
+
127
+ class GitHubIssue(BaseModel):
128
+ """GitHub issue model."""
129
+
130
+ number: int = Field(..., description="Issue number")
131
+ title: str = Field(..., description="Issue title")
132
+ message: str | None = Field(None, description="Issue message")
133
+ state: str = Field(..., description="Issue state")
134
+ author: GitHubUser = Field(..., description="Issue author")
135
+ assignees: list[GitHubUser] = Field(default_factory=list, description="Issue assignees")
136
+ labels: list[str] = Field(default_factory=list, description="Issue labels")
137
+ created_at: datetime = Field(..., description="Creation timestamp")
138
+ updated_at: datetime = Field(..., description="Last update timestamp")
139
+ closed_at: datetime | None = Field(None, description="Close timestamp")
140
+ url: str = Field(..., description="Issue URL")
141
+
142
+
143
+ class GitHubWebhookPayload(BaseModel):
144
+ """GitHub webhook payload model."""
145
+
146
+ action: str = Field(..., description="Webhook action")
147
+ repository: GitHubRepository = Field(..., description="Repository data")
148
+ sender: GitHubUser = Field(..., description="Event sender")
149
+ pull_request: PullRequestData | None = Field(None, description="Pull request data")
150
+ issue: GitHubIssue | None = Field(None, description="Issue data")
151
+ commits: list[GitHubCommit] | None = Field(None, description="Commit data")
152
+ ref: str | None = Field(None, description="Git reference")
153
+ before: str | None = Field(None, description="Before SHA")
154
+ after: str | None = Field(None, description="After SHA")
155
+
156
+
157
+ # Additional GitHub-specific models (consolidated from github_dataclasses.py)
158
+
159
+
160
+ class GitHubRepoInfo(BaseModel):
161
+ """GitHub repository information."""
162
+
163
+ owner: str = Field(..., description="Repository owner")
164
+ name: str = Field(..., description="Repository name")
165
+ url: str = Field(..., description="Repository URL")
166
+ default_branch: str = Field("main", description="Default branch")
167
+ description: str | None = Field(None, description="Repository description")
168
+ language: str | None = Field(None, description="Primary language")
169
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
170
+
171
+
172
+ class CloneResult(BaseModel):
173
+ """Result of repository cloning operation."""
174
+
175
+ success: bool = Field(..., description="Whether cloning was successful")
176
+ repo_id: str = Field(..., description="Repository identifier")
177
+ local_path: str = Field(..., description="Local path where repo was cloned")
178
+ error: str | None = Field(None, description="Error message if cloning failed")
179
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
180
+
181
+
182
+ class SyncResult(BaseModel):
183
+ """Result of repository synchronization."""
184
+
185
+ success: bool = Field(..., description="Whether sync was successful")
186
+ commits_behind: int = Field(0, description="Number of commits behind")
187
+ has_updates: bool = Field(False, description="Whether there are updates available")
188
+ error: str | None = Field(None, description="Error message if sync failed")
189
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
190
+
191
+
192
+ class CodeSnippet(BaseModel):
193
+ """Code snippet model for GitHub operations."""
194
+
195
+ code: str = Field(..., description="Code content")
196
+ language: str = Field(..., description="Programming language")
197
+ file_path: str | None = Field(None, description="File path")
198
+ line_number: int | None = Field(None, description="Line number")
199
+ description: str | None = Field(None, description="Code description")
200
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
201
+
202
+
203
+ class RepositoryContext(BaseModel):
204
+ """Context information about a repository."""
205
+
206
+ existing_patterns: list[dict[str, Any]] = Field(
207
+ default_factory=list, description="Existing code patterns"
208
+ )
209
+ style_guide: dict[str, Any] = Field(default_factory=dict, description="Style guide rules")
210
+ api_surface: dict[str, Any] = Field(default_factory=dict, description="API surface analysis")
211
+ size_metrics: dict[str, Any] = Field(
212
+ default_factory=dict, description="Repository size metrics"
213
+ )
214
+
215
+
216
+ class PRFileChange(BaseModel):
217
+ """File change information for pull requests."""
218
+
219
+ file_path: str = Field(..., description="Path to changed file")
220
+ change_type: str = Field(..., description="Type of change (added, modified, deleted)")
221
+ content: str | None = Field(None, description="New file content")
222
+ old_content: str | None = Field(None, description="Previous file content")
223
+ additions: int = Field(0, description="Number of lines added")
224
+ deletions: int = Field(0, description="Number of lines deleted")
225
+ patch: str | None = Field(None, description="Git patch content")
226
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
227
+
228
+
229
+ class PullRequest(BaseModel):
230
+ """Pull request model for GitHub operations."""
231
+
232
+ id: str = Field(..., description="Pull request ID")
233
+ number: int = Field(..., description="Pull request number")
234
+ title: str = Field(..., description="Pull request title")
235
+ description: str = Field(..., description="Pull request description")
236
+ url: str = Field(..., description="Pull request URL")
237
+ status: str = Field(..., description="Pull request status")
238
+ created_at: datetime = Field(..., description="Creation timestamp")
239
+ updated_at: datetime | None = Field(None, description="Last update timestamp")
240
+ merged_at: datetime | None = Field(None, description="Merge timestamp")
241
+ author: str = Field(..., description="Pull request author")
242
+ base_branch: str = Field(..., description="Base branch")
243
+ head_branch: str = Field(..., description="Head branch")
244
+ labels: list[str] = Field(default_factory=list, description="Labels")
245
+ reviewers: list[str] = Field(default_factory=list, description="Reviewers")
246
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
247
+
248
+
249
+ class RepoWorkspace(BaseModel):
250
+ """Repository workspace model."""
251
+
252
+ repo_id: str = Field(..., description="Repository ID")
253
+ repo_url: str = Field(..., description="Repository URL")
254
+ local_path: str = Field(..., description="Local workspace path")
255
+ branch: str = Field("main", description="Current branch")
256
+ last_sync: datetime = Field(..., description="Last sync timestamp")
257
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
258
+
259
+
260
+ class RepoSetupResult(BaseModel):
261
+ """Result of repository setup operation."""
262
+
263
+ success: bool = Field(..., description="Whether setup was successful")
264
+ workspace: RepoWorkspace | None = Field(None, description="Created workspace")
265
+ error: str | None = Field(None, description="Error message if setup failed")
266
+ metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
267
+
268
+
269
+ class SnippetAnalysisRequest(BaseModel):
270
+ """Request for code snippet analysis."""
271
+
272
+ snippet: CodeSnippet = Field(..., description="Code snippet to analyze")
273
+ workspace_id: str = Field(..., description="Workspace ID for context")
274
+ context: dict[str, Any] = Field(default_factory=dict, description="Additional context")
275
+
276
+
277
+ class SnippetEvaluationRequest(BaseModel):
278
+ """Request for code snippet evaluation."""
279
+
280
+ workspace_id: str = Field(..., description="Workspace ID")
281
+ snippet: CodeSnippet = Field(..., description="Code snippet to evaluate")
282
+ evaluation_options: dict[str, Any] = Field(
283
+ default_factory=dict, description="Evaluation options"
284
+ )
285
+
286
+
287
+ class PRCreationRequest(BaseModel):
288
+ """Request for pull request creation."""
289
+
290
+ workspace_id: str = Field(..., description="Workspace ID")
291
+ changes: list[PRFileChange] = Field(..., description="File changes")
292
+ pr_data: PullRequestData = Field(..., description="Pull request data")
293
+ analysis_options: dict[str, Any] = Field(default_factory=dict, description="Analysis options")
@@ -0,0 +1,29 @@
1
+ """
2
+ Shared logging models for the application.
3
+
4
+ This module provides common logging-related models used across different
5
+ components of the application to avoid duplication.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from enum import StrEnum
11
+
12
+
13
+ class LogLevel(StrEnum):
14
+ """Standard logging levels with clear hierarchy."""
15
+
16
+ DEBUG = "DEBUG"
17
+ INFO = "INFO"
18
+ WARNING = "WARNING"
19
+ ERROR = "ERROR"
20
+ CRITICAL = "CRITICAL"
21
+
22
+ @classmethod
23
+ def from_lowercase(cls, value: str) -> LogLevel:
24
+ """Convert lowercase log level to enum value."""
25
+ return cls(value.upper())
26
+
27
+ def to_lowercase(self) -> str:
28
+ """Convert enum value to lowercase string."""
29
+ return self.value.lower()
@@ -0,0 +1,257 @@
1
+ """Base models for package management across system and language package managers.
2
+
3
+ This module provides clear separation between system-level package managers
4
+ (apt, brew, yum) and language-specific package managers (pip, npm, cargo).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from enum import Enum
10
+
11
+ from pydantic import BaseModel, ConfigDict, Field
12
+
13
+
14
+ class SystemPackageManager(Enum):
15
+ """System-level package managers for OS dependencies."""
16
+
17
+ # Linux distributions
18
+ APT = "apt-get" # Debian/Ubuntu
19
+ YUM = "yum" # RHEL/CentOS (older)
20
+ DNF = "dnf" # Fedora/RHEL/CentOS (newer)
21
+ PACMAN = "pacman" # Arch Linux
22
+ ZYPPER = "zypper" # openSUSE
23
+ APK = "apk" # Alpine Linux
24
+
25
+ # macOS
26
+ BREW = "brew" # Homebrew
27
+
28
+ # Windows
29
+ CHOCO = "choco" # Chocolatey
30
+
31
+ @property
32
+ def install_command(self) -> list[str]:
33
+ """Get the base install command for this package manager."""
34
+ commands: dict[SystemPackageManager, list[str]] = {
35
+ SystemPackageManager.APT: ["sudo", "apt-get", "install", "-y"],
36
+ SystemPackageManager.YUM: ["sudo", "yum", "install", "-y"],
37
+ SystemPackageManager.DNF: ["sudo", "dnf", "install", "-y"],
38
+ SystemPackageManager.PACMAN: ["sudo", "pacman", "-S", "--noconfirm"],
39
+ SystemPackageManager.ZYPPER: ["sudo", "zypper", "install", "-y"],
40
+ SystemPackageManager.APK: ["sudo", "apk", "add"],
41
+ SystemPackageManager.BREW: ["brew", "install"],
42
+ SystemPackageManager.CHOCO: ["choco", "install", "-y"],
43
+ }
44
+ return commands.get(self, [])
45
+
46
+ @property
47
+ def update_command(self) -> list[str]:
48
+ """Get the update command for this package manager."""
49
+ commands: dict[SystemPackageManager, list[str]] = {
50
+ SystemPackageManager.APT: ["sudo", "apt-get", "update"],
51
+ SystemPackageManager.YUM: ["sudo", "yum", "update", "-y"],
52
+ SystemPackageManager.DNF: ["sudo", "dnf", "update", "-y"],
53
+ SystemPackageManager.PACMAN: ["sudo", "pacman", "-Syu", "--noconfirm"],
54
+ SystemPackageManager.ZYPPER: ["sudo", "zypper", "refresh"],
55
+ SystemPackageManager.APK: ["sudo", "apk", "update"],
56
+ SystemPackageManager.BREW: ["brew", "update"],
57
+ SystemPackageManager.CHOCO: ["choco", "upgrade", "all", "-y"],
58
+ }
59
+ return commands.get(self, [])
60
+
61
+
62
+ class LanguagePackageManager(Enum):
63
+ """Language-specific package managers."""
64
+
65
+ # Python
66
+ PIP = "pip"
67
+ POETRY = "poetry"
68
+ PIPENV = "pipenv"
69
+ UV = "uv"
70
+ CONDA = "conda"
71
+
72
+ # JavaScript/TypeScript
73
+ NPM = "npm"
74
+ YARN = "yarn"
75
+ PNPM = "pnpm"
76
+ BUN = "bun"
77
+
78
+ # Rust
79
+ CARGO = "cargo"
80
+
81
+ # Java
82
+ MAVEN = "maven"
83
+ GRADLE = "gradle"
84
+
85
+ # Ruby
86
+ GEM = "gem"
87
+ BUNDLER = "bundler"
88
+
89
+ # Go
90
+ GO_MOD = "go mod"
91
+
92
+ # PHP
93
+ COMPOSER = "composer"
94
+
95
+ # .NET
96
+ NUGET = "nuget"
97
+ DOTNET = "dotnet"
98
+
99
+ @property
100
+ def manifest_files(self) -> list[str]:
101
+ """Get typical manifest files for this package manager."""
102
+ manifests: dict[LanguagePackageManager, list[str]] = {
103
+ LanguagePackageManager.PIP: ["requirements.txt", "requirements/*.txt"],
104
+ LanguagePackageManager.POETRY: ["pyproject.toml", "poetry.lock"],
105
+ LanguagePackageManager.PIPENV: ["Pipfile", "Pipfile.lock"],
106
+ LanguagePackageManager.UV: ["pyproject.toml", "uv.lock"],
107
+ LanguagePackageManager.CONDA: ["environment.yml", "environment.yaml"],
108
+ LanguagePackageManager.NPM: ["package.json", "package-lock.json"],
109
+ LanguagePackageManager.YARN: ["package.json", "yarn.lock"],
110
+ LanguagePackageManager.PNPM: ["package.json", "pnpm-lock.yaml"],
111
+ LanguagePackageManager.BUN: ["package.json", "bun.lockb"],
112
+ LanguagePackageManager.CARGO: ["Cargo.toml", "Cargo.lock"],
113
+ LanguagePackageManager.MAVEN: ["pom.xml"],
114
+ LanguagePackageManager.GRADLE: ["build.gradle", "build.gradle.kts"],
115
+ LanguagePackageManager.GEM: ["Gemfile", "Gemfile.lock"],
116
+ LanguagePackageManager.BUNDLER: ["Gemfile", "Gemfile.lock"],
117
+ LanguagePackageManager.GO_MOD: ["go.mod", "go.sum"],
118
+ LanguagePackageManager.COMPOSER: ["composer.json", "composer.lock"],
119
+ LanguagePackageManager.NUGET: ["packages.config", "*.csproj"],
120
+ LanguagePackageManager.DOTNET: ["*.csproj", "*.fsproj", "*.vbproj"],
121
+ }
122
+ return manifests.get(self, [])
123
+
124
+ @property
125
+ def install_command(self) -> list[str]:
126
+ """Get the install command for this package manager."""
127
+ commands: dict[LanguagePackageManager, list[str]] = {
128
+ LanguagePackageManager.PIP: ["pip", "install"],
129
+ LanguagePackageManager.POETRY: ["poetry", "install"],
130
+ LanguagePackageManager.PIPENV: ["pipenv", "install"],
131
+ LanguagePackageManager.UV: ["uv", "pip", "install"],
132
+ LanguagePackageManager.CONDA: ["conda", "install"],
133
+ LanguagePackageManager.NPM: ["npm", "install"],
134
+ LanguagePackageManager.YARN: ["yarn", "install"],
135
+ LanguagePackageManager.PNPM: ["pnpm", "install"],
136
+ LanguagePackageManager.BUN: ["bun", "install"],
137
+ LanguagePackageManager.CARGO: ["cargo", "build"],
138
+ LanguagePackageManager.MAVEN: ["mvn", "install"],
139
+ LanguagePackageManager.GRADLE: ["gradle", "build"],
140
+ LanguagePackageManager.GEM: ["gem", "install"],
141
+ LanguagePackageManager.BUNDLER: ["bundle", "install"],
142
+ LanguagePackageManager.GO_MOD: ["go", "mod", "download"],
143
+ LanguagePackageManager.COMPOSER: ["composer", "install"],
144
+ LanguagePackageManager.NUGET: ["nuget", "restore"],
145
+ LanguagePackageManager.DOTNET: ["dotnet", "restore"],
146
+ }
147
+ return commands.get(self, [])
148
+
149
+
150
+ class ExternalPackage(BaseModel):
151
+ """Represents an external package dependency with optional enrichment data."""
152
+
153
+ name: str = Field(..., description="Package name")
154
+ version: str | None = Field(None, description="Version specification (e.g., >=1.0.0)")
155
+ manager: LanguagePackageManager | None = Field(None, description="Package manager used")
156
+ is_dev: bool = Field(False, description="Whether this is a development dependency")
157
+ source: str | None = Field(None, description="Source file where this dependency was found")
158
+ extras: list[str] = Field(
159
+ default_factory=list,
160
+ description="Extra features requested (e.g., package[extra1,extra2])",
161
+ )
162
+
163
+ # Extended fields for enrichment and security analysis
164
+ license: str | None = Field(None, description="Package license")
165
+ ecosystem: str | None = Field(None, description="Package ecosystem (pypi, npm, etc.)")
166
+ description: str | None = Field(None, description="Package description")
167
+ homepage: str | None = Field(None, description="Package homepage URL")
168
+ repository: str | None = Field(None, description="Package repository URL")
169
+
170
+ # Legacy fields for compatibility
171
+ current_version: str | None = Field(None, description="Currently installed version")
172
+ latest_version: str | None = Field(None, description="Latest available version")
173
+ severity: str | None = Field(None, description="Security severity if vulnerable")
174
+
175
+ model_config = ConfigDict(extra="forbid")
176
+
177
+ @property
178
+ def full_spec(self) -> str:
179
+ """Get the full package specification including version."""
180
+ spec = self.name
181
+ if self.extras:
182
+ spec = f"{spec}[{','.join(self.extras)}]"
183
+ if self.version:
184
+ spec = f"{spec}{self.version}"
185
+ return spec
186
+
187
+
188
+ class SystemDependency(BaseModel):
189
+ """Represents a system-level dependency required by a package."""
190
+
191
+ name: str = Field(..., description="Dependency name")
192
+ packages: dict[SystemPackageManager, list[str]] = Field(
193
+ ..., description="Mapping of system package managers to their package names"
194
+ )
195
+ description: str = Field(..., description="Human-readable description")
196
+ required_by: list[str] = Field(
197
+ default_factory=list,
198
+ description="List of language packages that require this system dependency",
199
+ )
200
+ test_command: str | None = Field(None, description="Command to test if dependency is installed")
201
+ version_command: str | None = Field(None, description="Command to get installed version")
202
+ min_version: str | None = Field(None, description="Minimum required version")
203
+
204
+ model_config = ConfigDict(extra="forbid")
205
+
206
+
207
+ class PackageManagerCapability(BaseModel):
208
+ """Describes capabilities of a package manager."""
209
+
210
+ manager: SystemPackageManager | LanguagePackageManager = Field(
211
+ ..., description="The package manager"
212
+ )
213
+ supports_lockfile: bool = Field(False, description="Whether this manager supports lock files")
214
+ supports_virtual_env: bool = Field(
215
+ False, description="Whether this manager supports virtual environments"
216
+ )
217
+ supports_dev_dependencies: bool = Field(
218
+ False, description="Whether this manager distinguishes dev dependencies"
219
+ )
220
+ supports_workspaces: bool = Field(
221
+ False, description="Whether this manager supports workspaces/monorepos"
222
+ )
223
+
224
+ model_config = ConfigDict(extra="forbid")
225
+
226
+
227
+ # Pre-defined capabilities for common package managers
228
+ PACKAGE_MANAGER_CAPABILITIES = {
229
+ LanguagePackageManager.PIP: PackageManagerCapability(
230
+ manager=LanguagePackageManager.PIP,
231
+ supports_lockfile=False,
232
+ supports_virtual_env=True,
233
+ supports_dev_dependencies=False,
234
+ supports_workspaces=False,
235
+ ),
236
+ LanguagePackageManager.POETRY: PackageManagerCapability(
237
+ manager=LanguagePackageManager.POETRY,
238
+ supports_lockfile=True,
239
+ supports_virtual_env=True,
240
+ supports_dev_dependencies=True,
241
+ supports_workspaces=False,
242
+ ),
243
+ LanguagePackageManager.NPM: PackageManagerCapability(
244
+ manager=LanguagePackageManager.NPM,
245
+ supports_lockfile=True,
246
+ supports_virtual_env=False,
247
+ supports_dev_dependencies=True,
248
+ supports_workspaces=True,
249
+ ),
250
+ LanguagePackageManager.CARGO: PackageManagerCapability(
251
+ manager=LanguagePackageManager.CARGO,
252
+ supports_lockfile=True,
253
+ supports_virtual_env=False,
254
+ supports_dev_dependencies=True,
255
+ supports_workspaces=True,
256
+ ),
257
+ }