@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/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
- Local development: npm run dev -- --project <name> --story-file <path>
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: runState.status || JobStatus.COMPLETED,
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) {
package/src/types/job.js CHANGED
@@ -2,7 +2,8 @@ export const JobStatus = {
2
2
  PENDING: 'pending',
3
3
  RUNNING: 'running',
4
4
  FAILED: 'failed',
5
- COMPLETED: 'completed'
5
+ COMPLETED: 'completed',
6
+ PAUSED: 'paused'
6
7
  };
7
8
 
8
9
  export const JobStep = {