Files
wehub-resource-sync bcbd1bdb22
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:44 +08:00

171 lines
7.3 KiB
Python

# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
from fastapi import APIRouter, HTTPException, Query, Request
from mirage import Workspace
from mirage.server.clone import clone_workspace_with_override
from mirage.server.summary import make_detail
from mirage.server.version.api import (branch, checkout, commit_state,
diff_live_vs_ref, read_version,
resolve_ref, status_state, version_diff,
version_log)
from mirage.server.version.errors import HeadMovedError, NoSuchBranchError
from mirage.server.version.state_tree import to_state
from mirage.server.version.store import VersionStore
from mirage.workspace.snapshot import to_state_dict
from mirage.server.schemas import ( # isort: skip
BranchRequest, BranchResponse, CheckoutRequest, CloneRequest,
CommitRequest, CommitResponse, DiffResponse, VersionLogItem,
WorkspaceDetail)
router = APIRouter(prefix="/v1")
async def _state_of(ws: Workspace) -> dict:
return await to_state_dict(ws)
@router.post("/workspaces/{workspace_id}/commit",
response_model=CommitResponse)
async def commit_version(workspace_id: str, req: CommitRequest,
request: Request) -> CommitResponse:
registry = request.app.state.registry
if workspace_id not in registry:
raise HTTPException(status_code=404, detail="workspace not found")
entry = registry.get(workspace_id)
state = await entry.runner.call(_state_of(entry.runner.ws))
store = await VersionStore.open(request.app.state.version_backend,
workspace_id)
try:
version = await commit_state(store, state, req.branch, req.message)
except HeadMovedError as e:
raise HTTPException(status_code=409, detail=str(e))
except NoSuchBranchError as e:
raise HTTPException(status_code=404, detail=str(e))
return CommitResponse(version=version.decode(), branch=req.branch)
@router.get("/workspaces/{workspace_id}/versions",
response_model=list[VersionLogItem])
async def list_versions(
workspace_id: str, request: Request, branch: str = Query("main")
) -> list[VersionLogItem]: # noqa: E125
store = await VersionStore.open(request.app.state.version_backend,
workspace_id)
if branch not in await store.branches():
return []
entries = await version_log(store, branch)
return [VersionLogItem(id=e["id"], message=e["message"]) for e in entries]
@router.post("/workspaces/{workspace_id}/branch",
response_model=BranchResponse,
status_code=201)
async def create_branch(workspace_id: str, req: BranchRequest,
request: Request) -> BranchResponse:
store = await VersionStore.open(request.app.state.version_backend,
workspace_id)
if req.name in await store.branches():
raise HTTPException(status_code=409,
detail=f"branch already exists: {req.name!r}")
try:
await branch(store, req.name, req.from_branch)
except KeyError:
raise HTTPException(status_code=404,
detail=f"no such branch: {req.from_branch!r}")
head = await store.head(req.name)
return BranchResponse(branch=req.name, version=head.decode())
@router.get("/workspaces/{workspace_id}/diff", response_model=DiffResponse)
async def diff_versions(
workspace_id: str,
request: Request,
a: str | None = Query(None),
b: str | None = Query(None),
branch: str = Query("main"),
) -> DiffResponse: # noqa: E125
store = await VersionStore.open(request.app.state.version_backend,
workspace_id)
state = None
if a is None or b is None:
registry = request.app.state.registry
if workspace_id not in registry:
raise HTTPException(status_code=404, detail="workspace not found")
entry = registry.get(workspace_id)
state = await entry.runner.call(_state_of(entry.runner.ws))
try:
if a is not None and b is not None:
changes = await version_diff(store, await resolve_ref(store, a),
await resolve_ref(store, b))
elif a is not None:
changes = await diff_live_vs_ref(store, state, a)
else:
changes = await status_state(store, state, branch)
except KeyError:
raise HTTPException(status_code=404, detail="version not found")
return DiffResponse(**changes)
@router.post("/workspaces/{workspace_id}/checkout",
response_model=WorkspaceDetail)
async def checkout_version(workspace_id: str, req: CheckoutRequest,
request: Request) -> WorkspaceDetail:
registry = request.app.state.registry
if workspace_id not in registry:
raise HTTPException(status_code=404, detail="workspace not found")
entry = registry.get(workspace_id)
store = await VersionStore.open(request.app.state.version_backend,
workspace_id)
try:
await entry.runner.call(checkout(store, entry.runner.ws, req.ref))
except KeyError:
raise HTTPException(status_code=404,
detail=f"version not found: {req.ref}")
return await make_detail(entry)
@router.post("/workspaces/clone",
response_model=WorkspaceDetail,
status_code=201)
async def clone_workspace_version(req: CloneRequest,
request: Request) -> WorkspaceDetail:
registry = request.app.state.registry
if req.id is not None and req.id in registry:
raise HTTPException(status_code=409,
detail=f"workspace id already exists: {req.id!r}")
if req.at is not None:
store = await VersionStore.open(request.app.state.version_backend,
req.source_id)
version = await resolve_ref(store, req.at)
try:
entries, meta = await read_version(store, version)
except KeyError:
raise HTTPException(status_code=404,
detail=f"version not found: {req.at}")
ws = await Workspace.from_state(to_state(entries, meta))
else:
if req.source_id not in registry:
raise HTTPException(status_code=404, detail="workspace not found")
src = registry.get(req.source_id)
ws = await src.runner.call(
clone_workspace_with_override(src.runner.ws, None))
try:
entry = registry.add(ws, workspace_id=req.id)
except ValueError as e:
raise HTTPException(status_code=409, detail=str(e))
return await make_detail(entry)