@telepat/rilo 0.1.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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +209 -0
  3. package/index.js +1 -0
  4. package/models/black-forest-labs__flux-2-pro.json +78 -0
  5. package/models/black-forest-labs__flux-schnell.json +95 -0
  6. package/models/bytedance__seedream-4.json +71 -0
  7. package/models/deepseek-ai__deepseek-v3.json +61 -0
  8. package/models/google__nano-banana-pro.json +92 -0
  9. package/models/google__veo-3.1-fast.json +93 -0
  10. package/models/google__veo-3.1.json +93 -0
  11. package/models/jaaari__kokoro-82m.json +86 -0
  12. package/models/kwaivgi__kling-v3-video.json +101 -0
  13. package/models/minimax__speech-02-turbo.json +141 -0
  14. package/models/pixverse__pixverse-v5.6.json +113 -0
  15. package/models/prunaai__z-image-turbo.json +107 -0
  16. package/models/resemble-ai__chatterbox-turbo.json +102 -0
  17. package/models/wan-video__wan-2.2-i2v-fast.json +139 -0
  18. package/package.json +67 -0
  19. package/src/api/firebaseFunction.js +46 -0
  20. package/src/api/middleware/auth.js +70 -0
  21. package/src/api/openapi/generateOpenApi.js +21 -0
  22. package/src/api/openapi/spec.js +831 -0
  23. package/src/api/routes/jobs.js +45 -0
  24. package/src/api/routes/projectAssets.js +63 -0
  25. package/src/api/routes/projects.js +647 -0
  26. package/src/api/routes/webhooks.js +13 -0
  27. package/src/api/server.js +88 -0
  28. package/src/backends/firebaseClient.js +57 -0
  29. package/src/backends/outputBackend.js +186 -0
  30. package/src/backends/projectMetadataBackend.js +550 -0
  31. package/src/cli/commands/openHome.js +70 -0
  32. package/src/cli/commands/settingsFlow.js +196 -0
  33. package/src/cli/index.js +192 -0
  34. package/src/config/env.js +158 -0
  35. package/src/config/keystore.js +175 -0
  36. package/src/config/models.js +281 -0
  37. package/src/config/settingsSchema.js +214 -0
  38. package/src/media/ffmpeg.js +144 -0
  39. package/src/media/files.js +77 -0
  40. package/src/media/subtitles.js +444 -0
  41. package/src/observability/apiTrace.js +17 -0
  42. package/src/observability/logger.js +7 -0
  43. package/src/observability/metrics.js +10 -0
  44. package/src/pipeline/inputSanitizer.js +6 -0
  45. package/src/pipeline/orchestrator.js +1669 -0
  46. package/src/policy/contentGuardrails.js +30 -0
  47. package/src/providers/predictions.js +188 -0
  48. package/src/providers/replicateClient.js +12 -0
  49. package/src/steps/alignSubtitles.js +156 -0
  50. package/src/steps/burnInSubtitles.js +22 -0
  51. package/src/steps/composeFinalVideo.js +57 -0
  52. package/src/steps/generateKeyframes.js +70 -0
  53. package/src/steps/generateVideoSegments.js +95 -0
  54. package/src/steps/generateVoiceover.js +128 -0
  55. package/src/steps/imageToVideoAdapters.js +100 -0
  56. package/src/steps/script.js +177 -0
  57. package/src/steps/textToImageAdapters.js +87 -0
  58. package/src/store/assetStore.js +5 -0
  59. package/src/store/jobStore.js +102 -0
  60. package/src/store/projectAnalyticsStore.js +625 -0
  61. package/src/store/projectStore.js +684 -0
  62. package/src/store/settingsStore.js +155 -0
  63. package/src/store/staleAssetStore.js +63 -0
  64. package/src/types/job.js +28 -0
  65. package/src/types/media.js +28 -0
  66. package/src/worker/processor.js +24 -0
