|
112 | 112 | stream_stdin_to_container, |
113 | 113 | stream_stdout_from_container, |
114 | 114 | ) |
| 115 | +from onyx.server.features.build.sandbox.image.sandbox_daemon.contract import ( |
| 116 | + OutputsManifestResponse, |
| 117 | +) |
115 | 118 | from onyx.server.features.build.sandbox.labels import ( |
116 | 119 | LABEL_K8S_MANAGED_BY, |
117 | 120 | LABEL_K8S_MANAGED_BY_ONYX, |
@@ -1621,6 +1624,32 @@ def list_directory( |
1621 | 1624 | entries = self._parse_ls_output(output, clean_path) |
1622 | 1625 | return sorted(entries, key=lambda e: (not e.is_directory, e.name.lower())) |
1623 | 1626 |
|
| 1627 | + def get_outputs_manifest( |
| 1628 | + self, sandbox_id: UUID, session_id: UUID |
| 1629 | + ) -> OutputsManifestResponse: |
| 1630 | + container = self._require_container(sandbox_id) |
| 1631 | + try: |
| 1632 | + # Root-owned interpreter and module: the sandbox user owns |
| 1633 | + # /workspace, so anything under it could be swapped to lie. |
| 1634 | + # workdir=/opt is load-bearing, python -m imports the root-owned |
| 1635 | + # /opt/sandbox_daemon only because cwd leads sys.path. -E -s |
| 1636 | + # ignore PYTHON* env vars and the user site directory. |
| 1637 | + result = _run_in_container_as_sandbox_user( |
| 1638 | + container, |
| 1639 | + [ |
| 1640 | + "/usr/local/bin/python3", |
| 1641 | + "-E", |
| 1642 | + "-s", |
| 1643 | + "-m", |
| 1644 | + "sandbox_daemon.manifest", |
| 1645 | + str(session_id), |
| 1646 | + ], |
| 1647 | + workdir="/opt", |
| 1648 | + ) |
| 1649 | + except ExecError as e: |
| 1650 | + raise RuntimeError(f"Failed to build outputs manifest: {e}") from e |
| 1651 | + return OutputsManifestResponse.model_validate_json(result.stdout_text) |
| 1652 | + |
1624 | 1653 | def _parse_ls_output(self, ls_output: str, base_path: str) -> list[FilesystemEntry]: |
1625 | 1654 | entries: list[FilesystemEntry] = [] |
1626 | 1655 | for line in ls_output.strip().split("\n"): |
|
0 commit comments