@agent-foundry/replay-server 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.cursor/dev.mdc +941 -0
  2. package/.cursor/project.mdc +17 -2
  3. package/.env +30 -0
  4. package/Dockerfile +6 -0
  5. package/README.md +153 -12
  6. package/dist/cli/render.js +14 -4
  7. package/dist/cli/render.js.map +1 -1
  8. package/dist/renderer/PuppeteerRenderer.d.ts +12 -2
  9. package/dist/renderer/PuppeteerRenderer.d.ts.map +1 -1
  10. package/dist/renderer/PuppeteerRenderer.js +23 -16
  11. package/dist/renderer/PuppeteerRenderer.js.map +1 -1
  12. package/dist/server/index.d.ts +4 -0
  13. package/dist/server/index.d.ts.map +1 -1
  14. package/dist/server/index.js +200 -46
  15. package/dist/server/index.js.map +1 -1
  16. package/dist/services/BundleManager.d.ts +99 -0
  17. package/dist/services/BundleManager.d.ts.map +1 -0
  18. package/dist/services/BundleManager.js +410 -0
  19. package/dist/services/BundleManager.js.map +1 -0
  20. package/dist/services/OSSClient.d.ts +51 -0
  21. package/dist/services/OSSClient.d.ts.map +1 -0
  22. package/dist/services/OSSClient.js +207 -0
  23. package/dist/services/OSSClient.js.map +1 -0
  24. package/dist/services/index.d.ts +7 -0
  25. package/dist/services/index.d.ts.map +1 -0
  26. package/dist/services/index.js +7 -0
  27. package/dist/services/index.js.map +1 -0
  28. package/dist/services/types.d.ts +73 -0
  29. package/dist/services/types.d.ts.map +1 -0
  30. package/dist/services/types.js +5 -0
  31. package/dist/services/types.js.map +1 -0
  32. package/docker-compose.local.yml +8 -0
  33. package/env.example +30 -0
  34. package/package.json +7 -3
  35. package/restart.sh +5 -0
  36. package/samples/jump_arena_0_ja-mks5um2x-nksbmz.json +1907 -0
  37. package/scripts/render-pipeline.sh +657 -0
  38. package/scripts/test-bundle-preload.sh +20 -0
  39. package/scripts/test-service-sts.sh +176 -0
  40. package/src/cli/render.ts +18 -7
  41. package/src/renderer/PuppeteerRenderer.ts +41 -21
  42. package/src/server/index.ts +249 -68
  43. package/src/services/BundleManager.ts +503 -0
  44. package/src/services/OSSClient.ts +286 -0
  45. package/src/services/index.ts +7 -0
  46. package/src/services/types.ts +78 -0
