StoryboardHandler (#37): breaks the project's script into a numbered
scene-by-scene breakdown (description + image prompt per scene) via
OpenAI in JSON mode (added json_mode support to OpenAiDriver for
reliable structured output), stored as a JSON asset.
VideoGenerationHandler (#38): generates one still image per storyboard
scene via StabilityAI — the scene *visuals*, not motion/video clips
(no text-to-video model integrated; see the task comment for why).
VideoEditingHandler (#39): the step that actually produces a video
file — assembles the scene stills into an MP4 via ffmpeg's concat
demuxer, timed to the voiceover audio's duration (probed via ffprobe)
or a fixed per-scene duration if there's no audio, then muxes the
audio track in. Verified against the real ffmpeg/ffprobe binaries on
this box, not just Process::fake().
Asset previews (#45, #46): a reusable partial renders each asset
inline by type — image/audio/video get real playback elements, text/
JSON gets a truncated <pre> block, everything else a download link.
Wired into both the awaiting_approval step view (so approving/
rejecting is an actual content review, not just a word count) and a
general project assets grid.
Also fixes a latent bug in AssetStorage::url(), found while wiring up
previews: it called Storage::url() on the default 'local' disk, which
has no configured URL driver and throws in production — only worked
before because Storage::fake() is more permissive than the real local
driver. Now serves assets through a new authenticated streaming route
instead (VideoProjectAssetController).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This was already implemented, migrated, and running live, but had
never been committed. Bringing it into git: Project/Task/Idea/
TaskComment models, their JSON API (CRUD + status transitions,
task comments, idea<->project linking), Livewire UI pages, and the
nav/route wiring for them. All 42 of its own tests pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Create WebhookController with 4 endpoints:
- POST /api/webhooks/posts - Create a new post
- GET /api/webhooks/posts - List posts with filters
- POST /api/webhooks/posts/{post}/publish - Trigger publishing
- GET /api/webhooks/posts/{post}/status - Get post status
- Add X-Muninn-Token header authentication
- Add MUNINN_WEBHOOK_TOKEN config in services.php
- Add MUNINN_WEBHOOK_TOKEN to .env.example
- Add webhook routes with /webhooks prefix
All endpoints return JSON responses with success/error fields.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
Adds JSON API for Posts (index/show/store/update/destroy) so an AI
agent can draft posts, read what's due for publishing, and report
back publish/fail status. Unlike the existing read-only Sites/Accounts
APIs, this is full CRUD since the app itself never publishes anything.
- PostController with status/site_id/social_account_id/due filters
- StorePostRequest/UpdatePostRequest validate against Post::TYPES/STATUSES
- PostResource shapes the JSON response
- Route::apiResource('posts', ...) registered with ->names('api.posts')
to avoid colliding with the existing web posts.index route name
- store() calls ->fresh() after create() so the response reflects the
DB-level default status ('draft') instead of a stale in-memory null
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add GET /api/accounts (list with optional ?network= and ?type= filters) and
GET /api/accounts/{socialAccount} (show) endpoints. Include plain-text password
field for AI agent consumption.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add GET /api/sites endpoint for listing sites with optional name filter,
and GET /api/sites/{site} for showing individual sites. Includes API
resource, controller, routes registration, and comprehensive tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>