@@ -0,0 +1,107 @@
1
+ {
2
+ "modelId": "prunaai/z-image-turbo",
3
+ "provider": "replicate",
4
+ "displayName": "Z Image Turbo",
5
+ "category": "image-generation",
6
+ "pricingSourceUrl": "https://replicate.com/prunaai/z-image-turbo",
7
+ "pricingNotes": "Replicate pricing is tiered by output image resolution (megapixels).",
8
+ "pricing": {
9
+ "usdPerSecond": null,
10
+ "usdPer1kInputTokens": null,
11
+ "usdPer1kOutputTokens": null
12
+ },
13
+ "pricingRules": {
14
+ "basis": "output_image_megapixels",
15
+ "tiers": [
16
+ {
17
+ "maxMegapixels": 0.5,
18
+ "usdPerImage": 0.0025
19
+ },
20
+ {
21
+ "maxMegapixels": 1,
22
+ "usdPerImage": 0.005
23
+ },
24
+ {
25
+ "maxMegapixels": 2,
26
+ "usdPerImage": 0.01
27
+ },
28
+ {
29
+ "maxMegapixels": 3,
30
+ "usdPerImage": 0.015
31
+ },
32
+ {
33
+ "maxMegapixels": 4,
34
+ "usdPerImage": 0.02
35
+ }
36
+ ]
37
+ },
38
+ "inputOptions": {
39
+ "userConfigurable": [
40
+ "num_inference_steps",
41
+ "guidance_scale",
42
+ "seed",
43
+ "go_fast",
44
+ "output_format",
45
+ "output_quality"
46
+ ],
47
+ "pipelineManaged": [
48
+ "prompt",
49
+ "width",
50
+ "height"
51
+ ],
52
+ "fields": {
53
+ "num_inference_steps": {
54
+ "type": "integer",
55
+ "default": 8,
56
+ "minimum": 1,
57
+ "maximum": 50
58
+ },
59
+ "guidance_scale": {
60
+ "type": "number",
61
+ "default": 0,
62
+ "minimum": 0,
63
+ "maximum": 20
64
+ },
65
+ "seed": {
66
+ "type": "integer",
67
+ "nullable": true
68
+ },
69
+ "go_fast": {
70
+ "type": "boolean",
71
+ "default": false
72
+ },
73
+ "output_format": {
74
+ "type": "string",
75
+ "default": "jpg",
76
+ "enum": [
77
+ "jpg",
78
+ "jpeg",
79
+ "png",
80
+ "webp"
81
+ ]
82
+ },
83
+ "output_quality": {
84
+ "type": "integer",
85
+ "default": 80,
86
+ "minimum": 0,
87
+ "maximum": 100
88
+ },
89
+ "prompt": {
90
+ "type": "string",
91
+ "required": true
92
+ },
93
+ "width": {
94
+ "type": "integer",
95
+ "required": true,
96
+ "minimum": 64,
97
+ "maximum": 2048
98
+ },
99
+ "height": {
100
+ "type": "integer",
101
+ "required": true,
102
+ "minimum": 64,
103
+ "maximum": 2048
104
+ }
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,102 @@
1
+ {
2
+ "modelId": "resemble-ai/chatterbox-turbo",
3
+ "provider": "replicate",
4
+ "displayName": "Chatterbox Turbo",
5
+ "category": "text-to-speech",
6
+ "pricingSourceUrl": "https://replicate.com/resemble-ai/chatterbox-turbo",
7
+ "pricingNotes": "Replicate bills this model at $0.025 per thousand input characters.",
8
+ "pricing": {
9
+ "usdPerSecond": null,
10
+ "usdPer1kInputTokens": null,
11
+ "usdPer1kOutputTokens": null,
12
+ "usdPer1kInputCharacters": 0.025
13
+ },
14
+ "capabilities": {
15
+ "voiceCloning": {
16
+ "supported": true,
17
+ "enabled": false,
18
+ "inputField": "reference_audio",
19
+ "notes": "Voice cloning is intentionally disabled in this app for now."
20
+ }
21
+ },
22
+ "inputOptions": {
23
+ "userConfigurable": [
24
+ "voice",
25
+ "temperature",
26
+ "top_p",
27
+ "top_k",
28
+ "repetition_penalty",
29
+ "seed"
30
+ ],
31
+ "pipelineManaged": [
32
+ "text",
33
+ "reference_audio"
34
+ ],
35
+ "fields": {
36
+ "voice": {
37
+ "type": "string",
38
+ "default": "Andy",
39
+ "enum": [
40
+ "Aaron",
41
+ "Abigail",
42
+ "Anaya",
43
+ "Andy",
44
+ "Archer",
45
+ "Brian",
46
+ "Chloe",
47
+ "Dylan",
48
+ "Emmanuel",
49
+ "Ethan",
50
+ "Evelyn",
51
+ "Gavin",
52
+ "Gordon",
53
+ "Ivan",
54
+ "Laura",
55
+ "Lucy",
56
+ "Madison",
57
+ "Marisol",
58
+ "Meera",
59
+ "Walter"
60
+ ]
61
+ },
62
+ "temperature": {
63
+ "type": "number",
64
+ "default": 0.8,
65
+ "minimum": 0.05,
66
+ "maximum": 2
67
+ },
68
+ "top_p": {
69
+ "type": "number",
70
+ "default": 0.95,
71
+ "minimum": 0.5,
72
+ "maximum": 1
73
+ },
74
+ "top_k": {
75
+ "type": "integer",
76
+ "default": 1000,
77
+ "minimum": 1,
78
+ "maximum": 2000
79
+ },
80
+ "repetition_penalty": {
81
+ "type": "number",
82
+ "default": 1.2,
83
+ "minimum": 1,
84
+ "maximum": 2
85
+ },
86
+ "seed": {
87
+ "type": "integer",
88
+ "nullable": true
89
+ },
90
+ "reference_audio": {
91
+ "type": "string",
92
+ "nullable": true,
93
+ "format": "uri"
94
+ },
95
+ "text": {
96
+ "type": "string",
97
+ "required": true,
98
+ "maxLength": 500
99
+ }
100
+ }
101
+ }
102
+ }
@@ -0,0 +1,139 @@
1
+ {
2
+ "modelId": "wan-video/wan-2.2-i2v-fast",
3
+ "provider": "replicate",
4
+ "displayName": "Wan 2.2 I2V Fast",
5
+ "category": "video-generation",
6
+ "pricingSourceUrl": "https://replicate.com/wan-video/wan-2.2-i2v-fast",
7
+ "pricingNotes": "Replicate pricing is per output video and depends on resolution and interpolation variant.",
8
+ "pricing": {
9
+ "usdPerSecond": 0.009876543209876543,
10
+ "usdPer1kInputTokens": null,
11
+ "usdPer1kOutputTokens": null
12
+ },
13
+ "pricingRules": {
14
+ "basis": "output_video",
15
+ "assumptions": {
16
+ "defaultFramesPerSecond": 16,
17
+ "defaultNumFrames": 81,
18
+ "defaultSeconds": 5.0625,
19
+ "usdPerSecondDerivedFrom": "480p_base"
20
+ },
21
+ "tiers": [
22
+ {
23
+ "resolution": "480p",
24
+ "variant": "base",
25
+ "usdPerVideo": 0.05
26
+ },
27
+ {
28
+ "resolution": "480p",
29
+ "variant": "interpolate",
30
+ "usdPerVideo": 0.065
31
+ },
32
+ {
33
+ "resolution": "720p",
34
+ "variant": "base",
35
+ "usdPerVideo": 0.11
36
+ },
37
+ {
38
+ "resolution": "720p",
39
+ "variant": "interpolate",
40
+ "usdPerVideo": 0.145
41
+ }
42
+ ]
43
+ },
44
+ "inputOptions": {
45
+ "userConfigurable": [
46
+ "interpolate_output",
47
+ "go_fast",
48
+ "sample_shift",
49
+ "seed",
50
+ "disable_safety_checker",
51
+ "lora_weights_transformer",
52
+ "lora_scale_transformer",
53
+ "lora_weights_transformer_2",
54
+ "lora_scale_transformer_2"
55
+ ],
56
+ "pipelineManaged": [
57
+ "prompt",
58
+ "image",
59
+ "last_image",
60
+ "num_frames",
61
+ "frames_per_second",
62
+ "resolution"
63
+ ],
64
+ "fields": {
65
+ "interpolate_output": {
66
+ "type": "boolean",
67
+ "default": false
68
+ },
69
+ "go_fast": {
70
+ "type": "boolean",
71
+ "default": true
72
+ },
73
+ "sample_shift": {
74
+ "type": "number",
75
+ "default": 12,
76
+ "minimum": 1,
77
+ "maximum": 20
78
+ },
79
+ "seed": {
80
+ "type": "integer",
81
+ "nullable": true
82
+ },
83
+ "disable_safety_checker": {
84
+ "type": "boolean",
85
+ "default": false
86
+ },
87
+ "lora_weights_transformer": {
88
+ "type": "string",
89
+ "nullable": true,
90
+ "allowAnyString": true
91
+ },
92
+ "lora_scale_transformer": {
93
+ "type": "number",
94
+ "default": 1
95
+ },
96
+ "lora_weights_transformer_2": {
97
+ "type": "string",
98
+ "nullable": true,
99
+ "allowAnyString": true
100
+ },
101
+ "lora_scale_transformer_2": {
102
+ "type": "number",
103
+ "default": 1
104
+ },
105
+ "prompt": {
106
+ "type": "string",
107
+ "required": true
108
+ },
109
+ "image": {
110
+ "type": "string",
111
+ "required": true
112
+ },
113
+ "last_image": {
114
+ "type": "string",
115
+ "required": true
116
+ },
117
+ "num_frames": {
118
+ "type": "integer",
119
+ "required": true,
120
+ "minimum": 81,
121
+ "maximum": 121
122
+ },
123
+ "frames_per_second": {
124
+ "type": "integer",
125
+ "required": true,
126
+ "minimum": 5,
127
+ "maximum": 30
128
+ },
129
+ "resolution": {
130
+ "type": "string",
131
+ "required": true,
132
+ "enum": [
133
+ "480p",
134
+ "720p"
135
+ ]
136
+ }
137
+ }
138
+ }
139
+ }
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@telepat/rilo",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "bin": {
6
+ "rilo": "src/cli/index.js"
7
+ },
8
+ "files": [
9
+ "src/",
10
+ "models/",
11
+ "index.js",
12
+ "README.md"
13
+ ],
14
+ "homepage": "https://docs.telepat.io/rilo/",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/telepat-io/rilo"
18
+ },
19
+ "type": "module",
20
+ "engines": {
21
+ "node": "22"
22
+ },
23
+ "scripts": {
24
+ "start": "node src/cli/index.js",
25
+ "dev": "node src/cli/index.js",
26
+ "api": "node src/api/server.js",
27
+ "build": "npm run frontend:build",
28
+ "frontend:install": "npm --prefix frontend install",
29
+ "frontend:dev": "npm --prefix frontend run dev",
30
+ "frontend:lint": "npm --prefix frontend run lint",
31
+ "frontend:build": "npm --prefix frontend run build",
32
+ "frontend:preview": "npm --prefix frontend run preview",
33
+ "docs:start": "npm --prefix docs-site run start",
34
+ "docs:build": "npm --prefix docs-site run build",
35
+ "docs:serve": "npm --prefix docs-site run serve",
36
+ "docs:deploy": "npm --prefix docs-site run deploy",
37
+ "dev:all": "concurrently -k -n API,WORKER,FRONTEND -c cyan,magenta,green \"npm run api\" \"npm run worker\" \"npm run frontend:dev\"",
38
+ "api:firebase:serve": "firebase emulators:start --only functions",
39
+ "api:firebase:deploy": "firebase deploy --only functions",
40
+ "openapi:generate": "node src/api/openapi/generateOpenApi.js",
41
+ "worker": "node src/worker/processor.js",
42
+ "lint": "eslint .",
43
+ "test": "node --test",
44
+ "test:coverage": "node --test --experimental-test-coverage",
45
+ "test:coverage:lcov": "c8 --reporter=lcov --reporter=text-summary --report-dir coverage node --test",
46
+ "smoke": "node src/cli/index.js --help"
47
+ },
48
+ "dependencies": {
49
+ "@inquirer/prompts": "^8.3.2",
50
+ "dotenv": "^16.5.0",
51
+ "express": "^4.21.2",
52
+ "firebase-admin": "^13.4.0",
53
+ "firebase-functions": "^6.4.0",
54
+ "replicate": "^1.0.1",
55
+ "uuid": "^11.1.0"
56
+ },
57
+ "devDependencies": {
58
+ "@eslint/js": "^10.0.1",
59
+ "c8": "^10.1.3",
60
+ "concurrently": "^9.2.1",
61
+ "eslint": "^10.0.2",
62
+ "globals": "^17.3.0"
63
+ },
64
+ "optionalDependencies": {
65
+ "keytar": "^7.9.0"
66
+ }
67
+ }
@@ -0,0 +1,46 @@
1
+ import { onRequest } from 'firebase-functions/v2/https';
2
+ import { createApiApp } from './server.js';
3
+
4
+ function parseOptionalNumber(value) {
5
+ if (value === undefined || value === null || value === '') {
6
+ return undefined;
7
+ }
8
+
9
+ const parsed = Number(value);
10
+ return Number.isFinite(parsed) ? parsed : undefined;
11
+ }
12
+
13
+ const minInstances = parseOptionalNumber(process.env.FUNCTION_MIN_INSTANCES);
14
+ const timeoutSeconds = parseOptionalNumber(process.env.FUNCTION_TIMEOUT_SECONDS) || 120;
15
+ const concurrency = parseOptionalNumber(process.env.FUNCTION_CONCURRENCY) || 80;
16
+
17
+ const runtimeOptions = {
18
+ region: process.env.FUNCTION_REGION || 'us-central1',
19
+ timeoutSeconds,
20
+ concurrency,
21
+ invoker: 'public',
22
+ secrets: [
23
+ 'SECRET_API_BEARER_TOKEN',
24
+ 'SECRET_REPLICATE_API_TOKEN',
25
+ 'SECRET_OUTPUT_BACKEND',
26
+ 'SECRET_FIREBASE_PROJECT_ID',
27
+ 'SECRET_FIREBASE_STORAGE_BUCKET'
28
+ ]
29
+ };
30
+
31
+ if (minInstances !== undefined) {
32
+ runtimeOptions.minInstances = minInstances;
33
+ }
34
+
35
+ let app;
36
+
37
+ function getApiApp() {
38
+ if (!app) {
39
+ app = createApiApp();
40
+ }
41
+ return app;
42
+ }
43
+
44
+ export const api = onRequest(runtimeOptions, (req, res) => {
45
+ getApiApp()(req, res);
46
+ });
@@ -0,0 +1,70 @@
1
+ import crypto from 'crypto';
2
+ import { env } from '../../config/env.js';
3
+
4
+ function unauthorized(res) {
5
+ res.status(401).json({ error: 'Unauthorized' });
6
+ }
7
+
8
+ function getTokenFromAuthorizationHeader(req) {
9
+ const authHeader = req.get('authorization');
10
+ if (!authHeader) {
11
+ return '';
12
+ }
13
+
14
+ const match = authHeader.match(/^Bearer\s+(.+)$/i);
15
+ if (!match) {
16
+ return '';
17
+ }
18
+
19
+ return String(match[1] || '').trim();
20
+ }
21
+
22
+ function isMatchingToken(providedToken) {
23
+ const expectedToken = env.apiBearerToken;
24
+ if (!providedToken || !expectedToken) {
25
+ return false;
26
+ }
27
+
28
+ const providedBuffer = Buffer.from(providedToken, 'utf8');
29
+ const expectedBuffer = Buffer.from(expectedToken, 'utf8');
30
+
31
+ if (providedBuffer.length !== expectedBuffer.length) {
32
+ return false;
33
+ }
34
+
35
+ return crypto.timingSafeEqual(providedBuffer, expectedBuffer);
36
+ }
37
+
38
+ export function isAuthorizedApiRequest(req, { allowQueryAccessToken = false } = {}) {
39
+ const headerToken = getTokenFromAuthorizationHeader(req);
40
+ if (isMatchingToken(headerToken)) {
41
+ return true;
42
+ }
43
+
44
+ if (!allowQueryAccessToken) {
45
+ return false;
46
+ }
47
+
48
+ const queryToken = typeof req.query?.access_token === 'string'
49
+ ? req.query.access_token.trim()
50
+ : '';
51
+ return isMatchingToken(queryToken);
52
+ }
53
+
54
+ export function requireBearerToken(req, res, next) {
55
+ if (!isAuthorizedApiRequest(req)) {
56
+ unauthorized(res);
57
+ return;
58
+ }
59
+
60
+ next();
61
+ }
62
+
63
+ export function requireBearerTokenOrAccessToken(req, res, next) {
64
+ if (!isAuthorizedApiRequest(req, { allowQueryAccessToken: true })) {
65
+ unauthorized(res);
66
+ return;
67
+ }
68
+
69
+ next();
70
+ }
@@ -0,0 +1,21 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { buildOpenApiSpec } from './spec.js';
5
+
6
+ export async function generateOpenApiFile({
7
+ outputPath = path.resolve('openapi', 'openapi.json'),
8
+ baseUrl = 'http://localhost:3000'
9
+ } = {}) {
10
+ const spec = buildOpenApiSpec({ baseUrl });
11
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
12
+ await fs.writeFile(outputPath, `${JSON.stringify(spec, null, 2)}\n`, 'utf8');
13
+ return outputPath;
14
+ }
15
+
16
+ const entryPoint = pathToFileURL(process.argv[1]).href;
17
+
18
+ if (import.meta.url === entryPoint) {
19
+ const outputPath = await generateOpenApiFile();
20
+ console.log(`OpenAPI generated at ${outputPath}`);
21
+ }