@@ -0,0 +1,657 @@
1
+ #!/bin/bash
2
+ #
3
+ # Replay Render Pipeline
4
+ #
5
+ # Full end-to-end rendering workflow:
6
+ # 1. Check/start server
7
+ # 2. Verify/build bundle
8
+ # 3. Preload bundle if needed
9
+ # 4. Submit render job via HTTP API
10
+ # 5. Poll for completion
11
+ # 6. Auto-download video
12
+ #
13
+ # Usage:
14
+ # ./render-pipeline.sh -m samples/replay.json
15
+ # ./render-pipeline.sh -m samples/replay.json -o output/video.mp4 --start-server
16
+ #
17
+
18
+ set -euo pipefail
19
+
20
+ # ============================================================================
21
+ # Configuration
22
+ # ============================================================================
23
+
24
+ SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
25
+ PACKAGE_DIR=$(cd "$SCRIPT_DIR/.." && pwd)
26
+ REPO_ROOT=$(cd "$PACKAGE_DIR/../.." && pwd)
27
+
28
+ # Default values
29
+ DEFAULT_SERVER_URL="http://localhost:3001"
30
+ DEFAULT_WIDTH=720
31
+ DEFAULT_HEIGHT=1080
32
+ DEFAULT_FPS=16
33
+ DEFAULT_OUTPUT_DIR="$PACKAGE_DIR/output"
34
+
35
+ # Variables
36
+ MANIFEST_FILE=""
37
+ OUTPUT_PATH=""
38
+ WIDTH=$DEFAULT_WIDTH
39
+ HEIGHT=$DEFAULT_HEIGHT
40
+ FPS=$DEFAULT_FPS
41
+ SERVER_URL=$DEFAULT_SERVER_URL
42
+ START_SERVER=false
43
+ BUILD_BUNDLE=false
44
+ VERBOSE=false
45
+
46
+ # ============================================================================
47
+ # Colors and Output
48
+ # ============================================================================
49
+
50
+ RED='\033[0;31m'
51
+ GREEN='\033[0;32m'
52
+ YELLOW='\033[1;33m'
53
+ BLUE='\033[0;34m'
54
+ CYAN='\033[0;36m'
55
+ NC='\033[0m' # No Color
56
+ BOLD='\033[1m'
57
+
58
+ log_info() {
59
+ echo -e "${CYAN}ℹ${NC} $1"
60
+ }
61
+
62
+ log_success() {
63
+ echo -e "${GREEN}✓${NC} $1"
64
+ }
65
+
66
+ log_warning() {
67
+ echo -e "${YELLOW}⚠${NC} $1"
68
+ }
69
+
70
+ log_error() {
71
+ echo -e "${RED}✗${NC} $1" >&2
72
+ }
73
+
74
+ log_step() {
75
+ echo -e "${BOLD}${BLUE}▶${NC} $1"
76
+ }
77
+
78
+ log_verbose() {
79
+ if [ "$VERBOSE" = true ]; then
80
+ echo -e "${CYAN}[DEBUG]${NC} $1"
81
+ fi
82
+ }
83
+
84
+ # ============================================================================
85
+ # Helper Functions
86
+ # ============================================================================
87
+
88
+ check_dependencies() {
89
+ local missing_deps=()
90
+
91
+ if ! command -v curl &> /dev/null; then
92
+ missing_deps+=("curl")
93
+ fi
94
+
95
+ if ! command -v jq &> /dev/null; then
96
+ missing_deps+=("jq")
97
+ fi
98
+
99
+ if [ ${#missing_deps[@]} -gt 0 ]; then
100
+ log_error "Missing required dependencies: ${missing_deps[*]}"
101
+ echo ""
102
+ echo "Install instructions:"
103
+ for dep in "${missing_deps[@]}"; do
104
+ case $dep in
105
+ curl)
106
+ echo " macOS: brew install curl"
107
+ echo " Linux: sudo apt install curl"
108
+ ;;
109
+ jq)
110
+ echo " macOS: brew install jq"
111
+ echo " Linux: sudo apt install jq"
112
+ ;;
113
+ esac
114
+ done
115
+ exit 1
116
+ fi
117
+ }
118
+
119
+ load_env() {
120
+ local env_file="$PACKAGE_DIR/.env"
121
+ if [ -f "$env_file" ]; then
122
+ log_verbose "Loading environment from $env_file"
123
+ # Export variables from .env (skip comments and empty lines)
124
+ set -a
125
+ while IFS= read -r line || [ -n "$line" ]; do
126
+ # Skip comments and empty lines
127
+ if [[ ! "$line" =~ ^[[:space:]]*# ]] && [[ -n "$line" ]]; then
128
+ eval "export $line" 2>/dev/null || true
129
+ fi
130
+ done < "$env_file"
131
+ set +a
132
+ fi
133
+ }
134
+
135
+ print_help() {
136
+ cat << EOF
137
+ 🎬 Replay Render Pipeline
138
+
139
+ Full end-to-end rendering workflow for AgentFoundry replays.
140
+
141
+ Usage:
142
+ ./render-pipeline.sh [options]
143
+
144
+ Required:
145
+ -m, --manifest <path> Path to replay manifest JSON file
146
+
147
+ Optional:
148
+ -o, --output <path> Output video path (default: ./output/<gameId>.mp4)
149
+ --width <px> Video width (default: 1080)
150
+ --height <px> Video height (default: 1920)
151
+ --fps <n> Frames per second (default: 30)
152
+ --server-url <url> Replay server URL (default: http://localhost:3001)
153
+ --start-server Auto-start server if not running
154
+ --build-bundle Auto-build bundle if missing
155
+ -v, --verbose Verbose output
156
+ -h, --help Show this help message
157
+
158
+ Examples:
159
+ # Basic usage
160
+ ./render-pipeline.sh -m samples/jump_arena_0_ja-mks5um2x-nksbmz.json
161
+
162
+ # With custom output and settings
163
+ ./render-pipeline.sh -m samples/replay.json -o videos/my-replay.mp4 --width 720 --height 1280
164
+
165
+ # Auto-start server if needed
166
+ ./render-pipeline.sh -m samples/replay.json --start-server
167
+
168
+ # Build bundle if missing and start server
169
+ ./render-pipeline.sh -m samples/replay.json --build-bundle --start-server
170
+
171
+ EOF
172
+ }
173
+
174
+ # ============================================================================
175
+ # Argument Parsing
176
+ # ============================================================================
177
+
178
+ parse_args() {
179
+ while [[ $# -gt 0 ]]; do
180
+ case $1 in
181
+ -m|--manifest)
182
+ MANIFEST_FILE="$2"
183
+ shift 2
184
+ ;;
185
+ -o|--output)
186
+ OUTPUT_PATH="$2"
187
+ shift 2
188
+ ;;
189
+ --width)
190
+ WIDTH="$2"
191
+ shift 2
192
+ ;;
193
+ --height)
194
+ HEIGHT="$2"
195
+ shift 2
196
+ ;;
197
+ --fps)
198
+ FPS="$2"
199
+ shift 2
200
+ ;;
201
+ --server-url)
202
+ SERVER_URL="$2"
203
+ shift 2
204
+ ;;
205
+ --start-server)
206
+ START_SERVER=true
207
+ shift
208
+ ;;
209
+ --build-bundle)
210
+ BUILD_BUNDLE=true
211
+ shift
212
+ ;;
213
+ -v|--verbose)
214
+ VERBOSE=true
215
+ shift
216
+ ;;
217
+ -h|--help)
218
+ print_help
219
+ exit 0
220
+ ;;
221
+ *)
222
+ log_error "Unknown option: $1"
223
+ echo ""
224
+ print_help
225
+ exit 1
226
+ ;;
227
+ esac
228
+ done
229
+
230
+ # Validate required arguments
231
+ if [ -z "$MANIFEST_FILE" ]; then
232
+ log_error "Missing required argument: --manifest"
233
+ echo ""
234
+ print_help
235
+ exit 1
236
+ fi
237
+
238
+ # Resolve manifest path
239
+ if [ ! -f "$MANIFEST_FILE" ]; then
240
+ log_error "Manifest file not found: $MANIFEST_FILE"
241
+ exit 1
242
+ fi
243
+ MANIFEST_FILE=$(cd "$(dirname "$MANIFEST_FILE")" && pwd)/$(basename "$MANIFEST_FILE")
244
+ }
245
+
246
+ # ============================================================================
247
+ # Server Management
248
+ # ============================================================================
249
+
250
+ check_server() {
251
+ log_step "Checking replay server..."
252
+
253
+ local health_check
254
+ health_check=$(curl -s -o /dev/null -w "%{http_code}" "$SERVER_URL/health" || echo "000")
255
+
256
+ if [ "$health_check" = "200" ]; then
257
+ log_success "Server is running at $SERVER_URL"
258
+ return 0
259
+ else
260
+ log_warning "Server is not responding at $SERVER_URL"
261
+ return 1
262
+ fi
263
+ }
264
+
265
+ start_server() {
266
+ log_step "Starting replay server..."
267
+
268
+ cd "$PACKAGE_DIR"
269
+
270
+ # Check if already running on the port
271
+ local port=$(echo "$SERVER_URL" | grep -oP '(?<=:)\d+$' || echo "3001")
272
+ if lsof -Pi :$port -sTCP:LISTEN -t >/dev/null 2>&1; then
273
+ log_warning "Port $port is already in use"
274
+ return 1
275
+ fi
276
+
277
+ # Start server in background
278
+ log_info "Starting server with: pnpm run dev"
279
+ nohup pnpm run dev > /tmp/replay-server.log 2>&1 &
280
+ local server_pid=$!
281
+
282
+ log_info "Server PID: $server_pid"
283
+
284
+ # Wait for server to be ready (max 30 seconds)
285
+ local max_wait=30
286
+ local wait_time=0
287
+ while [ $wait_time -lt $max_wait ]; do
288
+ sleep 2
289
+ wait_time=$((wait_time + 2))
290
+
291
+ if curl -s -o /dev/null -w "%{http_code}" "$SERVER_URL/health" | grep -q "200"; then
292
+ log_success "Server is ready (took ${wait_time}s)"
293
+ return 0
294
+ fi
295
+
296
+ echo -n "."
297
+ done
298
+
299
+ echo ""
300
+ log_error "Server failed to start within ${max_wait}s"
301
+ log_info "Check logs: tail -f /tmp/replay-server.log"
302
+ return 1
303
+ }
304
+
305
+ # ============================================================================
306
+ # Bundle Management
307
+ # ============================================================================
308
+
309
+ parse_manifest() {
310
+ log_step "Parsing manifest..."
311
+
312
+ BUNDLE_ID=$(jq -r '.bundleId // empty' "$MANIFEST_FILE")
313
+ BUNDLE_URL=$(jq -r '.bundleUrl // empty' "$MANIFEST_FILE")
314
+ GAME_ID=$(jq -r '.gameId' "$MANIFEST_FILE")
315
+
316
+ if [ -z "$BUNDLE_ID" ]; then
317
+ log_error "Manifest does not contain bundleId"
318
+ exit 1
319
+ fi
320
+
321
+ log_info "Bundle ID: $BUNDLE_ID"
322
+ log_info "Game ID: $GAME_ID"
323
+
324
+ if [ -n "$BUNDLE_URL" ]; then
325
+ log_info "Bundle URL: $BUNDLE_URL"
326
+ fi
327
+
328
+ # Set default output path if not specified
329
+ if [ -z "$OUTPUT_PATH" ]; then
330
+ OUTPUT_PATH="$DEFAULT_OUTPUT_DIR/${GAME_ID}.mp4"
331
+ fi
332
+ }
333
+
334
+ check_bundle() {
335
+ log_step "Checking bundle availability..."
336
+
337
+ # Use GET /api/bundles (list) and filter to avoid route conflict with static middleware
338
+ local response
339
+ response=$(curl -s "$SERVER_URL/api/bundles")
340
+
341
+ log_verbose "Bundles list response: $response"
342
+
343
+ # Extract bundle info from the list
344
+ local bundle_info=$(echo "$response" | jq --arg id "$BUNDLE_ID" '.bundles[]? | select(.bundleId == $id)')
345
+
346
+ if [ -z "$bundle_info" ]; then
347
+ log_warning "Bundle '$BUNDLE_ID' not found on server"
348
+ return 1
349
+ fi
350
+
351
+ local status=$(echo "$bundle_info" | jq -r '.status // "ready"')
352
+
353
+ log_verbose "Bundle '$BUNDLE_ID' status: $status"
354
+
355
+ if [ "$status" = "ready" ]; then
356
+ log_success "Bundle '$BUNDLE_ID' is ready"
357
+ return 0
358
+ elif [ "$status" = "downloading" ]; then
359
+ log_info "Bundle '$BUNDLE_ID' is currently downloading..."
360
+ wait_for_bundle
361
+ return 0
362
+ else
363
+ log_warning "Bundle '$BUNDLE_ID' status: $status"
364
+ return 1
365
+ fi
366
+ }
367
+
368
+ preload_bundle() {
369
+ if [ -z "$BUNDLE_URL" ]; then
370
+ log_error "No bundleUrl provided in manifest"
371
+
372
+ if [ "$BUILD_BUNDLE" = true ]; then
373
+ build_bundle
374
+ return 0
375
+ else
376
+ log_error "Cannot preload bundle without bundleUrl"
377
+ log_info "Use --build-bundle flag to build locally"
378
+ exit 1
379
+ fi
380
+ fi
381
+
382
+ log_step "Preloading bundle from OSS..."
383
+
384
+ local payload=$(jq -n \
385
+ --arg bundleId "$BUNDLE_ID" \
386
+ --arg bundleUrl "$BUNDLE_URL" \
387
+ '{bundleId: $bundleId, bundleUrl: $bundleUrl}')
388
+
389
+ local response
390
+ response=$(curl -s -X POST "$SERVER_URL/api/bundles/preload" \
391
+ -H "Content-Type: application/json" \
392
+ -d "$payload")
393
+
394
+ log_verbose "Preload response: $response"
395
+
396
+ local status=$(echo "$response" | jq -r '.status')
397
+
398
+ if [ "$status" = "downloading" ] || [ "$status" = "ready" ]; then
399
+ log_success "Bundle preload initiated"
400
+ wait_for_bundle
401
+ else
402
+ log_error "Failed to preload bundle"
403
+ echo "$response" | jq .
404
+ exit 1
405
+ fi
406
+ }
407
+
408
+ build_bundle() {
409
+ log_step "Building bundle locally..."
410
+
411
+ local build_script="$SCRIPT_DIR/build-bundle.sh"
412
+
413
+ if [ ! -f "$build_script" ]; then
414
+ log_error "Build script not found: $build_script"
415
+ exit 1
416
+ fi
417
+
418
+ log_info "Running: $build_script $BUNDLE_ID"
419
+
420
+ if bash "$build_script" "$BUNDLE_ID"; then
421
+ log_success "Bundle built successfully"
422
+ else
423
+ log_error "Failed to build bundle"
424
+ exit 1
425
+ fi
426
+ }
427
+
428
+ wait_for_bundle() {
429
+ log_info "Waiting for bundle to be ready..."
430
+
431
+ local max_wait=300 # 5 minutes
432
+ local wait_time=0
433
+ local last_progress=""
434
+
435
+ while [ $wait_time -lt $max_wait ]; do
436
+ sleep 2
437
+ wait_time=$((wait_time + 2))
438
+
439
+ # Use GET /api/bundles (list) and filter for consistency
440
+ local response
441
+ response=$(curl -s "$SERVER_URL/api/bundles")
442
+ local bundle_info=$(echo "$response" | jq --arg id "$BUNDLE_ID" '.bundles[]? | select(.bundleId == $id)')
443
+
444
+ if [ -z "$bundle_info" ]; then
445
+ echo ""
446
+ log_error "Bundle disappeared from server"
447
+ exit 1
448
+ fi
449
+
450
+ local status=$(echo "$bundle_info" | jq -r '.status')
451
+ local progress=$(echo "$bundle_info" | jq -r '.progress // 0')
452
+
453
+ if [ "$status" = "ready" ]; then
454
+ echo ""
455
+ log_success "Bundle is ready"
456
+ return 0
457
+ elif [ "$status" = "error" ]; then
458
+ echo ""
459
+ local error=$(echo "$bundle_info" | jq -r '.error')
460
+ log_error "Bundle download failed: $error"
461
+ exit 1
462
+ fi
463
+
464
+ # Show progress
465
+ if [ "$progress" != "$last_progress" ]; then
466
+ printf "\r Progress: %d%%" "$progress"
467
+ last_progress="$progress"
468
+ else
469
+ printf "."
470
+ fi
471
+ done
472
+
473
+ echo ""
474
+ log_error "Bundle download timeout (${max_wait}s)"
475
+ exit 1
476
+ }
477
+
478
+ # ============================================================================
479
+ # Render Job Management
480
+ # ============================================================================
481
+
482
+ submit_render_job() {
483
+ log_step "Submitting render job..."
484
+
485
+ # Read manifest and add config
486
+ local manifest_content
487
+ manifest_content=$(cat "$MANIFEST_FILE")
488
+
489
+ local config=$(jq -n \
490
+ --arg width "$WIDTH" \
491
+ --arg height "$HEIGHT" \
492
+ --arg fps "$FPS" \
493
+ '{width: ($width | tonumber), height: ($height | tonumber), fps: ($fps | tonumber)}')
494
+
495
+ local payload=$(jq -n \
496
+ --argjson manifest "$manifest_content" \
497
+ --argjson config "$config" \
498
+ '{manifest: $manifest, config: $config}')
499
+
500
+ log_verbose "Render payload: $payload"
501
+
502
+ local response
503
+ response=$(curl -s -X POST "$SERVER_URL/render" \
504
+ -H "Content-Type: application/json" \
505
+ -d "$payload")
506
+
507
+ log_verbose "Render response: $response"
508
+
509
+ JOB_ID=$(echo "$response" | jq -r '.jobId')
510
+
511
+ if [ -z "$JOB_ID" ] || [ "$JOB_ID" = "null" ]; then
512
+ log_error "Failed to submit render job"
513
+ echo "$response" | jq .
514
+ exit 1
515
+ fi
516
+
517
+ log_success "Render job submitted"
518
+ log_info "Job ID: $JOB_ID"
519
+ log_info "Resolution: ${WIDTH}x${HEIGHT} @ ${FPS}fps"
520
+ }
521
+
522
+ poll_job_status() {
523
+ log_step "Monitoring render progress..."
524
+
525
+ local last_message=""
526
+ local last_percent=-1
527
+
528
+ while true; do
529
+ sleep 2
530
+
531
+ local response
532
+ response=$(curl -s "$SERVER_URL/status/$JOB_ID")
533
+
534
+ log_verbose "Status response: $response"
535
+
536
+ local status=$(echo "$response" | jq -r '.status')
537
+ local progress=$(echo "$response" | jq -r '.progress // {}')
538
+ local current=$(echo "$progress" | jq -r '.current // 0')
539
+ local total=$(echo "$progress" | jq -r '.total // 1')
540
+ local message=$(echo "$progress" | jq -r '.message // ""')
541
+
542
+ # Calculate percentage
543
+ local percent=0
544
+ if [ "$total" -gt 0 ]; then
545
+ percent=$((current * 100 / total))
546
+ fi
547
+
548
+ # Show progress
549
+ if [ "$percent" != "$last_percent" ] || [ "$message" != "$last_message" ]; then
550
+ local bar_length=40
551
+ local filled=$((percent * bar_length / 100))
552
+ local empty=$((bar_length - filled))
553
+ local bar=$(printf '█%.0s' $(seq 1 $filled))$(printf '░%.0s' $(seq 1 $empty))
554
+
555
+ printf "\r [%s] %3d%% - %s" "$bar" "$percent" "$message"
556
+ last_percent=$percent
557
+ last_message="$message"
558
+ fi
559
+
560
+ # Check status
561
+ case $status in
562
+ completed)
563
+ echo ""
564
+ log_success "Render completed"
565
+ return 0
566
+ ;;
567
+ failed)
568
+ echo ""
569
+ local error=$(echo "$response" | jq -r '.error')
570
+ log_error "Render failed: $error"
571
+ exit 1
572
+ ;;
573
+ pending|processing)
574
+ # Continue polling
575
+ ;;
576
+ *)
577
+ echo ""
578
+ log_warning "Unknown status: $status"
579
+ ;;
580
+ esac
581
+ done
582
+ }
583
+
584
+ download_video() {
585
+ log_step "Downloading video..."
586
+
587
+ # Ensure output directory exists
588
+ local output_dir=$(dirname "$OUTPUT_PATH")
589
+ mkdir -p "$output_dir"
590
+
591
+ # Download video
592
+ if curl -s -o "$OUTPUT_PATH" "$SERVER_URL/download/$JOB_ID"; then
593
+ local file_size=$(ls -lh "$OUTPUT_PATH" | awk '{print $5}')
594
+ log_success "Video downloaded: $OUTPUT_PATH"
595
+ log_info "File size: $file_size"
596
+ else
597
+ log_error "Failed to download video"
598
+ exit 1
599
+ fi
600
+ }
601
+
602
+ # ============================================================================
603
+ # Main Pipeline
604
+ # ============================================================================
605
+
606
+ main() {
607
+ echo ""
608
+ echo "🎬 Replay Render Pipeline"
609
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
610
+ echo ""
611
+
612
+ # Check dependencies
613
+ check_dependencies
614
+
615
+ # Load environment
616
+ load_env
617
+
618
+ # Parse arguments
619
+ parse_args "$@"
620
+
621
+ # Check server
622
+ if ! check_server; then
623
+ if [ "$START_SERVER" = true ]; then
624
+ start_server || exit 1
625
+ else
626
+ log_error "Server is not running. Use --start-server to start it automatically"
627
+ exit 1
628
+ fi
629
+ fi
630
+
631
+ # Parse manifest
632
+ parse_manifest
633
+
634
+ # Check bundle
635
+ if ! check_bundle; then
636
+ preload_bundle
637
+ fi
638
+
639
+ # Submit render job
640
+ submit_render_job
641
+
642
+ # Poll status
643
+ poll_job_status
644
+
645
+ # Download video
646
+ download_video
647
+
648
+ echo ""
649
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
650
+ log_success "${BOLD}Pipeline completed successfully!${NC}"
651
+ echo ""
652
+ log_info "Output: $OUTPUT_PATH"
653
+ echo ""
654
+ }
655
+
656
+ # Run main function with all arguments
657
+ main "$@"
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+
3
+ SERVER_URL="http://localhost:3001"
4
+ BUNDLE_ID="game-jump-arena"
5
+ BUNDLE_URL="https://agent-foundry.oss-cn-beijing.aliyuncs.com/studio-bundles/92922750-9ea6-4fc7-aeef-a13b8cc9eae5/game-jump-arena/2026.01.24-104533/bundle-1769251533018.tar.gz"
6
+
7
+ set -x
8
+
9
+ payload=$(jq -n \
10
+ --arg bundleId "$BUNDLE_ID" \
11
+ --arg bundleUrl "$BUNDLE_URL" \
12
+ '{bundleId: $bundleId, bundleUrl: $bundleUrl}')
13
+
14
+ curl -s -X POST "$SERVER_URL/api/bundles/preload" \
15
+ -H "Content-Type: application/json" \
16
+ -d "$payload"
17
+
18
+
19
+ sleep 5
20
+ curl -s "http://$SERVER_URL/api/bundles"