@kokorolx/ai-sandbox-wrapper 2.6.0 → 2.7.0
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 +42 -12
- package/bin/ai-run +164 -7
- package/bin/cli.js +89 -13
- package/dockerfiles/base/Dockerfile +6 -28
- package/lib/install-base.sh +13 -1
- package/lib/install-claude.sh +17 -2
- package/lib/install-openclaw.sh +39 -0
- package/package.json +2 -1
- package/setup.sh +16 -6
- package/skills/rtk/SKILL.md +103 -0
- package/skills/rtk-setup/SKILL.md +118 -0
- package/dockerfiles/amp/Dockerfile +0 -17
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Protect your SSH keys, API tokens, and system files while using AI tools that need filesystem access.
|
|
6
6
|
|
|
7
|
-
*Last updated: February
|
|
7
|
+
*Last updated: February 25, 2026*
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -33,21 +33,24 @@ Protect your SSH keys, API tokens, and system files while using AI tools that ne
|
|
|
33
33
|
|
|
34
34
|
## ✨ What's New
|
|
35
35
|
|
|
36
|
-
### v2.
|
|
36
|
+
### v2.7.0: Git Fetch-Only Mode & Bundled Skills
|
|
37
37
|
|
|
38
|
-
- **
|
|
39
|
-
-
|
|
40
|
-
- **
|
|
38
|
+
- **Git Fetch-Only**: Allow git fetch/pull but block push — perfect for AI agents that should read but not write
|
|
39
|
+
- **Bundled Skills**: RTK token optimizer skills auto-installed for OpenCode users
|
|
40
|
+
- **SSH Config Fix**: Resolved crash during git credential setup
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
#
|
|
44
|
-
opencode
|
|
43
|
+
# Fetch-only mode (no push allowed)
|
|
44
|
+
opencode --git-fetch
|
|
45
45
|
|
|
46
|
-
#
|
|
47
|
-
|
|
46
|
+
# Or select from interactive menu:
|
|
47
|
+
# 4) Fetch only - allow once (no push, this session)
|
|
48
|
+
# 5) Fetch only - always for this workspace (no push)
|
|
48
49
|
|
|
49
|
-
#
|
|
50
|
-
|
|
50
|
+
# Manage via CLI
|
|
51
|
+
npx @kokorolx/ai-sandbox-wrapper git fetch-only ~/projects/myrepo
|
|
52
|
+
npx @kokorolx/ai-sandbox-wrapper git full ~/projects/myrepo
|
|
53
|
+
npx @kokorolx/ai-sandbox-wrapper git status
|
|
51
54
|
```
|
|
52
55
|
|
|
53
56
|
---
|
|
@@ -172,8 +175,21 @@ Git credentials are **not** shared by default. When you run a tool, you'll be pr
|
|
|
172
175
|
```
|
|
173
176
|
🔐 Git Access Control
|
|
174
177
|
1) Yes, allow once
|
|
175
|
-
2) Yes, always allow for this workspace
|
|
178
|
+
2) Yes, always allow for this workspace
|
|
176
179
|
3) No, keep Git disabled (secure default)
|
|
180
|
+
4) Fetch only - allow once (no push, this session)
|
|
181
|
+
5) Fetch only - always for this workspace (no push)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Fetch-only mode** allows `git fetch`, `git pull`, `git clone` but blocks `git push`. Uses git's `pushInsteadOf` config — no network restrictions needed.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# Force fetch-only via flag
|
|
188
|
+
opencode --git-fetch
|
|
189
|
+
|
|
190
|
+
# Manage via CLI
|
|
191
|
+
npx @kokorolx/ai-sandbox-wrapper git fetch-only ~/projects/myrepo
|
|
192
|
+
npx @kokorolx/ai-sandbox-wrapper git full ~/projects/myrepo
|
|
177
193
|
```
|
|
178
194
|
|
|
179
195
|
### Clipboard
|
|
@@ -233,6 +249,17 @@ After installation, configure your MCP client (e.g., OpenCode) to use them:
|
|
|
233
249
|
|
|
234
250
|
> **Note:** The `--no-sandbox` flags are required when running in Docker containers. This is safe because the container itself provides isolation.
|
|
235
251
|
|
|
252
|
+
### Bundled Skills (OpenCode)
|
|
253
|
+
|
|
254
|
+
OpenCode containers auto-install these skills on first run (existing skills are never overwritten):
|
|
255
|
+
|
|
256
|
+
| Skill | Description |
|
|
257
|
+
|-------|-------------|
|
|
258
|
+
| `rtk` | Command reference for RTK token optimizer (60-90% token savings) |
|
|
259
|
+
| `rtk-setup` | Persistent RTK enforcement — updates AGENTS.md and propagates to subagents |
|
|
260
|
+
|
|
261
|
+
Skills are copied to `~/.config/opencode/skills/` and available immediately.
|
|
262
|
+
|
|
236
263
|
---
|
|
237
264
|
|
|
238
265
|
## 📁 Directory Structure
|
|
@@ -290,6 +317,9 @@ opencode -e 3000,4000 # Multiple ports
|
|
|
290
317
|
# Network
|
|
291
318
|
opencode -n mynetwork # Join Docker network
|
|
292
319
|
|
|
320
|
+
# Git fetch-only
|
|
321
|
+
opencode --git-fetch # Fetch only (no push)
|
|
322
|
+
|
|
293
323
|
# Management
|
|
294
324
|
npx @kokorolx/ai-sandbox-wrapper workspace list
|
|
295
325
|
npx @kokorolx/ai-sandbox-wrapper clean
|
package/bin/ai-run
CHANGED
|
@@ -15,6 +15,7 @@ Options:
|
|
|
15
15
|
-p, --password <value> Set OpenCode server password (for web/serve mode)
|
|
16
16
|
--password-env <VAR> Read OpenCode server password from environment variable
|
|
17
17
|
--allow-unsecured Allow OpenCode server to run without password (suppresses warning)
|
|
18
|
+
--git-fetch Enable git fetch-only mode (blocks push)
|
|
18
19
|
-h, --help Show this help message
|
|
19
20
|
--help-env Show environment variables reference
|
|
20
21
|
|
|
@@ -39,6 +40,7 @@ Examples:
|
|
|
39
40
|
ai-run opencode web -p secret # Run with password
|
|
40
41
|
ai-run opencode --shell # Start shell, run tool manually
|
|
41
42
|
ai-run aider -n mynetwork # Connect to Docker network
|
|
43
|
+
ai-run opencode --git-fetch # Git fetch only (no push)
|
|
42
44
|
|
|
43
45
|
Documentation: https://github.com/kokorolx/ai-sandbox-wrapper
|
|
44
46
|
EOF
|
|
@@ -135,6 +137,7 @@ EXPOSE_ARG=""
|
|
|
135
137
|
SERVER_PASSWORD=""
|
|
136
138
|
PASSWORD_ENV_VAR=""
|
|
137
139
|
ALLOW_UNSECURED=false
|
|
140
|
+
GIT_FETCH_ONLY_FLAG=false
|
|
138
141
|
TOOL_ARGS=()
|
|
139
142
|
|
|
140
143
|
while [[ $# -gt 0 ]]; do
|
|
@@ -177,6 +180,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
177
180
|
ALLOW_UNSECURED=true
|
|
178
181
|
shift
|
|
179
182
|
;;
|
|
183
|
+
--git-fetch)
|
|
184
|
+
GIT_FETCH_ONLY_FLAG=true
|
|
185
|
+
shift
|
|
186
|
+
;;
|
|
180
187
|
*)
|
|
181
188
|
TOOL_ARGS+=("$1")
|
|
182
189
|
shift
|
|
@@ -411,7 +418,7 @@ upgrade_config_to_v2() {
|
|
|
411
418
|
fi
|
|
412
419
|
|
|
413
420
|
# Add missing v2 fields while preserving existing networks
|
|
414
|
-
jq '.version = 2 | .workspaces = (.workspaces // []) | .git = (.git // {"allowedWorkspaces":[],"keySelections":{}})' \
|
|
421
|
+
jq '.version = 2 | .workspaces = (.workspaces // []) | .git = (.git // {"allowedWorkspaces":[],"keySelections":{}}) | .git.fetchOnlyWorkspaces = (.git.fetchOnlyWorkspaces // [])' \
|
|
415
422
|
"$AI_SANDBOX_CONFIG" > "$AI_SANDBOX_CONFIG.tmp" \
|
|
416
423
|
&& mv "$AI_SANDBOX_CONFIG.tmp" "$AI_SANDBOX_CONFIG"
|
|
417
424
|
chmod 600 "$AI_SANDBOX_CONFIG"
|
|
@@ -426,7 +433,7 @@ init_config() {
|
|
|
426
433
|
|
|
427
434
|
if [[ ! -f "$AI_SANDBOX_CONFIG" ]]; then
|
|
428
435
|
# Create new v2 config
|
|
429
|
-
local initial_config='{"version":2,"workspaces":[],"git":{"allowedWorkspaces":[],"keySelections":{}},"networks":{"global":[],"workspaces":{}}}'
|
|
436
|
+
local initial_config='{"version":2,"workspaces":[],"git":{"allowedWorkspaces":[],"fetchOnlyWorkspaces":[],"keySelections":{}},"networks":{"global":[],"workspaces":{}}}'
|
|
430
437
|
echo "$initial_config" > "$AI_SANDBOX_CONFIG"
|
|
431
438
|
chmod 600 "$AI_SANDBOX_CONFIG"
|
|
432
439
|
|
|
@@ -583,9 +590,27 @@ case "$TOOL" in
|
|
|
583
590
|
opencode)
|
|
584
591
|
mount_tool_config "$HOME/.config/opencode" ".config/opencode"
|
|
585
592
|
mount_tool_config "$HOME/.local/share/opencode" ".local/share/opencode"
|
|
593
|
+
# Bundle default skills (copy if not already present)
|
|
594
|
+
AIRUN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
595
|
+
BUNDLED_SKILLS_DIR="$AIRUN_DIR/../skills"
|
|
596
|
+
if [[ -d "$BUNDLED_SKILLS_DIR" ]]; then
|
|
597
|
+
for skill_dir in "$BUNDLED_SKILLS_DIR"/*/; do
|
|
598
|
+
[[ ! -d "$skill_dir" ]] && continue
|
|
599
|
+
skill_name=$(basename "$skill_dir")
|
|
600
|
+
target_dir="$HOME/.config/opencode/skills/$skill_name"
|
|
601
|
+
if [[ ! -d "$target_dir" ]]; then
|
|
602
|
+
mkdir -p "$target_dir"
|
|
603
|
+
cp -r "$skill_dir"* "$target_dir/" 2>/dev/null || true
|
|
604
|
+
fi
|
|
605
|
+
done
|
|
606
|
+
fi
|
|
607
|
+
;;
|
|
608
|
+
openclaw)
|
|
609
|
+
mount_tool_config "$HOME/.openclaw" ".openclaw"
|
|
586
610
|
;;
|
|
587
611
|
claude)
|
|
588
612
|
mount_tool_config "$HOME/.claude" ".claude"
|
|
613
|
+
mount_tool_config "$HOME/.ccs" ".ccs"
|
|
589
614
|
;;
|
|
590
615
|
droid)
|
|
591
616
|
mount_tool_config "$HOME/.config/droid" ".config/droid"
|
|
@@ -1047,8 +1072,48 @@ is_git_allowed() {
|
|
|
1047
1072
|
return 1
|
|
1048
1073
|
}
|
|
1049
1074
|
|
|
1075
|
+
# Check if workspace is in fetch-only mode (config.json)
|
|
1076
|
+
is_git_fetch_only() {
|
|
1077
|
+
local ws="$1"
|
|
1078
|
+
if has_jq && [[ -f "$AI_SANDBOX_CONFIG" ]]; then
|
|
1079
|
+
local fetch_only=$(jq -r --arg ws "$ws" '.git.fetchOnlyWorkspaces // [] | index($ws) // -1' "$AI_SANDBOX_CONFIG" 2>/dev/null)
|
|
1080
|
+
[[ "$fetch_only" -ge 0 ]] && return 0
|
|
1081
|
+
fi
|
|
1082
|
+
return 1
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
# Apply fetch-only restrictions by adding pushInsteadOf to gitconfig
|
|
1086
|
+
apply_git_fetch_only() {
|
|
1087
|
+
local gitconfig_path="$HOME_DIR/.gitconfig"
|
|
1088
|
+
# Ensure .gitconfig exists
|
|
1089
|
+
touch "$gitconfig_path"
|
|
1090
|
+
# Append push-blocking config (redirects all push URLs to non-existent protocol)
|
|
1091
|
+
cat >> "$gitconfig_path" << 'FETCHONLY'
|
|
1092
|
+
|
|
1093
|
+
# AI Sandbox: Git fetch-only mode (push disabled)
|
|
1094
|
+
[url "no-push://blocked"]
|
|
1095
|
+
pushInsteadOf = git@
|
|
1096
|
+
pushInsteadOf = ssh://
|
|
1097
|
+
pushInsteadOf = https://
|
|
1098
|
+
pushInsteadOf = http://
|
|
1099
|
+
FETCHONLY
|
|
1100
|
+
echo "🔒 Git fetch-only mode: push operations are blocked"
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
GIT_FETCH_ONLY_MODE=false
|
|
1104
|
+
|
|
1105
|
+
# Check --git-fetch flag
|
|
1106
|
+
if [[ "$GIT_FETCH_ONLY_FLAG" == "true" ]]; then
|
|
1107
|
+
GIT_FETCH_ONLY_MODE=true
|
|
1108
|
+
fi
|
|
1109
|
+
|
|
1110
|
+
# Check if workspace is in fetch-only list
|
|
1111
|
+
if is_git_fetch_only "$CURRENT_DIR"; then
|
|
1112
|
+
GIT_FETCH_ONLY_MODE=true
|
|
1113
|
+
fi
|
|
1114
|
+
|
|
1050
1115
|
# Check if Git access is allowed for this workspace
|
|
1051
|
-
if is_git_allowed "$CURRENT_DIR"; then
|
|
1116
|
+
if is_git_allowed "$CURRENT_DIR" || is_git_fetch_only "$CURRENT_DIR" || [[ "$GIT_FETCH_ONLY_FLAG" == "true" ]]; then
|
|
1052
1117
|
# Previously allowed for this workspace
|
|
1053
1118
|
# Check if saved keys exist for this workspace
|
|
1054
1119
|
WORKSPACE_MD5=$(echo "$CURRENT_DIR" | md5sum | cut -c1-8)
|
|
@@ -1090,7 +1155,7 @@ if is_git_allowed "$CURRENT_DIR"; then
|
|
|
1090
1155
|
if [ -x "$SETUP_SSH" ]; then
|
|
1091
1156
|
# Join SAVED_KEYS into a comma-separated string for --keys
|
|
1092
1157
|
KEYS_ARG=$(IFS=,; echo "${SAVED_KEYS[*]}")
|
|
1093
|
-
output=$("$SETUP_SSH" --keys "$KEYS_ARG" 2>&1)
|
|
1158
|
+
output=$( "$SETUP_SSH" --keys "$KEYS_ARG" 2>&1 ) || true
|
|
1094
1159
|
TEMP_CONFIG=$(echo "$output" | grep "Config:" | tail -1 | awk '{print $NF}')
|
|
1095
1160
|
if [ -f "$TEMP_CONFIG" ]; then
|
|
1096
1161
|
cp "$TEMP_CONFIG" "$GIT_CACHE_DIR/ssh/config" 2>/dev/null || true
|
|
@@ -1123,6 +1188,11 @@ if is_git_allowed "$CURRENT_DIR"; then
|
|
|
1123
1188
|
# Copy gitconfig to HOME_DIR (can't mount file inside mounted directory)
|
|
1124
1189
|
cp "$HOME/.gitconfig" "$HOME_DIR/.gitconfig" 2>/dev/null || true
|
|
1125
1190
|
fi
|
|
1191
|
+
|
|
1192
|
+
# Apply fetch-only restrictions if mode is active
|
|
1193
|
+
if [[ "$GIT_FETCH_ONLY_MODE" == "true" ]]; then
|
|
1194
|
+
apply_git_fetch_only
|
|
1195
|
+
fi
|
|
1126
1196
|
else
|
|
1127
1197
|
# Ask user if they want Git access for this workspace (only in interactive mode)
|
|
1128
1198
|
if [[ -t 0 ]] && ([ -d "$HOME/.ssh" ] || [ -f "$HOME/.gitconfig" ]); then
|
|
@@ -1135,11 +1205,13 @@ else
|
|
|
1135
1205
|
echo " 1) Yes, allow once (this session only)"
|
|
1136
1206
|
echo " 2) Yes, always allow for this workspace"
|
|
1137
1207
|
echo " 3) No, keep Git disabled (secure default)"
|
|
1208
|
+
echo " 4) Fetch only - allow once (no push, this session)"
|
|
1209
|
+
echo " 5) Fetch only - always for this workspace (no push)"
|
|
1138
1210
|
echo ""
|
|
1139
|
-
read -p "Choice [1-
|
|
1211
|
+
read -p "Choice [1-5]: " git_choice
|
|
1140
1212
|
|
|
1141
1213
|
case "$git_choice" in
|
|
1142
|
-
1|2)
|
|
1214
|
+
1|2|4|5)
|
|
1143
1215
|
# Interactive SSH key selection
|
|
1144
1216
|
echo ""
|
|
1145
1217
|
echo "🔑 SSH Key Selection"
|
|
@@ -1195,7 +1267,7 @@ else
|
|
|
1195
1267
|
# Join SELECTED_SSH_KEYS into a comma-separated string for --keys
|
|
1196
1268
|
KEYS_ARG=$(IFS=,; echo "${SELECTED_SSH_KEYS[*]}")
|
|
1197
1269
|
# Run it and capture the filtered config path
|
|
1198
|
-
output=$("$SETUP_SSH" --keys "$KEYS_ARG" 2>&1)
|
|
1270
|
+
output=$( "$SETUP_SSH" --keys "$KEYS_ARG" 2>&1 ) || true
|
|
1199
1271
|
TEMP_CONFIG=$(echo "$output" | grep "Config:" | tail -1 | awk '{print $NF}')
|
|
1200
1272
|
if [ -f "$TEMP_CONFIG" ]; then
|
|
1201
1273
|
cp "$TEMP_CONFIG" "$GIT_CACHE_DIR/ssh/config" 2>/dev/null || true
|
|
@@ -1231,6 +1303,9 @@ else
|
|
|
1231
1303
|
cp "$HOME/.gitconfig" "$HOME_DIR/.gitconfig" 2>/dev/null || true
|
|
1232
1304
|
fi
|
|
1233
1305
|
|
|
1306
|
+
if [[ "$git_choice" == "4" || "$git_choice" == "5" ]]; then
|
|
1307
|
+
GIT_FETCH_ONLY_MODE=true
|
|
1308
|
+
fi
|
|
1234
1309
|
if [ "$git_choice" = "2" ]; then
|
|
1235
1310
|
# Save workspace and selected keys for future sessions
|
|
1236
1311
|
echo "$CURRENT_DIR" >> "$GIT_ALLOWED_FILE"
|
|
@@ -1245,6 +1320,20 @@ else
|
|
|
1245
1320
|
mkdir -p "$GIT_SHARED_DIR/keys"
|
|
1246
1321
|
printf "%s\n" "${SELECTED_SSH_KEYS[@]}" > "$GIT_SHARED_DIR/keys/$WORKSPACE_MD5"
|
|
1247
1322
|
echo "✅ Git access enabled and saved for: $CURRENT_DIR"
|
|
1323
|
+
elif [ "$git_choice" = "5" ]; then
|
|
1324
|
+
# Save workspace to fetchOnlyWorkspaces
|
|
1325
|
+
if has_jq && [[ -f "$AI_SANDBOX_CONFIG" ]]; then
|
|
1326
|
+
jq --arg ws "$CURRENT_DIR" '.git.fetchOnlyWorkspaces = ((.git.fetchOnlyWorkspaces // []) + [$ws] | unique)' "$AI_SANDBOX_CONFIG" > "$AI_SANDBOX_CONFIG.tmp" \
|
|
1327
|
+
&& mv "$AI_SANDBOX_CONFIG.tmp" "$AI_SANDBOX_CONFIG"
|
|
1328
|
+
chmod 600 "$AI_SANDBOX_CONFIG"
|
|
1329
|
+
fi
|
|
1330
|
+
# Save selected keys (one per line for easier parsing)
|
|
1331
|
+
WORKSPACE_MD5=$(echo "$CURRENT_DIR" | md5sum | cut -c1-8)
|
|
1332
|
+
mkdir -p "$GIT_SHARED_DIR/keys"
|
|
1333
|
+
printf "%s\n" "${SELECTED_SSH_KEYS[@]}" > "$GIT_SHARED_DIR/keys/$WORKSPACE_MD5"
|
|
1334
|
+
echo "✅ Git fetch-only access enabled and saved for: $CURRENT_DIR"
|
|
1335
|
+
elif [ "$git_choice" = "4" ]; then
|
|
1336
|
+
echo "✅ Git fetch-only access enabled for this session"
|
|
1248
1337
|
else
|
|
1249
1338
|
echo "✅ Git access enabled for this session"
|
|
1250
1339
|
fi
|
|
@@ -1260,6 +1349,11 @@ else
|
|
|
1260
1349
|
echo "🔒 Git access disabled (secure mode)"
|
|
1261
1350
|
;;
|
|
1262
1351
|
esac
|
|
1352
|
+
|
|
1353
|
+
# Apply fetch-only restrictions if mode is active
|
|
1354
|
+
if [[ "$GIT_FETCH_ONLY_MODE" == "true" ]]; then
|
|
1355
|
+
apply_git_fetch_only
|
|
1356
|
+
fi
|
|
1263
1357
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
1264
1358
|
echo ""
|
|
1265
1359
|
fi
|
|
@@ -1979,6 +2073,69 @@ fi
|
|
|
1979
2073
|
# Detect display configuration (clipboard integration)
|
|
1980
2074
|
DISPLAY_FLAGS=$(detect_display_config)
|
|
1981
2075
|
|
|
2076
|
+
# ============================================================================
|
|
2077
|
+
# OPENCLAW DOCKER-COMPOSE MODE
|
|
2078
|
+
# ============================================================================
|
|
2079
|
+
if [[ "$TOOL" == "openclaw" ]]; then
|
|
2080
|
+
OPENCLAW_REPO_DIR="$HOME/.ai-sandbox/tools/openclaw/repo"
|
|
2081
|
+
|
|
2082
|
+
if [[ ! -d "$OPENCLAW_REPO_DIR" ]]; then
|
|
2083
|
+
echo "❌ ERROR: OpenClaw repository not found at $OPENCLAW_REPO_DIR"
|
|
2084
|
+
echo " Run: npx @kokorolx/ai-sandbox-wrapper setup"
|
|
2085
|
+
exit 1
|
|
2086
|
+
fi
|
|
2087
|
+
|
|
2088
|
+
cd "$OPENCLAW_REPO_DIR"
|
|
2089
|
+
|
|
2090
|
+
OPENCLAW_COMPOSE_FILE="$OPENCLAW_REPO_DIR/docker-compose.yml"
|
|
2091
|
+
OPENCLAW_OVERRIDE_FILE="$OPENCLAW_REPO_DIR/docker-compose.override.yml"
|
|
2092
|
+
|
|
2093
|
+
echo "🔄 Generating OpenClaw docker-compose override..."
|
|
2094
|
+
|
|
2095
|
+
cat > "$OPENCLAW_OVERRIDE_FILE" <<EOF
|
|
2096
|
+
services:
|
|
2097
|
+
openclaw-gateway:
|
|
2098
|
+
environment:
|
|
2099
|
+
HOME: /home/node
|
|
2100
|
+
OPENCLAW_GATEWAY_TOKEN: \${OPENCLAW_GATEWAY_TOKEN:-}
|
|
2101
|
+
volumes:
|
|
2102
|
+
- $HOME/.openclaw:/home/node/.openclaw
|
|
2103
|
+
EOF
|
|
2104
|
+
|
|
2105
|
+
for workspace in "${WORKSPACES[@]}"; do
|
|
2106
|
+
echo " - $workspace:$workspace" >> "$OPENCLAW_OVERRIDE_FILE"
|
|
2107
|
+
done
|
|
2108
|
+
|
|
2109
|
+
cat >> "$OPENCLAW_OVERRIDE_FILE" <<EOF
|
|
2110
|
+
ports:
|
|
2111
|
+
- "18789:18789"
|
|
2112
|
+
- "18790:18790"
|
|
2113
|
+
working_dir: $CURRENT_DIR
|
|
2114
|
+
EOF
|
|
2115
|
+
|
|
2116
|
+
if [[ -n "$NETWORK_OPTIONS" ]]; then
|
|
2117
|
+
echo " networks:" >> "$OPENCLAW_OVERRIDE_FILE"
|
|
2118
|
+
for net in ${DOCKER_NETWORKS//,/ }; do
|
|
2119
|
+
echo " - $net" >> "$OPENCLAW_OVERRIDE_FILE"
|
|
2120
|
+
done
|
|
2121
|
+
echo "" >> "$OPENCLAW_OVERRIDE_FILE"
|
|
2122
|
+
echo "networks:" >> "$OPENCLAW_OVERRIDE_FILE"
|
|
2123
|
+
for net in ${DOCKER_NETWORKS//,/ }; do
|
|
2124
|
+
echo " $net:" >> "$OPENCLAW_OVERRIDE_FILE"
|
|
2125
|
+
echo " external: true" >> "$OPENCLAW_OVERRIDE_FILE"
|
|
2126
|
+
done
|
|
2127
|
+
fi
|
|
2128
|
+
|
|
2129
|
+
echo "🚀 Starting OpenClaw with docker-compose..."
|
|
2130
|
+
echo "🌐 Gateway: http://localhost:18789"
|
|
2131
|
+
echo "🌐 Bridge: http://localhost:18790"
|
|
2132
|
+
echo ""
|
|
2133
|
+
|
|
2134
|
+
exec docker compose -f "$OPENCLAW_COMPOSE_FILE" -f "$OPENCLAW_OVERRIDE_FILE" \
|
|
2135
|
+
--env-file "$ENV_FILE" \
|
|
2136
|
+
up --remove-orphans
|
|
2137
|
+
fi
|
|
2138
|
+
|
|
1982
2139
|
docker run $CONTAINER_NAME --rm $TTY_FLAGS \
|
|
1983
2140
|
--init \
|
|
1984
2141
|
--platform "$PLATFORM" \
|
package/bin/cli.js
CHANGED
|
@@ -33,6 +33,8 @@ Commands:
|
|
|
33
33
|
git status Show git-enabled workspaces
|
|
34
34
|
git enable <path> Enable git access for a workspace
|
|
35
35
|
git disable <path> Disable git access for a workspace
|
|
36
|
+
git fetch-only <path> Enable fetch-only git access (no push) for a workspace
|
|
37
|
+
git full <path> Enable full git access for a workspace (moves from fetch-only if needed)
|
|
36
38
|
|
|
37
39
|
network list List configured networks
|
|
38
40
|
network add <name> [--global|--workspace <path>] Add a network
|
|
@@ -462,17 +464,25 @@ function runWorkspaceRemove(inputPath) {
|
|
|
462
464
|
// GIT COMMANDS
|
|
463
465
|
// ============================================================================
|
|
464
466
|
function runGitStatus() {
|
|
465
|
-
const config = readConfig()
|
|
466
|
-
const allowed = config.git?.allowedWorkspaces || []
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
console.log('
|
|
467
|
+
const config = readConfig()
|
|
468
|
+
const allowed = config.git?.allowedWorkspaces || []
|
|
469
|
+
const fetchOnly = config.git?.fetchOnlyWorkspaces || []
|
|
470
|
+
|
|
471
|
+
console.log('\n🔐 Git-Enabled Workspaces:\n')
|
|
472
|
+
if (allowed.length === 0 && fetchOnly.length === 0) {
|
|
473
|
+
console.log(' (no workspaces with git access)')
|
|
474
|
+
console.log('\n Enable git: npx @kokorolx/ai-sandbox-wrapper git enable <workspace-path>')
|
|
472
475
|
} else {
|
|
473
|
-
allowed.
|
|
476
|
+
if (allowed.length > 0) {
|
|
477
|
+
console.log(' Full access:')
|
|
478
|
+
allowed.forEach((ws, i) => console.log(` ${i + 1}. ${ws}`))
|
|
479
|
+
}
|
|
480
|
+
if (fetchOnly.length > 0) {
|
|
481
|
+
console.log(' Fetch only (no push):')
|
|
482
|
+
fetchOnly.forEach((ws, i) => console.log(` ${i + 1}. ${ws}`))
|
|
483
|
+
}
|
|
474
484
|
}
|
|
475
|
-
console.log('')
|
|
485
|
+
console.log('')
|
|
476
486
|
}
|
|
477
487
|
|
|
478
488
|
function runGitEnable(inputPath) {
|
|
@@ -541,6 +551,66 @@ function runGitDisable(inputPath) {
|
|
|
541
551
|
console.log(`✅ Disabled git access for: ${expandedPath}`);
|
|
542
552
|
}
|
|
543
553
|
|
|
554
|
+
function runGitFetchOnly(inputPath) {
|
|
555
|
+
if (!inputPath) {
|
|
556
|
+
console.error('❌ Please provide a workspace path')
|
|
557
|
+
console.error('Usage: npx @kokorolx/ai-sandbox-wrapper git fetch-only <path>')
|
|
558
|
+
process.exit(1)
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const expandedPath = expandHome(inputPath)
|
|
562
|
+
const config = readConfig()
|
|
563
|
+
|
|
564
|
+
if (!config.git) config.git = { allowedWorkspaces: [], fetchOnlyWorkspaces: [], keySelections: {} }
|
|
565
|
+
if (!config.git.fetchOnlyWorkspaces) config.git.fetchOnlyWorkspaces = []
|
|
566
|
+
|
|
567
|
+
// Remove from full access if present
|
|
568
|
+
if (config.git.allowedWorkspaces) {
|
|
569
|
+
const fullIdx = config.git.allowedWorkspaces.indexOf(expandedPath)
|
|
570
|
+
if (fullIdx !== -1) config.git.allowedWorkspaces.splice(fullIdx, 1)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (config.git.fetchOnlyWorkspaces.includes(expandedPath)) {
|
|
574
|
+
console.log(`ℹ️ Git fetch-only already enabled for: ${expandedPath}`)
|
|
575
|
+
return
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
config.git.fetchOnlyWorkspaces.push(expandedPath)
|
|
579
|
+
writeConfig(config)
|
|
580
|
+
|
|
581
|
+
console.log(`✅ Enabled git fetch-only for: ${expandedPath}`)
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function runGitFull(inputPath) {
|
|
585
|
+
if (!inputPath) {
|
|
586
|
+
console.error('❌ Please provide a workspace path')
|
|
587
|
+
console.error('Usage: npx @kokorolx/ai-sandbox-wrapper git full <path>')
|
|
588
|
+
process.exit(1)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const expandedPath = expandHome(inputPath)
|
|
592
|
+
const config = readConfig()
|
|
593
|
+
|
|
594
|
+
if (!config.git) config.git = { allowedWorkspaces: [], fetchOnlyWorkspaces: [], keySelections: {} }
|
|
595
|
+
if (!config.git.allowedWorkspaces) config.git.allowedWorkspaces = []
|
|
596
|
+
|
|
597
|
+
// Remove from fetch-only if present
|
|
598
|
+
if (config.git.fetchOnlyWorkspaces) {
|
|
599
|
+
const foIdx = config.git.fetchOnlyWorkspaces.indexOf(expandedPath)
|
|
600
|
+
if (foIdx !== -1) config.git.fetchOnlyWorkspaces.splice(foIdx, 1)
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (config.git.allowedWorkspaces.includes(expandedPath)) {
|
|
604
|
+
console.log(`ℹ️ Git full access already enabled for: ${expandedPath}`)
|
|
605
|
+
return
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
config.git.allowedWorkspaces.push(expandedPath)
|
|
609
|
+
writeConfig(config)
|
|
610
|
+
|
|
611
|
+
console.log(`✅ Enabled git full access for: ${expandedPath}`)
|
|
612
|
+
}
|
|
613
|
+
|
|
544
614
|
// ============================================================================
|
|
545
615
|
// NETWORK COMMANDS
|
|
546
616
|
// ============================================================================
|
|
@@ -1259,11 +1329,17 @@ switch (command) {
|
|
|
1259
1329
|
runGitEnable(subArg);
|
|
1260
1330
|
break;
|
|
1261
1331
|
case 'disable':
|
|
1262
|
-
runGitDisable(subArg)
|
|
1263
|
-
break
|
|
1332
|
+
runGitDisable(subArg)
|
|
1333
|
+
break
|
|
1334
|
+
case 'fetch-only':
|
|
1335
|
+
runGitFetchOnly(subArg)
|
|
1336
|
+
break
|
|
1337
|
+
case 'full':
|
|
1338
|
+
runGitFull(subArg)
|
|
1339
|
+
break
|
|
1264
1340
|
default:
|
|
1265
|
-
console.error('Usage: npx @kokorolx/ai-sandbox-wrapper git <status|enable|disable> [path]')
|
|
1266
|
-
process.exit(1)
|
|
1341
|
+
console.error('Usage: npx @kokorolx/ai-sandbox-wrapper git <status|enable|disable|fetch-only|full> [path]')
|
|
1342
|
+
process.exit(1)
|
|
1267
1343
|
}
|
|
1268
1344
|
break;
|
|
1269
1345
|
case 'network':
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Build RTK from source (multi-stage: only binary is kept, Rust toolchain discarded)
|
|
2
|
+
FROM rust:bookworm AS rtk-builder
|
|
3
|
+
RUN cargo install --git https://github.com/rtk-ai/rtk --locked
|
|
4
|
+
|
|
1
5
|
FROM node:22-bookworm-slim
|
|
2
6
|
|
|
3
7
|
ARG AGENT_UID=1001
|
|
@@ -34,31 +38,8 @@ RUN mkdir -p /usr/local/lib/openspec && \
|
|
|
34
38
|
ln -sf /usr/local/lib/openspec/node_modules/.bin/openspec /usr/local/bin/openspec && \
|
|
35
39
|
chmod -R 755 /usr/local/lib/openspec && \
|
|
36
40
|
chmod +x /usr/local/bin/openspec
|
|
37
|
-
# Install
|
|
38
|
-
|
|
39
|
-
libglib2.0-0 \
|
|
40
|
-
libnspr4 \
|
|
41
|
-
libnss3 \
|
|
42
|
-
libdbus-1-3 \
|
|
43
|
-
libatk1.0-0 \
|
|
44
|
-
libatk-bridge2.0-0 \
|
|
45
|
-
libcups2 \
|
|
46
|
-
libxcb1 \
|
|
47
|
-
libxkbcommon0 \
|
|
48
|
-
libatspi2.0-0 \
|
|
49
|
-
libx11-6 \
|
|
50
|
-
libxcomposite1 \
|
|
51
|
-
libxdamage1 \
|
|
52
|
-
libxext6 \
|
|
53
|
-
libxfixes3 \
|
|
54
|
-
libxrandr2 \
|
|
55
|
-
libgbm1 \
|
|
56
|
-
libcairo2 \
|
|
57
|
-
libpango-1.0-0 \
|
|
58
|
-
libasound2 \
|
|
59
|
-
&& rm -rf /var/lib/apt/lists/*
|
|
60
|
-
# Install Playwright and browsers via npm
|
|
61
|
-
RUN npm install -g playwright && npx playwright install
|
|
41
|
+
# Install RTK - token optimizer for AI coding agents (built from source)
|
|
42
|
+
COPY --from=rtk-builder /usr/local/cargo/bin/rtk /usr/local/bin/rtk
|
|
62
43
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
63
44
|
libglib2.0-0 \
|
|
64
45
|
libnspr4 \
|
|
@@ -96,9 +77,6 @@ RUN mkdir -p /opt/playwright-browsers && \
|
|
|
96
77
|
npx playwright-core install-deps chromium && \
|
|
97
78
|
chmod -R 777 /opt/playwright-browsers && \
|
|
98
79
|
ln -sf $(ls -d /opt/playwright-browsers/chromium-*/chrome-linux/chrome | head -1) /opt/chromium
|
|
99
|
-
ENV CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS=1
|
|
100
|
-
RUN npm install -g chrome-devtools-mcp@latest && \
|
|
101
|
-
touch /opt/.mcp-chrome-devtools-installed
|
|
102
80
|
RUN touch /opt/.mcp-playwright-installed
|
|
103
81
|
|
|
104
82
|
# Create workspace
|
package/lib/install-base.sh
CHANGED
|
@@ -5,6 +5,7 @@ set -e
|
|
|
5
5
|
mkdir -p "dockerfiles/base"
|
|
6
6
|
|
|
7
7
|
ADDITIONAL_TOOLS_INSTALL=""
|
|
8
|
+
DOCKERFILE_BUILD_STAGES=""
|
|
8
9
|
|
|
9
10
|
if [[ "${INSTALL_SPEC_KIT:-0}" -eq 1 ]]; then
|
|
10
11
|
echo "📦 spec-kit will be installed in base image"
|
|
@@ -39,6 +40,17 @@ if [[ "${INSTALL_OPENSPEC:-0}" -eq 1 ]]; then
|
|
|
39
40
|
'
|
|
40
41
|
fi
|
|
41
42
|
|
|
43
|
+
if [[ "${INSTALL_RTK:-0}" -eq 1 ]]; then
|
|
44
|
+
echo "📦 RTK (Rust Token Killer) will be installed in base image (multi-stage build)"
|
|
45
|
+
DOCKERFILE_BUILD_STAGES+='# Build RTK from source (multi-stage: only binary is kept, Rust toolchain discarded)
|
|
46
|
+
FROM rust:bookworm AS rtk-builder
|
|
47
|
+
RUN cargo install --git https://github.com/rtk-ai/rtk --locked
|
|
48
|
+
'
|
|
49
|
+
ADDITIONAL_TOOLS_INSTALL+='# Install RTK - token optimizer for AI coding agents (built from source)
|
|
50
|
+
COPY --from=rtk-builder /usr/local/cargo/bin/rtk /usr/local/bin/rtk
|
|
51
|
+
'
|
|
52
|
+
fi
|
|
53
|
+
|
|
42
54
|
if [[ "${INSTALL_PLAYWRIGHT:-0}" -eq 1 ]]; then
|
|
43
55
|
echo "📦 Playwright will be installed in base image"
|
|
44
56
|
ADDITIONAL_TOOLS_INSTALL+='# Install Playwright system dependencies
|
|
@@ -166,6 +178,7 @@ if [[ "${INSTALL_PLAYWRIGHT_MCP:-0}" -eq 1 ]]; then
|
|
|
166
178
|
fi
|
|
167
179
|
|
|
168
180
|
cat > "dockerfiles/base/Dockerfile" <<EOF
|
|
181
|
+
${DOCKERFILE_BUILD_STAGES}
|
|
169
182
|
FROM node:22-bookworm-slim
|
|
170
183
|
|
|
171
184
|
ARG AGENT_UID=1001
|
|
@@ -228,4 +241,3 @@ docker build ${DOCKER_NO_CACHE:+--no-cache} \
|
|
|
228
241
|
--build-arg AGENT_UID="${HOST_UID}" \
|
|
229
242
|
-t "ai-base:latest" "dockerfiles/base"
|
|
230
243
|
echo "✅ Base image built (ai-base:latest)"
|
|
231
|
-
|
package/lib/install-claude.sh
CHANGED
|
@@ -16,6 +16,16 @@ cat <<'EOF' > "dockerfiles/$TOOL/Dockerfile"
|
|
|
16
16
|
FROM ai-base:latest
|
|
17
17
|
|
|
18
18
|
USER root
|
|
19
|
+
# Install tmux for Agent Teams split-pane mode
|
|
20
|
+
RUN apt-get update && apt-get install -y --no-install-recommends tmux && rm -rf /var/lib/apt/lists/*
|
|
21
|
+
|
|
22
|
+
# Install CCS (Claude Code Switch) for multi-provider model switching
|
|
23
|
+
# Use --ignore-scripts to avoid postinstall failures when HOME=/home/agent but running as root
|
|
24
|
+
RUN npm install -g @kaitranntt/ccs --ignore-scripts && \
|
|
25
|
+
mkdir -p /home/agent/.ccs && \
|
|
26
|
+
chown -R agent:agent /home/agent/.ccs && \
|
|
27
|
+
which ccs && ccs --version
|
|
28
|
+
|
|
19
29
|
# Install Claude Code using official native installer
|
|
20
30
|
RUN curl -fsSL https://claude.ai/install.sh | bash && \
|
|
21
31
|
mkdir -p /usr/local/share && \
|
|
@@ -33,10 +43,15 @@ docker build ${DOCKER_NO_CACHE:+--no-cache} -t "ai-$TOOL:latest" "dockerfiles/$T
|
|
|
33
43
|
echo "✅ $TOOL installed (Native Binary)"
|
|
34
44
|
echo ""
|
|
35
45
|
echo "Features:"
|
|
36
|
-
echo " ✓ Official native binary
|
|
46
|
+
echo " ✓ Official native binary"
|
|
37
47
|
echo " ✓ Claude 3.5 Sonnet/Opus models"
|
|
38
48
|
echo " ✓ Agentic coding with file editing"
|
|
39
49
|
echo " ✓ Web search and fetch built-in"
|
|
50
|
+
echo " ✓ Agent Teams (multi-agent tmux split-pane workflows)"
|
|
51
|
+
echo " ✓ CCS (Claude Code Switch) for multi-provider model switching"
|
|
40
52
|
echo ""
|
|
41
53
|
echo "Usage: ai-run claude"
|
|
42
|
-
echo "Auth: Set ANTHROPIC_API_KEY
|
|
54
|
+
echo "Auth: Set ANTHROPIC_API_KEY in ~/.ai-sandbox/env"
|
|
55
|
+
echo ""
|
|
56
|
+
echo "Agent Teams: Add CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 to ~/.ai-sandbox/env"
|
|
57
|
+
echo "CCS: Run 'ai-run claude --shell' then 'ccs help' to configure providers"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# OpenClaw installer: Uses official Docker Compose setup
|
|
5
|
+
TOOL="openclaw"
|
|
6
|
+
|
|
7
|
+
echo "🔄 Installing $TOOL (OpenClaw - Official Docker Compose)..."
|
|
8
|
+
|
|
9
|
+
# Create directories
|
|
10
|
+
OPENCLAW_REPO_DIR="$HOME/.ai-sandbox/tools/$TOOL/repo"
|
|
11
|
+
mkdir -p "$OPENCLAW_REPO_DIR"
|
|
12
|
+
mkdir -p "$HOME/.openclaw"
|
|
13
|
+
|
|
14
|
+
# Clone OpenClaw repository if not exists
|
|
15
|
+
if [[ ! -d "$OPENCLAW_REPO_DIR/.git" ]]; then
|
|
16
|
+
echo "📦 Cloning OpenClaw repository..."
|
|
17
|
+
git clone https://github.com/openclaw/openclaw.git "$OPENCLAW_REPO_DIR"
|
|
18
|
+
else
|
|
19
|
+
echo "📦 OpenClaw repository already exists, pulling latest..."
|
|
20
|
+
cd "$OPENCLAW_REPO_DIR"
|
|
21
|
+
git pull origin main || git pull origin master || true
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
cd "$OPENCLAW_REPO_DIR"
|
|
25
|
+
|
|
26
|
+
# Build OpenClaw Docker image using their docker-compose
|
|
27
|
+
echo "🔨 Building OpenClaw Docker image..."
|
|
28
|
+
docker compose build
|
|
29
|
+
|
|
30
|
+
echo "✅ $TOOL installed (Docker Compose)"
|
|
31
|
+
echo ""
|
|
32
|
+
echo "Features:"
|
|
33
|
+
echo " ✓ Official OpenClaw Docker setup"
|
|
34
|
+
echo " ✓ Gateway mode (port 18789)"
|
|
35
|
+
echo " ✓ Bridge mode (port 18790)"
|
|
36
|
+
echo " ✓ Workspace whitelist integration"
|
|
37
|
+
echo ""
|
|
38
|
+
echo "Usage: ai-run openclaw"
|
|
39
|
+
echo "Config: ~/.openclaw/"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kokorolx/ai-sandbox-wrapper",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Docker-based security sandbox for AI coding agents. Isolate Claude, Gemini, Aider, and other AI tools from your host system.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"bin/",
|
|
29
29
|
"lib/",
|
|
30
|
+
"skills/",
|
|
30
31
|
"dockerfiles/",
|
|
31
32
|
"setup.sh",
|
|
32
33
|
"README.md",
|
package/setup.sh
CHANGED
|
@@ -274,8 +274,8 @@ echo "📁 Legacy workspaces file: $WORKSPACES_FILE"
|
|
|
274
274
|
WORKSPACE="${WORKSPACES[0]}"
|
|
275
275
|
|
|
276
276
|
# Tool definitions
|
|
277
|
-
TOOL_OPTIONS="amp,opencode,droid,claude,gemini,kilo,qwen,codex,qoder,auggie,codebuddy,jules,shai"
|
|
278
|
-
TOOL_DESCS="AI coding assistant from @sourcegraph/amp,Open-source coding tool from opencode-ai,Factory CLI from factory.ai,Claude Code CLI from Anthropic,Google Gemini CLI (free tier),AI pair programmer (Git-native),Kilo Code (500+ models),Alibaba Qwen CLI (1M context),OpenAI Codex terminal agent,Qoder AI CLI assistant,Augment Auggie CLI,Tencent CodeBuddy CLI,Google Jules CLI,OVHcloud SHAI agent"
|
|
277
|
+
TOOL_OPTIONS="amp,opencode,openclaw,droid,claude,gemini,kilo,qwen,codex,qoder,auggie,codebuddy,jules,shai"
|
|
278
|
+
TOOL_DESCS="AI coding assistant from @sourcegraph/amp,Open-source coding tool from opencode-ai,OpenClaw AI gateway (Docker Compose),Factory CLI from factory.ai,Claude Code CLI from Anthropic,Google Gemini CLI (free tier),AI pair programmer (Git-native),Kilo Code (500+ models),Alibaba Qwen CLI (1M context),OpenAI Codex terminal agent,Qoder AI CLI assistant,Augment Auggie CLI,Tencent CodeBuddy CLI,Google Jules CLI,OVHcloud SHAI agent"
|
|
279
279
|
|
|
280
280
|
# Interactive multi-select
|
|
281
281
|
multi_select "Select AI Tools to Install" "$TOOL_OPTIONS" "$TOOL_DESCS"
|
|
@@ -290,7 +290,7 @@ echo "Installing tools: ${TOOLS[*]}"
|
|
|
290
290
|
|
|
291
291
|
CONTAINERIZED_TOOLS=()
|
|
292
292
|
for tool in "${TOOLS[@]}"; do
|
|
293
|
-
if [[ "$tool" =~ ^(amp|opencode|claude|aider|droid|gemini|kilo|qwen|codex|qoder|auggie|codebuddy|jules|shai)$ ]]; then
|
|
293
|
+
if [[ "$tool" =~ ^(amp|opencode|openclaw|claude|aider|droid|gemini|kilo|qwen|codex|qoder|auggie|codebuddy|jules|shai)$ ]]; then
|
|
294
294
|
CONTAINERIZED_TOOLS+=("$tool")
|
|
295
295
|
fi
|
|
296
296
|
done
|
|
@@ -298,8 +298,8 @@ done
|
|
|
298
298
|
echo ""
|
|
299
299
|
if [[ ${#CONTAINERIZED_TOOLS[@]} -gt 0 ]]; then
|
|
300
300
|
# Category 1: AI Enhancement Tools (spec-driven development, UI/UX, browser automation)
|
|
301
|
-
AI_TOOL_OPTIONS="spec-kit,ux-ui-promax,openspec,playwright"
|
|
302
|
-
AI_TOOL_DESCS="Spec-driven development toolkit,UI/UX design intelligence tool,OpenSpec - spec-driven development,Browser automation + Chromium/Firefox/WebKit (~500MB)"
|
|
301
|
+
AI_TOOL_OPTIONS="spec-kit,ux-ui-promax,openspec,playwright,rtk"
|
|
302
|
+
AI_TOOL_DESCS="Spec-driven development toolkit,UI/UX design intelligence tool,OpenSpec - spec-driven development,Browser automation + Chromium/Firefox/WebKit (~500MB),RTK token optimizer - reduces LLM token usage by 60-90% (~5MB)"
|
|
303
303
|
|
|
304
304
|
multi_select "Select AI Enhancement Tools (installed in containers)" "$AI_TOOL_OPTIONS" "$AI_TOOL_DESCS"
|
|
305
305
|
AI_ENHANCEMENT_TOOLS=("${SELECTED_ITEMS[@]}")
|
|
@@ -376,6 +376,7 @@ if [[ $NEEDS_BASE_IMAGE -eq 1 ]]; then
|
|
|
376
376
|
INSTALL_RUBY=0
|
|
377
377
|
INSTALL_CHROME_DEVTOOLS_MCP=0
|
|
378
378
|
INSTALL_PLAYWRIGHT_MCP=0
|
|
379
|
+
INSTALL_RTK=0
|
|
379
380
|
|
|
380
381
|
for addon in "${ADDITIONAL_TOOLS[@]}"; do
|
|
381
382
|
case "$addon" in
|
|
@@ -400,10 +401,13 @@ if [[ $NEEDS_BASE_IMAGE -eq 1 ]]; then
|
|
|
400
401
|
playwright-mcp)
|
|
401
402
|
INSTALL_PLAYWRIGHT_MCP=1
|
|
402
403
|
;;
|
|
404
|
+
rtk)
|
|
405
|
+
INSTALL_RTK=1
|
|
406
|
+
;;
|
|
403
407
|
esac
|
|
404
408
|
done
|
|
405
409
|
|
|
406
|
-
export INSTALL_SPEC_KIT INSTALL_UX_UI_PROMAX INSTALL_OPENSPEC INSTALL_PLAYWRIGHT INSTALL_RUBY INSTALL_CHROME_DEVTOOLS_MCP INSTALL_PLAYWRIGHT_MCP
|
|
410
|
+
export INSTALL_SPEC_KIT INSTALL_UX_UI_PROMAX INSTALL_OPENSPEC INSTALL_PLAYWRIGHT INSTALL_RUBY INSTALL_CHROME_DEVTOOLS_MCP INSTALL_PLAYWRIGHT_MCP INSTALL_RTK
|
|
407
411
|
bash "$SCRIPT_DIR/lib/install-base.sh"
|
|
408
412
|
|
|
409
413
|
# Save MCP selections to ~/.ai-sandbox/config.json for ai-run auto-configuration
|
|
@@ -431,6 +435,9 @@ for tool in "${TOOLS[@]}"; do
|
|
|
431
435
|
opencode)
|
|
432
436
|
bash "$SCRIPT_DIR/lib/install-opencode.sh"
|
|
433
437
|
;;
|
|
438
|
+
openclaw)
|
|
439
|
+
bash "$SCRIPT_DIR/lib/install-openclaw.sh"
|
|
440
|
+
;;
|
|
434
441
|
droid)
|
|
435
442
|
bash "$SCRIPT_DIR/lib/install-droid.sh"
|
|
436
443
|
;;
|
|
@@ -525,6 +532,9 @@ if [[ ${#ADDITIONAL_TOOLS[@]} -gt 0 ]]; then
|
|
|
525
532
|
playwright-mcp)
|
|
526
533
|
echo " @playwright/mcp - Microsoft Playwright MCP server"
|
|
527
534
|
;;
|
|
535
|
+
rtk)
|
|
536
|
+
echo " rtk - Token optimizer for AI coding agents (60-90% savings)"
|
|
537
|
+
;;
|
|
528
538
|
esac
|
|
529
539
|
done
|
|
530
540
|
fi
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rtk
|
|
3
|
+
description: "Token optimizer for AI coding agents. Wraps common CLI commands with rtk to reduce LLM token consumption by 60-90%."
|
|
4
|
+
compatibility: "OpenCode with RTK binary installed"
|
|
5
|
+
metadata:
|
|
6
|
+
author: Sisyphus
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# RTK — Rust Token Killer
|
|
11
|
+
|
|
12
|
+
**Purpose**: Reduce token consumption by 60-90% on shell commands. RTK filters and compresses command output before it reaches your context.
|
|
13
|
+
|
|
14
|
+
## When This Skill Activates
|
|
15
|
+
|
|
16
|
+
This skill is loaded automatically. Once active, **always** prefix supported commands with `rtk`.
|
|
17
|
+
|
|
18
|
+
## Command Mappings (MANDATORY)
|
|
19
|
+
|
|
20
|
+
When running shell commands via Bash tool, use `rtk` prefix for these commands:
|
|
21
|
+
|
|
22
|
+
| Instead of | Use | Savings |
|
|
23
|
+
|-----------|-----|---------|
|
|
24
|
+
| `git status` | `rtk git status` | ~80% |
|
|
25
|
+
| `git diff` | `rtk git diff` | ~75% |
|
|
26
|
+
| `git log` | `rtk git log` | ~80% |
|
|
27
|
+
| `git add/commit/push/pull` | `rtk git add/commit/push/pull` | ~90% |
|
|
28
|
+
| `ls` / `ls -la` | `rtk ls` | ~80% |
|
|
29
|
+
| `cat file` | `rtk read file` | ~70% |
|
|
30
|
+
| `grep pattern .` | `rtk grep pattern .` | ~80% |
|
|
31
|
+
| `rg pattern` | `rtk grep pattern .` | ~80% |
|
|
32
|
+
| `npm test` / `cargo test` / `pytest` | `rtk test <cmd>` | ~90% |
|
|
33
|
+
| `npm run build` / `cargo build` | `rtk err <cmd>` | ~80% |
|
|
34
|
+
| `gh pr list/view` | `rtk gh pr list/view` | ~70% |
|
|
35
|
+
| `docker ps` | `rtk docker ps` | ~80% |
|
|
36
|
+
| `eslint` / `tsc` | `rtk lint` / `rtk tsc` | ~80% |
|
|
37
|
+
|
|
38
|
+
## Searching Inside `node_modules` / Ignored Directories
|
|
39
|
+
|
|
40
|
+
By default, `rtk grep` respects `.gitignore` rules — meaning `node_modules`, `.nuxt`, `dist`, etc. are **excluded**. This is the right behavior 99% of the time.
|
|
41
|
+
|
|
42
|
+
When you **need** to search inside ignored directories (debugging a library, checking an API signature, tracing a dependency bug):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Search all files including node_modules (--no-ignore bypasses .gitignore)
|
|
46
|
+
rtk grep "defineStore" . --no-ignore
|
|
47
|
+
|
|
48
|
+
# Search a specific package only (combine --no-ignore with --glob)
|
|
49
|
+
rtk grep "defineStore" . --no-ignore --glob 'node_modules/pinia/**'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**What does NOT work:**
|
|
53
|
+
- `rtk grep "pattern" node_modules/pinia/` — still excluded even with direct path
|
|
54
|
+
- `rtk grep "pattern" . --glob 'node_modules/**'` — glob alone doesn't override .gitignore
|
|
55
|
+
|
|
56
|
+
**Key flag: `--no-ignore`** — this is the ONLY way to search ignored directories with rtk grep.
|
|
57
|
+
|
|
58
|
+
### Other useful `rtk grep` flags
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
rtk grep "pattern" . -t ts # Filter by file type (ts, py, rust, etc.)
|
|
62
|
+
rtk grep "pattern" . -m 100 # Increase max results (default: 50)
|
|
63
|
+
rtk grep "pattern" . -u # Ultra-compact mode (even fewer tokens)
|
|
64
|
+
rtk grep "pattern" . -l 120 # Max line length before truncation (default: 80)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Commands to NOT Wrap
|
|
68
|
+
|
|
69
|
+
Do NOT prefix these with `rtk` (unsupported or counterproductive):
|
|
70
|
+
|
|
71
|
+
- `npx`, `npm install`, `pip install` (package managers)
|
|
72
|
+
- `node`, `python3`, `ruby` (interpreters)
|
|
73
|
+
- `nano-brain`, `openspec`, `opencode` (custom tools)
|
|
74
|
+
- Heredocs (`<<EOF`)
|
|
75
|
+
- Piped commands (`cmd1 | cmd2`) — wrap only the first command if applicable
|
|
76
|
+
- Commands already prefixed with `rtk`
|
|
77
|
+
|
|
78
|
+
## How RTK Works
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
Without RTK: git status → 50 lines raw output → 2,000 tokens
|
|
82
|
+
With RTK: rtk git status → "3 modified, 1 untracked ✓" → 200 tokens
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
RTK runs the real command, then filters/compresses the output. The agent sees a compact summary instead of verbose raw output.
|
|
86
|
+
|
|
87
|
+
## Detection
|
|
88
|
+
|
|
89
|
+
Before using RTK commands, verify it's installed:
|
|
90
|
+
```bash
|
|
91
|
+
rtk --version
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
If `rtk` is not found, skip this skill — run commands normally without the `rtk` prefix.
|
|
95
|
+
|
|
96
|
+
## Token Savings Reference
|
|
97
|
+
|
|
98
|
+
Typical 30-min coding session:
|
|
99
|
+
- Without RTK: ~150,000 tokens
|
|
100
|
+
- With RTK: ~45,000 tokens
|
|
101
|
+
- **Savings: ~70%**
|
|
102
|
+
|
|
103
|
+
Biggest wins: test output (`rtk test` — 90%), git operations (`rtk git` — 80%), file reading (`rtk read` — 70%).
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rtk-setup
|
|
3
|
+
description: "One-time setup + ongoing enforcement of RTK (Rust Token Killer) across sessions and subagents. Persists RTK instructions to AGENTS.md and propagates to delegated tasks."
|
|
4
|
+
compatibility: "OpenCode with RTK binary installed"
|
|
5
|
+
metadata:
|
|
6
|
+
author: Sisyphus
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# RTK Setup — Persistent RTK Enforcement
|
|
11
|
+
|
|
12
|
+
This skill performs **setup** (persist RTK instructions) and **ongoing enforcement** (use rtk, propagate to subagents).
|
|
13
|
+
|
|
14
|
+
## Phase 1: Detection (ALWAYS run first)
|
|
15
|
+
|
|
16
|
+
Run these checks before anything else:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
rtk --version
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- If `rtk` is NOT found → **STOP**. Tell the user: "RTK is not installed. Install it first: `cargo install --git https://github.com/rtk-ai/rtk --locked`". Do NOT proceed with setup.
|
|
23
|
+
- If `rtk` IS found → continue.
|
|
24
|
+
|
|
25
|
+
### Check oh-my-opencode
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
ls ~/.config/opencode/oh-my-opencode.json 2>/dev/null
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- If found → omo is installed. Note this for Phase 2.
|
|
32
|
+
- If not found → skip omo-specific steps.
|
|
33
|
+
|
|
34
|
+
## Phase 2: Setup (persist to AGENTS.md)
|
|
35
|
+
|
|
36
|
+
Check BOTH of these files for the RTK managed block:
|
|
37
|
+
|
|
38
|
+
1. **User-level**: `~/.config/opencode/AGENTS.md`
|
|
39
|
+
2. **Project-level**: `./AGENTS.md` (current workspace)
|
|
40
|
+
|
|
41
|
+
For EACH file, check if `<!-- RTK:START -->` exists. If it does NOT exist, **append** the following managed block to the file (create the file if it doesn't exist). NEVER replace existing content — only append.
|
|
42
|
+
|
|
43
|
+
```markdown
|
|
44
|
+
<!-- RTK:START -->
|
|
45
|
+
<!-- Managed block - do not edit manually. Updated by: rtk-setup skill -->
|
|
46
|
+
|
|
47
|
+
## RTK Token Optimizer
|
|
48
|
+
|
|
49
|
+
This project uses **RTK** (Rust Token Killer) to reduce LLM token consumption by 60-90%.
|
|
50
|
+
|
|
51
|
+
### Rules for ALL agents (including subagents)
|
|
52
|
+
|
|
53
|
+
**ALWAYS** prefix these commands with `rtk`:
|
|
54
|
+
|
|
55
|
+
| Command | Use instead |
|
|
56
|
+
|---------|-------------|
|
|
57
|
+
| `git status/diff/log/add/commit/push/pull` | `rtk git ...` |
|
|
58
|
+
| `ls`, `ls -la` | `rtk ls` |
|
|
59
|
+
| `cat file` | `rtk read file` |
|
|
60
|
+
| `grep pattern .` / `rg pattern` | `rtk grep pattern .` |
|
|
61
|
+
| `npm test` / `cargo test` / `pytest` | `rtk test <cmd>` |
|
|
62
|
+
| `npm run build` / `cargo build` | `rtk err <cmd>` |
|
|
63
|
+
| `gh pr list/view` | `rtk gh pr list/view` |
|
|
64
|
+
| `docker ps` | `rtk docker ps` |
|
|
65
|
+
| `eslint` / `tsc` | `rtk lint` / `rtk tsc` |
|
|
66
|
+
|
|
67
|
+
**Do NOT** prefix: `npx`, `npm install`, `pip install`, `node`, `python3`, heredocs, piped commands.
|
|
68
|
+
|
|
69
|
+
<!-- RTK:END -->
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### If oh-my-opencode is installed
|
|
73
|
+
|
|
74
|
+
After updating AGENTS.md files, check if the omo config at `~/.config/opencode/oh-my-opencode.json` has `prompt_append` fields on agents. If it does, inform the user:
|
|
75
|
+
|
|
76
|
+
> "omo is installed. For maximum coverage, you can add RTK instructions to agent `prompt_append` fields in `oh-my-opencode.json`. This ensures subagents always use rtk even when AGENTS.md is not loaded. Want me to do this?"
|
|
77
|
+
|
|
78
|
+
**Only modify `oh-my-opencode.json` if the user explicitly confirms.** If they confirm, append this line to the `prompt_append` of `sisyphus-junior` agent (the primary coder):
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
\n\nRTK: ALWAYS prefix shell commands with `rtk` when available (git, ls, grep, test, build). Example: `rtk git status` not `git status`.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Do NOT modify other agents' prompt_append unless asked.
|
|
85
|
+
|
|
86
|
+
## Phase 3: Ongoing Enforcement (EVERY time this skill is loaded)
|
|
87
|
+
|
|
88
|
+
### For the current agent
|
|
89
|
+
|
|
90
|
+
From this point forward in this session, **you MUST**:
|
|
91
|
+
|
|
92
|
+
1. **Use `rtk` prefix** for all supported shell commands (see table above)
|
|
93
|
+
2. **Verify rtk is available** before first use: `rtk --version`
|
|
94
|
+
3. If rtk is not available in the current environment, fall back to normal commands silently
|
|
95
|
+
|
|
96
|
+
### For subagent delegation
|
|
97
|
+
|
|
98
|
+
When delegating ANY task that involves shell commands (via `task()`, `delegate_task()`, or any delegation mechanism), **ALWAYS** add this to the `MUST DO` section of the delegation prompt:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
- Use `rtk` prefix for shell commands when available (git, ls, grep, test, build). Run `rtk --version` first to check. If not installed, use normal commands. Examples: `rtk git status`, `rtk ls`, `rtk grep pattern .`, `rtk test npm test`.
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This is NON-NEGOTIABLE. Subagents do not read AGENTS.md, so RTK instructions MUST be passed explicitly in every delegation prompt.
|
|
105
|
+
|
|
106
|
+
### Quick reference for subagent prompts
|
|
107
|
+
|
|
108
|
+
Copy-paste this one-liner into MUST DO sections:
|
|
109
|
+
|
|
110
|
+
> Use `rtk` prefix for all shell commands (git, ls, grep, test, build, docker). Example: `rtk git status` not `git status`. If `rtk` is not found, use normal commands.
|
|
111
|
+
|
|
112
|
+
## Summary
|
|
113
|
+
|
|
114
|
+
| Phase | When | What |
|
|
115
|
+
|-------|------|------|
|
|
116
|
+
| Detection | Always first | Check rtk installed, check omo |
|
|
117
|
+
| Setup | Once (idempotent) | Append RTK block to AGENTS.md (user + project) |
|
|
118
|
+
| Enforcement | Every session | Use rtk yourself, propagate to all subagents |
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
FROM ai-base:latest
|
|
2
|
-
|
|
3
|
-
# Switch to root only for installing bun globally (needed for the system)
|
|
4
|
-
USER root
|
|
5
|
-
RUN npm install -g bun
|
|
6
|
-
USER agent
|
|
7
|
-
|
|
8
|
-
# Install Amp into user directory
|
|
9
|
-
RUN mkdir -p /home/agent/lib/amp && \
|
|
10
|
-
cd /home/agent/lib/amp && \
|
|
11
|
-
bun init -y && \
|
|
12
|
-
bun add @sourcegraph/amp
|
|
13
|
-
|
|
14
|
-
# Add the node_modules .bin to PATH
|
|
15
|
-
ENV PATH="/home/agent/lib/amp/node_modules/.bin:${PATH}"
|
|
16
|
-
|
|
17
|
-
ENTRYPOINT ["amp"]
|