codeframe-ai 0.9.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 (197) hide show
  1. codeframe/__init__.py +11 -0
  2. codeframe/__main__.py +20 -0
  3. codeframe/adapters/__init__.py +5 -0
  4. codeframe/adapters/e2b/__init__.py +13 -0
  5. codeframe/adapters/e2b/adapter.py +342 -0
  6. codeframe/adapters/e2b/budget.py +71 -0
  7. codeframe/adapters/e2b/credential_scanner.py +134 -0
  8. codeframe/adapters/llm/__init__.py +92 -0
  9. codeframe/adapters/llm/anthropic.py +414 -0
  10. codeframe/adapters/llm/base.py +444 -0
  11. codeframe/adapters/llm/mock.py +281 -0
  12. codeframe/adapters/llm/openai.py +483 -0
  13. codeframe/agents/__init__.py +8 -0
  14. codeframe/agents/dependency_resolver.py +714 -0
  15. codeframe/auth/__init__.py +16 -0
  16. codeframe/auth/api_key_router.py +238 -0
  17. codeframe/auth/api_keys.py +156 -0
  18. codeframe/auth/dependencies.py +358 -0
  19. codeframe/auth/manager.py +178 -0
  20. codeframe/auth/models.py +30 -0
  21. codeframe/auth/router.py +93 -0
  22. codeframe/auth/schemas.py +15 -0
  23. codeframe/auth/scopes.py +53 -0
  24. codeframe/cli/__init__.py +12 -0
  25. codeframe/cli/__main__.py +20 -0
  26. codeframe/cli/api_client.py +275 -0
  27. codeframe/cli/app.py +5688 -0
  28. codeframe/cli/auth.py +122 -0
  29. codeframe/cli/auth_commands.py +958 -0
  30. codeframe/cli/commands/__init__.py +5 -0
  31. codeframe/cli/config_commands.py +79 -0
  32. codeframe/cli/dashboard_commands.py +67 -0
  33. codeframe/cli/engines_commands.py +205 -0
  34. codeframe/cli/env_commands.py +409 -0
  35. codeframe/cli/helpers.py +56 -0
  36. codeframe/cli/hooks_commands.py +208 -0
  37. codeframe/cli/import_commands.py +129 -0
  38. codeframe/cli/pr_commands.py +549 -0
  39. codeframe/cli/proof_commands.py +415 -0
  40. codeframe/cli/stats_commands.py +311 -0
  41. codeframe/cli/telemetry_runtime.py +153 -0
  42. codeframe/cli/validators.py +123 -0
  43. codeframe/config/rate_limits.py +165 -0
  44. codeframe/core/__init__.py +15 -0
  45. codeframe/core/adapters/__init__.py +43 -0
  46. codeframe/core/adapters/agent_adapter.py +114 -0
  47. codeframe/core/adapters/builtin.py +326 -0
  48. codeframe/core/adapters/claude_code.py +62 -0
  49. codeframe/core/adapters/codex.py +393 -0
  50. codeframe/core/adapters/git_utils.py +40 -0
  51. codeframe/core/adapters/kilocode.py +126 -0
  52. codeframe/core/adapters/opencode.py +48 -0
  53. codeframe/core/adapters/streaming_chat.py +483 -0
  54. codeframe/core/adapters/subprocess_adapter.py +213 -0
  55. codeframe/core/adapters/verification_wrapper.py +269 -0
  56. codeframe/core/agent.py +2183 -0
  57. codeframe/core/agents_config.py +569 -0
  58. codeframe/core/api_key_service.py +211 -0
  59. codeframe/core/artifacts.py +428 -0
  60. codeframe/core/blocker_detection.py +218 -0
  61. codeframe/core/blockers.py +433 -0
  62. codeframe/core/checkpoints.py +481 -0
  63. codeframe/core/conductor.py +2255 -0
  64. codeframe/core/config.py +827 -0
  65. codeframe/core/config_watcher.py +268 -0
  66. codeframe/core/context.py +542 -0
  67. codeframe/core/context_packager.py +234 -0
  68. codeframe/core/credentials.py +735 -0
  69. codeframe/core/dependency_analyzer.py +229 -0
  70. codeframe/core/dependency_graph.py +290 -0
  71. codeframe/core/diagnostic_agent.py +712 -0
  72. codeframe/core/diagnostics.py +616 -0
  73. codeframe/core/editor.py +556 -0
  74. codeframe/core/engine_registry.py +256 -0
  75. codeframe/core/engine_stats.py +231 -0
  76. codeframe/core/environment.py +697 -0
  77. codeframe/core/events.py +375 -0
  78. codeframe/core/executor.py +1005 -0
  79. codeframe/core/fix_tracker.py +480 -0
  80. codeframe/core/gates.py +1322 -0
  81. codeframe/core/git.py +477 -0
  82. codeframe/core/github_connect_service.py +178 -0
  83. codeframe/core/github_integration_config.py +118 -0
  84. codeframe/core/github_issues_service.py +449 -0
  85. codeframe/core/hooks.py +184 -0
  86. codeframe/core/importers/__init__.py +1 -0
  87. codeframe/core/importers/ralph.py +540 -0
  88. codeframe/core/installer.py +650 -0
  89. codeframe/core/models.py +1026 -0
  90. codeframe/core/notifications_config.py +183 -0
  91. codeframe/core/planner.py +437 -0
  92. codeframe/core/prd.py +670 -0
  93. codeframe/core/prd_discovery.py +1118 -0
  94. codeframe/core/prd_stress_test.py +499 -0
  95. codeframe/core/progress.py +126 -0
  96. codeframe/core/proof/__init__.py +34 -0
  97. codeframe/core/proof/capture.py +79 -0
  98. codeframe/core/proof/evidence.py +56 -0
  99. codeframe/core/proof/ledger.py +574 -0
  100. codeframe/core/proof/models.py +162 -0
  101. codeframe/core/proof/obligations.py +103 -0
  102. codeframe/core/proof/runner.py +233 -0
  103. codeframe/core/proof/scope.py +81 -0
  104. codeframe/core/proof/stubs.py +156 -0
  105. codeframe/core/quick_fixes.py +558 -0
  106. codeframe/core/react_agent.py +1650 -0
  107. codeframe/core/reconciliation.py +183 -0
  108. codeframe/core/replay.py +788 -0
  109. codeframe/core/review.py +285 -0
  110. codeframe/core/runtime.py +1134 -0
  111. codeframe/core/sandbox/__init__.py +27 -0
  112. codeframe/core/sandbox/context.py +98 -0
  113. codeframe/core/sandbox/worktree.py +20 -0
  114. codeframe/core/schedule.py +396 -0
  115. codeframe/core/stall_detector.py +71 -0
  116. codeframe/core/stall_monitor.py +134 -0
  117. codeframe/core/state_machine.py +121 -0
  118. codeframe/core/streaming.py +502 -0
  119. codeframe/core/task_tree.py +400 -0
  120. codeframe/core/tasks.py +1022 -0
  121. codeframe/core/telemetry.py +232 -0
  122. codeframe/core/templates.py +221 -0
  123. codeframe/core/tools.py +942 -0
  124. codeframe/core/workspace.py +887 -0
  125. codeframe/core/worktrees.py +276 -0
  126. codeframe/git/__init__.py +5 -0
  127. codeframe/git/github_integration.py +505 -0
  128. codeframe/lib/__init__.py +0 -0
  129. codeframe/lib/audit_logger.py +248 -0
  130. codeframe/lib/metrics_tracker.py +800 -0
  131. codeframe/lib/quality/__init__.py +7 -0
  132. codeframe/lib/quality/complexity_analyzer.py +316 -0
  133. codeframe/lib/quality/owasp_patterns.py +284 -0
  134. codeframe/lib/quality/security_scanner.py +250 -0
  135. codeframe/lib/rate_limiter.py +312 -0
  136. codeframe/notifications/__init__.py +0 -0
  137. codeframe/notifications/webhook.py +380 -0
  138. codeframe/planning/__init__.py +30 -0
  139. codeframe/planning/issue_generator.py +219 -0
  140. codeframe/planning/prd_template_functions.py +137 -0
  141. codeframe/planning/prd_templates.py +975 -0
  142. codeframe/planning/task_scheduler.py +511 -0
  143. codeframe/planning/task_templates.py +533 -0
  144. codeframe/platform_store/__init__.py +5 -0
  145. codeframe/platform_store/database.py +277 -0
  146. codeframe/platform_store/repositories/__init__.py +24 -0
  147. codeframe/platform_store/repositories/api_key_repository.py +245 -0
  148. codeframe/platform_store/repositories/audit_repository.py +67 -0
  149. codeframe/platform_store/repositories/base.py +295 -0
  150. codeframe/platform_store/repositories/interactive_sessions.py +165 -0
  151. codeframe/platform_store/repositories/token_repository.py +598 -0
  152. codeframe/platform_store/repositories/workspace_registry_repository.py +175 -0
  153. codeframe/platform_store/schema_manager.py +321 -0
  154. codeframe/templates/AGENTS.md.default +94 -0
  155. codeframe/tui/__init__.py +5 -0
  156. codeframe/tui/app.py +256 -0
  157. codeframe/tui/data_service.py +103 -0
  158. codeframe/ui/__init__.py +0 -0
  159. codeframe/ui/dependencies.py +103 -0
  160. codeframe/ui/models.py +999 -0
  161. codeframe/ui/response_models.py +201 -0
  162. codeframe/ui/routers/__init__.py +5 -0
  163. codeframe/ui/routers/_helpers.py +29 -0
  164. codeframe/ui/routers/batches_v2.py +315 -0
  165. codeframe/ui/routers/blockers_v2.py +320 -0
  166. codeframe/ui/routers/checkpoints_v2.py +310 -0
  167. codeframe/ui/routers/costs_v2.py +322 -0
  168. codeframe/ui/routers/diagnose_v2.py +225 -0
  169. codeframe/ui/routers/discovery_v2.py +417 -0
  170. codeframe/ui/routers/environment_v2.py +284 -0
  171. codeframe/ui/routers/events_v2.py +75 -0
  172. codeframe/ui/routers/gates_v2.py +166 -0
  173. codeframe/ui/routers/git_v2.py +284 -0
  174. codeframe/ui/routers/github_integrations_v2.py +532 -0
  175. codeframe/ui/routers/interactive_sessions_v2.py +238 -0
  176. codeframe/ui/routers/pr_v2.py +709 -0
  177. codeframe/ui/routers/prd_v2.py +695 -0
  178. codeframe/ui/routers/proof_v2.py +755 -0
  179. codeframe/ui/routers/review_v2.py +360 -0
  180. codeframe/ui/routers/schedule_v2.py +214 -0
  181. codeframe/ui/routers/session_chat_ws.py +354 -0
  182. codeframe/ui/routers/settings_v2.py +562 -0
  183. codeframe/ui/routers/streaming_v2.py +155 -0
  184. codeframe/ui/routers/tasks_v2.py +1098 -0
  185. codeframe/ui/routers/templates_v2.py +232 -0
  186. codeframe/ui/routers/terminal_ws.py +267 -0
  187. codeframe/ui/routers/workspace_v2.py +527 -0
  188. codeframe/ui/server.py +568 -0
  189. codeframe/ui/shared.py +241 -0
  190. codeframe/workspace/__init__.py +5 -0
  191. codeframe/workspace/manager.py +249 -0
  192. codeframe_ai-0.9.0.dist-info/METADATA +517 -0
  193. codeframe_ai-0.9.0.dist-info/RECORD +197 -0
  194. codeframe_ai-0.9.0.dist-info/WHEEL +5 -0
  195. codeframe_ai-0.9.0.dist-info/entry_points.txt +3 -0
  196. codeframe_ai-0.9.0.dist-info/licenses/LICENSE +661 -0
  197. codeframe_ai-0.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,53 @@
