solwyn-cli 0.1.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.
- solwyn_cli/__init__.py +3 -0
- solwyn_cli/_vendor/__init__.py +1 -0
- solwyn_cli/_vendor/validation.py +21 -0
- solwyn_cli/_vendor/wire.py +185 -0
- solwyn_cli/artifacts/__init__.py +727 -0
- solwyn_cli/artifacts/_atomic.py +79 -0
- solwyn_cli/auth.py +252 -0
- solwyn_cli/client.py +279 -0
- solwyn_cli/commands/__init__.py +1 -0
- solwyn_cli/commands/_shared.py +49 -0
- solwyn_cli/commands/agent_context.py +19 -0
- solwyn_cli/commands/budget.py +976 -0
- solwyn_cli/commands/config.py +110 -0
- solwyn_cli/commands/costs.py +186 -0
- solwyn_cli/commands/init.py +659 -0
- solwyn_cli/commands/keys.py +386 -0
- solwyn_cli/commands/login.py +205 -0
- solwyn_cli/commands/mcp.py +28 -0
- solwyn_cli/commands/projects.py +449 -0
- solwyn_cli/commands/status.py +173 -0
- solwyn_cli/commands/whoami.py +170 -0
- solwyn_cli/config.py +209 -0
- solwyn_cli/errors.py +114 -0
- solwyn_cli/exit_codes.py +56 -0
- solwyn_cli/help.py +19 -0
- solwyn_cli/main.py +135 -0
- solwyn_cli/mcp/__init__.py +5 -0
- solwyn_cli/mcp/server.py +728 -0
- solwyn_cli/output.py +263 -0
- solwyn_cli/runtime.py +115 -0
- solwyn_cli/spec/__init__.py +1 -0
- solwyn_cli/spec/openapi_snapshot.json +12357 -0
- solwyn_cli-0.1.0.dist-info/METADATA +60 -0
- solwyn_cli-0.1.0.dist-info/RECORD +37 -0
- solwyn_cli-0.1.0.dist-info/WHEEL +4 -0
- solwyn_cli-0.1.0.dist-info/entry_points.txt +3 -0
- solwyn_cli-0.1.0.dist-info/licenses/LICENSE +201 -0
solwyn_cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Small, public-contract mirrors used by the standalone CLI."""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Structural validation for project API keys.
|
|
2
|
+
|
|
3
|
+
This module intentionally mirrors only the public key shape. Authentication
|
|
4
|
+
remains the API's responsibility.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from typing import Final
|
|
9
|
+
|
|
10
|
+
PROJECT_KEY_PATTERN: Final = re.compile(r"^sk_proj_[a-f0-9]{64}$")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def validate_project_key_format(project_key: str) -> str:
|
|
14
|
+
"""Return a structurally valid project API key without authenticating it.
|
|
15
|
+
|
|
16
|
+
Invalid values are never interpolated into the exception so credential
|
|
17
|
+
material cannot leak through user-facing output or logs.
|
|
18
|
+
"""
|
|
19
|
+
if PROJECT_KEY_PATTERN.fullmatch(project_key) is None:
|
|
20
|
+
raise ValueError("Invalid project key format")
|
|
21
|
+
return project_key
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Pydantic v2 mirrors of the request bodies constructed by the CLI."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
from typing import Annotated, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, SecretStr, field_serializer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _WireModel(BaseModel):
|
|
11
|
+
model_config = ConfigDict(extra="forbid")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ProviderName(StrEnum):
|
|
15
|
+
"""Provider identifiers accepted by budget request endpoints."""
|
|
16
|
+
|
|
17
|
+
OPENAI = "openai"
|
|
18
|
+
ANTHROPIC = "anthropic"
|
|
19
|
+
GOOGLE = "google"
|
|
20
|
+
BEDROCK = "bedrock"
|
|
21
|
+
XAI = "xai"
|
|
22
|
+
DEEPSEEK = "deepseek"
|
|
23
|
+
MISTRAL = "mistral"
|
|
24
|
+
QWEN = "qwen"
|
|
25
|
+
GROQ = "groq"
|
|
26
|
+
TOGETHER = "together"
|
|
27
|
+
FIREWORKS = "fireworks"
|
|
28
|
+
PERPLEXITY = "perplexity"
|
|
29
|
+
AZURE_OPENAI = "azure_openai"
|
|
30
|
+
OPENROUTER = "openrouter"
|
|
31
|
+
ZAI = "zai"
|
|
32
|
+
OLLAMA = "ollama"
|
|
33
|
+
VLLM = "vllm"
|
|
34
|
+
LMSTUDIO = "lmstudio"
|
|
35
|
+
OPENAI_COMPATIBLE = "openai_compatible"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ServiceTier(StrEnum):
|
|
39
|
+
"""Provider service tiers accepted by budget confirmation."""
|
|
40
|
+
|
|
41
|
+
AUTO = "auto"
|
|
42
|
+
DEFAULT = "default"
|
|
43
|
+
FLEX = "flex"
|
|
44
|
+
SCALE = "scale"
|
|
45
|
+
PRIORITY = "priority"
|
|
46
|
+
STANDARD = "standard"
|
|
47
|
+
OPTIMIZED = "optimized"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BudgetPeriod(StrEnum):
|
|
51
|
+
"""Rolling window for budget accounting."""
|
|
52
|
+
|
|
53
|
+
DAILY = "daily"
|
|
54
|
+
WEEKLY = "weekly"
|
|
55
|
+
MONTHLY = "monthly"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class BudgetMode(StrEnum):
|
|
59
|
+
"""Budget enforcement mode."""
|
|
60
|
+
|
|
61
|
+
ALERT_ONLY = "alert_only"
|
|
62
|
+
HARD_DENY = "hard_deny"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class _ThresholdConfig(_WireModel):
|
|
66
|
+
type: Literal["budget_threshold"] = "budget_threshold"
|
|
67
|
+
percent: Annotated[int, Field(ge=1, le=100)]
|
|
68
|
+
channel_ids: list[str] = Field(default_factory=list)
|
|
69
|
+
snoozed_until: datetime | None = None
|
|
70
|
+
paused: bool = False
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class _ForecastOverrunConfig(_WireModel):
|
|
74
|
+
type: Literal["forecast_overrun"]
|
|
75
|
+
spend_threshold_usd: Annotated[float, Field(gt=0)]
|
|
76
|
+
period: Literal["month"] = "month"
|
|
77
|
+
channel_ids: list[str] = Field(default_factory=list)
|
|
78
|
+
snoozed_until: datetime | None = None
|
|
79
|
+
paused: bool = False
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class _DailyAnomalyConfig(_WireModel):
|
|
83
|
+
type: Literal["daily_anomaly"]
|
|
84
|
+
multiplier: Annotated[float, Field(gt=1, le=100)]
|
|
85
|
+
lookback_days: Annotated[int, Field(ge=3, le=30)] = 7
|
|
86
|
+
channel_ids: list[str] = Field(default_factory=list)
|
|
87
|
+
snoozed_until: datetime | None = None
|
|
88
|
+
paused: bool = False
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class _UnitCostAnomalyConfig(_WireModel):
|
|
92
|
+
type: Literal["unit_cost_anomaly"]
|
|
93
|
+
multiplier: Annotated[float, Field(gt=1, le=100)]
|
|
94
|
+
lookback_days: Annotated[int, Field(ge=3, le=30)] = 7
|
|
95
|
+
channel_ids: list[str] = Field(default_factory=list)
|
|
96
|
+
snoozed_until: datetime | None = None
|
|
97
|
+
paused: bool = False
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
_BudgetRule = (
|
|
101
|
+
_ThresholdConfig
|
|
102
|
+
| _ForecastOverrunConfig
|
|
103
|
+
| _DailyAnomalyConfig
|
|
104
|
+
| _UnitCostAnomalyConfig
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class TokenDetails(_WireModel):
|
|
109
|
+
"""Normalized provider token usage used to settle a reservation."""
|
|
110
|
+
|
|
111
|
+
input_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
112
|
+
output_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
113
|
+
cached_input_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
114
|
+
cache_creation_5m_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
115
|
+
cache_creation_1h_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
116
|
+
reasoning_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
117
|
+
audio_input_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
118
|
+
audio_output_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
119
|
+
accepted_prediction_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
120
|
+
rejected_prediction_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
121
|
+
tool_use_input_tokens: Annotated[int, Field(ge=0, le=100_000_000)] = 0
|
|
122
|
+
is_estimated: bool = False
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class LoginRequest(_WireModel):
|
|
126
|
+
model_config = ConfigDict(extra="forbid", hide_input_in_errors=True)
|
|
127
|
+
|
|
128
|
+
email: str
|
|
129
|
+
password: Annotated[SecretStr, Field(max_length=128)]
|
|
130
|
+
plan: Literal["founder", "team", "scale"] | None = None
|
|
131
|
+
billing_interval: Literal["month", "year"] | None = None
|
|
132
|
+
|
|
133
|
+
@field_serializer("password")
|
|
134
|
+
def serialize_password(self, value: SecretStr) -> str:
|
|
135
|
+
"""Reveal the password only while constructing the login wire body."""
|
|
136
|
+
return value.get_secret_value()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class RefreshRequest(_WireModel):
|
|
140
|
+
refresh_token: str
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class ProjectCreateRequest(_WireModel):
|
|
144
|
+
name: Annotated[str, Field(min_length=1, max_length=100)]
|
|
145
|
+
budget_limit: Annotated[float, Field(gt=0)] = 100.0
|
|
146
|
+
budget_period: Annotated[str, Field(pattern=r"^(daily|weekly|monthly)$")] = (
|
|
147
|
+
"monthly"
|
|
148
|
+
)
|
|
149
|
+
budget_mode: Annotated[str, Field(pattern=r"^(alert_only|hard_deny)$")] = (
|
|
150
|
+
"alert_only"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class ProjectKeyRotateRequest(_WireModel):
|
|
155
|
+
grace_seconds: Annotated[int, Field(ge=0, le=604800)] | None = None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class BudgetUpdateRequest(_WireModel):
|
|
159
|
+
limit_usd: Annotated[float, Field(gt=0)]
|
|
160
|
+
period: BudgetPeriod
|
|
161
|
+
mode: BudgetMode
|
|
162
|
+
sub_period: BudgetPeriod | None = None
|
|
163
|
+
sub_limit_usd: float | None = None
|
|
164
|
+
thresholds: Annotated[list[_BudgetRule], Field(max_length=50)]
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class BudgetCheckRequest(_WireModel):
|
|
168
|
+
estimated_input_tokens: Annotated[int, Field(ge=0)]
|
|
169
|
+
model: Annotated[str, Field(max_length=2048)]
|
|
170
|
+
provider: ProviderName
|
|
171
|
+
fallback_providers: list[ProviderName] = Field(default_factory=list)
|
|
172
|
+
fallback_models: Annotated[
|
|
173
|
+
list[Annotated[str, Field(max_length=2048)]], Field(max_length=8)
|
|
174
|
+
] = Field(default_factory=list)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class BudgetConfirmRequest(_WireModel):
|
|
178
|
+
reservation_id: str
|
|
179
|
+
model: Annotated[str, Field(max_length=2048)]
|
|
180
|
+
provider: ProviderName
|
|
181
|
+
is_provider_fallback: bool = False
|
|
182
|
+
call_id: Annotated[str, Field(max_length=36)]
|
|
183
|
+
token_details: TokenDetails
|
|
184
|
+
provider_region: Annotated[str, Field(max_length=32)] | None = None
|
|
185
|
+
service_tier: ServiceTier | None = None
|