Skip to content

Commit 4b7f927

Browse files
committed
(feat) added --port-forward and --background flags
1 parent c490c0a commit 4b7f927

1 file changed

Lines changed: 124 additions & 24 deletions

File tree

agent-vm.sh

Lines changed: 124 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ _agent_vm_ensure_running() {
6363
local vm_name="$1"
6464
local host_dir="$2"
6565
shift 2
66-
local disk="" memory="" cpus="" reset="" offline="" rdonly="" git_ro=""
66+
local disk="" memory="" cpus="" reset="" offline="" rdonly="" git_ro="" portforward=""
6767
while [[ $# -gt 0 ]]; do
6868
case "$1" in
6969
--disk) disk="$2"; shift 2 ;;
@@ -73,6 +73,7 @@ _agent_vm_ensure_running() {
7373
--offline) offline=1; shift ;;
7474
--readonly) rdonly=1; shift ;;
7575
--git-read-only|--git-ro) git_ro=1; shift ;;
76+
--port-forward) portforward="$2"; shift 2 ;;
7677
*) shift ;;
7778
esac
7879
done
@@ -112,7 +113,7 @@ _agent_vm_ensure_running() {
112113
if [[ -f "$base_ver" ]]; then
113114
cp "$base_ver" "$AGENT_VM_STATE_DIR/.agent-vm-version-${vm_name}"
114115
fi
115-
elif [[ -n "$disk" || -n "$memory" || -n "$cpus" ]]; then
116+
elif [[ -n "$disk" || -n "$memory" || -n "$cpus" || -n "$portforward" ]]; then
116117
# Auto-resize existing VM if --disk, --memory, or --cpus changed
117118
if _agent_vm_running "$vm_name"; then
118119
echo "VM '$vm_name' is currently running. It must be stopped to apply new resource settings."
@@ -154,7 +155,9 @@ _agent_vm_ensure_running() {
154155

155156
if ! _agent_vm_running "$vm_name"; then
156157
echo "Starting VM '$vm_name'..."
157-
limactl start "$vm_name" &>/dev/null
158+
local start_args=("$vm_name")
159+
[[ -n "$portforward" ]] && start_args+=(--port-forward "$portforward")
160+
limactl start "${start_args[@]}" &>/dev/null
158161
fi
159162

160163
# Run per-user runtime script if it exists
@@ -219,6 +222,12 @@ agent-vm() {
219222
vm_opts+=(--git-read-only); shift ;;
220223
--rm)
221224
vm_opts+=(--rm); shift ;;
225+
--port-forward)
226+
vm_opts+=(--port-forward "$2"); shift 2 ;;
227+
--port-forward=*)
228+
vm_opts+=(--port-forward "${1#*=}"); shift ;;
229+
--background)
230+
vm_opts+=(--background); shift ;;
222231
*)
223232
break ;;
224233
esac
@@ -299,6 +308,8 @@ VM options (for claude, opencode, codex, shell, run):
299308
--readonly Mount the project directory as read-only
300309
--git-read-only Mount .git directory as read-only (allows git diff/log but not commit/stash)
301310
--rm Automatically destroy the VM after the command exits
311+
--port-forward Enable port forwarding (hostfwd)
312+
--background Run in background (detached from terminal)
302313
303314
Examples:
304315
agent-vm setup # Create base VM
@@ -311,6 +322,8 @@ Examples:
311322
agent-vm --offline claude # No internet access
312323
agent-vm --readonly shell # Read-only project mount
313324
agent-vm --git-ro claude # Protect .git from writes
325+
agent-vm --port-forward '3000:3000' opencode serve --port 3000 # Forward vm ports to the host
326+
agent-vm --background opencode serve --port 3000 # Run in background
314327
agent-vm shell # Shell into the VM
315328
agent-vm run npm install # Run a command in the VM
316329
agent-vm claude -p "fix lint errors" # Pass args to claude
@@ -331,18 +344,20 @@ _agent_vm_setup() {
331344
local disk=10
332345
local memory=2
333346
local cpus=1
347+
local portforward=""
334348

335349
while [[ $# -gt 0 ]]; do
336350
case "$1" in
337351
--help|-h)
338-
echo "Usage: agent-vm setup [--disk GB] [--memory GB] [--cpus N]"
352+
echo "Usage: agent-vm setup [--disk GB] [--memory GB] [--cpus N] [--port-forward]"
339353
echo ""
340354
echo "Create a base VM template with dev tools and agents pre-installed."
341355
echo ""
342356
echo "Options:"
343357
echo " --disk GB VM disk size (default: 10)"
344358
echo " --memory GB VM memory (default: 2)"
345359
echo " --cpus N Number of CPUs (default: 1)"
360+
echo " --port-forward Enable port forwarding (hostfwd)"
346361
echo " --help Show this help"
347362
return 0
348363
;;
@@ -372,6 +387,14 @@ _agent_vm_setup() {
372387
;;
373388
--reset|--offline|--readonly|--git-read-only|--git-ro)
374389
shift ;;
390+
--port-forward)
391+
portforward="$2"
392+
shift 2
393+
;;
394+
--port-forward=*)
395+
portforward="${1#*=}"
396+
shift
397+
;;
375398
*)
376399
echo "Unknown option: $1" >&2
377400
echo "Usage: agent-vm setup [--disk GB] [--memory GB] [--cpus N]" >&2
@@ -401,6 +424,7 @@ _agent_vm_setup() {
401424
--tty=false
402425
)
403426
[[ -n "$cpus" ]] && create_args+=(--cpus="$cpus")
427+
[[ -n "$portforward" ]] && create_args+=(--port-forward="$portforward")
404428
limactl create --name="$AGENT_VM_TEMPLATE" template:debian-13 \
405429
"${create_args[@]}" &>/dev/null || { echo "Error: Failed to create base VM." >&2; return 1; }
406430

