devtorch-core 3.0.1__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 (193) hide show
  1. devtorch_core/__init__.py +158 -0
  2. devtorch_core/aggphi_textual.py +275 -0
  3. devtorch_core/alerts/__init__.py +23 -0
  4. devtorch_core/alerts/base.py +46 -0
  5. devtorch_core/alerts/config.py +60 -0
  6. devtorch_core/alerts/dispatcher.py +110 -0
  7. devtorch_core/alerts/jira.py +96 -0
  8. devtorch_core/alerts/linear.py +72 -0
  9. devtorch_core/alerts/pagerduty.py +66 -0
  10. devtorch_core/alerts/slack.py +81 -0
  11. devtorch_core/alerts/teams.py +70 -0
  12. devtorch_core/audit/__init__.py +43 -0
  13. devtorch_core/audit/exporter.py +297 -0
  14. devtorch_core/audit/privacy.py +101 -0
  15. devtorch_core/audit/scrubber.py +149 -0
  16. devtorch_core/audit/service.py +67 -0
  17. devtorch_core/audit/signing.py +127 -0
  18. devtorch_core/broadcast/__init__.py +4 -0
  19. devtorch_core/broadcast/broadcaster.py +100 -0
  20. devtorch_core/broadcast/watcher.py +71 -0
  21. devtorch_core/capability.py +639 -0
  22. devtorch_core/cloud/__init__.py +1 -0
  23. devtorch_core/cloud/client_config.py +472 -0
  24. devtorch_core/cloud/client_configs/.claude-opencode-fallback.json +8 -0
  25. devtorch_core/cloud/client_configs/.claude-stdio.json +13 -0
  26. devtorch_core/cloud/client_configs/.cursor-mcp.json +13 -0
  27. devtorch_core/cloud/client_configs/.opencode-bridge.json +13 -0
  28. devtorch_core/cloud/client_configs/.opencode.json +15 -0
  29. devtorch_core/cloud/client_configs/.vscode-mcp.json +13 -0
  30. devtorch_core/cloud/devtorch-mcp-bridge.js +357 -0
  31. devtorch_core/cloud/mcp_client.py +229 -0
  32. devtorch_core/cloud/setup.py +144 -0
  33. devtorch_core/cloud/sync.py +143 -0
  34. devtorch_core/cloud/sync_bundle.py +603 -0
  35. devtorch_core/cloud/sync_conflicts.py +159 -0
  36. devtorch_core/cloud/sync_state.py +159 -0
  37. devtorch_core/cloud/team_sync.py +283 -0
  38. devtorch_core/codex/__init__.py +9 -0
  39. devtorch_core/codex/__main__.py +97 -0
  40. devtorch_core/codex/capture.py +208 -0
  41. devtorch_core/codex/proxy.py +412 -0
  42. devtorch_core/concept_catalog.py +209 -0
  43. devtorch_core/consolidation/__init__.py +3 -0
  44. devtorch_core/consolidation/synthesizer.py +87 -0
  45. devtorch_core/consolidation/workflow.py +175 -0
  46. devtorch_core/daemon/__init__.py +27 -0
  47. devtorch_core/daemon/supervisor.py +293 -0
  48. devtorch_core/daemon/watcher.py +244 -0
  49. devtorch_core/dashboard_api.py +2012 -0
  50. devtorch_core/deltaf.py +97 -0
  51. devtorch_core/disclosure.py +50 -0
  52. devtorch_core/divergence/__init__.py +3 -0
  53. devtorch_core/divergence/detector.py +166 -0
  54. devtorch_core/gateway/__init__.py +32 -0
  55. devtorch_core/gateway/key_manager.py +124 -0
  56. devtorch_core/gateway/metrics_webhook.py +252 -0
  57. devtorch_core/gateway/policy.py +262 -0
  58. devtorch_core/gateway/server.py +727 -0
  59. devtorch_core/gateway/sso.py +233 -0
  60. devtorch_core/gcc.py +1246 -0
  61. devtorch_core/github/__init__.py +35 -0
  62. devtorch_core/github/app.py +240 -0
  63. devtorch_core/github/comment_builder.py +113 -0
  64. devtorch_core/github/pat.py +76 -0
  65. devtorch_core/github/pr_parser.py +82 -0
  66. devtorch_core/github/pr_reporter.py +555 -0
  67. devtorch_core/gitlab/__init__.py +177 -0
  68. devtorch_core/hitl/__init__.py +4 -0
  69. devtorch_core/hitl/channels.py +129 -0
  70. devtorch_core/hitl/orchestrator.py +95 -0
  71. devtorch_core/hooks/__init__.py +17 -0
  72. devtorch_core/hooks/claude_code.py +228 -0
  73. devtorch_core/hooks/git_capture.py +341 -0
  74. devtorch_core/hooks/git_commit.py +182 -0
  75. devtorch_core/hooks/installer.py +733 -0
  76. devtorch_core/hooks/pre_commit.py +157 -0
  77. devtorch_core/hooks/runner.py +344 -0
  78. devtorch_core/identity/__init__.py +4 -0
  79. devtorch_core/identity/agent.py +86 -0
  80. devtorch_core/identity/providers.py +85 -0
  81. devtorch_core/invariants.py +182 -0
  82. devtorch_core/mcp/__init__.py +10 -0
  83. devtorch_core/mcp/auth.py +177 -0
  84. devtorch_core/mcp/server.py +1049 -0
  85. devtorch_core/metrics/__init__.py +35 -0
  86. devtorch_core/metrics/aggregate.py +215 -0
  87. devtorch_core/metrics/calibrate.py +198 -0
  88. devtorch_core/metrics/calibration.py +125 -0
  89. devtorch_core/metrics/credibility.py +288 -0
  90. devtorch_core/metrics/delivery_time.py +70 -0
  91. devtorch_core/metrics/dhs.py +126 -0
  92. devtorch_core/metrics/mcs.py +96 -0
  93. devtorch_core/metrics/roi.py +88 -0
  94. devtorch_core/metrics/session_writer.py +81 -0
  95. devtorch_core/metrics/shadow_ai.py +117 -0
  96. devtorch_core/metrics/sprint_writer.py +243 -0
  97. devtorch_core/observability/__init__.py +78 -0
  98. devtorch_core/observability/datadog.py +157 -0
  99. devtorch_core/observability/formatter.py +119 -0
  100. devtorch_core/observability/report.py +264 -0
  101. devtorch_core/observability/servicenow.py +147 -0
  102. devtorch_core/observability/splunk.py +218 -0
  103. devtorch_core/observability/webhook.py +227 -0
  104. devtorch_core/parser/__init__.py +30 -0
  105. devtorch_core/parser/blocks.py +216 -0
  106. devtorch_core/parser/inference.py +159 -0
  107. devtorch_core/parser/thinking.py +112 -0
  108. devtorch_core/projects.py +169 -0
  109. devtorch_core/prompt_artifact.py +76 -0
  110. devtorch_core/proxy/__init__.py +9 -0
  111. devtorch_core/proxy/routes/__init__.py +1 -0
  112. devtorch_core/proxy/routes/anthropic.py +264 -0
  113. devtorch_core/proxy/routes/azure_openai.py +336 -0
  114. devtorch_core/proxy/routes/gemini.py +331 -0
  115. devtorch_core/proxy/routes/groq.py +284 -0
  116. devtorch_core/proxy/routes/ollama.py +279 -0
  117. devtorch_core/proxy/routes/openai.py +287 -0
  118. devtorch_core/proxy/server.py +356 -0
  119. devtorch_core/query/__init__.py +15 -0
  120. devtorch_core/query/grep.py +181 -0
  121. devtorch_core/query/hybrid.py +86 -0
  122. devtorch_core/query/semantic.py +157 -0
  123. devtorch_core/rdp.py +105 -0
  124. devtorch_core/reasoning/__init__.py +4 -0
  125. devtorch_core/reasoning/entry.py +31 -0
  126. devtorch_core/reasoning/store.py +122 -0
  127. devtorch_core/reasoning_plus/__init__.py +70 -0
  128. devtorch_core/reasoning_plus/augmenter.py +326 -0
  129. devtorch_core/reasoning_plus/capture.py +51 -0
  130. devtorch_core/reasoning_plus/config.py +256 -0
  131. devtorch_core/reasoning_plus/context.py +262 -0
  132. devtorch_core/reasoning_plus/learning/__init__.py +72 -0
  133. devtorch_core/reasoning_plus/learning/analytics.py +141 -0
  134. devtorch_core/reasoning_plus/learning/api.py +313 -0
  135. devtorch_core/reasoning_plus/learning/chain.py +285 -0
  136. devtorch_core/reasoning_plus/learning/composer.py +74 -0
  137. devtorch_core/reasoning_plus/learning/cross_project.py +234 -0
  138. devtorch_core/reasoning_plus/learning/embeddings.py +209 -0
  139. devtorch_core/reasoning_plus/learning/extractor.py +207 -0
  140. devtorch_core/reasoning_plus/learning/models.py +116 -0
  141. devtorch_core/reasoning_plus/learning/provenance.py +126 -0
  142. devtorch_core/reasoning_plus/learning/recorder.py +81 -0
  143. devtorch_core/reasoning_plus/learning/relevance.py +122 -0
  144. devtorch_core/reasoning_plus/learning/state.py +86 -0
  145. devtorch_core/reasoning_plus/learning/store.py +160 -0
  146. devtorch_core/reasoning_plus/learning/theta_learning_bridge.py +94 -0
  147. devtorch_core/reasoning_plus/prompt.py +90 -0
  148. devtorch_core/rep.py +134 -0
  149. devtorch_core/rep_network/__init__.py +25 -0
  150. devtorch_core/rep_network/merge.py +70 -0
  151. devtorch_core/rep_network/node.py +137 -0
  152. devtorch_core/rep_network/server.py +140 -0
  153. devtorch_core/rep_network/sync.py +207 -0
  154. devtorch_core/sensitivity.py +182 -0
  155. devtorch_core/serve.py +258 -0
  156. devtorch_core/session/__init__.py +39 -0
  157. devtorch_core/session/disagreement.py +188 -0
  158. devtorch_core/session/models.py +114 -0
  159. devtorch_core/session/orchestrator.py +182 -0
  160. devtorch_core/session/planner.py +169 -0
  161. devtorch_core/session/simulator.py +132 -0
  162. devtorch_core/signing.py +290 -0
  163. devtorch_core/sis.py +197 -0
  164. devtorch_core/storage.py +308 -0
  165. devtorch_core/templates/__init__.py +6 -0
  166. devtorch_core/templates/engine.py +122 -0
  167. devtorch_core/templates/go.py +18 -0
  168. devtorch_core/templates/infra.py +19 -0
  169. devtorch_core/templates/library/__init__.py +18 -0
  170. devtorch_core/templates/library/api_design.md +27 -0
  171. devtorch_core/templates/library/bug_fix.md +27 -0
  172. devtorch_core/templates/library/decision_record.md +27 -0
  173. devtorch_core/templates/library/engine.py +228 -0
  174. devtorch_core/templates/library/security_review.md +30 -0
  175. devtorch_core/templates/python.py +19 -0
  176. devtorch_core/templates/react.py +18 -0
  177. devtorch_core/templates/typescript.py +18 -0
  178. devtorch_core/theta.py +221 -0
  179. devtorch_core/theta_synthesis.py +268 -0
  180. devtorch_core/topics.py +320 -0
  181. devtorch_core/variance.py +219 -0
  182. devtorch_core/wrapper/__init__.py +52 -0
  183. devtorch_core/wrapper/anthropic.py +487 -0
  184. devtorch_core/wrapper/base.py +562 -0
  185. devtorch_core/wrapper/bedrock.py +342 -0
  186. devtorch_core/wrapper/gemini.py +422 -0
  187. devtorch_core/wrapper/ollama.py +527 -0
  188. devtorch_core/wrapper/openai.py +461 -0
  189. devtorch_core-3.0.1.dist-info/METADATA +867 -0
  190. devtorch_core-3.0.1.dist-info/RECORD +193 -0
  191. devtorch_core-3.0.1.dist-info/WHEEL +5 -0
  192. devtorch_core-3.0.1.dist-info/entry_points.txt +2 -0
  193. devtorch_core-3.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,233 @@
