@higgsfield/video-editor-mcp 1.1.1-dev.0 → 2.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.
Files changed (59) hide show
  1. package/README.md +63 -26
  2. package/dist/cli.mjs +284 -112
  3. package/dist/guide.md +2 -3
  4. package/dist/index.html +1 -1
  5. package/dist/launcher.mjs +34 -14
  6. package/dist/server.mjs +209 -265
  7. package/node_modules/@higgsfield/higgsedit/dist/api.d.ts +481 -0
  8. package/node_modules/@higgsfield/higgsedit/dist/{chunk-PFQYUJQL.mjs → chunk-3XVMJSV2.mjs} +1 -1
  9. package/node_modules/@higgsfield/higgsedit/dist/{chunk-F4CMIG5G.mjs → chunk-4VE54E4U.mjs} +3 -1
  10. package/node_modules/@higgsfield/higgsedit/dist/{chunk-2JYLVSVV.mjs → chunk-55JARHLA.mjs} +205 -65
  11. package/node_modules/@higgsfield/higgsedit/dist/{chunk-YLVXQIPX.mjs → chunk-E4N76T2R.mjs} +1 -1
  12. package/node_modules/@higgsfield/higgsedit/dist/{chunk-OCIZ36XD.mjs → chunk-ECRSOIKI.mjs} +1 -1
  13. package/node_modules/@higgsfield/higgsedit/dist/{chunk-IEPPEDEU.mjs → chunk-ENALCTAD.mjs} +1 -1
  14. package/node_modules/@higgsfield/higgsedit/dist/chunk-HY2E7IWX.mjs +43 -0
  15. package/node_modules/@higgsfield/higgsedit/dist/{chunk-NARTAWRN.mjs → chunk-HYCUOCUQ.mjs} +9 -4
  16. package/node_modules/@higgsfield/higgsedit/dist/{chunk-C3GSB3XZ.mjs → chunk-NLEIBCAK.mjs} +1 -7
  17. package/node_modules/@higgsfield/higgsedit/dist/{chunk-2WKNH7OL.mjs → chunk-O3AVYZ6U.mjs} +1 -1
  18. package/node_modules/@higgsfield/higgsedit/dist/chunk-UXO22E3P.mjs +22 -0
  19. package/node_modules/@higgsfield/higgsedit/dist/{chunk-BH2QF635.mjs → chunk-V5XTCFYK.mjs} +4 -100
  20. package/node_modules/@higgsfield/higgsedit/dist/chunk-W2TANYEH.mjs +118 -0
  21. package/node_modules/@higgsfield/higgsedit/dist/{chunk-DVN4LUXV.mjs → chunk-XY5CCUOZ.mjs} +2 -6
  22. package/node_modules/@higgsfield/higgsedit/dist/{chunk-KIWGVAPJ.mjs → chunk-Y7SAX2P4.mjs} +8 -12
  23. package/node_modules/@higgsfield/higgsedit/dist/chunk-YNAIRXOP.mjs +22 -0
  24. package/node_modules/@higgsfield/higgsedit/dist/{chunk-WENFTPXQ.mjs → chunk-ZO3NDQD3.mjs} +1 -1
  25. package/node_modules/@higgsfield/higgsedit/dist/higgsedit.mjs +13 -13
  26. package/node_modules/@higgsfield/higgsedit/package.json +4 -1
  27. package/package.json +4 -4
  28. package/plugin/.codex-plugin/plugin.json +1 -1
  29. package/plugin/skills/video-editing/SKILL.md +109 -95
  30. package/plugin/mcp.json +0 -10
  31. package/plugin/plugin.json +0 -41
  32. package/plugin/skills/motion-craft/SKILL.md +0 -87
  33. package/plugin/skills/motion-craft/agents/openai.yaml +0 -6
  34. package/plugin/skills/motion-craft/references/causality.md +0 -25
  35. package/plugin/skills/motion-craft/references/composition.md +0 -20
  36. package/plugin/skills/motion-craft/references/easing.md +0 -19
  37. package/plugin/skills/motion-craft/references/qc.md +0 -33
  38. package/plugin/skills/motion-craft/references/recipes.md +0 -31
  39. package/plugin/skills/motion-craft/references/stepped.md +0 -43
  40. package/plugin/skills/motion-craft/references/timing.md +0 -60
  41. package/plugin/skills/motion-craft/references/tsx.md +0 -60
  42. package/plugin/skills/motion-craft/references/typography.md +0 -22
  43. package/plugin/skills/video-editing/agents/openai.yaml +0 -6
  44. package/plugin/skills/video-editing/references/animation-contract.md +0 -45
  45. package/plugin/skills/video-editing/references/assembly.md +0 -58
  46. package/plugin/skills/video-editing/references/caption-systems.md +0 -29
  47. package/plugin/skills/video-editing/references/caption-titling.md +0 -41
  48. package/plugin/skills/video-editing/references/clip-geometry.md +0 -37
  49. package/plugin/skills/video-editing/references/compose.md +0 -283
  50. package/plugin/skills/video-editing/references/editor-measured.md +0 -37
  51. package/plugin/skills/video-editing/references/failure-modes.md +0 -37
  52. package/plugin/skills/video-editing/references/local-setup.md +0 -25
  53. package/plugin/skills/video-editing/references/motion-language.md +0 -27
  54. package/plugin/skills/video-editing/references/provenance.md +0 -7
  55. package/plugin/skills/video-editing/references/sandbox.md +0 -31
  56. package/plugin/skills/video-editing/references/shot-blueprints.md +0 -24
  57. package/plugin/skills/video-editing/references/title-animation.md +0 -29
  58. package/plugin/skills/video-editing/references/tsx-projects.md +0 -187
  59. package/plugin/skills/video-editing/references/workflows.md +0 -41
