simulo 0.27.0__tar.gz → 0.28.1__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 (64) hide show
  1. simulo-0.28.1/PKG-INFO +147 -0
  2. simulo-0.28.1/PYPI.md +92 -0
  3. {simulo-0.27.0 → simulo-0.28.1}/pyproject.toml +40 -4
  4. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/_runner.py +15 -3
  5. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/app.py +37 -1
  6. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/asset_api.py +1 -0
  7. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/bundle.py +2 -1
  8. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/cli.py +192 -51
  9. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/credentials.py +134 -53
  10. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/http.py +115 -2
  11. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/packaging.py +10 -0
  12. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/preflight_api.py +5 -1
  13. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/preflight_render.py +36 -11
  14. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/submit_api.py +45 -28
  15. simulo-0.28.1/src/simulo.egg-info/PKG-INFO +147 -0
  16. {simulo-0.27.0 → simulo-0.28.1}/src/simulo.egg-info/requires.txt +1 -1
  17. simulo-0.27.0/PKG-INFO +0 -143
  18. simulo-0.27.0/PYPI.md +0 -103
  19. simulo-0.27.0/src/simulo.egg-info/PKG-INFO +0 -143
  20. {simulo-0.27.0 → simulo-0.28.1}/MANIFEST.in +0 -0
  21. {simulo-0.27.0 → simulo-0.28.1}/setup.cfg +0 -0
  22. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/__init__.py +0 -0
  23. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/__init__.py +0 -0
  24. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/_entrypoint.py +0 -0
  25. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/_mounts.py +0 -0
  26. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/_secure_downloads.py +0 -0
  27. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/asset.py +0 -0
  28. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/asset_package.py +0 -0
  29. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/asset_pins.py +0 -0
  30. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/builtin_aliases.py +0 -0
  31. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/cancel_api.py +0 -0
  32. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/config.py +0 -0
  33. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/discovery.py +0 -0
  34. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/export_api.py +0 -0
  35. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/export_bundle.py +0 -0
  36. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/facades.py +0 -0
  37. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/identity_api.py +0 -0
  38. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/install_samples.py +0 -0
  39. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/jobs_api.py +0 -0
  40. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/learning.py +0 -0
  41. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/login.py +0 -0
  42. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/mode.py +0 -0
  43. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/outputs.py +0 -0
  44. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/registry.py +0 -0
  45. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/runtime.py +0 -0
  46. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/runtime_display.py +0 -0
  47. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/seed_ref.py +0 -0
  48. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/stub.py +0 -0
  49. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/templates/__init__.py +0 -0
  50. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/templates/inference/app.py.tmpl +0 -0
  51. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/templates/inference/simuloignore.tmpl +0 -0
  52. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/templates/scenario/app.py.tmpl +0 -0
  53. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/templates/scenario/simuloignore.tmpl +0 -0
  54. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/templates/training/app.py.tmpl +0 -0
  55. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/templates/training/simuloignore.tmpl +0 -0
  56. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/view_fragment.py +0 -0
  57. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/view_session_api.py +0 -0
  58. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/_client/volume.py +0 -0
  59. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/callbacks.py +0 -0
  60. {simulo-0.27.0 → simulo-0.28.1}/src/simulo/py.typed +0 -0
  61. {simulo-0.27.0 → simulo-0.28.1}/src/simulo.egg-info/SOURCES.txt +0 -0
  62. {simulo-0.27.0 → simulo-0.28.1}/src/simulo.egg-info/dependency_links.txt +0 -0
  63. {simulo-0.27.0 → simulo-0.28.1}/src/simulo.egg-info/entry_points.txt +0 -0
  64. {simulo-0.27.0 → simulo-0.28.1}/src/simulo.egg-info/top_level.txt +0 -0
