chalkcompute 0.1.0__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.
Files changed (33) hide show
  1. chalkcompute-0.1.0/.gitignore +3 -0
  2. chalkcompute-0.1.0/PKG-INFO +365 -0
  3. chalkcompute-0.1.0/README.md +355 -0
  4. chalkcompute-0.1.0/chalkcompute/__init__.py +52 -0
  5. chalkcompute-0.1.0/chalkcompute/_proto/__init__.py +0 -0
  6. chalkcompute-0.1.0/chalkcompute/_proto/auth/__init__.py +0 -0
  7. chalkcompute-0.1.0/chalkcompute/_proto/auth/v1/__init__.py +0 -0
  8. chalkcompute-0.1.0/chalkcompute/_proto/auth/v1/permissions_pb2.py +107 -0
  9. chalkcompute-0.1.0/chalkcompute/_proto/auth/v1/permissions_pb2.pyi +84 -0
  10. chalkcompute-0.1.0/chalkcompute/_proto/container/__init__.py +0 -0
  11. chalkcompute-0.1.0/chalkcompute/_proto/container/v1/__init__.py +0 -0
  12. chalkcompute-0.1.0/chalkcompute/_proto/container/v1/service_pb2.py +96 -0
  13. chalkcompute-0.1.0/chalkcompute/_proto/container/v1/service_pb2.pyi +222 -0
  14. chalkcompute-0.1.0/chalkcompute/_proto/container/v1/service_pb2_grpc.py +318 -0
  15. chalkcompute-0.1.0/chalkcompute/_proto/sandbox/__init__.py +0 -0
  16. chalkcompute-0.1.0/chalkcompute/_proto/sandbox/v1/__init__.py +0 -0
  17. chalkcompute-0.1.0/chalkcompute/_proto/sandbox/v1/service_pb2.py +114 -0
  18. chalkcompute-0.1.0/chalkcompute/_proto/sandbox/v1/service_pb2.pyi +305 -0
  19. chalkcompute-0.1.0/chalkcompute/_proto/sandbox/v1/service_pb2_grpc.py +394 -0
  20. chalkcompute-0.1.0/chalkcompute/_proto/utils/__init__.py +0 -0
  21. chalkcompute-0.1.0/chalkcompute/_proto/utils/v1/__init__.py +0 -0
  22. chalkcompute-0.1.0/chalkcompute/_proto/utils/v1/encoding_pb2.py +41 -0
  23. chalkcompute-0.1.0/chalkcompute/_proto/utils/v1/encoding_pb2.pyi +23 -0
  24. chalkcompute-0.1.0/chalkcompute/_proto/volume/__init__.py +0 -0
  25. chalkcompute-0.1.0/chalkcompute/_proto/volume/v1/__init__.py +0 -0
  26. chalkcompute-0.1.0/chalkcompute/_proto/volume/v1/volume_pb2.py +91 -0
  27. chalkcompute-0.1.0/chalkcompute/_proto/volume/v1/volume_pb2.pyi +127 -0
  28. chalkcompute-0.1.0/chalkcompute/_proto/volume/v1/volume_pb2_grpc.py +401 -0
  29. chalkcompute-0.1.0/chalkcompute/container.py +562 -0
  30. chalkcompute-0.1.0/chalkcompute/image.py +320 -0
  31. chalkcompute-0.1.0/chalkcompute/sandbox.py +596 -0
  32. chalkcompute-0.1.0/chalkcompute/volume.py +436 -0
  33. chalkcompute-0.1.0/pyproject.toml +33 -0
