@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,88 @@
1
+ import express from 'express';
2
+ import { pathToFileURL } from 'node:url';
3
+ import { assertRequiredApiEnv, env } from '../config/env.js';
4
+ import { createJobsRouter } from './routes/jobs.js';
5
+ import { createProjectAssetsRouter } from './routes/projectAssets.js';
6
+ import { createProjectsRouter } from './routes/projects.js';
7
+ import { createWebhookRouter } from './routes/webhooks.js';
8
+ import { requireBearerToken, requireBearerTokenOrAccessToken } from './middleware/auth.js';
9
+ import { buildOpenApiSpec } from './openapi/spec.js';
10
+
11
+ const swaggerUiHtml = `<!doctype html>
12
+ <html lang="en">
13
+ <head>
14
+ <meta charset="utf-8" />
15
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
16
+ <title>Rilo API Docs</title>
17
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
18
+ </head>
19
+ <body>
20
+ <div id="swagger-ui"></div>
21
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
22
+ <script>
23
+ window.ui = SwaggerUIBundle({
24
+ url: '/openapi.json',
25
+ dom_id: '#swagger-ui'
26
+ });
27
+ </script>
28
+ </body>
29
+ </html>`;
30
+
31
+ function getRequestBaseUrl(req, { fallbackPort = 3000 } = {}) {
32
+ const forwardedProtoHeader = req.get('x-forwarded-proto');
33
+ const hostHeader = req.get('x-forwarded-host') || req.get('host');
34
+ const protocol = forwardedProtoHeader
35
+ ? forwardedProtoHeader.split(',')[0].trim()
36
+ : req.protocol || 'http';
37
+
38
+ if (hostHeader) {
39
+ return `${protocol}://${hostHeader}`;
40
+ }
41
+
42
+ return `http://localhost:${fallbackPort}`;
43
+ }
44
+
45
+ export function createApiApp({ baseUrl } = {}) {
46
+ assertRequiredApiEnv();
47
+
48
+ const app = express();
49
+ app.set('trust proxy', true);
50
+ app.use(express.json({ limit: '2mb' }));
51
+
52
+ app.get('/health', (_req, res) => {
53
+ res.json({ ok: true, service: 'rilo' });
54
+ });
55
+
56
+ app.get('/openapi.json', (req, res) => {
57
+ const resolvedBaseUrl =
58
+ typeof baseUrl === 'string' && baseUrl.trim().length > 0
59
+ ? baseUrl
60
+ : getRequestBaseUrl(req, { fallbackPort: env.port });
61
+ const openApiSpec = buildOpenApiSpec({ baseUrl: resolvedBaseUrl });
62
+ res.json(openApiSpec);
63
+ });
64
+
65
+ app.get('/docs', (_req, res) => {
66
+ res.type('html').send(swaggerUiHtml);
67
+ });
68
+
69
+ app.use('/webhooks', createWebhookRouter());
70
+ app.use('/projects', requireBearerTokenOrAccessToken, createProjectAssetsRouter());
71
+ app.use(requireBearerToken);
72
+ app.use('/jobs', createJobsRouter());
73
+ app.use('/projects', createProjectsRouter());
74
+
75
+ return app;
76
+ }
77
+
78
+ export function startApiServer({ port = env.port, baseUrl } = {}) {
79
+ const app = createApiApp({ baseUrl });
80
+ return app.listen(port, () => {
81
+ console.log(`rilo api listening on :${port}`);
82
+ });
83
+ }
84
+
85
+ const entryPoint = process.argv[1] ? pathToFileURL(process.argv[1]).href : '';
86
+ if (import.meta.url === entryPoint) {
87
+ startApiServer();
88
+ }
@@ -0,0 +1,57 @@
1
+ import { env } from '../config/env.js';
2
+
3
+ let initialized = false;
4
+ let admin;
5
+ let db;
6
+ let bucket;
7
+
8
+ export async function getFirebaseClients(options = {}) {
9
+ const envOverride = options.env || env;
10
+ const importFirebaseAdmin = options.importFirebaseAdmin || (async () => import('firebase-admin'));
11
+
12
+ if (initialized) {
13
+ return { admin, db, bucket };
14
+ }
15
+
16
+ const adminModule = await importFirebaseAdmin();
17
+ admin = adminModule.default;
18
+
19
+ const hasExplicitCredentials =
20
+ envOverride.firebaseProjectId && envOverride.firebaseClientEmail && envOverride.firebasePrivateKey;
21
+
22
+ const appConfig = hasExplicitCredentials
23
+ ? {
24
+ credential: admin.credential.cert({
25
+ projectId: envOverride.firebaseProjectId,
26
+ clientEmail: envOverride.firebaseClientEmail,
27
+ privateKey: envOverride.firebasePrivateKey.replace(/\\n/g, '\n')
28
+ }),
29
+ storageBucket: envOverride.firebaseStorageBucket || undefined
30
+ }
31
+ : {
32
+ credential: admin.credential.applicationDefault(),
33
+ storageBucket: envOverride.firebaseStorageBucket || undefined
34
+ };
35
+
36
+ admin.initializeApp(appConfig);
37
+ db = admin.firestore();
38
+ bucket = admin.storage().bucket();
39
+ initialized = true;
40
+
41
+ return { admin, db, bucket };
42
+ }
43
+
44
+ export function __resetFirebaseClientsForTests() {
45
+ initialized = false;
46
+ admin = undefined;
47
+ db = undefined;
48
+ bucket = undefined;
49
+ }
50
+
51
+ export function buildStorageHttpUrl(bucketName, objectPath) {
52
+ const encodedPath = objectPath
53
+ .split('/')
54
+ .map((segment) => encodeURIComponent(segment))
55
+ .join('/');
56
+ return `https://storage.googleapis.com/${bucketName}/${encodedPath}`;
57
+ }
@@ -0,0 +1,186 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { env } from '../config/env.js';
4
+ import { logError, logInfo } from '../observability/logger.js';
5
+ import { buildStorageHttpUrl, getFirebaseClients } from './firebaseClient.js';
6
+ import { writeProjectSync } from '../store/projectStore.js';
7
+
8
+ async function pathExists(targetPath) {
9
+ try {
10
+ await fs.access(targetPath);
11
+ return true;
12
+ } catch {
13
+ return false;
14
+ }
15
+ }
16
+
17
+ async function listProjectFiles(projectDir) {
18
+ const files = [];
19
+
20
+ async function walk(currentDir) {
21
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
22
+ for (const entry of entries) {
23
+ const fullPath = path.join(currentDir, entry.name);
24
+ if (entry.isDirectory()) {
25
+ await walk(fullPath);
26
+ } else {
27
+ files.push(fullPath);
28
+ }
29
+ }
30
+ }
31
+
32
+ if (await pathExists(projectDir)) {
33
+ await walk(projectDir);
34
+ }
35
+
36
+ return files;
37
+ }
38
+
39
+ export class LocalOutputBackend {
40
+ async syncProjectSnapshot({ project }) {
41
+ await writeProjectSync(project, {
42
+ backend: 'local',
43
+ syncedAt: new Date().toISOString()
44
+ });
45
+ }
46
+ }
47
+
48
+ export class FirebaseOutputBackend {
49
+ constructor(options = {}) {
50
+ this.db = null;
51
+ this.bucket = null;
52
+ this.getFirebaseClientsFn = options.getFirebaseClientsFn || getFirebaseClients;
53
+ this.buildStorageHttpUrlFn = options.buildStorageHttpUrlFn || buildStorageHttpUrl;
54
+ this.writeProjectSyncFn = options.writeProjectSyncFn || writeProjectSync;
55
+ this.logInfoFn = options.logInfoFn || logInfo;
56
+ }
57
+
58
+ async ensureInitialized() {
59
+ if (this.db && this.bucket) {
60
+ return;
61
+ }
62
+
63
+ const clients = await this.getFirebaseClientsFn();
64
+ this.db = clients.db;
65
+ this.bucket = clients.bucket;
66
+ }
67
+
68
+ async syncProjectSnapshot({ project, projectDir }) {
69
+ await this.ensureInitialized();
70
+
71
+ const files = await listProjectFiles(projectDir);
72
+ const assetRefs = [];
73
+ for (const filePath of files) {
74
+ const relativePath = path.relative(projectDir, filePath).split(path.sep).join('/');
75
+ const destination = `projects/${project}/${relativePath}`;
76
+ await this.bucket.upload(filePath, { destination });
77
+ assetRefs.push({
78
+ relativePath,
79
+ storagePath: destination,
80
+ url: this.buildStorageHttpUrlFn(this.bucket.name, destination)
81
+ });
82
+ }
83
+
84
+ const runStatePath = path.join(projectDir, 'run-state.json');
85
+ const artifactsPath = path.join(projectDir, 'artifacts.json');
86
+ const configPath = path.join(projectDir, 'config.json');
87
+ const storyPath = path.join(projectDir, 'story.md');
88
+
89
+ const projectRef = this.db.collection('projects').doc(project);
90
+ await projectRef.set(
91
+ {
92
+ project,
93
+ backend: 'firebase',
94
+ syncedAt: new Date().toISOString()
95
+ },
96
+ { merge: true }
97
+ );
98
+
99
+ if (await pathExists(runStatePath)) {
100
+ const runState = JSON.parse(await fs.readFile(runStatePath, 'utf8'));
101
+ await projectRef.collection('documents').doc('run-state').set(runState, { merge: true });
102
+ }
103
+
104
+ if (await pathExists(artifactsPath)) {
105
+ const artifacts = JSON.parse(await fs.readFile(artifactsPath, 'utf8'));
106
+ await projectRef.collection('documents').doc('artifacts').set(artifacts, { merge: true });
107
+ }
108
+
109
+ if (await pathExists(configPath)) {
110
+ const config = JSON.parse(await fs.readFile(configPath, 'utf8'));
111
+ await projectRef.collection('documents').doc('config').set(config, { merge: true });
112
+ }
113
+
114
+ if (await pathExists(storyPath)) {
115
+ const story = await fs.readFile(storyPath, 'utf8');
116
+ await projectRef.collection('documents').doc('story').set({ markdown: story }, { merge: true });
117
+ }
118
+
119
+ await projectRef.collection('documents').doc('assets-manifest').set(
120
+ {
121
+ refs: assetRefs,
122
+ syncedAt: new Date().toISOString()
123
+ },
124
+ { merge: true }
125
+ );
126
+
127
+ await projectRef.collection('documents').doc('sync').set(
128
+ {
129
+ backend: 'firebase',
130
+ syncedAt: new Date().toISOString(),
131
+ uploadedFiles: files.length
132
+ },
133
+ { merge: true }
134
+ );
135
+
136
+ await this.writeProjectSyncFn(project, {
137
+ backend: 'firebase',
138
+ syncedAt: new Date().toISOString(),
139
+ uploadedFiles: files.length
140
+ });
141
+
142
+ this.logInfoFn('firebase_sync_completed', { project, uploadedFiles: files.length });
143
+ }
144
+ }
145
+
146
+ let backend;
147
+
148
+ export function getOutputBackend(options = {}) {
149
+ const backendType = options.backendType || env.outputBackend;
150
+
151
+ if (backend) {
152
+ return backend;
153
+ }
154
+
155
+ if (backendType === 'firebase') {
156
+ backend = new FirebaseOutputBackend({
157
+ getFirebaseClientsFn: options.getFirebaseClientsFn,
158
+ buildStorageHttpUrlFn: options.buildStorageHttpUrlFn,
159
+ writeProjectSyncFn: options.writeProjectSyncFn,
160
+ logInfoFn: options.logInfoFn
161
+ });
162
+ } else {
163
+ backend = new LocalOutputBackend();
164
+ }
165
+
166
+ return backend;
167
+ }
168
+
169
+ export function __resetOutputBackendForTests() {
170
+ backend = undefined;
171
+ }
172
+
173
+ export async function syncProjectSnapshot(payload, options = {}) {
174
+ const outputBackend = options.outputBackend || getOutputBackend(options);
175
+ const logErrorFn = options.logErrorFn || logError;
176
+ try {
177
+ await outputBackend.syncProjectSnapshot(payload);
178
+ } catch (error) {
179
+ logErrorFn('output_backend_sync_failed', {
180
+ backend: options.backendType || env.outputBackend,
181
+ project: payload.project,
182
+ error: error.message
183
+ });
184
+ throw error;
185
+ }
186
+ }