@@ -434,6 +458,7 @@ _agent_vm_claude() {
434458
local vm_opts=()
435459
local args=()
436460
local rm=""
461+
local background=""
437462
while [[ $# -gt 0 ]]; do
438463
case "$1" in
439464
--disk) vm_opts+=(--disk "$2"); shift 2 ;;
@@ -444,9 +469,17 @@ _agent_vm_claude() {
444469
--readonly) vm_opts+=(--readonly); shift ;;
445470
--git-read-only|--git-ro) vm_opts+=(--git-read-only); shift ;;
446471
--rm) rm=1; shift ;;
472+
--port-forward) vm_opts+=(--port-forward "$2"); shift 2 ;;
473+
--background) background=1; shift ;;
447474
*) args+=("$1"); shift ;;
448475
esac
449476
done
477+
478+
if [[ -n "$background" && -n "$rm" ]]; then
479+
echo "Error: --background and --rm cannot be used together" >&2
480+
return 1
481+
fi
482+
450483
local host_dir
451484
host_dir="$(pwd)"
452485
local vm_name
@@ -455,17 +488,28 @@ _agent_vm_claude() {
455488
_agent_vm_ensure_running "$vm_name" "$host_dir" "${vm_opts[@]}" || return 1
456489
_agent_vm_print_resources "$vm_name"
457490

458-
local exit_code=0
459-
limactl shell --workdir "$host_dir" "$vm_name" claude --dangerously-skip-permissions "${args[@]}"
460-
exit_code=$?
461-
[[ -n "$rm" ]] && { echo "Removing VM..."; _agent_vm_destroy; }
462-
return $exit_code
491+
if [[ -n "$background" ]]; then
492+
nohup limactl shell --workdir "$host_dir" "$vm_name" claude --dangerously-skip-permissions "${args[@]}" &>/dev/null &
493+
disown
494+
echo "Started claude in background (pid: $!)"
495+
return 0
496+
elif [[ -n "$rm" ]]; then
497+
limactl shell --workdir "$host_dir" "$vm_name" claude --dangerously-skip-permissions "${args[@]}"
498+
exit_code=$?
499+
echo "Removing VM..."
500+
_agent_vm_destroy
501+
return $exit_code
502+
else
503+
limactl shell --workdir "$host_dir" "$vm_name" claude --dangerously-skip-permissions "${args[@]}"
504+
return $?
505+
fi
463506
}
464507

