aibox-cli 0.3.0 → 0.4.2

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 (3) hide show
  1. package/README.md +14 -4
  2. package/bin/aibox +69 -29
  3. package/package.json +4 -1
package/README.md CHANGED
@@ -19,11 +19,8 @@ On macOS, if Docker isn't installed, aibox will offer to install [Colima](https:
19
19
  ## Usage
20
20
 
21
21
  ```bash
22
- # first time (once)
23
- aibox build
24
-
25
22
  # in any project directory
26
- aibox up # start container
23
+ aibox up # start container (auto-builds image on first run)
27
24
  aibox claude --yolo # no prompts, full sudo, no firewall
28
25
  aibox claude --safe # keep prompts, restricted sudo, firewall on
29
26
  aibox claude # asks you each time
@@ -71,6 +68,17 @@ aibox down --all # stop all containers for this project
71
68
  aibox nuke # remove ALL aibox containers
72
69
  ```
73
70
 
71
+ ### From a Git Repo
72
+
73
+ Start directly from a repo URL — aibox clones it and runs:
74
+
75
+ ```bash
76
+ aibox --repo https://github.com/user/project.git claude --yolo
77
+ aibox --repo git@github.com:user/project.git --branch dev claude
78
+ ```
79
+
80
+ Repos are cloned to `~/.config/aibox/repos/` with `--recursive` (submodules included). On subsequent runs, the existing clone is reused.
81
+
74
82
  ### Custom Image
75
83
 
