Skip to content

Commit 70c2f48

Browse files
authored
Merge pull request #10 from yepcode/feat/YEP-3134
YEP-3134 Update yepcode-run with new endpoints (py)
2 parents 5d3be97 + eea5fff commit 70c2f48

3 files changed

Lines changed: 231 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
```bash
8+
# Install dependencies
9+
poetry install
10+
11+
# Run all tests
12+
poetry run pytest
13+
14+
# Run a single test file
15+
poetry run pytest tests/test_language_detector.py
16+
17+
# Run a single test function
18+
poetry run pytest tests/test_yepcode_run.py::test_run_python_code
19+
20+
# Run with coverage
21+
poetry run pytest --cov=yepcode_run
22+
23+
# Build the package
24+
poetry build
25+
```
26+
27+
Tests require a `YEPCODE_API_TOKEN` environment variable. Set it in `.env` or export it before running tests. Most tests are integration tests that hit the live YepCode cloud API.
28+
29+
## Architecture
30+
31+
The SDK is a thin Python client for executing code in YepCode's serverless runtime.
32+
33+
**Layers:**
34+
35+
1. **Public API**`YepCodeRun`, `YepCodeEnv`, `YepCodeStorage` (in `yepcode_run/`)
36+
2. **Execution engine**`yepcode_run/run/execution.py` handles polling loop, status transitions (`CREATED → RUNNING → FINISHED/ERROR`), and event callbacks (`onLog`, `onFinish`, `onError`)
37+
3. **API Manager**`yepcode_run/api/api_manager.py` singleton keyed by config hash; merges env vars + constructor params
38+
4. **HTTP client**`yepcode_run/api/yepcode_api.py` handles auth (API token or JWT with auto-refresh), all REST calls
39+
40+
**Key design decisions:**
41+
- `YepCodeRun` hashes submitted code (SHA256) to reuse existing cloud processes rather than creating new ones each run
42+
- `YepCodeApiManager` uses a singleton per config hash, so multiple `YepCodeRun` instances with the same credentials share one API client
43+
- Language detection (`yepcode_run/utils/language_detector.py`) uses a score-based heuristic on stripped code (comments removed) when `language` is not specified
44+
45+
**Config priority:** constructor params > environment variables > `.env` file. Key env vars: `YEPCODE_API_TOKEN`, `YEPCODE_API_HOST` (defaults to `https://cloud.yepcode.io`), `YEPCODE_TIMEOUT` (ms, default 60000).
46+
47+
## OpenAPI spec
48+
49+
The live spec is always available at `https://cloud.yepcode.io/api/rest/public/api-docs`. Fetch it with WebFetch to audit which endpoints are missing from `yepcode_run/api/yepcode_api.py` before adding new ones. New endpoints are deployed to that URL before this SDK is updated.

yepcode_run/api/types.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,82 @@ def from_dict(data: dict) -> "StorageObject":
468468
class CreateStorageObjectInput:
469469
name: str
470470
file: Any
471+
472+
473+
# Service account types
474+
@dataclass
475+
class ServiceAccount:
476+
id: str
477+
name: str
478+
client_id: str
479+
client_secret: Optional[str] = None
480+
created_at: Optional[datetime] = None
481+
updated_at: Optional[datetime] = None
482+
483+
484+
@dataclass
485+
class ServiceAccountInput:
486+
name: str
487+
488+
489+
# Dependency manifest types
490+
@dataclass
491+
class ProgrammingLanguageManifest:
492+
id: str
493+
programming_language: ProgrammingLanguage
494+
dependencies: Optional[Dict[str, str]] = None
495+
next_installation: Optional[Dict[str, str]] = None
496+
497+
498+
@dataclass
499+
class UpdateTeamDependenciesInput:
500+
dependencies: Optional[Dict[str, str]] = None
501+
502+
503+
# Team types
504+
@dataclass
505+
class Team:
506+
slug: str
507+
name: str
508+
zone_id: Optional[str] = None
509+
parent_team_slugs: Optional[List[str]] = None
510+
params_schema_validation_enabled: Optional[bool] = None
511+
error_handler_config: Optional[Dict[str, Any]] = None
512+
created_at: Optional[datetime] = None
513+
514+
515+
@dataclass
516+
class UpdateTeamInput:
517+
name: Optional[str] = None
518+
zone_id: Optional[str] = None
519+
parent_team_slugs: Optional[List[str]] = None
520+
params_schema_validation_enabled: Optional[bool] = None
521+
error_handler_config: Optional[Dict[str, Any]] = None
522+
523+
524+
# Sandbox types
525+
@dataclass
526+
class Sandbox:
527+
id: str
528+
name: str
529+
grpc_server_url: Optional[str] = None
530+
grpc_api_key: Optional[str] = None
531+
image_id: Optional[str] = None
532+
public_http_ports: Optional[List[int]] = None
533+
metadata: Optional[Dict[str, Any]] = None
534+
timeout_at: Optional[datetime] = None
535+
536+
537+
@dataclass
538+
class CreateSandboxInput:
539+
name: str
540+
image_id: Optional[str] = None
541+
timeout: Optional[int] = None
542+
metadata: Optional[Dict[str, Any]] = None
543+
public_http_ports: Optional[List[int]] = None
544+
public_http_ports_basic_auth: Optional[Dict[str, str]] = None
545+
546+
547+
@dataclass
548+
class UpdateSandboxInput:
549+
timeout: Optional[int] = None

