self-hosted-agent-sandbox 0.3.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,5 @@
1
+ """self-hosted-agent-sandbox: disposable gVisor sandboxes for AI coding agents on your own k3s."""
2
+ from .core import Sandbox
3
+
4
+ __all__ = ["Sandbox", "__version__"]
5
+ __version__ = "0.3.0"
@@ -0,0 +1,74 @@
1
+ """``sbx``: run commands for an agent session inside a sandbox claimed from your warm pool.
2
+
3
+ sbx exec --session ID [--cwd DIR] [--keep] -- COMMAND... run one command (host-routing applies)
4
+ sbx shell --session ID [--cwd DIR] read commands from stdin, one per line
5
+ sbx release --session ID delete the session's claim
6
+
7
+ Configuration comes from AGENT_SANDBOX_* environment variables (KUBECONFIG, POOL, NAMESPACE,
8
+ CODE_DIRS, HOST_COMMANDS, FORWARD_ENV, ENV_SKIP, SYNC_PATHS, IDLE_TTL, ...); see the README.
9
+ ``--keep`` keeps the claim for the next ``sbx exec --session ID`` (persistent mode), so a
10
+ sequence of calls shares one sandbox until ``sbx release``.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import os
16
+ import shlex
17
+ import sys
18
+
19
+ from .core import Sandbox
20
+
21
+
22
+ def _open(a, persistent: bool) -> Sandbox:
23
+ cfg = {"agent_sandbox_persistent": "true"} if persistent else {}
24
+ return Sandbox(cwd=os.path.abspath(a.cwd), timeout=a.timeout, session=a.session, config=cfg, app=a.app)
25
+
26
+
27
+ def main(argv: list[str] | None = None) -> int:
28
+ ap = argparse.ArgumentParser(prog="sbx", description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
29
+ sub = ap.add_subparsers(dest="cmd", required=True)
30
+ for name in ("exec", "shell", "release"):
31
+ p = sub.add_parser(name)
32
+ p.add_argument("--session", required=True)
33
+ p.add_argument("--cwd", default=os.getcwd())
34
+ p.add_argument("--timeout", type=int, default=600)
35
+ p.add_argument("--app", default="sbx")
36
+ if name == "exec":
37
+ p.add_argument("--keep", action="store_true", help="keep the claim for later calls")
38
+ p.add_argument("command", nargs=argparse.REMAINDER)
39
+ a = ap.parse_args(argv)
40
+ if a.cmd == "release":
41
+ try:
42
+ n = Sandbox.release(a.session)
43
+ except RuntimeError as e:
44
+ print(e, file=sys.stderr)
45
+ return 1
46
+ print(f"released {n} claim(s)", file=sys.stderr)
47
+ return 0
48
+ if a.cmd == "exec":
49
+ cmd = a.command[1:] if a.command[:1] == ["--"] else a.command
50
+ if not cmd:
51
+ ap.error("exec needs a command")
52
+ command = cmd[0] if len(cmd) == 1 else shlex.join(cmd)
53
+ sb = _open(a, persistent=a.keep)
54
+ try:
55
+ r = sb.execute(command, os.path.abspath(a.cwd), timeout=a.timeout, stream=sys.stdout.buffer)
56
+ finally:
57
+ sys.stdout.flush()
58
+ sb.close()
59
+ return r["returncode"]
60
+ sb = _open(a, persistent=False)
61
+ rc = 0
62
+ try:
63
+ for line in sys.stdin:
64
+ line = line.rstrip("\n")
65
+ if line.strip():
66
+ rc = sb.execute(line, sb.cwd, timeout=a.timeout, stream=sys.stdout.buffer)["returncode"]
67
+ sys.stdout.flush()
68
+ finally:
69
+ sb.close()
70
+ return rc
71
+
72
+
73
+ if __name__ == "__main__":
74
+ sys.exit(main())