@agent-foundry/replay-server 1.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/.cursor/project.mdc +694 -0
- package/.dockerignore +11 -0
- package/Dockerfile +156 -0
- package/README.md +628 -0
- package/bundles/.gitkeep +2 -0
- package/dist/cli/render.d.ts +10 -0
- package/dist/cli/render.d.ts.map +1 -0
- package/dist/cli/render.js +206 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/renderer/PuppeteerRenderer.d.ts +72 -0
- package/dist/renderer/PuppeteerRenderer.d.ts.map +1 -0
- package/dist/renderer/PuppeteerRenderer.js +392 -0
- package/dist/renderer/PuppeteerRenderer.js.map +1 -0
- package/dist/renderer/index.d.ts +2 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +2 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/server/index.d.ts +14 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +348 -0
- package/dist/server/index.js.map +1 -0
- package/docker-compose.local.yml +29 -0
- package/package.json +35 -0
- package/samples/life-replay-lr-mkcdfzc2-u8cqbs.json +1770 -0
- package/scripts/build-bundle.sh +52 -0
- package/scripts/deploy-aliyun.sh +81 -0
- package/src/cli/render.ts +243 -0
- package/src/index.ts +2 -0
- package/src/renderer/PuppeteerRenderer.ts +512 -0
- package/src/renderer/index.ts +1 -0
- package/src/server/index.ts +407 -0
- package/tsconfig.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
# AgentFoundry Replay Renderer
|
|
2
|
+
|
|
3
|
+
Video rendering service for AgentFoundry mini-game replays using Puppeteer and FFmpeg.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
This renderer uses a **Puppeteer-based approach** where:
|
|
8
|
+
|
|
9
|
+
1. The actual game is loaded in a headless browser
|
|
10
|
+
2. A replay manifest is injected into the game
|
|
11
|
+
3. The game enters "replay mode" and plays back the events
|
|
12
|
+
4. Screenshots are captured at each frame
|
|
13
|
+
5. FFmpeg combines screenshots into video
|
|
14
|
+
|
|
15
|
+
This approach ensures the video looks **exactly like the gameplay** with zero code duplication.
|
|
16
|
+
|
|
17
|
+
### Static Bundle Hosting
|
|
18
|
+
|
|
19
|
+
The replay-server can host game bundles internally, eliminating the need for a separate game server:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ replay-server │
|
|
24
|
+
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ │
|
|
25
|
+
│ │ Bundle Host │ │ Puppeteer │ │ HTTP API │ │
|
|
26
|
+
│ │ /bundles/* │◄──│ Renderer │◄──│ POST /render │ │
|
|
27
|
+
│ └─────────────┘ └──────────────┘ └─────────────────┘ │
|
|
28
|
+
│ │ ▲ │
|
|
29
|
+
│ ▼ │ │
|
|
30
|
+
│ bundles/game-life-restart/ manifest.json │
|
|
31
|
+
└──────────────────────────────────────────────────────────────┘
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
With `bundleId` in the manifest, the server:
|
|
35
|
+
1. Serves the game from `/bundles/<bundleId>/`
|
|
36
|
+
2. Puppeteer loads the game from the internal static server
|
|
37
|
+
3. No external game server required
|
|
38
|
+
|
|
39
|
+
## Prerequisites
|
|
40
|
+
|
|
41
|
+
- Node.js 18+
|
|
42
|
+
- pnpm 9.1.0+ (this is a pnpm monorepo)
|
|
43
|
+
- FFmpeg installed and in PATH
|
|
44
|
+
- **Option A**: Game running at `http://localhost:5173` (development mode)
|
|
45
|
+
- **Option B**: Game bundle in `bundles/` directory (production mode)
|
|
46
|
+
|
|
47
|
+
### Installing pnpm
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install -g pnpm@9.1.0
|
|
51
|
+
# or
|
|
52
|
+
corepack enable
|
|
53
|
+
corepack prepare pnpm@9.1.0 --activate
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Installing FFmpeg
|
|
57
|
+
|
|
58
|
+
**macOS:**
|
|
59
|
+
```bash
|
|
60
|
+
brew install ffmpeg
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Windows:**
|
|
64
|
+
```bash
|
|
65
|
+
choco install ffmpeg
|
|
66
|
+
# or download from https://ffmpeg.org/download.html
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Linux:**
|
|
70
|
+
```bash
|
|
71
|
+
sudo apt install ffmpeg
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
### Setup
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# From project root - install all dependencies
|
|
80
|
+
pnpm install
|
|
81
|
+
|
|
82
|
+
# Or install only this package and its dependencies
|
|
83
|
+
cd packages/replay-server
|
|
84
|
+
pnpm install
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### CLI Tool
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# From project root (recommended)
|
|
91
|
+
pnpm --filter replay-server render -- --manifest ./packages/replay-server/samples/replay.json --output ./packages/replay-server/output/output.mp4
|
|
92
|
+
|
|
93
|
+
# From package directory
|
|
94
|
+
cd packages/replay-server
|
|
95
|
+
pnpm run render -- --manifest ./samples/replay.json --output ./output/output.mp4
|
|
96
|
+
|
|
97
|
+
# Alternative: Use tsx directly
|
|
98
|
+
cd packages/replay-server
|
|
99
|
+
pnpm exec tsx src/cli/render.ts --manifest ./samples/replay.json --output ./output/output.mp4
|
|
100
|
+
|
|
101
|
+
# Render with custom settings
|
|
102
|
+
pnpm --filter replay-server render -- \
|
|
103
|
+
--manifest ./replay.json \
|
|
104
|
+
--output ./output.mp4 \
|
|
105
|
+
--game-url http://localhost:5173 \
|
|
106
|
+
--width 1080 \
|
|
107
|
+
--height 1920 \
|
|
108
|
+
--fps 30 \
|
|
109
|
+
--seconds-per-age 2
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### HTTP Server
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# From project root (recommended)
|
|
116
|
+
pnpm dev:replay-server
|
|
117
|
+
|
|
118
|
+
# Or from package directory
|
|
119
|
+
cd packages/replay-server
|
|
120
|
+
pnpm run dev
|
|
121
|
+
|
|
122
|
+
# Production build and start
|
|
123
|
+
# From project root
|
|
124
|
+
pnpm build:replay-server
|
|
125
|
+
cd packages/replay-server
|
|
126
|
+
pnpm start
|
|
127
|
+
|
|
128
|
+
# Or from package directory
|
|
129
|
+
cd packages/replay-server
|
|
130
|
+
pnpm run build
|
|
131
|
+
pnpm start
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### API Endpoints
|
|
135
|
+
|
|
136
|
+
**POST /render** - Start a render job
|
|
137
|
+
|
|
138
|
+
With bundleId (recommended - no external game server needed):
|
|
139
|
+
```bash
|
|
140
|
+
curl -X POST http://localhost:3001/render \
|
|
141
|
+
-H "Content-Type: application/json" \
|
|
142
|
+
-d '{
|
|
143
|
+
"manifest": {
|
|
144
|
+
"schema": "lifeRestart.replay.v1",
|
|
145
|
+
"bundleId": "game-life-restart",
|
|
146
|
+
"gameId": "test-game",
|
|
147
|
+
"timeline": [...],
|
|
148
|
+
"highlights": []
|
|
149
|
+
},
|
|
150
|
+
"config": {
|
|
151
|
+
"width": 1080,
|
|
152
|
+
"height": 1920
|
|
153
|
+
}
|
|
154
|
+
}'
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
With external gameUrl (legacy mode):
|
|
158
|
+
```bash
|
|
159
|
+
curl -X POST http://localhost:3001/render \
|
|
160
|
+
-H "Content-Type: application/json" \
|
|
161
|
+
-d '{
|
|
162
|
+
"manifest": { ... },
|
|
163
|
+
"config": {
|
|
164
|
+
"gameUrl": "http://localhost:5173",
|
|
165
|
+
"width": 1080,
|
|
166
|
+
"height": 1920
|
|
167
|
+
}
|
|
168
|
+
}'
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Response:
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"jobId": "5eaee18f-48a7-4193-b594-86756ac76ce2",
|
|
175
|
+
"status": "pending",
|
|
176
|
+
"message": "Render job started"
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**GET /bundles** - List available game bundles
|
|
181
|
+
```bash
|
|
182
|
+
curl http://localhost:3001/bundles
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Response:
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"bundles": [
|
|
189
|
+
{
|
|
190
|
+
"bundleId": "game-life-restart",
|
|
191
|
+
"path": "/app/packages/replay-server/bundles/game-life-restart",
|
|
192
|
+
"url": "http://localhost:3001/bundles/game-life-restart/",
|
|
193
|
+
"ready": true
|
|
194
|
+
}
|
|
195
|
+
],
|
|
196
|
+
"count": 1
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**GET /jobs** - List all jobs
|
|
201
|
+
```bash
|
|
202
|
+
curl http://localhost:3001/jobs
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Response:
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"jobs": [
|
|
209
|
+
{
|
|
210
|
+
"jobId": "5eaee18f-48a7-4193-b594-86756ac76ce2",
|
|
211
|
+
"status": "completed",
|
|
212
|
+
"progress": { ... },
|
|
213
|
+
"error": null,
|
|
214
|
+
"createdAt": "2024-01-01T00:00:00.000Z",
|
|
215
|
+
"completedAt": "2024-01-01T00:05:00.000Z"
|
|
216
|
+
}
|
|
217
|
+
],
|
|
218
|
+
"count": 1
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**GET /status/:jobId** - Check job status
|
|
223
|
+
```bash
|
|
224
|
+
curl http://localhost:3001/status/5eaee18f-48a7-4193-b594-86756ac76ce2
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**GET /download/:jobId** - Download completed video
|
|
228
|
+
```bash
|
|
229
|
+
curl -o video.mp4 http://localhost:3001/download/5eaee18f-48a7-4193-b594-86756ac76ce2
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Game Integration
|
|
233
|
+
|
|
234
|
+
For the renderer to work, the game must support replay mode:
|
|
235
|
+
|
|
236
|
+
### 1. Listen for replay manifest
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// In your game's main component
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
// Check for manifest from URL or window
|
|
242
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
243
|
+
if (urlParams.get('mode') === 'replay') {
|
|
244
|
+
// Wait for manifest injection
|
|
245
|
+
const handleManifest = (e: CustomEvent) => {
|
|
246
|
+
const manifest = e.detail;
|
|
247
|
+
enterReplayMode(manifest);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
window.addEventListener('replay-manifest-loaded', handleManifest);
|
|
251
|
+
|
|
252
|
+
// Also check if already injected
|
|
253
|
+
if ((window as any).__REPLAY_MANIFEST__) {
|
|
254
|
+
enterReplayMode((window as any).__REPLAY_MANIFEST__);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}, []);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 2. Handle replay-next-age event
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
window.addEventListener('replay-next-age', () => {
|
|
264
|
+
// Advance to next age in replay
|
|
265
|
+
replayNextAge();
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 3. Signal when ready
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
// Add data attribute when ready for capture
|
|
273
|
+
<div data-replay-ready="true">
|
|
274
|
+
{/* game content */}
|
|
275
|
+
</div>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Configuration
|
|
279
|
+
|
|
280
|
+
Environment variables:
|
|
281
|
+
|
|
282
|
+
| Variable | Default | Description |
|
|
283
|
+
|----------|---------|-------------|
|
|
284
|
+
| `PORT` | 3001 | Server port |
|
|
285
|
+
| `GAME_URL` | http://localhost:5173 | Fallback URL when no bundleId specified |
|
|
286
|
+
| `BUNDLES_DIR` | ./bundles | Directory containing game bundles |
|
|
287
|
+
| `OUTPUT_DIR` | ./output | Directory for rendered videos |
|
|
288
|
+
|
|
289
|
+
### Game URL Resolution Order
|
|
290
|
+
|
|
291
|
+
When rendering a video, the game URL is resolved in this order:
|
|
292
|
+
1. `config.gameUrl` (explicit URL in render request)
|
|
293
|
+
2. `manifest.bundleId` (serves from `/bundles/<bundleId>/`)
|
|
294
|
+
3. `GAME_URL` environment variable (fallback)
|
|
295
|
+
|
|
296
|
+
## Output
|
|
297
|
+
|
|
298
|
+
Videos are rendered in:
|
|
299
|
+
- Format: MP4 (H.264)
|
|
300
|
+
- Resolution: 720x1080 (portrait, for mobile/Feed)
|
|
301
|
+
- FPS: 16 (configurable)
|
|
302
|
+
- Duration: ~1.2s per age (configurable)
|
|
303
|
+
|
|
304
|
+
## Building Game Bundles
|
|
305
|
+
|
|
306
|
+
Before deploying, you need to build game bundles that will be hosted by the replay-server.
|
|
307
|
+
|
|
308
|
+
### Option A: Using the build script
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
# Build a specific game
|
|
312
|
+
cd packages/replay-server
|
|
313
|
+
./scripts/build-bundle.sh game-life-restart
|
|
314
|
+
|
|
315
|
+
# This will:
|
|
316
|
+
# 1. Build the game from repo/game-life-restart
|
|
317
|
+
# 2. Copy dist/ to bundles/game-life-restart/
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Option B: Manual build
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
# Build the game
|
|
324
|
+
cd repo/game-life-restart
|
|
325
|
+
pnpm install
|
|
326
|
+
pnpm build
|
|
327
|
+
|
|
328
|
+
# Copy to bundles
|
|
329
|
+
mkdir -p packages/replay-server/bundles/game-life-restart
|
|
330
|
+
cp -r dist/* packages/replay-server/bundles/game-life-restart/
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Option C: Docker (automatic)
|
|
334
|
+
|
|
335
|
+
The Dockerfile includes a multi-stage build that automatically builds and embeds the game bundle. No manual steps required.
|
|
336
|
+
|
|
337
|
+
## Deployment Options
|
|
338
|
+
|
|
339
|
+
### Local Development
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# From project root (recommended)
|
|
343
|
+
pnpm dev:replay-server
|
|
344
|
+
|
|
345
|
+
# Or from package directory
|
|
346
|
+
cd packages/replay-server
|
|
347
|
+
pnpm run dev
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Docker with Embedded Bundle (Recommended)
|
|
351
|
+
|
|
352
|
+
The Dockerfile uses multi-stage builds to:
|
|
353
|
+
1. Build the game-life-restart bundle
|
|
354
|
+
2. Build the replay-server
|
|
355
|
+
3. Create a runtime image with both embedded
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
# Build from project root
|
|
359
|
+
docker build -f packages/replay-server/Dockerfile -t replay-server:latest .
|
|
360
|
+
|
|
361
|
+
# Or use docker-compose for local testing
|
|
362
|
+
cd packages/replay-server
|
|
363
|
+
docker-compose -f docker-compose.local.yml up --build
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Serverless (Aliyun FC)
|
|
367
|
+
|
|
368
|
+
Build and deploy to Aliyun Function Compute:
|
|
369
|
+
|
|
370
|
+
#### 1. Login to Aliyun Container Registry (ACR)
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
# Login using Aliyun CLI (recommended)
|
|
374
|
+
aliyun cr GetAuthorizationToken --region cn-beijing --InstanceId xxx
|
|
375
|
+
docker login --username=<your-username> --password=<token> registry.cn-beijing.aliyuncs.com
|
|
376
|
+
|
|
377
|
+
# Or use temporary password from Aliyun console
|
|
378
|
+
docker login registry.cn-beijing.aliyuncs.com
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### 2. Build Docker Image
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
# Build from project root (must be from root, as it needs access to multiple packages in monorepo)
|
|
385
|
+
cd /path/to/agent-foundry
|
|
386
|
+
docker build -f packages/replay-server/Dockerfile -t replay-server:latest .
|
|
387
|
+
|
|
388
|
+
# Note:
|
|
389
|
+
# - Dockerfile uses multi-stage builds
|
|
390
|
+
# - Stage 1: Builds game-life-restart bundle
|
|
391
|
+
# - Stage 2: Builds replay-server
|
|
392
|
+
# - Stage 3: Creates runtime with embedded bundle
|
|
393
|
+
# - Includes Chinese fonts (Noto CJK, WQY Zenhei) for proper text rendering
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
#### 3. Local Docker Testing
|
|
397
|
+
|
|
398
|
+
Before pushing to ACR, test the Docker image locally:
|
|
399
|
+
|
|
400
|
+
##### 3.1 Using Docker Compose (Recommended)
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
cd packages/replay-server
|
|
404
|
+
docker-compose -f docker-compose.local.yml up --build
|
|
405
|
+
|
|
406
|
+
# In another terminal:
|
|
407
|
+
curl http://localhost:3001/health
|
|
408
|
+
curl http://localhost:3001/bundles
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
##### 3.2 Using Docker Run
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
# Run container with embedded bundle
|
|
415
|
+
docker run -d \
|
|
416
|
+
--name replay-server-test \
|
|
417
|
+
-p 3001:3001 \
|
|
418
|
+
--shm-size=2g \
|
|
419
|
+
-e PORT=3001 \
|
|
420
|
+
-v $(pwd)/packages/replay-server/output:/app/packages/replay-server/output \
|
|
421
|
+
replay-server:latest
|
|
422
|
+
|
|
423
|
+
# View container logs
|
|
424
|
+
docker logs -f replay-server-test
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
##### 3.3 Test Health Check
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
# Check if the service started normally
|
|
431
|
+
curl http://localhost:3001/health
|
|
432
|
+
|
|
433
|
+
# Expected response:
|
|
434
|
+
# {
|
|
435
|
+
# "status": "ok",
|
|
436
|
+
# "gameUrl": "http://localhost:5173",
|
|
437
|
+
# "bundlesDir": "/app/packages/replay-server/bundles",
|
|
438
|
+
# "bundleCount": 1
|
|
439
|
+
# }
|
|
440
|
+
|
|
441
|
+
# List available bundles
|
|
442
|
+
curl http://localhost:3001/bundles
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
##### 3.4 Test Render API with bundleId
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
# Submit a render task using embedded bundle (no external game server needed!)
|
|
449
|
+
curl -X POST http://localhost:3001/render \
|
|
450
|
+
-H "Content-Type: application/json" \
|
|
451
|
+
-d '{
|
|
452
|
+
"manifest": {
|
|
453
|
+
"schema": "lifeRestart.replay.v1",
|
|
454
|
+
"bundleId": "game-life-restart",
|
|
455
|
+
"gameId": "test-game",
|
|
456
|
+
"timeline": [],
|
|
457
|
+
"highlights": []
|
|
458
|
+
},
|
|
459
|
+
"config": {
|
|
460
|
+
"width": 1080,
|
|
461
|
+
"height": 1920,
|
|
462
|
+
"fps": 30
|
|
463
|
+
}
|
|
464
|
+
}'
|
|
465
|
+
|
|
466
|
+
# Or use local manifest file
|
|
467
|
+
curl -X POST http://localhost:3001/render \
|
|
468
|
+
-H "Content-Type: application/json" \
|
|
469
|
+
-d "{\"manifest\": $(cat packages/replay-server/samples/life-replay-lr-mkcdfzc2-u8cqbs.json)}"
|
|
470
|
+
|
|
471
|
+
# View task list
|
|
472
|
+
curl http://localhost:3001/jobs
|
|
473
|
+
|
|
474
|
+
# Check specific task status (replace <job-id> with actual task ID)
|
|
475
|
+
curl http://localhost:3001/status/<job-id>
|
|
476
|
+
|
|
477
|
+
# Download completed video (replace <job-id> with actual task ID)
|
|
478
|
+
curl -o test-video.mp4 http://localhost:3001/download/<job-id>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
##### 3.5 View Output Files
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
# View generated video files locally
|
|
485
|
+
ls -lh packages/replay-server/output/
|
|
486
|
+
|
|
487
|
+
# Or enter container to view
|
|
488
|
+
docker exec -it replay-server-test ls -lh /app/packages/replay-server/output/
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
##### 3.6 Stop and Cleanup
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
# Stop container
|
|
495
|
+
docker stop replay-server-test
|
|
496
|
+
|
|
497
|
+
# Remove container
|
|
498
|
+
docker rm replay-server-test
|
|
499
|
+
|
|
500
|
+
# Or stop and remove in one command
|
|
501
|
+
docker rm -f replay-server-test
|
|
502
|
+
|
|
503
|
+
# Remove image (optional)
|
|
504
|
+
docker rmi replay-server:latest
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
##### 3.7 Debugging Tips
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
# Enter container for debugging
|
|
511
|
+
docker exec -it replay-server-test /bin/bash
|
|
512
|
+
|
|
513
|
+
# Check processes inside container
|
|
514
|
+
docker exec replay-server-test ps aux
|
|
515
|
+
|
|
516
|
+
# Check environment variables
|
|
517
|
+
docker exec replay-server-test env
|
|
518
|
+
|
|
519
|
+
# View container resource usage
|
|
520
|
+
docker stats replay-server-test
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
#### 4. Deploy to ACR
|
|
524
|
+
|
|
525
|
+
Use the deploy script for easy deployment:
|
|
526
|
+
|
|
527
|
+
```bash
|
|
528
|
+
# Set your ACR namespace
|
|
529
|
+
export ACR_NAMESPACE="your-namespace"
|
|
530
|
+
|
|
531
|
+
# Deploy (builds and pushes to ACR)
|
|
532
|
+
cd packages/replay-server
|
|
533
|
+
./scripts/deploy-aliyun.sh
|
|
534
|
+
|
|
535
|
+
# Or with a custom version tag
|
|
536
|
+
./scripts/deploy-aliyun.sh v1.0.0
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Or manually:
|
|
540
|
+
|
|
541
|
+
```bash
|
|
542
|
+
# Replace <your-namespace> with your namespace
|
|
543
|
+
export ACR_REGISTRY="registry.cn-beijing.aliyuncs.com"
|
|
544
|
+
export ACR_NAMESPACE="<your-namespace>"
|
|
545
|
+
export IMAGE_NAME="replay-server"
|
|
546
|
+
export IMAGE_TAG="latest"
|
|
547
|
+
|
|
548
|
+
# Tag image
|
|
549
|
+
docker tag replay-server:latest ${ACR_REGISTRY}/${ACR_NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}
|
|
550
|
+
|
|
551
|
+
# Push image
|
|
552
|
+
docker push ${ACR_REGISTRY}/${ACR_NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
#### 5. Configure in Aliyun Function Compute
|
|
556
|
+
|
|
557
|
+
** Rebuild to be compatible with Aliyun FC **
|
|
558
|
+
```bash
|
|
559
|
+
# make sure you are at root dir
|
|
560
|
+
# build disabling attestation/provennace
|
|
561
|
+
docker buildx build \
|
|
562
|
+
--platform linux/amd64 \
|
|
563
|
+
--provenance=false \
|
|
564
|
+
--sbom=false \
|
|
565
|
+
-t ${ACR_REGISTRY}/${ACR_NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG} \
|
|
566
|
+
-f packages/replay-server/Dockerfile \
|
|
567
|
+
--push \
|
|
568
|
+
.
|
|
569
|
+
|
|
570
|
+
# imagetools inspectation
|
|
571
|
+
docker buildx imagetools inspect ${ACR_REGISTRY}/${ACR_NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
**Environment Variables:**
|
|
575
|
+
- `PORT`: Server port (default: 9000, FC will set this automatically)
|
|
576
|
+
- `BUNDLES_DIR`: `/app/packages/replay-server/bundles` (already set in Dockerfile)
|
|
577
|
+
- `OUTPUT_DIR`: `/tmp/output` (use /tmp for FC writable directory)
|
|
578
|
+
|
|
579
|
+
**Function Configuration Requirements:**
|
|
580
|
+
- **Memory**: **Minimum 2048 MB (2GB)**, recommended 4GB
|
|
581
|
+
- Chromium requires significant memory to launch and render
|
|
582
|
+
- Less than 2GB may cause browser crashes or timeouts
|
|
583
|
+
- **Timeout**: **Minimum 300 seconds (5 minutes)**, recommended 600 seconds (10 minutes)
|
|
584
|
+
- Cold start + browser launch can take 30-60 seconds
|
|
585
|
+
- Video rendering time depends on replay length
|
|
586
|
+
- **Instance Concurrency**: **1** (required)
|
|
587
|
+
- Prevents resource contention between concurrent renders
|
|
588
|
+
- Each instance needs dedicated CPU/memory for Chromium
|
|
589
|
+
- **CPU**: Auto-scaled with memory (FC default is fine)
|
|
590
|
+
|
|
591
|
+
**Important Notes for FC Deployment:**
|
|
592
|
+
- The image includes all Chromium dependencies pre-installed
|
|
593
|
+
- Browser launch parameters are optimized for containerized environments
|
|
594
|
+
- Cold starts may take longer on first invocation (expect 30-60s for browser initialization)
|
|
595
|
+
|
|
596
|
+
#### 6. Verify Deployment
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
# Test if function started normally
|
|
600
|
+
curl https://<your-function-url>/health
|
|
601
|
+
|
|
602
|
+
# Check available bundles
|
|
603
|
+
curl https://<your-function-url>/bundles
|
|
604
|
+
|
|
605
|
+
# **NEW: Debug Chromium installation**
|
|
606
|
+
curl https://<your-function-url>/debug/chrome
|
|
607
|
+
# This endpoint verifies Chromium is properly installed and all dependencies are available
|
|
608
|
+
# Expected response:
|
|
609
|
+
# {
|
|
610
|
+
# "executablePath": "/usr/bin/chromium",
|
|
611
|
+
# "tests": {
|
|
612
|
+
# "version": { "ok": true, "output": "Chromium 120.0.6099.224" },
|
|
613
|
+
# "dependencies": { "ok": true, "summary": "All libraries found" },
|
|
614
|
+
# "chineseFonts": { "ok": true, "count": 15, "sample": ["Noto Sans CJK...", "..."] }
|
|
615
|
+
# }
|
|
616
|
+
# }
|
|
617
|
+
|
|
618
|
+
# Submit a render job
|
|
619
|
+
curl -X POST https://<your-function-url>/render \
|
|
620
|
+
-H "Content-Type: application/json" \
|
|
621
|
+
-d '{
|
|
622
|
+
"manifest": {
|
|
623
|
+
"schema": "lifeRestart.replay.v1",
|
|
624
|
+
"bundleId": "game-life-restart",
|
|
625
|
+
...
|
|
626
|
+
}
|
|
627
|
+
}'
|
|
628
|
+
```
|
package/bundles/.gitkeep
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI tool for offline video rendering
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx tsx src/cli/render.ts --manifest ./replay.json --output ./output.mp4
|
|
7
|
+
* npx tsx src/cli/render.ts --manifest-url https://... --game-url http://localhost:5173
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/cli/render.ts"],"names":[],"mappings":";AACA;;;;;;GAMG"}
|