@vorec/cli 0.11.0 → 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.
- package/README.md +2 -3
- package/dist/commands/run.d.ts +0 -1
- package/dist/commands/run.js +35 -57
- package/dist/index.js +3 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,9 +49,8 @@ Create a `vorec.json` file describing the actions to record:
|
|
|
49
49
|
| Command | What it does |
|
|
50
50
|
|---------|-------------|
|
|
51
51
|
| `vorec init` | Save your API key |
|
|
52
|
-
| `vorec run <manifest>` | Record and
|
|
53
|
-
| `vorec run <manifest> --
|
|
54
|
-
| `vorec run <manifest> --auto --skip-record` | Resume an existing project |
|
|
52
|
+
| `vorec run <manifest>` | Record, upload, and generate narrated tutorial |
|
|
53
|
+
| `vorec run <manifest> --skip-record --video <file>` | Use existing video instead of recording |
|
|
55
54
|
| `vorec upload <video>` | Upload a video file directly |
|
|
56
55
|
| `vorec status` | Check project status |
|
|
57
56
|
|
package/dist/commands/run.d.ts
CHANGED
package/dist/commands/run.js
CHANGED
|
@@ -3,27 +3,12 @@ import ora from 'ora';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { loadManifest } from '../manifest.js';
|
|
5
5
|
import { apiCall, uploadVideo } from '../api.js';
|
|
6
|
-
import { saveProjectState
|
|
6
|
+
import { saveProjectState } from '../config.js';
|
|
7
7
|
export async function runCommand(manifestPath, opts) {
|
|
8
8
|
const manifest = loadManifest(manifestPath);
|
|
9
9
|
console.log(chalk.bold(`Vorec: ${manifest.title}`));
|
|
10
10
|
console.log(`Actions: ${manifest.actions.length}, URL: ${manifest.url}\n`);
|
|
11
|
-
//
|
|
12
|
-
const savedState = loadProjectState();
|
|
13
|
-
const resuming = opts.skipRecord && savedState?.projectId && savedState?.status === 'analyzing';
|
|
14
|
-
if (resuming) {
|
|
15
|
-
// Resume: skip recording + upload, go straight to analysis
|
|
16
|
-
console.log(`Resuming project: ${savedState.projectId}`);
|
|
17
|
-
if (opts.auto) {
|
|
18
|
-
await runAnalysis(savedState.projectId, manifest);
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
const status = await apiCall('get-status', { projectId: savedState.projectId });
|
|
22
|
-
console.log(`\n${chalk.bold('Editor:')} ${status.editor_url}`);
|
|
23
|
-
}
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
// Fresh run: record → upload → (optionally analyze)
|
|
11
|
+
// Record → upload → generate
|
|
27
12
|
let videoPath = opts.video;
|
|
28
13
|
let trackedActions = [];
|
|
29
14
|
let videoDuration = 0;
|
|
@@ -107,57 +92,50 @@ export async function runCommand(manifestPath, opts) {
|
|
|
107
92
|
status: 'analyzing',
|
|
108
93
|
});
|
|
109
94
|
spinner.succeed(`Project created: ${createResult.projectId}`);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
console.log(`\nVideo uploaded. Run with --auto to continue processing.`);
|
|
113
|
-
console.log(`${chalk.bold('Editor:')} ${status.editor_url}`);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
// Auto mode: continue with analysis
|
|
117
|
-
await runAnalysis(createResult.projectId, manifest);
|
|
118
|
-
}
|
|
119
|
-
async function runAnalysis(projectId, manifest) {
|
|
120
|
-
// Analyze video
|
|
121
|
-
const descSpinner = ora('Analyzing video...').start();
|
|
122
|
-
try {
|
|
123
|
-
const descResult = await apiCall('describe-video', {
|
|
124
|
-
projectId,
|
|
125
|
-
videoContext: manifest.videoContext,
|
|
126
|
-
});
|
|
127
|
-
descSpinner.succeed(`Video analyzed (${descResult.description?.length || 0} chars)`);
|
|
128
|
-
}
|
|
129
|
-
catch (err) {
|
|
130
|
-
descSpinner.fail(`Analysis failed: ${err.message}`);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
// Generate narration
|
|
134
|
-
const narrSpinner = ora('Writing narration...').start();
|
|
95
|
+
// Trigger analysis (runs in background on server)
|
|
96
|
+
const analysisSpinner = ora('Generating narrated tutorial...').start();
|
|
135
97
|
try {
|
|
136
|
-
|
|
137
|
-
projectId,
|
|
98
|
+
await apiCall('generate-video', {
|
|
99
|
+
projectId: createResult.projectId,
|
|
138
100
|
language: manifest.language,
|
|
139
101
|
narrationStyle: manifest.narrationStyle,
|
|
102
|
+
videoContext: manifest.videoContext,
|
|
140
103
|
customPrompt: manifest.customPrompt,
|
|
141
104
|
includeIntro: false,
|
|
142
105
|
});
|
|
143
|
-
narrSpinner.succeed(`${narrResult.segments_created} narration segments`);
|
|
144
|
-
if (narrResult.segments) {
|
|
145
|
-
for (const seg of narrResult.segments) {
|
|
146
|
-
console.log(chalk.dim(` [${seg.sort_order}] ${seg.timestamp_seconds.toFixed(1)}s — ${seg.action_name}`));
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
106
|
}
|
|
150
107
|
catch (err) {
|
|
151
|
-
|
|
108
|
+
analysisSpinner.fail(`Failed to start analysis: ${err.message}`);
|
|
152
109
|
process.exit(1);
|
|
153
110
|
}
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
111
|
+
// Poll until done
|
|
112
|
+
let attempts = 0;
|
|
113
|
+
while (attempts < 120) {
|
|
114
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
115
|
+
const status = await apiCall('get-status', { projectId: createResult.projectId });
|
|
116
|
+
const segCount = status.segments?.length || 0;
|
|
117
|
+
if (status.status === 'editing' || status.status === 'complete') {
|
|
118
|
+
analysisSpinner.succeed(`Done! ${segCount} narration segments`);
|
|
119
|
+
if (status.segments) {
|
|
120
|
+
for (const seg of status.segments) {
|
|
121
|
+
console.log(chalk.dim(` [${seg.sort_order}] ${Number(seg.timestamp_seconds).toFixed(1)}s — ${seg.action_name}`));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
console.log(`\n${chalk.bold('Editor:')} ${status.editor_url}`);
|
|
125
|
+
console.log(chalk.dim('Open the editor to preview narration and generate audio.'));
|
|
126
|
+
saveProjectState({ projectId: createResult.projectId, status: 'complete' });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (status.status === 'draft' || status.status === 'error') {
|
|
130
|
+
analysisSpinner.fail(`Analysis ended with status: ${status.status}`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
analysisSpinner.text = `Generating narrated tutorial... (${status.status})`;
|
|
134
|
+
attempts++;
|
|
135
|
+
}
|
|
136
|
+
analysisSpinner.fail('Timed out waiting for analysis');
|
|
137
|
+
const finalStatus = await apiCall('get-status', { projectId: createResult.projectId });
|
|
157
138
|
console.log(`${chalk.bold('Editor:')} ${finalStatus.editor_url}`);
|
|
158
|
-
console.log(chalk.dim('Open the editor to preview narration and generate audio.'));
|
|
159
|
-
// Clear state so next run creates a fresh project
|
|
160
|
-
saveProjectState({ projectId, status: 'complete' });
|
|
161
139
|
}
|
|
162
140
|
async function recordAndTrack(manifest) {
|
|
163
141
|
let pw;
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { statusCommand } from './commands/status.js';
|
|
|
7
7
|
const program = new Command()
|
|
8
8
|
.name('vorec')
|
|
9
9
|
.description('Turn screen recordings into narrated tutorials')
|
|
10
|
-
.version('0.
|
|
10
|
+
.version('1.0.0');
|
|
11
11
|
program
|
|
12
12
|
.command('init')
|
|
13
13
|
.description('Save your Vorec API key')
|
|
@@ -27,9 +27,8 @@ program
|
|
|
27
27
|
.action(uploadCommand);
|
|
28
28
|
program
|
|
29
29
|
.command('run <manifest>')
|
|
30
|
-
.description('
|
|
31
|
-
.option('--
|
|
32
|
-
.option('--skip-record', 'Skip Playwright recording (use --video)', false)
|
|
30
|
+
.description('Record screen, upload, and generate narrated tutorial')
|
|
31
|
+
.option('--skip-record', 'Skip recording, use --video instead', false)
|
|
33
32
|
.option('--video <path>', 'Use existing video file instead of recording')
|
|
34
33
|
.option('--tracked-actions <path>', 'JSON file with tracked actions (for --skip-record)')
|
|
35
34
|
.action(runCommand);
|