simulo-0.28.1/PKG-INFO ADDED
@@ -0,0 +1,147 @@
1
+ Metadata-Version: 2.4
2
+ Name: simulo
3
+ Version: 0.28.1
4
+ Summary: Python SDK and CLI for cloud-native robotics simulation, reinforcement learning, and robot policy training on managed GPUs.
5
+ Author-email: Simulo Team <team@simulo.ai>
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://simulo.ai
8
+ Project-URL: Documentation, https://docs.simulo.ai
9
+ Project-URL: Quickstart, https://docs.simulo.ai/getting-started/quickstart/
10
+ Project-URL: Examples, https://github.com/simulo-org/simulo-samples
11
+ Keywords: simulo,robotics,robotics simulation,robot training,robot learning,reinforcement learning,cloud robotics,cloud simulation,GPU simulation,policy training,physical AI,embodied AI,NVIDIA Isaac Sim,Isaac Lab,OpenUSD,USD,URDF,ONNX,MCAP,Python SDK
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Education
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: BSD License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Topic :: Scientific/Engineering
25
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
26
+ Classifier: Topic :: Scientific/Engineering :: Visualization
27
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
28
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
29
+ Classifier: Topic :: System :: Distributed Computing
30
+ Classifier: Typing :: Typed
31
+ Requires-Python: >=3.11
32
+ Description-Content-Type: text/markdown
33
+ Requires-Dist: simulo-interfaces<0.21,>=0.20.1
34
+ Requires-Dist: mcap<2,>=1.3
35
+ Requires-Dist: defusedxml>=0.7.1
36
+ Requires-Dist: usd-core<27,>=26.5
37
+ Provides-Extra: assets
38
+ Requires-Dist: usd-core<27,>=26.5; extra == "assets"
39
+ Provides-Extra: dev
40
+ Requires-Dist: black==26.5.1; extra == "dev"
41
+ Requires-Dist: isort==8.0.1; extra == "dev"
42
+ Requires-Dist: ruff==0.15.21; extra == "dev"
43
+ Requires-Dist: mypy==2.3.0; extra == "dev"
44
+ Requires-Dist: types-defusedxml==0.7.0.20260504; extra == "dev"
45
+ Requires-Dist: pytest>=8.2; extra == "dev"
46
+ Requires-Dist: pytest-timeout==2.4.0; extra == "dev"
47
+ Requires-Dist: build>=1.2; extra == "dev"
48
+ Requires-Dist: twine>=5.1; extra == "dev"
49
+ Requires-Dist: setuptools>=68; extra == "dev"
50
+ Requires-Dist: wheel; extra == "dev"
51
+ Provides-Extra: release
52
+ Requires-Dist: commitizen>=3.27; extra == "release"
53
+ Requires-Dist: build>=1.2; extra == "release"
54
+ Requires-Dist: twine>=5.1; extra == "release"
55
+
56
+ # Simulo for Cloud Robotics Simulation and Training
57
+
58
+ **Train robot policies in simulation on managed cloud GPUs.**
59
+
60
+ Simulo is a cloud-native AI platform for robotics simulation and training. Its
61
+ lightweight `simulo` Python SDK and CLI let you define robot-learning applications
62
+ locally, submit them to the Simulo cloud, and inspect the results. The platform
63
+ handles managed GPU execution, reproducible packaging, versioned assets,
64
+ checkpoints, trained models, recordings, and live 3D viewing.
65
+
66
+ The package installed on your computer is torch-free and requires no local GPU or
67
+ simulator. The managed runtime currently uses NVIDIA Isaac Sim and Isaac Lab to
68
+ execute reinforcement-learning training, policy evaluation and inference, and
69
+ scripted robotics simulations.
70
+
71
+ ## What you can do
72
+
73
+ - Write robotics applications in ordinary Python against a small, stable API.
74
+ - Build vectorized reinforcement-learning environments and train robot policies with PPO.
75
+ - Start with validated catalog robots or publish your own OpenUSD (USD) and URDF assets.
76
+ - Continue training from the best or latest checkpoint produced by an earlier job.
77
+ - Evaluate policies, run inference, and export trained policies as verified ONNX bundles.
78
+ - Stream logs, retrieve results and outputs, capture MCAP rollout recordings, and watch a live 3D view.
79
+
80
+ ## Install
81
+
82
+ ```bash
83
+ python -m pip install simulo
84
+ ```
85
+
86
+ Simulo requires Python 3.11 or newer. One installation provides both the
87
+ `import simulo` Python API and the `simulo` command-line interface.
88
+
89
+ ## Train a first policy
90
+
91
+ ```bash
92
+ simulo create mybot
93
+ simulo login
94
+ cd mybot
95
+ simulo run app.py --frozen --strict-assets
96
+ ```
97
+
98
+ `simulo create` writes a complete starter application to your machine without
99
+ requiring a login or network connection. The default application trains a PPO
100
+ policy on a validated cartpole asset. `simulo run` packages the source, resolves
101
+ and pins its assets, submits the job, and follows its cloud logs.
102
+
103
+ ## Explore robotics samples
104
+
105
+ The public [Simulo samples repository](https://github.com/simulo-org/simulo-samples)
106
+ contains projects for cartpole, humanoid locomotion, JetBot navigation, Franka
107
+ arm control, a bring-your-own URDF arm, and F1TENTH-compatible drift training.
108
+ Its current published compatibility contract is `simulo>=0.26.0,<0.27`. Check
109
+ the repository README for the supported SDK range before running a checkout.
110
+
111
+ ## Monitor jobs and retrieve artifacts
112
+
113
+ ```bash
114
+ simulo jobs # list submitted jobs and status
115
+ simulo logs --follow # stream the latest job's logs
116
+ simulo result # read the job's result
117
+ simulo models # list or download trained models
118
+ simulo export # export the best policy as verified ONNX
119
+ simulo recordings # download MCAP rollout recordings
120
+ simulo outputs # retrieve other saved outputs
121
+ simulo view # view a running --viewstream job in 3D
122
+ simulo cancel # stop a queued or running job
123
+ ```
124
+
125
+ Commands that accept a job ID default to the latest job and tell you which job
126
+ they selected.
127
+
128
+ ## Use versioned robot assets
129
+
130
+ ```bash
131
+ simulo asset list --global
132
+ simulo asset inspect simulo/robot/cartpole:v1
133
+ simulo asset publish ./my-robot --kind robot
134
+ ```
135
+
136
+ Simulo catalogs keep robot, world, and prop assets versioned and traceable. You
137
+ can use validated global assets immediately or publish your own USD or URDF
138
+ package for reuse across training, evaluation, and inference jobs.
139
+
140
+ ## Learn more
141
+
142
+ - [Quickstart](https://docs.simulo.ai/getting-started/quickstart/)
143
+ - [Documentation](https://docs.simulo.ai/)
144
+ - [Python API reference](https://docs.simulo.ai/api/)
145
+ - [CLI reference](https://docs.simulo.ai/cli/)
146
+ - [Examples](https://docs.simulo.ai/examples/)
147
+ - [Simulo website](https://simulo.ai)
simulo-0.28.1/PYPI.md ADDED
@@ -0,0 +1,92 @@
1
+ # Simulo for Cloud Robotics Simulation and Training
2
+
3
+ **Train robot policies in simulation on managed cloud GPUs.**
4
+
5
+ Simulo is a cloud-native AI platform for robotics simulation and training. Its
6
+ lightweight `simulo` Python SDK and CLI let you define robot-learning applications
7
+ locally, submit them to the Simulo cloud, and inspect the results. The platform
8
+ handles managed GPU execution, reproducible packaging, versioned assets,
9
+ checkpoints, trained models, recordings, and live 3D viewing.
10
+
11
+ The package installed on your computer is torch-free and requires no local GPU or
12
+ simulator. The managed runtime currently uses NVIDIA Isaac Sim and Isaac Lab to
13
+ execute reinforcement-learning training, policy evaluation and inference, and
14
+ scripted robotics simulations.
15
+
16
+ ## What you can do
17
+
18
+ - Write robotics applications in ordinary Python against a small, stable API.
19
+ - Build vectorized reinforcement-learning environments and train robot policies with PPO.
20
+ - Start with validated catalog robots or publish your own OpenUSD (USD) and URDF assets.
21
+ - Continue training from the best or latest checkpoint produced by an earlier job.
22
+ - Evaluate policies, run inference, and export trained policies as verified ONNX bundles.
23
+ - Stream logs, retrieve results and outputs, capture MCAP rollout recordings, and watch a live 3D view.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ python -m pip install simulo
29
+ ```
30
+
31
+ Simulo requires Python 3.11 or newer. One installation provides both the
32
+ `import simulo` Python API and the `simulo` command-line interface.
33
+
34
+ ## Train a first policy
35
+
36
+ ```bash
37
+ simulo create mybot
38
+ simulo login
39
+ cd mybot
40
+ simulo run app.py --frozen --strict-assets
41
+ ```
42
+
43
+ `simulo create` writes a complete starter application to your machine without
44
+ requiring a login or network connection. The default application trains a PPO
45
+ policy on a validated cartpole asset. `simulo run` packages the source, resolves
46
+ and pins its assets, submits the job, and follows its cloud logs.
47
+
48
+ ## Explore robotics samples
49
+
50
+ The public [Simulo samples repository](https://github.com/simulo-org/simulo-samples)
51
+ contains projects for cartpole, humanoid locomotion, JetBot navigation, Franka
52
+ arm control, a bring-your-own URDF arm, and F1TENTH-compatible drift training.
53
+ Its current published compatibility contract is `simulo>=0.26.0,<0.27`. Check
54
+ the repository README for the supported SDK range before running a checkout.
55
+
56
+ ## Monitor jobs and retrieve artifacts
57
+
58
+ ```bash
59
+ simulo jobs # list submitted jobs and status
60
+ simulo logs --follow # stream the latest job's logs
61
+ simulo result # read the job's result
62
+ simulo models # list or download trained models
63
+ simulo export # export the best policy as verified ONNX
64
+ simulo recordings # download MCAP rollout recordings
65
+ simulo outputs # retrieve other saved outputs
66
+ simulo view # view a running --viewstream job in 3D
67
+ simulo cancel # stop a queued or running job
68
+ ```
69
+
70
+ Commands that accept a job ID default to the latest job and tell you which job
71
+ they selected.
72
+
73
+ ## Use versioned robot assets
74
+
75
+ ```bash
76
+ simulo asset list --global
77
+ simulo asset inspect simulo/robot/cartpole:v1
78
+ simulo asset publish ./my-robot --kind robot
79
+ ```
80
+
81
+ Simulo catalogs keep robot, world, and prop assets versioned and traceable. You
82
+ can use validated global assets immediately or publish your own USD or URDF
83
+ package for reuse across training, evaluation, and inference jobs.
84
+
85
+ ## Learn more
86
+
87
+ - [Quickstart](https://docs.simulo.ai/getting-started/quickstart/)
88
+ - [Documentation](https://docs.simulo.ai/)
89
+ - [Python API reference](https://docs.simulo.ai/api/)
90
+ - [CLI reference](https://docs.simulo.ai/cli/)
91
+ - [Examples](https://docs.simulo.ai/examples/)
92
+ - [Simulo website](https://simulo.ai)
@@ -4,20 +4,53 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simulo"
7
- version = "0.27.0"
8
- description = "Simulo SDK and CLI — define robotics simulation and training apps in Python and run them on the Simulo cloud."
7
+ version = "0.28.1"
8
+ description = "Python SDK and CLI for cloud-native robotics simulation, reinforcement learning, and robot policy training on managed GPUs."
9
9
  readme = "PYPI.md"
10
10
  requires-python = ">=3.11"
11
11
  license = { text = "BSD-3-Clause" }
12
12
  authors = [{ name = "Simulo Team", email = "team@simulo.ai" }]
13
- keywords = ["robotics", "simulation", "reinforcement-learning", "simulo"]
13
+ keywords = [
14
+ "simulo",
15
+ "robotics",
16
+ "robotics simulation",
17
+ "robot training",
18
+ "robot learning",
19
+ "reinforcement learning",
20
+ "cloud robotics",
21
+ "cloud simulation",
22
+ "GPU simulation",
23
+ "policy training",
24
+ "physical AI",
25
+ "embodied AI",
26
+ "NVIDIA Isaac Sim",
27
+ "Isaac Lab",
28
+ "OpenUSD",
29
+ "USD",
30
+ "URDF",
31
+ "ONNX",
32
+ "MCAP",
33
+ "Python SDK",
34
+ ]
14
35
  classifiers = [
15
36
  "Development Status :: 3 - Alpha",
37
+ "Environment :: Console",
16
38
  "Intended Audience :: Developers",
39
+ "Intended Audience :: Education",
40
+ "Intended Audience :: Science/Research",
17
41
  "License :: OSI Approved :: BSD License",
18
42
  "Operating System :: OS Independent",
43
+ "Programming Language :: Python",
44
+ "Programming Language :: Python :: 3",
45
+ "Programming Language :: Python :: 3 :: Only",
19
46
  "Programming Language :: Python :: 3.11",
20
47
  "Programming Language :: Python :: 3.12",
48
+ "Topic :: Scientific/Engineering",
49
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
50
+ "Topic :: Scientific/Engineering :: Visualization",
51
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
52
+ "Topic :: Software Development :: Libraries :: Python Modules",
53
+ "Topic :: System :: Distributed Computing",
21
54
  "Typing :: Typed",
22
55
  ]
23
56
  # Torch-free by construction: depends only on the contract layer and mcap.
@@ -26,7 +59,7 @@ classifiers = [
26
59
  # mcap: used by `simulo recordings` to read back a downloaded recording and
27
60
  # verify it (message count) — MIT-licensed, deps only lz4+zstandard.
28
61
  dependencies = [
29
- "simulo-interfaces>=0.19.0,<0.20",
62
+ "simulo-interfaces>=0.20.1,<0.21",
30
63
  "mcap>=1.3,<2",
31
64
  # Hardened XML parsing for URDF ingestion (USD Asset Catalogs, PR-12 fix
32
65
  # loop 2, security NIT): stdlib xml.etree.ElementTree relies on
@@ -102,6 +135,9 @@ simulo = "simulo._client.cli:main"
102
135
 
103
136
  [project.urls]
104
137
  Homepage = "https://simulo.ai"
138
+ Documentation = "https://docs.simulo.ai"
139
+ Quickstart = "https://docs.simulo.ai/getting-started/quickstart/"
140
+ Examples = "https://github.com/simulo-org/simulo-samples"
105
141
 
106
142
  # Namespace packaging: this is the only distribution that ships
107
143
  # `src/simulo/__init__.py`, so sibling `simulo.*` distributions can share the
@@ -27,14 +27,26 @@ from typing import Any, NoReturn, Optional
27
27
 
28
28
  from simulo.interfaces.ids import JobId, JobPublicId
29
29
  from simulo.interfaces.platform.enums import JobStatus
30
- from simulo.interfaces.platform.runs import JOB_SCOPE_MINE, TERMINAL_JOB_STATUSES
30
+ from simulo.interfaces.platform.runs import JOB_SCOPE_MINE
31
31
 
32
32
  #: Poll cadence for cloud ``.get()``. Module-level so tests can shrink it.
33
33
  _POLL_INTERVAL_S = 1.0
34
34
 
35
- _TERMINAL_STATUS_STRINGS = frozenset(str(status) for status in TERMINAL_JOB_STATUSES)
36
35
  _COMPLETED_STATUS = str(JobStatus.COMPLETED)
37
36
 
37
+ #: The tolerant-client check (`M55-org-runtime-policy`, issue #1537):
38
+ #: EVERY status this client can observe outside these two live values is
39
+ #: terminal, whether or not it is one this client's own ``JobStatus`` happens
40
+ #: to know by name. Membership is checked as "not one of the known LIVE
41
+ #: values", never "is one of the known TERMINAL values" — a closed terminal
42
+ #: set silently treats a FUTURE value it has never heard of as still-live
43
+ #: and polls forever (:class:`JobTimeoutError` only fires when the caller
44
+ #: passed a ``timeout``; with none, :meth:`JobHandle._get_cloud` hangs).
45
+ #: Mirrors the existing precedent for an unrecognized ``kind``
46
+ #: (``simulo.interfaces.platform.submit``): a value outside the known set is
47
+ #: treated as data to render, never as grounds to keep waiting on it.
48
+ _LIVE_STATUS_STRINGS = frozenset({str(JobStatus.QUEUED), str(JobStatus.RUNNING)})
49
+
38
50
 
39
51
  class JobFailedError(RuntimeError):
40
52
  """A cloud job reached a terminal ``failed``/``cancelled`` status.
@@ -154,7 +166,7 @@ class JobHandle:
154
166
  while True:
155
167
  record = client.get_job(str(self._job_id), scope=JOB_SCOPE_MINE)
156
168
  status = str(record.get("status"))
157
- if status in _TERMINAL_STATUS_STRINGS:
169
+ if status not in _LIVE_STATUS_STRINGS:
158
170
  if status == _COMPLETED_STATUS:
159
171
  return client.get_result(str(self._job_id), scope=JOB_SCOPE_MINE)
160
172
  exit_code = record.get("exit_code")
@@ -514,6 +514,26 @@ def _spawn_cloud(
514
514
  # already lives there, keyed by this same `job_name`.
515
515
  job_entry = manifest.get("jobs", {}).get(job_name) or {}
516
516
  system = (job_entry.get("resources") or {}).get("system")
517
+ # The job's own manifest-declared `timeout` (`M55-org-runtime-policy`,
518
+ # issue #1537) — already validated at `@app.job(timeout=...)` decoration
519
+ # time and recorded on THIS job's own manifest entry, exactly like
520
+ # `system` just above. Forwarded explicitly to `create_job` because the
521
+ # control plane never parses the uploaded manifest archive itself (see
522
+ # `JobSubmitRequest.manifest_timeout_s`'s own docstring).
523
+ manifest_timeout_s = job_entry.get("timeout")
524
+ if manifest_timeout_s is not None and (
525
+ isinstance(manifest_timeout_s, bool) or not isinstance(manifest_timeout_s, int)
526
+ ):
527
+ # This manifest was just written by THIS process (see the comment on
528
+ # `Manifest.from_json` re-parsing above) — a non-integer `timeout`
529
+ # here would mean `@app.job`'s own decoration-time validation
530
+ # somehow let one through, not an untrusted value to degrade
531
+ # silently on. Fail loudly rather than send a malformed wire field.
532
+ raise RuntimeError(
533
+ f"Package at {package_path} has a non-integer jobs[{job_name!r}].timeout "
534
+ f"({manifest_timeout_s!r}) in its manifest — this should be impossible; "
535
+ "please report this as a bug."
536
+ )
517
537
 
518
538
  tar_path = ensure_tar(package_path)
519
539
  archive_bytes = tar_path.read_bytes()
@@ -568,6 +588,7 @@ def _spawn_cloud(
568
588
  assets=asset_pins,
569
589
  app_name=app_name,
570
590
  system=system,
591
+ timeout_s=manifest_timeout_s,
571
592
  )
572
593
 
573
594
  raw_public_id = record.get("public_id")
@@ -590,9 +611,24 @@ def _spawn_cloud(
590
611
  # the ref resolved to the intended source job. Seed provenance still
591
612
  # carries machine UUIDs only, so keep this confirmation neutral rather
592
613
  # than leaking a UUID while that response contract catches up.
614
+ #
615
+ # `seed["name"]`/`seed["kind"]` are server-supplied text: a
616
+ # compromised control plane — or a user pointed at an attacker-run
617
+ # `SIMULO_API_URL` — could embed ANSI/OSC sequences here, so both
618
+ # route through the same module `cli.py`'s own sanitizers are built
619
+ # on (`preflight_render`) rather than a second one.
620
+ # `sanitize_server_text` alone is NOT enough here (cross-vendor
621
+ # review): it deliberately preserves TAB/LF for its usual
622
+ # multi-line preflight-finding callers, but this confirmation is a
623
+ # single line — an unstripped LF would forge an extra line this
624
+ # code never printed. `_single_line` collapses it instead.
625
+ from simulo._client.preflight_render import sanitize_server_text_single_line
626
+
593
627
  seed = record.get("seed") or {}
628
+ seed_name = sanitize_server_text_single_line(str(seed.get("name")))
629
+ seed_kind = sanitize_server_text_single_line(str(seed.get("kind")))
594
630
  print(
595
- f"Continuing from selected job ({seed.get('name')}, {seed.get('kind')} checkpoint)",
631
+ f"Continuing from selected job ({seed_name}, {seed_kind} checkpoint)",
596
632
  file=sys.stderr,
597
633
  )
598
634
 
@@ -296,6 +296,7 @@ class AssetApiClient:
296
296
  api_base_url=self._base_url,
297
297
  timeout=_ARCHIVE_DOWNLOAD_TIMEOUT_S,
298
298
  unavailable_hint=_UNAVAILABLE_HINT,
299
+ validate_download_url=True,
299
300
  )
300
301
  except http.HttpError as exc:
301
302
  raise _translate(exc) from exc
@@ -20,7 +20,7 @@ from pathlib import Path
20
20
  from typing import TYPE_CHECKING, Any, Optional, Sequence
21
21
 
22
22
  from simulo._client.asset import Asset
23
- from simulo._client.packaging import canonical_args, package, read_bundle_package_id
23
+ from simulo._client.packaging import assert_credentials_outside_project, canonical_args, package, read_bundle_package_id
24
24
  from simulo._client.volume import Volume
25
25
  from simulo.interfaces.platform import is_platform_pinned
26
26
  from simulo.interfaces.platform.manifest import Manifest
@@ -222,6 +222,7 @@ def ensure_bundle(
222
222
  (deterministically restoring the correct content for this exact
223
223
  submission), never served as a silently wrong package.
224
224
  """
225
+ assert_credentials_outside_project(project_root)
225
226
  extra_asset_uris = tuple(sorted({asset.uri for asset in extra_assets}))
226
227
  cache_key = (id(app), job_name, canonical_args(args), extra_asset_uris)
227
228
  cached = _BUNDLE_CACHE.get(cache_key)