@higgsfield/video-editor-mcp 1.1.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 +3 -3
  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
package/dist/cli.mjs CHANGED
@@ -9,8 +9,8 @@ var __export = (target, all) => {
9
9
 
10
10
  // src/video-editor-local/setup.ts
11
11
  import { spawnSync as spawnSync2 } from "node:child_process";
12
- import { existsSync as existsSync4, mkdirSync as mkdirSync4, rmSync as rmSync3 } from "node:fs";
13
- import { dirname as dirname4, join as join5, sep } from "node:path";
12
+ import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync4 } from "node:fs";
13
+ import { dirname as dirname4, join as join6 } from "node:path";
14
14
  import { pathToFileURL } from "node:url";
15
15
 
16
16
  // src/video-editor-local/higgsedit.ts
@@ -32,19 +32,10 @@ function findCli(env = process.env, from = import.meta.url) {
32
32
  return path && existsSync(path) ? path : void 0;
33
33
  }
34
34
 
35
- // src/video-editor-local/updates.ts
36
- import { execFileSync as execFileSync2 } from "node:child_process";
37
- import {
38
- existsSync as existsSync3,
39
- mkdirSync as mkdirSync3,
40
- readFileSync as readFileSync3,
41
- renameSync as renameSync2,
42
- rmSync as rmSync2,
43
- statSync as statSync2,
44
- writeFileSync as writeFileSync2
45
- } from "node:fs";
46
- import { homedir as homedir2 } from "node:os";
47
- import { dirname as dirname3, join as join4 } from "node:path";
35
+ // src/video-editor-local/install.ts
36
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "node:fs";
37
+ import { homedir as homedir3 } from "node:os";
38
+ import { join as join5, sep } from "node:path";
48
39
 
49
40
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
50
41
  var external_exports = {};
@@ -1237,13 +1228,13 @@ function unwrapMessage(message) {
1237
1228
  }
1238
1229
  function finalizeIssue(iss, ctx, config2) {
1239
1230
  const message = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config2.customError?.(iss)) ?? unwrapMessage(config2.localeError?.(iss)) ?? "Invalid input";
1240
- const { inst: _inst, continue: _continue, input: _input, ...rest } = iss;
1241
- rest.path ?? (rest.path = []);
1242
- rest.message = message;
1231
+ const { inst: _inst, continue: _continue, input: _input, ...rest2 } = iss;
1232
+ rest2.path ?? (rest2.path = []);
1233
+ rest2.message = message;
1243
1234
  if (ctx?.reportInput) {
1244
- rest.input = _input;
1235
+ rest2.input = _input;
1245
1236
  }
1246
- return rest;
1237
+ return rest2;
1247
1238
  }
