aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeonard Kugis <leonard@kug.is>2026-01-31 14:08:20 +0100
committerLeonard Kugis <leonard@kug.is>2026-01-31 14:08:20 +0100
commit94000f6b6d07b31151deccc4e3e1fdccf5b5c487 (patch)
treec0d5450d5d445fc675d899d72de2957d5e6e289b
parent4742f1125021f6bc1a2631c6e54cf6365f9d37f6 (diff)
downloadllm-functions-docker-94000f6b6d07b31151deccc4e3e1fdccf5b5c487.tar.gz
Added docker scripts
-rw-r--r--tools/docker_exec.sh27
-rw-r--r--tools/docker_start.sh70
-rw-r--r--tools/docker_stop.sh18
3 files changed, 115 insertions, 0 deletions
diff --git a/tools/docker_exec.sh b/tools/docker_exec.sh
new file mode 100644
index 0000000..66f79bc
--- /dev/null
+++ b/tools/docker_exec.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# @describe Execute a shell command inside a sandbox container.
+# @option --container! Container name/id to execute in.
+# @option --command! Shell command to run (executed via sh -lc inside container).
+# @option --timeout_sec Timeout in seconds. Default: 60
+
+main() {
+ local c="${argc_container}"
+ local cmd="${argc_command}"
+ local t="${argc_timeout_sec:-60}"
+
+ if ! docker ps --format '{{.Names}}' | grep -qx "$c"; then
+ echo "ERROR: container not running: $c" >> "$LLM_OUTPUT"
+ exit 2
+ fi
+
+ local out
+ local rc=0
+ out="$(timeout "${t}" docker exec "$c" sh -lc "$cmd" 2>&1)" || rc=$?
+
+ printf '{"exit_code":%d,"output":%s}\n' "$rc" "$(python -c 'import json,sys; print(json.dumps(sys.stdin.read()))' <<<"$out")" >> "$LLM_OUTPUT"
+}
+
+eval "$(argc --argc-eval "$0" "$@")"
+
diff --git a/tools/docker_start.sh b/tools/docker_start.sh
new file mode 100644
index 0000000..cec5808
--- /dev/null
+++ b/tools/docker_start.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# @describe Start (or reuse) a sandbox Docker container with a bind-mounted workspace.
+# @option --workspace! Host path to mount into the container at /work (must be under $AICHAT_SANDBOX_BASE).
+# @option --image Docker image to use. Default: aichat-docker:latest
+# @option --name Optional explicit container name. Default: auto-generated.
+# @option --network Enable network access (true/false). Default: true
+# @option --ttl_minutes Optional TTL label for cleanup tooling. Default: 0 (no TTL)
+
+main() {
+ local base="${AICHAT_SANDBOX_BASE:-$HOME/aichat-workspaces}"
+ local image="${argc_image:-aichat-docker:latest}"
+ local net="${argc_network:-true}"
+ local ttl="${argc_ttl_minutes:-0}"
+
+ mkdir -p "$base"
+
+ local ws
+ ws="$(realpath -m "${argc_workspace}")"
+
+ local base_real
+ base_real="$(realpath -m "$base")"
+ case "$ws" in
+ "$base_real"/*) ;;
+ *)
+ echo "ERROR: workspace must be under $base_real (got $ws)" >> "$LLM_OUTPUT"
+ exit 2
+ ;;
+ esac
+
+ mkdir -p "$ws"
+
+ local name="${argc_name:-aichat-sbx-$(date +%s)-$RANDOM}"
+
+ if docker ps -a --format '{{.Names}}' | grep -qx "$name"; then
+ printf '{"container":"%s","workspace":"%s","status":"already-exists"}\n' "$name" "$ws" >> "$LLM_OUTPUT"
+ return 0
+ fi
+
+ local net_arg=()
+ if [[ "$net" == "false" ]]; then
+ net_arg+=(--network none)
+ fi
+
+ local uid gid
+ uid="$(id -u)"
+ gid="$(id -g)"
+
+ docker run -d --rm \
+ --name "$name" \
+ -u "${uid}:${gid}" \
+ -w /work \
+ -v "${ws}:/work:rw" \
+ --cap-drop=ALL \
+ --security-opt=no-new-privileges \
+ --pids-limit 256 \
+ --memory 2g \
+ --cpus 2 \
+ "${net_arg[@]}" \
+ --label "aichat.sandbox=true" \
+ --label "aichat.ttl_minutes=${ttl}" \
+ "$image" \
+ sh -lc 'sleep infinity' >/dev/null
+
+ printf '{"container":"%s","workspace":"%s","status":"started"}\n' "$name" "$ws" >> "$LLM_OUTPUT"
+}
+
+eval "$(argc --argc-eval "$0" "$@")"
+
diff --git a/tools/docker_stop.sh b/tools/docker_stop.sh
new file mode 100644
index 0000000..780a346
--- /dev/null
+++ b/tools/docker_stop.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# @describe Stop a sandbox container (it will be removed if started with --rm).
+# @option --container! Container name/id to stop.
+
+main() {
+ local c="${argc_container}"
+ if docker ps --format '{{.Names}}' | grep -qx "$c"; then
+ docker stop "$c" >/dev/null
+ printf '{"container":"%s","status":"stopped"}\n' "$c" >> "$LLM_OUTPUT"
+ else
+ printf '{"container":"%s","status":"not-running"}\n' "$c" >> "$LLM_OUTPUT"
+ fi
+}
+
+eval "$(argc --argc-eval "$0" "$@")"
+