Implement SAND-WP-0006: SaaS payments, routing, and ext.saas-stub

Add credits store, metering on create/destroy, extension routing resolver,
metered SaaS stub extension, burst/saas profiles, credits CLI, docs, and tests.
This commit is contained in:
tegwick 2026-06-24 07:52:20 +02:00
parent eee336149e
commit 1415e17230
29 changed files with 878 additions and 18 deletions

View file

@ -86,10 +86,32 @@ class ProfileMetadata(BaseModel):
observability: Literal["none", "canary"] = "none"
class RouteSpec(BaseModel):
strategy: RouteStrategy = RouteStrategy.EXPLICIT
extensions: list[str] = Field(default_factory=list)
max_cost_per_hour_usd: float | None = None
class MeterQuote(BaseModel):
extension_id: str
estimated_usd: float
unit: Literal["per_hour", "per_session"] = "per_hour"
duration_s: int = 3600
class MeterRecord(BaseModel):
pricing_model: Literal["self-hosted", "metered"] = "self-hosted"
estimate_usd: float | None = None
actual_usd: float | None = None
duration_s: float | None = None
currency: str = "USD"
class Profile(BaseModel):
id: str
version: str
extension: str
route: RouteSpec | None = None
isolation: IsolationSpec = Field(default_factory=IsolationSpec)
network: NetworkSpec = Field(default_factory=NetworkSpec)
workspace: WorkspaceSpec = Field(default_factory=WorkspaceSpec)
@ -130,6 +152,7 @@ class Reachability(BaseModel):
remote_dir: str | None = None
compose_project: str | None = None
host: str | None = None
endpoint: str | None = None
class SandboxStatus(BaseModel):
@ -142,6 +165,7 @@ class SandboxStatus(BaseModel):
reachability: Reachability | None = None
inputs: dict[str, str] = Field(default_factory=dict)
error: str | None = None
meter: MeterRecord | None = None
telemetry: dict | None = None # IntrospectionReport JSON when canary
created_at: datetime
updated_at: datetime