1248
1239
  function getSizableOrigin(input) {
1249
1240
  if (input instanceof Set)
@@ -2630,11 +2621,11 @@ var $ZodCIDRv6 = /* @__PURE__ */ $constructor("$ZodCIDRv6", (inst, def) => {
2630
2621
  try {
2631
2622
  if (parts.length !== 2)
2632
2623
  throw new Error();
2633
- const [address, prefix] = parts;
2634
- if (!prefix)
2624
+ const [address, prefix2] = parts;
2625
+ if (!prefix2)
2635
2626
  throw new Error();
2636
- const prefixNum = Number(prefix);
2637
- if (`${prefixNum}` !== prefix)
2627
+ const prefixNum = Number(prefix2);
2628
+ if (`${prefixNum}` !== prefix2)
2638
2629
  throw new Error();
2639
2630
  if (prefixNum < 0 || prefixNum > 128)
2640
2631
  throw new Error();
@@ -3583,8 +3574,8 @@ var $ZodTuple = /* @__PURE__ */ $constructor("$ZodTuple", (inst, def) => {
3583
3574
  }
3584
3575
  if (def.rest) {
3585
3576
  let i = items.length - 1;
3586
- const rest = input.slice(items.length);
3587
- for (const el of rest) {
3577
+ const rest2 = input.slice(items.length);
3578
+ for (const el of rest2) {
3588
3579
  i++;
3589
3580
  const result = def.rest._zod.run({ value: el, issues: [] }, ctx);
3590
3581
  if (result instanceof Promise) {
@@ -11042,12 +11033,12 @@ function _includes(includes, params) {
11042
11033
  });
11043
11034
  }
11044
11035
  // @__NO_SIDE_EFFECTS__
11045
- function _startsWith(prefix, params) {
11036
+ function _startsWith(prefix2, params) {
11046
11037
  return new $ZodCheckStartsWith({
11047
11038
  check: "string_format",
11048
11039
  format: "starts_with",
11049
11040
  ...normalizeParams(params),
11050
- prefix
11041
+ prefix: prefix2
11051
11042
  });
11052
11043
  }
11053
11044
  // @__NO_SIDE_EFFECTS__
@@ -11151,11 +11142,11 @@ function _intersection(Class2, left, right) {
11151
11142
  function _tuple(Class2, items, _paramsOrRest, _params) {
11152
11143
  const hasRest = _paramsOrRest instanceof $ZodType;
11153
11144
  const params = hasRest ? _params : _paramsOrRest;
11154
- const rest = hasRest ? _paramsOrRest : null;
11145
+ const rest2 = hasRest ? _paramsOrRest : null;
11155
11146
  return new Class2({
11156
11147
  type: "tuple",
11157
11148
  items,
11158
- rest,
11149
+ rest: rest2,
11159
11150
  ...normalizeParams(params)
11160
11151
  });
11161
11152
  }
@@ -12136,30 +12127,30 @@ var tupleProcessor = (schema, ctx, _json, params) => {
12136
12127
  ...params,
12137
12128
  path: [...params.path, prefixPath, i]
12138
12129
  }));
12139
- const rest = def.rest ? process2(def.rest, ctx, {
12130
+ const rest2 = def.rest ? process2(def.rest, ctx, {
12140
12131
  ...params,
12141
12132
  path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
12142
12133
  }) : null;
12143
12134
  if (ctx.target === "draft-2020-12") {
12144
12135
  json2.prefixItems = prefixItems;
12145
- if (rest) {
12146
- json2.items = rest;
12136
+ if (rest2) {
12137
+ json2.items = rest2;
12147
12138
  }
12148
12139
  } else if (ctx.target === "openapi-3.0") {
12149
12140
  json2.items = {
12150
12141
  anyOf: prefixItems
12151
12142
  };
12152
- if (rest) {
12153
- json2.items.anyOf.push(rest);
12143
+ if (rest2) {
12144
+ json2.items.anyOf.push(rest2);
12154
12145
  }
12155
12146
  json2.minItems = prefixItems.length;
12156
- if (!rest) {
12147
+ if (!rest2) {
12157
12148
  json2.maxItems = prefixItems.length;
12158
12149
  }
12159
12150
  } else {
12160
12151
  json2.items = prefixItems;
12161
- if (rest) {
12162
- json2.additionalItems = rest;
12152
+ if (rest2) {
12153
+ json2.additionalItems = rest2;
12163
12154
  }
12164
12155
  }
12165
12156
  const { minimum, maximum } = schema._zod.bag;
@@ -13524,19 +13515,19 @@ var ZodTuple = /* @__PURE__ */ $constructor("ZodTuple", (inst, def) => {
13524
13515
  $ZodTuple.init(inst, def);
13525
13516
  ZodType.init(inst, def);
13526
13517
  inst._zod.processJSONSchema = (ctx, json2, params) => tupleProcessor(inst, ctx, json2, params);
13527
- inst.rest = (rest) => inst.clone({
13518
+ inst.rest = (rest2) => inst.clone({
13528
13519
  ...inst._zod.def,
13529
- rest
13520
+ rest: rest2
13530
13521
  });
13531
13522
  });
13532
13523
  function tuple(items, _paramsOrRest, _params) {
13533
13524
  const hasRest = _paramsOrRest instanceof $ZodType;
13534
13525
  const params = hasRest ? _params : _paramsOrRest;
13535
- const rest = hasRest ? _paramsOrRest : null;
13526
+ const rest2 = hasRest ? _paramsOrRest : null;
13536
13527
  return new ZodTuple({
13537
13528
  type: "tuple",
13538
13529
  items,
13539
- rest,
13530
+ rest: rest2,
13540
13531
  ...util_exports.normalizeParams(params)
13541
13532
  });
13542
13533
  }
@@ -14400,9 +14391,9 @@ function convertBaseSchema(schema, ctx) {
14400
14391
  const items = schema.items;
14401
14392
  if (prefixItems && Array.isArray(prefixItems)) {
14402
14393
  const tupleItems = prefixItems.map((item) => convertSchema(item, ctx));
14403
- const rest = items && typeof items === "object" && !Array.isArray(items) ? convertSchema(items, ctx) : void 0;
14404
- if (rest) {
14405
- zodSchema = z.tuple(tupleItems).rest(rest);
14394
+ const rest2 = items && typeof items === "object" && !Array.isArray(items) ? convertSchema(items, ctx) : void 0;
14395
+ if (rest2) {
14396
+ zodSchema = z.tuple(tupleItems).rest(rest2);
14406
14397
  } else {
14407
14398
  zodSchema = z.tuple(tupleItems);
14408
14399
  }
@@ -14414,9 +14405,9 @@ function convertBaseSchema(schema, ctx) {
14414
14405
  }
14415
14406
  } else if (Array.isArray(items)) {
14416
14407
  const tupleItems = items.map((item) => convertSchema(item, ctx));
14417
- const rest = schema.additionalItems && typeof schema.additionalItems === "object" ? convertSchema(schema.additionalItems, ctx) : void 0;
14418
- if (rest) {
14419
- zodSchema = z.tuple(tupleItems).rest(rest);
14408
+ const rest2 = schema.additionalItems && typeof schema.additionalItems === "object" ? convertSchema(schema.additionalItems, ctx) : void 0;
14409
+ if (rest2) {
14410
+ zodSchema = z.tuple(tupleItems).rest(rest2);
14420
14411
  } else {
14421
14412
  zodSchema = z.tuple(tupleItems);
14422
14413
  }
@@ -14602,8 +14593,8 @@ var ensureCodexHome = () => mkdirSync(process.env.CODEX_HOME ?? join2(homedir(),
14602
14593
  function installCodex(name, entry2, ours) {
14603
14594
  ensureCodexHome();
14604
14595
  const codex = findCodex();
14605
- const run3 = (...args) => spawnSync(codex, args, { encoding: "utf8" });
14606
- const current = run3("mcp", "get", name, "--json");
14596
+ const run2 = (...args) => spawnSync(codex, args, { encoding: "utf8" });
14597
+ const current = run2("mcp", "get", name, "--json");
14607
14598
  if (current.status === 0) {
14608
14599
  const { transport } = JSON.parse(current.stdout);
14609
14600
  if (transport?.command === entry2.command && transport.args?.join("\0") === entry2.args.join("\0"))
@@ -14612,15 +14603,16 @@ function installCodex(name, entry2, ours) {
14612
14603
  throw new Error(
14613
14604
  `${name} already exists with a different configuration; review it with \`codex mcp get ${name}\`. Nothing changed.`
14614
14605
  );
14615
- const removed = run3("mcp", "remove", name);
14606
+ const removed = run2("mcp", "remove", name);
14616
14607
  if (removed.status !== 0) throw new Error(removed.stderr || `Could not update ${name}.`);
14617
14608
  }
14618
- const added = run3("mcp", "add", name, "--", entry2.command, ...entry2.args);
14609
+ const added = run2("mcp", "add", name, "--", entry2.command, ...entry2.args);
14619
14610
  if (added.status !== 0) throw new Error(added.stderr || "Codex registration failed.");
14620
14611
  return `Registered ${name}. Start a new chat to use it.`;
14621
14612
  }
14622
14613
 
14623
14614
  // src/adapters/codex-plugin.ts
14615
+ var marketplaceName = (channel) => `higgsfield-video-editor-updates-${channel}`;
14624
14616
  var PLUGIN_ROLLBACK_ERROR = "Plugin refresh failed and rollback requires attention.";
14625
14617
  var identity = external_exports.object({
14626
14618
  name: external_exports.string().regex(/^[a-z][a-z0-9-]{0,63}$/),
@@ -14649,7 +14641,7 @@ function validateTree(root) {
14649
14641
  }
14650
14642
  }
14651
14643
  function bindPackageRuntime(root, dist) {
14652
- for (const file2 of ["mcp.json", ".mcp.json"]) {
14644
+ for (const file2 of [".mcp.json"]) {
14653
14645
  const path = join3(root, file2);
14654
14646
  if (!existsSync2(path)) continue;
14655
14647
  const config2 = external_exports.object({ mcpServers: external_exports.record(external_exports.string(), external_exports.unknown()) }).parse(JSON.parse(readFileSync2(path, "utf8")));
@@ -14672,14 +14664,34 @@ function bundledPlugin(options) {
14672
14664
  if (!existsSync2(root)) return null;
14673
14665
  if (!lstatSync(root).isDirectory()) throw new Error("Plugin bundle must be a directory");
14674
14666
  validateTree(root);
14675
- const manifest = identity.parse(JSON.parse(readFileSync2(join3(root, "plugin.json"), "utf8")));
14676
- const codex = identity.parse(
14667
+ const manifest = identity.parse(
14677
14668
  JSON.parse(readFileSync2(join3(root, ".codex-plugin/plugin.json"), "utf8"))
14678
14669
  );
14679
- if (manifest.name !== codex.name || manifest.version !== options.version || codex.version !== options.version)
14670
+ if (manifest.version !== options.version)
14680
14671
  throw new Error("Plugin identity/version must match its package and Codex manifest");
14681
14672
  return { root, name: manifest.name };
14682
14673
  }
14674
+ var marketplaceList = external_exports.object({
14675
+ marketplaces: external_exports.array(external_exports.object({ name: external_exports.string(), root: external_exports.string() }))
14676
+ });
14677
+ function listMarketplaces(codex, runner, install) {
14678
+ const list = () => marketplaceList.parse(JSON.parse(runner(codex, ["plugin", "marketplace", "list", "--json"]))).marketplaces;
14679
+ try {
14680
+ return list();
14681
+ } catch (error51) {
14682
+ const message = error51 instanceof Error ? error51.message : String(error51);
14683
+ const broken = [...message.matchAll(/^- `([^`]+)` at /gm)].map((match) => match[1]);
14684
+ if (!broken.length) throw error51;
14685
+ const ours = broken.filter((name) => name.startsWith("higgsfield-video-editor-"));
14686
+ if (install && ours.length === broken.length) {
14687
+ for (const name of ours) runner(codex, ["plugin", "marketplace", "remove", name]);
14688
+ return list();
14689
+ }
14690
+ throw new Error(
14691
+ `The ChatGPT desktop app has plugin registrations whose folders are gone: ${broken.join(", ")}. Run \`codex plugin marketplace remove <name>\` for each, then try again.`
14692
+ );
14693
+ }
14694
+ }
14683
14695
  function refreshBundledPlugin(options, install = false, runner = run) {
14684
14696
  const marker = join3(options.root, "plugin-registration.json");
14685
14697
  if (!install && !existsSync2(marker)) return void 0;
@@ -14692,21 +14704,20 @@ function refreshBundledPlugin(options, install = false, runner = run) {
14692
14704
  if (previous && previous.name !== bundle.name)
14693
14705
  throw new Error("Plugin identity changed; refusing to replace it");
14694
14706
  const codex = previous?.codex ?? findCodex("plugin");
14695
- const marketplaceName = `higgsfield-video-editor-updates-${options.channel}`;
14707
+ const marketplace = marketplaceName(options.channel);
14696
14708
  const root = join3(options.root, "marketplace");
14697
14709
  mkdirSync2(root, { recursive: true });
14698
- const marketplaces = external_exports.object({ marketplaces: external_exports.array(external_exports.object({ name: external_exports.string(), root: external_exports.string() })) }).parse(JSON.parse(runner(codex, ["plugin", "marketplace", "list", "--json"])));
14699
- const existing = marketplaces.marketplaces.find(
14700
- (marketplace) => marketplace.name === marketplaceName
14710
+ const existing = listMarketplaces(codex, runner, install).find(
14711
+ (entry2) => entry2.name === marketplace
14701
14712
  );
14702
14713
  if (existing && realpathSync(existing.root) !== realpathSync(root))
14703
14714
  throw new Error("Marketplace name belongs to another source; nothing changed");
14704
14715
  const installedSchema = external_exports.object({
14705
14716
  installed: external_exports.array(external_exports.object({ pluginId: external_exports.string(), enabled: external_exports.boolean() }))
14706
14717
  });
14707
- const selector = `${bundle.name}@${marketplaceName}`;
14718
+ const selector = `${bundle.name}@${marketplace}`;
14708
14719
  const current = installedSchema.parse(
14709
- JSON.parse(runner(codex, ["plugin", "list", "--marketplace", marketplaceName, "--json"]))
14720
+ JSON.parse(runner(codex, ["plugin", "list", "--marketplace", marketplace, "--json"]))
14710
14721
  ).installed.find((plugin) => plugin.pluginId === selector);
14711
14722
  if (!install && (!current || !current.enabled))
14712
14723
  throw new Error("Plugin was removed or disabled; refusing to reinstall or re-enable it");
@@ -14741,7 +14752,7 @@ function refreshBundledPlugin(options, install = false, runner = run) {
14741
14752
  if (current) runner(codex, ["plugin", "add", selector, "--json"]);
14742
14753
  else runner(codex, ["plugin", "remove", selector]);
14743
14754
  }
14744
- if (addedMarketplace) runner(codex, ["plugin", "marketplace", "remove", marketplaceName]);
14755
+ if (addedMarketplace) runner(codex, ["plugin", "marketplace", "remove", marketplace]);
14745
14756
  if (oldCatalog === null) rmSync(catalog, { force: true });
14746
14757
  if (oldMarker !== null) writeAtomic(marker, oldMarker);
14747
14758
  else rmSync(marker, { force: true });
@@ -14750,7 +14761,7 @@ function refreshBundledPlugin(options, install = false, runner = run) {
14750
14761
  writeAtomic(
14751
14762
  catalog,
14752
14763
  JSON.stringify({
14753
- name: marketplaceName,
14764
+ name: marketplace,
14754
14765
  interface: { displayName: "Higgsfield Video Editor Updates" },
14755
14766
  plugins: [
14756
14767
  {
@@ -14782,6 +14793,18 @@ function refreshBundledPlugin(options, install = false, runner = run) {
14782
14793
  }
14783
14794
 
14784
14795
  // src/video-editor-local/updates.ts
14796
+ import { execFileSync as execFileSync2 } from "node:child_process";
14797
+ import {
14798
+ existsSync as existsSync3,
14799
+ mkdirSync as mkdirSync3,
14800
+ readFileSync as readFileSync3,
14801
+ renameSync as renameSync2,
14802
+ rmSync as rmSync2,
14803
+ statSync as statSync2,
14804
+ writeFileSync as writeFileSync2
14805
+ } from "node:fs";
14806
+ import { homedir as homedir2 } from "node:os";
14807
+ import { dirname as dirname3, join as join4 } from "node:path";
14785
14808
  var UPDATE_PACKAGE = "@higgsfield/video-editor-mcp";
14786
14809
  var UPDATE_REGISTRY = "https://registry.npmjs.org/";
14787
14810
  var CHECK_INTERVAL = 60 * 60 * 1e3;
@@ -14843,8 +14866,8 @@ function newerVersion(candidate, current) {
14843
14866
  }
14844
14867
  return false;
14845
14868
  }
14846
- function packageDist(prefix) {
14847
- return join4(prefix, "node_modules", UPDATE_PACKAGE, "dist");
14869
+ function packageDist(prefix2) {
14870
+ return join4(prefix2, "node_modules", UPDATE_PACKAGE, "dist");
14848
14871
  }
14849
14872
  function validPackage(dist, version2) {
14850
14873
  try {
@@ -14871,7 +14894,7 @@ function saveState(root, state) {
14871
14894
  writeFileSync2(temp, JSON.stringify(state));
14872
14895
  renameSync2(temp, join4(root, "state.json"));
14873
14896
  }
14874
- var run2 = (command2, args, timeout, cwd) => execFileSync2(command2, args, {
14897
+ var defaultRunner = (command2, args, timeout, cwd) => execFileSync2(command2, args, {
14875
14898
  cwd,
14876
14899
  env: Object.fromEntries(
14877
14900
  Object.entries(process.env).filter(([name]) => !name.toLowerCase().startsWith("npm_config_"))
@@ -14882,7 +14905,7 @@ var run2 = (command2, args, timeout, cwd) => execFileSync2(command2, args, {
14882
14905
  maxBuffer: 1024 * 1024,
14883
14906
  stdio: ["ignore", "pipe", "pipe"]
14884
14907
  });
14885
- function updateEditor(options, force = false, runner = run2) {
14908
+ function updateEditor(options, force = false, runner = defaultRunner) {
14886
14909
  const { root, channel } = options;
14887
14910
  if (!force && !checkDue(root)) return "Already checked within the last hour.";
14888
14911
  mkdirSync3(root, { recursive: true });
@@ -14987,6 +15010,71 @@ function updateEditor(options, force = false, runner = run2) {
14987
15010
  }
14988
15011
  }
14989
15012
 
15013
+ // src/video-editor-local/install.ts
15014
+ var INSTALL_PREFIX = join5(homedir3(), ".higgsfield", "video-editor-mcp");
15015
+ var installedCli = (prefix2 = INSTALL_PREFIX) => join5(prefix2, "node_modules", UPDATE_PACKAGE, "dist", "cli.mjs");
15016
+ function installPrefixOf(dist) {
15017
+ const parts = dist.split(sep);
15018
+ if (parts.includes("_npx")) return void 0;
15019
+ if (parts.slice(-4).join("/") !== `node_modules/${UPDATE_PACKAGE}/dist`) return void 0;
15020
+ return parts.slice(0, -4).join(sep);
15021
+ }
15022
+ function installPackage(version2, prefix2 = INSTALL_PREFIX, runner = defaultRunner) {
15023
+ mkdirSync4(prefix2, { recursive: true });
15024
+ const userConfig = join5(prefix2, "npmrc");
15025
+ const globalConfig2 = join5(prefix2, "npmrc-global");
15026
+ writeFileSync3(userConfig, `registry=${UPDATE_REGISTRY}
15027
+ `);
15028
+ writeFileSync3(globalConfig2, "");
15029
+ runner(
15030
+ "npm",
15031
+ [
15032
+ "install",
15033
+ "--prefix",
15034
+ prefix2,
15035
+ "--ignore-scripts",
15036
+ "--no-audit",
15037
+ "--no-fund",
15038
+ "--save-exact",
15039
+ "--engine-strict",
15040
+ `--registry=${UPDATE_REGISTRY}`,
15041
+ `--userconfig=${userConfig}`,
15042
+ `--globalconfig=${globalConfig2}`,
15043
+ "--fetch-retries=2",
15044
+ `${UPDATE_PACKAGE}@${version2}`
15045
+ ],
15046
+ 3e5,
15047
+ prefix2
15048
+ );
15049
+ const cli = installedCli(prefix2);
15050
+ if (!existsSync4(cli))
15051
+ throw new Error(`${UPDATE_PACKAGE}@${version2} did not install under ${prefix2}.`);
15052
+ return cli;
15053
+ }
15054
+ var registration2 = external_exports.object({ name: external_exports.string().min(1), codex: external_exports.string().min(1) });
15055
+ function uninstallEditor(prefix2 = INSTALL_PREFIX, runner = defaultRunner) {
15056
+ const lines = [];
15057
+ for (const channel of ["latest", "next"]) {
15058
+ const marker = join5(prefix2, "public-updates", channel, "plugin-registration.json");
15059
+ if (!existsSync4(marker)) continue;
15060
+ const { name, codex } = registration2.parse(JSON.parse(readFileSync4(marker, "utf8")));
15061
+ const marketplace = marketplaceName(channel);
15062
+ for (const args of [
15063
+ ["plugin", "remove", `${name}@${marketplace}`],
15064
+ ["plugin", "marketplace", "remove", marketplace]
15065
+ ]) {
15066
+ try {
15067
+ runner(codex, args, 2e4, prefix2);
15068
+ } catch {
15069
+ }
15070
+ }
15071
+ lines.push(`Removed the ${name} plugin from the ChatGPT desktop app.`);
15072
+ }
15073
+ rmSync3(prefix2, { recursive: true, force: true });
15074
+ lines.push(`Removed ${prefix2}. Your projects were not touched.`);
15075
+ return lines.join("\n");
15076
+ }
15077
+
14990
15078
  // src/adapters/claude.ts
14991
15079
  var claudeConfig = (name, entry2) => ({
14992
15080
  mcpServers: { [name]: entry2 }
@@ -14995,38 +15083,142 @@ var claudeConfig = (name, entry2) => ({
14995
15083
  // src/video-editor-local/setup.ts
14996
15084
  var NAME = "higgsfield-use-video-editor";
14997
15085
  var DIST = dirname4(import.meta.filename);
14998
- var SERVER = join5(DIST, "server.mjs");
14999
- var LAUNCHER = join5(DIST, "launcher.mjs");
15000
- var PANEL = join5(DIST, "index.html");
15086
+ var SERVER = join6(DIST, "server.mjs");
15087
+ var LAUNCHER = join6(DIST, "launcher.mjs");
15088
+ var PANEL = join6(DIST, "index.html");
15001
15089
  var entry = { command: process.execPath, args: [LAUNCHER] };
15090
+ var INSTALL = `npx ${UPDATE_PACKAGE} install`;
15091
+ var prefix = installPrefixOf(DIST);
15092
+ var persistent = prefix !== void 0;
15002
15093
  function doctor() {
15003
15094
  const [major = 0, minor = 0] = process.versions.node.split(".").map(Number);
15004
15095
  const cli = findCli({}, pathToFileURL(SERVER).href);
15005
- const version2 = cli ? spawnSync2(process.execPath, [cli, "version"], { encoding: "utf8" }) : void 0;
15096
+ const report = cli ? spawnSync2(process.execPath, [cli, "doctor", "--json"], { encoding: "utf8" }) : void 0;
15097
+ let diagnosis;
15098
+ try {
15099
+ diagnosis = report ? JSON.parse(report.stdout) : void 0;
15100
+ } catch {
15101
+ diagnosis = void 0;
15102
+ }
15006
15103
  return [
15007
15104
  { name: "node", ok: major > 22 || major === 22 && minor >= 12, detail: process.version },
15008
15105
  { name: "platform", ok: process.platform === "darwin", detail: process.platform },
15009
- { name: "server", ok: existsSync4(SERVER), detail: SERVER },
15010
- { name: "launcher", ok: existsSync4(LAUNCHER), detail: LAUNCHER },
15011
- { name: "panel", ok: existsSync4(PANEL), detail: PANEL },
15106
+ { name: "server", ok: existsSync5(SERVER), detail: SERVER },
15107
+ { name: "launcher", ok: existsSync5(LAUNCHER), detail: LAUNCHER },
15108
+ { name: "panel", ok: existsSync5(PANEL), detail: PANEL },
15012
15109
  {
15013
15110
  name: "higgsedit",
15014
- ok: version2?.status === 0,
15015
- detail: version2 ? (version2.stdout || version2.stderr).trim() : "the bundled CLI is missing"
15111
+ ok: diagnosis !== void 0,
15112
+ detail: cli ? diagnosis ? cli : ((report?.stdout || report?.stderr) ?? "").trim().slice(-500) || "did not answer" : "the bundled CLI is missing"
15113
+ },
15114
+ {
15115
+ name: "ffmpeg",
15116
+ ok: typeof diagnosis?.ffmpeg?.path === "string",
15117
+ detail: diagnosis?.ffmpeg?.path ?? diagnosis?.ffmpeg?.missing ?? "unknown until higgsedit answers"
15016
15118
  }
15017
15119
  ];
15018
15120
  }
15019
- function assertPersistentInstall() {
15020
- if (DIST.split(sep).includes("_npx"))
15021
- throw new Error(
15022
- `Install a persistent copy first (npm install --prefix ~/.higgsfield/video-editor-mcp ${UPDATE_PACKAGE}); an npx cache path must not be registered.`
15121
+ function listing() {
15122
+ try {
15123
+ const manifest = JSON.parse(
15124
+ readFileSync5(join6(dirname4(DIST), "plugin/.codex-plugin/plugin.json"), "utf8")
15023
15125
  );
15024
- if (!existsSync4(SERVER))
15025
- throw new Error(`The server is missing at ${SERVER}; reinstall the package.`);
15126
+ return {
15127
+ displayName: manifest.interface?.displayName ?? "Higgsfield Video Editor",
15128
+ defaultPrompt: manifest.interface?.defaultPrompt ?? []
15129
+ };
15130
+ } catch {
15131
+ return { displayName: "Higgsfield Video Editor", defaultPrompt: [] };
15132
+ }
15026
15133
  }
15027
- var command = process.argv[2] ?? "help";
15134
+ function requirePersistent() {
15135
+ if (!persistent) throw new Error(`Run \`${INSTALL}\` first.`);
15136
+ if (!existsSync5(SERVER)) throw new Error(`The server is missing at ${SERVER}; reinstall the package.`);
15137
+ }
15138
+ function installPlugin() {
15139
+ ensureCodexHome();
15140
+ const options = updateOptions(DIST);
15141
+ const lock = join6(options.root, "update.lock");
15142
+ mkdirSync5(options.root, { recursive: true });
15143
+ try {
15144
+ mkdirSync5(lock);
15145
+ } catch {
15146
+ throw new Error("Another update is running; retry after it finishes.");
15147
+ }
15148
+ try {
15149
+ const dist = selectedDist(options);
15150
+ const refreshed = refreshBundledPlugin(
15151
+ dist === DIST ? options : { ...options, dist, version: readUpdateState(options.root).active },
15152
+ true
15153
+ );
15154
+ return refreshed ? "Registered the plugin with the ChatGPT desktop app." : "This package has no plugin bundle.";
15155
+ } finally {
15156
+ rmSync4(lock, { recursive: true, force: true });
15157
+ }
15158
+ }
15159
+ function handOver(args) {
15160
+ const result = spawnSync2(process.execPath, [installedCli(), ...args], { stdio: "inherit" });
15161
+ process.exitCode = result.status ?? 1;
15162
+ }
15163
+ var HELP = `Usage: npx ${UPDATE_PACKAGE} <install|uninstall|doctor|config|update>
15164
+
15165
+ install install the editor under ${INSTALL_PREFIX} and register it with the ChatGPT desktop app
15166
+ uninstall remove that registration and the installed editor (projects are kept)
15167
+ doctor check Node, ffmpeg, the bundled higgsedit CLI, the server and the panel
15168
+ config print the MCP server entry for another client (Claude Code, Codex CLI)
15169
+ update check npm for a newer version now
15170
+
15171
+ Lower level: install-plugin, install-codex (the MCP server alone; remove it with \`codex mcp remove ${NAME}\`).`;
15172
+ var [command = "help", ...rest] = process.argv.slice(2);
15173
+ var delegated = ["uninstall", "doctor", "config", "update", "install-codex", "install-plugin"];
15028
15174
  try {
15029
- if (command === "config") {
15175
+ if (command === "install") {
15176
+ const { version: version2 } = updateOptions(DIST);
15177
+ if (!persistent) {
15178
+ console.log(`Installing ${UPDATE_PACKAGE} ${version2} under ${INSTALL_PREFIX}...`);
15179
+ installPackage(version2);
15180
+ handOver(["install", ...rest]);
15181
+ } else {
15182
+ const checks = doctor();
15183
+ const failed = checks.filter((check2) => !check2.ok);
15184
+ let registered = false;
15185
+ let plugin;
15186
+ try {
15187
+ plugin = installPlugin();
15188
+ registered = true;
15189
+ } catch (error51) {
15190
+ plugin = `Not registered with the ChatGPT desktop app: ${error51 instanceof Error ? error51.message : String(error51)}`;
15191
+ }
15192
+ const { displayName, defaultPrompt } = listing();
15193
+ const next = registered ? [
15194
+ "",
15195
+ `Start a new chat, type @ and pick ${displayName}, then try:`,
15196
+ ...defaultPrompt.map((prompt) => ` @${displayName} ${prompt}`),
15197
+ "",
15198
+ `Other MCP clients (Claude Code, the Codex CLI): npx ${UPDATE_PACKAGE} config`
15199
+ ] : [
15200
+ "",
15201
+ "For an MCP client (Claude Code, the Codex CLI), add this server:",
15202
+ JSON.stringify(claudeConfig(NAME, entry), null, 2)
15203
+ ];
15204
+ console.log(
15205
+ [
15206
+ `Installed ${UPDATE_PACKAGE} ${version2}.`,
15207
+ plugin,
15208
+ ...failed.map((check2) => `Needs attention: ${check2.name} (${check2.detail})`),
15209
+ ...next
15210
+ ].join("\n")
15211
+ );
15212
+ if (failed.some((check2) => ["server", "launcher", "panel", "higgsedit"].includes(check2.name)))
15213
+ process.exitCode = 1;
15214
+ }
15215
+ } else if (delegated.includes(command) && !persistent && existsSync5(installedCli())) {
15216
+ handOver([command, ...rest]);
15217
+ } else if (command === "uninstall") {
15218
+ if (prefix === void 0) throw new Error(`Nothing is installed under ${INSTALL_PREFIX}.`);
15219
+ console.log(uninstallEditor(prefix));
15220
+ } else if (command === "config") {
15221
+ requirePersistent();
15030
15222
  console.log(JSON.stringify(claudeConfig(NAME, entry), null, 2));
15031
15223
  } else if (command === "doctor") {
15032
15224
  const checks = doctor();
@@ -15036,41 +15228,21 @@ try {
15036
15228
  );
15037
15229
  process.exitCode = ok ? 0 : 1;
15038
15230
  } else if (command === "install-codex") {
15039
- assertPersistentInstall();
15231
+ requirePersistent();
15040
15232
  console.log(installCodex(NAME, entry, [SERVER, LAUNCHER]));
15041
15233
  } else if (command === "install-plugin") {
15042
- ensureCodexHome();
15043
- const options = updateOptions(DIST);
15044
- const lock = join5(options.root, "update.lock");
15045
- mkdirSync4(options.root, { recursive: true });
15046
- try {
15047
- mkdirSync4(lock);
15048
- } catch {
15049
- throw new Error("Another update is running; retry install-plugin after it finishes.");
15050
- }
15051
- try {
15052
- const dist = selectedDist(options);
15053
- const refreshed = refreshBundledPlugin(
15054
- dist === DIST ? options : { ...options, dist, version: readUpdateState(options.root).active },
15055
- true
15056
- );
15057
- console.log(
15058
- refreshed ? "Installed the bundled plugin. Reload the desktop app to load its skills." : "This package has no plugin bundle yet."
15059
- );
15060
- } finally {
15061
- rmSync3(lock, { recursive: true, force: true });
15062
- }
15234
+ requirePersistent();
15235
+ console.log(`${installPlugin()} Reload the desktop app to load its skills.`);
15063
15236
  } else if (command === "update") {
15237
+ requirePersistent();
15064
15238
  const result = updateEditor(updateOptions(DIST), true);
15065
15239
  console.log(result);
15066
15240
  if (result.startsWith("Update failed")) process.exitCode = 1;
15067
15241
  } else if (["help", "--help", "-h"].includes(command)) {
15068
- console.log(
15069
- `Usage: fnf-video-editor <doctor|config|install-codex|install-plugin|update>
15070
- The MCP server is fnf-video-editor-mcp (stdio). Remove it with: codex mcp remove ${NAME}`
15071
- );
15242
+ console.log(HELP);
15072
15243
  } else {
15073
- throw new Error(`Unknown command '${command}'. Use --help.`);
15244
+ throw new Error(`Unknown command '${command}'.
15245
+ ${HELP}`);
15074
15246
  }
15075
15247
  } catch (error51) {
15076
15248
  console.error(error51 instanceof Error ? error51.message : String(error51));
package/dist/guide.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # Local project guide
2
2
 
3
- The open folder is a video-editor-next project, where the TSX is the document. Its `higgsedit` is not
4
- the sandbox CLI of the video-editing skill: never use `project.json`, `higgsedit build`, `do`/`ops` or
5
- `sandbox_exec` on it.
3
+ The open folder is a higgsedit project: the TSX is the document, and every edit below changes it on the
4
+ user's disk. Nothing is uploaded.
6
5
 
7
6
  ## Project
8
7