@@ -0,0 +1,481 @@
1
+ /**
2
+ * The HTTP API of `higgsedit dev`, as a client sees it. Published as `@higgsfield/higgsedit/api` (types only), so
3
+ * a program driving a project through its dev server (the Video Editor MCP server) shares these shapes with the
4
+ * handlers instead of restating them. Nothing here imports the engine: the file stands on its own once packed.
5
+ *
6
+ * Every route lives under the project the server was started for; relative media paths resolve inside it.
7
+ * An error answers with a non-2xx status and `{ error }`.
8
+ *
9
+ * GET /api/outline Outline
10
+ * POST /api/edit EditRequest → EditReply
11
+ * POST /api/undo | /api/redo → UndoReply
12
+ * POST /api/render RenderRequest → RenderStarted; GET /api/render?id=<id> → RenderJob
13
+ * POST /api/sheet SheetRequest → image/png
14
+ * POST /api/media/probe MediaRequest → MediaProbe
15
+ * POST /api/media/grab MediaRequest & GrabOptions → GrabResult
16
+ * POST /api/media/filmstrip MediaRequest & PreviewOptions → FilmstripResult
17
+ * POST /api/media/waveform MediaRequest & WaveformOptions → WaveformResult
18
+ * POST /api/media/transcribe MediaRequest & TranscribeOptions → JobStarted (a Job<TranscribeResult>)
19
+ * POST /api/media/segment MediaRequest & SegmentRequest → JobStarted (a Job<SegmentResult>)
20
+ * GET /api/jobs/<id> Job<…>
21
+ * GET /api/fonts?family=&category=&cached=&limit=&offset= FontList
22
+ * GET /api/doctor Diagnosis
23
+ */
24
+ /** Seconds, frames ("45f") or "MM:SS"; where a route says so, a negative counts back from the end. */
25
+ export type TimeArg = number | string;
26
+ export interface ApiError {
27
+ error: string;
28
+ }
29
+ export interface Composition {
30
+ width: number;
31
+ height: number;
32
+ fps: number;
33
+ duration: number;
34
+ frames: number;
35
+ }
36
+ export type InspectType = "number" | "color" | "text" | "font" | "boolean" | "select";
37
+ export type InspectValue = string | number | boolean;
38
+ /** An `@inspect` const: edited by name with `set-const`, not by element id. */
39
+ export interface InspectVariable {
40
+ type?: InspectType;
41
+ min?: number;
42
+ max?: number;
43
+ step?: number;
44
+ /** Choices for `select`, listed as written. */
45
+ options?: string[];
46
+ /** `path="A/B/Label"` split into segments. */
47
+ path?: string[];
48
+ file: string;
49
+ line: number;
50
+ name: string;
51
+ label: string;
52
+ group: string[];
53
+ /** The initializer's literal, when it is one the control can show. */
54
+ value?: InspectValue;
55
+ /** Whether `set-const` can write it; `reason` says why not. */
56
+ editable: boolean;
57
+ reason?: string;
58
+ }
59
+ export interface OutlineElement {
60
+ key: string;
61
+ tag: string;
62
+ parent: string | null;
63
+ depth: number;
64
+ /** On the parent's clock, as `start`/`end` are written. An open end resolves to the parent's end (null if that is open too). */
65
+ start: number;
66
+ end: number | null;
67
+ /** True when the source gives no end and nothing (media, children) implies one. */
68
+ openEnd?: boolean;
69
+ /** On the scene's root clock. */
70
+ absStart: number;
71
+ absEnd: number | null;
72
+ props: Record<string, unknown>;
73
+ text?: string;
74
+ /** Props the source computes, by their source text; edits refuse these. */
75
+ code?: Record<string, string>;
76
+ component?: string;
77
+ file?: string;
78
+ line?: number;
79
+ /** `syncTo`: the measured offset (seconds after the target's recording) and the sourceIn it plays from, or why it keeps its default placement. */
80
+ sync?: {
81
+ to: string;
82
+ offset?: number;
83
+ sourceIn?: number;
84
+ error?: string;
85
+ };
86
+ }
87
+ /** What `check` learned about a media file the project references. */
88
+ export interface MediaSummary {
89
+ codec: string;
90
+ duration: number;
91
+ hasAudio: boolean;
92
+ pixFmt?: string;
93
+ width?: number;
94
+ height?: number;
95
+ fps?: number;
96
+ }
97
+ export interface Outline {
98
+ ok: boolean;
99
+ name: string;
100
+ errors: string[];
101
+ warnings: string[];
102
+ composition?: Composition;
103
+ media: {
104
+ src: string;
105
+ probe?: MediaSummary;
106
+ }[];
107
+ elements: OutlineElement[];
108
+ variables: InspectVariable[];
109
+ /** The same, as a compact tree a model can read. */
110
+ text: string;
111
+ }
112
+ /** What a source can spell as a literal. */
113
+ export type PropValue = number | string | boolean | null | PropValue[] | {
114
+ [key: string]: PropValue;
115
+ };
116
+ export type Edit =
117
+ /** `name` may be a path into a literal (`animate.opacity.1.1`). */
118
+ {
119
+ op: "set-prop";
120
+ name: string;
121
+ value: PropValue;
122
+ } | {
123
+ op: "set-text";
124
+ text: string;
125
+ }
126
+ /** Inserts a new element under the target. */
127
+ | {
128
+ op: "insert";
129
+ tag: string;
130
+ props?: Record<string, PropValue>;
131
+ text?: string;
132
+ before?: string;
133
+ /** For a component: where `tag` is imported from, written as in an import declaration (relative to the file the element lands in). */
134
+ import?: {
135
+ from: string;
136
+ };
137
+ } | {
138
+ op: "remove";
139
+ } | {
140
+ op: "move";
141
+ parent: string;
142
+ before?: string;
143
+ } | {
144
+ op: "retime";
145
+ start?: number | string;
146
+ end?: number | string;
147
+ }
148
+ /** Rewrites an `@inspect` const's literal; addressed by name (and file when the name is not unique), not by element key. */
149
+ | {
150
+ op: "set-const";
151
+ name: string;
152
+ file?: string;
153
+ value: InspectValue;
154
+ };
155
+ export type RefusalCode = "not-found" | "ambiguous-id" | "expression" | "loop-bound" | "loop-body" | "spread" | "shared-component" | "invalid" | "syntax";
156
+ export interface Refusal {
157
+ code: RefusalCode;
158
+ reason: string;
159
+ /** Where the refusal was decided, `file:line`. */
160
+ at?: string;
161
+ /** The attribute or child text that blocked it. */
162
+ source?: string;
163
+ }
164
+ export interface EditResult {
165
+ ok: boolean;
166
+ written: string[];
167
+ refused?: Refusal;
168
+ /** The JSX the edit landed on (the call site when it followed a prop there). */
169
+ target?: {
170
+ file: string;
171
+ line: number;
172
+ id: string | undefined;
173
+ };
174
+ /** Id of an inserted element. */
175
+ id?: string;
176
+ }
177
+ /** `key` is the element id (empty for `set-const`); all or nothing, one undo step. */
178
+ export interface EditRequest {
179
+ name?: string;
180
+ edits: {
181
+ key: string;
182
+ edit: Edit;
183
+ }[];
184
+ dryRun?: boolean;
185
+ }
186
+ export interface UndoState {
187
+ canUndo: boolean;
188
+ canRedo: boolean;
189
+ undoName: string | null;
190
+ redoName: string | null;
191
+ }
192
+ export interface EditReply {
193
+ ok: boolean;
194
+ results: EditResult[];
195
+ undo: UndoState;
196
+ }
197
+ export type HistoryResult = {
198
+ ok: true;
199
+ written: string[];
200
+ } | {
201
+ ok: false;
202
+ reason: string;
203
+ };
204
+ export type UndoReply = HistoryResult & {
205
+ undo: UndoState;
206
+ };
207
+ /** The editor's export settings (`spec`) are the panel's own; a program leaves them out and gets the defaults. */
208
+ export interface RenderRequest {
209
+ /** Under `renders/`; default `render.mp4`, never overwriting (a suffix is added). */
210
+ fileName?: string;
211
+ from?: string;
212
+ to?: string;
213
+ depth?: 8 | 10;
214
+ sceneId?: string;
215
+ allowFontFallback?: boolean;
216
+ }
217
+ export interface RenderStarted {
218
+ id: string;
219
+ }
220
+ export interface RenderJob {
221
+ state: "running" | "done" | "failed" | "cancelled";
222
+ done: number;
223
+ total: number;
224
+ /** Project-relative, once done. */
225
+ out?: string;
226
+ error?: string;
227
+ msPerFrame?: number;
228
+ }
229
+ /** Frames of the project at `times`, side by side on one PNG. */
230
+ export interface SheetRequest {
231
+ times: TimeArg[];
232
+ /** Default min(4, times). */
233
+ columns?: number;
234
+ /** Thumbnail width in px, default 480. */
235
+ width?: number;
236
+ allowFontFallback?: boolean;
237
+ }
238
+ /** A file: absolute, `~/…`, or relative to the project (`media/clip.mp4`). */
239
+ export interface MediaRequest {
240
+ path: string;
241
+ }
242
+ export interface MediaTrack {
243
+ id: number;
244
+ type: string;
245
+ codec: string | null;
246
+ codecLongName?: string;
247
+ profile?: string;
248
+ language: string;
249
+ firstTimestamp: number;
250
+ duration: number;
251
+ bitRate?: number;
252
+ frames?: number;
253
+ width?: number;
254
+ height?: number;
255
+ fps?: number;
256
+ pixFmt?: string;
257
+ colorSpace?: string;
258
+ colorRange?: string;
259
+ colorTransfer?: string;
260
+ colorPrimaries?: string;
261
+ rotation?: number;
262
+ sampleRate?: number;
263
+ channels?: number;
264
+ channelLayout?: string;
265
+ }
266
+ export interface MediaProbe {
267
+ path: string;
268
+ name: string;
269
+ /** video, audio, image, or other (not readable as media). */
270
+ type: "video" | "audio" | "image" | "other";
271
+ size: number;
272
+ /** ffmpeg's container name; null when the file could not be read as media. */
273
+ format: string | null;
274
+ formatLongName?: string;
275
+ /** Seconds. */
276
+ duration?: number;
277
+ bitRate?: number;
278
+ width?: number;
279
+ height?: number;
280
+ tags?: Record<string, string>;
281
+ tracks: MediaTrack[];
282
+ }
283
+ export type Quality = "small" | "medium" | "large" | "fullres";
284
+ export interface GrabOptions {
285
+ /** Source times; negatives count back from the end. Default [0]. */
286
+ times?: TimeArg[];
287
+ /** Instead of times: this many frames evenly spaced across the clip or the start/end window. */
288
+ count?: number;
289
+ start?: TimeArg;
290
+ end?: TimeArg;
291
+ quality?: Quality;
292
+ /** One PNG per frame instead of contact sheets. */
293
+ separate?: boolean;
294
+ /** Frames per contact sheet, 1–12. */
295
+ perSheet?: number;
296
+ /** Lift the 100-frame cap. */
297
+ uncapped?: boolean;
298
+ /** Directory for the PNGs; default a fresh one under the system temp dir. */
299
+ output?: string;
300
+ }
301
+ export interface GrabbedImage {
302
+ /** The frame's stamp, or `first-last` for a sheet. */
303
+ timecode: string;
304
+ path: string;
305
+ }
306
+ export interface GrabResult {
307
+ images: GrabbedImage[];
308
+ }
309
+ export interface PreviewOptions {
310
+ start?: TimeArg;
311
+ end?: TimeArg;
312
+ /** PNG path, or a directory to put a fresh file in; default a fresh file under the system temp dir. */
313
+ output?: string;
314
+ /** Thumbnail scale, clamped to 0.25–4: smaller fits more rows and columns. Default 1. */
315
+ scale?: number;
316
+ }
317
+ export interface FilmstripResult {
318
+ path: string;
319
+ width: number;
320
+ height: number;
321
+ columns: number;
322
+ rows: number;
323
+ cell: {
324
+ width: number;
325
+ height: number;
326
+ };
327
+ /** Height of the ruler band above each row. */
328
+ ruler: number;
329
+ /** Source seconds of each cell, row by row. */
330
+ times: number[];
331
+ }
332
+ export interface WaveformOptions extends PreviewOptions {
333
+ /** Peak level, in dBFS, at or below which audio counts as silent. Default -40. */
334
+ silenceDb?: number;
335
+ /** Shortest silence reported, in seconds. Default 0.4. */
336
+ minSilence?: number;
337
+ }
338
+ export interface Silence {
339
+ start: number;
340
+ end: number;
341
+ }
342
+ export interface WaveformResult {
343
+ path: string;
344
+ /** Silent spans in absolute source seconds. */
345
+ silences: Silence[];
346
+ width: number;
347
+ height: number;
348
+ rows: number;
349
+ }
350
+ export interface TranscribeOptions {
351
+ /** JSON path to write, or a folder for a fresh file; default a fresh file under the system temp dir. */
352
+ output?: string;
353
+ model?: string;
354
+ language?: string;
355
+ }
356
+ export interface TranscribeResult {
357
+ path: string;
358
+ segments: number;
359
+ words: number;
360
+ language?: string;
361
+ }
362
+ export interface Point {
363
+ x: number;
364
+ y: number;
365
+ }
366
+ export type SegmentModel = "tiny" | "small" | "base-plus" | "large";
367
+ /** Prompts in 0..1 of the frame (x right, y down); times in source time. */
368
+ export interface SegmentRequest {
369
+ points?: Point[];
370
+ exclude?: Point[];
371
+ box?: [number, number, number, number];
372
+ /** The frame the prompts are on; default `start`. */
373
+ at?: TimeArg;
374
+ /** Span to track; default the whole file. */
375
+ start?: TimeArg;
376
+ end?: TimeArg;
377
+ /** Default tiny. */
378
+ model?: SegmentModel;
379
+ /** Where the `.mask` goes; default `masks/<file name>.mask` in the project. */
380
+ output?: string;
381
+ }
382
+ export interface SegmentResult {
383
+ path: string;
384
+ model: SegmentModel;
385
+ backend: "webgpu" | "wasm";
386
+ frameRate: number;
387
+ /** Source seconds of the first masked frame, and just past the last. */
388
+ start: number;
389
+ end: number;
390
+ frames: number;
391
+ /** On the prompted frame: the object's bounds in 0..1 (null if nothing was found) and its share of the frame. */
392
+ bbox: [number, number, number, number] | null;
393
+ area: number;
394
+ score: number;
395
+ iou: number;
396
+ /** [start, end) source seconds where no object was found. */
397
+ lost: [number, number][];
398
+ /** [start, end) source seconds where the model rated its mask below 0.5. */
399
+ weak: [number, number][];
400
+ msPerFrame: number;
401
+ }
402
+ export interface JobStarted {
403
+ id: string;
404
+ }
405
+ export type Job<T> = {
406
+ status: "running";
407
+ } | {
408
+ status: "done";
409
+ result: T;
410
+ } | {
411
+ status: "failed";
412
+ error: string;
413
+ };
414
+ export type FontCategory = "sans-serif" | "serif" | "display" | "handwriting" | "monospace";
415
+ export interface FontQuery {
416
+ /** Substring of the family name, any case. */
417
+ family?: string;
418
+ category?: FontCategory;
419
+ /** Only families with a variant in the local cache. */
420
+ cached?: boolean;
421
+ limit?: number;
422
+ /** Families to skip, for paging. */
423
+ offset?: number;
424
+ }
425
+ export interface GoogleFamily {
426
+ family: string;
427
+ /** sans-serif, serif, display, handwriting or monospace. */
428
+ category: string;
429
+ /** Google's popularity rank (1 = most used). */
430
+ popularity: number;
431
+ /** Weights with a normal-style file, lightest first. */
432
+ weights: number[];
433
+ /** Weights with an italic file, lightest first. */
434
+ italics: number[];
435
+ }
436
+ export interface ListedFamily extends GoogleFamily {
437
+ /** Variants in HIGGSEDIT_HOME/fonts ("400", "700i"): these render offline. */
438
+ cached: string[];
439
+ /** css2 stylesheet with every variant, for an <html> element. */
440
+ stylesheet: string;
441
+ }
442
+ export interface FontList {
443
+ families: ListedFamily[];
444
+ /** How many families pass the filters (can exceed `families.length` when `limit` cuts it). */
445
+ total: number;
446
+ catalogue: {
447
+ fetchedAt: string;
448
+ stale: boolean;
449
+ };
450
+ }
451
+ export type SelectStatus = {
452
+ url: null;
453
+ } | {
454
+ url: string;
455
+ answers: boolean;
456
+ detail: string;
457
+ };
458
+ export interface Diagnosis {
459
+ node: string;
460
+ platform: string;
461
+ ffmpeg: {
462
+ path: string;
463
+ } | {
464
+ missing: string;
465
+ };
466
+ ffprobe: {
467
+ path: string;
468
+ } | {
469
+ missing: string;
470
+ };
471
+ chrome: {
472
+ version: string;
473
+ path?: string;
474
+ note?: string;
475
+ };
476
+ /** HIGGSEDIT_SELECT_URL and whether its server answers `GET v1`. */
477
+ select: SelectStatus;
478
+ log: string;
479
+ /** Error entries from the end of the log, newest last. */
480
+ errors: string[];
481
+ }
@@ -6,7 +6,7 @@ const __filename = __higgseditPath(import.meta.url);
6
6
  const __dirname = __filename.slice(0, __filename.lastIndexOf("/"));
7
7
  import {
8
8
  CLI_MANIFEST
9
- } from "./chunk-2WKNH7OL.mjs";
9
+ } from "./chunk-O3AVYZ6U.mjs";
10
10
  import "./chunk-75WO2NOF.mjs";
11
11
 
12
12
  // src/init.ts
@@ -11,9 +11,10 @@ import {
11
11
  rateFilter,
12
12
  render,
13
13
  sheet,
14
+ sheetPng,
14
15
  shuffle,
15
16
  verify
16
- } from "./chunk-NARTAWRN.mjs";
17
+ } from "./chunk-HYCUOCUQ.mjs";
17
18
  import "./chunk-7PYXSJS7.mjs";
18
19
  import "./chunk-UDL5AHKO.mjs";
19
20
  import "./chunk-LTKMGZ6T.mjs";
@@ -31,6 +32,7 @@ export {
31
32
  rateFilter,
32
33
  render,
33
34
  sheet,
35
+ sheetPng,
34
36
  shuffle,
35
37
  verify
36
38
  };