@kokorolx/ai-sandbox-wrapper 3.4.3 → 4.0.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/bin/ai-run +367 -1
- package/bin/cli.js +2 -0
- package/dockerfiles/base/Dockerfile +31 -2
- package/dockerfiles/sandbox/Dockerfile +36 -8
- package/lib/build-sandbox.sh +8 -1
- package/lib/install-aider.sh +4 -3
- package/lib/install-base.sh +45 -3
- package/lib/install-open-design.sh +60 -0
- package/lib/install-openclaw.sh +2 -2
- package/lib/install-opencode.sh +10 -3
- package/lib/playwright-mcp-config.sh +1 -0
- package/package.json +1 -1
- package/setup.sh +71 -6
- package/skills/dd-pup/SKILL.md +186 -0
- package/dockerfiles/base/skills/rtk/SKILL.md +0 -103
- package/dockerfiles/base/skills/rtk-setup/SKILL.md +0 -118
- package/dockerfiles/opencode/Dockerfile +0 -9
- package/dockerfiles/sandbox/skills/rtk/SKILL.md +0 -103
- package/dockerfiles/sandbox/skills/rtk-setup/SKILL.md +0 -118
package/bin/ai-run
CHANGED
|
@@ -141,6 +141,290 @@ else
|
|
|
141
141
|
TOOL=""
|
|
142
142
|
fi
|
|
143
143
|
|
|
144
|
+
# ────────────────────────────────────────────────────────────────
|
|
145
|
+
# OPEN-DESIGN SERVICE-TYPE TOOL DISPATCHER
|
|
146
|
+
# Unlike ephemeral CLI tools, open-design is a long-running daemon.
|
|
147
|
+
# Routes init/start/stop/restart/status/logs subcommands and exits.
|
|
148
|
+
# ────────────────────────────────────────────────────────────────
|
|
149
|
+
if [[ "$TOOL" == "open-design" ]]; then
|
|
150
|
+
OD_CONTAINER_NAME="ai-open-design"
|
|
151
|
+
OD_IMAGE="ai-open-design:latest"
|
|
152
|
+
OD_NETWORK="ai-sandbox"
|
|
153
|
+
OD_VOLUME="ai-open-design-data"
|
|
154
|
+
OD_ENV_FILE="$HOME/.ai-sandbox/env"
|
|
155
|
+
OD_DEFAULT_URL="http://ai-open-design:7456"
|
|
156
|
+
|
|
157
|
+
od_print_help() {
|
|
158
|
+
cat <<HLP
|
|
159
|
+
Usage: ai-run open-design <subcommand> [options]
|
|
160
|
+
|
|
161
|
+
Subcommands:
|
|
162
|
+
init [--force] One-time setup: generate API token, create network/volume
|
|
163
|
+
start [--expose] [--port N] Boot daemon (detached). --expose publishes port to host
|
|
164
|
+
stop Stop daemon (preserves container for restart)
|
|
165
|
+
restart [start-flags...] Stop then start
|
|
166
|
+
status Show daemon state, network, port, token, health
|
|
167
|
+
logs [-f|--follow] Show daemon logs (-f to follow)
|
|
168
|
+
--help, -h Show this help
|
|
169
|
+
|
|
170
|
+
Examples:
|
|
171
|
+
ai-run open-design init
|
|
172
|
+
ai-run open-design start
|
|
173
|
+
ai-run open-design start --expose # publishes 7456 to host
|
|
174
|
+
ai-run open-design start --expose --port 17456
|
|
175
|
+
ai-run open-design status
|
|
176
|
+
HLP
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
od_env_has_token() {
|
|
180
|
+
[[ -f "$OD_ENV_FILE" ]] && grep -q "^OD_API_TOKEN=" "$OD_ENV_FILE"
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
od_read_token() {
|
|
184
|
+
[[ -f "$OD_ENV_FILE" ]] && grep "^OD_API_TOKEN=" "$OD_ENV_FILE" | head -1 | cut -d= -f2-
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
od_init() {
|
|
188
|
+
local force=false
|
|
189
|
+
if [[ "${1:-}" == "--force" ]]; then
|
|
190
|
+
force=true
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
mkdir -p "$(dirname "$OD_ENV_FILE")"
|
|
194
|
+
touch "$OD_ENV_FILE"
|
|
195
|
+
chmod 600 "$OD_ENV_FILE"
|
|
196
|
+
|
|
197
|
+
if od_env_has_token && [[ "$force" != "true" ]]; then
|
|
198
|
+
echo "ℹ️ OD_API_TOKEN already set in $OD_ENV_FILE — nothing to do"
|
|
199
|
+
echo " Use 'ai-run open-design init --force' to regenerate (will invalidate running sessions)"
|
|
200
|
+
else
|
|
201
|
+
if [[ "$force" == "true" ]] && od_env_has_token; then
|
|
202
|
+
printf "⚠️ This will replace the existing OD_API_TOKEN. Continue? [y/N] "
|
|
203
|
+
read -r confirm
|
|
204
|
+
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
|
205
|
+
echo "Aborted."
|
|
206
|
+
exit 0
|
|
207
|
+
fi
|
|
208
|
+
# Strip existing OD_API_TOKEN line
|
|
209
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
210
|
+
sed -i '' '/^OD_API_TOKEN=/d' "$OD_ENV_FILE"
|
|
211
|
+
else
|
|
212
|
+
sed -i '/^OD_API_TOKEN=/d' "$OD_ENV_FILE"
|
|
213
|
+
fi
|
|
214
|
+
fi
|
|
215
|
+
if ! command -v openssl >/dev/null 2>&1; then
|
|
216
|
+
echo "❌ ERROR: 'openssl' is required to generate a token" >&2
|
|
217
|
+
exit 1
|
|
218
|
+
fi
|
|
219
|
+
local token
|
|
220
|
+
token="$(openssl rand -hex 32)"
|
|
221
|
+
# Ensure trailing newline before append to avoid joining with previous line
|
|
222
|
+
if [[ -s "$OD_ENV_FILE" && -n "$(tail -c 1 "$OD_ENV_FILE" 2>/dev/null)" ]]; then
|
|
223
|
+
echo "" >> "$OD_ENV_FILE"
|
|
224
|
+
fi
|
|
225
|
+
echo "OD_API_TOKEN=$token" >> "$OD_ENV_FILE"
|
|
226
|
+
echo "✅ Generated OD_API_TOKEN (256-bit, written to $OD_ENV_FILE)"
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
# Ensure OD_DAEMON_URL line
|
|
230
|
+
if ! grep -q "^OD_DAEMON_URL=" "$OD_ENV_FILE"; then
|
|
231
|
+
if [[ -s "$OD_ENV_FILE" && -n "$(tail -c 1 "$OD_ENV_FILE" 2>/dev/null)" ]]; then
|
|
232
|
+
echo "" >> "$OD_ENV_FILE"
|
|
233
|
+
fi
|
|
234
|
+
echo "OD_DAEMON_URL=$OD_DEFAULT_URL" >> "$OD_ENV_FILE"
|
|
235
|
+
echo "✅ Set OD_DAEMON_URL=$OD_DEFAULT_URL in $OD_ENV_FILE"
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
chmod 600 "$OD_ENV_FILE"
|
|
239
|
+
|
|
240
|
+
# Ensure network
|
|
241
|
+
if ensure_network "$OD_NETWORK"; then
|
|
242
|
+
echo "✅ Docker network '$OD_NETWORK' ready"
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
# Ensure volume
|
|
246
|
+
if ! docker volume inspect "$OD_VOLUME" >/dev/null 2>&1; then
|
|
247
|
+
docker volume create "$OD_VOLUME" >/dev/null
|
|
248
|
+
echo "✅ Docker volume '$OD_VOLUME' created"
|
|
249
|
+
else
|
|
250
|
+
echo "ℹ️ Docker volume '$OD_VOLUME' already exists"
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
echo ""
|
|
254
|
+
echo "Next: ai-run open-design start"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
od_start() {
|
|
258
|
+
local expose=false
|
|
259
|
+
local host_port=7456
|
|
260
|
+
|
|
261
|
+
while [[ $# -gt 0 ]]; do
|
|
262
|
+
case "$1" in
|
|
263
|
+
--expose) expose=true; shift ;;
|
|
264
|
+
--port)
|
|
265
|
+
shift
|
|
266
|
+
if [[ $# -gt 0 && "$1" =~ ^[0-9]+$ ]]; then
|
|
267
|
+
if (( $1 < 1 || $1 > 65535 )); then
|
|
268
|
+
echo "❌ ERROR: --port value '$1' out of range (1-65535)" >&2; exit 1
|
|
269
|
+
fi
|
|
270
|
+
host_port="$1"
|
|
271
|
+
shift
|
|
272
|
+
else
|
|
273
|
+
echo "❌ ERROR: --port requires a numeric value (e.g. 7456)" >&2; exit 1
|
|
274
|
+
fi
|
|
275
|
+
;;
|
|
276
|
+
*) echo "❌ ERROR: unknown start flag: $1" >&2; exit 1 ;;
|
|
277
|
+
esac
|
|
278
|
+
done
|
|
279
|
+
|
|
280
|
+
if ! od_env_has_token; then
|
|
281
|
+
echo "❌ ERROR: OD_API_TOKEN not found in $OD_ENV_FILE"
|
|
282
|
+
echo " Run: ai-run open-design init"
|
|
283
|
+
exit 1
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
if ! docker image inspect "$OD_IMAGE" >/dev/null 2>&1; then
|
|
287
|
+
echo "❌ ERROR: image '$OD_IMAGE' not built"
|
|
288
|
+
echo " Run: bash lib/install-open-design.sh"
|
|
289
|
+
exit 1
|
|
290
|
+
fi
|
|
291
|
+
|
|
292
|
+
ensure_network "$OD_NETWORK" || exit 1
|
|
293
|
+
docker volume inspect "$OD_VOLUME" >/dev/null 2>&1 || docker volume create "$OD_VOLUME" >/dev/null
|
|
294
|
+
|
|
295
|
+
# If a container with this name exists, handle it gracefully
|
|
296
|
+
if docker ps -a --format '{{.Names}}' | grep -q "^${OD_CONTAINER_NAME}$"; then
|
|
297
|
+
if docker ps --format '{{.Names}}' | grep -q "^${OD_CONTAINER_NAME}$"; then
|
|
298
|
+
echo "ℹ️ '$OD_CONTAINER_NAME' is already running"
|
|
299
|
+
echo " Use 'ai-run open-design restart' to apply new flags"
|
|
300
|
+
return 0
|
|
301
|
+
else
|
|
302
|
+
echo "🔄 Removing stopped container '$OD_CONTAINER_NAME' to recreate with current flags..."
|
|
303
|
+
docker rm "$OD_CONTAINER_NAME" >/dev/null
|
|
304
|
+
fi
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
local run_args=(
|
|
308
|
+
run -d
|
|
309
|
+
--name "$OD_CONTAINER_NAME"
|
|
310
|
+
--network "$OD_NETWORK"
|
|
311
|
+
--restart unless-stopped
|
|
312
|
+
-v "$OD_VOLUME:/app/.od"
|
|
313
|
+
--env-file "$OD_ENV_FILE"
|
|
314
|
+
-p "127.0.0.1:${host_port}:7456"
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
if [[ "$expose" == "true" ]]; then
|
|
318
|
+
echo "ℹ️ Port ${host_port} already published by default (127.0.0.1:${host_port}:7456)"
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
run_args+=("$OD_IMAGE")
|
|
322
|
+
|
|
323
|
+
echo "🔄 Starting $OD_CONTAINER_NAME..."
|
|
324
|
+
docker "${run_args[@]}" >/dev/null
|
|
325
|
+
echo "✅ $OD_CONTAINER_NAME running on network '$OD_NETWORK'"
|
|
326
|
+
echo " Published to host: http://localhost:${host_port}"
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
od_stop() {
|
|
330
|
+
if docker ps --format '{{.Names}}' | grep -q "^${OD_CONTAINER_NAME}$"; then
|
|
331
|
+
echo "🔄 Stopping $OD_CONTAINER_NAME..."
|
|
332
|
+
docker stop "$OD_CONTAINER_NAME" >/dev/null
|
|
333
|
+
echo "✅ $OD_CONTAINER_NAME stopped (data preserved in volume '$OD_VOLUME')"
|
|
334
|
+
else
|
|
335
|
+
echo "ℹ️ $OD_CONTAINER_NAME is not running"
|
|
336
|
+
fi
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
od_restart() {
|
|
340
|
+
od_stop
|
|
341
|
+
od_start "$@"
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
od_status() {
|
|
345
|
+
echo "Container : $OD_CONTAINER_NAME"
|
|
346
|
+
if docker ps --format '{{.Names}}' | grep -q "^${OD_CONTAINER_NAME}$"; then
|
|
347
|
+
echo "State : running"
|
|
348
|
+
local uptime
|
|
349
|
+
uptime="$(docker inspect -f '{{.State.StartedAt}}' "$OD_CONTAINER_NAME" 2>/dev/null || echo unknown)"
|
|
350
|
+
echo "Started : $uptime"
|
|
351
|
+
local ports
|
|
352
|
+
ports="$(docker inspect -f '{{json .NetworkSettings.Ports}}' "$OD_CONTAINER_NAME" 2>/dev/null || echo '{}')"
|
|
353
|
+
echo "Ports : $ports"
|
|
354
|
+
elif docker ps -a --format '{{.Names}}' | grep -q "^${OD_CONTAINER_NAME}$"; then
|
|
355
|
+
echo "State : stopped"
|
|
356
|
+
echo " Hint : ai-run open-design start"
|
|
357
|
+
else
|
|
358
|
+
echo "State : not installed"
|
|
359
|
+
echo " Hint : ai-run open-design init && ai-run open-design start"
|
|
360
|
+
fi
|
|
361
|
+
|
|
362
|
+
echo "Network : $OD_NETWORK"
|
|
363
|
+
echo "Volume : $OD_VOLUME"
|
|
364
|
+
if od_env_has_token; then
|
|
365
|
+
local tok
|
|
366
|
+
tok="$(od_read_token)"
|
|
367
|
+
echo "API token : set (***${tok: -4})"
|
|
368
|
+
else
|
|
369
|
+
echo "API token : (unset — run 'ai-run open-design init')"
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
# Try health check (only meaningful if container is running)
|
|
373
|
+
if docker ps --format '{{.Names}}' | grep -q "^${OD_CONTAINER_NAME}$"; then
|
|
374
|
+
# Try in-container curl first (fast path); fall back to one-off container on same network
|
|
375
|
+
# because upstream daemon image may not bundle curl
|
|
376
|
+
local health_ok=false
|
|
377
|
+
if docker exec "$OD_CONTAINER_NAME" sh -c 'command -v curl' >/dev/null 2>&1; then
|
|
378
|
+
if docker exec "$OD_CONTAINER_NAME" curl -sf --max-time 3 http://127.0.0.1:7456/api/health >/dev/null 2>&1; then
|
|
379
|
+
health_ok=true
|
|
380
|
+
fi
|
|
381
|
+
else
|
|
382
|
+
# Fallback: probe via a tiny one-off container in the same network
|
|
383
|
+
if docker run --rm --network "$OD_NETWORK" curlimages/curl:latest \
|
|
384
|
+
-sf --max-time 3 "http://${OD_CONTAINER_NAME}:7456/api/health" >/dev/null 2>&1; then
|
|
385
|
+
health_ok=true
|
|
386
|
+
fi
|
|
387
|
+
fi
|
|
388
|
+
if [[ "$health_ok" == "true" ]]; then
|
|
389
|
+
echo "Health : OK"
|
|
390
|
+
else
|
|
391
|
+
echo "Health : FAIL (daemon not responding on /api/health)"
|
|
392
|
+
fi
|
|
393
|
+
fi
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
od_logs() {
|
|
397
|
+
if ! docker ps -a --format '{{.Names}}' | grep -q "^${OD_CONTAINER_NAME}$"; then
|
|
398
|
+
echo "❌ ERROR: container '$OD_CONTAINER_NAME' does not exist" >&2
|
|
399
|
+
exit 1
|
|
400
|
+
fi
|
|
401
|
+
docker logs "$@" "$OD_CONTAINER_NAME"
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
# Dispatch subcommand
|
|
405
|
+
SUBCMD="${1:-}"
|
|
406
|
+
if [[ -n "$SUBCMD" ]]; then shift; fi
|
|
407
|
+
case "$SUBCMD" in
|
|
408
|
+
init) od_init "$@" ;;
|
|
409
|
+
start) od_start "$@" ;;
|
|
410
|
+
stop) od_stop ;;
|
|
411
|
+
restart) od_restart "$@" ;;
|
|
412
|
+
status) od_status ;;
|
|
413
|
+
logs) od_logs "$@" ;;
|
|
414
|
+
--help|-h|"") od_print_help ;;
|
|
415
|
+
*)
|
|
416
|
+
echo "❌ ERROR: unknown subcommand '$SUBCMD'" >&2
|
|
417
|
+
echo ""
|
|
418
|
+
od_print_help
|
|
419
|
+
exit 1
|
|
420
|
+
;;
|
|
421
|
+
esac
|
|
422
|
+
exit 0
|
|
423
|
+
fi
|
|
424
|
+
# ────────────────────────────────────────────────────────────────
|
|
425
|
+
# END OPEN-DESIGN DISPATCHER
|
|
426
|
+
# ────────────────────────────────────────────────────────────────
|
|
427
|
+
|
|
144
428
|
# Handle no tool specified
|
|
145
429
|
if [[ -z "$TOOL" ]]; then
|
|
146
430
|
if [[ ! -t 0 ]] || [[ ! -t 1 ]]; then
|
|
@@ -810,6 +1094,20 @@ unset -f _pmcp_resolve_script_dir
|
|
|
810
1094
|
|
|
811
1095
|
if [[ "$TOOL" == "opencode" ]] && command -v jq &>/dev/null && [[ -f "$AI_SANDBOX_CONFIG" ]] && declare -f pmcp::sanitize_name >/dev/null; then
|
|
812
1096
|
PLAYWRIGHT_HOST_CHROME=$(jq -r '.mcp.chromePath // empty' "$AI_SANDBOX_CONFIG" 2>/dev/null)
|
|
1097
|
+
if [[ -n "$PLAYWRIGHT_HOST_CHROME" ]] && [[ -f "$PLAYWRIGHT_HOST_CHROME" ]]; then
|
|
1098
|
+
# Ask user whether to open host Chrome browser
|
|
1099
|
+
if [[ -t 0 ]]; then
|
|
1100
|
+
echo ""
|
|
1101
|
+
echo "🌐 Host Chrome browser is configured: $PLAYWRIGHT_HOST_CHROME"
|
|
1102
|
+
printf " Open browser for AI agent? [y/N] "
|
|
1103
|
+
read -r -t 10 OPEN_CHROME_ANSWER || OPEN_CHROME_ANSWER=""
|
|
1104
|
+
echo ""
|
|
1105
|
+
if [[ ! "$OPEN_CHROME_ANSWER" =~ ^[Yy]$ ]]; then
|
|
1106
|
+
echo " ⏭️ Skipping host Chrome (using container Chromium if available)"
|
|
1107
|
+
PLAYWRIGHT_HOST_CHROME=""
|
|
1108
|
+
fi
|
|
1109
|
+
fi
|
|
1110
|
+
fi
|
|
813
1111
|
if [[ -n "$PLAYWRIGHT_HOST_CHROME" ]] && [[ -f "$PLAYWRIGHT_HOST_CHROME" ]]; then
|
|
814
1112
|
HOST_CHROME_CDP=true
|
|
815
1113
|
echo "🌐 Host Chrome CDP mode: $PLAYWRIGHT_HOST_CHROME"
|
|
@@ -1000,6 +1298,19 @@ get_network_containers() {
|
|
|
1000
1298
|
docker network inspect "$network" --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null | xargs | tr ' ' ', '
|
|
1001
1299
|
}
|
|
1002
1300
|
|
|
1301
|
+
# Ensure shared Docker network exists for cross-container service discovery
|
|
1302
|
+
# (e.g., agent containers reaching the open-design daemon by name)
|
|
1303
|
+
ensure_network() {
|
|
1304
|
+
local net="${1:-ai-sandbox}"
|
|
1305
|
+
if ! docker network inspect "$net" >/dev/null 2>&1; then
|
|
1306
|
+
docker network create "$net" >/dev/null 2>&1 || {
|
|
1307
|
+
echo "⚠️ WARNING: failed to create Docker network '$net'" >&2
|
|
1308
|
+
return 1
|
|
1309
|
+
}
|
|
1310
|
+
fi
|
|
1311
|
+
return 0
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1003
1314
|
# Interactive network selection menu (multi-select)
|
|
1004
1315
|
show_network_menu() {
|
|
1005
1316
|
local compose_nets=()
|
|
@@ -2597,7 +2908,15 @@ fi
|
|
|
2597
2908
|
|
|
2598
2909
|
# Nano-brain targeted preflight + auto-repair wrapper
|
|
2599
2910
|
if [[ "$SHELL_MODE" == "true" ]] && [[ "$NANO_BRAIN_AUTO_REPAIR" == "true" ]] && [[ "${DOCKER_COMMAND[0]:-}" == "-c" ]]; then
|
|
2600
|
-
|
|
2911
|
+
# Separate hook from following command with newline so bash parses them
|
|
2912
|
+
# as distinct statements. Without this, the hook's trailing `export -f`
|
|
2913
|
+
# line gets joined with the next `echo` call, producing:
|
|
2914
|
+
# export -f nano_brain_shell_wrapper npx echo ''; echo '...'
|
|
2915
|
+
# which makes bash try to `export -f echo` (a builtin, not a function)
|
|
2916
|
+
# and `export -f ''` (empty name), emitting two harmless but ugly errors:
|
|
2917
|
+
# bash: line 68: export: echo: not a function
|
|
2918
|
+
# bash: line 68: export: : not a function
|
|
2919
|
+
DOCKER_COMMAND[1]="$NANO_BRAIN_SHELL_HOOK"$'\n'"${DOCKER_COMMAND[1]}"
|
|
2601
2920
|
fi
|
|
2602
2921
|
|
|
2603
2922
|
if [[ "$SHELL_MODE" != "true" ]] && is_nano_brain_command; then
|
|
@@ -2739,12 +3058,23 @@ DOCKER_ARGS+=($TOOL_CONFIG_MOUNTS)
|
|
|
2739
3058
|
DOCKER_ARGS+=($RG_COMPAT_MOUNT)
|
|
2740
3059
|
DOCKER_ARGS+=($GIT_MOUNTS)
|
|
2741
3060
|
DOCKER_ARGS+=($SSH_AGENT_ENV)
|
|
3061
|
+
# Default to ai-sandbox network for service discovery if user didn't specify.
|
|
3062
|
+
# Only add --network if creation succeeded; otherwise Docker uses its default bridge.
|
|
3063
|
+
if [[ -z "$NETWORK_OPTIONS" ]]; then
|
|
3064
|
+
if ensure_network "ai-sandbox" >/dev/null 2>&1; then
|
|
3065
|
+
DOCKER_ARGS+=(--network ai-sandbox)
|
|
3066
|
+
fi
|
|
3067
|
+
fi
|
|
2742
3068
|
DOCKER_ARGS+=($NETWORK_OPTIONS)
|
|
2743
3069
|
DOCKER_ARGS+=($DISPLAY_FLAGS)
|
|
2744
3070
|
DOCKER_ARGS+=($HOST_ACCESS_ARGS)
|
|
2745
3071
|
DOCKER_ARGS+=($PORT_MAPPINGS)
|
|
2746
3072
|
DOCKER_ARGS+=($OPENCODE_PASSWORD_ENV)
|
|
2747
3073
|
DOCKER_ARGS+=(-v "$HOME_DIR":/home/agent)
|
|
3074
|
+
# Auto-mount open-design data volume read-only so agents can read generated artifacts
|
|
3075
|
+
if docker volume inspect ai-open-design-data >/dev/null 2>&1; then
|
|
3076
|
+
DOCKER_ARGS+=(-v "ai-open-design-data:/workspace/.od:ro")
|
|
3077
|
+
fi
|
|
2748
3078
|
DOCKER_ARGS+=($SHARED_CACHE_MOUNTS)
|
|
2749
3079
|
DOCKER_ARGS+=($NANO_BRAIN_MOUNT)
|
|
2750
3080
|
DOCKER_ARGS+=(-w "$CURRENT_DIR")
|
|
@@ -2763,5 +3093,41 @@ DOCKER_ARGS+=($TERMINAL_SIZE)
|
|
|
2763
3093
|
DOCKER_ARGS+=("$IMAGE")
|
|
2764
3094
|
DOCKER_ARGS+=("${DOCKER_COMMAND[@]}")
|
|
2765
3095
|
|
|
3096
|
+
# Auto-start open-design daemon if image exists but container not running
|
|
3097
|
+
if docker image inspect ai-open-design:latest >/dev/null 2>&1; then
|
|
3098
|
+
if ! docker ps --format '{{.Names}}' | grep -q "^ai-open-design$"; then
|
|
3099
|
+
if [[ -t 0 && -t 1 ]]; then
|
|
3100
|
+
printf "🎨 Open Design daemon is not running. Start it? [Y/n] "
|
|
3101
|
+
read -r OD_ANSWER
|
|
3102
|
+
if [[ ! "$OD_ANSWER" =~ ^[Nn]$ ]]; then
|
|
3103
|
+
OD_ENV_FILE="$HOME/.ai-sandbox/env"
|
|
3104
|
+
OD_NETWORK="ai-sandbox"
|
|
3105
|
+
OD_VOLUME="ai-open-design-data"
|
|
3106
|
+
# Auto-init if token missing
|
|
3107
|
+
if ! grep -q "^OD_API_TOKEN=" "$OD_ENV_FILE" 2>/dev/null; then
|
|
3108
|
+
mkdir -p "$(dirname "$OD_ENV_FILE")"
|
|
3109
|
+
touch "$OD_ENV_FILE"
|
|
3110
|
+
chmod 600 "$OD_ENV_FILE"
|
|
3111
|
+
OD_TOKEN="$(openssl rand -hex 32)"
|
|
3112
|
+
echo "OD_API_TOKEN=$OD_TOKEN" >> "$OD_ENV_FILE"
|
|
3113
|
+
echo "OD_DAEMON_URL=http://ai-open-design:7456" >> "$OD_ENV_FILE"
|
|
3114
|
+
echo "✅ Generated OD_API_TOKEN"
|
|
3115
|
+
fi
|
|
3116
|
+
# Ensure network and volume
|
|
3117
|
+
docker network inspect "$OD_NETWORK" >/dev/null 2>&1 || docker network create "$OD_NETWORK" >/dev/null
|
|
3118
|
+
docker volume inspect "$OD_VOLUME" >/dev/null 2>&1 || docker volume create "$OD_VOLUME" >/dev/null
|
|
3119
|
+
# Remove stopped container if exists
|
|
3120
|
+
if docker ps -a --format '{{.Names}}' | grep -q "^ai-open-design$"; then
|
|
3121
|
+
docker rm ai-open-design >/dev/null 2>&1 || true
|
|
3122
|
+
fi
|
|
3123
|
+
docker run -d --name ai-open-design --network "$OD_NETWORK" \
|
|
3124
|
+
--restart unless-stopped -v "$OD_VOLUME:/app/.od" \
|
|
3125
|
+
--env-file "$OD_ENV_FILE" ai-open-design:latest >/dev/null
|
|
3126
|
+
echo "✅ Open Design daemon started (http://ai-open-design:7456)"
|
|
3127
|
+
fi
|
|
3128
|
+
fi
|
|
3129
|
+
fi
|
|
3130
|
+
fi
|
|
3131
|
+
|
|
2766
3132
|
# Execute docker run with proper argument handling
|
|
2767
3133
|
docker run "${DOCKER_ARGS[@]}"
|
package/bin/cli.js
CHANGED
|
@@ -140,6 +140,8 @@ function runRebuild() {
|
|
|
140
140
|
INSTALL_CHROME_DEVTOOLS_MCP: hasMcp('chrome-devtools') ? '1' : '0',
|
|
141
141
|
INSTALL_PLAYWRIGHT_HOST: useHostChrome ? '1' : '0',
|
|
142
142
|
INSTALL_RTK: '0',
|
|
143
|
+
INSTALL_PUP: '0',
|
|
144
|
+
INSTALL_OD_HELPERS: '1',
|
|
143
145
|
INSTALL_SPEC_KIT: '0',
|
|
144
146
|
INSTALL_UX_UI_PROMAX: '0',
|
|
145
147
|
INSTALL_OPENSPEC: '0',
|
|
@@ -1,3 +1,6 @@
|
|
|
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
|
|
1
4
|
|
|
2
5
|
FROM node:22-bookworm-slim
|
|
3
6
|
|
|
@@ -23,6 +26,30 @@ RUN npm install -g typescript typescript-language-server pyright vscode-langserv
|
|
|
23
26
|
RUN node --version && npm --version && pnpm --version && tsc --version
|
|
24
27
|
|
|
25
28
|
# Install additional tools (if selected)
|
|
29
|
+
RUN PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install specify-cli --pip-args="git+https://github.com/github/spec-kit.git" && \
|
|
30
|
+
chmod +x /usr/local/bin/specify && \
|
|
31
|
+
ln -sf /usr/local/bin/specify /usr/local/bin/specify-cli
|
|
32
|
+
RUN mkdir -p /usr/local/lib/uipro-cli && \
|
|
33
|
+
cd /usr/local/lib/uipro-cli && \
|
|
34
|
+
npm init -y && \
|
|
35
|
+
npm install uipro-cli && \
|
|
36
|
+
ln -sf /usr/local/lib/uipro-cli/node_modules/.bin/uipro /usr/local/bin/uipro && \
|
|
37
|
+
ln -sf /usr/local/bin/uipro /usr/local/bin/uipro-cli && \
|
|
38
|
+
chmod -R 755 /usr/local/lib/uipro-cli && \
|
|
39
|
+
chmod +x /usr/local/bin/uipro
|
|
40
|
+
RUN mkdir -p /usr/local/lib/openspec && \
|
|
41
|
+
cd /usr/local/lib/openspec && \
|
|
42
|
+
npm init -y && \
|
|
43
|
+
npm install @fission-ai/openspec && \
|
|
44
|
+
ln -sf /usr/local/lib/openspec/node_modules/.bin/openspec /usr/local/bin/openspec && \
|
|
45
|
+
chmod -R 755 /usr/local/lib/openspec && \
|
|
46
|
+
chmod +x /usr/local/bin/openspec
|
|
47
|
+
# Install RTK - token optimizer for AI coding agents (built from source)
|
|
48
|
+
COPY --from=rtk-builder /usr/local/cargo/bin/rtk /usr/local/bin/rtk
|
|
49
|
+
# Install RTK OpenCode skills (auto-discovered by OpenCode agents)
|
|
50
|
+
RUN mkdir -p /home/agent/.config/opencode/skills/rtk /home/agent/.config/opencode/skills/rtk-setup
|
|
51
|
+
COPY skills/rtk/SKILL.md /home/agent/.config/opencode/skills/rtk/SKILL.md
|
|
52
|
+
COPY skills/rtk-setup/SKILL.md /home/agent/.config/opencode/skills/rtk-setup/SKILL.md
|
|
26
53
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
27
54
|
libglib2.0-0 \
|
|
28
55
|
libnspr4 \
|
|
@@ -56,11 +83,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
|
56
83
|
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
|
|
57
84
|
RUN mkdir -p /opt/playwright-browsers && \
|
|
58
85
|
npm install -g @playwright/mcp@latest && \
|
|
59
|
-
|
|
86
|
+
npx playwright-core install --no-shell chromium && \
|
|
87
|
+
npx playwright-core install-deps chromium && \
|
|
88
|
+
chmod -R 777 /opt/playwright-browsers && \
|
|
89
|
+
ln -sf $(ls -d /opt/playwright-browsers/chromium-*/chrome-linux/chrome | sort -V | tail -1) /opt/chromium
|
|
60
90
|
ENV CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS=1
|
|
61
91
|
RUN npm install -g chrome-devtools-mcp@latest && \
|
|
62
92
|
touch /opt/.mcp-chrome-devtools-installed
|
|
63
|
-
RUN touch /opt/.mcp-playwright-installed
|
|
64
93
|
|
|
65
94
|
# Create workspace
|
|
66
95
|
WORKDIR /workspace
|
|
@@ -1,3 +1,6 @@
|
|
|
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
|
|
1
4
|
|
|
2
5
|
FROM node:22-bookworm-slim
|
|
3
6
|
|
|
@@ -23,6 +26,30 @@ RUN npm install -g typescript typescript-language-server pyright vscode-langserv
|
|
|
23
26
|
RUN node --version && npm --version && pnpm --version && tsc --version
|
|
24
27
|
|
|
25
28
|
# Install additional tools (if selected)
|
|
29
|
+
RUN PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install specify-cli --pip-args="git+https://github.com/github/spec-kit.git" && \
|
|
30
|
+
chmod +x /usr/local/bin/specify && \
|
|
31
|
+
ln -sf /usr/local/bin/specify /usr/local/bin/specify-cli
|
|
32
|
+
RUN mkdir -p /usr/local/lib/uipro-cli && \
|
|
33
|
+
cd /usr/local/lib/uipro-cli && \
|
|
34
|
+
npm init -y && \
|
|
35
|
+
npm install uipro-cli && \
|
|
36
|
+
ln -sf /usr/local/lib/uipro-cli/node_modules/.bin/uipro /usr/local/bin/uipro && \
|
|
37
|
+
ln -sf /usr/local/bin/uipro /usr/local/bin/uipro-cli && \
|
|
38
|
+
chmod -R 755 /usr/local/lib/uipro-cli && \
|
|
39
|
+
chmod +x /usr/local/bin/uipro
|
|
40
|
+
RUN mkdir -p /usr/local/lib/openspec && \
|
|
41
|
+
cd /usr/local/lib/openspec && \
|
|
42
|
+
npm init -y && \
|
|
43
|
+
npm install @fission-ai/openspec && \
|
|
44
|
+
ln -sf /usr/local/lib/openspec/node_modules/.bin/openspec /usr/local/bin/openspec && \
|
|
45
|
+
chmod -R 755 /usr/local/lib/openspec && \
|
|
46
|
+
chmod +x /usr/local/bin/openspec
|
|
47
|
+
# Install RTK - token optimizer for AI coding agents (built from source)
|
|
48
|
+
COPY --from=rtk-builder /usr/local/cargo/bin/rtk /usr/local/bin/rtk
|
|
49
|
+
# Install RTK OpenCode skills (auto-discovered by OpenCode agents)
|
|
50
|
+
RUN mkdir -p /home/agent/.config/opencode/skills/rtk /home/agent/.config/opencode/skills/rtk-setup
|
|
51
|
+
COPY skills/rtk/SKILL.md /home/agent/.config/opencode/skills/rtk/SKILL.md
|
|
52
|
+
COPY skills/rtk-setup/SKILL.md /home/agent/.config/opencode/skills/rtk-setup/SKILL.md
|
|
26
53
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
27
54
|
libglib2.0-0 \
|
|
28
55
|
libnspr4 \
|
|
@@ -56,11 +83,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
|
56
83
|
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
|
|
57
84
|
RUN mkdir -p /opt/playwright-browsers && \
|
|
58
85
|
npm install -g @playwright/mcp@latest && \
|
|
59
|
-
|
|
86
|
+
npx playwright-core install --no-shell chromium && \
|
|
87
|
+
npx playwright-core install-deps chromium && \
|
|
88
|
+
chmod -R 777 /opt/playwright-browsers && \
|
|
89
|
+
ln -sf $(ls -d /opt/playwright-browsers/chromium-*/chrome-linux/chrome | sort -V | tail -1) /opt/chromium
|
|
60
90
|
ENV CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS=1
|
|
61
91
|
RUN npm install -g chrome-devtools-mcp@latest && \
|
|
62
92
|
touch /opt/.mcp-chrome-devtools-installed
|
|
63
|
-
RUN touch /opt/.mcp-playwright-installed
|
|
64
93
|
|
|
65
94
|
# Create workspace
|
|
66
95
|
WORKDIR /workspace
|
|
@@ -77,13 +106,12 @@ RUN curl -fsSL https://opencode.ai/install | bash && \
|
|
|
77
106
|
mv /root/.opencode/bin/opencode /usr/local/bin/opencode && \
|
|
78
107
|
rm -rf /root/.opencode
|
|
79
108
|
|
|
80
|
-
# ===
|
|
109
|
+
# === claude ===
|
|
81
110
|
USER root
|
|
82
|
-
RUN
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
ln -s /usr/local/lib/codex/node_modules/.bin/codex /usr/local/bin/codex
|
|
111
|
+
RUN export HOME=/root && curl -fsSL https://claude.ai/install.sh | bash && \
|
|
112
|
+
mkdir -p /usr/local/share && \
|
|
113
|
+
mv /root/.local/share/claude /usr/local/share/claude && \
|
|
114
|
+
ln -sf /usr/local/share/claude/versions/$(ls /usr/local/share/claude/versions | head -1) /usr/local/bin/claude
|
|
87
115
|
USER agent
|
|
88
116
|
|
|
89
117
|
USER agent
|
package/lib/build-sandbox.sh
CHANGED
|
@@ -9,7 +9,7 @@ fi
|
|
|
9
9
|
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
|
-
cd "$PROJECT_DIR"
|
|
12
|
+
cd "$PROJECT_DIR" || exit 1
|
|
13
13
|
|
|
14
14
|
SANDBOX_DIR="dockerfiles/sandbox"
|
|
15
15
|
mkdir -p "$SANDBOX_DIR"
|
|
@@ -18,6 +18,8 @@ echo "🔄 Generating unified sandbox Dockerfile..."
|
|
|
18
18
|
echo " Tools: $TOOLS"
|
|
19
19
|
|
|
20
20
|
GENERATE_ONLY=1 INSTALL_RTK="${INSTALL_RTK:-0}" \
|
|
21
|
+
INSTALL_PUP="${INSTALL_PUP:-0}" \
|
|
22
|
+
INSTALL_OD_HELPERS="${INSTALL_OD_HELPERS:-1}" \
|
|
21
23
|
INSTALL_PLAYWRIGHT_MCP="${INSTALL_PLAYWRIGHT_MCP:-0}" \
|
|
22
24
|
INSTALL_CHROME_DEVTOOLS_MCP="${INSTALL_CHROME_DEVTOOLS_MCP:-0}" \
|
|
23
25
|
INSTALL_PLAYWRIGHT="${INSTALL_PLAYWRIGHT:-0}" \
|
|
@@ -51,6 +53,7 @@ BASE_PREAMBLE=$(echo "$BASE_CONTENT" | sed '/^USER agent$/,$d')
|
|
|
51
53
|
fi
|
|
52
54
|
|
|
53
55
|
echo "# === $tool ==="
|
|
56
|
+
# shellcheck source=/dev/null
|
|
54
57
|
SNIPPET_MODE=1 source "$INSTALL_SCRIPT"
|
|
55
58
|
dockerfile_snippet
|
|
56
59
|
echo ""
|
|
@@ -65,6 +68,10 @@ if [[ -d "dockerfiles/base/skills" ]]; then
|
|
|
65
68
|
cp -r "dockerfiles/base/skills" "$SANDBOX_DIR/"
|
|
66
69
|
fi
|
|
67
70
|
|
|
71
|
+
if [[ -d "dockerfiles/base/scripts" ]]; then
|
|
72
|
+
cp -r "dockerfiles/base/scripts" "$SANDBOX_DIR/"
|
|
73
|
+
fi
|
|
74
|
+
|
|
68
75
|
echo "✅ Dockerfile generated at $SANDBOX_DIR/Dockerfile"
|
|
69
76
|
|
|
70
77
|
echo "🔨 Building ai-sandbox:latest..."
|
package/lib/install-aider.sh
CHANGED
|
@@ -3,8 +3,9 @@ set -e
|
|
|
3
3
|
|
|
4
4
|
dockerfile_snippet() {
|
|
5
5
|
cat <<'SNIPPET'
|
|
6
|
+
USER root
|
|
7
|
+
RUN UV_TOOL_BIN_DIR=/usr/local/bin uv tool install aider-chat
|
|
6
8
|
USER agent
|
|
7
|
-
RUN python3 -m pip install --break-system-packages aider-install && aider-install
|
|
8
9
|
SNIPPET
|
|
9
10
|
}
|
|
10
11
|
|
|
@@ -24,9 +25,9 @@ mkdir -p "$HOME/.ai-sandbox/tools/$TOOL/home"
|
|
|
24
25
|
# Create Dockerfile (extends base image which has Python)
|
|
25
26
|
cat <<'EOF' > "dockerfiles/$TOOL/Dockerfile"
|
|
26
27
|
FROM ai-base:latest
|
|
28
|
+
USER root
|
|
29
|
+
RUN UV_TOOL_BIN_DIR=/usr/local/bin uv tool install aider-chat
|
|
27
30
|
USER agent
|
|
28
|
-
# Install aider via aider-install
|
|
29
|
-
RUN python3 -m pip install --break-system-packages aider-install && aider-install
|
|
30
31
|
ENTRYPOINT ["aider"]
|
|
31
32
|
EOF
|
|
32
33
|
|
package/lib/install-base.sh
CHANGED
|
@@ -67,6 +67,51 @@ COPY skills/rtk-setup/SKILL.md /home/agent/.config/opencode/skills/rtk-setup/SKI
|
|
|
67
67
|
fi
|
|
68
68
|
fi
|
|
69
69
|
|
|
70
|
+
if [[ "${INSTALL_PUP:-0}" -eq 1 ]]; then
|
|
71
|
+
echo "📦 Pup (Datadog CLI) will be installed in base image (multi-stage build)"
|
|
72
|
+
DOCKERFILE_BUILD_STAGES+='# Build Pup from source (multi-stage: only binary is kept, Rust toolchain discarded)
|
|
73
|
+
FROM rust:bookworm AS pup-builder
|
|
74
|
+
RUN cargo install --git https://github.com/DataDog/pup --locked
|
|
75
|
+
'
|
|
76
|
+
ADDITIONAL_TOOLS_INSTALL+='# Install Pup - Datadog CLI for AI agents (built from source)
|
|
77
|
+
COPY --from=pup-builder /usr/local/cargo/bin/pup /usr/local/bin/pup
|
|
78
|
+
'
|
|
79
|
+
# Copy Pup OpenCode skill into build context so it can be COPY'd into the image
|
|
80
|
+
SCRIPT_BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
81
|
+
PUP_SKILLS_SRC="${SCRIPT_BASE_DIR}/../skills"
|
|
82
|
+
if [[ -d "$PUP_SKILLS_SRC/dd-pup" ]]; then
|
|
83
|
+
mkdir -p "dockerfiles/base/skills/dd-pup"
|
|
84
|
+
cp "$PUP_SKILLS_SRC/dd-pup/SKILL.md" "dockerfiles/base/skills/dd-pup/SKILL.md"
|
|
85
|
+
ADDITIONAL_TOOLS_INSTALL+='# Install Pup OpenCode skill (auto-discovered by OpenCode agents)
|
|
86
|
+
RUN mkdir -p /home/agent/.config/opencode/skills/dd-pup
|
|
87
|
+
COPY skills/dd-pup/SKILL.md /home/agent/.config/opencode/skills/dd-pup/SKILL.md
|
|
88
|
+
'
|
|
89
|
+
echo " ✅ Pup OpenCode skill will be copied into container"
|
|
90
|
+
else
|
|
91
|
+
echo " ⚠️ Pup skill not found at $PUP_SKILLS_SRC/dd-pup — skipping skill installation"
|
|
92
|
+
fi
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
if [[ "${INSTALL_OD_HELPERS:-1}" -eq 1 ]]; then
|
|
96
|
+
echo "📦 open-design helper scripts (od-status, od-health) will be installed in base image"
|
|
97
|
+
# Copy helper scripts into build context so they can be COPY'd into the image
|
|
98
|
+
SCRIPT_BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
99
|
+
OD_HELPERS_SRC="${SCRIPT_BASE_DIR}/../scripts"
|
|
100
|
+
if [[ -f "$OD_HELPERS_SRC/od-status" && -f "$OD_HELPERS_SRC/od-health" ]]; then
|
|
101
|
+
mkdir -p "dockerfiles/base/scripts"
|
|
102
|
+
cp "$OD_HELPERS_SRC/od-status" "dockerfiles/base/scripts/od-status"
|
|
103
|
+
cp "$OD_HELPERS_SRC/od-health" "dockerfiles/base/scripts/od-health"
|
|
104
|
+
ADDITIONAL_TOOLS_INSTALL+='# Install open-design helper scripts (od-status, od-health) for agent containers
|
|
105
|
+
COPY scripts/od-status /usr/local/bin/od-status
|
|
106
|
+
COPY scripts/od-health /usr/local/bin/od-health
|
|
107
|
+
RUN chmod +x /usr/local/bin/od-status /usr/local/bin/od-health
|
|
108
|
+
'
|
|
109
|
+
echo " ✅ open-design helpers will be copied into container"
|
|
110
|
+
else
|
|
111
|
+
echo " ⚠️ open-design helpers not found at $OD_HELPERS_SRC — skipping"
|
|
112
|
+
fi
|
|
113
|
+
fi
|
|
114
|
+
|
|
70
115
|
if [[ "${INSTALL_PLAYWRIGHT:-0}" -eq 1 ]]; then
|
|
71
116
|
echo "📦 Playwright will be installed in base image"
|
|
72
117
|
ADDITIONAL_TOOLS_INSTALL+='# Install Playwright system dependencies
|
|
@@ -134,10 +179,7 @@ fi
|
|
|
134
179
|
|
|
135
180
|
# MCP Tools for AI agent browser automation
|
|
136
181
|
# Both tools share Playwright's Chromium (native ARM64/x86_64, avoids Puppeteer arch issues)
|
|
137
|
-
MCP_BROWSER_INSTALLED=false
|
|
138
|
-
|
|
139
182
|
if [[ "${INSTALL_CHROME_DEVTOOLS_MCP:-0}" -eq 1 ]] || [[ "${INSTALL_PLAYWRIGHT_MCP:-0}" -eq 1 ]]; then
|
|
140
|
-
MCP_BROWSER_INSTALLED=true
|
|
141
183
|
echo "📦 Installing shared Chromium browser for MCP tools"
|
|
142
184
|
ADDITIONAL_TOOLS_INSTALL+='RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
143
185
|
libglib2.0-0 \
|