brainferno-mcp-bridge 0.1.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 (111) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +27 -0
  4. package/README.md +410 -0
  5. package/THIRD_PARTY_NOTICES.md +52 -0
  6. package/dist/bridge/handshake.d.ts +34 -0
  7. package/dist/bridge/handshake.js +36 -0
  8. package/dist/bridge/handshake.js.map +1 -0
  9. package/dist/bridge/script-escape.d.ts +16 -0
  10. package/dist/bridge/script-escape.js +47 -0
  11. package/dist/bridge/script-escape.js.map +1 -0
  12. package/dist/bridge/socket.d.ts +79 -0
  13. package/dist/bridge/socket.js +424 -0
  14. package/dist/bridge/socket.js.map +1 -0
  15. package/dist/bridge/types.d.ts +49 -0
  16. package/dist/bridge/types.js +38 -0
  17. package/dist/bridge/types.js.map +1 -0
  18. package/dist/config.d.ts +87 -0
  19. package/dist/config.js +172 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/drivers/ame-webservice.d.ts +118 -0
  22. package/dist/drivers/ame-webservice.js +356 -0
  23. package/dist/drivers/ame-webservice.js.map +1 -0
  24. package/dist/drivers/illustrator-delegate.d.ts +71 -0
  25. package/dist/drivers/illustrator-delegate.js +120 -0
  26. package/dist/drivers/illustrator-delegate.js.map +1 -0
  27. package/dist/drivers/osscript.d.ts +60 -0
  28. package/dist/drivers/osscript.js +230 -0
  29. package/dist/drivers/osscript.js.map +1 -0
  30. package/dist/http.d.ts +26 -0
  31. package/dist/http.js +126 -0
  32. package/dist/http.js.map +1 -0
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.js +42 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/install/cli.d.ts +2 -0
  37. package/dist/install/cli.js +310 -0
  38. package/dist/install/cli.js.map +1 -0
  39. package/dist/install/lib.d.ts +76 -0
  40. package/dist/install/lib.js +194 -0
  41. package/dist/install/lib.js.map +1 -0
  42. package/dist/jobs.d.ts +117 -0
  43. package/dist/jobs.js +196 -0
  44. package/dist/jobs.js.map +1 -0
  45. package/dist/logging.d.ts +8 -0
  46. package/dist/logging.js +33 -0
  47. package/dist/logging.js.map +1 -0
  48. package/dist/server.d.ts +38 -0
  49. package/dist/server.js +90 -0
  50. package/dist/server.js.map +1 -0
  51. package/dist/tools/after-effects.d.ts +100 -0
  52. package/dist/tools/after-effects.js +695 -0
  53. package/dist/tools/after-effects.js.map +1 -0
  54. package/dist/tools/audio.d.ts +118 -0
  55. package/dist/tools/audio.js +356 -0
  56. package/dist/tools/audio.js.map +1 -0
  57. package/dist/tools/audition.d.ts +15 -0
  58. package/dist/tools/audition.js +329 -0
  59. package/dist/tools/audition.js.map +1 -0
  60. package/dist/tools/diagnostics.d.ts +17 -0
  61. package/dist/tools/diagnostics.js +61 -0
  62. package/dist/tools/diagnostics.js.map +1 -0
  63. package/dist/tools/illustrator-delegate.d.ts +9 -0
  64. package/dist/tools/illustrator-delegate.js +61 -0
  65. package/dist/tools/illustrator-delegate.js.map +1 -0
  66. package/dist/tools/illustrator.d.ts +36 -0
  67. package/dist/tools/illustrator.js +299 -0
  68. package/dist/tools/illustrator.js.map +1 -0
  69. package/dist/tools/jobs.d.ts +39 -0
  70. package/dist/tools/jobs.js +83 -0
  71. package/dist/tools/jobs.js.map +1 -0
  72. package/dist/tools/media-encoder.d.ts +17 -0
  73. package/dist/tools/media-encoder.js +180 -0
  74. package/dist/tools/media-encoder.js.map +1 -0
  75. package/dist/tools/photoshop.d.ts +17 -0
  76. package/dist/tools/photoshop.js +202 -0
  77. package/dist/tools/photoshop.js.map +1 -0
  78. package/dist/tools/pipelines.d.ts +23 -0
  79. package/dist/tools/pipelines.js +254 -0
  80. package/dist/tools/pipelines.js.map +1 -0
  81. package/dist/tools/premiere.d.ts +26 -0
  82. package/dist/tools/premiere.js +396 -0
  83. package/dist/tools/premiere.js.map +1 -0
  84. package/dist/tools/result.d.ts +17 -0
  85. package/dist/tools/result.js +51 -0
  86. package/dist/tools/result.js.map +1 -0
  87. package/package.json +64 -0
  88. package/panels/panel-cep/CSXS/manifest.xml +53 -0
  89. package/panels/panel-cep/bridge-client.js +204 -0
  90. package/panels/panel-cep/host.jsx +62 -0
  91. package/panels/panel-cep/icons/logo.svg +61 -0
  92. package/panels/panel-cep/index.html +48 -0
  93. package/panels/panel-cep/main.js +173 -0
  94. package/panels/panel-uxp/README.md +36 -0
  95. package/panels/panel-uxp/bridge-client.js +204 -0
  96. package/panels/panel-uxp/commands.js +375 -0
  97. package/panels/panel-uxp/icons/icon.png +0 -0
  98. package/panels/panel-uxp/icons/icon@2x.png +0 -0
  99. package/panels/panel-uxp/icons/logo.svg +61 -0
  100. package/panels/panel-uxp/index.html +47 -0
  101. package/panels/panel-uxp/main.js +109 -0
  102. package/panels/panel-uxp/manifest.json +89 -0
  103. package/panels/panel-uxp-ppro/README.md +11 -0
  104. package/panels/panel-uxp-ppro/bridge-client.js +204 -0
  105. package/panels/panel-uxp-ppro/commands.js +797 -0
  106. package/panels/panel-uxp-ppro/icons/icon.png +0 -0
  107. package/panels/panel-uxp-ppro/icons/icon@2x.png +0 -0
  108. package/panels/panel-uxp-ppro/icons/logo.svg +61 -0
  109. package/panels/panel-uxp-ppro/index.html +64 -0
  110. package/panels/panel-uxp-ppro/main.js +113 -0
  111. package/panels/panel-uxp-ppro/manifest.json +65 -0
