@telepat/rilo 0.1.0 → 0.1.6
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/README.md +31 -16
- package/frontend/dist/assets/index-BvNWHzrr.js +13 -0
- package/frontend/dist/assets/index-Wcsyfv3q.css +1 -0
- package/frontend/dist/favicon.svg +19 -0
- package/frontend/dist/index.html +14 -0
- package/frontend/dist/talefire-logo-dark.svg +34 -0
- package/frontend/dist/talefire-logo-light.svg +36 -0
- package/package.json +3 -1
- package/src/api/middleware/auth.js +67 -11
- package/src/api/openapi/spec.js +3 -1
- package/src/api/routes/jobs.js +21 -2
- package/src/api/routes/projects.js +16 -1
- package/src/api/server.js +42 -8
- package/src/cli/commands/preview.js +126 -0
- package/src/cli/index.js +41 -2
- package/src/pipeline/orchestrator.js +17 -1
- package/src/store/projectStore.js +5 -0
- package/src/types/job.js +2 -1
package/src/cli/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '../store/projectStore.js';
|
|
15
15
|
import { openSettings } from './commands/settingsFlow.js';
|
|
16
16
|
import { openHome } from './commands/openHome.js';
|
|
17
|
+
import { startPreview } from './commands/preview.js';
|
|
17
18
|
import { applyStoredSettings } from '../config/env.js';
|
|
18
19
|
|
|
19
20
|
function getArg(flag) {
|
|
@@ -50,6 +51,7 @@ Rilo — Story-first vertical video generation
|
|
|
50
51
|
|
|
51
52
|
USAGE
|
|
52
53
|
rilo --project <name> [--story-file <path>] [--force]
|
|
54
|
+
rilo preview [--port <n>] [--host <host>] [--no-open] [--expose --unsafe-no-auth]
|
|
53
55
|
rilo settings
|
|
54
56
|
rilo home
|
|
55
57
|
rilo --help
|
|
@@ -62,6 +64,7 @@ COMMANDS
|
|
|
62
64
|
--project <name> Project identifier (required); creates projects/<name>/
|
|
63
65
|
--story-file <path> Path to story text file (required on first run)
|
|
64
66
|
--force Force restart from earlier stages (use after config changes)
|
|
67
|
+
--full-run Skip the pause after keyframe generation and run all the way through
|
|
65
68
|
|
|
66
69
|
rilo settings
|
|
67
70
|
Configure API tokens, timeouts, and binary paths interactively
|
|
@@ -69,9 +72,19 @@ COMMANDS
|
|
|
69
72
|
rilo home
|
|
70
73
|
Open ~/.rilo, the default home for projects and output files
|
|
71
74
|
|
|
75
|
+
rilo preview [--port <n>] [--host <host>] [--no-open] [--expose --unsafe-no-auth]
|
|
76
|
+
Start API, worker, and dashboard preview for local use
|
|
77
|
+
|
|
78
|
+
--port <n> API/dashboard port (default: 3000)
|
|
79
|
+
--host <host> Host bind address (default: 127.0.0.1)
|
|
80
|
+
--no-open Do not auto-open browser
|
|
81
|
+
--expose Bind preview for external/container access
|
|
82
|
+
--unsafe-no-auth Allow unauthenticated exposed preview (required with --expose)
|
|
83
|
+
|
|
72
84
|
FLAGS
|
|
73
85
|
--help Show this help message
|
|
74
86
|
--version Show version information
|
|
87
|
+
--full-run Skip the keyframe review pause and run all pipeline stages
|
|
75
88
|
|
|
76
89
|
EXAMPLES
|
|
77
90
|
# First run: create project and generate
|
|
@@ -89,6 +102,12 @@ EXAMPLES
|
|
|
89
102
|
# Open the default Rilo home folder
|
|
90
103
|
rilo home
|
|
91
104
|
|
|
105
|
+
# Run local dashboard/API preview
|
|
106
|
+
rilo preview
|
|
107
|
+
|
|
108
|
+
# Expose preview over container or tunnel (unsafe)
|
|
109
|
+
rilo preview --expose --unsafe-no-auth --host 0.0.0.0 --port 3000
|
|
110
|
+
|
|
92
111
|
# Using npx (no installation needed)
|
|
93
112
|
npx @telepat/rilo --project wedding-case --story-file ./story.txt
|
|
94
113
|
npx @telepat/rilo home
|
|
@@ -115,6 +134,9 @@ SETTINGS
|
|
|
115
134
|
Configure via interactive menu:
|
|
116
135
|
rilo settings
|
|
117
136
|
|
|
137
|
+
Recommended install:
|
|
138
|
+
npm install -g @telepat/rilo
|
|
139
|
+
|
|
118
140
|
Or with environment variables:
|
|
119
141
|
export RILO_REPLICATE_API_TOKEN=r8_xxxxx
|
|
120
142
|
export RILO_MAX_RETRIES=5
|
|
@@ -129,7 +151,7 @@ SETTINGS
|
|
|
129
151
|
INVOCATION METHODS
|
|
130
152
|
Global install: rilo --project <name> --story-file <path>
|
|
131
153
|
No install (npx): npx @telepat/rilo --project <name> --story-file <path>
|
|
132
|
-
|
|
154
|
+
Contributor workflow: npm run dev -- --project <name> --story-file <path>
|
|
133
155
|
`;
|
|
134
156
|
console.log(helpText);
|
|
135
157
|
return;
|
|
@@ -146,6 +168,12 @@ INVOCATION METHODS
|
|
|
146
168
|
return;
|
|
147
169
|
}
|
|
148
170
|
|
|
171
|
+
if (process.argv[2] === 'preview') {
|
|
172
|
+
await applyStoredSettings();
|
|
173
|
+
await startPreview();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
149
177
|
// Merge stored settings (config.json + keystore) into env before running
|
|
150
178
|
await applyStoredSettings();
|
|
151
179
|
|
|
@@ -156,6 +184,7 @@ INVOCATION METHODS
|
|
|
156
184
|
|
|
157
185
|
const project = resolveProjectName(projectArg);
|
|
158
186
|
const forceRestart = hasFlag('--force');
|
|
187
|
+
const fullRun = hasFlag('--full-run');
|
|
159
188
|
const storyFile = getArg('--story-file');
|
|
160
189
|
|
|
161
190
|
await ensureProject(project);
|
|
@@ -173,7 +202,17 @@ INVOCATION METHODS
|
|
|
173
202
|
}
|
|
174
203
|
|
|
175
204
|
const job = createJob({ story, project });
|
|
176
|
-
const result = await runPipeline(job.id, { forceRestart });
|
|
205
|
+
const result = await runPipeline(job.id, { forceRestart, pauseAfterKeyframes: !fullRun });
|
|
206
|
+
|
|
207
|
+
if (result.status === 'paused') {
|
|
208
|
+
console.log(JSON.stringify({
|
|
209
|
+
jobId: result.id,
|
|
210
|
+
project,
|
|
211
|
+
status: 'paused',
|
|
212
|
+
message: `Keyframes generated. Review assets in projects/${project}/assets/, then run again to continue to video generation.`
|
|
213
|
+
}, null, 2));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
177
216
|
|
|
178
217
|
if (result.status !== 'completed') {
|
|
179
218
|
throw new Error(`Generation failed: ${result.error || 'unknown error'}`);
|
|
@@ -571,9 +571,10 @@ export async function regenerateProjectAsset(projectName, target, options = {})
|
|
|
571
571
|
}
|
|
572
572
|
|
|
573
573
|
const updatedAt = new Date().toISOString();
|
|
574
|
+
const nextStatus = targetType === 'keyframe' ? JobStatus.PAUSED : (runState.status || JobStatus.COMPLETED);
|
|
574
575
|
await deps.persistArtifacts(project, artifacts);
|
|
575
576
|
await deps.writeProjectRunState(project, {
|
|
576
|
-
status:
|
|
577
|
+
status: nextStatus,
|
|
577
578
|
error: runState.error || null,
|
|
578
579
|
steps,
|
|
579
580
|
artifacts,
|
|
@@ -629,6 +630,9 @@ export async function runPipeline(jobId, options = {}) {
|
|
|
629
630
|
const projectDir = deps.getProjectDir(project);
|
|
630
631
|
const targetDurationSec = resolveTargetDurationSec(projectConfig);
|
|
631
632
|
const plannedShots = resolveShotCount(projectConfig);
|
|
633
|
+
const shouldPause = options.pauseAfterKeyframes !== undefined
|
|
634
|
+
? Boolean(options.pauseAfterKeyframes)
|
|
635
|
+
: Boolean(projectConfig.pauseAfterKeyframes);
|
|
632
636
|
const safeStory = deps.preprocessStory(job.payload.story);
|
|
633
637
|
analyticsProjectDir = projectDir;
|
|
634
638
|
const analyticsRunId = deps.createRunId();
|
|
@@ -1326,6 +1330,12 @@ export async function runPipeline(jobId, options = {}) {
|
|
|
1326
1330
|
await finishStageAnalytics(JobStep.KEYFRAMES, { executed: true, status: 'succeeded', details: { mode: 'partial_regen', changedShots: changed } });
|
|
1327
1331
|
await finishStageAnalytics(JobStep.SEGMENTS, { executed: true, status: 'succeeded', details: { mode: 'partial_regen' } });
|
|
1328
1332
|
currentJob = getJob(jobId);
|
|
1333
|
+
|
|
1334
|
+
if (shouldPause) {
|
|
1335
|
+
const paused = updateJob(jobId, { status: JobStatus.PAUSED });
|
|
1336
|
+
await persistCheckpoint(paused, null, deps);
|
|
1337
|
+
return paused;
|
|
1338
|
+
}
|
|
1329
1339
|
}
|
|
1330
1340
|
|
|
1331
1341
|
if (!currentJob.steps[JobStep.KEYFRAMES]) {
|
|
@@ -1387,6 +1397,12 @@ export async function runPipeline(jobId, options = {}) {
|
|
|
1387
1397
|
await persistCheckpoint(getJob(jobId), null, deps);
|
|
1388
1398
|
await finishStageAnalytics(JobStep.KEYFRAMES, { executed: true, status: 'succeeded' });
|
|
1389
1399
|
currentJob = getJob(jobId);
|
|
1400
|
+
|
|
1401
|
+
if (shouldPause) {
|
|
1402
|
+
const paused = updateJob(jobId, { status: JobStatus.PAUSED });
|
|
1403
|
+
await persistCheckpoint(paused, null, deps);
|
|
1404
|
+
return paused;
|
|
1405
|
+
}
|
|
1390
1406
|
} else if (
|
|
1391
1407
|
currentJob.artifacts.keyframeUrls.length > 0 &&
|
|
1392
1408
|
(!currentJob.artifacts.keyframePaths || currentJob.artifacts.keyframePaths.length === 0)
|
|
@@ -53,6 +53,7 @@ export const DEFAULT_PROJECT_CONFIG = {
|
|
|
53
53
|
aspectRatio: '9:16',
|
|
54
54
|
targetDurationSec: DEFAULT_VIDEO_CONFIG.durationSec,
|
|
55
55
|
finalDurationMode: 'match_audio',
|
|
56
|
+
pauseAfterKeyframes: true,
|
|
56
57
|
subtitleOptions: {
|
|
57
58
|
...DEFAULT_SUBTITLE_OPTIONS
|
|
58
59
|
},
|
|
@@ -513,6 +514,10 @@ export function validateProjectConfig(config) {
|
|
|
513
514
|
|
|
514
515
|
validateSubtitleOptions(config.subtitleOptions);
|
|
515
516
|
|
|
517
|
+
if (typeof config.pauseAfterKeyframes !== 'boolean') {
|
|
518
|
+
throw new Error('Invalid project config: pauseAfterKeyframes must be a boolean');
|
|
519
|
+
}
|
|
520
|
+
|
|
516
521
|
const hasWidth = config.keyframeWidth !== undefined;
|
|
517
522
|
const hasHeight = config.keyframeHeight !== undefined;
|
|
518
523
|
if (hasWidth !== hasHeight) {
|