@@ -0,0 +1,3 @@
1
+ .env
2
+ .env.enc
3
+ __pycache__/
@@ -0,0 +1,365 @@
1
+ Metadata-Version: 2.4
2
+ Name: chalkcompute
3
+ Version: 0.1.0
4
+ Summary: SDK for Chalk sandboxes, containers, and volumes
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: grpcio>=1.60.0
7
+ Requires-Dist: httpx>=0.25.0
8
+ Requires-Dist: protobuf>=4.25.0
9
+ Description-Content-Type: text/markdown
10
+
11
+ # Chalk Sandbox SDK
12
+
13
+ Python SDK for the Chalk Sandbox gRPC service. Create sandboxes, execute commands, and stream output over bidirectional gRPC streams.
14
+
15
+ ## Install
16
+
17
+ ```
18
+ pip install grpcio protobuf
19
+ ```
20
+
21
+ ## Quick start
22
+
23
+ ```python
24
+ from sandbox import SandboxClient
25
+
26
+ client = SandboxClient("localhost:50051")
27
+
28
+ # Create a sandbox from a pre-built image
29
+ sandbox = client.create(image="ubuntu:latest", name="my-sandbox")
30
+
31
+ # Run a command
32
+ result = sandbox.exec("echo", "hello world")
33
+ print(result.stdout_text) # "hello world"
34
+ print(result.exit_code) # 0
35
+
36
+ # Clean up
37
+ sandbox.terminate()
38
+ ```
39
+
40
+ ## Declarative images
41
+
42
+ Build custom container images with a fluent API instead of writing Dockerfiles.
43
+ The image spec is serialized as protobuf and transmitted to the sandbox service,
44
+ which builds and caches the image before starting the container.
45
+
46
+ ```python
47
+ from sandbox import SandboxClient
48
+ from image import Image
49
+
50
+ client = SandboxClient("localhost:50051")
51
+
52
+ # Build a data-science image declaratively
53
+ img = (
54
+ Image.debian_slim("3.12")
55
+ .pip_install(["pandas", "numpy", "scikit-learn"])
56
+ .run_commands(
57
+ "apt-get update && apt-get install -y git curl",
58
+ )
59
+ .workdir("/home/user/app")
60
+ .env({"PYTHONDONTWRITEBYTECODE": "1"})
61
+ )
62
+
63
+ sandbox = client.create(image=img, name="data-science")
64
+ result = sandbox.exec("python", "-c", "import pandas; print(pandas.__version__)")
65
+ print(result.stdout_text)
66
+ ```
67
+
68
+ ### Base images
69
+
70
+ ```python
71
+ # Arbitrary base image
72
+ img = Image.base("node:22-slim")
73
+
74
+ # Convenience: python + debian slim
75
+ img = Image.debian_slim("3.12") # python:3.12-slim-bookworm
76
+
77
+ # From an existing Dockerfile (contents are inlined, so you can chain more steps)
78
+ img = Image.from_dockerfile("Dockerfile").pip_install(["extra-dep"])
79
+ ```
80
+
81
+ ### Build steps
82
+
83
+ ```python
84
+ img = (
85
+ Image.debian_slim("3.12")
86
+ # Install Python packages
87
+ .pip_install(["requests>=2.28", "flask"])
88
+
89
+ # Install from a requirements.txt (read locally, inlined into the spec)
90
+ .pip_install_from_requirements("requirements.txt")
91
+
92
+ # Run shell commands (each becomes a Docker RUN layer)
93
+ .run_commands(
94
+ "apt-get update && apt-get install -y git",
95
+ "mkdir -p /app/data",
96
+ )
97
+
98
+ # Add local files into the image
99
+ .add_local_file("config.yaml", "/app/config.yaml")
100
+ .add_local_file("entrypoint.sh", "/app/entrypoint.sh", mode=0o755)
101
+ .add_local_dir("src", "/app/src")
102
+
103
+ # Raw Dockerfile instructions
104
+ .dockerfile_commands(["EXPOSE 8080", "HEALTHCHECK CMD curl -f http://localhost:8080/"])
105
+
106
+ # Image-level configuration
107
+ .workdir("/app")
108
+ .env({"FLASK_APP": "app:create_app"})
109
+ .entrypoint(["/app/entrypoint.sh"])
110
+ .cmd(["serve"])
111
+ )
112
+ ```
113
+
114
+ ### Immutable composition
115
+
116
+ Each builder method returns a new `Image`, so intermediate images can be shared:
117
+
118
+ ```python
119
+ base = Image.debian_slim("3.12").pip_install(["requests"])
120
+
121
+ # Two different images that share the same base
122
+ api_image = base.pip_install(["flask"]).workdir("/api")
123
+ worker_image = base.pip_install(["celery"]).workdir("/worker")
124
+
125
+ api_sandbox = client.create(image=api_image, name="api")
126
+ worker_sandbox = client.create(image=worker_image, name="worker")
127
+ ```
128
+
129
+ ## Connecting
130
+
131
+ ```python
132
+ from sandbox import SandboxClient
133
+ import grpc
134
+
135
+ # Insecure (local dev)
136
+ client = SandboxClient("localhost:50051")
137
+
138
+ # With TLS
139
+ creds = grpc.ssl_channel_credentials()
140
+ client = SandboxClient("sandbox.example.com:443", credentials=creds)
141
+
142
+ # As a context manager
143
+ with SandboxClient("localhost:50051") as client:
144
+ ...
145
+ ```
146
+
147
+ ## Sandbox lifecycle
148
+
149
+ ```python
150
+ # Create with resource limits
151
+ sandbox = client.create(
152
+ image="ubuntu:latest",
153
+ name="dev-sandbox",
154
+ cpu="2",
155
+ memory="4Gi",
156
+ env={"DEBIAN_FRONTEND": "noninteractive"},
157
+ )
158
+
159
+ # List all sandboxes
160
+ for info in client.list():
161
+ print(f"{info.id} {info.state} {info.name}")
162
+
163
+ # Get a handle to an existing sandbox by ID
164
+ sandbox = client.get(id="550e8400-e29b-41d4-a716-446655440000")
165
+
166
+ # Fetch info from server
167
+ print(sandbox.info.state)
168
+ sandbox.refresh() # force re-fetch
169
+
170
+ # Terminate
171
+ sandbox.terminate()
172
+ sandbox.terminate(grace_period_seconds=30)
173
+ ```
174
+
175
+ ## Executing commands
176
+
177
+ ### Run and wait
178
+
179
+ ```python
180
+ result = sandbox.exec("ls", "-la", "/tmp")
181
+ for line in result.stdout:
182
+ print(line)
183
+ for line in result.stderr:
184
+ print(f"ERR: {line}")
185
+ print(f"exit code: {result.exit_code}")
186
+
187
+ # Or get the full text at once
188
+ print(result.stdout_text)
189
+ print(result.stderr_text)
190
+ ```
191
+
192
+ ### Stream output in real time
193
+
194
+ ```python
195
+ for event in sandbox.exec_stream("make", "build", workdir="/app"):
196
+ if event.stdout:
197
+ print(event.stdout, end="")
198
+ if event.stderr:
199
+ print(event.stderr, end="", file=sys.stderr)
200
+ if event.is_exited:
201
+ print(f"\nDone: exit code {event.exit_code}")
202
+ ```
203
+
204
+ ### Interactive processes (stdin + signals)
205
+
206
+ ```python
207
+ process = sandbox.exec_start("bash")
208
+
209
+ process.write_stdin("echo hello\n")
210
+ process.write_stdin("exit\n")
211
+ process.close_stdin()
212
+
213
+ for event in process.output():
214
+ if event.stdout:
215
+ print(event.stdout, end="")
216
+ ```
217
+
218
+ Send signals to running processes:
219
+
220
+ ```python
221
+ import signal
222
+
223
+ process = sandbox.exec_start("sleep", "300")
224
+ process.send_signal(signal.SIGTERM)
225
+ result = process.wait()
226
+ ```
227
+
228
+ ### Options
229
+
230
+ All exec methods accept the same keyword arguments:
231
+
232
+ ```python
233
+ result = sandbox.exec(
234
+ "python", "train.py",
235
+ workdir="/app", # working directory
236
+ timeout_secs=3600, # kill after 1 hour
237
+ env={"CUDA_VISIBLE_DEVICES": "0"}, # environment variables
238
+ )
239
+ ```
240
+
241
+ ## Examples
242
+
243
+ ### Clone a GitHub repo into a sandbox
244
+
245
+ ```python
246
+ from sandbox import SandboxClient
247
+
248
+ client = SandboxClient("localhost:50051")
249
+ sandbox = client.create(image="ubuntu:latest", name="repo-sandbox")
250
+
251
+ # Install git
252
+ sandbox.exec("apt-get", "update")
253
+ sandbox.exec("apt-get", "install", "-y", "git")
254
+
255
+ # Clone
256
+ result = sandbox.exec(
257
+ "git", "clone", "https://github.com/chalk-ai/chalk.git", "/workspace/chalk"
258
+ )
259
+ if result.exit_code != 0:
260
+ print(f"Clone failed: {result.stderr_text}")
261
+ else:
262
+ # List what we got
263
+ result = sandbox.exec("ls", "-la", "/workspace/chalk")
264
+ for line in result.stdout:
265
+ print(line)
266
+ ```
267
+
268
+ ### Spawn an OpenCode agent in a sandbox
269
+
270
+ [OpenCode](https://github.com/opencode-ai/opencode) is a terminal-based AI coding agent. You can run it inside a sandbox to give it an isolated environment to work in.
271
+
272
+ ```python
273
+ from sandbox import SandboxClient
274
+
275
+ client = SandboxClient("localhost:50051")
276
+ sandbox = client.create(
277
+ image="ubuntu:latest",
278
+ name="opencode-agent",
279
+ cpu="2",
280
+ memory="4Gi",
281
+ env={
282
+ "ANTHROPIC_API_KEY": "sk-ant-...",
283
+ },
284
+ )
285
+
286
+ # Install dependencies
287
+ sandbox.exec("apt-get", "update")
288
+ sandbox.exec("apt-get", "install", "-y", "git", "curl", "build-essential")
289
+
290
+ # Install Go (opencode is a Go binary)
291
+ sandbox.exec("bash", "-c", "curl -fsSL https://go.dev/dl/go1.24.1.linux-amd64.tar.gz | tar -C /usr/local -xz")
292
+ sandbox.exec("bash", "-c", "echo 'export PATH=$PATH:/usr/local/go/bin:/root/go/bin' >> /root/.bashrc")
293
+
294
+ # Install opencode
295
+ sandbox.exec("bash", "-c", "export PATH=$PATH:/usr/local/go/bin:/root/go/bin && go install github.com/opencode-ai/opencode@latest")
296
+
297
+ # Clone a repo to work on
298
+ sandbox.exec("git", "clone", "https://github.com/your-org/your-repo.git", "/workspace/repo")
299
+
300
+ # Run opencode non-interactively with a prompt
301
+ result = sandbox.exec(
302
+ "bash", "-c",
303
+ "export PATH=$PATH:/usr/local/go/bin:/root/go/bin && cd /workspace/repo && opencode -p 'fix the failing tests in pkg/auth'",
304
+ timeout_secs=600,
305
+ )
306
+ print(result.stdout_text)
307
+
308
+ # Or run it interactively and feed it commands
309
+ process = sandbox.exec_start(
310
+ "bash", "-c",
311
+ "export PATH=$PATH:/usr/local/go/bin:/root/go/bin && cd /workspace/repo && opencode",
312
+ )
313
+
314
+ # Stream its output
315
+ for event in process.output():
316
+ if event.stdout:
317
+ print(event.stdout, end="")
318
+ if event.stderr:
319
+ print(event.stderr, end="", file=sys.stderr)
320
+ if event.is_exited:
321
+ break
322
+ ```
323
+
324
+ ### Long-running build with real-time output
325
+
326
+ ```python
327
+ sandbox = client.create(image="node:22", name="build")
328
+
329
+ sandbox.exec("git", "clone", "https://github.com/your-org/frontend.git", "/app")
330
+ sandbox.exec("npm", "install", workdir="/app")
331
+
332
+ # Stream the build output as it happens
333
+ for event in sandbox.exec_stream("npm", "run", "build", workdir="/app"):
334
+ if event.stdout:
335
+ print(event.stdout, end="")
336
+ if event.stderr:
337
+ print(event.stderr, end="", file=sys.stderr)
338
+ if event.is_exited and event.exit_code != 0:
339
+ print(f"Build failed with exit code {event.exit_code}")
340
+
341
+ sandbox.terminate()
342
+ ```
343
+
344
+ ## CLI tools
345
+
346
+ ### `sandbox_exec.py` - Run a command
347
+
348
+ ```bash
349
+ python sandbox_exec.py --target localhost:50051 --sandbox-id <id> --exec "ls -la"
350
+ ```
351
+
352
+ ### `sandbox_stdout.py` - Interactive shell
353
+
354
+ ```bash
355
+ echo "echo hello" | python sandbox_stdout.py --target localhost:50051 --sandbox-id <id> --exec "bash"
356
+ ```
357
+
358
+ ## Regenerating proto stubs
359
+
360
+ If the proto definition changes, regenerate the Python stubs:
361
+
362
+ ```bash
363
+ pip install grpcio-tools
364
+ ./generate.sh
365
+ ```