Merge commit '3896991bdb91210d1574044bc5a321e269b4698e'
This commit is contained in:
16
.eslintrc.json
Normal file
16
.eslintrc.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"es2022": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": ["@typescript-eslint"],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
27
.github/workflows/ci.yml
vendored
27
.github/workflows/ci.yml
vendored
@@ -38,3 +38,30 @@ jobs:
|
|||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: npm test --if-present
|
run: npm test --if-present
|
||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [work]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint-and-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm install --frozen-lockfile=true
|
||||||
|
- run: pnpm lint
|
||||||
|
- name: Validate JSON
|
||||||
|
run: jq empty $(find . -name "*.json")
|
||||||
|
- name: Validate YAML
|
||||||
|
run: |
|
||||||
|
pnpm add -g yamllint
|
||||||
|
yamllint prompts
|
||||||
|
|||||||
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
2
.prettierignore
Normal file
2
.prettierignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Handlebars templates should not be formatted by prettier
|
||||||
|
workflows/*.json.hbs
|
||||||
8
.prettierrc.cjs
Normal file
8
.prettierrc.cjs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Note: The 90-character line limit mentioned in prompts/video-intro.yml applies only to subtitle text.
|
||||||
|
// The printWidth setting below is for code formatting and does not affect subtitle text.
|
||||||
|
module.exports = {
|
||||||
|
singleQuote: true,
|
||||||
|
semi: true,
|
||||||
|
trailingComma: 'all',
|
||||||
|
printWidth: 100,
|
||||||
|
};
|
||||||
39
README.md
39
README.md
@@ -36,3 +36,42 @@ Creator-focused pack for BlackRoad OS with content planning, longform scripting,
|
|||||||
## Limitations
|
## Limitations
|
||||||
- Channel adapters (YouTube, TikTok, etc.) are not wired; outputs are adapter-ready stubs.
|
- Channel adapters (YouTube, TikTok, etc.) are not wired; outputs are adapter-ready stubs.
|
||||||
- Analytics hooks are placeholders; performance insights should be injected via API or data warehouse taps.
|
- Analytics hooks are placeholders; performance insights should be injected via API or data warehouse taps.
|
||||||
|
# Blackroad OS · Creator-Studio Pack (Gen-0)
|
||||||
|
|
||||||
|
CreatorPack-Gen-0 scaffolds a prompt-first toolkit for designers, writers, and video makers.
|
||||||
|
It ships with curated prompt presets, tiny agent helpers, and workflow templates that can be
|
||||||
|
rendered with Handlebars.
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm i
|
||||||
|
pnpm br-create list
|
||||||
|
pnpm br-create run brand-kit
|
||||||
|
```
|
||||||
|
|
||||||
|
Set environment variables using `creator-studio.env.example` and export them before running
|
||||||
|
remote APIs.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
- `/prompts` — YAML presets with front-matter metadata for creative tasks.
|
||||||
|
- `/agents` — TypeScript and Python helpers for prompt generation, media remixing, and Canva
|
||||||
|
rendering (stubs).
|
||||||
|
- `/workflows` — Handlebars JSON templates for Canva and FFmpeg jobs.
|
||||||
|
- `/lib` — Shared Handlebars renderer and zod schemas.
|
||||||
|
- `/src` — CLI entry for `br-create` commands.
|
||||||
|
- `/scripts` — Build-time helpers such as beacon injection.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
- `pnpm br-create list` — enumerate prompts and workflows.
|
||||||
|
- `pnpm br-create run <prompt>` — send a prompt preset to the configured agent.
|
||||||
|
- `pnpm br-create render <workflow>` — fill a workflow JSON template.
|
||||||
|
- `pnpm br-create render-canva <workflow>` — fill a Canva workflow JSON template.
|
||||||
|
- `pnpm lint` — run ESLint + Prettier checks.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
- TODO(creator-pack-next): Blender pipeline for 3D packshots.
|
||||||
|
- TODO(creator-pack-next): Audio mastering agent for podcast polish.
|
||||||
|
|||||||
26
agents/generate_prompt.ts
Normal file
26
agents/generate_prompt.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
import { promptPresetSchema, PromptPreset } from '../lib/schema.js';
|
||||||
|
|
||||||
|
export const loadPromptPreset = (slug: string): PromptPreset => {
|
||||||
|
const filePath = path.join(process.cwd(), 'prompts', `${slug}.yml`);
|
||||||
|
const file = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
const data = yaml.load(file);
|
||||||
|
const parsed = promptPresetSchema.parse(data);
|
||||||
|
return parsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderPrompt = (preset: PromptPreset, agentName: string): string => {
|
||||||
|
const steps = preset.steps.map((step, index) => `${index + 1}. ${step.text}`).join('\n');
|
||||||
|
const header = `# ${preset.title}\nmodel: ${preset.model}\nagent: ${agentName}`;
|
||||||
|
const notes = preset.notes ? `\n\nNotes:\n${preset.notes}` : '';
|
||||||
|
return `${header}\n\n${preset.description}\n\nSteps:\n${steps}${notes}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const dispatchPrompt = async (preset: PromptPreset, agentName: string): Promise<string> => {
|
||||||
|
const promptText = renderPrompt(preset, agentName);
|
||||||
|
// Placeholder for SDK call to OpenAI or internal agent.
|
||||||
|
// TODO(creator-pack-next): Stream completions to file for downstream workflows.
|
||||||
|
return Promise.resolve(`Sent to ${agentName}:\n${promptText}`);
|
||||||
|
};
|
||||||
30
agents/remix_media.py
Normal file
30
agents/remix_media.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
"""Tiny ffmpeg wrapper used for text-only parameter composition."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RemixJob:
|
||||||
|
"""Describes a simplified ffmpeg remix operation."""
|
||||||
|
|
||||||
|
input_path: Path
|
||||||
|
output_path: Path
|
||||||
|
filters: List[str]
|
||||||
|
bitrate: str = "6M"
|
||||||
|
|
||||||
|
def build_command(self) -> str:
|
||||||
|
filtergraph = ",".join(self.filters)
|
||||||
|
return f"ffmpeg -i {self.input_path} -b:v {self.bitrate} -vf \"{filtergraph}\" {self.output_path}"
|
||||||
|
|
||||||
|
|
||||||
|
def preview_job(job: RemixJob) -> str:
|
||||||
|
"""Return the ffmpeg command without executing it."""
|
||||||
|
|
||||||
|
return job.build_command()
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(creator-pack-next): Execute ffmpeg with subprocess and stream logs.
|
||||||
22
agents/render_canva.ts
Normal file
22
agents/render_canva.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { renderTemplate } from '../lib/template.js';
|
||||||
|
|
||||||
|
interface CanvaJobContext {
|
||||||
|
brand: string;
|
||||||
|
assets: string[];
|
||||||
|
agent: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const renderCanvaBatch = (templateName: string, context: CanvaJobContext): string => {
|
||||||
|
const filePath = path.join(process.cwd(), 'workflows', `${templateName}.json.hbs`);
|
||||||
|
const source = readFileSync(filePath, 'utf-8');
|
||||||
|
const rendered = renderTemplate(source, context);
|
||||||
|
return rendered;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sendToCanva = async (payload: string): Promise<string> => {
|
||||||
|
const token = process.env.CANVA_API_TOKEN || 'unset-token';
|
||||||
|
// Placeholder for Canva API call.
|
||||||
|
return Promise.resolve(`POST /canva with token=${token}\n${payload}`);
|
||||||
|
};
|
||||||
8
creator-studio.env.example
Normal file
8
creator-studio.env.example
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Canva API token
|
||||||
|
CANVA_API_TOKEN=your-canva-token
|
||||||
|
|
||||||
|
# OpenAI API key used by generate_prompt.ts
|
||||||
|
OPENAI_API_KEY=your-openai-key
|
||||||
|
|
||||||
|
# Default agent name for CLI operations
|
||||||
|
CREATOR_AGENT=lucidia
|
||||||
24
lib/schema.ts
Normal file
24
lib/schema.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const promptStepSchema = z.object({
|
||||||
|
text: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const promptPresetSchema = z.object({
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
model: z.string(),
|
||||||
|
tags: z.array(z.string()),
|
||||||
|
steps: z.array(promptStepSchema),
|
||||||
|
notes: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const workflowTemplateSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
engine: z.enum(['canva', 'ffmpeg']),
|
||||||
|
template: z.unknown(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PromptPreset = z.infer<typeof promptPresetSchema>;
|
||||||
|
export type WorkflowTemplate = z.infer<typeof workflowTemplateSchema>;
|
||||||
9
lib/template.ts
Normal file
9
lib/template.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import Handlebars from 'handlebars';
|
||||||
|
|
||||||
|
export const renderTemplate = <T extends object>(source: string, context: T): string => {
|
||||||
|
// WARNING: 'noEscape: true' disables HTML escaping in Handlebars templates.
|
||||||
|
// This is intentional for non-HTML contexts (e.g., JSON), but can lead to injection vulnerabilities
|
||||||
|
// if used with untrusted user input. Ensure that 'context' is trusted and sanitized before use.
|
||||||
|
const template = Handlebars.compile(source, { noEscape: true });
|
||||||
|
return template(context);
|
||||||
|
};
|
||||||
21
package.json
21
package.json
@@ -10,6 +10,27 @@
|
|||||||
"@types/mocha": "^10.0.6",
|
"@types/mocha": "^10.0.6",
|
||||||
"@types/node": "^22.7.4",
|
"@types/node": "^22.7.4",
|
||||||
"mocha": "^10.7.3",
|
"mocha": "^10.7.3",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"br-create": "ts-node src/cli.ts",
|
||||||
|
"lint": "eslint ./src ./agents ./lib ./scripts --ext .ts && prettier -c .",
|
||||||
|
"format": "prettier -w .",
|
||||||
|
"postbuild": "ts-node scripts/postbuild.ts",
|
||||||
|
"test": "pnpm lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"handlebars": "^4.7.8",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
|
"zod": "^3.23.8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.7.4",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.8.1",
|
||||||
|
"@typescript-eslint/parser": "^8.8.1",
|
||||||
|
"eslint": "^9.0.0",
|
||||||
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
"prettier": "^3.3.3",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.6.3"
|
"typescript": "^5.6.3"
|
||||||
}
|
}
|
||||||
|
|||||||
13
prompts/brand-kit.yml
Normal file
13
prompts/brand-kit.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: Brand Kit Narrative
|
||||||
|
description: Condense a brand story, tone, and visual system into a shareable doc.
|
||||||
|
model: gpt-4o
|
||||||
|
tags: [branding, writing]
|
||||||
|
steps:
|
||||||
|
- text: 'Summarize mission, values, and target audience in under 120 words'
|
||||||
|
- text: 'List a palette of five colors with hex values and usage guidance'
|
||||||
|
- text: 'Propose a typography stack (display, body, mono) with pairing notes'
|
||||||
|
- text: "Draft a 6-point voice and tone guide with do/don't bullets"
|
||||||
|
- text: 'Add a mermaid graph showing brand assets flowing into social, web, print'
|
||||||
|
notes: |
|
||||||
|
Keep copy tight but not dry. Include example headlines and a fallback palette for dark mode.
|
||||||
12
prompts/logo-neon.yml
Normal file
12
prompts/logo-neon.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
title: Neon Logo Generator
|
||||||
|
description: Create a glowing BR-OS logo with golden-ratio curves.
|
||||||
|
model: gpt-4o
|
||||||
|
tags: [branding, design]
|
||||||
|
steps:
|
||||||
|
- text: 'Generate SVG path for fractal-road glyph'
|
||||||
|
- text: 'Apply brand gradient #ff4fd8 ➜ #7cf9ff'
|
||||||
|
- text: 'Return layered SVG wrapped in <svg> with viewBox set to 0 0 1024 1024'
|
||||||
|
notes: |
|
||||||
|
Encourage minimal anchor points and smooth Bezier curves. Embed a mermaid diagram if it
|
||||||
|
helps document the SVG layering order for handoff.
|
||||||
13
prompts/video-intro.yml
Normal file
13
prompts/video-intro.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: Video Intro Script
|
||||||
|
description: Write a 30-second intro script for a Creator Studio case study reel.
|
||||||
|
model: gpt-4o
|
||||||
|
tags: [video, scripting]
|
||||||
|
steps:
|
||||||
|
- text: 'Open with a 2-line hook about design velocity and BR-OS automation'
|
||||||
|
- text: 'List three storyboard beats with timing markers'
|
||||||
|
- text: 'Propose subtitle text and on-screen motion cues'
|
||||||
|
- text: 'Return an ffmpeg-friendly filtergraph description for lower third animations'
|
||||||
|
notes: |
|
||||||
|
Keep lines under 90 characters for subtitle friendliness. Reference the brand gradient from
|
||||||
|
`logo-neon.yml` when suggesting color overlays.
|
||||||
4
public/sig.beacon.json
Normal file
4
public/sig.beacon.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"ts": "__BUILD_TIMESTAMP__",
|
||||||
|
"agent": "CreatorPack-Gen-0"
|
||||||
|
}
|
||||||
12
scripts/postbuild.ts
Normal file
12
scripts/postbuild.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
const beaconPath = path.join(process.cwd(), 'public', 'sig.beacon.json');
|
||||||
|
const payload = {
|
||||||
|
ts: new Date().toISOString(),
|
||||||
|
agent: 'CreatorPack-Gen-0',
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync(beaconPath, JSON.stringify(payload, null, 2));
|
||||||
|
|
||||||
|
// TODO(creator-pack-next): Add checksum validation for published artifacts.
|
||||||
132
src/cli.ts
Normal file
132
src/cli.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { loadPromptPreset, dispatchPrompt } from '../agents/generate_prompt.js';
|
||||||
|
import { renderCanvaBatch, sendToCanva } from '../agents/render_canva.js';
|
||||||
|
import { renderTemplate } from '../lib/template.js';
|
||||||
|
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const command = args[0];
|
||||||
|
|
||||||
|
const list = (): void => {
|
||||||
|
try {
|
||||||
|
const prompts = fs
|
||||||
|
.readdirSync(path.join(process.cwd(), 'prompts'))
|
||||||
|
.filter((file) => file.endsWith('.yml'));
|
||||||
|
const workflows = fs
|
||||||
|
.readdirSync(path.join(process.cwd(), 'workflows'))
|
||||||
|
.filter((file) => file.endsWith('.json.hbs'));
|
||||||
|
console.log('Prompts:');
|
||||||
|
prompts.forEach((prompt) => console.log(`- ${prompt.replace('.yml', '')}`));
|
||||||
|
console.log('\nWorkflows:');
|
||||||
|
workflows.forEach((flow) => console.log(`- ${flow.replace('.json.hbs', '')}`));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error listing resources:', (error as Error).message);
|
||||||
|
console.error('Make sure the prompts/ and workflows/ directories exist.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const runPrompt = async (slug: string, agentName: string): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const preset = loadPromptPreset(slug);
|
||||||
|
const response = await dispatchPrompt(preset, agentName);
|
||||||
|
console.log(response);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error running prompt '${slug}':`, (error as Error).message);
|
||||||
|
console.error(`Make sure the file prompts/${slug}.yml exists and is valid.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderWorkflow = async (templateName: string): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const filePath = path.join(process.cwd(), 'workflows', `${templateName}.json.hbs`);
|
||||||
|
const source = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
const payload = renderTemplate(source, {
|
||||||
|
input: 'input.mp4',
|
||||||
|
output: 'output.mp4',
|
||||||
|
filters: ['scale=1280:-1', 'format=yuv420p'],
|
||||||
|
bitrate: '6M',
|
||||||
|
});
|
||||||
|
console.log(payload);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error rendering workflow '${templateName}':`, (error as Error).message);
|
||||||
|
console.error(`Make sure the file workflows/${templateName}.json.hbs exists.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderCanva = async (templateName: string): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const payload = renderCanvaBatch(templateName, {
|
||||||
|
brand: 'Blackroad',
|
||||||
|
assets: ['cover.png', 'thumbnail.png'],
|
||||||
|
agent: process.env.CREATOR_AGENT || 'lucidia',
|
||||||
|
});
|
||||||
|
const response = await sendToCanva(payload);
|
||||||
|
console.log(response);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error rendering Canva template '${templateName}':`, (error as Error).message);
|
||||||
|
console.error(`Make sure the file workflows/${templateName}.json.hbs exists.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const usage = () => {
|
||||||
|
console.log('Usage: br-create <command>');
|
||||||
|
console.log('Commands:');
|
||||||
|
console.log(' list');
|
||||||
|
console.log(' run <prompt> [--agent name]');
|
||||||
|
console.log(' render <workflow>');
|
||||||
|
console.log(' render-canva <workflow>');
|
||||||
|
};
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
|
switch (command) {
|
||||||
|
case 'list':
|
||||||
|
list();
|
||||||
|
break;
|
||||||
|
case 'run': {
|
||||||
|
const slug = args[1];
|
||||||
|
if (!slug) {
|
||||||
|
console.error('Error: Missing prompt name.');
|
||||||
|
console.error('Usage: br-create run <prompt> [--agent name]');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const agentFlagIndex = args.indexOf('--agent');
|
||||||
|
const agentName =
|
||||||
|
agentFlagIndex >= 0 && args[agentFlagIndex + 1]
|
||||||
|
? args[agentFlagIndex + 1]
|
||||||
|
: process.env.CREATOR_AGENT || 'lucidia';
|
||||||
|
await runPrompt(slug, agentName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'render':
|
||||||
|
if (!args[1]) {
|
||||||
|
console.error('Error: Missing workflow name.');
|
||||||
|
console.error('Usage: br-create render <workflow>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
await renderWorkflow(args[1]);
|
||||||
|
break;
|
||||||
|
case 'render-canva':
|
||||||
|
if (!args[1]) {
|
||||||
|
console.error('Error: Missing workflow name.');
|
||||||
|
console.error('Usage: br-create render-canva <workflow>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
await renderCanva(args[1]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error('An unexpected error occurred while running the command.');
|
||||||
|
console.error('Error details:', (error as Error).message);
|
||||||
|
if ((error as Error).stack) {
|
||||||
|
console.error('\nStack trace:', (error as Error).stack);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -12,4 +12,17 @@
|
|||||||
},
|
},
|
||||||
"include": ["agents/**/*.ts", "tests/**/*.ts"],
|
"include": ["agents/**/*.ts", "tests/**/*.ts"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "."
|
||||||
|
},
|
||||||
|
"include": ["src", "lib", "agents", "scripts"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
|||||||
6
workflows/canva-batch.json.hbs
Normal file
6
workflows/canva-batch.json.hbs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"job": "canva-batch",
|
||||||
|
"agent": "{{agent}}",
|
||||||
|
"brand": "{{brand}}",
|
||||||
|
"assets": [{{#each assets}}"{{this}}"{{#unless @last}}, {{/unless}}{{/each}}]
|
||||||
|
}
|
||||||
8
workflows/ffmpeg-transcode.json.hbs
Normal file
8
workflows/ffmpeg-transcode.json.hbs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"job": "ffmpeg-transcode",
|
||||||
|
"engine": "ffmpeg",
|
||||||
|
"input": "{{input}}",
|
||||||
|
"output": "{{output}}",
|
||||||
|
"filters": [{{#each filters}}"{{this}}"{{#unless @last}}, {{/unless}}{{/each}}],
|
||||||
|
"bitrate": "{{bitrate}}"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user