@scenerok/cli 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 ADDED
@@ -0,0 +1,115 @@
1
+ # SceneRok CLI
2
+
3
+ Create videos from your terminal and agent workflows.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @scenerok/cli
9
+ ```
10
+
11
+ Or use with `npx`:
12
+ ```bash
13
+ npx @scenerok/cli auth login
14
+ ```
15
+
16
+ ## Quick Start
17
+
18
+ ```bash
19
+ # Authenticate
20
+ scenerok auth login
21
+
22
+ # Validate a VidScript
23
+ scenerok validate video.vidscript
24
+
25
+ # Render a video
26
+ scenerok render video.vidscript --resolution 1080x1920 --watch
27
+
28
+ # Check render status
29
+ scenerok status 42
30
+ ```
31
+
32
+ ## Commands
33
+
34
+ ### `auth`
35
+
36
+ ```bash
37
+ scenerok auth login # Log in via browser
38
+ reelforge auth status # Check auth status
39
+ reelforge auth logout # Remove credentials
40
+ ```
41
+
42
+ ### `skills`
43
+
44
+ ```bash
45
+ scenerok skills install <platform> # Install skills for your agent
46
+ scenerok skills list # List available platforms
47
+ ```
48
+
49
+ Supported platforms: `opencode`, `claude`, `codex`, `cursor`, `aider`
50
+
51
+ ### `validate`
52
+
53
+ ```bash
54
+ scenerok validate <file.vidscript>
55
+ ```
56
+
57
+ Validates VidScript syntax and returns scene count, duration, and warnings.
58
+
59
+ ### `render`
60
+
61
+ ```bash
62
+ scenerok render <file.vidscript> [options]
63
+ ```
64
+
65
+ Options:
66
+ - `-r, --resolution <resolution>` - Output resolution (e.g. 1080x1920)
67
+ - `-w, --watch` - Watch render status until complete
68
+
69
+ ### `status`
70
+
71
+ ```bash
72
+ scenerok status <render-id>
73
+ ```
74
+
75
+ Check the status of a render job.
76
+
77
+ ## Configuration
78
+
79
+ Config is stored in `~/.scenerok/config.json`:
80
+
81
+ ```json
82
+ {
83
+ "apiToken": "rf_...",
84
+ "baseUrl": "https://scenerok.com"
85
+ }
86
+ ```
87
+
88
+ ## VidScript
89
+
90
+ VidScript is a declarative language for describing video compositions.
91
+
92
+ ```vidscript
93
+ config {
94
+ resolution = "1080x1920";
95
+ fps = 30;
96
+ output = "video.mp4";
97
+ }
98
+
99
+ scene "intro" {
100
+ duration = 3;
101
+ background { color = "#FF5733"; }
102
+ text "title" {
103
+ content = "Hello World";
104
+ font = "Inter Bold";
105
+ size = 72;
106
+ x = 50%; y = 50%;
107
+ align = "center";
108
+ }
109
+ }
110
+ ```
111
+
112
+ ## Links
113
+
114
+ - Website: https://scenerok.com
115
+ - Docs: https://scenerok.com/agents/get-started.md
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const authCommand: Command;
3
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,WAAW,SA6ErB,CAAC"}
@@ -0,0 +1,69 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { readConfig, writeConfig, isAuthenticated } from '../lib/config.js';
5
+ import { initiateDeviceAuth, pollDeviceAuth } from '../lib/api.js';
6
+ export const authCommand = new Command('auth')
7
+ .description('Authenticate with SceneRok')
8
+ .addCommand(new Command('login')
9
+ .description('Log in to SceneRok via browser')
10
+ .option('-n, --name <name>', 'Device name', 'CLI')
11
+ .action(async (options) => {
12
+ console.log(chalk.cyan('\\n🔐 SceneRok Authentication\\n'));
13
+ const spinner = ora('Initiating device auth...').start();
14
+ try {
15
+ const auth = await initiateDeviceAuth(options.name);
16
+ spinner.succeed('Device auth initiated');
17
+ console.log(chalk.yellow('\\n1. Open this URL in your browser:'));
18
+ console.log(chalk.underline(auth.verification_uri));
19
+ console.log('');
20
+ console.log(chalk.yellow('2. Or run this command:'));
21
+ console.log(` open "${auth.verification_uri}"`);
22
+ console.log('');
23
+ console.log(chalk.dim(`Waiting for authentication... (expires in ${auth.expires_in}s)\\n`));
24
+ const startTime = Date.now();
25
+ const expiresIn = auth.expires_in * 1000;
26
+ while (Date.now() - startTime < expiresIn) {
27
+ await new Promise((resolve) => setTimeout(resolve, auth.interval * 1000));
28
+ const result = await pollDeviceAuth(auth.device_code);
29
+ if (result.status === 'complete' && result.access_token) {
30
+ const config = readConfig();
31
+ config.apiToken = result.access_token;
32
+ writeConfig(config);
33
+ console.log(chalk.green('\\n✅ Successfully authenticated with SceneRok!\\n'));
34
+ console.log(chalk.dim('Your API token has been saved to ~/.scenerok/config.json'));
35
+ return;
36
+ }
37
+ if (result.error === 'expired_token') {
38
+ console.log(chalk.red('\\n❌ Authentication expired. Please try again.\\n'));
39
+ process.exit(1);
40
+ }
41
+ }
42
+ console.log(chalk.red('\\n❌ Authentication timed out. Please try again.\\n'));
43
+ process.exit(1);
44
+ }
45
+ catch (error) {
46
+ spinner.fail('Authentication failed');
47
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
48
+ process.exit(1);
49
+ }
50
+ }))
51
+ .addCommand(new Command('status')
52
+ .description('Check authentication status')
53
+ .action(() => {
54
+ if (isAuthenticated()) {
55
+ console.log(chalk.green('✅ Authenticated with SceneRok'));
56
+ }
57
+ else {
58
+ console.log(chalk.yellow('⚠️ Not authenticated'));
59
+ console.log(chalk.dim('Run: scenerok auth login'));
60
+ }
61
+ }))
62
+ .addCommand(new Command('logout')
63
+ .description('Log out and remove stored credentials')
64
+ .action(() => {
65
+ const config = readConfig();
66
+ delete config.apiToken;
67
+ writeConfig(config);
68
+ console.log(chalk.green('✅ Logged out successfully'));
69
+ }));
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const renderCommand: Command;
3
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/commands/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,aAAa,SAwEtB,CAAC"}
@@ -0,0 +1,108 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { readFileSync } from 'node:fs';
5
+ import { submitRender, getRenderStatus } from '../lib/api.js';
6
+ import { isAuthenticated } from '../lib/config.js';
7
+ export const renderCommand = new Command('render')
8
+ .description('Submit a VidScript for rendering')
9
+ .argument('<file>', 'Path to .vidscript file')
10
+ .option('-r, --resolution <resolution>', 'Output resolution (e.g. 1080x1920)')
11
+ .option('-w, --watch', 'Watch render status until complete')
12
+ .action(async (file, options) => {
13
+ if (!isAuthenticated()) {
14
+ console.log(chalk.yellow('⚠️ Not authenticated'));
15
+ console.log(chalk.dim('Run: scenerok auth login'));
16
+ process.exit(1);
17
+ }
18
+ let vidscript;
19
+ try {
20
+ vidscript = readFileSync(file, 'utf-8');
21
+ }
22
+ catch {
23
+ console.log(chalk.red(`❌ Could not read file: ${file}`));
24
+ process.exit(1);
25
+ }
26
+ const spinner = ora('Submitting render job...').start();
27
+ try {
28
+ const result = await submitRender(vidscript, options.resolution);
29
+ spinner.succeed(`Render job submitted (#${result.renderId})`);
30
+ console.log(chalk.cyan('\\n📹 Render Job\\n'));
31
+ console.log(` ID: ${result.renderId}`);
32
+ console.log(` Status: ${result.status}`);
33
+ console.log(` Resolution: ${result.resolution}`);
34
+ console.log(` Output: ${result.outputFilename}`);
35
+ console.log(` Mode: ${result.dispatchMode}`);
36
+ console.log(` Render URL: ${result.renderUrl}`);
37
+ console.log(` Download: ${result.downloadUrl}`);
38
+ if (result.logUrl) {
39
+ console.log(` Log: ${result.logUrl}`);
40
+ }
41
+ console.log('');
42
+ if (options.watch) {
43
+ console.log(chalk.dim('Watching render status...\\n'));
44
+ while (true) {
45
+ await new Promise((resolve) => setTimeout(resolve, 3000));
46
+ const status = await getRenderStatus(result.renderId);
47
+ if (status.status === 'completed') {
48
+ console.log(chalk.green(`\\n✅ Render complete!\\n`));
49
+ console.log(` Download: ${status.downloadUrl}`);
50
+ console.log('');
51
+ break;
52
+ }
53
+ if (status.status === 'failed') {
54
+ console.log(chalk.red(`\\n❌ Render failed: ${status.error}\\n`));
55
+ process.exit(1);
56
+ }
57
+ process.stdout.write(`\r ${status.status}... ${status.progress}%`);
58
+ }
59
+ }
60
+ else {
61
+ console.log(chalk.dim(`Check status: scenerok status ${result.renderId}`));
62
+ console.log(chalk.dim(`Open render: ${result.renderUrl}`));
63
+ }
64
+ }
65
+ catch (error) {
66
+ spinner.fail('Render submission failed');
67
+ if (error instanceof Error) {
68
+ console.error(chalk.red(error.message));
69
+ }
70
+ process.exit(1);
71
+ }
72
+ });
73
+ renderCommand.addCommand(new Command('status')
74
+ .description('Check render status')
75
+ .argument('<id>', 'Render job ID')
76
+ .action(async (id) => {
77
+ if (!isAuthenticated()) {
78
+ console.log(chalk.yellow('⚠️ Not authenticated'));
79
+ console.log(chalk.dim('Run: scenerok auth login'));
80
+ process.exit(1);
81
+ }
82
+ const renderId = Number.parseInt(id, 10);
83
+ if (Number.isNaN(renderId)) {
84
+ console.log(chalk.red('❌ Invalid render ID'));
85
+ process.exit(1);
86
+ }
87
+ try {
88
+ const status = await getRenderStatus(renderId);
89
+ console.log(chalk.cyan('\\n📹 Render Status\\n'));
90
+ console.log(` ID: ${status.id}`);
91
+ console.log(` Status: ${status.status}`);
92
+ console.log(` Progress: ${status.progress}%`);
93
+ if (status.outputUrl) {
94
+ console.log(` Output: ${status.outputUrl}`);
95
+ }
96
+ if (status.downloadUrl) {
97
+ console.log(` Download: ${status.downloadUrl}`);
98
+ }
99
+ if (status.error) {
100
+ console.log(chalk.red(` Error: ${status.error}`));
101
+ }
102
+ console.log('');
103
+ }
104
+ catch (error) {
105
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
106
+ process.exit(1);
107
+ }
108
+ }));
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const skillsCommand: Command;
3
+ //# sourceMappingURL=skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4FpC,eAAO,MAAM,aAAa,SAqDvB,CAAC"}
@@ -0,0 +1,121 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { existsSync, copyFileSync, mkdirSync } from 'node:fs';
4
+ import { join, dirname } from 'node:path';
5
+ import { homedir } from 'node:os';
6
+ function getPlatformConfig(platform) {
7
+ const cwd = process.cwd();
8
+ const configs = {
9
+ opencode: {
10
+ name: 'OpenCode',
11
+ skillDir: join(homedir(), '.agents/skills/scenerok'),
12
+ files: [
13
+ { source: 'skills/opencode/SKILL.md', dest: 'SKILL.md' },
14
+ { source: 'skills/opencode/vidscript-guide.md', dest: 'vidscript-guide.md' },
15
+ { source: 'skills/opencode/vidscript-sample.md', dest: 'vidscript-sample.md' },
16
+ ],
17
+ },
18
+ claude: {
19
+ name: 'Claude Code',
20
+ skillDir: join(homedir(), '.claude/skills/scenerok'),
21
+ files: [
22
+ { source: 'skills/claude/SKILL.md', dest: 'SKILL.md' },
23
+ { source: 'skills/claude/vidscript-guide.md', dest: 'vidscript-guide.md' },
24
+ { source: 'skills/claude/vidscript-sample.md', dest: 'vidscript-sample.md' },
25
+ ],
26
+ },
27
+ codex: {
28
+ name: 'Codex',
29
+ skillDir: join(homedir(), '.codex/skills/scenerok'),
30
+ files: [
31
+ { source: 'skills/codex/SKILL.md', dest: 'SKILL.md' },
32
+ { source: 'skills/codex/vidscript-guide.md', dest: 'vidscript-guide.md' },
33
+ { source: 'skills/codex/vidscript-sample.md', dest: 'vidscript-sample.md' },
34
+ ],
35
+ },
36
+ cursor: {
37
+ name: 'Cursor',
38
+ skillDir: join(homedir(), '.cursor/skills/scenerok'),
39
+ files: [
40
+ { source: 'skills/cursor/SKILL.md', dest: 'SKILL.md' },
41
+ { source: 'skills/cursor/vidscript-guide.md', dest: 'vidscript-guide.md' },
42
+ { source: 'skills/cursor/vidscript-sample.md', dest: 'vidscript-sample.md' },
43
+ ],
44
+ },
45
+ aider: {
46
+ name: 'Aider',
47
+ skillDir: join(homedir(), '.aider/skills/scenerok'),
48
+ files: [
49
+ { source: 'skills/aider/SKILL.md', dest: 'SKILL.md' },
50
+ { source: 'skills/aider/vidscript-guide.md', dest: 'vidscript-guide.md' },
51
+ { source: 'skills/aider/vidscript-sample.md', dest: 'vidscript-sample.md' },
52
+ ],
53
+ },
54
+ };
55
+ return configs[platform] || null;
56
+ }
57
+ async function findSkillSource(sourcePath) {
58
+ // Check relative to node_modules/@scenerok/cli (scoped package)
59
+ const modulePath = join(dirname(new URL(import.meta.url).pathname), '../../../..', sourcePath);
60
+ if (existsSync(modulePath))
61
+ return modulePath;
62
+ // Check relative to CWD
63
+ const cwdPath = join(process.cwd(), sourcePath);
64
+ if (existsSync(cwdPath))
65
+ return cwdPath;
66
+ // Check global npm root
67
+ try {
68
+ const { execSync } = await import('node:child_process');
69
+ const globalRoot = execSync('npm root -g', { encoding: 'utf-8' }).trim();
70
+ const globalPath = join(globalRoot, '@scenerok', 'cli', sourcePath);
71
+ if (existsSync(globalPath))
72
+ return globalPath;
73
+ }
74
+ catch {
75
+ // ignore
76
+ }
77
+ return null;
78
+ }
79
+ export const skillsCommand = new Command('skills')
80
+ .description('Install SceneRok skills to your agent')
81
+ .addCommand(new Command('install')
82
+ .description('Install skills for a platform')
83
+ .argument('<platform>', 'Agent platform: opencode, claude, codex, cursor, aider')
84
+ .action(async (platform) => {
85
+ const config = getPlatformConfig(platform);
86
+ if (!config) {
87
+ console.log(chalk.red(`❌ Unknown platform: ${platform}`));
88
+ console.log(chalk.dim('Supported platforms: opencode, claude, codex, cursor, aider'));
89
+ process.exit(1);
90
+ }
91
+ console.log(chalk.cyan(`\\n📦 Installing SceneRok skills for ${config.name}...\\n`));
92
+ // Create skill directory
93
+ if (!existsSync(config.skillDir)) {
94
+ mkdirSync(config.skillDir, { recursive: true });
95
+ }
96
+ for (const file of config.files) {
97
+ const source = await findSkillSource(file.source);
98
+ const dest = join(config.skillDir, file.dest);
99
+ if (!source) {
100
+ console.log(chalk.yellow(` ⚠️ Skipping ${file.dest} (source not found)`));
101
+ continue;
102
+ }
103
+ copyFileSync(source, dest);
104
+ console.log(chalk.green(` ✓ ${file.dest}`));
105
+ }
106
+ console.log(chalk.cyan(`\\n✅ Skills installed to ${config.skillDir}\\n`));
107
+ console.log(chalk.dim('Your agent can now compose VidScripts and render videos!'));
108
+ console.log(chalk.dim('Try: Ask your agent to "create a product promo video using scenerok"'));
109
+ }))
110
+ .addCommand(new Command('list')
111
+ .description('List available skill platforms')
112
+ .action(() => {
113
+ console.log(chalk.cyan('\\n📦 Available SceneRok Skill Platforms\\n'));
114
+ console.log(' opencode - OpenCode agent (.agents/skills/)');
115
+ console.log(' claude - Claude Code (.claude/skills/)');
116
+ console.log(' codex - OpenAI Codex (.codex/skills/)');
117
+ console.log(' cursor - Cursor editor (.cursor/skills/)');
118
+ console.log(' aider - Aider (.aider/skills/)');
119
+ console.log('');
120
+ console.log(chalk.dim('Install with: scenerok skills install <platform>'));
121
+ }));
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const validateCommand: Command;
3
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,eAAe,SAqCxB,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { readFileSync } from 'node:fs';
4
+ import { validateVidscript } from '../lib/api.js';
5
+ export const validateCommand = new Command('validate')
6
+ .description('Validate a VidScript file')
7
+ .argument('<file>', 'Path to .vidscript file')
8
+ .action(async (file) => {
9
+ let vidscript;
10
+ try {
11
+ vidscript = readFileSync(file, 'utf-8');
12
+ }
13
+ catch {
14
+ console.log(chalk.red(`❌ Could not read file: ${file}`));
15
+ process.exit(1);
16
+ }
17
+ try {
18
+ const result = await validateVidscript(vidscript);
19
+ if (result.valid) {
20
+ console.log(chalk.green('\\n✅ VidScript is valid\\n'));
21
+ console.log(` Scenes: ${result.scenes}`);
22
+ console.log(` Duration: ${result.duration}s`);
23
+ if (result.warnings && result.warnings.length > 0) {
24
+ console.log(chalk.yellow('\\n Warnings:'));
25
+ for (const warning of result.warnings) {
26
+ console.log(` • ${warning}`);
27
+ }
28
+ }
29
+ }
30
+ else {
31
+ console.log(chalk.red('\\n❌ VidScript is invalid\\n'));
32
+ console.log(` Error: ${result.error}`);
33
+ process.exit(1);
34
+ }
35
+ console.log('');
36
+ }
37
+ catch (error) {
38
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
39
+ process.exit(1);
40
+ }
41
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { authCommand } from './commands/auth.js';
4
+ import { skillsCommand } from './commands/skills.js';
5
+ import { renderCommand } from './commands/render.js';
6
+ import { validateCommand } from './commands/validate.js';
7
+ const program = new Command();
8
+ program
9
+ .name('scenerok')
10
+ .description('SceneRok CLI - Create videos from your terminal and agent workflows')
11
+ .version('1.0.0');
12
+ program.addCommand(authCommand);
13
+ program.addCommand(skillsCommand);
14
+ program.addCommand(renderCommand);
15
+ program.addCommand(validateCommand);
16
+ // Default help
17
+ program.on('--help', () => {
18
+ console.log('');
19
+ console.log(chalk.cyan('Examples:'));
20
+ console.log(' $ scenerok auth login');
21
+ console.log(' $ scenerok skills install opencode');
22
+ console.log(' $ scenerok validate video.vidscript');
23
+ console.log(' $ scenerok render video.vidscript --resolution 1080x1920');
24
+ console.log(' $ scenerok status 42');
25
+ console.log('');
26
+ console.log(chalk.cyan('Get started:'));
27
+ console.log(' https://scenerok.com/agents');
28
+ });
29
+ program.parse();
@@ -0,0 +1,52 @@
1
+ declare class ApiError extends Error {
2
+ status: number;
3
+ responseBody?: unknown | undefined;
4
+ constructor(message: string, status: number, responseBody?: unknown | undefined);
5
+ }
6
+ export declare function validateVidscript(vidscript: string): Promise<{
7
+ valid: boolean;
8
+ scenes?: number;
9
+ duration?: number;
10
+ warnings?: string[];
11
+ error?: string;
12
+ }>;
13
+ export declare function submitRender(vidscript: string, resolution?: string): Promise<{
14
+ renderId: number;
15
+ status: string;
16
+ resolution: string;
17
+ outputFilename: string;
18
+ renderUrl: string;
19
+ downloadUrl: string;
20
+ logUrl?: string;
21
+ dispatchMode: string;
22
+ queueJobId: string | null;
23
+ }>;
24
+ export declare function getRenderStatus(renderId: number): Promise<{
25
+ id: number;
26
+ status: string;
27
+ progress: number;
28
+ outputUrl: string | null;
29
+ outputFilename: string;
30
+ renderUrl: string;
31
+ downloadUrl: string;
32
+ downloadReady: boolean;
33
+ error: string | null;
34
+ createdAt: string;
35
+ completedAt: string | null;
36
+ }>;
37
+ export declare function initiateDeviceAuth(deviceName: string): Promise<{
38
+ device_code: string;
39
+ user_code: string;
40
+ verification_uri: string;
41
+ expires_in: number;
42
+ interval: number;
43
+ }>;
44
+ export declare function pollDeviceAuth(deviceCode: string): Promise<{
45
+ status: string;
46
+ access_token?: string;
47
+ token_type?: string;
48
+ expires_in?: number;
49
+ error?: string;
50
+ }>;
51
+ export { ApiError };
52
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AASA,cAAM,QAAS,SAAQ,KAAK;IAGjB,MAAM,EAAE,MAAM;IACd,YAAY,CAAC,EAAE,OAAO;gBAF7B,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,OAAO,YAAA;CAKhC;AAqCD,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM;WAE9C,OAAO;aACL,MAAM;eACJ,MAAM;eACN,MAAM,EAAE;YACX,MAAM;GAEjB;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;cAE3D,MAAM;YACR,MAAM;gBACF,MAAM;oBACF,MAAM;eACX,MAAM;iBACJ,MAAM;aACV,MAAM;kBACD,MAAM;gBACR,MAAM,GAAG,IAAI;GAE5B;AAED,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM;QAE9C,MAAM;YACF,MAAM;cACJ,MAAM;eACL,MAAM,GAAG,IAAI;oBACR,MAAM;eACX,MAAM;iBACJ,MAAM;mBACJ,OAAO;WACf,MAAM,GAAG,IAAI;eACT,MAAM;iBACJ,MAAM,GAAG,IAAI;GAE7B;AAED,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM;iBAE1C,MAAM;eACR,MAAM;sBACC,MAAM;gBACZ,MAAM;cACR,MAAM;GAEnB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM;YAE3C,MAAM;mBACC,MAAM;iBACR,MAAM;iBACN,MAAM;YACX,MAAM;GAEjB;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,48 @@
1
+ import fetch from 'node-fetch';
2
+ import { getBaseUrl, getApiToken } from './config.js';
3
+ class ApiError extends Error {
4
+ status;
5
+ responseBody;
6
+ constructor(message, status, responseBody) {
7
+ super(message);
8
+ this.status = status;
9
+ this.responseBody = responseBody;
10
+ this.name = 'ApiError';
11
+ }
12
+ }
13
+ async function apiCall(method, path, body) {
14
+ const baseUrl = getBaseUrl();
15
+ const token = getApiToken();
16
+ const headers = {
17
+ 'Content-Type': 'application/json',
18
+ };
19
+ if (token) {
20
+ headers['Authorization'] = `Bearer ${token}`;
21
+ }
22
+ const response = await fetch(`${baseUrl}${path}`, {
23
+ method,
24
+ headers,
25
+ body: body ? JSON.stringify(body) : undefined,
26
+ });
27
+ const data = await response.json().catch(() => ({}));
28
+ if (!response.ok) {
29
+ throw new ApiError(data.error || `HTTP ${response.status}`, response.status, data);
30
+ }
31
+ return data;
32
+ }
33
+ export async function validateVidscript(vidscript) {
34
+ return apiCall('POST', '/api/cli/validate', { vidscript });
35
+ }
36
+ export async function submitRender(vidscript, resolution) {
37
+ return apiCall('POST', '/api/cli/render', { vidscript, resolution });
38
+ }
39
+ export async function getRenderStatus(renderId) {
40
+ return apiCall('GET', `/api/cli/status?id=${renderId}`);
41
+ }
42
+ export async function initiateDeviceAuth(deviceName) {
43
+ return apiCall('POST', '/api/cli/auth/device', { deviceName });
44
+ }
45
+ export async function pollDeviceAuth(deviceCode) {
46
+ return apiCall('GET', `/api/cli/auth/device?device_code=${deviceCode}`);
47
+ }
48
+ export { ApiError };
@@ -0,0 +1,11 @@
1
+ export interface CliConfig {
2
+ apiToken?: string;
3
+ baseUrl?: string;
4
+ defaultResolution?: string;
5
+ }
6
+ export declare function readConfig(): CliConfig;
7
+ export declare function writeConfig(config: CliConfig): void;
8
+ export declare function getBaseUrl(): string;
9
+ export declare function getApiToken(): string | undefined;
10
+ export declare function isAuthenticated(): boolean;
11
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAQD,wBAAgB,UAAU,IAAI,SAAS,CAUtC;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,QAG5C;AAED,wBAAgB,UAAU,IAAI,MAAM,CAGnC;AAED,wBAAgB,WAAW,IAAI,MAAM,GAAG,SAAS,CAGhD;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC"}
@@ -0,0 +1,37 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ const CONFIG_DIR = join(homedir(), '.scenerok');
5
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
6
+ function ensureConfigDir() {
7
+ if (!existsSync(CONFIG_DIR)) {
8
+ mkdirSync(CONFIG_DIR, { recursive: true });
9
+ }
10
+ }
11
+ export function readConfig() {
12
+ ensureConfigDir();
13
+ if (!existsSync(CONFIG_FILE)) {
14
+ return {};
15
+ }
16
+ try {
17
+ return JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));
18
+ }
19
+ catch {
20
+ return {};
21
+ }
22
+ }
23
+ export function writeConfig(config) {
24
+ ensureConfigDir();
25
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
26
+ }
27
+ export function getBaseUrl() {
28
+ const config = readConfig();
29
+ return config.baseUrl || 'https://scenerok.com';
30
+ }
31
+ export function getApiToken() {
32
+ const config = readConfig();
33
+ return config.apiToken;
34
+ }
35
+ export function isAuthenticated() {
36
+ return !!getApiToken();
37
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@scenerok/cli",
3
+ "version": "1.0.0",
4
+ "description": "SceneRok CLI - Create videos from your terminal and agent workflows",
5
+ "type": "module",
6
+ "bin": {
7
+ "scenerok": "dist/index.js",
8
+ "sr": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsc --watch",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "scenerok",
21
+ "@scenerok/cli",
22
+ "video",
23
+ "cli",
24
+ "vidscript",
25
+ "agent",
26
+ "ai"
27
+ ],
28
+ "author": "SceneRok",
29
+ "license": "MIT",
30
+ "dependencies": {
31
+ "commander": "^12.0.0",
32
+ "chalk": "^5.3.0",
33
+ "ora": "^8.0.1",
34
+ "inquirer": "^9.2.0",
35
+ "node-fetch": "^3.3.2"
36
+ },
37
+ "devDependencies": {
38
+ "@types/inquirer": "^9.0.7",
39
+ "@types/node": "^20.11.0",
40
+ "typescript": "^5.3.0"
41
+ },
42
+ "engines": {
43
+ "node": ">=18.0.0"
44
+ }
45
+ }