76
84
  ```bash
@@ -151,6 +159,8 @@ SHARED_MODULES=false
151
159
  |-------|------|-------------|
152
160
  | `-n` | `--name NAME` | Named instance (multiple containers per project) |
153
161
  | `-d` | `--dir PATH` | Run in a different project directory |
162
+ | `-r` | `--repo URL` | Clone a git repo and run in it |
163
+ | `-b` | `--branch NAME` | Branch to checkout (with `--repo`) |
154
164
  | `-i` | `--image NAME` | Override base Docker image |
155
165
  | `-c` | `--copy` | Copy repo into Docker volume (full isolation) |
156
166
  | `-w` | `--worktree` | Use git worktree (lightweight isolation) |
package/bin/aibox CHANGED
@@ -23,6 +23,8 @@
23
23
  # -n, --name NAME Run a named instance (e.g. -n refactor)
24
24
  # Allows multiple containers per project
25
25
  # -d, --dir PATH Run in a different project directory
26
+ # -r, --repo URL Clone a git repo and run in it
27
+ # -b, --branch NAME Branch to checkout (with --repo)
26
28
  # -i, --image NAME Override base Docker image (default: aibox:latest)
27
29
  # -c, --copy Copy project into container (full isolation, no host sync)
28
30
  # -w, --worktree Use git worktree (lightweight isolation, stays on host)
@@ -55,11 +57,25 @@ set -euo pipefail
55
57
 
56
58
  # ── Script identity ──────────────────────────────────────────────
57
59
  SCRIPT_NAME="$(basename "$0")"
58
- AIBOX_VERSION="6"
60
+ AIBOX_VERSION="7"
59
61
  CONFIG_DIR="${HOME}/.config/aibox"
60
62
  DEFAULT_IMAGE="aibox:latest"
61
63
  CONTAINER_PREFIX="aibox"
62
64
 
65
+ # ── Output helpers ─────────────────────────────────────────────
66
+ _step() {
67
+ local msg="$1"
68
+ local width=60
69
+ local pad
70
+ pad=$(( width - ${#msg} - 5 )) # 5 = "── " + " ─"
71
+ [[ $pad -lt 3 ]] && pad=3
72
+ local line
73
+ line=$(printf '─%.0s' $(seq 1 "$pad"))
74
+ echo ""
75
+ echo "── ${msg} ${line}"
76
+ echo ""
77
+ }
78
+
63
79
  # ── Machine-aware Colima start ───────────────────────────────────
64
80
  _colima_start() {
65
81
  local total_cpu total_mem_gb vm_cpu vm_mem vm_disk
@@ -185,6 +201,8 @@ IMAGE="$DEFAULT_IMAGE"
185
201
  SHARED_MODULES=false
186
202
  INSTANCE_NAME=""
187
203
  PROJECT_DIR_FLAG=""
204
+ REPO_URL=""
205
+ REPO_BRANCH=""
188
206
  DOWN_ALL=false
189
207
  DOWN_CLEAN=false
190
208
  SKIP_PERMISSIONS=false
@@ -207,6 +225,14 @@ parse_flags() {
207
225
  PROJECT_DIR_FLAG="${2:?'--dir requires a value'}"
208
226
  shift 2
209
227
  ;;
228
+ -r|--repo)
229
+ REPO_URL="${2:?'--repo requires a value'}"
230
+ shift 2
231
+ ;;
232
+ -b|--branch)
233
+ REPO_BRANCH="${2:?'--branch requires a value'}"
234
+ shift 2
235
+ ;;
210
236
  --shared-modules)
211
237
  SHARED_MODULES=true
212
238
  shift
@@ -259,6 +285,31 @@ elif [[ "$SAFE_MODE" == "true" ]]; then
259
285
  export AIBOX_MODE="safe"
260
286
  fi
261
287
 
288
+ # ── Repo clone ───────────────────────────────────────────────────
289
+ if [[ -n "$REPO_URL" ]]; then
290
+ # Derive a directory name from the repo URL (hash prevents collisions)
291
+ _repo_name=$(basename "$REPO_URL" .git)
292
+ _repo_hash=$(printf '%s' "$REPO_URL" | shasum | cut -c1-6)
293
+ _repo_dir="${CONFIG_DIR}/repos/${_repo_name}-${_repo_hash}"
294
+
295
+ if [[ -d "$_repo_dir/.git" ]]; then
296
+ echo "Repo already cloned (${_repo_dir}). Reusing."
297
+ # Fetch latest and checkout branch if specified
298
+ if [[ -n "$REPO_BRANCH" ]]; then
299
+ git -C "$_repo_dir" fetch --all --quiet 2>/dev/null || true
300
+ git -C "$_repo_dir" checkout "$REPO_BRANCH" 2>/dev/null || true
301
+ fi
302
+ else
303
+ echo "Cloning ${REPO_URL}..."
304
+ mkdir -p "${CONFIG_DIR}/repos"
305
+ _clone_args=(--recursive)
306
+ [[ -n "$REPO_BRANCH" ]] && _clone_args+=(--branch "$REPO_BRANCH")
307
+ git clone "${_clone_args[@]}" "$REPO_URL" "$_repo_dir"
308
+ fi
309
+
310
+ PROJECT_DIR_FLAG="$_repo_dir"
311
+ fi
312
+
262
313
  # ── Per-project config file (.aibox) ─────────────────────────────
263
314
  if [[ -n "$PROJECT_DIR_FLAG" ]]; then
264
315
  PROJECT_DIR="$(cd "$PROJECT_DIR_FLAG" 2>/dev/null && pwd)" || {
@@ -347,7 +398,7 @@ if ! [[ "$INSTANCE_NAME" =~ ^[a-z0-9_-]+$ ]]; then
347
398
  exit 1
348
399
  fi
349
400
  CONTAINER_NAME="${BASE_CONTAINER_NAME}-${INSTANCE_NAME}"
350
- WORKSPACE_DIR="/workspace/${PROJECT_NAME}"
401
+ WORKSPACE_DIR="${PROJECT_DIR}"
351
402
 
352
403
  # ── Isolation mode setup ─────────────────────────────────────────
353
404
  COPY_VOLUME=""
@@ -385,7 +436,7 @@ ensure_dockerfile() {
385
436
 
386
437
  if [[ "$current_version" != "$AIBOX_VERSION" || ! -f "$dockerfile" ]]; then
387
438
  if [[ -f "$dockerfile" && "$current_version" != "$AIBOX_VERSION" ]]; then
388
- echo "Dockerfile outdated (v${current_version} → v${AIBOX_VERSION}). Regenerating..."
439
+ echo "Dockerfile outdated (v${current_version} → v${AIBOX_VERSION}). Regenerating (one-time)..."
389
440
  fi
390
441
  cat > "$dockerfile" << DOCKERFILE
391
442
  FROM node:20-alpine
@@ -436,16 +487,6 @@ set -e
436
487
  # Fix auth volume ownership (Docker creates volumes as root)
437
488
  chown -R aibox:aibox /home/aibox/.claude 2>/dev/null || true
438
489
 
439
- # ── IDE path symlink ─────────────────────────────────────────
440
- # Create symlink so container paths match host paths for IDE integration.
441
- # Claude Code reports cwd-based paths to the IDE plugin; if they don't
442
- # match host paths, diffs break (empty left side, wrong file path).
443
- if [[ -n "${AIBOX_HOST_DIR:-}" && -n "${AIBOX_WORKSPACE_DIR:-}" \
444
- && "$AIBOX_HOST_DIR" != "$AIBOX_WORKSPACE_DIR" ]]; then
445
- mkdir -p "$(dirname "$AIBOX_HOST_DIR")"
446
- ln -sfn "$AIBOX_WORKSPACE_DIR" "$AIBOX_HOST_DIR"
447
- fi
448
-
449
490
  # ── Mode-dependent setup ──────────────────────────────────────
450
491
  MODE="${AIBOX_MODE:-safe}"
451
492
 
@@ -545,14 +586,14 @@ _prepare_copy_volume() {
545
586
 
546
587
  # Check if volume already has content (--entrypoint bypasses su-exec drop)
547
588
  local has_content
548
- has_content=$(docker run --rm --entrypoint sh -v "${COPY_VOLUME}:${WORKSPACE_DIR}" "$IMAGE" -c "ls -A ${WORKSPACE_DIR} 2>/dev/null | head -1" 2>/dev/null || true)
589
+ has_content=$(docker run --rm --entrypoint sh -v "${COPY_VOLUME}:${WORKSPACE_DIR}" "$IMAGE" -c "ls -A '${WORKSPACE_DIR}' 2>/dev/null | head -1" 2>/dev/null || true)
549
590
 
550
591
  if [[ -n "$has_content" ]]; then
551
592
  echo "Copy volume already populated (${COPY_VOLUME}). Reusing."
552
593
  return
553
594
  fi
554
595
 
555
- echo "Copying project into isolated volume (branch: ${branch_name})..."
596
+ _step "Copying project into volume (branch: ${branch_name})"
556
597
 
557
598
  # Create bundle to a temp file first so we can check for failure
558
599
  local bundle_file
@@ -568,12 +609,12 @@ _prepare_copy_volume() {
568
609
  -v "${COPY_VOLUME}:${WORKSPACE_DIR}" \
569
610
  "$IMAGE" -c "
570
611
  cat > /tmp/repo.bundle
571
- mkdir -p ${WORKSPACE_DIR}
572
- cd ${WORKSPACE_DIR}
612
+ mkdir -p '${WORKSPACE_DIR}'
613
+ cd '${WORKSPACE_DIR}'
573
614
  git clone /tmp/repo.bundle .
574
615
  rm /tmp/repo.bundle
575
616
  git checkout -b '${branch_name}' 2>/dev/null || git checkout '${branch_name}'
576
- chown -R aibox:aibox ${WORKSPACE_DIR}
617
+ chown -R aibox:aibox '${WORKSPACE_DIR}'
577
618
  "; then
578
619
  rm -f "$bundle_file"
579
620
  docker volume rm "$COPY_VOLUME" 2>/dev/null || true
@@ -596,7 +637,7 @@ _prepare_worktree() {
596
637
 
597
638
  mkdir -p "${CONFIG_DIR}/worktrees"
598
639
 
599
- echo "Creating worktree (branch: ${branch_name})..."
640
+ _step "Creating worktree (branch: ${branch_name})"
600
641
 
601
642
  # Try creating with a new branch, fall back to existing branch
602
643
  if git -C "$PROJECT_DIR" worktree add "$WORKTREE_DIR" -b "$branch_name" 2>/dev/null; then
@@ -659,8 +700,6 @@ _environment_yaml() {
659
700
  echo "${indent}- CLAUDE_CONFIG_DIR=/home/aibox/.claude"
660
701
  echo "${indent}- AIBOX_MODE=\${AIBOX_MODE:-safe}"
661
702
  echo "${indent}- AIBOX_EXTRA_DOMAINS=\${AIBOX_EXTRA_DOMAINS:-}"
662
- echo "${indent}- AIBOX_HOST_DIR=${PROJECT_DIR}"
663
- echo "${indent}- AIBOX_WORKSPACE_DIR=${WORKSPACE_DIR}"
664
703
  }
665
704
 
666
705
  # Build -e flags for docker exec from current terminal environment
@@ -832,15 +871,16 @@ dc() {
832
871
  cmd_build() {
833
872
  _check_deps
834
873
  ensure_dockerfile
835
- echo "Building ${IMAGE}..."
874
+ _step "Building ${IMAGE}"
836
875
  docker build -t "$IMAGE" "$CONFIG_DIR"
837
- echo "Done. Image: ${IMAGE}"
876
+ echo "Done."
838
877
  }
839
878
 
840
879
  ensure_init() {
841
880
  if [[ ! -f "${PROJECT_DIR}/compose.dev.yaml" ]]; then
842
881
  _require_safe_dir
843
- echo "No ${SCRIPT_NAME} config found. Will create compose.dev.yaml, .aibox, and .idea/workspace.xml."
882
+ _step "Initializing project (one-time)"
883
+ echo "Will create compose.dev.yaml, .aibox, and .idea/workspace.xml."
844
884
  if _confirm_yes "Initialize?"; then
845
885
  _init_files
846
886
  echo ""
@@ -856,14 +896,14 @@ cmd_up() {
856
896
  ensure_init
857
897
 
858
898
  if ! docker image inspect "$IMAGE" &>/dev/null; then
859
- echo "Image '${IMAGE}' not found. Building..."
899
+ echo "Image '${IMAGE}' not found. Building (one-time)..."
860
900
  cmd_build
861
901
  else
862
902
  # Check if image version matches script version
863
903
  local img_version
864
904
  img_version=$(docker inspect "$IMAGE" --format '{{index .Config.Labels "aibox.version"}}' 2>/dev/null || echo "")
865
905
  if [[ "$img_version" != "$AIBOX_VERSION" ]]; then
866
- echo "Image outdated (v${img_version:-0} → v${AIBOX_VERSION}). Rebuilding..."
906
+ echo "Image outdated (v${img_version:-0} → v${AIBOX_VERSION}). Rebuilding (one-time)..."
867
907
  cmd_build
868
908
  fi
869
909
  fi
@@ -878,9 +918,9 @@ cmd_up() {
878
918
  if ! docker ps --format '{{.Names}}' | grep -Fxq "$CONTAINER_NAME"; then
879
919
  # Ensure shared auth volume exists (external: true requires it)
880
920
  docker volume create "$AUTH_VOLUME" &>/dev/null || true
881
- echo "Starting ${CONTAINER_NAME} (image: ${IMAGE})..."
921
+ _step "Starting ${CONTAINER_NAME}"
882
922
  dc up -d
883
- echo "Container running. Workspace: ${WORKSPACE_DIR}"
923
+ echo "Container running."
884
924
  else
885
925
  echo "${CONTAINER_NAME} is already running."
886
926
  fi
@@ -930,7 +970,7 @@ cmd_claude() {
930
970
  local current_mode
931
971
  current_mode=$(docker exec "$CONTAINER_NAME" sh -c 'echo ${AIBOX_MODE:-safe}' 2>/dev/null || echo "unknown")
932
972
  if [[ "$current_mode" != "$AIBOX_MODE" ]]; then
933
- echo "Restarting container in ${AIBOX_MODE} mode (was: ${current_mode})..."
973
+ _step "Restarting container in ${AIBOX_MODE} mode (was: ${current_mode})"
934
974
  dc down --remove-orphans >/dev/null
935
975
  fi
936
976
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aibox-cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.2",
4
4
  "description": "Run AI coding agents in isolated Docker containers",
5
5
  "author": "repalash <palash@shaders.app>",
6
6
  "license": "MIT",
@@ -35,5 +35,8 @@
35
35
  ],
36
36
  "engines": {
37
37
  "node": ">=18"
38
+ },
39
+ "scripts": {
40
+ "release": "git tag v$npm_package_version && git push origin v$npm_package_version"
38
41
  }
39
42
  }