plato-sdk-v2 2.3.10__py3-none-any.whl → 2.4.0__py3-none-any.whl

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.
@@ -0,0 +1,27 @@
1
+ # World runner image for plato chronos dev
2
+ # Includes git, docker CLI, and Python dependencies
3
+
4
+ FROM python:3.12-slim
5
+
6
+ # Install git and docker CLI
7
+ RUN apt-get update && apt-get install -y --no-install-recommends \
8
+ git \
9
+ curl \
10
+ ca-certificates \
11
+ && curl -fsSL https://get.docker.com -o get-docker.sh \
12
+ && sh get-docker.sh \
13
+ && rm get-docker.sh \
14
+ && apt-get clean \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ # Install uv for fast package installation
18
+ RUN pip install --no-cache-dir uv
19
+
20
+ WORKDIR /world
21
+
22
+ # Entry point expects:
23
+ # - /world mounted with world source
24
+ # - /python-sdk mounted with plato SDK source (optional, for dev)
25
+ # - /config.json mounted with config
26
+ # - WORLD_NAME env var set
27
+ CMD ["bash", "-c", "if [ -d /python-sdk ]; then uv pip install --system /python-sdk; fi && uv pip install --system . 2>/dev/null || pip install -q . && plato-world-runner run --world $WORLD_NAME --config /config.json"]
plato/worlds/base.py CHANGED
@@ -25,6 +25,7 @@ from plato.agents.otel import (
25
25
  init_tracing,
26
26
  shutdown_tracing,
27
27
  )
28
+ from plato.agents.runner import run_agent as _run_agent_raw
28
29
 
29
30
  logger = logging.getLogger(__name__)
30
31
 
@@ -126,6 +127,7 @@ class BaseWorld(ABC, Generic[ConfigT]):
126
127
  self.plato_session = None
127
128
  self._current_step_id: str | None = None
128
129
  self._session_id: str | None = None
130
+ self._agent_containers: list[str] = [] # Track spawned agent containers for cleanup
129
131
 
130
132
  @classmethod
131
133
  def get_config_class(cls) -> type[RunConfig]:
@@ -185,7 +187,70 @@ class BaseWorld(ABC, Generic[ConfigT]):
185
187
 
186
188
  async def close(self) -> None:
187
189
  """Cleanup resources. Called after run completes."""
188
- pass
190
+ await self._cleanup_agent_containers()
191
+
192
+ async def _cleanup_agent_containers(self) -> None:
193
+ """Stop any agent containers spawned by this world."""
194
+ import asyncio
195
+
196
+ if not self._agent_containers:
197
+ return
198
+
199
+ self.logger.info(f"Stopping {len(self._agent_containers)} agent container(s)...")
200
+ for container_name in self._agent_containers:
201
+ try:
202
+ proc = await asyncio.create_subprocess_exec(
203
+ "docker",
204
+ "stop",
205
+ container_name,
206
+ stdout=asyncio.subprocess.DEVNULL,
207
+ stderr=asyncio.subprocess.DEVNULL,
208
+ )
209
+ await proc.wait()
210
+ self.logger.debug(f"Stopped container: {container_name}")
211
+ except Exception as e:
212
+ self.logger.warning(f"Failed to stop container {container_name}: {e}")
213
+ self._agent_containers.clear()
214
+ self.logger.info("Agent containers stopped")
215
+
216
+ async def run_agent(
217
+ self,
218
+ image: str,
219
+ config: dict,
220
+ secrets: dict[str, str],
221
+ instruction: str,
222
+ workspace: str | None = None,
223
+ logs_dir: str | None = None,
224
+ pull: bool = True,
225
+ ) -> str:
226
+ """Run an agent in a Docker container, tracking the container for cleanup.
227
+
228
+ This is a wrapper around plato.agents.runner.run_agent that automatically
229
+ tracks spawned containers so they can be cleaned up when the world closes.
230
+
231
+ Args:
232
+ image: Docker image URI
233
+ config: Agent configuration dict
234
+ secrets: Secret values (API keys, etc.)
235
+ instruction: Task instruction for the agent
236
+ workspace: Docker volume name for workspace
237
+ logs_dir: Ignored (kept for backwards compatibility)
238
+ pull: Whether to pull the image first
239
+
240
+ Returns:
241
+ The container name that was created
242
+ """
243
+ container_name = await _run_agent_raw(
244
+ image=image,
245
+ config=config,
246
+ secrets=secrets,
247
+ instruction=instruction,
248
+ workspace=workspace,
249
+ logs_dir=logs_dir,
250
+ pull=pull,
251
+ )
252
+ self._agent_containers.append(container_name)
253
+ return container_name
189
254
 
