opencomputer-sdk 0.6.3__tar.gz → 0.6.5__tar.gz
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.
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/.gitignore +3 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/PKG-INFO +1 -1
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/mounts.py +79 -15
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/sandbox.py +25 -2
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/pyproject.toml +1 -1
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/README.md +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/run_all_tests.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/stream_demo.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_commands.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_concurrent.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_declarative_images.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_default_template.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_disk_size.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_domain_tls.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_environment.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_exec.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_file_ops.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_large_files.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_multi_template.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_python_sdk.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_reconnect.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_secret_store_fork.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_secretstore.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_shell.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/examples/test_timeout.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/__init__.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/agent.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/commands.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/exec.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/filesystem.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/image.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/project.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/pty.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/shell.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/snapshot.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/sse.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/template.py +0 -0
- {opencomputer_sdk-0.6.3 → opencomputer_sdk-0.6.5}/opencomputer/usage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencomputer-sdk
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: Python SDK for OpenComputer - cloud sandbox platform
|
|
5
5
|
Project-URL: Homepage, https://github.com/diggerhq/opensandbox
|
|
6
6
|
Project-URL: Repository, https://github.com/diggerhq/opensandbox
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
"""FUSE-backed
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
"""FUSE-backed filesystem mounts inside a sandbox.
|
|
2
|
+
|
|
3
|
+
Two drivers:
|
|
4
|
+
|
|
5
|
+
- ``rclone`` (default, via :meth:`Mounts.add`): wrap any of rclone's ~40
|
|
6
|
+
backends (S3, GCS, Azure Blob, SFTP, WebDAV, Dropbox, …) behind a simple
|
|
7
|
+
remote+creds shape. Creds are written to a tmpfs file (mode 0600), never
|
|
8
|
+
persisted on the worker.
|
|
9
|
+
- ``command`` (via :meth:`Mounts.add_command`): run your own FUSE daemon /
|
|
10
|
+
mount command. Use this when you already have a FUSE-ready filesystem and
|
|
11
|
+
don't want rclone as a middle layer. Secrets are injected into the daemon's
|
|
12
|
+
process env (never the command line) and never persisted.
|
|
8
13
|
"""
|
|
9
14
|
|
|
10
15
|
from __future__ import annotations
|
|
11
16
|
|
|
12
|
-
from dataclasses import dataclass
|
|
17
|
+
from dataclasses import dataclass, field
|
|
13
18
|
from typing import Literal
|
|
14
19
|
|
|
15
20
|
import httpx
|
|
@@ -21,17 +26,22 @@ MountBackend = Literal["s3", "gcs", "azureblob", "sftp", "webdav", "dropbox"]
|
|
|
21
26
|
class MountInfo:
|
|
22
27
|
"""An active mount as tracked by the worker.
|
|
23
28
|
|
|
24
|
-
``rclone_version`` is the rclone version inside the sandbox
|
|
25
|
-
mount-add time (e.g. ``"v1.65.2"``). rclone is baked into the
|
|
26
|
-
different sandboxes may carry different versions; this lets ops
|
|
27
|
-
backend-specific bug reports quickly.
|
|
29
|
+
``rclone_version`` (rclone driver) is the rclone version inside the sandbox
|
|
30
|
+
captured at mount-add time (e.g. ``"v1.65.2"``). rclone is baked into the
|
|
31
|
+
rootfs, so different sandboxes may carry different versions; this lets ops
|
|
32
|
+
triage backend-specific bug reports quickly.
|
|
28
33
|
"""
|
|
29
34
|
|
|
30
35
|
path: str
|
|
31
|
-
remote: str
|
|
32
36
|
read_only: bool
|
|
37
|
+
driver: str = "rclone"
|
|
38
|
+
# rclone driver
|
|
39
|
+
remote: str = ""
|
|
33
40
|
backend: str = ""
|
|
34
41
|
rclone_version: str = ""
|
|
42
|
+
# command driver
|
|
43
|
+
command: list[str] = field(default_factory=list)
|
|
44
|
+
env: dict[str, str] = field(default_factory=dict)
|
|
35
45
|
|
|
36
46
|
|
|
37
47
|
@dataclass
|
|
@@ -90,6 +100,57 @@ class Mounts:
|
|
|
90
100
|
data = resp.json()
|
|
91
101
|
return _mount_from_dict(data)
|
|
92
102
|
|
|
103
|
+
async def add_command(
|
|
104
|
+
self,
|
|
105
|
+
path: str,
|
|
106
|
+
command: list[str],
|
|
107
|
+
env: dict[str, str] | None = None,
|
|
108
|
+
secrets: dict[str, str] | None = None,
|
|
109
|
+
read_only: bool = True,
|
|
110
|
+
) -> MountInfo:
|
|
111
|
+
"""Mount a filesystem by running your own FUSE daemon / mount command.
|
|
112
|
+
|
|
113
|
+
Use this when you already have a FUSE-ready filesystem (your own VFS,
|
|
114
|
+
gcsfuse, s3fs, …) and don't want rclone as a middle layer. The platform
|
|
115
|
+
manages the mountpoint, env/secret injection, and teardown; ``command``
|
|
116
|
+
establishes the mount.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
path: Absolute mountpoint inside the VM.
|
|
120
|
+
command: argv for the FUSE daemon. Any ``"{mountpoint}"`` token is
|
|
121
|
+
replaced with ``path``.
|
|
122
|
+
env: Env vars for the command (returned by :meth:`list`).
|
|
123
|
+
secrets: Secret env vars — injected into the daemon's process env
|
|
124
|
+
(never the command line, so they don't leak via ``ps``), and
|
|
125
|
+
never recorded or returned by :meth:`list`.
|
|
126
|
+
read_only: Advisory for this driver — your command must honor it.
|
|
127
|
+
Also exported to the daemon as ``OC_MOUNT_READONLY=1``. Default
|
|
128
|
+
``True``.
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
>>> await sandbox.mounts.add_command(
|
|
132
|
+
... path="/mnt/data",
|
|
133
|
+
... command=["gcsfuse", "my-bucket", "{mountpoint}"],
|
|
134
|
+
... secrets={"GOOGLE_APPLICATION_CREDENTIALS_JSON": sa_json},
|
|
135
|
+
... )
|
|
136
|
+
"""
|
|
137
|
+
body: dict[str, object] = {
|
|
138
|
+
"path": path,
|
|
139
|
+
"driver": "command",
|
|
140
|
+
"command": command,
|
|
141
|
+
"readOnly": read_only,
|
|
142
|
+
}
|
|
143
|
+
if env is not None:
|
|
144
|
+
body["env"] = env
|
|
145
|
+
if secrets is not None:
|
|
146
|
+
body["secrets"] = secrets
|
|
147
|
+
|
|
148
|
+
resp = await self._client.post(
|
|
149
|
+
f"/sandboxes/{self._sandbox_id}/mounts", json=body
|
|
150
|
+
)
|
|
151
|
+
resp.raise_for_status()
|
|
152
|
+
return _mount_from_dict(resp.json())
|
|
153
|
+
|
|
93
154
|
async def list(self) -> list[MountInfo]:
|
|
94
155
|
"""List the mounts this worker is tracking for the sandbox.
|
|
95
156
|
|
|
@@ -117,8 +178,11 @@ class Mounts:
|
|
|
117
178
|
def _mount_from_dict(data: dict) -> MountInfo:
|
|
118
179
|
return MountInfo(
|
|
119
180
|
path=data["path"],
|
|
120
|
-
|
|
121
|
-
backend=data.get("backend", ""),
|
|
181
|
+
driver=data.get("driver", "rclone"),
|
|
122
182
|
read_only=data.get("readOnly", True),
|
|
183
|
+
remote=data.get("remote", ""),
|
|
184
|
+
backend=data.get("backend", ""),
|
|
123
185
|
rclone_version=data.get("rcloneVersion", ""),
|
|
186
|
+
command=data.get("command", []) or [],
|
|
187
|
+
env=data.get("env", {}) or {},
|
|
124
188
|
)
|
|
@@ -602,18 +602,41 @@ class Sandbox:
|
|
|
602
602
|
pty_key = self._token or self._api_key
|
|
603
603
|
return Pty(self._ops_client, self.sandbox_id, pty_url, pty_key)
|
|
604
604
|
|
|
605
|
-
async def create_checkpoint(
|
|
605
|
+
async def create_checkpoint(
|
|
606
|
+
self,
|
|
607
|
+
name: str,
|
|
608
|
+
kind: str | None = None,
|
|
609
|
+
promote_to_full: bool | None = None,
|
|
610
|
+
retention_policy: dict | None = None,
|
|
611
|
+
) -> dict:
|
|
606
612
|
"""Create a named checkpoint of the running sandbox.
|
|
607
613
|
|
|
608
614
|
Args:
|
|
609
615
|
name: A unique name for this checkpoint.
|
|
616
|
+
kind: Optional checkpoint kind. Use "full" for disk, memory, and
|
|
617
|
+
device state, or "disk_only" for a disk-only checkpoint that
|
|
618
|
+
automatically prepares a full artifact for faster future forks.
|
|
619
|
+
promote_to_full: Advanced override for disk-only checkpoints.
|
|
620
|
+
Defaults to server behavior.
|
|
621
|
+
retention_policy: Optional policy such as
|
|
622
|
+
{"mode": "delete_oldest", "maxCount": 10}. When set, the
|
|
623
|
+
server may delete older eligible checkpoints before creating
|
|
624
|
+
this one.
|
|
610
625
|
|
|
611
626
|
Returns:
|
|
612
627
|
Checkpoint info dict with id, sandboxId, name, status, etc.
|
|
613
628
|
"""
|
|
629
|
+
body = {"name": name}
|
|
630
|
+
if kind is not None:
|
|
631
|
+
body["kind"] = kind
|
|
632
|
+
if promote_to_full is not None:
|
|
633
|
+
body["promoteToFull"] = promote_to_full
|
|
634
|
+
if retention_policy is not None:
|
|
635
|
+
body["retentionPolicy"] = retention_policy
|
|
636
|
+
|
|
614
637
|
resp = await self._client.post(
|
|
615
638
|
f"/sandboxes/{self.sandbox_id}/checkpoints",
|
|
616
|
-
json=
|
|
639
|
+
json=body,
|
|
617
640
|
)
|
|
618
641
|
resp.raise_for_status()
|
|
619
642
|
return resp.json()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|