@@ -0,0 +1,52 @@
1
+ # Third-party notices
2
+
3
+ Brainferno MCP Bridge is licensed under the Apache License 2.0 (see `LICENSE`). It depends
4
+ on, talks to, or was informed by the following third-party software and materials. None of
5
+ the Adobe materials below are copied into this repository.
6
+
7
+ ## Runtime dependencies (bundled by `npm install`)
8
+
9
+ | Package | License | Copyright |
10
+ | --- | --- | --- |
11
+ | `@modelcontextprotocol/sdk` | MIT | Anthropic, PBC |
12
+ | `ws` | MIT | Einar Otto Stangvik and contributors |
13
+ | `zod` | MIT | Colin McDonnell |
14
+
15
+ Development-only dependencies (`typescript`, `vitest`, `tsx`, `@types/*`) are MIT or
16
+ Apache-2.0 licensed and are not distributed with the product.
17
+
18
+ ## Adobe interfaces this software talks to (not distributed here)
19
+
20
+ - **Adobe UXP** (Photoshop, Premiere Pro panels) and **Adobe CEP** (After Effects,
21
+ Audition panel), **ExtendScript**, the **Illustrator MCP server** built into Adobe
22
+ Illustrator, the **Adobe Media Encoder web service** (`ame_webservice_console`), and
23
+ **aerender**. These ship with the Adobe applications under Adobe's own terms; users need
24
+ their own licensed copies. Use of the Adobe developer platforms is governed by the Adobe
25
+ Developer Terms of Use (https://www.adobe.com/legal/terms/developer.html).
26
+ - `@adobe/premierepro` (TypeScript type definitions, Apache-2.0, Adobe) was used as a
27
+ reference while writing the Premiere Pro panel. It is not bundled.
28
+ - Adobe sample code was read for API behavior but not copied: `Adobe-CEP/Samples` (MIT,
29
+ Adobe), `AdobeDocs/uxp-premiere-pro-samples` (Apache-2.0, Adobe), and the Remote AME web
30
+ console assets shipped with Adobe Media Encoder.
31
+ - `docs/api-dumps/audition-26.3.json` is a list of class, property and method names and
32
+ types obtained by ExtendScript reflection from Adobe Audition, recorded for
33
+ interoperability. Adobe-authored help text was removed before publishing.
34
+
35
+ ## Documentation consulted
36
+
37
+ - Adobe Developer documentation for UXP, Premiere Pro, and Photoshop
38
+ (https://developer.adobe.com), Adobe's CEP resources (https://github.com/Adobe-CEP), and
39
+ the community-maintained *Adobe Media Encoder Scripting Guide*
40
+ (https://ame-scripting.docsforadobe.dev, © Adobe).
41
+
42
+ ## Optional external programs (not distributed here)
43
+
44
+ - **ffmpeg / ffprobe** (https://ffmpeg.org; LGPL-2.1+ or GPL depending on the build) power
45
+ the `audio_*` tools. The user installs ffmpeg separately; this software only runs it.
46
+ - **Node.js** (MIT-style license, https://github.com/nodejs/node/blob/main/LICENSE).
47
+
48
+ ## Trademarks
49
+
50
+ Adobe, After Effects, Audition, Creative Cloud, Illustrator, Media Encoder, Photoshop, and
51
+ Premiere Pro are either registered trademarks or trademarks of Adobe in the United States
52
+ and/or other countries. Other names may be trademarks of their respective owners.
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The handshake file solves two problems at once for the in-app panels:
3
+ * discovering which port the bridge bound, and learning the per-install token
4
+ * they must present. The server writes it once it is listening; each panel
5
+ * reads it (CEP via Node `fs`, UXP via `localFileSystem: "fullAccess"`) before
6
+ * dialing out.
7
+ *
8
+ * It is written mode-600 under the user's home directory. It carries a live
9
+ * secret, so it must never be world-readable and is removed on clean shutdown.
10
+ */
11
+ export interface Handshake {
12
+ /** Wire protocol the server speaks; panels refuse a mismatch. */
13
+ protocolVersion: number;
14
+ /** Loopback port the bridge is listening on. */
15
+ port: number;
16
+ /** Shared secret the panel must present, or "" when auth is disabled. */
17
+ token: string;
18
+ /** Server process id, so a panel can detect a stale file. */
19
+ pid: number;
20
+ }
21
+ /** Default location of the handshake file. */
22
+ export declare function defaultHandshakePath(): string;
23
+ /**
24
+ * Writes the handshake file mode-600, creating its directory if needed.
25
+ *
26
+ * The default write flag ("w") follows symlinks and applies its mode only when
27
+ * creating a new file — so a pre-existing (or symlinked) file could see the
28
+ * token written through it with looser permissions. Remove any existing entry
29
+ * first and create exclusively ("wx", i.e. O_CREAT|O_EXCL, which refuses to
30
+ * follow an existing symlink) so the secret always lands in a fresh 600 file.
31
+ */
32
+ export declare function writeHandshake(path: string, handshake: Handshake): void;
33
+ /** Removes the handshake file, ignoring the case where it is already gone. */
34
+ export declare function removeHandshake(path: string): void;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The handshake file solves two problems at once for the in-app panels:
3
+ * discovering which port the bridge bound, and learning the per-install token
4
+ * they must present. The server writes it once it is listening; each panel
5
+ * reads it (CEP via Node `fs`, UXP via `localFileSystem: "fullAccess"`) before
6
+ * dialing out.
7
+ *
8
+ * It is written mode-600 under the user's home directory. It carries a live
9
+ * secret, so it must never be world-readable and is removed on clean shutdown.
10
+ */
11
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
12
+ import { homedir } from "node:os";
13
+ import { dirname, join } from "node:path";
14
+ /** Default location of the handshake file. */
15
+ export function defaultHandshakePath() {
16
+ return join(homedir(), ".brainferno-mcp-bridge", "bridge.json");
17
+ }
18
+ /**
19
+ * Writes the handshake file mode-600, creating its directory if needed.
20
+ *
21
+ * The default write flag ("w") follows symlinks and applies its mode only when
22
+ * creating a new file — so a pre-existing (or symlinked) file could see the
23
+ * token written through it with looser permissions. Remove any existing entry
24
+ * first and create exclusively ("wx", i.e. O_CREAT|O_EXCL, which refuses to
25
+ * follow an existing symlink) so the secret always lands in a fresh 600 file.
26
+ */
27
+ export function writeHandshake(path, handshake) {
28
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
29
+ rmSync(path, { force: true });
30
+ writeFileSync(path, JSON.stringify(handshake, null, 2), { mode: 0o600, flag: "wx" });
31
+ }
32
+ /** Removes the handshake file, ignoring the case where it is already gone. */
33
+ export function removeHandshake(path) {
34
+ rmSync(path, { force: true });
35
+ }
36
+ //# sourceMappingURL=handshake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../src/bridge/handshake.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAa1C,8CAA8C;AAC9C,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,wBAAwB,EAAE,aAAa,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,SAAoB;IAC/D,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9B,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Turning a runtime string into a literal that is safe to splice into generated
3
+ * host script source (ExtendScript ES3, or UXP JS).
4
+ *
5
+ * `JSON.stringify` is *not* a correct JS-source escaper: it emits U+2028 (LINE
6
+ * SEPARATOR) and U+2029 (PARAGRAPH SEPARATOR) verbatim, and both are line
7
+ * terminators to a JavaScript parser — an ES3 engine treats them as a newline
8
+ * mid-string and throws a syntax error (and, for attacker-influenced input such
9
+ * as a layer or comp name, it is a script-injection seam). Escape them, along
10
+ * with quotes, backslashes, and the other control characters, explicitly.
11
+ */
12
+ /**
13
+ * Returns a double-quoted JavaScript string literal (quotes included) encoding
14
+ * `value`, safe to embed directly in generated script source.
15
+ */
16
+ export declare function jsStringLiteral(value: string): string;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Turning a runtime string into a literal that is safe to splice into generated
3
+ * host script source (ExtendScript ES3, or UXP JS).
4
+ *
5
+ * `JSON.stringify` is *not* a correct JS-source escaper: it emits U+2028 (LINE
6
+ * SEPARATOR) and U+2029 (PARAGRAPH SEPARATOR) verbatim, and both are line
7
+ * terminators to a JavaScript parser — an ES3 engine treats them as a newline
8
+ * mid-string and throws a syntax error (and, for attacker-influenced input such
9
+ * as a layer or comp name, it is a script-injection seam). Escape them, along
10
+ * with quotes, backslashes, and the other control characters, explicitly.
11
+ */
12
+ const SIMPLE_ESCAPES = {
13
+ "\\": "\\\\",
14
+ '"': '\\"',
15
+ "\n": "\\n",
16
+ "\r": "\\r",
17
+ "\t": "\\t",
18
+ "\b": "\\b",
19
+ "\f": "\\f",
20
+ "\v": "\\v",
21
+ };
22
+ /**
23
+ * Returns a double-quoted JavaScript string literal (quotes included) encoding
24
+ * `value`, safe to embed directly in generated script source.
25
+ */
26
+ export function jsStringLiteral(value) {
27
+ let out = '"';
28
+ for (const ch of value) {
29
+ const mapped = SIMPLE_ESCAPES[ch];
30
+ if (mapped !== undefined) {
31
+ out += mapped;
32
+ continue;
33
+ }
34
+ const code = ch.codePointAt(0);
35
+ // U+2028 / U+2029 are JS line terminators; C0/C1 controls must not appear
36
+ // raw in source. All of these are in the BMP, so a 4-digit \uXXXX escape is
37
+ // always sufficient (and ES3-valid). Everything else passes through.
38
+ if (code < 0x20 || (code >= 0x7f && code <= 0x9f) || code === 0x2028 || code === 0x2029) {
39
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
40
+ }
41
+ else {
42
+ out += ch;
43
+ }
44
+ }
45
+ return out + '"';
46
+ }
47
+ //# sourceMappingURL=script-escape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-escape.js","sourceRoot":"","sources":["../../src/bridge/script-escape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,cAAc,GAA2B;IAC7C,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,KAAK;CACZ,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,IAAI,GAAG,GAAG,GAAG,CAAC;IACd,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;QAClC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,GAAG,IAAI,MAAM,CAAC;YACd,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC;QAChC,0EAA0E;QAC1E,4EAA4E;QAC5E,qEAAqE;QACrE,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACxF,GAAG,IAAI,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,GAAG,IAAI,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,GAAG,GAAG,GAAG,CAAC;AACnB,CAAC"}
@@ -0,0 +1,79 @@
1
+ import type { AppId } from "@brainferno/mcp-bridge-protocol";
2
+ import { type AppBridge } from "./types.js";
3
+ export interface BridgeServerOptions {
4
+ port: number;
5
+ /** Explicit shared secret. Ignored when `insecure` is set. */
6
+ token: string;
7
+ /** Disable authentication and skip the handshake file (debug/tests only). */
8
+ insecure?: boolean;
9
+ defaultTimeoutMs: number;
10
+ /** Ping cadence; 0 disables the heartbeat (tests). */
11
+ heartbeatIntervalMs?: number;
12
+ /** How long a fresh socket has to send a valid hello before it is closed. */
13
+ authDeadlineMs?: number;
14
+ /** Where to write the port+token handshake file. Skipped when `insecure`. */
15
+ handshakeFilePath?: string;
16
+ /** Extra Origins to accept, beyond loopback origins and the no-Origin case. */
17
+ allowedOrigins?: string[];
18
+ /** Maximum concurrent sockets before new upgrades are refused. */
19
+ maxConnections?: number;
20
+ /** Loopback only by default — this port evaluates arbitrary script. */
21
+ host?: string;
22
+ }
23
+ /**
24
+ * Accepts panel connections and routes commands to whichever application each
25
+ * panel authenticated as. One panel per application; a second authenticated
26
+ * connection for the same application replaces the first.
27
+ *
28
+ * Security posture (a localhost WebSocket is reachable by any local process and,
29
+ * because browsers exempt WebSockets from the same-origin policy, by any web
30
+ * page the user visits): the upgrade is Origin/Host-validated; every socket must
31
+ * present a valid token in its hello within a short deadline before any other
32
+ * frame is honored; and results are matched per-socket so one panel can never
33
+ * settle another's in-flight call.
34
+ */
35
+ export declare class BridgeServer {
36
+ private readonly options;
37
+ private wss;
38
+ private readonly panels;
39
+ private readonly states;
40
+ /** Serializes commands per app: one script/modal scope per host at a time. */
41
+ private readonly appQueues;
42
+ private readonly token;
43
+ private readonly insecure;
44
+ private readonly heartbeatIntervalMs;
45
+ private readonly authDeadlineMs;
46
+ private readonly allowedOrigins;
47
+ private readonly maxConnections;
48
+ private heartbeat?;
49
+ constructor(options: BridgeServerOptions);
50
+ private listen;
51
+ /**
52
+ * Resolves once a listening socket is bound. If the preferred port is taken
53
+ * (typically a stale server instance still holding it), falls back to an
54
+ * OS-assigned port: panels discover the real port from the handshake file,
55
+ * so a fixed port is a preference, not a requirement.
56
+ */
57
+ ready(): Promise<void>;
58
+ private waitListening;
59
+ /** The bound port, or 0 before the server is listening. */
60
+ port(): number;
61
+ bridgeFor(appId: AppId): AppBridge;
62
+ connectedApps(): AppId[];
63
+ private onListening;
64
+ private verifyUpgrade;
65
+ private originAllowed;
66
+ private onConnection;
67
+ private onMessage;
68
+ private handleHello;
69
+ private tokenMatches;
70
+ private settle;
71
+ private onClose;
72
+ private pingAll;
73
+ private execute;
74
+ private dispatch;
75
+ private resolveTimeout;
76
+ private connectHint;
77
+ private send;
78
+ close(): Promise<void>;
79
+ }