465508
_agent_vm_opencode() {
466509
local vm_opts=()
467510
local args=()
468511
local rm=""
512+
local background=""
469513
while [[ $# -gt 0 ]]; do
470514
case "$1" in
471515
--disk) vm_opts+=(--disk "$2"); shift 2 ;;
@@ -476,9 +520,17 @@ _agent_vm_opencode() {
476520
--readonly) vm_opts+=(--readonly); shift ;;
477521
--git-read-only|--git-ro) vm_opts+=(--git-read-only); shift ;;
478522
--rm) rm=1; shift ;;
523+
--port-forward) vm_opts+=(--port-forward "$2"); shift 2 ;;
524+
--background) background=1; shift ;;
479525
*) args+=("$1"); shift ;;
480526
esac
481527
done
528+
529+
if [[ -n "$background" && -n "$rm" ]]; then
530+
echo "Error: --background and --rm cannot be used together" >&2
531+
return 1
532+
fi
533+
482534
local host_dir
483535
host_dir="$(pwd)"
484536
local vm_name
@@ -489,17 +541,28 @@ _agent_vm_opencode() {
489541

490542
# TODO: add --dangerously-skip-permissions once released
491543
# (waiting on https://github.com/anomalyco/opencode/pull/11833)
492-
local exit_code=0
493-
limactl shell --tty --workdir "$host_dir" "$vm_name" opencode "${args[@]}"
494-
exit_code=$?
495-
[[ -n "$rm" ]] && { echo "Removing VM..."; _agent_vm_destroy; }
496-
return $exit_code
544+
if [[ -n "$background" ]]; then
545+
nohup limactl shell --workdir "$host_dir" "$vm_name" opencode "${args[@]}" &>/dev/null &
546+
disown
547+
echo "Started opencode in background (pid: $!)"
548+
return 0
549+
elif [[ -n "$rm" ]]; then
550+
limactl shell --tty --workdir "$host_dir" "$vm_name" opencode "${args[@]}"
551+
exit_code=$?
552+
echo "Removing VM..."
553+
_agent_vm_destroy
554+
return $exit_code
555+
else
556+
limactl shell --tty --workdir "$host_dir" "$vm_name" opencode "${args[@]}"
557+
return $?
558+
fi
497559
}
498560

499561
_agent_vm_codex() {
500562
local vm_opts=()
501563
local args=()
502564
local rm=""
565+
local background=""
503566
while [[ $# -gt 0 ]]; do
504567
case "$1" in
505568
--disk) vm_opts+=(--disk "$2"); shift 2 ;;
@@ -510,9 +573,17 @@ _agent_vm_codex() {
510573
--readonly) vm_opts+=(--readonly); shift ;;
511574
--git-read-only|--git-ro) vm_opts+=(--git-read-only); shift ;;
512575
--rm) rm=1; shift ;;
576+
--port-forward) vm_opts+=(--port-forward "$2"); shift 2 ;;
577+
--background) background=1; shift ;;
513578
*) args+=("$1"); shift ;;
514579
esac
515580
done
581+
582+
if [[ -n "$background" && -n "$rm" ]]; then
583+
echo "Error: --background and --rm cannot be used together" >&2
584+
return 1
585+
fi
586+
516587
local host_dir
517588
host_dir="$(pwd)"
518589
local vm_name
@@ -521,11 +592,21 @@ _agent_vm_codex() {
521592
_agent_vm_ensure_running "$vm_name" "$host_dir" "${vm_opts[@]}" || return 1
522593
_agent_vm_print_resources "$vm_name"
523594

524-
local exit_code=0
525-
limactl shell --workdir "$host_dir" "$vm_name" codex --full-auto "${args[@]}"
526-
exit_code=$?
527-
[[ -n "$rm" ]] && { echo "Removing VM..."; _agent_vm_destroy; }
528-
return $exit_code
595+
if [[ -n "$background" ]]; then
596+
nohup limactl shell --workdir "$host_dir" "$vm_name" codex --full-auto "${args[@]}" &>/dev/null &
597+
disown
598+
echo "Started codex in background (pid: $!)"
599+
return 0
600+
elif [[ -n "$rm" ]]; then
601+
limactl shell --workdir "$host_dir" "$vm_name" codex --full-auto "${args[@]}"
602+
exit_code=$?
603+
echo "Removing VM..."
604+
_agent_vm_destroy
605+
return $exit_code
606+
else
607+
limactl shell --workdir "$host_dir" "$vm_name" codex --full-auto "${args[@]}"
608+
return $?
609+
fi
529610
}
530611

