Skip to content

Add Cutline video editing Mini App - #76

Merged
MyPrototypeWhat merged 5 commits into
MiniMax-AI:mainfrom
klarkxy:add-fablecut-miniapp
Oct 27, 2026
Merged

MyPrototypeWhat merged 5 commits into
MiniMax-AI:mainfrom
klarkxy:add-fablecut-miniapp

Conversation

@klarkxy

@klarkxy klarkxy commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add Cutline 0.1.0, a community video editing Mini App for MiniMax Code, under plugins/klarkxy/cutline/, and list it in both root READMEs. It edits existing media with multitrack timelines, captions, motion graphics, color grading, audio processing and export. Media generation is outside this release's scope.

The package vendors FableCut 1.16.0 at fd2af081d73ec94464b88df8c21f640adcc78985. MIT attribution, font licenses, upstream provenance and documented local patches are retained. Cutline is a community adaptation, not an official FableCut release.

Editing and interface

  • English and Simplified Chinese interfaces, with an immediate language switch and a browser-local preference. User names, captions, title content and stored enum values are preserved.
  • Responsive timeline toolbars, collapsible side panels, a separate audio-meter gutter, and icon controls with localized tooltips and accessible names.
  • A compact monitor header with canvas parameters in a keyboard-accessible popover. Playback controls wrap instead of overlapping timecodes. Snap targets, sequence actions, action toasts, the export batch list and timeline transition/fade tooltips follow the selected language.
  • Plugin, Skill and MCP server identity: cutline; MCP endpoint: /mcp/cutline. The existing 19 upstream tools and project-folder import tool retain their fablecut_* names. Standalone export is exposed as cutline_export.

Export and runtime

  • Original fablecut_export continues to render in the open editor tab, including batch outputs.
  • cutline_export prepares, reports and cancels standalone export jobs. Preparation snapshots the saved project, supported media, library files and renderer. The chat Agent launches the returned Node runner in its own background shell task; rendering can continue after the Mini App service stops. Continued rendering after exiting the entire client is not supported or tested.
  • The standalone runner uses the existing compositor and audio mix through local Chrome/Edge and FFmpeg. Puppeteer Core and dependency licenses are bundled; browsers and FFmpeg/FFprobe are not downloaded or installed. Each job produces one file, checked with ffprobe and complete ffmpeg decoding; failed outputs remain provisional.
  • State lives in context.dataDir. The port isolates Agent file capabilities from browser requests, guards stale project writes, saves a version before project import, pins validated DNS addresses during URL imports and redirects, and cleans up runtime-owned media subprocesses. The standalone runner has no Host credentials and uses an isolated loopback server with outbound browser networking blocked.

Review hardening

  • Whole-project MCP writes require an explicit baseRevision; submitted document revisions cannot impersonate a prior read. Forced REST writes always advance the on-disk revision instead of rewinding it.
  • Managed media accepts only supported video, audio, image and SVG types; fonts stay in the library. Upload, project import and standalone-export preparation reject non-media files before copying.
  • Node and standalone-runner responses use nosniff. Browser-executable documents under user-content routes receive a sandbox CSP, while the editor's static client remains executable.
  • Unknown delivery preset IDs are refused before job creation instead of silently becoming Custom.
  • Heartbeat failures enter an idempotent cleanup path. Browser close is bounded, and an independent 15-second teardown deadline also bounds cleanup initiated by the overall render timeout.

Validation