1
+ """
2
+ devtorch_core.gateway.sso
3
+ ==========================
4
+ SSO / JWT token validation for the enterprise gateway.
5
+
6
+ Supports:
7
+ "none" — pass-through; returns an anonymous identity.
8
+ "okta" — validates a signed JWT from Okta.
9
+ "azure-ad" — validates a signed JWT from Azure Active Directory.
10
+ "google" — validates a signed JWT from Google Workspace.
11
+
12
+ PyJWT is an optional dependency. If it is not installed the module falls back
13
+ to a base64 payload decode (no signature verification) and logs a clear
14
+ security warning. This fallback is only intended for integration testing in
15
+ environments where PyJWT cannot be installed.
16
+
17
+ SECURITY NOTE: The "none" provider is safe for development only. In
18
+ production, always configure a real SSO provider with JWKS validation.
19
+
20
+ Design decisions:
21
+ - Returns None for any invalid/expired/malformed token so callers always get
22
+ a typed result and never see an exception.
23
+ - extract_identity() is pure (no network I/O) and is always safe to call
24
+ on the dict returned by validate_token().
25
+ """
26
+ from __future__ import annotations
27
+
28
+ import base64
29
+ import json
30
+ import logging
31
+ from typing import Optional
32
+ from urllib.parse import urlparse
33
+
34
+ logger = logging.getLogger("devtorch.gateway.sso")
35
+
36
+ # Supported providers (besides "none")
37
+ _JWT_PROVIDERS = {"okta", "azure-ad", "google"}
38
+
39
+
40
+ class SSOValidator:
41
+ """
42
+ Validate SSO/JWT tokens and extract developer identity.
43
+
44
+ Parameters
45
+ ----------
46
+ provider:
47
+ One of "none", "okta", "azure-ad", "google".
48
+ jwks_url:
49
+ URL to the JWKS endpoint for the provider.
50
+ Required when provider != "none" and signature validation is needed.
51
+ audience:
52
+ Expected JWT audience claim ("aud").
53
+ token_issuer:
54
+ Optional expected JWT issuer ("iss"). Enforced only when provided.
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ provider: str = "none",
60
+ jwks_url: str = "",
61
+ audience: str = "",
62
+ token_issuer: str = "",
63
+ ) -> None:
64
+ self._provider = provider.lower()
65
+ self._jwks_url = jwks_url
66
+ self._audience = audience
67
+ self._token_issuer = token_issuer
68
+
69
+ if self._provider not in {"none"} | _JWT_PROVIDERS:
70
+ logger.warning(
71
+ "SSOValidator: unknown provider '%s'; treating as 'none'",
72
+ provider,
73
+ )
74
+ self._provider = "none"
75
+
76
+ def __repr__(self) -> str:
77
+ return (
78
+ f"{self.__class__.__name__}("
79
+ f"provider={self._provider!r}, "
80
+ f"jwks_url={self.jwks_url!r}, "
81
+ f"audience={self._audience!r}, "
82
+ f"token_issuer={self._token_issuer!r})"
83
+ )
84
+
85
+ @property
86
+ def jwks_url(self) -> str:
87
+ """Return the JWKS URL with any query-string / path secret-ish tail masked."""
88
+ return self._mask_url(self._jwks_url)
89
+
90
+ @staticmethod
91
+ def _mask_url(url: str) -> str:
92
+ if not url:
93
+ return ""
94
+ parsed = urlparse(url)
95
+ if parsed.username or parsed.password:
96
+ # Mask credentials in the netloc
97
+ host = parsed.hostname or ""
98
+ port = parsed.port
99
+ port_part = f":{port}" if port else ""
100
+ netloc = f"{parsed.username or ''}:***@{host}{port_part}" if parsed.password else f"***@{host}{port_part}"
101
+ return parsed._replace(netloc=netloc).geturl()
102
+ # Mask last path/query component if it looks like a secret
103
+ if "?" in url:
104
+ base, _ = url.split("?", 1)
105
+ return f"{base}?***"
106
+ return url
107
+
108
+ # ------------------------------------------------------------------
109
+ # Public API
110
+ # ------------------------------------------------------------------
111
+
112
+ def validate_token(self, token: str) -> Optional[dict]:
113
+ """
114
+ Validate *token* and return the decoded JWT claims dict.
115
+
116
+ Returns:
117
+ dict — decoded claims on success
118
+ None — invalid / expired / malformed token
119
+ """
120
+ if self._provider == "none":
121
+ # Development / no-auth mode — always succeeds.
122
+ return {
123
+ "sub": "anonymous",
124
+ "email": "anonymous@local",
125
+ "name": "Anonymous",
126
+ "provider": "none",
127
+ }
128
+
129
+ if not token:
130
+ return None
131
+
132
+ # Real SSO providers — attempt JWT validation.
133
+ try:
134
+ return self._validate_jwt(token)
135
+ except Exception as exc:
136
+ logger.debug("SSOValidator: token validation error — %s", exc)
137
+ return None
138
+
139
+ def extract_identity(self, claims: dict) -> dict:
140
+ """
141
+ Extract a normalised identity dict from raw JWT claims.
142
+
143
+ Returns:
144
+ {
145
+ "user_id": str, # JWT 'sub' claim (or 'upn' for Azure AD)
146
+ "email": str, # JWT 'email' claim (or 'preferred_username' for Azure AD)
147
+ "display_name": str, # JWT 'name' claim
148
+ }
149
+ """
150
+ user_id = str(claims.get("sub", ""))
151
+ email = str(claims.get("email", ""))
152
+ display_name = str(claims.get("name", ""))
153
+
154
+ if self._provider == "azure-ad":
155
+ if not user_id:
156
+ user_id = str(claims.get("upn", ""))
157
+ if not email:
158
+ email = str(claims.get("preferred_username", ""))
159
+
160
+ if not user_id:
161
+ user_id = "unknown"
162
+ if not email:
163
+ email = ""
164
+ if not display_name:
165
+ display_name = email or user_id or "unknown"
166
+
167
+ return {
168
+ "user_id": user_id,
169
+ "email": email,
170
+ "display_name": display_name,
171
+ }
172
+
173
+ # ------------------------------------------------------------------
174
+ # Internal helpers
175
+ # ------------------------------------------------------------------
176
+
177
+ def _validate_jwt(self, token: str) -> Optional[dict]:
178
+ """
179
+ Attempt JWT validation using PyJWT.
180
+
181
+ Real SSO providers *always* require a configured ``jwks_url`` and a
182
+ valid signature. If PyJWT is missing or the JWKS URL is absent, the
183
+ token is rejected rather than accepted via an insecure fallback.
184
+ """
185
+ try:
186
+ import jwt # type: ignore[import]
187
+ except ImportError:
188
+ logger.warning(
189
+ "SSOValidator: PyJWT not installed; cannot validate JWT for "
190
+ "provider '%s'. Install PyJWT: pip install PyJWT",
191
+ self._provider,
192
+ )
193
+ return None
194
+
195
+ return self._validate_with_pyjwt(jwt, token)
196
+
197
+ def _validate_with_pyjwt(self, jwt_module: object, token: str) -> Optional[dict]:
198
+ """
199
+ Decode and verify a JWT using PyJWT.
200
+
201
+ A ``jwks_url`` is mandatory for real providers. If it is missing, the
202
+ token is rejected instead of being decoded without signature
203
+ verification.
204
+ """
205
+ try:
206
+ import jwt # type: ignore[import]
207
+
208
+ if not self._jwks_url:
209
+ logger.warning(
210
+ "SSOValidator: no jwks_url configured for provider '%s'; "
211
+ "rejecting JWT.",
212
+ self._provider,
213
+ )
214
+ return None
215
+
216
+ jwks_client = jwt.PyJWKClient(self._jwks_url)
217
+ signing_key = jwks_client.get_signing_key_from_jwt(token)
218
+ decode_kwargs: dict = {
219
+ "algorithms": ["RS256"],
220
+ "options": {"verify_exp": True},
221
+ }
222
+ if self._audience:
223
+ decode_kwargs["audience"] = self._audience
224
+ if self._token_issuer:
225
+ decode_kwargs["issuer"] = self._token_issuer
226
+ return jwt.decode(
227
+ token,
228
+ signing_key.key,
229
+ **decode_kwargs,
230
+ ) # type: ignore[return-value]
231
+ except Exception as exc:
232
+ logger.debug("PyJWT decode failed: %s", exc)
233
+ return None