@alanbem/dclaude 0.0.13 → 0.0.15

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 +111 -18
  2. package/dclaude +424 -22
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,29 +1,31 @@
1
- # dclaude
1
+ # dclaude — Dockerized Claude Code
2
2
 
3
+ [![CI/CD](https://github.com/alanbem/dclaude/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/alanbem/dclaude/actions/workflows/ci-cd.yml)
3
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
- [![Docker Hub](https://img.shields.io/docker/v/alanbem/dclaude?label=Docker%20Hub)](https://hub.docker.com/r/alanbem/dclaude)
5
+ [![Docker Pulls](https://img.shields.io/docker/pulls/alanbem/dclaude)](https://hub.docker.com/r/alanbem/dclaude)
5
6
  [![npm](https://img.shields.io/npm/v/@alanbem/dclaude)](https://www.npmjs.com/package/@alanbem/dclaude)
6
7
 
7
- Run Claude Code CLI in Docker - no local installation needed. Full MCP support, persistent sessions, and seamless host integration.
8
+ Run Claude Code CLI in Docker no local installation needed. Full MCP support, persistent sessions, and seamless host integration.
8
9
 
9
10
  ## Why dclaude?
10
11
 
11
12
  **Claude Code CLI is powerful, but installing it locally means:**
12
- - Node.js version conflicts
13
- - Global npm packages cluttering your system
14
13
  - MCP servers needing specific Python/Node setups
15
14
  - Different behavior across machines
15
+ - Claude has access to your entire filesystem
16
16
 
17
17
  **dclaude solves this by running Claude in a container that feels native:**
18
+ - **Safer `--dangerously-skip-permissions`** - container isolation means Claude can only access your project, not your whole system
18
19
  - Your files appear at the same paths (no `/app` or `/workspace` confusion)
19
20
  - Docker commands work (socket is mounted)
20
21
  - SSH keys and git config just work
21
22
  - Homebrew included - easy migration from local macOS setup
22
23
  - Works on Linux, macOS, and Windows
23
- - **Safer `--dangerously-skip-permissions`** - container isolation means Claude can only access your project, not your whole system
24
24
 
25
25
  ## Quick Start
26
26
 
27
+ > **Prerequisite:** A Docker-compatible runtime such as [Docker Desktop](https://docs.docker.com/get-docker/), [OrbStack](https://orbstack.dev/), or [Colima](https://github.com/abiosoft/colima) must be installed and running.
28
+
27
29
  ### Install via NPM (Recommended)
28
30
 
29
31
  ```bash
@@ -42,6 +44,8 @@ sudo ln -s ~/tools/dclaude/dclaude /usr/local/bin/dclaude
42
44
  dclaude
43
45
  ```
44
46
 
47
+ > **First run:** The Docker image (~1GB) is pulled automatically on first launch. You'll be prompted to authenticate with your Anthropic account.
48
+
45
49
  ## Basic Usage
46
50
 
47
51
  **dclaude passes all arguments directly to Claude CLI** - use it exactly as you would use `claude`:
@@ -69,9 +73,10 @@ dclaude exec brew install ripgrep
69
73
  dclaude creates a container that mirrors your host environment:
70
74
 
71
75
  1. **Path Mirroring**: Your current directory is mounted at the *same path*
72
- - On host: `/Users/alice/projects/myapp`
73
- - In container: `/Users/alice/projects/myapp`
74
- - All your file paths just work
76
+ ```
77
+ Host: /Users/alice/projects/myapp ──mount──> Container: /Users/alice/projects/myapp
78
+ ```
79
+ No `/app` or `/workspace` confusion — all your file paths just work
75
80
 
76
81
  2. **Docker Access**: The Docker socket is mounted, so Claude can build images, run containers, and manage compose stacks
77
82
 
@@ -112,7 +117,7 @@ DCLAUDE_GIT_AUTH=agent-forwarding dclaude
112
117
  DCLAUDE_GIT_AUTH=key-mount dclaude
113
118
  ```
114
119
 
115
- Make sure your SSH key is loaded: `ssh-add -l`
120
+ dclaude warns you if no keys are loaded and suggests `dclaude ssh keys` to load them automatically (see also [SSH Key and Server Management](#ssh-key-and-server-management)).
116
121
 
117
122
  ### Homebrew Support
118
123
 
@@ -132,25 +137,68 @@ dclaude gh # Interactive GitHub login
132
137
  dclaude exec gh pr list # Use gh commands
133
138
  ```
134
139
 
135
- ### SSH Server for IDEs
140
+ ### Git Configuration
141
+
142
+ SSH agent forwarding provides authentication for git, but git also needs your identity (name/email) for commits. Use `dclaude git` to configure this:
143
+
144
+ ```bash
145
+ dclaude git # Configure git name/email in container
146
+ ```
147
+
148
+ This reads your host's `~/.gitconfig` and offers to copy the identity into the container's persistent volume.
136
149
 
137
- Connect JetBrains Gateway, VS Code Remote, or any SSH client:
150
+ ### SSH Key and Server Management
151
+
152
+ Load SSH keys and start SSH server for JetBrains Gateway, VS Code Remote, or any SSH client (see also [SSH Authentication](#ssh-authentication)):
138
153
 
139
154
  ```bash
140
- dclaude ssh # Start SSH server, shows port
155
+ dclaude ssh # Load keys + start SSH server
156
+ dclaude ssh keys # Load SSH keys into agent
157
+ dclaude ssh server # Start SSH server, shows port
158
+ dclaude ssh server --stop # Stop SSH server
141
159
  # Connect: ssh claude@localhost -p <port>
142
160
  # Password: claude
143
161
  ```
144
162
 
145
163
  ### Chrome DevTools Integration
146
164
 
147
- Control Chrome via MCP for browser automation:
165
+ Control Chrome via MCP for browser automation. Chrome runs on the host with remote debugging; Claude connects from the container:
148
166
 
149
167
  ```bash
150
- dclaude chrome # Launch Chrome with DevTools
168
+ dclaude chrome # Launch Chrome with DevTools + create .mcp.json
169
+ dclaude chrome --port=9223 # Use custom debugging port
170
+ dclaude chrome --setup-only # Create .mcp.json without launching Chrome
151
171
  dclaude # Claude can now interact with the browser
152
172
  ```
153
173
 
174
+ Chrome profiles are stored per-project in `.dclaude.d/chrome/profiles/`. Customize with `DCLAUDE_CHROME_PROFILE`, `DCLAUDE_CHROME_PORT`, `DCLAUDE_CHROME_BIN`, and `DCLAUDE_CHROME_FLAGS`.
175
+
176
+ ### AWS CLI Integration
177
+
178
+ AWS CLI v2 is pre-installed in the container. Configure how AWS credentials are provided:
179
+
180
+ ```bash
181
+ # Auto (default): mounts ~/.aws from host if it exists, otherwise no config
182
+ dclaude
183
+
184
+ # Mount host's ~/.aws directory (read-write, shared with host)
185
+ DCLAUDE_AWS_CLI=mount dclaude
186
+
187
+ # Use isolated Docker volume (persists across container recreations)
188
+ DCLAUDE_AWS_CLI=volume dclaude
189
+
190
+ # No AWS config mounting
191
+ DCLAUDE_AWS_CLI=none dclaude
192
+ ```
193
+
194
+ **Volume mode** provides isolated AWS config per namespace:
195
+
196
+ ```bash
197
+ dclaude aws configure # Copy ~/.aws/config (profiles, regions) into container
198
+ dclaude aws login # Run 'aws login' in container
199
+ dclaude aws login --profile staging # Login with specific profile
200
+ ```
201
+
154
202
  ### iTerm2 Shell Integration
155
203
 
156
204
  If you use iTerm2 on macOS, dclaude automatically enables [iTerm2 Shell Integration](https://iterm2.com/documentation-shell-integration.html):
@@ -173,6 +221,7 @@ dclaude automatically tells Claude about its container environment so it can giv
173
221
  - **Network mode** - Whether `localhost` works or needs `host.docker.internal`
174
222
  - **Docker access** - Whether Docker commands are available
175
223
  - **SSH auth method** - How git authentication is configured
224
+ - **AWS CLI** - Whether and how AWS credentials are configured
176
225
  - **Path mirroring** - That file paths match the host
177
226
 
178
227
  This helps Claude understand its environment without you explaining it. Disable if needed:
@@ -181,6 +230,24 @@ This helps Claude understand its environment without you explaining it. Disable
181
230
  DCLAUDE_SYSTEM_CONTEXT=false dclaude
182
231
  ```
183
232
 
233
+ ## Container Management
234
+
235
+ ```bash
236
+ # Session management
237
+ dclaude new # Start a new Claude session
238
+ dclaude attach <session> # Reattach to a named tmux session
239
+ DCLAUDE_TMUX_SESSION=reviewer dclaude # Start a named session
240
+
241
+ # Container lifecycle
242
+ dclaude stop # Stop container for current directory
243
+ dclaude rm # Remove container (add -f to force)
244
+
245
+ # Maintenance
246
+ dclaude pull # Pull latest Docker image
247
+ dclaude update # Update Claude CLI inside container
248
+ dclaude shell # Open a bash shell (alias for exec)
249
+ ```
250
+
184
251
  ## Environment Variables
185
252
 
186
253
  | Variable | Default | Description |
@@ -195,7 +262,10 @@ DCLAUDE_SYSTEM_CONTEXT=false dclaude
195
262
  | `DCLAUDE_QUIET` | `false` | Suppress info messages |
196
263
  | `DCLAUDE_NO_UPDATE` | `false` | Skip image update check |
197
264
  | `DCLAUDE_SYSTEM_CONTEXT` | `true` | Inform Claude about container environment |
265
+ | `DCLAUDE_AWS_CLI` | `auto` | AWS config: `auto`, `mount`, `volume`, `none` |
266
+ | `DCLAUDE_TMUX_SESSION` | `claude-TIMESTAMP` | Custom tmux session name for concurrent sessions |
198
267
  | `DCLAUDE_ITERM2` | `true` | Enable iTerm2 shell integration (only affects iTerm2) |
268
+ | `DCLAUDE_CA_CERT` | (none) | Path to CA certificate for corporate SSL inspection |
199
269
 
200
270
  ## Configuration File
201
271
 
@@ -206,6 +276,7 @@ Create a `.dclaude` file at your project root to configure dclaude for that dire
206
276
  NAMESPACE=mycompany
207
277
  NETWORK=host
208
278
  DEBUG=true
279
+ CA_CERT=certs/corporate-ca.pem
209
280
  ```
210
281
 
211
282
  dclaude walks up the directory tree to find `.dclaude` files. Any dclaude session started from that directory or any subdirectory will use these settings.
@@ -220,6 +291,10 @@ dclaude walks up the directory tree to find `.dclaude` files. Any dclaude sessio
220
291
  | `MOUNT_ROOT` | Mount directory (relative to config file, or absolute path) |
221
292
  | `DEBUG` | Enable debug output (`true`, `false`) |
222
293
  | `CHROME_PORT` | Chrome DevTools port |
294
+ | `AWS_CLI` | AWS config mode (`mount`, `volume`, `none`) |
295
+ | `CA_CERT` | Path to CA certificate for corporate SSL inspection (relative to config file, or absolute) |
296
+
297
+ Config file variables are the unprefixed equivalents of `DCLAUDE_*` environment variables (e.g., `NAMESPACE` in `.dclaude` = `DCLAUDE_NAMESPACE` env var).
223
298
 
224
299
  **Precedence:** Environment variables override `.dclaude` file settings.
225
300
 
@@ -315,18 +390,19 @@ DCLAUDE_NETWORK=bridge dclaude
315
390
  |----------|--------|-------|
316
391
  | Linux | Full support | Host networking available |
317
392
  | macOS | Full support | Host networking with OrbStack or Docker Desktop beta |
318
- | Windows | Full support | WSL2/Docker Desktop, host networking with beta features |
393
+ | Windows | Full support | Requires WSL2; host networking with Docker Desktop beta features |
319
394
 
320
395
  ## What's Included
321
396
 
322
397
  The container includes:
323
398
  - **Ubuntu 24.04 LTS** base
324
- - **Claude Code CLI** (latest)
399
+ - **Claude Code CLI** (latest, AMD64 and ARM64/Apple Silicon)
325
400
  - **Node.js 20+**, **Python 3** with pip
326
401
  - **Homebrew/Linuxbrew** for package management
327
402
  - **Docker CLI** and **Docker Compose**
328
403
  - **Git**, **GitHub CLI** (`gh`), common dev tools
329
404
  - **tmux** for session management
405
+ - **AWS CLI v2** for cloud operations
330
406
  - **SSH server** for IDE integration
331
407
 
332
408
  ## Troubleshooting
@@ -384,6 +460,21 @@ mv problematic-dir problematic-dir.tmp && mv problematic-dir.tmp problematic-dir
384
460
 
385
461
  Upgrading to the latest OrbStack version may also help.
386
462
 
463
+ ## Shell Completions
464
+
465
+ Tab completion is available for bash and zsh:
466
+
467
+ ```bash
468
+ # Bash — add to ~/.bashrc
469
+ source "$(npm root -g)/@alanbem/dclaude/completions/dclaude.bash"
470
+
471
+ # Zsh — add to ~/.zshrc
472
+ source "$(npm root -g)/@alanbem/dclaude/completions/dclaude.zsh"
473
+
474
+ # Source install — use the repo path directly
475
+ source ~/tools/dclaude/completions/dclaude.bash
476
+ ```
477
+
387
478
  ## Project Structure
388
479
 
389
480
  ```text
@@ -393,7 +484,9 @@ Upgrading to the latest OrbStack version may also help.
393
484
  │ ├── Dockerfile # Container image definition
394
485
  │ ├── README.md # Docker Hub documentation
395
486
  │ ├── usr/local/bin/
396
- │ │ └── docker-entrypoint.sh
487
+ │ │ ├── docker-entrypoint.sh
488
+ │ │ ├── claude-launcher.sh
489
+ │ │ └── tmux-wrapper.sh
397
490
  │ └── home/claude/
398
491
  │ └── .tmux.conf
399
492
  ├── .github/workflows/ # CI/CD (lint, scan, publish)
package/dclaude CHANGED
@@ -135,6 +135,21 @@ load_config_file() {
135
135
  fi
136
136
  fi
137
137
  ;;
138
+ AWS_CLI)
139
+ [[ -z "${DCLAUDE_AWS_CLI:-}" ]] && DCLAUDE_AWS_CLI="$value"
140
+ ;;
141
+ CA_CERT)
142
+ if [[ -z "${DCLAUDE_CA_CERT:-}" ]]; then
143
+ # Resolve relative paths relative to config file's directory
144
+ if [[ "$value" != /* ]]; then
145
+ local config_dir
146
+ config_dir=$(dirname "$config_file")
147
+ DCLAUDE_CA_CERT=$(cd "$config_dir" && realpath "$value" 2>/dev/null)
148
+ else
149
+ DCLAUDE_CA_CERT="$value"
150
+ fi
151
+ fi
152
+ ;;
138
153
  esac
139
154
  fi
140
155
  done < "$config_file"
@@ -151,6 +166,12 @@ readonly DEBUG="${DCLAUDE_DEBUG:-false}"
151
166
  readonly GIT_AUTH_MODE="${DCLAUDE_GIT_AUTH:-auto}" # auto, agent-forwarding, key-mount, none
152
167
  readonly NAMESPACE="${DCLAUDE_NAMESPACE:-}"
153
168
  CHROME_PORT="${DCLAUDE_CHROME_PORT:-9222}"
169
+ readonly AWS_CLI_MODE="${DCLAUDE_AWS_CLI:-auto}" # auto, mount, volume, none
170
+ # Resolve CA_CERT relative paths (env var: relative to PWD, config file: already resolved)
171
+ if [[ -n "${DCLAUDE_CA_CERT:-}" && "${DCLAUDE_CA_CERT}" != /* ]]; then
172
+ DCLAUDE_CA_CERT=$(realpath "$DCLAUDE_CA_CERT" 2>/dev/null) || DCLAUDE_CA_CERT="$PWD/$DCLAUDE_CA_CERT"
173
+ fi
174
+ readonly CA_CERT="${DCLAUDE_CA_CERT:-}" # Path to CA certificate for corporate proxies
154
175
 
155
176
  # Show config file if loaded (always at info level, details at debug)
156
177
  if [[ -n "$DCLAUDE_CONFIG_FILE" ]]; then
@@ -161,6 +182,8 @@ if [[ -n "$DCLAUDE_CONFIG_FILE" ]]; then
161
182
  [[ -n "${DCLAUDE_GIT_AUTH:-}" ]] && debug " GIT_AUTH=$DCLAUDE_GIT_AUTH"
162
183
  [[ -n "${DCLAUDE_CHROME_PORT:-}" ]] && debug " CHROME_PORT=$DCLAUDE_CHROME_PORT"
163
184
  [[ -n "${DCLAUDE_MOUNT_ROOT:-}" ]] && debug " MOUNT_ROOT=$DCLAUDE_MOUNT_ROOT"
185
+ [[ -n "${DCLAUDE_AWS_CLI:-}" ]] && debug " AWS_CLI=$DCLAUDE_AWS_CLI"
186
+ [[ -n "$CA_CERT" ]] && debug " CA_CERT=$CA_CERT"
164
187
  fi
165
188
  fi
166
189
 
@@ -510,6 +533,36 @@ get_volume_name() {
510
533
  fi
511
534
  }
512
535
 
536
+ # Get AWS volume name (includes namespace if set)
537
+ get_aws_volume_name() {
538
+ if [[ -n "$NAMESPACE" ]]; then
539
+ echo "${VOLUME_PREFIX}-${NAMESPACE}-aws"
540
+ else
541
+ echo "${VOLUME_PREFIX}-aws"
542
+ fi
543
+ }
544
+
545
+ # Resolve AWS CLI mode from configured value
546
+ # auto → mount (if ~/.aws exists) or none
547
+ resolve_aws_cli_mode() {
548
+ case "$AWS_CLI_MODE" in
549
+ mount|volume|none)
550
+ echo "$AWS_CLI_MODE"
551
+ ;;
552
+ auto)
553
+ if [[ -d "${HOME}/.aws" ]]; then
554
+ echo "mount"
555
+ else
556
+ echo "none"
557
+ fi
558
+ ;;
559
+ *)
560
+ warning "Unknown AWS_CLI mode: $AWS_CLI_MODE (using none)"
561
+ echo "none"
562
+ ;;
563
+ esac
564
+ }
565
+
513
566
  # Create Docker volumes if they don't exist
514
567
  create_volumes() {
515
568
  # Create essential volume for Claude CLI persistence
@@ -526,6 +579,24 @@ create_volumes() {
526
579
  else
527
580
  debug "Volume exists: $volume"
528
581
  fi
582
+
583
+ # Create AWS volume if volume mode is active
584
+ local aws_mode
585
+ aws_mode=$(resolve_aws_cli_mode)
586
+ if [[ "$aws_mode" == "volume" ]]; then
587
+ local aws_volume
588
+ aws_volume=$(get_aws_volume_name)
589
+ if ! docker volume inspect "$aws_volume" &> /dev/null; then
590
+ info "Creating AWS volume: $aws_volume"
591
+ if ! docker volume create "$aws_volume" > /dev/null; then
592
+ error "Failed to create volume: $aws_volume"
593
+ exit 1
594
+ fi
595
+ debug "Volume created successfully: $aws_volume"
596
+ else
597
+ debug "Volume exists: $aws_volume"
598
+ fi
599
+ fi
529
600
  }
530
601
 
531
602
  # Pull or update the Docker image
@@ -660,6 +731,7 @@ generate_system_context() {
660
731
  local has_docker="${3:-false}"
661
732
  local platform="${4:-unknown}"
662
733
  local docker_socket="${5:-}"
734
+ local aws_cli_mode="${6:-none}"
663
735
 
664
736
  cat <<'EOF'
665
737
 
@@ -806,8 +878,36 @@ EOF
806
878
  ## Development Tools Available
807
879
  - **Languages**: Node.js 20+, Python 3
808
880
  - **Package Managers**: npm, pip, Homebrew/Linuxbrew
809
- - **Tools**: git, gh (GitHub CLI), docker, docker-compose, curl, tmux, nano
881
+ - **Tools**: git, gh (GitHub CLI), docker, docker-compose, aws (AWS CLI v2), curl, tmux, nano
810
882
  - **Shell**: bash (your commands execute in bash shell)
883
+ EOF
884
+
885
+ # AWS CLI context
886
+ case "$aws_cli_mode" in
887
+ mount)
888
+ cat <<'EOF'
889
+ - **AWS CLI**: Configured — credentials mounted from host's `~/.aws` directory
890
+ - All host AWS profiles, SSO config, and credentials are available
891
+ - Changes to AWS config (e.g., `aws sso login`) are shared with host
892
+ EOF
893
+ ;;
894
+ volume)
895
+ cat <<'EOF'
896
+ - **AWS CLI**: Configured — credentials stored in persistent Docker volume
897
+ - AWS config is isolated from host and persists across container recreations
898
+ - Run `aws configure` or `aws configure sso` to set up credentials
899
+ EOF
900
+ ;;
901
+ *)
902
+ cat <<'EOF'
903
+ - **AWS CLI**: Installed but no credentials configured
904
+ - User can set `DCLAUDE_AWS_CLI=mount` when launching dclaude to use host's `~/.aws` config
905
+ - User can set `DCLAUDE_AWS_CLI=volume` when launching dclaude for isolated persistent config
906
+ EOF
907
+ ;;
908
+ esac
909
+
910
+ cat <<'EOF'
811
911
 
812
912
  ## Git Configuration Requirements
813
913
  **IMPORTANT**: Before performing any git operations (commit, push, etc.), you MUST:
@@ -1573,14 +1673,27 @@ main() {
1573
1673
  debug "Git auth: $resolved_git_auth (user-specified)"
1574
1674
  fi
1575
1675
 
1676
+ # Warn if agent-forwarding is active but no keys are loaded
1677
+ if [[ "$resolved_git_auth" == "agent-forwarding" ]]; then
1678
+ local ssh_add_rc=0
1679
+ ssh-add -l >/dev/null 2>&1 || ssh_add_rc=$?
1680
+ if [[ $ssh_add_rc -eq 1 ]]; then
1681
+ warning "SSH agent has no keys loaded — SSH won't work inside container."
1682
+ warning "Run 'dclaude ssh keys' to load your keys."
1683
+ fi
1684
+ fi
1685
+
1576
1686
  # Generate system context for Claude (if enabled) - must be done early for all code paths
1577
1687
  local claude_args=()
1578
1688
  if [[ "$ENABLE_SYSTEM_CONTEXT" == "true" ]]; then
1579
1689
  local has_docker="false"
1580
1690
  [[ -n "$DOCKER_SOCKET" ]] && [[ -S "$DOCKER_SOCKET" ]] && has_docker="true"
1581
1691
 
1692
+ local resolved_aws_cli_mode
1693
+ resolved_aws_cli_mode=$(resolve_aws_cli_mode)
1694
+
1582
1695
  local system_context
1583
- system_context=$(generate_system_context "$network_mode" "$resolved_git_auth" "$has_docker" "$platform" "$DOCKER_SOCKET")
1696
+ system_context=$(generate_system_context "$network_mode" "$resolved_git_auth" "$has_docker" "$platform" "$DOCKER_SOCKET" "$resolved_aws_cli_mode")
1584
1697
 
1585
1698
  claude_args+=("--append-system-prompt" "$system_context")
1586
1699
  debug "System context enabled (${#system_context} chars, has_docker=$has_docker, git_auth=$resolved_git_auth, platform=$platform)"
@@ -1793,6 +1906,49 @@ main() {
1793
1906
  [[ -n "$ssh_arg" ]] && DOCKER_ARGS+=("$ssh_arg")
1794
1907
  done < <(handle_git_auth)
1795
1908
 
1909
+ # Handle AWS CLI configuration mounting
1910
+ local resolved_aws_mode
1911
+ resolved_aws_mode=$(resolve_aws_cli_mode)
1912
+ # Pass mode to entrypoint so it knows whether to chown .aws (volume only)
1913
+ DOCKER_ARGS+=(-e "DCLAUDE_AWS_CLI_MODE=${resolved_aws_mode}")
1914
+ case "$resolved_aws_mode" in
1915
+ mount)
1916
+ if [[ -d "${HOME}/.aws" ]]; then
1917
+ DOCKER_ARGS+=(-v "${HOME}/.aws:/home/claude/.aws")
1918
+ info "AWS CLI: mounting host ~/.aws"
1919
+ debug "AWS config mounted from host: ${HOME}/.aws"
1920
+ else
1921
+ warning "AWS_CLI=mount but ~/.aws not found, skipping"
1922
+ fi
1923
+ ;;
1924
+ volume)
1925
+ local aws_volume
1926
+ aws_volume=$(get_aws_volume_name)
1927
+ DOCKER_ARGS+=(-v "${aws_volume}:/home/claude/.aws")
1928
+ debug "AWS config using volume: $aws_volume"
1929
+ ;;
1930
+ none)
1931
+ debug "AWS CLI config mounting disabled"
1932
+ ;;
1933
+ esac
1934
+
1935
+ # Handle CA certificate for corporate proxies / SSL inspection
1936
+ # The cert file must be within the mounted directory tree to be accessible inside the container
1937
+ if [[ -n "$CA_CERT" ]]; then
1938
+ if [[ ! -f "$CA_CERT" ]]; then
1939
+ warning "CA certificate not found: $CA_CERT"
1940
+ elif [[ "$CA_CERT" != "$MOUNT_ROOT"* ]]; then
1941
+ warning "CA certificate is outside the mounted directory: $CA_CERT"
1942
+ warning "It must be within: $MOUNT_ROOT"
1943
+ else
1944
+ DOCKER_ARGS+=(
1945
+ -e "NODE_EXTRA_CA_CERTS=${CA_CERT}"
1946
+ -e "SSL_CERT_FILE=${CA_CERT}"
1947
+ )
1948
+ info "CA certificate: $CA_CERT"
1949
+ fi
1950
+ fi
1951
+
1796
1952
  # Add any additional environment variables
1797
1953
  if [[ -n "${CLAUDE_MODEL:-}" ]]; then
1798
1954
  DOCKER_ARGS+=(-e "CLAUDE_MODEL=${CLAUDE_MODEL}")
@@ -2269,8 +2425,117 @@ cmd_stop() {
2269
2425
  exit 0
2270
2426
  }
2271
2427
 
2272
- # Subcommand: SSH server for remote access (JetBrains Gateway, debugging, etc.)
2428
+ # Subcommand: SSH dispatches to 'keys', 'server', or both
2273
2429
  cmd_ssh() {
2430
+ local rc=0
2431
+ case "${1:-}" in
2432
+ keys)
2433
+ shift
2434
+ cmd_ssh_keys "$@" || rc=$?
2435
+ ;;
2436
+ server)
2437
+ shift
2438
+ cmd_ssh_server "$@" || rc=$?
2439
+ ;;
2440
+ --help|-h)
2441
+ cat << 'SSH_HELP'
2442
+ dclaude ssh - SSH Key and Server Management
2443
+
2444
+ Usage:
2445
+ dclaude ssh Load SSH keys into agent and start SSH server
2446
+ dclaude ssh keys Load SSH keys into agent
2447
+ dclaude ssh server Start SSH server and show connection info
2448
+ dclaude ssh server --stop Stop SSH server
2449
+
2450
+ SSH_HELP
2451
+ exit 0
2452
+ ;;
2453
+ "")
2454
+ # Bare 'dclaude ssh': load keys (non-fatal), then start server
2455
+ cmd_ssh_keys "$@" || true
2456
+ echo ""
2457
+ cmd_ssh_server "$@" || rc=$?
2458
+ ;;
2459
+ *)
2460
+ error "Unknown subcommand: $1"
2461
+ info "Usage: dclaude ssh [keys | server]"
2462
+ exit 1
2463
+ ;;
2464
+ esac
2465
+ exit "$rc"
2466
+ }
2467
+
2468
+ # Subcommand: SSH key loading
2469
+ cmd_ssh_keys() {
2470
+ # Parse flags
2471
+ while [[ $# -gt 0 ]]; do
2472
+ case "$1" in
2473
+ --help|-h)
2474
+ cat << 'SSH_KEYS_HELP'
2475
+ dclaude ssh keys - Load SSH Keys into Agent
2476
+
2477
+ Usage:
2478
+ dclaude ssh keys Detect and load SSH keys from ~/.ssh/
2479
+
2480
+ Detects private keys (id_rsa, id_ed25519, etc.) in ~/.ssh/ and loads
2481
+ them into the SSH agent via ssh-add. May prompt for passphrases.
2482
+
2483
+ SSH_KEYS_HELP
2484
+ return 0
2485
+ ;;
2486
+ *)
2487
+ error "Unknown option: $1"
2488
+ info "Usage: dclaude ssh keys"
2489
+ return 1
2490
+ ;;
2491
+ esac
2492
+ done
2493
+
2494
+ local ssh_dir="${HOME}/.ssh"
2495
+ if [[ ! -d "$ssh_dir" ]]; then
2496
+ error "No ~/.ssh directory found"
2497
+ return 1
2498
+ fi
2499
+
2500
+ # Detect private key files
2501
+ local keys=()
2502
+ for key_file in "$ssh_dir"/id_*; do
2503
+ [[ -f "$key_file" ]] || continue
2504
+ # Skip public keys and certificates
2505
+ [[ "$key_file" == *.pub ]] && continue
2506
+ [[ "$key_file" == *-cert.pub ]] && continue
2507
+ keys+=("$key_file")
2508
+ done
2509
+
2510
+ if [[ ${#keys[@]} -eq 0 ]]; then
2511
+ warning "No SSH keys found in ~/.ssh/"
2512
+ info "Generate a key with: ssh-keygen -t ed25519"
2513
+ return 1
2514
+ fi
2515
+
2516
+ info "Loading SSH keys..."
2517
+ local loaded=0
2518
+ for key_file in "${keys[@]}"; do
2519
+ local display_path="~/.ssh/$(basename "$key_file")"
2520
+ if ssh-add "$key_file" 2>/dev/null; then
2521
+ success " $display_path"
2522
+ loaded=$((loaded + 1))
2523
+ else
2524
+ warning " $display_path (failed — wrong passphrase or unsupported key)"
2525
+ fi
2526
+ done
2527
+
2528
+ echo ""
2529
+ if [[ $loaded -gt 0 ]]; then
2530
+ success "$loaded key(s) loaded into SSH agent."
2531
+ else
2532
+ error "No keys were loaded"
2533
+ return 1
2534
+ fi
2535
+ }
2536
+
2537
+ # Subcommand: SSH server for remote access (JetBrains Gateway, debugging, etc.)
2538
+ cmd_ssh_server() {
2274
2539
  local action=""
2275
2540
 
2276
2541
  # Parse flags
@@ -2281,12 +2546,12 @@ cmd_ssh() {
2281
2546
  shift
2282
2547
  ;;
2283
2548
  --help|-h)
2284
- cat << 'SSH_HELP'
2285
- dclaude ssh - SSH Server for Remote Access
2549
+ cat << 'SSH_SERVER_HELP'
2550
+ dclaude ssh server - SSH Server for Remote Access
2286
2551
 
2287
2552
  Usage:
2288
- dclaude ssh Start SSH server and show connection info
2289
- dclaude ssh --stop Stop SSH server
2553
+ dclaude ssh server Start SSH server and show connection info
2554
+ dclaude ssh server --stop Stop SSH server
2290
2555
 
2291
2556
  Connection:
2292
2557
  ssh claude@localhost -p <port>
@@ -2301,20 +2566,20 @@ Use Cases:
2301
2566
 
2302
2567
  JetBrains Gateway Setup:
2303
2568
  1. Start container: dclaude
2304
- 2. Start SSH: dclaude ssh
2569
+ 2. Start SSH: dclaude ssh server
2305
2570
  3. Open JetBrains Gateway
2306
2571
  4. New Connection → SSH → localhost:<port shown above>
2307
2572
  5. Username: claude, Password: claude
2308
2573
  6. Gateway will download and install IDE backend automatically
2309
2574
  7. Select your project directory
2310
2575
 
2311
- SSH_HELP
2312
- exit 0
2576
+ SSH_SERVER_HELP
2577
+ return 0
2313
2578
  ;;
2314
2579
  *)
2315
2580
  error "Unknown option: $1"
2316
- info "Usage: dclaude ssh [--stop]"
2317
- exit 1
2581
+ info "Usage: dclaude ssh server [--stop]"
2582
+ return 1
2318
2583
  ;;
2319
2584
  esac
2320
2585
  done
@@ -2325,7 +2590,7 @@ SSH_HELP
2325
2590
  if ! docker ps -a --format '{{.Names}}' | grep -q "^${container_name}$"; then
2326
2591
  error "No container found for this directory"
2327
2592
  info "Run 'dclaude' first to create a persistent container"
2328
- exit 1
2593
+ return 1
2329
2594
  fi
2330
2595
 
2331
2596
  local container_status=$(docker inspect --format='{{.State.Status}}' "$container_name" 2>/dev/null)
@@ -2337,7 +2602,7 @@ SSH_HELP
2337
2602
  sleep 1
2338
2603
  else
2339
2604
  error "Container $container_name is in unexpected state: $container_status"
2340
- exit 1
2605
+ return 1
2341
2606
  fi
2342
2607
  fi
2343
2608
 
@@ -2353,9 +2618,9 @@ SSH_HELP
2353
2618
  echo ""
2354
2619
  echo " dclaude rm -f"
2355
2620
  echo " dclaude"
2356
- echo " dclaude ssh"
2621
+ echo " dclaude ssh server"
2357
2622
  echo ""
2358
- exit 1
2623
+ return 1
2359
2624
  fi
2360
2625
 
2361
2626
  case "$action" in
@@ -2397,8 +2662,6 @@ SSH_HELP
2397
2662
  echo ""
2398
2663
  ;;
2399
2664
  esac
2400
-
2401
- exit 0
2402
2665
  }
2403
2666
 
2404
2667
  # Subcommand: remove container for current directory
@@ -2477,6 +2740,133 @@ cmd_rm() {
2477
2740
  }
2478
2741
 
2479
2742
  # Subcommand: configure git identity
2743
+ cmd_aws_configure() {
2744
+ local container_name="$1"
2745
+
2746
+ # This subcommand only makes sense for volume mode
2747
+ local resolved_mode
2748
+ resolved_mode=$(resolve_aws_cli_mode)
2749
+ if [[ "$resolved_mode" != "volume" ]]; then
2750
+ error "dclaude aws configure is only for AWS_CLI=volume mode"
2751
+ if [[ "$resolved_mode" == "mount" ]]; then
2752
+ info "Current mode is 'mount' — host's ~/.aws is already available in the container"
2753
+ else
2754
+ info "Set DCLAUDE_AWS_CLI=volume to use persistent AWS volume"
2755
+ fi
2756
+ exit 1
2757
+ fi
2758
+
2759
+ echo ""
2760
+ echo "AWS CLI Configuration"
2761
+ echo "─────────────────────"
2762
+
2763
+ # Check existing config in container
2764
+ local existing_config
2765
+ existing_config=$(docker exec -u claude "$container_name" cat /home/claude/.aws/config 2>/dev/null || echo "")
2766
+
2767
+ if [[ -n "$existing_config" ]]; then
2768
+ echo "Current config in container:"
2769
+ echo "$existing_config" | sed 's/^/ /'
2770
+ echo ""
2771
+ read -p "Overwrite with host config? [y/N]: " overwrite
2772
+ if [[ "$overwrite" != "y" && "$overwrite" != "Y" ]]; then
2773
+ exit 0
2774
+ fi
2775
+ fi
2776
+
2777
+ # Check host config
2778
+ if [[ ! -f "${HOME}/.aws/config" ]]; then
2779
+ error "No ~/.aws/config found on host"
2780
+ info "Run 'aws configure sso' on your host first, then re-run this command"
2781
+ exit 1
2782
+ fi
2783
+
2784
+ echo "Found on host:"
2785
+ sed 's/^/ /' "${HOME}/.aws/config"
2786
+ echo ""
2787
+ info "Only config (profiles, regions, SSO URLs) will be copied — no credentials or tokens"
2788
+ echo ""
2789
+ read -p "Copy to container? [Y/n]: " copy
2790
+ if [[ "$copy" == "n" || "$copy" == "N" ]]; then
2791
+ exit 0
2792
+ fi
2793
+
2794
+ # Copy config file into container (docker cp copies as root, fix ownership after)
2795
+ docker exec -u claude "$container_name" mkdir -p /home/claude/.aws
2796
+ docker cp "${HOME}/.aws/config" "${container_name}:/home/claude/.aws/config"
2797
+ docker exec "$container_name" chown claude:claude /home/claude/.aws /home/claude/.aws/config
2798
+ docker exec "$container_name" chmod 600 /home/claude/.aws/config
2799
+
2800
+ echo ""
2801
+ success "AWS config copied to container"
2802
+ info "Run 'dclaude aws login' or 'aws login' inside dclaude to authenticate"
2803
+ exit 0
2804
+ }
2805
+
2806
+ cmd_aws_login() {
2807
+ local container_name="$1"
2808
+ shift
2809
+
2810
+ local tty_flags
2811
+ tty_flags=$(detect_tty_flags)
2812
+
2813
+ info "Starting AWS login..."
2814
+ # shellcheck disable=SC2086
2815
+ exec docker exec $tty_flags -u claude "$container_name" aws login "$@"
2816
+ }
2817
+
2818
+ cmd_aws() {
2819
+ local container_name=$(get_container_name "$HOST_PATH")
2820
+
2821
+ # Check if container exists
2822
+ if ! docker ps -a --format '{{.Names}}' | grep -q "^${container_name}$"; then
2823
+ error "No container found for this directory"
2824
+ info "Run 'dclaude' first to create a container"
2825
+ exit 1
2826
+ fi
2827
+
2828
+ # Check if container is running
2829
+ local container_status=$(docker inspect --format='{{.State.Status}}' "$container_name" 2>/dev/null)
2830
+ if [[ "$container_status" != "running" ]]; then
2831
+ if [[ "$container_status" == "exited" ]]; then
2832
+ info "Starting container: $container_name"
2833
+ docker start "$container_name" >/dev/null
2834
+ sleep 1
2835
+ else
2836
+ error "Container $container_name is in unexpected state: $container_status"
2837
+ exit 1
2838
+ fi
2839
+ fi
2840
+
2841
+ # Dispatch subcommands
2842
+ local subcmd="${1:-}"
2843
+ case "$subcmd" in
2844
+ configure)
2845
+ shift
2846
+ cmd_aws_configure "$container_name" "$@"
2847
+ ;;
2848
+ login)
2849
+ shift
2850
+ cmd_aws_login "$container_name" "$@"
2851
+ ;;
2852
+ --help|-h|"")
2853
+ echo "Usage:"
2854
+ echo " dclaude aws configure Copy AWS config from host (volume mode)"
2855
+ echo " dclaude aws login Run AWS login in container"
2856
+ echo " dclaude aws login --profile <name> Login with specific profile"
2857
+ exit 0
2858
+ ;;
2859
+ *)
2860
+ error "Unknown subcommand: $subcmd"
2861
+ echo "Usage:"
2862
+ echo " dclaude aws configure Copy AWS config from host (volume mode)"
2863
+ echo " dclaude aws login Run AWS login in container"
2864
+ echo " dclaude aws login --profile <name> Login with specific profile"
2865
+ exit 1
2866
+ ;;
2867
+ esac
2868
+ }
2869
+
2480
2870
  cmd_git() {
2481
2871
  local container_name=$(get_container_name "$HOST_PATH")
2482
2872
 
@@ -2616,6 +3006,10 @@ if [[ $# -gt 0 ]]; then
2616
3006
  shift
2617
3007
  cmd_git "$@"
2618
3008
  ;;
3009
+ aws)
3010
+ shift
3011
+ cmd_aws "$@"
3012
+ ;;
2619
3013
  --help|-h|help)
2620
3014
  cat << 'EOF'
2621
3015
  dclaude - Dockerized Claude Code Launcher
@@ -2628,8 +3022,12 @@ Usage:
2628
3022
  dclaude update Update Claude CLI inside container
2629
3023
  dclaude stop Stop container for current directory
2630
3024
  dclaude rm [-f] Remove container for current directory
2631
- dclaude ssh Start SSH server for remote access
3025
+ dclaude ssh Load SSH keys and start SSH server
3026
+ dclaude ssh keys Load SSH keys into agent
3027
+ dclaude ssh server Start SSH server for remote access
2632
3028
  dclaude git Configure git identity (name/email)
3029
+ dclaude aws configure Copy AWS config from host to container (volume mode)
3030
+ dclaude aws login Run AWS login in container
2633
3031
  dclaude chrome [options] Launch Chrome with DevTools and MCP integration
2634
3032
  dclaude gh Authenticate GitHub CLI (runs gh auth login)
2635
3033
  dclaude exec [command] Execute command in container (default: bash)
@@ -2644,6 +3042,7 @@ Environment Variables:
2644
3042
  DCLAUDE_GIT_AUTH SSH auth for Git: auto, agent-forwarding, key-mount, none
2645
3043
  DCLAUDE_NETWORK Network mode: auto, host, bridge
2646
3044
  DCLAUDE_MOUNT_ROOT Mount parent directory (absolute or relative path)
3045
+ DCLAUDE_AWS_CLI AWS config mode: auto, mount, volume, none
2647
3046
  DCLAUDE_DOCKER_SOCKET Override Docker socket path
2648
3047
  DCLAUDE_TMUX_SESSION Custom tmux session name (default: claude-TIMESTAMP)
2649
3048
  DCLAUDE_CHROME_BIN Chrome executable path (auto-detected if not set)
@@ -2655,6 +3054,7 @@ Configuration File (.dclaude):
2655
3054
  Create a .dclaude file at project root to configure dclaude for that tree:
2656
3055
  NAMESPACE=mycompany
2657
3056
  NETWORK=host
3057
+ AWS_CLI=mount
2658
3058
  DEBUG=true
2659
3059
  MOUNT_ROOT=. # Mount config file's directory
2660
3060
  MOUNT_ROOT=.. # Mount parent of config file's directory
@@ -2693,9 +3093,11 @@ Examples:
2693
3093
  dclaude rm # Remove stopped container
2694
3094
  dclaude rm -f # Force remove running container
2695
3095
 
2696
- # SSH server for remote access (JetBrains Gateway, VS Code Remote, etc.)
2697
- dclaude ssh # Start SSH server, show connection info
2698
- dclaude ssh --stop # Stop SSH server
3096
+ # SSH key and server management
3097
+ dclaude ssh # Load keys + start SSH server
3098
+ dclaude ssh keys # Load SSH keys into agent
3099
+ dclaude ssh server # Start SSH server, show connection info
3100
+ dclaude ssh server --stop # Stop SSH server
2699
3101
 
2700
3102
  # Launch Chrome with DevTools for MCP integration
2701
3103
  dclaude chrome
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alanbem/dclaude",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "Dockerized Claude Code CLI launcher with MCP support",
5
5
  "main": "dclaude",
6
6
  "bin": {