claude-code-runner 0.2.10 → 0.2.12
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.
- package/README.md +56 -11
- package/README.zh-Hans.md +56 -11
- package/dist/cli.js +1 -1
- package/dist/container.d.ts +0 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +43 -154
- package/dist/container.js.map +1 -1
- package/dist/git/shadow-repository.d.ts.map +1 -1
- package/dist/git/shadow-repository.js +3 -3
- package/dist/git/shadow-repository.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -2
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts +1 -0
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +15 -0
- package/dist/ui.js.map +1 -1
- package/dist/web-server-attach.js +1 -1
- package/dist/web-server-attach.js.map +1 -1
- package/dist/web-server.d.ts +3 -0
- package/dist/web-server.d.ts.map +1 -1
- package/dist/web-server.js +39 -4
- package/dist/web-server.js.map +1 -1
- package/docker/Dockerfile +26 -0
- package/package.json +4 -2
- package/public/app.js +104 -42
- package/public/index.html +148 -0
- package/public/locales/en.json +34 -2
- package/public/locales/zh-CN.json +34 -2
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ npm install -g claude-code-runner
|
|
|
54
54
|
>
|
|
55
55
|
> For the fastest setup with pre-built image, use the official image by setting `buildImage: false` in your config. The default image (`ghcr.io/yanranxiaoxi/claude-code-runner:latest`) will be used automatically.
|
|
56
56
|
|
|
57
|
-
Simply run in any
|
|
57
|
+
Simply run in any directory:
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
60
|
claude-run
|
|
@@ -67,6 +67,19 @@ This will:
|
|
|
67
67
|
3. Launch a web UI at `http://localhost:3456`
|
|
68
68
|
4. Open your browser automatically
|
|
69
69
|
|
|
70
|
+
> [!WARNING]
|
|
71
|
+
>
|
|
72
|
+
> **Non-Git Directory Support**
|
|
73
|
+
>
|
|
74
|
+
> If you run `claude-run` in a non-git directory, the tool will prompt you to initialize a git repository. If you agree, it will automatically run `git init`, create an initial commit, and start the container. **Important**: After initialization, you must manually set up a remote repository (e.g., on GitHub) and configure the upstream to save your changes. Use commands like:
|
|
75
|
+
>
|
|
76
|
+
> ```bash
|
|
77
|
+
> git remote add origin <your-repo-url>
|
|
78
|
+
> git push -u origin main
|
|
79
|
+
> ```
|
|
80
|
+
>
|
|
81
|
+
> Without setting up the upstream, your changes will only exist locally in the container.
|
|
82
|
+
|
|
70
83
|
> [!NOTE]
|
|
71
84
|
>
|
|
72
85
|
> **Working with Passphrase-Protected SSH Keys**
|
|
@@ -83,6 +96,13 @@ This will:
|
|
|
83
96
|
|
|
84
97
|
### Commands
|
|
85
98
|
|
|
99
|
+
#### Shortcuts
|
|
100
|
+
|
|
101
|
+
The following commands are shortcuts for `claude-run`:
|
|
102
|
+
|
|
103
|
+
- `clauderun`
|
|
104
|
+
- `ccrun`
|
|
105
|
+
|
|
86
106
|
#### `claude-run` (default)
|
|
87
107
|
|
|
88
108
|
Start a new container with web UI (recommended):
|
|
@@ -227,6 +247,7 @@ Create a `claude-run.config.json` file (see `claude-run.config.example.json` for
|
|
|
227
247
|
"forwardSshKeys": true,
|
|
228
248
|
"forwardGpgKeys": true,
|
|
229
249
|
"forwardSshAgent": true,
|
|
250
|
+
"forwardGpgAgent": false,
|
|
230
251
|
"enableGpgSigning": false
|
|
231
252
|
}
|
|
232
253
|
```
|
|
@@ -254,6 +275,7 @@ Create a `claude-run.config.json` file (see `claude-run.config.example.json` for
|
|
|
254
275
|
- `forwardSshKeys`: Forward SSH keys from `~/.ssh` to container (default: true)
|
|
255
276
|
- `forwardGpgKeys`: Forward GPG keys from `~/.gnupg` to container (default: true)
|
|
256
277
|
- `forwardSshAgent`: Forward SSH agent for passphrase-protected keys (default: true)
|
|
278
|
+
- `forwardGpgAgent`: Forward GPG agent for passphrase-protected GPG keys (default: false, requires explicit enabling)
|
|
257
279
|
- `enableGpgSigning`: Enable GPG commit signing in container (default: false)
|
|
258
280
|
|
|
259
281
|
#### Mount Configuration
|
|
@@ -362,7 +384,9 @@ Claude Code Runner now supports Podman as an alternative to Docker. The tool aut
|
|
|
362
384
|
- **Custom socket paths**: Use the `dockerSocketPath` configuration option to specify a custom socket
|
|
363
385
|
- **Environment variable**: Set `DOCKER_HOST` to override socket detection
|
|
364
386
|
|
|
365
|
-
>
|
|
387
|
+
> [!IMPORTANT]
|
|
388
|
+
>
|
|
389
|
+
> If you're using Podman in rootless mode, you need to enable the Podman socket service:
|
|
366
390
|
>
|
|
367
391
|
> ```bash
|
|
368
392
|
> systemctl --user enable --now podman.socket
|
|
@@ -410,6 +434,8 @@ claude-run # SSH agent is forwarded to container
|
|
|
410
434
|
|
|
411
435
|
The container will use your host's SSH agent, so you don't need to enter the passphrase again.
|
|
412
436
|
|
|
437
|
+
> [!NOTE]
|
|
438
|
+
>
|
|
413
439
|
> **Troubleshooting: SSH Agent Connection Failed**
|
|
414
440
|
>
|
|
415
441
|
> If running `ssh-add -l` inside the container shows "communication with agent failed":
|
|
@@ -427,17 +453,35 @@ The container will use your host's SSH agent, so you don't need to enter the pas
|
|
|
427
453
|
|
|
428
454
|
#### GPG Key Support
|
|
429
455
|
|
|
430
|
-
GPG keys from `~/.gnupg` are
|
|
456
|
+
GPG keys from `~/.gnupg` are automatically forwarded to the container. **GPG commit signing is disabled by default** to avoid passphrase prompts in non-interactive environments.
|
|
431
457
|
|
|
432
|
-
**
|
|
458
|
+
**Three ways to use GPG:**
|
|
433
459
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
```
|
|
460
|
+
1. **Direct GPG key usage (recommended for keys without passphrase)**
|
|
461
|
+
- GPG key files are automatically copied to the container
|
|
462
|
+
- No additional configuration needed
|
|
463
|
+
- Only works for keys without a passphrase
|
|
439
464
|
|
|
440
|
-
|
|
465
|
+
2. **GPG agent forwarding (recommended for password-protected keys)**
|
|
466
|
+
- Enable in your config:
|
|
467
|
+
```json
|
|
468
|
+
{
|
|
469
|
+
"forwardGpgAgent": true,
|
|
470
|
+
"enableGpgSigning": true
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
- Host GPG agent is automatically forwarded when container starts
|
|
474
|
+
- Supports password-protected keys (no need to enter passphrase in container)
|
|
475
|
+
- Requires `gpg-agent` running on your host machine
|
|
476
|
+
|
|
477
|
+
3. **Completely disable GPG signing**
|
|
478
|
+
- Default behavior (if you don't set `enableGpgSigning`)
|
|
479
|
+
- GPG signing will be disabled even if your host `.gitconfig` has `commit.gpgsign = true`
|
|
480
|
+
- This prevents signing failures in containerized environments where `/dev/tty` is not accessible
|
|
481
|
+
|
|
482
|
+
> [!NOTE]
|
|
483
|
+
>
|
|
484
|
+
> For security, consider using SSH commit signing (if your Git server supports it). This way you don't need to configure GPG at all.
|
|
441
485
|
|
|
442
486
|
#### Disabling SSH/GPG Forwarding
|
|
443
487
|
|
|
@@ -447,7 +491,8 @@ If you don't want to forward your keys, you can disable this feature:
|
|
|
447
491
|
{
|
|
448
492
|
"forwardSshKeys": false,
|
|
449
493
|
"forwardGpgKeys": false,
|
|
450
|
-
"forwardSshAgent": false
|
|
494
|
+
"forwardSshAgent": false,
|
|
495
|
+
"forwardGpgAgent": false
|
|
451
496
|
}
|
|
452
497
|
```
|
|
453
498
|
|
package/README.zh-Hans.md
CHANGED
|
@@ -52,7 +52,7 @@ npm install -g claude-code-runner
|
|
|
52
52
|
|
|
53
53
|
> **提示**:为了最快的启动速度,使用预构建的官方镜像:在配置中设置 `buildImage: false`。默认镜像(`ghcr.io/yanranxiaoxi/claude-code-runner:latest`)会自动使用。
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
只需在任何目录中运行:
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
58
|
claude-run
|
|
@@ -65,6 +65,19 @@ claude-run
|
|
|
65
65
|
3. 在 `http://localhost:3456` 上启动 Web UI
|
|
66
66
|
4. 自动打开浏览器
|
|
67
67
|
|
|
68
|
+
> [!WARNING]
|
|
69
|
+
>
|
|
70
|
+
> **非 Git 目录支持**
|
|
71
|
+
>
|
|
72
|
+
> 如果你在非 Git 目录中运行 `claude-run`,工具会提示你初始化 Git 仓库。如果你同意,它会自动运行 `git init`,创建初始提交,并启动容器。**重要**:初始化后,你必须手动设置远程仓库(例如在 GitHub 上)并配置上游以保存更改。使用如下命令:
|
|
73
|
+
>
|
|
74
|
+
> ```bash
|
|
75
|
+
> git remote add origin <你的仓库 URL>
|
|
76
|
+
> git push -u origin main
|
|
77
|
+
> ```
|
|
78
|
+
>
|
|
79
|
+
> 如果不设置上游,你的更改将仅存在于容器内的本地。
|
|
80
|
+
|
|
68
81
|
> [!NOTE]
|
|
69
82
|
>
|
|
70
83
|
> **使用带密码的 SSH 密钥**
|
|
@@ -101,6 +114,13 @@ claude-run
|
|
|
101
114
|
|
|
102
115
|
### 命令
|
|
103
116
|
|
|
117
|
+
#### 快捷命令
|
|
118
|
+
|
|
119
|
+
以下命令是 `claude-run` 的快捷方式:
|
|
120
|
+
|
|
121
|
+
- `clauderun`
|
|
122
|
+
- `ccrun`
|
|
123
|
+
|
|
104
124
|
#### `claude-run` (默认)
|
|
105
125
|
|
|
106
126
|
使用 Web UI 启动新容器(推荐):
|
|
@@ -247,6 +267,7 @@ claud-run update # 别名
|
|
|
247
267
|
"forwardSshKeys": true,
|
|
248
268
|
"forwardGpgKeys": true,
|
|
249
269
|
"forwardSshAgent": true,
|
|
270
|
+
"forwardGpgAgent": false,
|
|
250
271
|
"enableGpgSigning": false
|
|
251
272
|
}
|
|
252
273
|
```
|
|
@@ -274,6 +295,7 @@ claud-run update # 别名
|
|
|
274
295
|
- `forwardSshKeys`: 将 `~/.ssh` 中的 SSH 密钥转发到容器(默认:true)
|
|
275
296
|
- `forwardGpgKeys`: 将 `~/.gnupg` 中的 GPG 密钥转发到容器(默认:true)
|
|
276
297
|
- `forwardSshAgent`: 转发 SSH agent 以支持带密码的密钥(默认:true)
|
|
298
|
+
- `forwardGpgAgent`: 转发 GPG agent 以支持带密码的 GPG 密钥(默认:false,需显式启用)
|
|
277
299
|
- `enableGpgSigning`: 在容器中启用 GPG 提交签名(默认:false)
|
|
278
300
|
|
|
279
301
|
#### 挂载配置
|
|
@@ -382,7 +404,9 @@ Claude Code Runner 还支持 Podman 作为 Docker 的替代方案。该工具通
|
|
|
382
404
|
- **自定义套接字路径**: 使用 `dockerSocketPath` 配置选项指定自定义套接字
|
|
383
405
|
- **环境变量**: 设置 `DOCKER_HOST` 来覆盖套接字检测
|
|
384
406
|
|
|
385
|
-
>
|
|
407
|
+
> [!IMPORTANT]
|
|
408
|
+
>
|
|
409
|
+
> 如果你使用 Podman 的 rootless(无根)模式,需要启用 Podman socket 服务:
|
|
386
410
|
>
|
|
387
411
|
> ```bash
|
|
388
412
|
> systemctl --user enable --now podman.socket
|
|
@@ -430,6 +454,8 @@ claude-run # SSH agent 会被转发到容器
|
|
|
430
454
|
|
|
431
455
|
容器将使用宿主机的 SSH agent,因此你无需再次输入密码。
|
|
432
456
|
|
|
457
|
+
> [!NOTE]
|
|
458
|
+
>
|
|
433
459
|
> **故障排除:SSH Agent 无法连接**
|
|
434
460
|
>
|
|
435
461
|
> 如果在容器内运行 `ssh-add -l` 显示 "communication with agent failed":
|
|
@@ -447,17 +473,35 @@ claude-run # SSH agent 会被转发到容器
|
|
|
447
473
|
|
|
448
474
|
#### GPG 密钥支持
|
|
449
475
|
|
|
450
|
-
来自 `~/.gnupg` 的 GPG
|
|
476
|
+
来自 `~/.gnupg` 的 GPG 密钥会自动转发到容器。**GPG 提交签名默认是禁用的**,以避免在非交互式环境中出现密码提示。
|
|
451
477
|
|
|
452
|
-
|
|
478
|
+
**三种使用方式:**
|
|
453
479
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
```
|
|
480
|
+
1. **直接使用 GPG 密钥(推荐用于无密码密钥)**
|
|
481
|
+
- GPG 密钥文件自动复制到容器
|
|
482
|
+
- 无需额外配置
|
|
483
|
+
- 仅适用于没有密码的 GPG 密钥
|
|
459
484
|
|
|
460
|
-
|
|
485
|
+
2. **通过 GPG agent 转发(推荐用于密码保护的密钥)**
|
|
486
|
+
- 需要在配置中启用:
|
|
487
|
+
```json
|
|
488
|
+
{
|
|
489
|
+
"forwardGpgAgent": true,
|
|
490
|
+
"enableGpgSigning": true
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
- 宿主机 GPG agent 会在容器启动时自动转发
|
|
494
|
+
- 支持密码保护的密钥(无需在容器内输入密码)
|
|
495
|
+
- 需要宿主机 `gpg-agent` 正在运行
|
|
496
|
+
|
|
497
|
+
3. **完全禁用 GPG 签名**
|
|
498
|
+
- 默认行为(如果不配置 `enableGpgSigning`)
|
|
499
|
+
- 即使宿主机配置了 `commit.gpgsign = true`,容器内也会禁用签名
|
|
500
|
+
- 这是为了避免在无法访问 `/dev/tty` 的容器环境中出现签名失败
|
|
501
|
+
|
|
502
|
+
> [!NOTE]
|
|
503
|
+
>
|
|
504
|
+
> 为了安全起见,建议使用 SSH 提交签名(如果 Git 服务器支持)。这样不需要配置 GPG 就能实现签名。
|
|
461
505
|
|
|
462
506
|
#### 禁用 SSH/GPG 转发
|
|
463
507
|
|
|
@@ -467,7 +511,8 @@ claude-run # SSH agent 会被转发到容器
|
|
|
467
511
|
{
|
|
468
512
|
"forwardSshKeys": false,
|
|
469
513
|
"forwardGpgKeys": false,
|
|
470
|
-
"forwardSshAgent": false
|
|
514
|
+
"forwardSshAgent": false,
|
|
515
|
+
"forwardGpgAgent": false
|
|
471
516
|
}
|
|
472
517
|
```
|
|
473
518
|
|
package/dist/cli.js
CHANGED
|
@@ -17,7 +17,7 @@ const docker_config_1 = require("./docker-config");
|
|
|
17
17
|
const index_1 = require("./index");
|
|
18
18
|
const web_server_1 = require("./web-server");
|
|
19
19
|
// Package info - injected at build time
|
|
20
|
-
const currentVersion = '0.2.
|
|
20
|
+
const currentVersion = '0.2.12';
|
|
21
21
|
const packageName = 'claude-code-runner';
|
|
22
22
|
// Check for updates (non-blocking)
|
|
23
23
|
async function checkForUpdates() {
|
package/dist/container.d.ts
CHANGED
package/dist/container.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAe,aAAa,EAAE,MAAM,SAAS,CAAC;AAU1D,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAA4C;gBAElD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa;IAK3C,KAAK,CAAC,eAAe,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;YA+DpC,WAAW;
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAe,aAAa,EAAE,MAAM,SAAS,CAAC;AAU1D,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAA4C;gBAElD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa;IAK3C,KAAK,CAAC,eAAe,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;YA+DpC,WAAW;YAgDX,SAAS;IA6FvB,OAAO,CAAC,WAAW;YASL,UAAU;YAkCV,eAAe;IAoC7B,OAAO,CAAC,kBAAkB;IAsK1B,OAAO,CAAC,cAAc;YAyHR,qBAAqB;YAwIrB,iBAAiB;YAqMjB,cAAc;YAgFd,wBAAwB;YA4IxB,gBAAgB;IAuExB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAY9B"}
|
package/dist/container.js
CHANGED
|
@@ -138,8 +138,7 @@ class ContainerManager {
|
|
|
138
138
|
await this.pullImage(imageName);
|
|
139
139
|
}
|
|
140
140
|
catch (error) {
|
|
141
|
-
|
|
142
|
-
await this.buildDefaultImage(imageName);
|
|
141
|
+
throw new Error(`Failed to pull image '${imageName}' and no Dockerfile available. Please ensure docker/Dockerfile exists or use a pre-built image.`);
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
144
|
}
|
|
@@ -222,130 +221,6 @@ class ContainerManager {
|
|
|
222
221
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
223
222
|
return `${(bytes / k ** i).toFixed(1)} ${sizes[i]}`;
|
|
224
223
|
}
|
|
225
|
-
async buildDefaultImage(imageName) {
|
|
226
|
-
const dockerfile = `
|
|
227
|
-
FROM docker.io/library/almalinux:10
|
|
228
|
-
|
|
229
|
-
# Install system dependencies
|
|
230
|
-
RUN dnf install -y epel-release && dnf install -y \
|
|
231
|
-
curl \\
|
|
232
|
-
git \\
|
|
233
|
-
openssh-clients \\
|
|
234
|
-
python3 \\
|
|
235
|
-
python3-pip \\
|
|
236
|
-
gcc \\
|
|
237
|
-
gcc-c++ \\
|
|
238
|
-
make \\
|
|
239
|
-
sudo \\
|
|
240
|
-
vim \\
|
|
241
|
-
jq \\
|
|
242
|
-
ca-certificates \\
|
|
243
|
-
gnupg2 \\
|
|
244
|
-
inotify-tools \\
|
|
245
|
-
rsync \\
|
|
246
|
-
&& dnf clean all
|
|
247
|
-
|
|
248
|
-
# Install GitHub CLI
|
|
249
|
-
RUN dnf install -y 'dnf-command(config-manager)' \\
|
|
250
|
-
&& dnf config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo \\
|
|
251
|
-
&& dnf install -y gh
|
|
252
|
-
|
|
253
|
-
# Install Claude Code using native installer
|
|
254
|
-
RUN curl -fsSL https://claude.ai/install.sh | bash
|
|
255
|
-
|
|
256
|
-
# Ensure claude is in PATH for all users
|
|
257
|
-
ENV PATH="/root/.local/bin:\${PATH}"
|
|
258
|
-
|
|
259
|
-
# Create a non-root user with sudo privileges
|
|
260
|
-
RUN useradd -m -s /bin/bash claude && \\
|
|
261
|
-
echo 'claude ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \\
|
|
262
|
-
usermod -aG wheel claude
|
|
263
|
-
|
|
264
|
-
# Switch to claude user to install Claude Code
|
|
265
|
-
USER claude
|
|
266
|
-
|
|
267
|
-
# Install Claude Code for claude user
|
|
268
|
-
RUN curl -fsSL https://claude.ai/install.sh | bash
|
|
269
|
-
|
|
270
|
-
# Ensure claude is in PATH for claude user
|
|
271
|
-
RUN echo 'export PATH="$HOME/.local/bin:$PATH"' >> /home/claude/.bashrc
|
|
272
|
-
|
|
273
|
-
# Switch back to root for remaining setup
|
|
274
|
-
USER root
|
|
275
|
-
|
|
276
|
-
# Create workspace directory and set ownership
|
|
277
|
-
RUN mkdir -p /workspace && \\
|
|
278
|
-
chown -R claude:claude /workspace
|
|
279
|
-
|
|
280
|
-
# Switch to non-root user
|
|
281
|
-
USER claude
|
|
282
|
-
WORKDIR /workspace
|
|
283
|
-
|
|
284
|
-
# Set up entrypoint
|
|
285
|
-
ENTRYPOINT ["/bin/bash", "-c"]
|
|
286
|
-
`;
|
|
287
|
-
/*
|
|
288
|
-
RUN echo '#!/bin/bash\\n\\
|
|
289
|
-
# Allow the initial branch creation\\n\\
|
|
290
|
-
if [ ! -f /tmp/.branch-created ]; then\\n\\
|
|
291
|
-
/usr/bin/git "$@"\\n\\
|
|
292
|
-
if [[ "$1" == "checkout" ]] && [[ "$2" == "-b" ]]; then\\n\\
|
|
293
|
-
touch /tmp/.branch-created\\n\\
|
|
294
|
-
fi\\n\\
|
|
295
|
-
else\\n\\
|
|
296
|
-
# After initial branch creation, prevent switching\\n\\
|
|
297
|
-
if [[ "$1" == "checkout" ]] && [[ "$2" != "-b" ]]; then\\n\\
|
|
298
|
-
echo "Branch switching is disabled in claude-code-runner"\\n\\
|
|
299
|
-
exit 1\\n\\
|
|
300
|
-
fi\\n\\
|
|
301
|
-
if [[ "$1" == "switch" ]]; then\\n\\
|
|
302
|
-
echo "Branch switching is disabled in claude-code-runner"\\n\\
|
|
303
|
-
exit 1\\n\\
|
|
304
|
-
fi\\n\\
|
|
305
|
-
/usr/bin/git "$@"\\n\\
|
|
306
|
-
fi' > /usr/local/bin/git && \\
|
|
307
|
-
chmod +x /usr/local/bin/git
|
|
308
|
-
# Create startup script
|
|
309
|
-
RUN echo '#!/bin/bash\\n\\
|
|
310
|
-
echo "Waiting for attachment..."\\n\\
|
|
311
|
-
sleep 2\\n\\
|
|
312
|
-
cd /workspace\\n\\
|
|
313
|
-
git checkout -b "$1"\\n\\
|
|
314
|
-
echo "Starting Claude Code on branch $1..."\\n\\
|
|
315
|
-
exec claude --dangerously-skip-permissions' > /start-claude.sh && \\
|
|
316
|
-
chmod +x /start-claude.sh */
|
|
317
|
-
// Build image from string
|
|
318
|
-
const pack = tar_stream_1.default.pack();
|
|
319
|
-
// Add Dockerfile to tar
|
|
320
|
-
pack.entry({ name: 'Dockerfile' }, dockerfile, (err) => {
|
|
321
|
-
if (err)
|
|
322
|
-
throw err;
|
|
323
|
-
pack.finalize();
|
|
324
|
-
});
|
|
325
|
-
// Convert to buffer for docker
|
|
326
|
-
const chunks = [];
|
|
327
|
-
pack.on('data', (chunk) => chunks.push(chunk));
|
|
328
|
-
await new Promise((resolve) => {
|
|
329
|
-
pack.on('end', resolve);
|
|
330
|
-
});
|
|
331
|
-
const tarBuffer = node_buffer_1.Buffer.concat(chunks);
|
|
332
|
-
const buildStream = await this.docker.buildImage(tarBuffer, {
|
|
333
|
-
t: imageName,
|
|
334
|
-
});
|
|
335
|
-
// Wait for build to complete
|
|
336
|
-
await new Promise((resolve, reject) => {
|
|
337
|
-
this.docker.modem.followProgress(buildStream, (err, res) => {
|
|
338
|
-
if (err)
|
|
339
|
-
reject(err);
|
|
340
|
-
else
|
|
341
|
-
resolve(res);
|
|
342
|
-
}, (event) => {
|
|
343
|
-
if (event.stream) {
|
|
344
|
-
node_process_1.default.stdout.write(event.stream);
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
224
|
async buildImage(dockerfilePath, imageName) {
|
|
350
225
|
const buildContext = node_path_1.default.dirname(dockerfilePath);
|
|
351
226
|
const buildStream = await this.docker.buildImage({
|
|
@@ -493,8 +368,18 @@ exec claude --dangerously-skip-permissions' > /start-claude.sh && \\
|
|
|
493
368
|
if (sshAuthSock && fs.existsSync(sshAuthSock)) {
|
|
494
369
|
env.push('SSH_AUTH_SOCK=/tmp/.ssh-agent-sock');
|
|
495
370
|
}
|
|
496
|
-
// GPG agent -
|
|
497
|
-
|
|
371
|
+
// GPG agent forwarding - if enabled and socket is available
|
|
372
|
+
const gpgAgentSocket = node_path_1.default.join(os.homedir(), '.gnupg', 'S.gpg-agent');
|
|
373
|
+
if (this.config.forwardGpgAgent === true && fs.existsSync(gpgAgentSocket)) {
|
|
374
|
+
// Point to the container's GPG agent socket relay
|
|
375
|
+
env.push('GPG_TTY=/dev/tty');
|
|
376
|
+
// Set GNUPGHOME to use the forwarded agent
|
|
377
|
+
env.push('GNUPGHOME=/home/claude/.gnupg');
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
// Disable TTY requirement for non-interactive use (no agent available)
|
|
381
|
+
env.push('GPG_TTY=');
|
|
382
|
+
}
|
|
498
383
|
// Pass GPG signing preference to container
|
|
499
384
|
if (this.config.enableGpgSigning) {
|
|
500
385
|
env.push('GIT_COMMIT_GPG_SIGN=true');
|
|
@@ -541,6 +426,19 @@ exec claude --dangerously-skip-permissions' > /start-claude.sh && \\
|
|
|
541
426
|
volumes.push(`${sshAuthSock}:/tmp/.ssh-agent-sock:rw`);
|
|
542
427
|
console.log(chalk_1.default.blue('✓ SSH agent forwarding enabled'));
|
|
543
428
|
}
|
|
429
|
+
// Mount GPG agent socket if available and explicitly enabled (for GPG signing)
|
|
430
|
+
const forwardGpgAgent = this.config.forwardGpgAgent === true; // Default: false - must be explicit
|
|
431
|
+
if (forwardGpgAgent) {
|
|
432
|
+
// GPG agent socket is typically at ~/.gnupg/S.gpg-agent
|
|
433
|
+
const gpgAgentSocket = node_path_1.default.join(os.homedir(), '.gnupg', 'S.gpg-agent');
|
|
434
|
+
if (fs.existsSync(gpgAgentSocket)) {
|
|
435
|
+
volumes.push(`${gpgAgentSocket}:/tmp/.gpg-agent-sock:rw`);
|
|
436
|
+
console.log(chalk_1.default.blue('✓ GPG agent forwarding enabled'));
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
console.log(chalk_1.default.yellow('⚠ GPG agent socket not found - GPG signing may not work'));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
544
442
|
// Add custom volumes (legacy format)
|
|
545
443
|
if (this.config.volumes) {
|
|
546
444
|
volumes.push(...this.config.volumes);
|
|
@@ -949,11 +847,16 @@ exec /bin/bash`;
|
|
|
949
847
|
git config --global --add safe.directory /workspace &&
|
|
950
848
|
# Clean up macOS resource fork files in git pack directory
|
|
951
849
|
find .git/objects/pack -name "._pack-*.idx" -type f -delete 2>/dev/null || true &&
|
|
952
|
-
# Configure git commit signing
|
|
953
|
-
#
|
|
954
|
-
|
|
850
|
+
# Configure git commit signing
|
|
851
|
+
# Disable GPG signing by default to avoid passphrase prompts in non-interactive environments
|
|
852
|
+
# Even if host .gitconfig has commit.gpgsign=true, override it unless explicitly enabled
|
|
853
|
+
ENABLE_GPG_SIGNING="${this.config.enableGpgSigning === true ? 'true' : 'false'}" &&
|
|
854
|
+
if [ "$ENABLE_GPG_SIGNING" = "true" ]; then
|
|
855
|
+
git config --global commit.gpgsign true
|
|
856
|
+
echo "✓ GPG signing enabled"
|
|
857
|
+
else
|
|
955
858
|
git config --global commit.gpgsign false
|
|
956
|
-
echo "✓ GPG signing disabled (
|
|
859
|
+
echo "✓ GPG signing disabled (enable with enableGpgSigning: true in config)"
|
|
957
860
|
fi &&
|
|
958
861
|
# Start SSH agent if not already running and we have keys
|
|
959
862
|
if [ -d "/home/claude/.ssh" ] && [ -z "$SSH_AUTH_SOCK" ]; then
|
|
@@ -976,33 +879,17 @@ exec /bin/bash`;
|
|
|
976
879
|
if [ -n "${prFetchRef || ''}" ]; then
|
|
977
880
|
echo "• Fetching PR branch..." &&
|
|
978
881
|
git fetch origin ${prFetchRef} &&
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
echo "✓ Switched to existing PR branch: ${branchName}"
|
|
982
|
-
else
|
|
983
|
-
git checkout "${branchName}" &&
|
|
984
|
-
echo "✓ Checked out PR branch: ${branchName}"
|
|
985
|
-
fi
|
|
882
|
+
git checkout -B "${branchName}" FETCH_HEAD &&
|
|
883
|
+
echo "✓ Checked out PR branch: ${branchName}"
|
|
986
884
|
elif [ -n "${remoteFetchRef || ''}" ]; then
|
|
987
885
|
echo "• Fetching remote branch..." &&
|
|
988
886
|
git fetch origin &&
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
git pull origin "${branchName}" &&
|
|
992
|
-
echo "✓ Switched to existing remote branch: ${branchName}"
|
|
993
|
-
else
|
|
994
|
-
git checkout -b "${branchName}" "${remoteFetchRef}" &&
|
|
995
|
-
echo "✓ Created local branch from remote: ${branchName}"
|
|
996
|
-
fi
|
|
887
|
+
git checkout -B "${branchName}" "${remoteFetchRef}" &&
|
|
888
|
+
echo "✓ Checked out remote branch: ${branchName}"
|
|
997
889
|
else
|
|
998
890
|
# Regular branch creation
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
echo "✓ Switched to existing branch: ${branchName}"
|
|
1002
|
-
else
|
|
1003
|
-
git checkout -b "${branchName}" &&
|
|
1004
|
-
echo "✓ Created new branch: ${branchName}"
|
|
1005
|
-
fi
|
|
891
|
+
git checkout -B "${branchName}" &&
|
|
892
|
+
echo "✓ Checked out branch: ${branchName}"
|
|
1006
893
|
fi &&
|
|
1007
894
|
cat > /home/claude/start-session.sh << 'EOF'
|
|
1008
895
|
${startupScript}
|
|
@@ -1028,6 +915,8 @@ EOF
|
|
|
1028
915
|
|| output.includes('✓ Switched to existing remote branch')
|
|
1029
916
|
|| output.includes('✓ Switched to existing PR branch')
|
|
1030
917
|
|| output.includes('✓ Checked out PR branch')
|
|
918
|
+
|| output.includes('✓ Checked out remote branch')
|
|
919
|
+
|| output.includes('✓ Checked out branch')
|
|
1031
920
|
|| output.includes('✓ Created local branch from remote'))
|
|
1032
921
|
&& output.includes('✓ Startup script created')) {
|
|
1033
922
|
resolve();
|