@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.
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Replay Render Server
3
+ *
4
+ * HTTP API for rendering replays to video
5
+ *
6
+ * Endpoints:
7
+ * - POST /render - Start a render job
8
+ * - GET /jobs - List all jobs
9
+ * - GET /status/:jobId - Get job status
10
+ * - GET /download/:jobId - Download completed video
11
+ * - GET /bundles - List available game bundles
12
+ */
13
+ import express from 'express';
14
+ import cors from 'cors';
15
+ import { v4 as uuidv4 } from 'uuid';
16
+ import * as fs from 'fs';
17
+ import * as path from 'path';
18
+ import { fileURLToPath } from 'url';
19
+ import { dirname } from 'path';
20
+ import { execSync } from 'child_process';
21
+ import { PuppeteerRenderer } from '../renderer/PuppeteerRenderer.js';
22
+ const __dirname = dirname(fileURLToPath(import.meta.url));
23
+ const app = express();
24
+ app.use(cors());
25
+ app.use(express.json({ limit: '10mb' }));
26
+ // Configuration
27
+ const PORT = process.env.PORT || 3001;
28
+ const GAME_URL = process.env.GAME_URL || 'http://localhost:5173';
29
+ const OUTPUT_DIR = process.env.OUTPUT_DIR || path.join(process.cwd(), 'output');
30
+ const BUNDLES_DIR = process.env.BUNDLES_DIR || path.join(__dirname, '../../bundles');
31
+ /**
32
+ * GET /bundles
33
+ * List available game bundles
34
+ * This route must be registered BEFORE the static middleware to handle /bundles requests
35
+ */
36
+ app.get('/bundles', (req, res) => {
37
+ console.log('[GET /bundles] Request received');
38
+ try {
39
+ // Ensure bundles directory exists
40
+ if (!fs.existsSync(BUNDLES_DIR)) {
41
+ return res.json({ bundles: [], count: 0 });
42
+ }
43
+ // List directories in bundles folder
44
+ const entries = fs.readdirSync(BUNDLES_DIR, { withFileTypes: true });
45
+ const bundles = entries
46
+ .filter(entry => entry.isDirectory())
47
+ .map(entry => {
48
+ const bundlePath = path.join(BUNDLES_DIR, entry.name);
49
+ const indexPath = path.join(bundlePath, 'index.html');
50
+ const hasIndex = fs.existsSync(indexPath);
51
+ return {
52
+ bundleId: entry.name,
53
+ path: bundlePath,
54
+ url: `http://localhost:${PORT}/bundles/${entry.name}/`,
55
+ ready: hasIndex,
56
+ };
57
+ });
58
+ console.log('[GET /bundles] Found bundles:', bundles.length);
59
+ res.json({
60
+ bundles,
61
+ count: bundles.length,
62
+ bundlesDir: BUNDLES_DIR,
63
+ });
64
+ }
65
+ catch (error) {
66
+ console.error('[GET /bundles] Error listing bundles:', error);
67
+ res.status(500).json({ error: 'Failed to list bundles' });
68
+ }
69
+ });
70
+ // Serve static game bundles (AFTER API route to allow /bundles/<bundleId>/ paths)
71
+ app.use('/bundles', express.static(BUNDLES_DIR, {
72
+ index: 'index.html',
73
+ extensions: ['html']
74
+ }));
75
+ /**
76
+ * GET /debug/chrome
77
+ * Debug endpoint to verify Chromium installation
78
+ */
79
+ app.get('/debug/chrome', async (req, res) => {
80
+ const results = {
81
+ executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || '/usr/bin/chromium',
82
+ tests: {}
83
+ };
84
+ try {
85
+ // Test 1: Check if chromium exists
86
+ const version = execSync(`${results.executablePath} --version`, {
87
+ encoding: 'utf8',
88
+ timeout: 5000
89
+ });
90
+ results.tests.version = { ok: true, output: version.trim() };
91
+ }
92
+ catch (e) {
93
+ results.tests.version = {
94
+ ok: false,
95
+ error: e.message,
96
+ stderr: e.stderr?.toString()
97
+ };
98
+ }
99
+ try {
100
+ // Test 2: Check library dependencies
101
+ const ldd = execSync(`ldd ${results.executablePath}`, {
102
+ encoding: 'utf8',
103
+ timeout: 5000
104
+ });
105
+ results.tests.dependencies = { ok: true, summary: 'All libraries found' };
106
+ }
107
+ catch (e) {
108
+ results.tests.dependencies = {
109
+ ok: false,
110
+ error: e.message,
111
+ stderr: e.stderr?.toString()
112
+ };
113
+ }
114
+ try {
115
+ // Test 3: Check if Chinese fonts are available
116
+ const fonts = execSync(`fc-list :lang=zh-cn`, {
117
+ encoding: 'utf8',
118
+ timeout: 5000
119
+ });
120
+ const fontLines = fonts.split('\n').filter(l => l.trim());
121
+ results.tests.chineseFonts = {
122
+ ok: fontLines.length > 0,
123
+ count: fontLines.length,
124
+ sample: fontLines.slice(0, 3)
125
+ };
126
+ }
127
+ catch (e) {
128
+ results.tests.chineseFonts = {
129
+ ok: false,
130
+ error: 'fontconfig not available or no Chinese fonts found'
131
+ };
132
+ }
133
+ res.json(results);
134
+ });
135
+ /**
136
+ * Resolve game URL from manifest bundleId or config
137
+ */
138
+ function resolveGameUrl(manifest, configGameUrl) {
139
+ // 1. Explicit gameUrl in config takes precedence
140
+ if (configGameUrl) {
141
+ return configGameUrl;
142
+ }
143
+ // 2. Use bundleId from manifest to serve from local bundles
144
+ if (manifest.bundleId) {
145
+ const bundlePath = path.join(BUNDLES_DIR, manifest.bundleId);
146
+ if (!fs.existsSync(bundlePath)) {
147
+ throw new Error(`Bundle not found: ${manifest.bundleId}`);
148
+ }
149
+ return `http://localhost:${PORT}/bundles/${manifest.bundleId}/`;
150
+ }
151
+ // 3. Fall back to default GAME_URL
152
+ return GAME_URL;
153
+ }
154
+ const jobs = new Map();
155
+ // Ensure output directory exists
156
+ fs.mkdirSync(OUTPUT_DIR, { recursive: true });
157
+ /**
158
+ * POST /render
159
+ * Start a new render job
160
+ */
161
+ app.post('/render', async (req, res) => {
162
+ console.log('[POST /render] Request received');
163
+ try {
164
+ const { manifest, manifestUrl, config } = req.body;
165
+ // Get manifest from body or URL
166
+ let replayManifest;
167
+ if (manifest) {
168
+ replayManifest = manifest;
169
+ }
170
+ else if (manifestUrl) {
171
+ const response = await fetch(manifestUrl);
172
+ if (!response.ok) {
173
+ console.error('[POST /render] Failed to fetch manifest from URL:', manifestUrl);
174
+ return res.status(400).json({ error: 'Failed to fetch manifest from URL' });
175
+ }
176
+ replayManifest = await response.json();
177
+ }
178
+ else {
179
+ console.error('[POST /render] Missing required parameter: manifest or manifestUrl');
180
+ return res.status(400).json({ error: 'Either manifest or manifestUrl is required' });
181
+ }
182
+ // Validate manifest
183
+ if (replayManifest.schema !== 'lifeRestart.replay.v1') {
184
+ console.error('[POST /render] Invalid manifest schema:', replayManifest.schema);
185
+ return res.status(400).json({ error: 'Invalid manifest schema' });
186
+ }
187
+ // Create job
188
+ const jobId = uuidv4();
189
+ const outputPath = path.join(OUTPUT_DIR, `${jobId}.mp4`);
190
+ const job = {
191
+ id: jobId,
192
+ status: 'pending',
193
+ progress: null,
194
+ outputPath: null,
195
+ error: null,
196
+ createdAt: new Date(),
197
+ completedAt: null,
198
+ };
199
+ jobs.set(jobId, job);
200
+ // Resolve game URL from bundleId or config
201
+ let gameUrl;
202
+ try {
203
+ gameUrl = resolveGameUrl(replayManifest, config?.gameUrl);
204
+ }
205
+ catch (error) {
206
+ const errorMessage = error instanceof Error ? error.message : String(error);
207
+ console.error('[POST /render] Failed to resolve game URL:', errorMessage);
208
+ return res.status(400).json({ error: errorMessage });
209
+ }
210
+ // Start rendering in background
211
+ processJob(job, replayManifest, {
212
+ gameUrl,
213
+ outputPath,
214
+ width: config?.width,
215
+ height: config?.height,
216
+ fps: config?.fps,
217
+ secondsPerAge: config?.secondsPerAge,
218
+ });
219
+ console.log('[POST /render] Render job started successfully, jobId:', jobId);
220
+ console.log('[POST /render] Using game URL:', gameUrl);
221
+ res.json({
222
+ jobId,
223
+ status: job.status,
224
+ message: 'Render job started',
225
+ });
226
+ }
227
+ catch (error) {
228
+ console.error('[POST /render] Error starting render job:', error);
229
+ res.status(500).json({ error: 'Internal server error' });
230
+ }
231
+ });
232
+ /**
233
+ * GET /jobs
234
+ * List all jobs
235
+ */
236
+ app.get('/jobs', (req, res) => {
237
+ console.log('[GET /jobs] Request received');
238
+ try {
239
+ const jobList = Array.from(jobs.values()).map(job => ({
240
+ jobId: job.id,
241
+ status: job.status,
242
+ progress: job.progress,
243
+ error: job.error,
244
+ createdAt: job.createdAt,
245
+ completedAt: job.completedAt,
246
+ }));
247
+ console.log('[GET /jobs] Returning job list, count:', jobList.length);
248
+ res.json({
249
+ jobs: jobList,
250
+ count: jobList.length,
251
+ });
252
+ }
253
+ catch (error) {
254
+ console.error('[GET /jobs] Error listing jobs:', error);
255
+ res.status(500).json({ error: 'Internal server error' });
256
+ }
257
+ });
258
+ /**
259
+ * GET /status/:jobId
260
+ * Get job status
261
+ */
262
+ app.get('/status/:jobId', (req, res) => {
263
+ const { jobId } = req.params;
264
+ const job = jobs.get(jobId);
265
+ if (!job) {
266
+ return res.status(404).json({ error: 'Job not found' });
267
+ }
268
+ res.json({
269
+ jobId: job.id,
270
+ status: job.status,
271
+ progress: job.progress,
272
+ error: job.error,
273
+ createdAt: job.createdAt,
274
+ completedAt: job.completedAt,
275
+ });
276
+ });
277
+ /**
278
+ * GET /download/:jobId
279
+ * Download completed video
280
+ */
281
+ app.get('/download/:jobId', (req, res) => {
282
+ const { jobId } = req.params;
283
+ const job = jobs.get(jobId);
284
+ if (!job) {
285
+ return res.status(404).json({ error: 'Job not found' });
286
+ }
287
+ if (job.status !== 'completed' || !job.outputPath) {
288
+ return res.status(400).json({ error: 'Job not completed' });
289
+ }
290
+ if (!fs.existsSync(job.outputPath)) {
291
+ return res.status(404).json({ error: 'Video file not found' });
292
+ }
293
+ res.download(job.outputPath, `replay-${jobId}.mp4`);
294
+ });
295
+ /**
296
+ * GET /health
297
+ * Health check endpoint
298
+ */
299
+ app.get('/health', (req, res) => {
300
+ // Count available bundles
301
+ let bundleCount = 0;
302
+ if (fs.existsSync(BUNDLES_DIR)) {
303
+ const entries = fs.readdirSync(BUNDLES_DIR, { withFileTypes: true });
304
+ bundleCount = entries.filter(e => e.isDirectory()).length;
305
+ }
306
+ res.json({
307
+ status: 'ok',
308
+ gameUrl: GAME_URL,
309
+ bundlesDir: BUNDLES_DIR,
310
+ bundleCount,
311
+ });
312
+ });
313
+ /**
314
+ * Process a render job
315
+ */
316
+ async function processJob(job, manifest, config) {
317
+ job.status = 'processing';
318
+ const renderer = new PuppeteerRenderer();
319
+ try {
320
+ const result = await renderer.render(manifest, {
321
+ ...config,
322
+ onProgress: (progress) => {
323
+ job.progress = progress;
324
+ },
325
+ });
326
+ if (result.success) {
327
+ job.status = 'completed';
328
+ job.outputPath = result.outputPath || null;
329
+ }
330
+ else {
331
+ job.status = 'failed';
332
+ job.error = result.error || 'Unknown error';
333
+ }
334
+ }
335
+ catch (error) {
336
+ job.status = 'failed';
337
+ job.error = error instanceof Error ? error.message : String(error);
338
+ }
339
+ job.completedAt = new Date();
340
+ }
341
+ // Start server
342
+ app.listen(PORT, () => {
343
+ console.log(`🎬 Replay Render Server running on http://localhost:${PORT}`);
344
+ console.log(`📺 Default Game URL: ${GAME_URL}`);
345
+ console.log(`📦 Bundles directory: ${BUNDLES_DIR}`);
346
+ console.log(`📁 Output directory: ${OUTPUT_DIR}`);
347
+ });
348
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ version: '3.8'
2
+
3
+ # Local development Docker Compose for replay-server
4
+ #
5
+ # Usage:
6
+ # cd packages/replay-server
7
+ # docker-compose -f docker-compose.local.yml up --build
8
+ #
9
+ # This will:
10
+ # 1. Build the replay-server with embedded game-life-restart bundle
11
+ # 2. Start the server on port 3001
12
+ # 3. Mount ./output for video files
13
+
14
+ services:
15
+ replay-server:
16
+ build:
17
+ context: ../..
18
+ dockerfile: packages/replay-server/Dockerfile
19
+ ports:
20
+ - "3001:3001"
21
+ environment:
22
+ - PORT=3001
23
+ - BUNDLES_DIR=/app/packages/replay-server/bundles
24
+ - OUTPUT_DIR=/app/packages/replay-server/output
25
+ volumes:
26
+ # Mount output directory for easy access to rendered videos
27
+ - ./output:/app/packages/replay-server/output
28
+ # Increase shared memory for Chromium
29
+ shm_size: '2gb'
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@agent-foundry/replay-server",
3
+ "version": "1.0.0",
4
+ "description": "Video rendering service for AgentFoundry mini-game replays",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "scripts": {
8
+ "dev": "tsx watch src/server/index.ts",
9
+ "build": "tsc",
10
+ "start": "node dist/server/index.js",
11
+ "render": "tsx src/cli/render.ts",
12
+ "clean": "rimraf dist output"
13
+ },
14
+ "dependencies": {
15
+ "@agent-foundry/replay": "^1.0.1",
16
+ "express": "^4.18.2",
17
+ "puppeteer": "^22.0.0",
18
+ "fluent-ffmpeg": "^2.1.2",
19
+ "cors": "^2.8.5",
20
+ "uuid": "^9.0.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/express": "^4.17.21",
24
+ "@types/fluent-ffmpeg": "^2.1.24",
25
+ "@types/cors": "^2.8.17",
26
+ "@types/uuid": "^9.0.7",
27
+ "@types/node": "^20.11.0",
28
+ "typescript": "^5.3.3",
29
+ "tsx": "^4.7.0",
30
+ "rimraf": "^5.0.5"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ }
35
+ }