yepcode_run/api/yepcode_api.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@
4242
ScheduledProcessInput,
4343
CreateStorageObjectInput,
4444
StorageObject,
45+
ServiceAccount,
46+
ServiceAccountInput,
47+
ProgrammingLanguage,
48+
ProgrammingLanguageManifest,
49+
UpdateTeamDependenciesInput,
50+
Team,
51+
UpdateTeamInput,
52+
Sandbox,
53+
CreateSandboxInput,
54+
UpdateSandboxInput,
4555
)
4656

4757

@@ -469,6 +479,99 @@ def create_module_version_alias(
469479
) -> VersionedModuleAlias:
470480
return self._request("POST", f"/modules/{module_id}/aliases", {"data": data})
471481

482+
def get_module_version(self, module_id: str, version_id: str) -> VersionedModule:
483+
return self._request("GET", f"/modules/{module_id}/versions/{version_id}")
484+
485+
def delete_module_version(self, module_id: str, version_id: str) -> None:
486+
self._request("DELETE", f"/modules/{module_id}/versions/{version_id}")
487+
488+
def get_module_version_alias(
489+
self, module_id: str, alias_id: str
490+
) -> VersionedModuleAlias:
491+
return self._request("GET", f"/modules/{module_id}/aliases/{alias_id}")
492+
493+
def update_module_version_alias(
494+
self, module_id: str, alias_id: str, data: VersionedModuleAliasInput
495+
) -> VersionedModuleAlias:
496+
return self._request(
497+
"PATCH", f"/modules/{module_id}/aliases/{alias_id}", {"data": data}
498+
)
499+
500+
def delete_module_version_alias(self, module_id: str, alias_id: str) -> None:
501+
self._request("DELETE", f"/modules/{module_id}/aliases/{alias_id}")
502+
503+
def get_process_version(
504+
self, process_id: str, version_id: str
505+
) -> VersionedProcess:
506+
return self._request("GET", f"/processes/{process_id}/versions/{version_id}")
507+
508+
def delete_process_version(self, process_id: str, version_id: str) -> None:
509+
self._request("DELETE", f"/processes/{process_id}/versions/{version_id}")
510+
511+
def get_process_version_alias(
512+
self, process_id: str, alias_id: str
513+
) -> VersionedProcessAlias:
514+
return self._request("GET", f"/processes/{process_id}/aliases/{alias_id}")
515+
516+
def update_process_version_alias(
517+
self, process_id: str, alias_id: str, data: VersionedProcessAliasInput
518+
) -> VersionedProcessAlias:
519+
return self._request(
520+
"PATCH", f"/processes/{process_id}/aliases/{alias_id}", {"data": data}
521+
)
522+
523+
def delete_process_version_alias(self, process_id: str, alias_id: str) -> None:
524+
self._request("DELETE", f"/processes/{process_id}/aliases/{alias_id}")
525+
526+
def update_schedule(self, id: str, data: ScheduledProcessInput) -> Schedule:
527+
return self._request("PATCH", f"/schedules/{id}", {"data": data})
528+
529+
def get_service_accounts(self) -> List[ServiceAccount]:
530+
return self._request("GET", "/auth/service-accounts")
531+
532+
def create_service_account(self, data: ServiceAccountInput) -> ServiceAccount:
533+
return self._request("POST", "/auth/service-accounts", {"data": data})
534+
535+
def delete_service_account(self, id: str) -> None:
536+
self._request("DELETE", f"/auth/service-accounts/{id}")
537+
538+
def get_team_dependencies(
539+
self, language: ProgrammingLanguage
540+
) -> ProgrammingLanguageManifest:
541+
return self._request("GET", f"/dependencies/{language.value}")
542+
543+
def update_team_dependencies(
544+
self, language: ProgrammingLanguage, data: UpdateTeamDependenciesInput
545+
) -> ProgrammingLanguageManifest:
546+
return self._request(
547+
"PUT", f"/dependencies/{language.value}", {"data": data}
548+
)
549+
550+
def install_team_dependencies(
551+
self, language: ProgrammingLanguage
552+
) -> ProgrammingLanguageManifest:
553+
return self._request("POST", f"/dependencies/{language.value}/install")
554+
555+
def discard_team_dependencies_installation(
556+
self, language: ProgrammingLanguage
557+
) -> None:
558+
self._request("DELETE", f"/dependencies/{language.value}/install")
559+
560+
def get_team(self) -> Team:
561+
return self._request("GET", "/team")
562+
563+
def update_team(self, data: UpdateTeamInput) -> Team:
564+
return self._request("PATCH", "/team", {"data": data})
565+
566+
def create_sandbox(self, data: CreateSandboxInput) -> Sandbox:
567+
return self._request("POST", "/sandboxes", {"data": data})
568+
569+
def update_sandbox(self, sandbox_id: str, data: UpdateSandboxInput) -> Sandbox:
570+
return self._request("POST", f"/sandboxes/{sandbox_id}", {"data": data})
571+
572+
def kill_sandbox(self, sandbox_id: str) -> None:
573+
self._request("POST", f"/sandboxes/{sandbox_id}/kill")
574+
472575
def get_objects(self, params: Optional[Dict[str, Any]] = None) -> List[StorageObject]:
473576
response = self._request("GET", "/storage/objects", {"params": params or {}})
474577
return [StorageObject.from_dict(obj) for obj in response]

0 commit comments

Comments
 (0)