1
+ """Scope hierarchy and permission checking for API key authentication.
2
+
3
+ Defines the permission model:
4
+ - read: Read-only access to resources
5
+ - write: Read and write access (implies read)
6
+ - admin: Full access including admin operations (implies all)
7
+ """
8
+
9
+ from typing import Dict, List
10
+
11
+ from codeframe.auth.api_keys import SCOPE_READ, SCOPE_WRITE, SCOPE_ADMIN
12
+
13
+ # Scope hierarchy: each scope grants the permissions listed
14
+ SCOPE_HIERARCHY: Dict[str, List[str]] = {
15
+ SCOPE_ADMIN: [SCOPE_READ, SCOPE_WRITE, SCOPE_ADMIN],
16
+ SCOPE_WRITE: [SCOPE_READ, SCOPE_WRITE],
17
+ SCOPE_READ: [SCOPE_READ],
18
+ }
19
+
20
+
21
+ def has_scope(principal: dict, required_scope: str) -> bool:
22
+ """Check if a principal has the required scope.
23
+
24
+ Uses scope hierarchy: admin grants all, write grants read.
25
+
26
+ Args:
27
+ principal: Authentication dict with 'scopes' list
28
+ required_scope: The scope to check for
29
+
30
+ Returns:
31
+ True if principal has the required scope (directly or via hierarchy)
32
+ """
33
+ user_scopes = principal.get("scopes", [])
34
+
35
+ # Check each user scope and its implied permissions
36
+ for scope in user_scopes:
37
+ granted_permissions = SCOPE_HIERARCHY.get(scope, [scope])
38
+ if required_scope in granted_permissions:
39
+ return True
40
+
41
+ return False
42
+
43
+
44
+ def get_scope_permissions(scope: str) -> List[str]:
45
+ """Get all permissions granted by a scope.
46
+
47
+ Args:
48
+ scope: The scope to check
49
+
50
+ Returns:
51
+ List of all permissions granted by this scope
52
+ """
53
+ return SCOPE_HIERARCHY.get(scope, [scope])
@@ -0,0 +1,12 @@
1
+ """Command-line interface for CodeFRAME.
2
+
3
+ The Typer entry point lives in :mod:`codeframe.cli.app` and is exposed as the
4
+ ``codeframe`` / ``cf`` console scripts (see ``pyproject.toml``):
5
+
6
+ codeframe = "codeframe.cli.app:app"
7
+ cf = "codeframe.cli.app:app"
8
+
9
+ This package ``__init__`` is intentionally empty so that importing
10
+ ``codeframe.cli.app`` does not drag in unrelated command modules as a side
11
+ effect.
12
+ """
@@ -0,0 +1,20 @@
1
+ """Entry point for `python -m codeframe.cli` invocation.
2
+
3
+ This module enables running the CLI via:
4
+ python -m codeframe.cli [command] [options]
5
+
6
+ The help text will correctly show 'codeframe' as the command name.
7
+ """
8
+
9
+
10
+ def main():
11
+ """Run the CLI with proper program name."""
12
+ from codeframe.cli.app import main as app_main
13
+
14
+ # The telemetry-aware wrapper invokes the app with prog_name="codeframe",
15
+ # so the usage line shows the right command name here too.
16
+ app_main()
17
+
18
+
19
+ if __name__ == "__main__":
20
+ main()
@@ -0,0 +1,275 @@
1
+ """CLI API client module - HTTP client with authentication.
2
+
3
+ This module provides:
4
+ - APIClient class for making authenticated HTTP requests
5
+ - Automatic token injection in Authorization header
6
+ - Retry logic for transient failures
7
+ - User-friendly error messages
8
+
9
+ Usage:
10
+ from codeframe.cli.api_client import APIClient
11
+
12
+ client = APIClient() # Auto-loads token from storage
13
+ projects = client.get("/api/projects")
14
+ """
15
+
16
+ import json
17
+ import logging
18
+ import os
19
+ import time
20
+ from typing import Any
21
+
22
+ import requests
23
+
24
+ from codeframe.cli.auth import get_token
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class APIError(Exception):
30
+ """Base exception for API errors."""
31
+
32
+ def __init__(self, message: str, status_code: int | None = None, detail: str | None = None):
33
+ self.status_code = status_code
34
+ self.detail = detail
35
+ super().__init__(message)
36
+
37
+
38
+ class AuthenticationError(APIError):
39
+ """Exception for authentication failures (401, 403)."""
40
+
41
+ pass
42
+
43
+
44
+ def get_api_base_url() -> str:
45
+ """Get the API base URL.
46
+
47
+ Returns:
48
+ API base URL from CODEFRAME_API_URL env var, or default localhost:8080
49
+ """
50
+ url = os.environ.get("CODEFRAME_API_URL", "http://localhost:8080")
51
+ return url.rstrip("/")
52
+
53
+
54
+ class APIClient:
55
+ """HTTP client for CodeFRAME API with authentication.
56
+
57
+ Args:
58
+ base_url: API base URL. Defaults to CODEFRAME_API_URL env var or localhost:8080
59
+ token: JWT token. If not provided, loads from storage
60
+ max_retries: Maximum number of retries for transient failures (default: 3)
61
+ timeout: Request timeout in seconds (default: 30)
62
+ """
63
+
64
+ def __init__(
65
+ self,
66
+ base_url: str | None = None,
67
+ token: str | None = None,
68
+ max_retries: int = 3,
69
+ timeout: int = 30,
70
+ ):
71
+ self.base_url = base_url or get_api_base_url()
72
+ self.token = token if token is not None else get_token()
73
+ self.max_retries = max_retries
74
+ self.timeout = timeout
75
+
76
+ def _get_headers(self) -> dict[str, str]:
77
+ """Get HTTP headers including auth token.
78
+
79
+ Returns:
80
+ Headers dict with Content-Type and optional Authorization
81
+ """
82
+ headers = {"Content-Type": "application/json"}
83
+
84
+ if self.token:
85
+ headers["Authorization"] = f"Bearer {self.token}"
86
+
87
+ return headers
88
+
89
+ def _make_url(self, endpoint: str) -> str:
90
+ """Build full URL from endpoint.
91
+
92
+ Args:
93
+ endpoint: API endpoint (e.g., "/api/projects")
94
+
95
+ Returns:
96
+ Full URL
97
+ """
98
+ if endpoint.startswith("http"):
99
+ return endpoint
100
+ return f"{self.base_url}{endpoint}"
101
+
102
+ def _handle_response(self, response: requests.Response) -> Any:
103
+ """Handle HTTP response, raising appropriate exceptions.
104
+
105
+ Args:
106
+ response: requests Response object
107
+
108
+ Returns:
109
+ Parsed JSON response, or None for 204
110
+
111
+ Raises:
112
+ AuthenticationError: For 401/403 responses
113
+ APIError: For other error responses
114
+ """
115
+ status = response.status_code
116
+
117
+ # Success - return JSON or None
118
+ if 200 <= status < 300:
119
+ if status == 204 or not response.text:
120
+ return None
121
+ try:
122
+ return response.json()
123
+ except json.JSONDecodeError:
124
+ return response.text
125
+
126
+ # Authentication errors
127
+ if status == 401:
128
+ raise AuthenticationError(
129
+ "Authentication failed. Please log in with: codeframe auth login",
130
+ status_code=status,
131
+ )
132
+
133
+ if status == 403:
134
+ raise AuthenticationError(
135
+ "Access denied. You don't have permission for this resource.",
136
+ status_code=status,
137
+ )
138
+
139
+ # Parse error detail from response
140
+ detail = None
141
+ try:
142
+ error_data = response.json()
143
+ detail = error_data.get("detail", str(error_data))
144
+ except (json.JSONDecodeError, TypeError):
145
+ detail = response.text or f"HTTP {status}"
146
+
147
+ # Client errors (4xx)
148
+ if 400 <= status < 500:
149
+ raise APIError(
150
+ f"Request failed: {detail}",
151
+ status_code=status,
152
+ detail=detail,
153
+ )
154
+
155
+ # Server errors (5xx)
156
+ raise APIError(
157
+ f"Server error ({status}): {detail}",
158
+ status_code=status,
159
+ detail=detail,
160
+ )
161
+
162
+ def _request_with_retry(
163
+ self,
164
+ method: str,
165
+ endpoint: str,
166
+ **kwargs,
167
+ ) -> Any:
168
+ """Make HTTP request with retry logic.
169
+
170
+ Args:
171
+ method: HTTP method (GET, POST, etc.)
172
+ endpoint: API endpoint
173
+ **kwargs: Additional arguments for requests
174
+
175
+ Returns:
176
+ Parsed response data
177
+
178
+ Raises:
179
+ APIError: After all retries exhausted
180
+ """
181
+ url = self._make_url(endpoint)
182
+ headers = self._get_headers()
183
+
184
+ for attempt in range(self.max_retries):
185
+ try:
186
+ response = requests.request(
187
+ method=method,
188
+ url=url,
189
+ headers=headers,
190
+ timeout=self.timeout,
191
+ **kwargs,
192
+ )
193
+ return self._handle_response(response)
194
+
195
+ except requests.ConnectionError as e:
196
+ logger.warning(f"Connection error (attempt {attempt + 1}/{self.max_retries}): {e}")
197
+
198
+ if attempt < self.max_retries - 1:
199
+ # Exponential backoff: 1s, 2s, 4s, ...
200
+ sleep_time = 2 ** attempt
201
+ time.sleep(sleep_time)
202
+ continue
203
+
204
+ except requests.Timeout as e:
205
+ logger.warning(f"Request timeout (attempt {attempt + 1}/{self.max_retries}): {e}")
206
+
207
+ if attempt < self.max_retries - 1:
208
+ time.sleep(2 ** attempt)
209
+ continue
210
+
211
+ # All retries exhausted
212
+ raise APIError(
213
+ f"Connection error: Unable to connect to {self.base_url}. "
214
+ "Please check the server is running and try again.",
215
+ status_code=None,
216
+ )
217
+
218
+ def get(self, endpoint: str, params: dict | None = None) -> Any:
219
+ """Make GET request.
220
+
221
+ Args:
222
+ endpoint: API endpoint (e.g., "/api/projects")
223
+ params: Query parameters
224
+
225
+ Returns:
226
+ Parsed JSON response
227
+ """
228
+ return self._request_with_retry("GET", endpoint, params=params)
229
+
230
+ def post(self, endpoint: str, data: dict | None = None) -> Any:
231
+ """Make POST request.
232
+
233
+ Args:
234
+ endpoint: API endpoint
235
+ data: Request body (will be sent as JSON)
236
+
237
+ Returns:
238
+ Parsed JSON response
239
+ """
240
+ return self._request_with_retry("POST", endpoint, json=data)
241
+
242
+ def put(self, endpoint: str, data: dict | None = None) -> Any:
243
+ """Make PUT request.
244
+
245
+ Args:
246
+ endpoint: API endpoint
247
+ data: Request body (will be sent as JSON)
248
+
249
+ Returns:
250
+ Parsed JSON response
251
+ """
252
+ return self._request_with_retry("PUT", endpoint, json=data)
253
+
254
+ def patch(self, endpoint: str, data: dict | None = None) -> Any:
255
+ """Make PATCH request.
256
+
257
+ Args:
258
+ endpoint: API endpoint
259
+ data: Request body (will be sent as JSON)
260
+
261
+ Returns:
262
+ Parsed JSON response
263
+ """
264
+ return self._request_with_retry("PATCH", endpoint, json=data)
265
+
266
+ def delete(self, endpoint: str) -> Any:
267
+ """Make DELETE request.
268
+
269
+ Args:
270
+ endpoint: API endpoint
271
+
272
+ Returns:
273
+ Parsed JSON response, or None for 204
274
+ """
275
+ return self._request_with_retry("DELETE", endpoint)