531612
_agent_vm_shell() {
@@ -540,6 +621,7 @@ _agent_vm_shell() {
540621
--offline) vm_opts+=(--offline); shift ;;
541622
--readonly) vm_opts+=(--readonly); shift ;;
542623
--git-read-only|--git-ro) vm_opts+=(--git-read-only); shift ;;
624+
--port-forward) vm_opts+=(--port-forward "$2"); shift 2 ;;
543625
--rm) rm=1; shift ;;
544626
*) shift ;;
545627
esac
@@ -569,6 +651,7 @@ _agent_vm_run() {
569651
local vm_opts=()
570652
local args=()
571653
local rm=""
654+
local background=""
572655
while [[ $# -gt 0 ]]; do
573656
case "$1" in
574657
--disk) vm_opts+=(--disk "$2"); shift 2 ;;
@@ -579,9 +662,16 @@ _agent_vm_run() {
579662
--readonly) vm_opts+=(--readonly); shift ;;
580663
--git-read-only|--git-ro) vm_opts+=(--git-read-only); shift ;;
581664
--rm) rm=1; shift ;;
665+
--port-forward) vm_opts+=(--port-forward "$2"); shift 2 ;;
666+
--background) background=1; shift ;;
582667
*) args+=("$1"); shift ;;
583668
esac
584669
done
670+
if [[ -n "$background" && -n "$rm" ]]; then
671+
echo "Error: --background and --rm cannot be used together" >&2
672+
return 1
673+
fi
674+
585675
if [[ ${#args[@]} -eq 0 ]]; then
586676
echo "Usage: agent-vm run <command> [args]" >&2
587677
return 1
@@ -594,11 +684,21 @@ _agent_vm_run() {
594684
_agent_vm_ensure_running "$vm_name" "$host_dir" "${vm_opts[@]}" || return 1
595685
_agent_vm_print_resources "$vm_name"
596686

597-
local exit_code=0
598-
limactl shell --workdir "$host_dir" "$vm_name" "${args[@]}"
599-
exit_code=$?
600-
[[ -n "$rm" ]] && { echo "Removing VM..."; _agent_vm_destroy; }
601-
return $exit_code
687+
if [[ -n "$background" ]]; then
688+
nohup limactl shell --workdir "$host_dir" "$vm_name" "${args[@]}" &>/dev/null &
689+
disown
690+
echo "Started '${args[*]}' in background (pid: $!)"
691+
return 0
692+
elif [[ -n "$rm" ]]; then
693+
limactl shell --workdir "$host_dir" "$vm_name" "${args[@]}"
694+
exit_code=$?
695+
echo "Removing VM..."
696+
_agent_vm_destroy
697+
return $exit_code
698+
else
699+
limactl shell --workdir "$host_dir" "$vm_name" "${args[@]}"
700+
return $?
701+
fi
602702
}
603703

604704
_agent_vm_stop() {

0 commit comments

Comments
 (0)