shotgun-sh 0.2.8.dev2__py3-none-any.whl → 0.3.3.dev1__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.
- shotgun/agents/agent_manager.py +382 -60
- shotgun/agents/common.py +15 -9
- shotgun/agents/config/README.md +89 -0
- shotgun/agents/config/__init__.py +10 -1
- shotgun/agents/config/constants.py +0 -6
- shotgun/agents/config/manager.py +383 -82
- shotgun/agents/config/models.py +122 -18
- shotgun/agents/config/provider.py +81 -15
- shotgun/agents/config/streaming_test.py +119 -0
- shotgun/agents/context_analyzer/__init__.py +28 -0
- shotgun/agents/context_analyzer/analyzer.py +475 -0
- shotgun/agents/context_analyzer/constants.py +9 -0
- shotgun/agents/context_analyzer/formatter.py +115 -0
- shotgun/agents/context_analyzer/models.py +212 -0
- shotgun/agents/conversation/__init__.py +18 -0
- shotgun/agents/conversation/filters.py +164 -0
- shotgun/agents/conversation/history/chunking.py +278 -0
- shotgun/agents/{history → conversation/history}/compaction.py +36 -5
- shotgun/agents/{history → conversation/history}/constants.py +5 -0
- shotgun/agents/conversation/history/file_content_deduplication.py +216 -0
- shotgun/agents/{history → conversation/history}/history_processors.py +380 -8
- shotgun/agents/{history → conversation/history}/token_counting/anthropic.py +25 -1
- shotgun/agents/{history → conversation/history}/token_counting/base.py +14 -3
- shotgun/agents/{history → conversation/history}/token_counting/openai.py +11 -1
- shotgun/agents/{history → conversation/history}/token_counting/sentencepiece_counter.py +8 -0
- shotgun/agents/{history → conversation/history}/token_counting/tokenizer_cache.py +3 -1
- shotgun/agents/{history → conversation/history}/token_counting/utils.py +0 -3
- shotgun/agents/{conversation_manager.py → conversation/manager.py} +36 -20
- shotgun/agents/{conversation_history.py → conversation/models.py} +8 -92
- shotgun/agents/error/__init__.py +11 -0
- shotgun/agents/error/models.py +19 -0
- shotgun/agents/export.py +2 -2
- shotgun/agents/plan.py +2 -2
- shotgun/agents/research.py +3 -3
- shotgun/agents/runner.py +230 -0
- shotgun/agents/specify.py +2 -2
- shotgun/agents/tasks.py +2 -2
- shotgun/agents/tools/codebase/codebase_shell.py +6 -0
- shotgun/agents/tools/codebase/directory_lister.py +6 -0
- shotgun/agents/tools/codebase/file_read.py +11 -2
- shotgun/agents/tools/codebase/query_graph.py +6 -0
- shotgun/agents/tools/codebase/retrieve_code.py +6 -0
- shotgun/agents/tools/file_management.py +27 -7
- shotgun/agents/tools/registry.py +217 -0
- shotgun/agents/tools/web_search/__init__.py +8 -8
- shotgun/agents/tools/web_search/anthropic.py +8 -2
- shotgun/agents/tools/web_search/gemini.py +7 -1
- shotgun/agents/tools/web_search/openai.py +8 -2
- shotgun/agents/tools/web_search/utils.py +2 -2
- shotgun/agents/usage_manager.py +16 -11
- shotgun/api_endpoints.py +7 -3
- shotgun/build_constants.py +2 -2
- shotgun/cli/clear.py +53 -0
- shotgun/cli/compact.py +188 -0
- shotgun/cli/config.py +8 -5
- shotgun/cli/context.py +154 -0
- shotgun/cli/error_handler.py +24 -0
- shotgun/cli/export.py +34 -34
- shotgun/cli/feedback.py +4 -2
- shotgun/cli/models.py +1 -0
- shotgun/cli/plan.py +34 -34
- shotgun/cli/research.py +18 -10
- shotgun/cli/spec/__init__.py +5 -0
- shotgun/cli/spec/backup.py +81 -0
- shotgun/cli/spec/commands.py +132 -0
- shotgun/cli/spec/models.py +48 -0
- shotgun/cli/spec/pull_service.py +219 -0
- shotgun/cli/specify.py +20 -19
- shotgun/cli/tasks.py +34 -34
- shotgun/cli/update.py +16 -2
- shotgun/codebase/core/change_detector.py +5 -3
- shotgun/codebase/core/code_retrieval.py +4 -2
- shotgun/codebase/core/ingestor.py +163 -15
- shotgun/codebase/core/manager.py +13 -4
- shotgun/codebase/core/nl_query.py +1 -1
- shotgun/codebase/models.py +2 -0
- shotgun/exceptions.py +357 -0
- shotgun/llm_proxy/__init__.py +17 -0
- shotgun/llm_proxy/client.py +215 -0
- shotgun/llm_proxy/models.py +137 -0
- shotgun/logging_config.py +60 -27
- shotgun/main.py +77 -11
- shotgun/posthog_telemetry.py +38 -29
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +28 -2
- shotgun/prompts/agents/partials/interactive_mode.j2 +3 -3
- shotgun/prompts/agents/plan.j2 +16 -0
- shotgun/prompts/agents/research.j2 +16 -3
- shotgun/prompts/agents/specify.j2 +54 -1
- shotgun/prompts/agents/state/system_state.j2 +0 -2
- shotgun/prompts/agents/tasks.j2 +16 -0
- shotgun/prompts/history/chunk_summarization.j2 +34 -0
- shotgun/prompts/history/combine_summaries.j2 +53 -0
- shotgun/sdk/codebase.py +14 -3
- shotgun/sentry_telemetry.py +163 -16
- shotgun/settings.py +243 -0
- shotgun/shotgun_web/__init__.py +67 -1
- shotgun/shotgun_web/client.py +42 -1
- shotgun/shotgun_web/constants.py +46 -0
- shotgun/shotgun_web/exceptions.py +29 -0
- shotgun/shotgun_web/models.py +390 -0
- shotgun/shotgun_web/shared_specs/__init__.py +32 -0
- shotgun/shotgun_web/shared_specs/file_scanner.py +175 -0
- shotgun/shotgun_web/shared_specs/hasher.py +83 -0
- shotgun/shotgun_web/shared_specs/models.py +71 -0
- shotgun/shotgun_web/shared_specs/upload_pipeline.py +329 -0
- shotgun/shotgun_web/shared_specs/utils.py +34 -0
- shotgun/shotgun_web/specs_client.py +703 -0
- shotgun/shotgun_web/supabase_client.py +31 -0
- shotgun/telemetry.py +10 -33
- shotgun/tui/app.py +310 -46
- shotgun/tui/commands/__init__.py +1 -1
- shotgun/tui/components/context_indicator.py +179 -0
- shotgun/tui/components/mode_indicator.py +70 -0
- shotgun/tui/components/status_bar.py +48 -0
- shotgun/tui/containers.py +91 -0
- shotgun/tui/dependencies.py +39 -0
- shotgun/tui/layout.py +5 -0
- shotgun/tui/protocols.py +45 -0
- shotgun/tui/screens/chat/__init__.py +5 -0
- shotgun/tui/screens/chat/chat.tcss +54 -0
- shotgun/tui/screens/chat/chat_screen.py +1531 -0
- shotgun/tui/screens/chat/codebase_index_prompt_screen.py +243 -0
- shotgun/tui/screens/chat/codebase_index_selection.py +12 -0
- shotgun/tui/screens/chat/help_text.py +40 -0
- shotgun/tui/screens/chat/prompt_history.py +48 -0
- shotgun/tui/screens/chat.tcss +11 -0
- shotgun/tui/screens/chat_screen/command_providers.py +91 -4
- shotgun/tui/screens/chat_screen/hint_message.py +76 -1
- shotgun/tui/screens/chat_screen/history/__init__.py +22 -0
- shotgun/tui/screens/chat_screen/history/agent_response.py +66 -0
- shotgun/tui/screens/chat_screen/history/chat_history.py +115 -0
- shotgun/tui/screens/chat_screen/history/formatters.py +115 -0
- shotgun/tui/screens/chat_screen/history/partial_response.py +43 -0
- shotgun/tui/screens/chat_screen/history/user_question.py +42 -0
- shotgun/tui/screens/confirmation_dialog.py +191 -0
- shotgun/tui/screens/directory_setup.py +45 -41
- shotgun/tui/screens/feedback.py +14 -7
- shotgun/tui/screens/github_issue.py +111 -0
- shotgun/tui/screens/model_picker.py +77 -32
- shotgun/tui/screens/onboarding.py +580 -0
- shotgun/tui/screens/pipx_migration.py +205 -0
- shotgun/tui/screens/provider_config.py +116 -35
- shotgun/tui/screens/shared_specs/__init__.py +21 -0
- shotgun/tui/screens/shared_specs/create_spec_dialog.py +273 -0
- shotgun/tui/screens/shared_specs/models.py +56 -0
- shotgun/tui/screens/shared_specs/share_specs_dialog.py +390 -0
- shotgun/tui/screens/shared_specs/upload_progress_screen.py +452 -0
- shotgun/tui/screens/shotgun_auth.py +112 -18
- shotgun/tui/screens/spec_pull.py +288 -0
- shotgun/tui/screens/welcome.py +137 -11
- shotgun/tui/services/__init__.py +5 -0
- shotgun/tui/services/conversation_service.py +187 -0
- shotgun/tui/state/__init__.py +7 -0
- shotgun/tui/state/processing_state.py +185 -0
- shotgun/tui/utils/mode_progress.py +14 -7
- shotgun/tui/widgets/__init__.py +5 -0
- shotgun/tui/widgets/widget_coordinator.py +263 -0
- shotgun/utils/file_system_utils.py +22 -2
- shotgun/utils/marketing.py +110 -0
- shotgun/utils/update_checker.py +69 -14
- shotgun_sh-0.3.3.dev1.dist-info/METADATA +472 -0
- shotgun_sh-0.3.3.dev1.dist-info/RECORD +229 -0
- {shotgun_sh-0.2.8.dev2.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/WHEEL +1 -1
- {shotgun_sh-0.2.8.dev2.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/entry_points.txt +1 -0
- {shotgun_sh-0.2.8.dev2.dist-info → shotgun_sh-0.3.3.dev1.dist-info}/licenses/LICENSE +1 -1
- shotgun/tui/screens/chat.py +0 -996
- shotgun/tui/screens/chat_screen/history.py +0 -335
- shotgun_sh-0.2.8.dev2.dist-info/METADATA +0 -126
- shotgun_sh-0.2.8.dev2.dist-info/RECORD +0 -155
- /shotgun/agents/{history → conversation/history}/__init__.py +0 -0
- /shotgun/agents/{history → conversation/history}/context_extraction.py +0 -0
- /shotgun/agents/{history → conversation/history}/history_building.py +0 -0
- /shotgun/agents/{history → conversation/history}/message_utils.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/__init__.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_estimation.py +0 -0
shotgun/settings.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""Centralized application settings using Pydantic Settings.
|
|
2
|
+
|
|
3
|
+
All environment variables use the SHOTGUN_ prefix to avoid conflicts with other tools.
|
|
4
|
+
Settings are loaded with the following priority:
|
|
5
|
+
1. Environment variables (highest priority)
|
|
6
|
+
2. Build constants (embedded at build time)
|
|
7
|
+
3. Default values (lowest priority)
|
|
8
|
+
|
|
9
|
+
Example usage:
|
|
10
|
+
from shotgun.settings import settings
|
|
11
|
+
|
|
12
|
+
# Access telemetry settings
|
|
13
|
+
if settings.telemetry.sentry_dsn:
|
|
14
|
+
sentry_sdk.init(dsn=settings.telemetry.sentry_dsn)
|
|
15
|
+
|
|
16
|
+
# Access logging settings
|
|
17
|
+
logger.setLevel(settings.logging.log_level)
|
|
18
|
+
|
|
19
|
+
# Access API settings
|
|
20
|
+
response = httpx.get(settings.api.web_base_url)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
from pydantic import Field, field_validator
|
|
26
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_build_constant(name: str, default: Any = None) -> Any:
|
|
30
|
+
"""Get a value from build_constants.py, falling back to default.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
name: The constant name to retrieve (e.g., "SENTRY_DSN")
|
|
34
|
+
default: Default value if constant not found
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
The constant value, or default if not found/import fails
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
from shotgun import build_constants
|
|
41
|
+
|
|
42
|
+
return getattr(build_constants, name, default)
|
|
43
|
+
except ImportError:
|
|
44
|
+
return default
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TelemetrySettings(BaseSettings):
|
|
48
|
+
"""Telemetry and observability settings.
|
|
49
|
+
|
|
50
|
+
These settings control error tracking (Sentry), analytics (PostHog),
|
|
51
|
+
and observability (Logfire) integrations.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
sentry_dsn: str = Field(
|
|
55
|
+
default_factory=lambda: _get_build_constant("SENTRY_DSN", ""),
|
|
56
|
+
description="Sentry DSN for error tracking",
|
|
57
|
+
)
|
|
58
|
+
posthog_api_key: str = Field(
|
|
59
|
+
default_factory=lambda: _get_build_constant("POSTHOG_API_KEY", ""),
|
|
60
|
+
description="PostHog API key for analytics",
|
|
61
|
+
)
|
|
62
|
+
posthog_project_id: str = Field(
|
|
63
|
+
default_factory=lambda: _get_build_constant("POSTHOG_PROJECT_ID", ""),
|
|
64
|
+
description="PostHog project ID",
|
|
65
|
+
)
|
|
66
|
+
logfire_enabled: bool = Field(
|
|
67
|
+
default_factory=lambda: _get_build_constant("LOGFIRE_ENABLED", False),
|
|
68
|
+
description="Enable Logfire observability (dev builds only)",
|
|
69
|
+
)
|
|
70
|
+
logfire_token: str = Field(
|
|
71
|
+
default_factory=lambda: _get_build_constant("LOGFIRE_TOKEN", ""),
|
|
72
|
+
description="Logfire authentication token",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
model_config = SettingsConfigDict(
|
|
76
|
+
env_prefix="SHOTGUN_",
|
|
77
|
+
env_file=".env",
|
|
78
|
+
env_file_encoding="utf-8",
|
|
79
|
+
extra="ignore",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@field_validator("logfire_enabled", mode="before")
|
|
83
|
+
@classmethod
|
|
84
|
+
def parse_bool(cls, v: Any) -> bool:
|
|
85
|
+
"""Parse boolean values from strings (matches is_truthy behavior)."""
|
|
86
|
+
if isinstance(v, bool):
|
|
87
|
+
return v
|
|
88
|
+
if isinstance(v, str):
|
|
89
|
+
return v.lower() in ("true", "1", "yes")
|
|
90
|
+
return bool(v)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class LoggingSettings(BaseSettings):
|
|
94
|
+
"""Logging configuration settings.
|
|
95
|
+
|
|
96
|
+
Controls log level, console output, and file logging behavior.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
log_level: str = Field(
|
|
100
|
+
default="INFO",
|
|
101
|
+
description="Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)",
|
|
102
|
+
)
|
|
103
|
+
logging_to_console: bool = Field(
|
|
104
|
+
default=False,
|
|
105
|
+
description="Enable console logging output",
|
|
106
|
+
)
|
|
107
|
+
logging_to_file: bool = Field(
|
|
108
|
+
default=True,
|
|
109
|
+
description="Enable file logging output",
|
|
110
|
+
)
|
|
111
|
+
max_log_files: int = Field(
|
|
112
|
+
default=10,
|
|
113
|
+
description="Maximum number of log files to keep (older files are deleted)",
|
|
114
|
+
ge=1,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
model_config = SettingsConfigDict(
|
|
118
|
+
env_prefix="SHOTGUN_",
|
|
119
|
+
env_file=".env",
|
|
120
|
+
env_file_encoding="utf-8",
|
|
121
|
+
extra="ignore",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
@field_validator("log_level")
|
|
125
|
+
@classmethod
|
|
126
|
+
def validate_log_level(cls, v: str) -> str:
|
|
127
|
+
"""Validate log level is one of the allowed values."""
|
|
128
|
+
v = v.upper()
|
|
129
|
+
valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
130
|
+
if v not in valid_levels:
|
|
131
|
+
return "INFO" # Default to INFO if invalid
|
|
132
|
+
return v
|
|
133
|
+
|
|
134
|
+
@field_validator("logging_to_console", "logging_to_file", mode="before")
|
|
135
|
+
@classmethod
|
|
136
|
+
def parse_bool(cls, v: Any) -> bool:
|
|
137
|
+
"""Parse boolean values from strings (matches is_truthy behavior)."""
|
|
138
|
+
if isinstance(v, bool):
|
|
139
|
+
return v
|
|
140
|
+
if isinstance(v, str):
|
|
141
|
+
return v.lower() in ("true", "1", "yes")
|
|
142
|
+
return bool(v)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class ApiSettings(BaseSettings):
|
|
146
|
+
"""API endpoint settings.
|
|
147
|
+
|
|
148
|
+
Configuration for Shotgun backend services.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
web_base_url: str = Field(
|
|
152
|
+
default="https://api-219702594231.us-east4.run.app",
|
|
153
|
+
description="Shotgun Web API base URL (authentication/subscription)",
|
|
154
|
+
)
|
|
155
|
+
account_llm_base_url: str = Field(
|
|
156
|
+
default="https://litellm-219702594231.us-east4.run.app",
|
|
157
|
+
description="Shotgun's LiteLLM proxy base URL (AI model requests)",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
model_config = SettingsConfigDict(
|
|
161
|
+
env_prefix="SHOTGUN_",
|
|
162
|
+
env_file=".env",
|
|
163
|
+
env_file_encoding="utf-8",
|
|
164
|
+
extra="ignore",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class DevelopmentSettings(BaseSettings):
|
|
169
|
+
"""Development and testing settings.
|
|
170
|
+
|
|
171
|
+
These settings are primarily used for testing and development purposes.
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
home: str | None = Field(
|
|
175
|
+
default=None,
|
|
176
|
+
description="Override Shotgun home directory (for testing)",
|
|
177
|
+
)
|
|
178
|
+
pipx_simulate: bool = Field(
|
|
179
|
+
default=False,
|
|
180
|
+
description="Simulate pipx installation (for testing)",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
model_config = SettingsConfigDict(
|
|
184
|
+
env_prefix="SHOTGUN_",
|
|
185
|
+
env_file=".env",
|
|
186
|
+
env_file_encoding="utf-8",
|
|
187
|
+
extra="ignore",
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
@field_validator("pipx_simulate", mode="before")
|
|
191
|
+
@classmethod
|
|
192
|
+
def parse_bool(cls, v: Any) -> bool:
|
|
193
|
+
"""Parse boolean values from strings (matches is_truthy behavior)."""
|
|
194
|
+
if isinstance(v, bool):
|
|
195
|
+
return v
|
|
196
|
+
if isinstance(v, str):
|
|
197
|
+
return v.lower() in ("true", "1", "yes")
|
|
198
|
+
return bool(v)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class Settings(BaseSettings):
|
|
202
|
+
"""Main application settings with SHOTGUN_ prefix.
|
|
203
|
+
|
|
204
|
+
This is the main settings class that composes all other settings groups.
|
|
205
|
+
Access settings via the global `settings` singleton instance.
|
|
206
|
+
|
|
207
|
+
Example:
|
|
208
|
+
from shotgun.settings import settings
|
|
209
|
+
|
|
210
|
+
# Telemetry settings
|
|
211
|
+
settings.telemetry.sentry_dsn
|
|
212
|
+
settings.telemetry.posthog_api_key
|
|
213
|
+
settings.telemetry.logfire_enabled
|
|
214
|
+
|
|
215
|
+
# Logging settings
|
|
216
|
+
settings.logging.log_level
|
|
217
|
+
settings.logging.logging_to_console
|
|
218
|
+
|
|
219
|
+
# API settings
|
|
220
|
+
settings.api.web_base_url
|
|
221
|
+
settings.api.account_llm_base_url
|
|
222
|
+
|
|
223
|
+
# Development settings
|
|
224
|
+
settings.dev.home
|
|
225
|
+
settings.dev.pipx_simulate
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
telemetry: TelemetrySettings = Field(default_factory=TelemetrySettings)
|
|
229
|
+
logging: LoggingSettings = Field(default_factory=LoggingSettings)
|
|
230
|
+
api: ApiSettings = Field(default_factory=ApiSettings)
|
|
231
|
+
dev: DevelopmentSettings = Field(default_factory=DevelopmentSettings)
|
|
232
|
+
|
|
233
|
+
model_config = SettingsConfigDict(
|
|
234
|
+
env_prefix="SHOTGUN_",
|
|
235
|
+
env_file=".env",
|
|
236
|
+
env_file_encoding="utf-8",
|
|
237
|
+
extra="ignore",
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# Global settings singleton
|
|
242
|
+
# Import this in your modules: from shotgun.settings import settings
|
|
243
|
+
settings = Settings()
|
shotgun/shotgun_web/__init__.py
CHANGED
|
@@ -1,14 +1,48 @@
|
|
|
1
|
-
"""Shotgun Web API client for subscription and
|
|
1
|
+
"""Shotgun Web API client for subscription, authentication, and shared specs."""
|
|
2
2
|
|
|
3
3
|
from .client import ShotgunWebClient, check_token_status, create_unification_token
|
|
4
|
+
from .exceptions import (
|
|
5
|
+
ConflictError,
|
|
6
|
+
ForbiddenError,
|
|
7
|
+
NotFoundError,
|
|
8
|
+
PayloadTooLargeError,
|
|
9
|
+
RateLimitExceededError,
|
|
10
|
+
ShotgunWebError,
|
|
11
|
+
UnauthorizedError,
|
|
12
|
+
)
|
|
4
13
|
from .models import (
|
|
14
|
+
# Specs models
|
|
15
|
+
ErrorDetail,
|
|
16
|
+
ErrorResponse,
|
|
17
|
+
FileListResponse,
|
|
18
|
+
FileMetadata,
|
|
19
|
+
FileUploadRequest,
|
|
20
|
+
FileUploadResponse,
|
|
21
|
+
PermissionCheckResponse,
|
|
22
|
+
PublicSpecResponse,
|
|
23
|
+
SpecCreateRequest,
|
|
24
|
+
SpecCreateResponse,
|
|
25
|
+
SpecFileResponse,
|
|
26
|
+
SpecListResponse,
|
|
27
|
+
SpecResponse,
|
|
28
|
+
SpecUpdateRequest,
|
|
29
|
+
SpecVersionCreateRequest,
|
|
30
|
+
SpecVersionResponse,
|
|
31
|
+
SpecVersionState,
|
|
32
|
+
# Token models
|
|
5
33
|
TokenCreateRequest,
|
|
6
34
|
TokenCreateResponse,
|
|
7
35
|
TokenStatus,
|
|
8
36
|
TokenStatusResponse,
|
|
37
|
+
VersionCloseResponse,
|
|
38
|
+
VersionCreateResponse,
|
|
39
|
+
VersionListResponse,
|
|
40
|
+
WorkspaceRole,
|
|
9
41
|
)
|
|
42
|
+
from .specs_client import SpecsClient
|
|
10
43
|
|
|
11
44
|
__all__ = [
|
|
45
|
+
# Existing exports
|
|
12
46
|
"ShotgunWebClient",
|
|
13
47
|
"create_unification_token",
|
|
14
48
|
"check_token_status",
|
|
@@ -16,4 +50,36 @@ __all__ = [
|
|
|
16
50
|
"TokenCreateResponse",
|
|
17
51
|
"TokenStatus",
|
|
18
52
|
"TokenStatusResponse",
|
|
53
|
+
# Specs client
|
|
54
|
+
"SpecsClient",
|
|
55
|
+
# Exceptions
|
|
56
|
+
"ShotgunWebError",
|
|
57
|
+
"UnauthorizedError",
|
|
58
|
+
"ForbiddenError",
|
|
59
|
+
"NotFoundError",
|
|
60
|
+
"ConflictError",
|
|
61
|
+
"PayloadTooLargeError",
|
|
62
|
+
"RateLimitExceededError",
|
|
63
|
+
# Specs models
|
|
64
|
+
"ErrorDetail",
|
|
65
|
+
"ErrorResponse",
|
|
66
|
+
"FileListResponse",
|
|
67
|
+
"FileMetadata",
|
|
68
|
+
"FileUploadRequest",
|
|
69
|
+
"FileUploadResponse",
|
|
70
|
+
"PermissionCheckResponse",
|
|
71
|
+
"PublicSpecResponse",
|
|
72
|
+
"SpecCreateRequest",
|
|
73
|
+
"SpecCreateResponse",
|
|
74
|
+
"SpecFileResponse",
|
|
75
|
+
"SpecListResponse",
|
|
76
|
+
"SpecResponse",
|
|
77
|
+
"SpecUpdateRequest",
|
|
78
|
+
"SpecVersionCreateRequest",
|
|
79
|
+
"SpecVersionResponse",
|
|
80
|
+
"SpecVersionState",
|
|
81
|
+
"VersionCloseResponse",
|
|
82
|
+
"VersionCreateResponse",
|
|
83
|
+
"VersionListResponse",
|
|
84
|
+
"WorkspaceRole",
|
|
19
85
|
]
|
shotgun/shotgun_web/client.py
CHANGED
|
@@ -5,11 +5,13 @@ import httpx
|
|
|
5
5
|
from shotgun.logging_config import get_logger
|
|
6
6
|
|
|
7
7
|
from .constants import (
|
|
8
|
+
ME_PATH,
|
|
8
9
|
SHOTGUN_WEB_BASE_URL,
|
|
9
10
|
UNIFICATION_TOKEN_CREATE_PATH,
|
|
10
11
|
UNIFICATION_TOKEN_STATUS_PATH,
|
|
11
12
|
)
|
|
12
13
|
from .models import (
|
|
14
|
+
MeResponse,
|
|
13
15
|
TokenCreateRequest,
|
|
14
16
|
TokenCreateResponse,
|
|
15
17
|
TokenStatusResponse,
|
|
@@ -66,7 +68,7 @@ class ShotgunWebClient:
|
|
|
66
68
|
return result
|
|
67
69
|
|
|
68
70
|
except httpx.HTTPError as e:
|
|
69
|
-
logger.error("Failed to create unification token: %s", e)
|
|
71
|
+
logger.error("Failed to create unification token at %s: %s", url, e)
|
|
70
72
|
raise
|
|
71
73
|
|
|
72
74
|
def check_token_status(self, token: str) -> TokenStatusResponse:
|
|
@@ -106,6 +108,45 @@ class ShotgunWebClient:
|
|
|
106
108
|
logger.error("Failed to check token status: %s", e)
|
|
107
109
|
raise
|
|
108
110
|
|
|
111
|
+
def get_me(self, jwt: str) -> MeResponse:
|
|
112
|
+
"""Get current user info including workspace.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
jwt: Supabase JWT for authentication
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
User info including workspace details
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
httpx.HTTPStatusError: If authentication fails (401) or other HTTP errors
|
|
122
|
+
httpx.HTTPError: For other request failures
|
|
123
|
+
"""
|
|
124
|
+
url = f"{self.base_url}{ME_PATH}"
|
|
125
|
+
|
|
126
|
+
logger.debug("Fetching user info from /api/me")
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
response = httpx.get(
|
|
130
|
+
url,
|
|
131
|
+
headers={"Authorization": f"Bearer {jwt}"},
|
|
132
|
+
timeout=self.timeout,
|
|
133
|
+
)
|
|
134
|
+
response.raise_for_status()
|
|
135
|
+
|
|
136
|
+
data = response.json()
|
|
137
|
+
result = MeResponse.model_validate(data)
|
|
138
|
+
|
|
139
|
+
logger.info("Successfully fetched user info for %s", result.email)
|
|
140
|
+
return result
|
|
141
|
+
|
|
142
|
+
except httpx.HTTPStatusError as e:
|
|
143
|
+
if e.response.status_code == 401:
|
|
144
|
+
logger.error("Authentication failed for /api/me")
|
|
145
|
+
raise
|
|
146
|
+
except httpx.HTTPError as e:
|
|
147
|
+
logger.error("Failed to fetch user info: %s", e)
|
|
148
|
+
raise
|
|
149
|
+
|
|
109
150
|
|
|
110
151
|
# Convenience functions for standalone use
|
|
111
152
|
def create_unification_token(shotgun_instance_id: str) -> TokenCreateResponse:
|
shotgun/shotgun_web/constants.py
CHANGED
|
@@ -6,16 +6,62 @@ from shotgun.api_endpoints import SHOTGUN_WEB_BASE_URL
|
|
|
6
6
|
# API endpoints
|
|
7
7
|
UNIFICATION_TOKEN_CREATE_PATH = "/api/unification/token/create" # noqa: S105
|
|
8
8
|
UNIFICATION_TOKEN_STATUS_PATH = "/api/unification/token/{token}/status" # noqa: S105
|
|
9
|
+
ME_PATH = "/api/me"
|
|
9
10
|
|
|
10
11
|
# Polling configuration
|
|
11
12
|
DEFAULT_POLL_INTERVAL_SECONDS = 3
|
|
12
13
|
DEFAULT_TOKEN_TIMEOUT_SECONDS = 1800 # 30 minutes
|
|
13
14
|
|
|
15
|
+
# Workspaces API endpoint
|
|
16
|
+
WORKSPACES_PATH = "/api/workspaces"
|
|
17
|
+
|
|
18
|
+
# Specs API endpoints
|
|
19
|
+
PERMISSIONS_PATH = "/api/workspaces/{workspace_id}/specs/permissions"
|
|
20
|
+
SPECS_BASE_PATH = "/api/workspaces/{workspace_id}/specs"
|
|
21
|
+
SPECS_DETAIL_PATH = "/api/workspaces/{workspace_id}/specs/{spec_id}"
|
|
22
|
+
VERSIONS_PATH = "/api/workspaces/{workspace_id}/specs/{spec_id}/versions"
|
|
23
|
+
VERSION_DETAIL_PATH = (
|
|
24
|
+
"/api/workspaces/{workspace_id}/specs/{spec_id}/versions/{version_id}"
|
|
25
|
+
)
|
|
26
|
+
VERSION_CLOSE_PATH = (
|
|
27
|
+
"/api/workspaces/{workspace_id}/specs/{spec_id}/versions/{version_id}/close"
|
|
28
|
+
)
|
|
29
|
+
VERSION_SET_LATEST_PATH = (
|
|
30
|
+
"/api/workspaces/{workspace_id}/specs/{spec_id}/versions/{version_id}/set-latest"
|
|
31
|
+
)
|
|
32
|
+
FILES_PATH = (
|
|
33
|
+
"/api/workspaces/{workspace_id}/specs/{spec_id}/versions/{version_id}/files"
|
|
34
|
+
)
|
|
35
|
+
FILE_DETAIL_PATH = "/api/workspaces/{workspace_id}/specs/{spec_id}/versions/{version_id}/files/{file_id}"
|
|
36
|
+
PUBLIC_SPEC_PATH = "/api/public/specs/{spec_id}"
|
|
37
|
+
PUBLIC_SPEC_FILES_PATH = "/api/public/specs/{spec_id}/files"
|
|
38
|
+
PUBLIC_FILE_PATH = "/api/public/specs/{spec_id}/files/{file_id}"
|
|
39
|
+
|
|
40
|
+
# CLI convenience endpoint (version lookup by ID only)
|
|
41
|
+
VERSION_BY_ID_PATH = "/api/versions/{version_id}"
|
|
42
|
+
|
|
14
43
|
# Re-export for backward compatibility
|
|
15
44
|
__all__ = [
|
|
16
45
|
"SHOTGUN_WEB_BASE_URL",
|
|
17
46
|
"UNIFICATION_TOKEN_CREATE_PATH",
|
|
18
47
|
"UNIFICATION_TOKEN_STATUS_PATH",
|
|
48
|
+
"ME_PATH",
|
|
19
49
|
"DEFAULT_POLL_INTERVAL_SECONDS",
|
|
20
50
|
"DEFAULT_TOKEN_TIMEOUT_SECONDS",
|
|
51
|
+
# Workspaces endpoint
|
|
52
|
+
"WORKSPACES_PATH",
|
|
53
|
+
# Specs endpoints
|
|
54
|
+
"PERMISSIONS_PATH",
|
|
55
|
+
"SPECS_BASE_PATH",
|
|
56
|
+
"SPECS_DETAIL_PATH",
|
|
57
|
+
"VERSIONS_PATH",
|
|
58
|
+
"VERSION_DETAIL_PATH",
|
|
59
|
+
"VERSION_CLOSE_PATH",
|
|
60
|
+
"VERSION_SET_LATEST_PATH",
|
|
61
|
+
"FILES_PATH",
|
|
62
|
+
"FILE_DETAIL_PATH",
|
|
63
|
+
"PUBLIC_SPEC_PATH",
|
|
64
|
+
"PUBLIC_SPEC_FILES_PATH",
|
|
65
|
+
"PUBLIC_FILE_PATH",
|
|
66
|
+
"VERSION_BY_ID_PATH",
|
|
21
67
|
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Typed exceptions for Shotgun Web API operations."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ShotgunWebError(Exception):
|
|
5
|
+
"""Base exception for Shotgun Web API operations."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class UnauthorizedError(ShotgunWebError):
|
|
9
|
+
"""401 - Missing or invalid authentication token."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ForbiddenError(ShotgunWebError):
|
|
13
|
+
"""403 - Insufficient permissions for the requested operation."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NotFoundError(ShotgunWebError):
|
|
17
|
+
"""404 - Resource not found."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ConflictError(ShotgunWebError):
|
|
21
|
+
"""409 - Name conflict or duplicate path."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PayloadTooLargeError(ShotgunWebError):
|
|
25
|
+
"""413 - File exceeds maximum size limit."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class RateLimitExceededError(ShotgunWebError):
|
|
29
|
+
"""429 - Rate limit exceeded."""
|