190
255
  async def _connect_plato_session(self) -> None:
191
256
  """Connect to Plato session from config.
plato/worlds/runner.py CHANGED
@@ -3,15 +3,13 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
- import json
7
6
  import logging
8
- import os
9
7
  from pathlib import Path
10
8
  from typing import Annotated
11
9
 
12
10
  import typer
13
11
 
14
- from plato.worlds.config import EnvConfig, RunConfig
12
+ from plato.worlds.config import RunConfig
15
13
 
16
14
  app = typer.Typer(
17
15
  name="plato-world-runner",
@@ -135,461 +133,6 @@ def list_worlds(
135
133
  typer.echo(f" {name} (v{version}): {desc}")
136
134
 
137
135
 
138
- def _get_docker_platform() -> str:
139
- """Get the appropriate Docker platform for the current system.
140
-
141
- Returns:
142
- Docker platform string (e.g., "linux/arm64" or "linux/amd64")
143
- """
144
- import platform as plat
145
-
146
- system = plat.system()
147
- machine = plat.machine().lower()
148
-
149
- # On macOS with Apple Silicon (arm64/aarch64), use linux/arm64
150
- if system == "Darwin" and machine in ("arm64", "aarch64"):
151
- return "linux/arm64"
152
- # On Linux ARM
153
- elif system == "Linux" and machine in ("arm64", "aarch64"):
154
- return "linux/arm64"
155
- # Default to amd64 for x86_64 or other architectures
156
- else:
157
- return "linux/amd64"
158
-
159
-
160
- async def _build_agent_image(
161
- agent_name: str,
162
- agents_dir: Path,
163
- plato_client_root: Path | None = None,
164
- ) -> bool:
165
- """Build a local agent Docker image.
166
-
167
- Args:
168
- agent_name: Name of the agent (e.g., "openhands")
169
- agents_dir: Directory containing agent subdirectories
170
- plato_client_root: Root of plato-client repo (for dev builds), or None for prod builds
171
-
172
- Returns:
173
- True if build succeeded, False otherwise
174
- """
175
- import subprocess
176
-
177
- # Resolve paths to absolute
178
- agents_dir = agents_dir.expanduser().resolve()
179
- agent_path = agents_dir / agent_name
180
- dockerfile_path = agent_path / "Dockerfile"
181
-
182
- if not dockerfile_path.exists():
183
- logger.warning(f"No Dockerfile found for agent '{agent_name}' at {dockerfile_path}")
184
- return False
185
-
186
- image_tag = f"{agent_name}:latest"
187
-
188
- # Determine build context and target
189
- if plato_client_root:
190
- plato_client_root = plato_client_root.expanduser().resolve()
191
-
192
- if plato_client_root and plato_client_root.exists():
193
- # Dev build from plato-client root (includes local python-sdk)
194
- build_context = str(plato_client_root)
195
- dockerfile_abs = str(dockerfile_path)
196
- target = "dev"
197
- logger.info(f"Building {image_tag} (dev mode from {build_context})...")
198
- else:
199
- # Prod build from agent directory
200
- build_context = str(agent_path)
201
- dockerfile_abs = str(dockerfile_path)
202
- target = "prod"
203
- logger.info(f"Building {image_tag} (prod mode from {build_context})...")
204
-
205
- # Detect platform for ARM Mac support
206
- docker_platform = _get_docker_platform()
207
- logger.info(f"Building for platform: {docker_platform}")
208
-
209
- cmd = [
210
- "docker",
211
- "build",
212
- "--platform",
213
- docker_platform,
214
- "--build-arg",
215
- f"PLATFORM={docker_platform}",
216
- "--target",
217
- target,
218
- "-t",
219
- image_tag,
220
- "-f",
221
- dockerfile_abs,
222
- ]
223
-
224
- cmd.append(build_context)
225
-
226
- logger.debug(f"Build command: {' '.join(cmd)}")
227
-
228
- result = subprocess.run(cmd, capture_output=True, text=True)
229
-
230
- if result.returncode != 0:
231
- logger.error(f"Failed to build {image_tag}:\n{result.stderr}")
232
- return False
233
-
234
- logger.info(f"Successfully built {image_tag}")
235
- return True
236
-
237
-
238
- def _extract_agent_images_from_config(config_data: dict) -> list[str]:
239
- """Extract agent image names from config data.
240
-
241
- Args:
242
- config_data: Raw config dictionary
243
-
244
- Returns:
245
- List of image names (without tags) that are local (not from a registry)
246
- """
247
- images = []
248
-
249
- # Check agents section
250
- agents = config_data.get("agents", {})
251
- for agent_config in agents.values():
252
- if isinstance(agent_config, dict):
253
- image = agent_config.get("image", "")
254
- # Only include local images (no registry prefix like ghcr.io/)
255
- if image and "/" not in image.split(":")[0]:
256
- # Extract name without tag
257
- name = image.split(":")[0]
258
- if name not in images:
259
- images.append(name)
260
-
261
- # Also check direct coder/verifier fields
262
- for field in ["coder", "verifier"]:
263
- agent_config = config_data.get(field, {})
264
- if isinstance(agent_config, dict):
265
- image = agent_config.get("image", "")
266
- if image and "/" not in image.split(":")[0]:
267
- name = image.split(":")[0]
268
- if name not in images:
269
- images.append(name)
270
-
271
- return images
272
-
273
-
274
- class ChronosSessionInfo:
275
- """Info returned from Chronos session creation."""
276
-
277
- def __init__(self, public_id: str, otel_url: str, upload_url: str):
278
- self.public_id = public_id
279
- self.otel_url = otel_url
280
- self.upload_url = upload_url
281
-
282
-
283
- async def _create_chronos_session(
284
- chronos_url: str,
285
- api_key: str,
286
- world_name: str,
287
- world_config: dict,
288
- plato_session_id: str | None = None,
289
- ) -> ChronosSessionInfo:
290
- """Create a session in Chronos.
291
-
292
- Args:
293
- chronos_url: Chronos base URL (e.g., https://chronos.plato.so)
294
- api_key: Plato API key for authentication
295
- world_name: Name of the world being run
296
- world_config: World configuration dict
297
- plato_session_id: Optional Plato session ID if already created
298
-
299
- Returns:
300
- ChronosSessionInfo with session_id, otel_url, and upload_url
301
- """
302
- import httpx
303
-
304
- url = f"{chronos_url.rstrip('/')}/api/sessions"
305
-
306
- async with httpx.AsyncClient(timeout=30.0) as client:
307
- response = await client.post(
308
- url,
309
- json={
310
- "world_name": world_name,
311
- "world_config": world_config,
312
- "plato_session_id": plato_session_id,
313
- },
314
- headers={"x-api-key": api_key},
315
- )
316
- response.raise_for_status()
317
- data = response.json()
318
-
319
- print(f"[Runner] Chronos session response: {data}")
320
- return ChronosSessionInfo(
321
- public_id=data["public_id"],
322
- otel_url=data["otel_url"],
323
- upload_url=data["upload_url"],
324
- )
325
-
326
-
327
- async def _close_chronos_session(
328
- chronos_url: str,
329
- api_key: str,
330
- session_id: str,
331
- ) -> None:
332
- """Close a Chronos session.
333
-
334
- Args:
335
- chronos_url: Chronos base URL
336
- api_key: Plato API key for authentication
337
- session_id: Chronos session public ID to close
338
- """
339
- import httpx
340
-
341
- url = f"{chronos_url.rstrip('/')}/api/sessions/{session_id}/close"
342
-
343
- try:
344
- async with httpx.AsyncClient(timeout=30.0) as client:
345
- response = await client.post(
346
- url,
347
- headers={"x-api-key": api_key},
348
- )
349
- response.raise_for_status()
350
- logger.info(f"Closed Chronos session: {session_id}")
351
- except Exception as e:
352
- logger.warning(f"Failed to close Chronos session: {e}")
353
-
354
-
355
- async def _run_dev(
356
- world_name: str,
357
- config_path: Path,
358
- env_timeout: int = 7200,
359
- agents_dir: Path | None = None,
360
- ) -> None:
361
- """Run a world locally with automatic environment creation.
362
-
363
- This mimics what Chronos does but runs locally for debugging:
364
- 1. Load and parse the config
365
- 2. Build local agent images if --agents-dir is provided
366
- 3. Create Plato session with all environments
367
- 4. Create Chronos session for OTel traces
368
- 5. Run the world with the session attached
369
-
370
- Requires environment variables:
371
- CHRONOS_URL: Chronos base URL (e.g., https://chronos.plato.so)
372
- PLATO_API_KEY: API key for Plato and Chronos authentication
373
-
374
- Args:
375
- world_name: Name of the world to run
376
- config_path: Path to the config JSON file
377
- env_timeout: Timeout for environment creation (seconds)
378
- agents_dir: Optional directory containing agent source code
379
- """
380
- from plato.v2 import AsyncPlato
381
- from plato.worlds.base import get_world
382
-
383
- # Get required env vars
384
- chronos_url = os.environ.get("CHRONOS_URL")
385
- api_key = os.environ.get("PLATO_API_KEY")
386
-
387
- if not chronos_url:
388
- raise ValueError("CHRONOS_URL environment variable is required")
389
- if not api_key:
390
- raise ValueError("PLATO_API_KEY environment variable is required")
391
-
392
- discover_worlds()
393
-
394
- world_cls = get_world(world_name)
395
- if world_cls is None:
396
- from plato.worlds.base import get_registered_worlds
397
-
398
- available = list(get_registered_worlds().keys())
399
- raise ValueError(f"World '{world_name}' not found. Available: {available}")
400
-
401
- # Load config
402
- config_class = world_cls.get_config_class()
403
- with open(config_path) as f:
404
- config_data = json.load(f)
405
-
406
- # Parse the config to get typed access
407
- run_config = config_class._from_dict(config_data.copy())
408
-
409
- # Build local agent images if agents_dir is provided
410
- if agents_dir:
411
- # Resolve agents_dir to absolute path
412
- agents_dir = agents_dir.expanduser().resolve()
413
- agent_images = _extract_agent_images_from_config(config_data)
414
- if agent_images:
415
- logger.info(f"Building local agent images: {agent_images}")
416
- # Determine if we're in a plato-client repo for dev builds
417
- # (agents_dir is something like /path/to/plato-client/agents)
418
- plato_client_root = agents_dir.parent if agents_dir.name == "agents" else None
419
- for agent_name in agent_images:
420
- success = await _build_agent_image(agent_name, agents_dir, plato_client_root)
421
- if not success:
422
- raise RuntimeError(f"Failed to build agent image: {agent_name}")
423
- else:
424
- logger.info("No local agent images found in config")
425
-
426
- # Get environment configs from the parsed config
427
- env_configs: list[EnvConfig] = run_config.get_envs()
428
-
429
- # Create Plato client
430
- plato = AsyncPlato()
431
- session = None
432
- plato_session_id: str | None = None
433
- chronos_session_id: str | None = None
434
-
435
- try:
436
- if env_configs:
437
- logger.info(f"Creating {len(env_configs)} environments...")
438
- session = await plato.sessions.create(envs=env_configs, timeout=env_timeout)
439
- plato_session_id = session.session_id
440
- logger.info(f"Created Plato session: {plato_session_id}")
441
- logger.info(f"Environments: {[e.alias for e in session.envs]}")
442
-
443
- # Serialize and add to config
444
- serialized = session.dump()
445
- run_config.plato_session = serialized
446
- else:
447
- logger.info("No environments defined for this world")
448
-
449
- # Create Chronos session (after Plato session so we can link them)
450
- logger.info(f"Creating Chronos session at {chronos_url}...")
451
- chronos_session = await _create_chronos_session(
452
- chronos_url=chronos_url,
453
- api_key=api_key,
454
- world_name=world_name,
455
- world_config=config_data,
456
- plato_session_id=plato_session_id,
457
- )
458
- chronos_session_id = chronos_session.public_id
459
- logger.info(f"Created Chronos session: {chronos_session_id}")
460
- logger.info(f"View at: {chronos_url}/sessions/{chronos_session_id}")
461
-
462
- # Update run_config with session info from Chronos
463
- run_config.session_id = chronos_session_id
464
- # Use base chronos URL for OTEL endpoint (more reliable than session-provided URL)
465
- run_config.otel_url = f"{chronos_url.rstrip('/')}/api/otel"
466
- run_config.upload_url = chronos_session.upload_url
467
-
468
- # Run the world
469
- logger.info(f"Starting world '{world_name}'...")
470
- world_instance = world_cls()
471
- await world_instance.run(run_config)
472
-
473
- finally:
474
- # Cleanup
475
- if session:
476
- logger.info("Closing Plato session...")
477
- await session.close()
478
- await plato.close()
479
-
480
- # Close Chronos session
481
- if chronos_session_id:
482
- await _close_chronos_session(chronos_url, api_key, chronos_session_id)
483
-
484
-
485
- def _setup_colored_logging(verbose: bool = False) -> None:
486
- """Setup colored logging with filtered noisy loggers."""
487
- log_level = logging.DEBUG if verbose else logging.INFO
488
-
489
- # Define colors for different log levels
490
- colors = {
491
- "DEBUG": "\033[36m", # Cyan
492
- "INFO": "\033[32m", # Green
493
- "WARNING": "\033[33m", # Yellow
494
- "ERROR": "\033[31m", # Red
495
- "CRITICAL": "\033[35m", # Magenta
496
- }
497
- reset = "\033[0m"
498
-
499
- class ColoredFormatter(logging.Formatter):
500
- def format(self, record: logging.LogRecord) -> str:
501
- color = colors.get(record.levelname, "")
502
- record.levelname = f"{color}{record.levelname}{reset}"
503
- record.name = f"\033[34m{record.name}{reset}" # Blue for logger name
504
- return super().format(record)
505
-
506
- # Create handler with colored formatter
507
- handler = logging.StreamHandler()
508
- handler.setFormatter(
509
- ColoredFormatter(
510
- "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
511
- datefmt="%H:%M:%S",
512
- )
513
- )
514
-
515
- # Configure root logger
516
- root_logger = logging.getLogger()
517
- root_logger.setLevel(log_level)
518
- root_logger.handlers = [handler]
519
-
520
- # Silence noisy HTTP loggers
521
- for noisy_logger in ["httpcore", "httpx", "urllib3", "hpack"]:
522
- logging.getLogger(noisy_logger).setLevel(logging.WARNING)
523
-
524
-
525
- @app.command("dev")
526
- def dev(
527
- world: Annotated[str, typer.Option("--world", "-w", help="World name to run")],
528
- config: Annotated[Path, typer.Option("--config", "-c", help="Path to config JSON file")],
529
- env_timeout: Annotated[
530
- int,
531
- typer.Option("--env-timeout", help="Timeout for environment creation (seconds)"),
532
- ] = 7200,
533
- agents_dir: Annotated[
534
- Path | None,
535
- typer.Option(
536
- "--agents-dir",
537
- "-a",
538
- help="Directory containing agent source code (builds local images)",
539
- ),
540
- ] = None,
541
- verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Enable verbose logging")] = False,
542
- ) -> None:
543
- """Run a world locally for development/debugging.
544
-
545
- This creates Plato environments automatically (like Chronos does),
546
- creates a Chronos session for logging, and runs the world.
547
-
548
- Example config.json:
549
- {
550
- "instruction": "Create a git repo and upload files to S3",
551
- "coder": {
552
- "image": "openhands:latest",
553
- "config": {"model_name": "gemini/gemini-3-flash-preview"}
554
- },
555
- "secrets": {
556
- "gemini_api_key": "..."
557
- }
558
- }
559
-
560
- Required environment variables:
561
- CHRONOS_URL: Chronos base URL (e.g., https://chronos.plato.so)
562
- PLATO_API_KEY: API key for Plato and Chronos authentication
563
-
564
- Examples:
565
- # Basic usage
566
- CHRONOS_URL=https://chronos.plato.so plato-world-runner dev -w code -c config.json
567
-
568
- # With local agent builds (from plato-client repo)
569
- plato-world-runner dev -w code -c config.json --agents-dir ~/plato-client/agents
570
- """
571
- # Setup colored logging with filtered noisy loggers
572
- _setup_colored_logging(verbose)
573
-
574
- if not config.exists():
575
- typer.echo(f"Error: Config file not found: {config}", err=True)
576
- raise typer.Exit(1)
577
-
578
- if not os.environ.get("CHRONOS_URL"):
579
- typer.echo("Error: CHRONOS_URL environment variable required", err=True)
580
- raise typer.Exit(1)
581
-
582
- if not os.environ.get("PLATO_API_KEY"):
583
- typer.echo("Error: PLATO_API_KEY environment variable required", err=True)
584
- raise typer.Exit(1)
585
-
586
- try:
587
- asyncio.run(_run_dev(world, config, env_timeout, agents_dir))
588
- except Exception as e:
589
- logger.exception(f"World execution failed: {e}")
590
- raise typer.Exit(1)
591
-
592
-
593
136
  def main() -> None:
594
137
  """CLI entry point."""
595
138
  app()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plato-sdk-v2
3
- Version: 2.3.10
3
+ Version: 2.4.0
4
4
  Summary: Python SDK for the Plato API
5
5
  Author-email: Plato <support@plato.so>
6
6
  License-Expression: MIT
@@ -301,9 +301,9 @@ plato/agents/__init__.py,sha256=Cxc-HUMwRGQ4D1hHnFo9vt2AV5upPRYP4e3y8X6Hzr0,3052
301
301
  plato/agents/artifacts.py,sha256=ljeI0wzsp7Q6uKqMb-k7kTb680Vizs54ohtM-d7zvOg,2929
302
302
  plato/agents/base.py,sha256=vUbPQuNSo6Ka2lIB_ZOXgi4EoAjtAD7GIj9LnNotam0,4577
303
303
  plato/agents/build.py,sha256=CNMbVQFs2_pYit1dA29Davve28Yi4c7TNK9wBB7odrE,1621
304
- plato/agents/config.py,sha256=CmRS6vOAg7JeqX4Hgp_KpA1YWBX_LuMicHm7SBjQEbs,5077
305
- plato/agents/otel.py,sha256=UOEBeMyyfbffsC3tRXlHAydoj9bXwJupA_UeLZ5h97w,8585
306
- plato/agents/runner.py,sha256=ZzEK-O0tCZMBb-y-d2Y8f0BkfPgC-vKkZVOnHfZd7e8,6560
304
+ plato/agents/config.py,sha256=GWXEAbruNVI2q3XIWpQ9vGLK2wGhsFPYA-oekmAlrg8,5392
305
+ plato/agents/otel.py,sha256=A2LkkjBtjSe0eztr9UvYvSUOwfmShCgPg3OUN8nOyIo,9159
306
+ plato/agents/runner.py,sha256=piow_uO1eymreRMU-TX8tcIOn6aurafpjA5ptSR9JPM,9211
307
307
  plato/agents/trajectory.py,sha256=WdiBmua0KvCrNaM3qgPI7-7B4xmSkfbP4oZ_9_8qHzU,10529
308
308
  plato/chronos/__init__.py,sha256=RHMvSrQS_-vkKOyTRuAkp2gKDP1HEuBLDnw8jcZs1Jg,739
309
309
  plato/chronos/client.py,sha256=YcOGtHWERyOD9z8LKt8bRMVL0cEwL2hiAP4qQgdZlUI,5495
@@ -387,14 +387,15 @@ plato/v1/sync_flow_executor.py,sha256=kgvNYOtA9FHeNfP7qb8ZPUIlTsfIss_Z98W8uX5vec
387
387
  plato/v1/sync_sdk.py,sha256=2sedg1QJiSxr1I3kCyfaLAnlAgHlbblc3QQP_47O30k,25697
388
388
  plato/v1/cli/__init__.py,sha256=om4b7PxgsoI7rEwuQelmQkqPdhMVn53_5qEN8kvksYw,105
389
389
  plato/v1/cli/agent.py,sha256=G6TV3blG_BqMDBWS-CG7GwzqoqcJTMsIKQ88jvLXb4k,43745
390
- plato/v1/cli/chronos.py,sha256=g0Re69Ma3bBYZzoQcSABdl61UEWCD7_hqrpzzc4nb1U,7335
390
+ plato/v1/cli/chronos.py,sha256=RDFzzfP800dzeGVSKKilKGQkG5DairYjSI4RgWVNV_I,25483
391
391
  plato/v1/cli/main.py,sha256=iKUz6Mu-4-dgr29qOUmDqBaumOCzNQKZsHAalVtaH0Q,6932
392
- plato/v1/cli/pm.py,sha256=uLM6WszKqxq9Czg1FraDyWb9_INUuHZq63imvRYfRLw,49734
393
- plato/v1/cli/sandbox.py,sha256=N7DIpXsxExtZB47tWKxp-3cV0_gLnI7C-mTKW3tTi8Y,95360
392
+ plato/v1/cli/pm.py,sha256=TIvXBIWFDjr4s1girMMCuvHWQJkjpmsS-igAamddIWE,49746
393
+ plato/v1/cli/sandbox.py,sha256=jhTney-Pr8bGmWIXOjVIMtZJ7v7uIoRnuh3wfG7weRg,98718
394
394
  plato/v1/cli/ssh.py,sha256=enrf7Y01ZeRIyHDEX0Yt7up5zEe7MCvE9u8SP4Oqiz4,6926
395
395
  plato/v1/cli/utils.py,sha256=ba7Crv4OjDmgCv4SeB8UeZDin-iOdQw_3N6fd-g5XVk,4572
396
396
  plato/v1/cli/verify.py,sha256=7QmQwfOOkr8a51f8xfVIr2zif7wGl2E8HOZTbOaIoV0,20671
397
397
  plato/v1/cli/world.py,sha256=yBUadOJs1QYm6Jmx_ACDzogybRq5x4B-BnTvGO_ulQk,9757
398
+ plato/v1/cli/templates/world-runner.Dockerfile,sha256=p59nPCAOUgphSiOpWA3eteRXUWTmZV6n57zn3dWUoYM,932
398
399
  plato/v1/examples/doordash_tasks.py,sha256=8Sz9qx-vTmiOAiCAbrDRvZGsA1qQQBr1KHbxXdjr7OI,23233
399
400
  plato/v1/examples/loadtest.py,sha256=ZsQYNN_fZjE7CbrbVJb4KDc0OLaH7b66iPrEHDhuw0U,5609
400
401
  plato/v1/examples/test_env.py,sha256=8kUISbZyMi0Xh9HK7Il1okKQyz0Iq-vAKWgzC8kqUfU,4513
@@ -459,11 +460,11 @@ plato/v2/utils/models.py,sha256=PwehSSnIRG-tM3tWL1PzZEH77ZHhIAZ9R0UPs6YknbM,1441
459
460
  plato/v2/utils/proxy_tunnel.py,sha256=8ZTd0jCGSfIHMvSv1fgEyacuISWnGPHLPbDglWroTzY,10463
460
461
  plato/worlds/README.md,sha256=XFOkEA3cNNcrWkk-Cxnsl-zn-y0kvUENKQRSqFKpdqw,5479
461
462
  plato/worlds/__init__.py,sha256=ALoou3l5lXvs_YZc5eH6HdMHpvhnpzKWqz__aSC1jFc,2152
462
- plato/worlds/base.py,sha256=kIX02gMQR43ZsZK25vZvCoNkYtICVRMeofNk-im5IRM,28215
463
+ plato/worlds/base.py,sha256=1O3iKilXlr56mUPVovHY_BjM3S8T57FrotF4895qv5Y,30675
463
464
  plato/worlds/build_hook.py,sha256=KSoW0kqa5b7NyZ7MYOw2qsZ_2FkWuz0M3Ru7AKOP7Qw,3486
464
465
  plato/worlds/config.py,sha256=a5frj3mt06rSlT25kE-L8Q2b2MTWkR-8cUoBKpC8tG4,11036
465
- plato/worlds/runner.py,sha256=2H5EV77bTYrMyI7qez0kwxOp9EApQxG19Ob9a_GTdbw,19383
466
- plato_sdk_v2-2.3.10.dist-info/METADATA,sha256=dx-ovSiFY62KWqAM0mR_wfXlILoZLq9qGSBaluEcFJQ,8653
467
- plato_sdk_v2-2.3.10.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
468
- plato_sdk_v2-2.3.10.dist-info/entry_points.txt,sha256=upGMbJCx6YWUTKrPoYvYUYfFCqYr75nHDwhA-45m6p8,136
469
- plato_sdk_v2-2.3.10.dist-info/RECORD,,
466
+ plato/worlds/runner.py,sha256=r9B2BxBae8_dM7y5cJf9xhThp_I1Qvf_tlPq2rs8qC8,4013
467
+ plato_sdk_v2-2.4.0.dist-info/METADATA,sha256=IxvrIY5w2FyH_FYPZLPLlFFS34b3fa25HTAfDc988vo,8652
468
+ plato_sdk_v2-2.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
469
+ plato_sdk_v2-2.4.0.dist-info/entry_points.txt,sha256=upGMbJCx6YWUTKrPoYvYUYfFCqYr75nHDwhA-45m6p8,136
470
+ plato_sdk_v2-2.4.0.dist-info/RECORD,,