Environment: Windows 11 x64, Node.js 26.10.0, FFmpeg/FFprobe 8.1.1 and Google Chrome. Target client: MiniMax Code 3.1.2 or later.

  • Final serial plugin suite: 188 tests, 187 passed, 0 failed, 1 skipped (node --test --test-concurrency=1 plugins/klarkxy/cutline/tests/*.test.mjs, 2026-10-26). The skip is the existing Windows symlink-permission test (EPERM). Coverage includes stale-write attacks, forced revision handling, active-content refusal, path boundaries, DNS pinning, unknown presets, heartbeat and stuck-teardown recovery, standalone gate headers, localization and browser interactions.
  • npm run validate: repository and package validation passed; Cutline has no errors or warnings.
  • Root tests: 67/69 passed. Two existing fixtures in test/files.test.mjs fail while creating symlinks with Windows EPERM, before validation assertions run. Root test and validator code are unchanged.
  • Real standalone render: 3-second 1280x720, 30 fps, H.264/AAC output; runner exited 0, ffprobe and full decode passed. The cancel path exited 130 with no task-owned renderer or encoder processes remaining.
  • The final independent review of the concurrency, media-boundary, response-header and teardown fixes returned READY.
  • Installed-Host smoke checks from the original package validation remain documented in the README, including a 180-second standalone export that continued after confirmed Mini App service exit and completed successfully.

Known limits

Intermittent ECONNRESET failures occurred in export transport tests on earlier runs, including a serial run; isolated reruns and the final complete run passed, but the underlying flake remains unresolved. These synthetic checks do not certify every effect or sustained long/high-resolution workloads. macOS, optional Google Fonts/MediaPipe behavior and real remote TLS downloads remain unverified; remote-import regressions use deterministic transport fixtures.

Test projects, generated media, local archives, machine-specific paths and acceptance artifacts are excluded from the commit. Export-job snapshots consume additional disk space and are not automatically deleted; this is documented in the README and editing Skill.

@klarkxy klarkxy changed the title Add FableCut video editing Mini App Add MiniMax Cut video editing Mini App Oct 25, 2026

@MyPrototypeWhat MyPrototypeWhat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at b3025b0.

Thanks for the standalone export path and the bilingual editor. minimax_cut_export (prepare / status / cancel) copies a self-contained job under context.dataDir/export-jobs/<jobId>/, hands the Agent a runner path, and keeps rendering after the Mini App service exits; the finished file lands in that job's exports/ folder. Job ids are validated, prepare uses realpath so sources cannot escape the project media/library trees, and a second start on the same job is refused with a wx lock on runner.lock. The English / 简体中文 switch is local (i18n.js, i18n-en.js, i18n-zh-CN.js, localStorage key fablecut-locale), preserves user text and stored enum ids, and is covered by unit and browser tests. No new Host-credential, session-store, or auto-approve issues showed up in the new export code.

The vendor tree under miniapp/node/vendor/fablecut/ is unchanged in this commit, so the first two Must fix items below from 7b89fb2 are still open; the third is new in this review.

node scripts/validate.mjs reports plugins/klarkxy/minimax-cut: OK. Locally on Node 20.19.2 the plugin suite is 148 pass / 10 skip / 0 fail (node --test --test-concurrency=1 plugins/klarkxy/minimax-cut/tests/*.test.mjs); the 10 skips are Chromium-driven UI tests that self-skip when the discovery path used by those files does not yield a browser. The author's "158/158" claim is consistent with zero failures (pass + skip = 158). CI for this head is action_required (waiting for maintainer approval), not a failure. Plugin tests are not wired into repo CI, which is correct.

Must fix

  1. fablecut_analyze_reference copies any file into media/. For a path outside /media/, the tool only checks that the file exists, then copies it into MEDIA_DIR before running the analysis (miniapp/node/vendor/fablecut/mcp-server.js:1696-1713, copy at :1711). There is no extension or type check, so a non-media file (a private key, for example) ends up in the plugin's media folder, where the page can read it through /media/ (miniapp/node/vendor/fablecut/server.js:1372-1376) and it shows up in the media listing (server.js:741). The sister tool fablecut_import_media checks the extension against KIND_BY_EXT before copying (mcp-server.js:1758-1762). Unchanged since 7b89fb2.

    I see PATCHES.md ("Managed reference paths and bounded MCP sessions") says explicit absolute source paths stay supported on purpose, and that's reasonable for a reference video. Could you add an extension allowlist (the same KIND_BY_EXT video/audio set would do), or an ffprobe check that the file has a video/audio stream, before the copy? Then a mistaken or prompted path can't put arbitrary files into media/.

  2. Temp files written outside context.dataDir. Three paths create directories under os.tmpdir():

    • miniapp/node/vendor/fablecut/encode-profiles.js:254 (fablecut-dry- dry-run probe)
    • miniapp/node/vendor/fablecut/server.js:328 (fablecut-dry- multi-output dry run)
    • miniapp/node/vendor/fablecut/export-jobs.js:100 (fablecut-export- headless browser profile; can't be reached while allowHeadless is false, but it's still in the code)

    The repo rule is that a Mini App writes only inside context.dataDir. Please put these under something like <dataDir>/analysis/tmp (the export session already uses EXPORTS_DIR at server.js:338 so rename can't hit EXDEV). For export-jobs.js:100, either point it at the same place or remove the headless launch path, since this package never enables it. Please also record the change in PATCHES.md. Unchanged since 7b89fb2; the new standalone runner keeps its own profile under the job folder (export/runner.mjs:1035), which is good, but a dry-run through the copied encode-profiles.js / server.js still hits os.tmpdir().

3. Please rename the plugin without "MiniMax". Plugins named "MiniMax …" in this repo are published by the MiniMax team, so "MiniMax Cut" (minimax-cut, minimax_cut_export) would read as an official plugin in the marketplace. Please pick a name of your own and update the id, display name, tool names and README to match.

  • Plugin folder: plugins/klarkxy/minimax-cut/
  • Id / name: package.json:2; .minimax-plugin/plugin.json:3-4 (name, displayName). miniapp/miniapp.json has no id/name field.
  • MCP server minimax-cut and route /mcp/minimax-cut: miniapp/miniapp.json:27-28, servers.mcp.json:4, :6, miniapp/node/server.mjs:46
  • Tool minimax_cut_export: miniapp/node/export/export-mcp.mjs:19
  • Skill: skills/minimax-cut/ (SKILL.md:2-3, :25)
  • READMEs: README.md:1, :40; README.zh-CN.md:1, :40; root catalog rows README.md:62, README.zh-CN.md:62
  • i18n dictionary: miniapp/client/i18n.js:31, i18n-en.js:16, :1132, i18n-zh-CN.js:22, :1190
  • Tests: tests/i18n.test.mjs:300-308, tests/i18n-browser.test.mjs:152, :175

Optional

  1. Fallback data dir. paths.js:26-28 falls back to ~/FableCut or to the package folder when FABLECUT_DATA_DIR is unset. server.mjs:56 always sets it before the module loads, so this is fine today. A guard that throws when the variable is missing would keep a future load-order change from silently writing into the home folder or the package.

  2. Agent file capability scope. Please say plainly in the README that the agent tools can read any local file the user can (absolute path=, server.js:1251-1255; fablecut_import_project, mcp-server.js:459), and that imports copy into the data folder. fablecut_import_project itself is well guarded: relative paths only, realpath containment and COPYFILE_EXCL (mcp-server.js:1117-1141, :1197, :1336). The new "Files read" wording for export prepare is clear; the absolute-path MCP capability is still soft.

  3. Vendored CLAUDE.md (miniapp/node/vendor/fablecut/CLAUDE.md, 2,472 lines) still documents npx -y fablecut, claude mcp add, and localhost:7777 (for example lines 15, 139, 167). fablecut_docs serves this file to the agent (mcp-server.js:1484), so those instructions are wrong in this package and could lead an agent to run a runtime npx install. Consider trimming them or adding a patch note.

  4. Duplicate payload. Shared modules still exist in both miniapp/client/ and miniapp/node/vendor/fablecut/, and this commit also vendors Puppeteer Core under miniapp/node/vendor/puppeteer/ (licenses inventoried). Package size is fine for review; deduping client/vendor copies would still shrink re-vendor diffs.

  5. No realpath on /media/ and /library/ lookups. resolveSrc (miniapp/node/vendor/fablecut/server.js:219-227), mediaFile (miniapp/node/vendor/fablecut/mcp-server.js:481-487) and the static /media/ route (server.js:1372-1376) normalize the path and check the prefix but don't resolve symlinks, so a symlink that already sits in media/ could point reads outside it. Low risk, since I found no code path that creates one, but the import-project code already has folderFile/realPath (mcp-server.js:1092-1141) that could be reused here — and the new export prepare/runner path already does use realpath.

  6. Check-then-copy for unique names. fablecut_analyze_reference (mcp-server.js:1710-1711) and fablecut_import_media (:1768-1769) pick a free name with existsSync and then call copyFileSync without flags, so two concurrent imports with the same name could overwrite each other. copyToMedia (:1187-1203) already does this race-free with COPYFILE_EXCL and an EEXIST retry; reusing it would fix both.

  7. Small leftovers. PATCHES.md still mentions an archive path for the removed references/media-generation.md that is not in the package. Tool-count wording still varies: package.json says 19 upstream + import + standalone, server.mjs / mcp-http.mjs say 20 or 19 upstream, while plugin.json and servers.mcp.json say 21 (which matches 20 fablecut_* + minimax_cut_export).

Notes

  • Good design choices on the new path: DNS-pinned URL import (unchanged), Origin/Fetch-Metadata gate on MCP, in-process localRequest for path=, job-folder isolation with realpath, wx runner lock, loopback-only runner with request interception that blocks outbound browser networking, and no Host credential in the runner.
  • License: MIT for both the package and upstream. Puppeteer Core and its dependency licenses are inventoried under vendor/puppeteer/licenses/. No compiled browser binaries are shipped.
  • Child processes: ffmpeg/ffprobe stay on argument arrays (no shell). The standalone runner tracks owned children and stops with its parent task.
  • Export jobs are not auto-deleted; the README documents the disk cost.

Copy link
Copy Markdown
Collaborator

Update to the review above: item 2 (temp files under os.tmpdir()) is now optional, since those directories are removed after use. Items 1 (arbitrary files copied into media/) and 3 (rename) are still required.

@klarkxy klarkxy changed the title Add MiniMax Cut video editing Mini App Add Cutline video editing Mini App Oct 26, 2026

@MyPrototypeWhat MyPrototypeWhat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 9f9ecf5. Thanks for the extra hardening and for updating the PR title. Nothing in the new commit blocks merge, so I'm still approving.

Optional

  1. The reference check uses the name, not the target. fablecut_analyze_reference still takes the extension from the path as given (plugins/klarkxy/cutline/miniapp/node/vendor/fablecut/mcp-server.js:1749-1751), and the new isMediaName guard in copyToMedia (mcp-server.js:1187) is name-based too. A symlink named x.mp4 that points at a non-media file would still be copied. Taking the extension from realPath(file), or running an ffprobe check for a video or audio stream before the copy, would close that.
  2. Leftover internal identifiers. global.__MINIMAX_CUT_EN__ (miniapp/client/i18n-en.js:1887), global.__MINIMAX_CUT_ZH_CN__ (miniapp/client/i18n-zh-CN.js:1969) and the x-minimax-export-token header (miniapp/node/export/runner.mjs:37) still carry the old name. Users don't see them, but renaming them would finish the cleanup.
  3. Temp files under os.tmpdir(). Still optional, as before: encode-profiles.js:254, server.js:343 and export-jobs.js:100 under miniapp/node/vendor/fablecut/.
  4. Fallback data dir. The earlier optional item about paths.js still applies.

Verified

  • PR title now reads "Add Cutline video editing Mini App".
  • File access in the README. "Data & access" already says a local path can reach any file the user can read, and "Files written" now describes the media allowlist, nosniff and the sandbox CSP. Nothing more needed there.
  • New hardening in this commit.
    • fablecut_set_project requires an explicit integer baseRevision and no longer trusts the revision inside the submitted document or the process-global lastReadRevision (mcp-server.js, around :1598-1620).
    • A shared allowlist in vendor/fablecut/media-types.js is used by upload, URL import, project-folder import and export preparation. Non-media files are refused before anything is copied, and fonts stay in the library (copyToLibrary, mcp-server.js).
    • Every Node response sets X-Content-Type-Options: nosniff (miniapp/node/server.mjs:139), and browser-executable documents under user-content routes get a sandbox CSP.
    • Unknown delivery preset IDs are refused at export preparation instead of falling back to Custom.
    • Runner heartbeat failures and timeouts go through one bounded, idempotent teardown.
    • The changes are recorded in PATCHES.md.
  • No new network hosts, credentials or host-level access in the new code.
  • Outside the plugin folder, only the two root catalog rows change. CI and root test/ are untouched.
  • After merging into the latest origin/main locally, npm run check passes on Node 22 (87 pass, 0 fail).
  • Plugin tests on Node 22: 175 pass, 13 skip because they need a local Chromium, 0 fail (node --test --test-concurrency=1 tests/*.test.mjs).

@MyPrototypeWhat
MyPrototypeWhat merged commit df8f76c into MiniMax-AI:main Oct 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants