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.
- package/CHANGELOG.md +19 -0
- package/LICENSE +202 -0
- package/NOTICE +27 -0
- package/README.md +410 -0
- package/THIRD_PARTY_NOTICES.md +52 -0
- package/dist/bridge/handshake.d.ts +34 -0
- package/dist/bridge/handshake.js +36 -0
- package/dist/bridge/handshake.js.map +1 -0
- package/dist/bridge/script-escape.d.ts +16 -0
- package/dist/bridge/script-escape.js +47 -0
- package/dist/bridge/script-escape.js.map +1 -0
- package/dist/bridge/socket.d.ts +79 -0
- package/dist/bridge/socket.js +424 -0
- package/dist/bridge/socket.js.map +1 -0
- package/dist/bridge/types.d.ts +49 -0
- package/dist/bridge/types.js +38 -0
- package/dist/bridge/types.js.map +1 -0
- package/dist/config.d.ts +87 -0
- package/dist/config.js +172 -0
- package/dist/config.js.map +1 -0
- package/dist/drivers/ame-webservice.d.ts +118 -0
- package/dist/drivers/ame-webservice.js +356 -0
- package/dist/drivers/ame-webservice.js.map +1 -0
- package/dist/drivers/illustrator-delegate.d.ts +71 -0
- package/dist/drivers/illustrator-delegate.js +120 -0
- package/dist/drivers/illustrator-delegate.js.map +1 -0
- package/dist/drivers/osscript.d.ts +60 -0
- package/dist/drivers/osscript.js +230 -0
- package/dist/drivers/osscript.js.map +1 -0
- package/dist/http.d.ts +26 -0
- package/dist/http.js +126 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/install/cli.d.ts +2 -0
- package/dist/install/cli.js +310 -0
- package/dist/install/cli.js.map +1 -0
- package/dist/install/lib.d.ts +76 -0
- package/dist/install/lib.js +194 -0
- package/dist/install/lib.js.map +1 -0
- package/dist/jobs.d.ts +117 -0
- package/dist/jobs.js +196 -0
- package/dist/jobs.js.map +1 -0
- package/dist/logging.d.ts +8 -0
- package/dist/logging.js +33 -0
- package/dist/logging.js.map +1 -0
- package/dist/server.d.ts +38 -0
- package/dist/server.js +90 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/after-effects.d.ts +100 -0
- package/dist/tools/after-effects.js +695 -0
- package/dist/tools/after-effects.js.map +1 -0
- package/dist/tools/audio.d.ts +118 -0
- package/dist/tools/audio.js +356 -0
- package/dist/tools/audio.js.map +1 -0
- package/dist/tools/audition.d.ts +15 -0
- package/dist/tools/audition.js +329 -0
- package/dist/tools/audition.js.map +1 -0
- package/dist/tools/diagnostics.d.ts +17 -0
- package/dist/tools/diagnostics.js +61 -0
- package/dist/tools/diagnostics.js.map +1 -0
- package/dist/tools/illustrator-delegate.d.ts +9 -0
- package/dist/tools/illustrator-delegate.js +61 -0
- package/dist/tools/illustrator-delegate.js.map +1 -0
- package/dist/tools/illustrator.d.ts +36 -0
- package/dist/tools/illustrator.js +299 -0
- package/dist/tools/illustrator.js.map +1 -0
- package/dist/tools/jobs.d.ts +39 -0
- package/dist/tools/jobs.js +83 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/media-encoder.d.ts +17 -0
- package/dist/tools/media-encoder.js +180 -0
- package/dist/tools/media-encoder.js.map +1 -0
- package/dist/tools/photoshop.d.ts +17 -0
- package/dist/tools/photoshop.js +202 -0
- package/dist/tools/photoshop.js.map +1 -0
- package/dist/tools/pipelines.d.ts +23 -0
- package/dist/tools/pipelines.js +254 -0
- package/dist/tools/pipelines.js.map +1 -0
- package/dist/tools/premiere.d.ts +26 -0
- package/dist/tools/premiere.js +396 -0
- package/dist/tools/premiere.js.map +1 -0
- package/dist/tools/result.d.ts +17 -0
- package/dist/tools/result.js +51 -0
- package/dist/tools/result.js.map +1 -0
- package/package.json +64 -0
- package/panels/panel-cep/CSXS/manifest.xml +53 -0
- package/panels/panel-cep/bridge-client.js +204 -0
- package/panels/panel-cep/host.jsx +62 -0
- package/panels/panel-cep/icons/logo.svg +61 -0
- package/panels/panel-cep/index.html +48 -0
- package/panels/panel-cep/main.js +173 -0
- package/panels/panel-uxp/README.md +36 -0
- package/panels/panel-uxp/bridge-client.js +204 -0
- package/panels/panel-uxp/commands.js +375 -0
- package/panels/panel-uxp/icons/icon.png +0 -0
- package/panels/panel-uxp/icons/icon@2x.png +0 -0
- package/panels/panel-uxp/icons/logo.svg +61 -0
- package/panels/panel-uxp/index.html +47 -0
- package/panels/panel-uxp/main.js +109 -0
- package/panels/panel-uxp/manifest.json +89 -0
- package/panels/panel-uxp-ppro/README.md +11 -0
- package/panels/panel-uxp-ppro/bridge-client.js +204 -0
- package/panels/panel-uxp-ppro/commands.js +797 -0
- package/panels/panel-uxp-ppro/icons/icon.png +0 -0
- package/panels/panel-uxp-ppro/icons/icon@2x.png +0 -0
- package/panels/panel-uxp-ppro/icons/logo.svg +61 -0
- package/panels/panel-uxp-ppro/index.html +64 -0
- package/panels/panel-uxp-ppro/main.js +113 -0
- package/panels/panel-uxp-ppro/manifest.json +65 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { networkInterfaces } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
5
|
+
import { INSTALLABLE_APPS, parseApps } from "../config.js";
|
|
6
|
+
export const APP_CHOICES = [
|
|
7
|
+
{ id: "photoshop", label: "Photoshop", folder: /^Adobe Photoshop/i, needs: ["uxp"] },
|
|
8
|
+
{ id: "after_effects", label: "After Effects", folder: /^Adobe After Effects/i, needs: ["cep"] },
|
|
9
|
+
{ id: "premiere", label: "Premiere Pro", folder: /^Adobe Premiere Pro/i, needs: ["uxp"] },
|
|
10
|
+
{ id: "illustrator", label: "Illustrator", folder: /^Adobe Illustrator/i, needs: ["illustrator-key"] },
|
|
11
|
+
{ id: "audition", label: "Audition", folder: /^Adobe Audition/i, needs: ["cep"] },
|
|
12
|
+
{ id: "media_encoder", label: "Media Encoder", folder: /^Adobe Media Encoder/i, needs: ["ame-ini"] },
|
|
13
|
+
];
|
|
14
|
+
/** Which of the six are installed, judged by their app folders. */
|
|
15
|
+
export function detectInstalledApps(platform = process.platform) {
|
|
16
|
+
const root = platform === "win32" ? join(process.env["ProgramFiles"] ?? "C:\\Program Files", "Adobe") : platform === "darwin" ? "/Applications" : "";
|
|
17
|
+
if (!root || !existsSync(root))
|
|
18
|
+
return [];
|
|
19
|
+
let names;
|
|
20
|
+
try {
|
|
21
|
+
names = readdirSync(root);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
return APP_CHOICES.filter((c) => names.some((n) => c.folder.test(n))).map((c) => c.id);
|
|
27
|
+
}
|
|
28
|
+
/** "1,3,5", "1 3", "all", or app names → app ids (in canonical order). */
|
|
29
|
+
export function pickApps(answer, fallback) {
|
|
30
|
+
const a = answer.trim().toLowerCase();
|
|
31
|
+
if (a === "")
|
|
32
|
+
return [...fallback];
|
|
33
|
+
if (a === "all" || a === "*")
|
|
34
|
+
return [...INSTALLABLE_APPS];
|
|
35
|
+
if (/^[\d,\s]+$/.test(a)) {
|
|
36
|
+
const idx = a.split(/[,\s]+/).filter(Boolean).map(Number);
|
|
37
|
+
return APP_CHOICES.filter((_, i) => idx.includes(i + 1)).map((c) => c.id);
|
|
38
|
+
}
|
|
39
|
+
// Names like "ps, ae" — the config parser throws on unknown names.
|
|
40
|
+
return parseApps(answer);
|
|
41
|
+
}
|
|
42
|
+
export function appsNeed(apps, need) {
|
|
43
|
+
return APP_CHOICES.some((c) => apps.includes(c.id) && c.needs.includes(need));
|
|
44
|
+
}
|
|
45
|
+
export const DEFAULT_HTTP_PORT = 7898;
|
|
46
|
+
export function randomToken() {
|
|
47
|
+
return randomBytes(24).toString("base64url");
|
|
48
|
+
}
|
|
49
|
+
/** Non-internal IPv4 addresses, the ones other machines can reach. */
|
|
50
|
+
export function lanAddresses() {
|
|
51
|
+
const out = [];
|
|
52
|
+
for (const list of Object.values(networkInterfaces())) {
|
|
53
|
+
for (const i of list ?? [])
|
|
54
|
+
if (i.family === "IPv4" && !i.internal)
|
|
55
|
+
out.push(i.address);
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The Illustrator MCP key as the user pasted it: either the bare `ilst_…` key or
|
|
61
|
+
* the whole `claude mcp add … --header "Authorization: Bearer ilst_…" …` line
|
|
62
|
+
* Illustrator shows. Returns null when nothing key-like is in there.
|
|
63
|
+
*/
|
|
64
|
+
export function extractIllustratorKey(input) {
|
|
65
|
+
const s = input.trim();
|
|
66
|
+
if (s === "")
|
|
67
|
+
return null;
|
|
68
|
+
const bearer = /Bearer\s+([A-Za-z0-9_.-]+)/i.exec(s);
|
|
69
|
+
if (bearer)
|
|
70
|
+
return bearer[1] ?? null;
|
|
71
|
+
if (/^[A-Za-z0-9_.-]{12,}$/.test(s))
|
|
72
|
+
return s;
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
/** The endpoint URL from a pasted `claude mcp add` line, if it carries one. */
|
|
76
|
+
export function extractIllustratorUrl(input) {
|
|
77
|
+
const m = /(https?:\/\/[^\s"']+\/mcp)\b/i.exec(input);
|
|
78
|
+
return m ? (m[1] ?? null) : null;
|
|
79
|
+
}
|
|
80
|
+
/** Try an MCP initialize against Adobe's Illustrator endpoint with the key. */
|
|
81
|
+
export async function checkIllustratorKey(url, key, timeoutMs = 4000) {
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
84
|
+
try {
|
|
85
|
+
const res = await fetch(url, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
signal: controller.signal,
|
|
88
|
+
headers: { authorization: `Bearer ${key}`, "content-type": "application/json", accept: "application/json, text/event-stream" },
|
|
89
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "brainferno-mcp-bridge-installer", version: "0.1.0" } } }),
|
|
90
|
+
});
|
|
91
|
+
if (res.status === 401 || res.status === 403)
|
|
92
|
+
return { ok: false, reason: "refused", detail: `HTTP ${res.status}` };
|
|
93
|
+
if (!res.ok)
|
|
94
|
+
return { ok: false, reason: "unexpected", detail: `HTTP ${res.status}` };
|
|
95
|
+
const text = await res.text();
|
|
96
|
+
const m = /"serverInfo"\s*:\s*\{[^}]*"name"\s*:\s*"([^"]+)"/.exec(text);
|
|
97
|
+
return { ok: true, serverName: m ? (m[1] ?? null) : null };
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
101
|
+
return { ok: false, reason: /ECONNREFUSED|abort|fetch failed/i.test(msg) ? "not-running" : "unexpected", detail: msg };
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
clearTimeout(timer);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** Apply the chosen mode to the user config file (other keys untouched). */
|
|
108
|
+
export function mergeUserConfig(existing, mode, o = {}) {
|
|
109
|
+
const next = { ...existing };
|
|
110
|
+
if (o.apps !== undefined) {
|
|
111
|
+
const all = INSTALLABLE_APPS.every((a) => o.apps.includes(a));
|
|
112
|
+
if (all)
|
|
113
|
+
delete next.enabledApps;
|
|
114
|
+
else
|
|
115
|
+
next.enabledApps = INSTALLABLE_APPS.filter((a) => o.apps.includes(a));
|
|
116
|
+
}
|
|
117
|
+
if (o.illustratorKey !== undefined) {
|
|
118
|
+
if (o.illustratorKey === null)
|
|
119
|
+
delete next.illustratorKey;
|
|
120
|
+
else
|
|
121
|
+
next.illustratorKey = o.illustratorKey;
|
|
122
|
+
}
|
|
123
|
+
if (o.illustratorUrl !== undefined) {
|
|
124
|
+
if (o.illustratorUrl === null)
|
|
125
|
+
delete next.illustratorUrl;
|
|
126
|
+
else
|
|
127
|
+
next.illustratorUrl = o.illustratorUrl;
|
|
128
|
+
}
|
|
129
|
+
if (mode === "local") {
|
|
130
|
+
delete next.httpPort;
|
|
131
|
+
delete next.httpHost;
|
|
132
|
+
delete next.httpToken;
|
|
133
|
+
return next;
|
|
134
|
+
}
|
|
135
|
+
next.httpPort = o.port ?? existing.httpPort ?? DEFAULT_HTTP_PORT;
|
|
136
|
+
next.httpHost = o.host ?? "0.0.0.0";
|
|
137
|
+
next.httpToken = o.token ?? existing.httpToken ?? randomToken();
|
|
138
|
+
return next;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Media Encoder's web-service ini: local pins `ip = 127.0.0.1`; shared leaves
|
|
142
|
+
* the address to the service (it picks a LAN adapter). Other lines are kept.
|
|
143
|
+
*/
|
|
144
|
+
export function rewriteAmeIni(ini, mode) {
|
|
145
|
+
const lines = ini.split(/\r?\n/);
|
|
146
|
+
let seen = false;
|
|
147
|
+
const out = lines.map((line) => {
|
|
148
|
+
const m = /^\s*(#\s*)?ip\s*=\s*(.*)$/.exec(line);
|
|
149
|
+
if (!m)
|
|
150
|
+
return line;
|
|
151
|
+
seen = true;
|
|
152
|
+
return mode === "local" ? "ip = 127.0.0.1" : "#ip = 127.0.0.1";
|
|
153
|
+
});
|
|
154
|
+
if (!seen && mode === "local")
|
|
155
|
+
out.unshift("ip = 127.0.0.1");
|
|
156
|
+
const eol = ini.includes("\r\n") ? "\r\n" : "\n";
|
|
157
|
+
return out.join(eol);
|
|
158
|
+
}
|
|
159
|
+
export function platformPaths(platform, home, appData) {
|
|
160
|
+
if (platform === "win32") {
|
|
161
|
+
const roaming = appData ?? join(home, "AppData", "Roaming");
|
|
162
|
+
const pf = process.env["ProgramFiles"] ?? "C:\\Program Files";
|
|
163
|
+
return {
|
|
164
|
+
cepExtensionsDir: join(roaming, "Adobe", "CEP", "extensions"),
|
|
165
|
+
csxsDebugCommands: [11, 12, 13, 14].map((v) => ["reg", "add", `HKCU\\Software\\Adobe\\CSXS.${v}`, "/v", "PlayerDebugMode", "/t", "REG_SZ", "/d", "1", "/f"]),
|
|
166
|
+
ameIniCandidates: [2027, 2026, 2025, 2024].map((y) => join(pf, "Adobe", `Adobe Media Encoder ${y}`, "ame_webservice_config.ini")),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (platform === "darwin") {
|
|
170
|
+
return {
|
|
171
|
+
cepExtensionsDir: join(home, "Library", "Application Support", "Adobe", "CEP", "extensions"),
|
|
172
|
+
csxsDebugCommands: [11, 12, 13, 14].map((v) => ["defaults", "write", `com.adobe.CSXS.${v}`, "PlayerDebugMode", "1"]),
|
|
173
|
+
ameIniCandidates: [2027, 2026, 2025, 2024].map((y) => join("/Applications", `Adobe Media Encoder ${y}`, "ame_webservice_config.ini")),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return { cepExtensionsDir: join(home, ".brainferno-mcp-bridge", "cep-extensions-unsupported"), csxsDebugCommands: [], ameIniCandidates: [] };
|
|
177
|
+
}
|
|
178
|
+
/** Windows Defender Firewall rule for the remote port, private networks only. */
|
|
179
|
+
export function firewallCommands(platform, mode, port) {
|
|
180
|
+
if (platform !== "win32")
|
|
181
|
+
return [];
|
|
182
|
+
const name = "Brainferno MCP Bridge (remote MCP)";
|
|
183
|
+
const del = ["netsh", "advfirewall", "firewall", "delete", "rule", `name=${name}`];
|
|
184
|
+
if (mode === "local")
|
|
185
|
+
return [del];
|
|
186
|
+
return [del, ["netsh", "advfirewall", "firewall", "add", "rule", `name=${name}`, "dir=in", "action=allow", "protocol=TCP", `localport=${port}`, "profile=private"]];
|
|
187
|
+
}
|
|
188
|
+
/** The `claude mcp add` lines to print/run. */
|
|
189
|
+
export function mcpAddCommands(o) {
|
|
190
|
+
const local = `claude mcp add --scope user brainferno -- node "${o.distIndex}"`;
|
|
191
|
+
const remote = o.mode === "shared" ? o.addresses.map((a) => `claude mcp add --scope user --transport http --header "Authorization: Bearer ${o.token}" brainferno http://${a}:${o.port}/mcp`) : [];
|
|
192
|
+
return { local, remote };
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=lib.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib.js","sourceRoot":"","sources":["../../src/install/lib.ts"],"names":[],"mappings":"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"}
|
package/dist/jobs.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory job registry for anything that runs longer than a tool call
|
|
3
|
+
* should block: aerender, Premiere exports, and the cross-app pipelines.
|
|
4
|
+
*
|
|
5
|
+
* A job is a named list of steps run in order. Each step's result is kept,
|
|
6
|
+
* failures record which step broke and which tool recovers it, and a job can
|
|
7
|
+
* be cancelled between steps (process-lane steps also get an AbortSignal so
|
|
8
|
+
* a running ffmpeg/aerender is killed). Every job gets a private work folder
|
|
9
|
+
* under `<workRoot>/<jobId>/` for cross-app hand-offs.
|
|
10
|
+
*/
|
|
11
|
+
export type JobStatus = "queued" | "running" | "succeeded" | "failed" | "cancelled";
|
|
12
|
+
export type StepStatus = "pending" | "running" | "succeeded" | "failed" | "skipped";
|
|
13
|
+
export interface JobContext {
|
|
14
|
+
readonly jobId: string;
|
|
15
|
+
readonly workDir: string;
|
|
16
|
+
readonly signal: AbortSignal;
|
|
17
|
+
/** Append a line to the job log (tail kept) and make it the progress message. */
|
|
18
|
+
log(message: string): void;
|
|
19
|
+
/** Report progress inside the current step (0–100). */
|
|
20
|
+
progress(message: string, percent?: number): void;
|
|
21
|
+
/** Record a file the job produced. */
|
|
22
|
+
artifact(path: string): void;
|
|
23
|
+
}
|
|
24
|
+
export interface JobStepDef {
|
|
25
|
+
name: string;
|
|
26
|
+
/** Tool to call by hand when this step fails. */
|
|
27
|
+
recoveryTool?: string;
|
|
28
|
+
/** Return false to skip this step (recorded as skipped). */
|
|
29
|
+
when?: (previous: unknown[]) => boolean;
|
|
30
|
+
run: (ctx: JobContext, previous: unknown[]) => Promise<unknown>;
|
|
31
|
+
}
|
|
32
|
+
export interface JobStep {
|
|
33
|
+
name: string;
|
|
34
|
+
status: StepStatus;
|
|
35
|
+
recoveryTool?: string;
|
|
36
|
+
startedAt?: number;
|
|
37
|
+
finishedAt?: number;
|
|
38
|
+
result?: unknown;
|
|
39
|
+
error?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface JobFailure {
|
|
42
|
+
step: string;
|
|
43
|
+
message: string;
|
|
44
|
+
recoveryTool?: string;
|
|
45
|
+
completedSteps: string[];
|
|
46
|
+
}
|
|
47
|
+
export interface JobProgress {
|
|
48
|
+
step: string | null;
|
|
49
|
+
stepIndex: number;
|
|
50
|
+
stepCount: number;
|
|
51
|
+
percent: number;
|
|
52
|
+
message: string;
|
|
53
|
+
}
|
|
54
|
+
export interface Job {
|
|
55
|
+
id: string;
|
|
56
|
+
kind: string;
|
|
57
|
+
status: JobStatus;
|
|
58
|
+
createdAt: number;
|
|
59
|
+
startedAt?: number;
|
|
60
|
+
finishedAt?: number;
|
|
61
|
+
workDir: string;
|
|
62
|
+
steps: JobStep[];
|
|
63
|
+
progress: JobProgress;
|
|
64
|
+
artifacts: string[];
|
|
65
|
+
log: string[];
|
|
66
|
+
result?: unknown;
|
|
67
|
+
error?: JobFailure;
|
|
68
|
+
}
|
|
69
|
+
export declare class JobCancelledError extends Error {
|
|
70
|
+
constructor(jobId: string);
|
|
71
|
+
}
|
|
72
|
+
export declare class JobRegistry {
|
|
73
|
+
private readonly options;
|
|
74
|
+
private readonly jobs;
|
|
75
|
+
constructor(options: {
|
|
76
|
+
workRoot: string;
|
|
77
|
+
keep?: number;
|
|
78
|
+
});
|
|
79
|
+
/** Start a job now. Resolves `done` when it reaches a terminal state (never rejects). */
|
|
80
|
+
start(kind: string, steps: JobStepDef[], finalize?: (results: unknown[], job: Job) => unknown): {
|
|
81
|
+
job: Job;
|
|
82
|
+
done: Promise<Job>;
|
|
83
|
+
};
|
|
84
|
+
get(id: string): Job | undefined;
|
|
85
|
+
list(limit?: number): Job[];
|
|
86
|
+
/** Request cancellation. Returns false if the job is unknown or already finished. */
|
|
87
|
+
cancel(id: string): boolean;
|
|
88
|
+
/** Wait for a terminal state or the timeout; `onTick` fires every ~2 s with the live job. */
|
|
89
|
+
wait(id: string, timeoutMs: number, onTick?: (job: Job) => void): Promise<Job>;
|
|
90
|
+
private prune;
|
|
91
|
+
private execute;
|
|
92
|
+
}
|
|
93
|
+
export declare function isTerminal(status: JobStatus): boolean;
|
|
94
|
+
/** JSON-safe view of a job for tool results (no internal handles, bounded log). */
|
|
95
|
+
export declare function jobView(job: Job, options?: {
|
|
96
|
+
includeLog?: boolean;
|
|
97
|
+
}): {
|
|
98
|
+
log?: string[] | undefined;
|
|
99
|
+
error?: JobFailure | undefined;
|
|
100
|
+
result?: {} | null | undefined;
|
|
101
|
+
jobId: string;
|
|
102
|
+
kind: string;
|
|
103
|
+
status: JobStatus;
|
|
104
|
+
createdAt: string;
|
|
105
|
+
startedAt: string | null;
|
|
106
|
+
finishedAt: string | null;
|
|
107
|
+
elapsedSeconds: number;
|
|
108
|
+
progress: JobProgress;
|
|
109
|
+
steps: {
|
|
110
|
+
recoveryTool?: string | undefined;
|
|
111
|
+
error?: string | undefined;
|
|
112
|
+
name: string;
|
|
113
|
+
status: StepStatus;
|
|
114
|
+
}[];
|
|
115
|
+
artifacts: string[];
|
|
116
|
+
workDir: string;
|
|
117
|
+
};
|
package/dist/jobs.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
export class JobCancelledError extends Error {
|
|
5
|
+
constructor(jobId) {
|
|
6
|
+
super(`Job ${jobId} was cancelled.`);
|
|
7
|
+
this.name = "JobCancelledError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const LOG_TAIL = 200;
|
|
11
|
+
export class JobRegistry {
|
|
12
|
+
options;
|
|
13
|
+
jobs = new Map();
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
/** Start a job now. Resolves `done` when it reaches a terminal state (never rejects). */
|
|
18
|
+
start(kind, steps, finalize) {
|
|
19
|
+
const id = randomUUID();
|
|
20
|
+
const job = {
|
|
21
|
+
id,
|
|
22
|
+
kind,
|
|
23
|
+
status: "queued",
|
|
24
|
+
createdAt: Date.now(),
|
|
25
|
+
workDir: join(this.options.workRoot, id),
|
|
26
|
+
steps: steps.map((s) => ({ name: s.name, status: "pending", ...(s.recoveryTool !== undefined ? { recoveryTool: s.recoveryTool } : {}) })),
|
|
27
|
+
progress: { step: null, stepIndex: 0, stepCount: steps.length, percent: 0, message: "queued" },
|
|
28
|
+
artifacts: [],
|
|
29
|
+
log: [],
|
|
30
|
+
};
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const done = this.execute(job, controller, steps, finalize);
|
|
33
|
+
this.jobs.set(id, { job, controller, done });
|
|
34
|
+
this.prune();
|
|
35
|
+
return { job, done };
|
|
36
|
+
}
|
|
37
|
+
get(id) {
|
|
38
|
+
return this.jobs.get(id)?.job;
|
|
39
|
+
}
|
|
40
|
+
list(limit = 50) {
|
|
41
|
+
return [...this.jobs.values()]
|
|
42
|
+
.map((r) => r.job)
|
|
43
|
+
.sort((a, b) => b.createdAt - a.createdAt)
|
|
44
|
+
.slice(0, limit);
|
|
45
|
+
}
|
|
46
|
+
/** Request cancellation. Returns false if the job is unknown or already finished. */
|
|
47
|
+
cancel(id) {
|
|
48
|
+
const rec = this.jobs.get(id);
|
|
49
|
+
if (!rec || isTerminal(rec.job.status))
|
|
50
|
+
return false;
|
|
51
|
+
rec.controller.abort(new JobCancelledError(id));
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
/** Wait for a terminal state or the timeout; `onTick` fires every ~2 s with the live job. */
|
|
55
|
+
async wait(id, timeoutMs, onTick) {
|
|
56
|
+
const rec = this.jobs.get(id);
|
|
57
|
+
if (!rec)
|
|
58
|
+
throw new Error(`No job ${id}. Call cc_list_jobs.`);
|
|
59
|
+
const deadline = Date.now() + timeoutMs;
|
|
60
|
+
let lastTick = 0;
|
|
61
|
+
while (!isTerminal(rec.job.status) && Date.now() < deadline) {
|
|
62
|
+
if (onTick && Date.now() - lastTick >= 2000) {
|
|
63
|
+
lastTick = Date.now();
|
|
64
|
+
onTick(rec.job);
|
|
65
|
+
}
|
|
66
|
+
await Promise.race([rec.done, sleep(500)]);
|
|
67
|
+
}
|
|
68
|
+
return rec.job;
|
|
69
|
+
}
|
|
70
|
+
prune() {
|
|
71
|
+
const keep = this.options.keep ?? 100;
|
|
72
|
+
if (this.jobs.size <= keep)
|
|
73
|
+
return;
|
|
74
|
+
const finished = [...this.jobs.entries()].filter(([, r]) => isTerminal(r.job.status)).sort((a, b) => a[1].job.createdAt - b[1].job.createdAt);
|
|
75
|
+
for (const [id] of finished.slice(0, this.jobs.size - keep))
|
|
76
|
+
this.jobs.delete(id);
|
|
77
|
+
}
|
|
78
|
+
async execute(job, controller, steps, finalize) {
|
|
79
|
+
const results = [];
|
|
80
|
+
const completed = [];
|
|
81
|
+
const setProgress = (i, message, within = 0) => {
|
|
82
|
+
job.progress = {
|
|
83
|
+
step: steps[i]?.name ?? null,
|
|
84
|
+
stepIndex: i,
|
|
85
|
+
stepCount: steps.length,
|
|
86
|
+
percent: steps.length === 0 ? 100 : Math.min(100, Math.round(((i + within / 100) / steps.length) * 100)),
|
|
87
|
+
message,
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
const ctx = (i) => ({
|
|
91
|
+
jobId: job.id,
|
|
92
|
+
workDir: job.workDir,
|
|
93
|
+
signal: controller.signal,
|
|
94
|
+
log: (m) => {
|
|
95
|
+
job.log.push(m);
|
|
96
|
+
if (job.log.length > LOG_TAIL)
|
|
97
|
+
job.log.splice(0, job.log.length - LOG_TAIL);
|
|
98
|
+
setProgress(i, m, within(job));
|
|
99
|
+
},
|
|
100
|
+
progress: (m, p) => setProgress(i, m, p ?? within(job)),
|
|
101
|
+
artifact: (p) => {
|
|
102
|
+
if (!job.artifacts.includes(p))
|
|
103
|
+
job.artifacts.push(p);
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
const within = (j) => (j.progress.stepIndex === job.progress.stepIndex ? Math.round(((j.progress.percent / 100) * steps.length - j.progress.stepIndex) * 100) : 0);
|
|
107
|
+
job.status = "running";
|
|
108
|
+
job.startedAt = Date.now();
|
|
109
|
+
try {
|
|
110
|
+
await mkdir(job.workDir, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
/* the work dir is a convenience; steps that need it will fail loudly */
|
|
114
|
+
}
|
|
115
|
+
for (let i = 0; i < steps.length; i++) {
|
|
116
|
+
const def = steps[i];
|
|
117
|
+
const step = job.steps[i];
|
|
118
|
+
if (controller.signal.aborted) {
|
|
119
|
+
job.status = "cancelled";
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
if (def.when && !def.when(results)) {
|
|
123
|
+
step.status = "skipped";
|
|
124
|
+
results.push(undefined);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
step.status = "running";
|
|
128
|
+
step.startedAt = Date.now();
|
|
129
|
+
setProgress(i, `${def.name}…`);
|
|
130
|
+
try {
|
|
131
|
+
const value = await def.run(ctx(i), results);
|
|
132
|
+
step.result = value;
|
|
133
|
+
step.status = "succeeded";
|
|
134
|
+
step.finishedAt = Date.now();
|
|
135
|
+
results.push(value);
|
|
136
|
+
completed.push(def.name);
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
step.finishedAt = Date.now();
|
|
140
|
+
if (controller.signal.aborted || e instanceof JobCancelledError) {
|
|
141
|
+
step.status = "skipped";
|
|
142
|
+
job.status = "cancelled";
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
146
|
+
step.status = "failed";
|
|
147
|
+
step.error = message;
|
|
148
|
+
job.status = "failed";
|
|
149
|
+
job.error = { step: def.name, message, completedSteps: completed, ...(def.recoveryTool !== undefined ? { recoveryTool: def.recoveryTool } : {}) };
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (job.status === "running") {
|
|
154
|
+
job.status = "succeeded";
|
|
155
|
+
try {
|
|
156
|
+
job.result = finalize ? finalize(results, job) : results;
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
job.status = "failed";
|
|
160
|
+
job.error = { step: "finalize", message: e instanceof Error ? e.message : String(e), completedSteps: completed };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const s of job.steps)
|
|
164
|
+
if (s.status === "pending")
|
|
165
|
+
s.status = "skipped";
|
|
166
|
+
job.finishedAt = Date.now();
|
|
167
|
+
job.progress = { ...job.progress, percent: job.status === "succeeded" ? 100 : job.progress.percent, message: job.status === "succeeded" ? "done" : job.status === "failed" ? `failed at ${job.error?.step ?? "?"}` : job.status };
|
|
168
|
+
return job;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export function isTerminal(status) {
|
|
172
|
+
return status === "succeeded" || status === "failed" || status === "cancelled";
|
|
173
|
+
}
|
|
174
|
+
/** JSON-safe view of a job for tool results (no internal handles, bounded log). */
|
|
175
|
+
export function jobView(job, options = {}) {
|
|
176
|
+
return {
|
|
177
|
+
jobId: job.id,
|
|
178
|
+
kind: job.kind,
|
|
179
|
+
status: job.status,
|
|
180
|
+
createdAt: new Date(job.createdAt).toISOString(),
|
|
181
|
+
startedAt: job.startedAt !== undefined ? new Date(job.startedAt).toISOString() : null,
|
|
182
|
+
finishedAt: job.finishedAt !== undefined ? new Date(job.finishedAt).toISOString() : null,
|
|
183
|
+
elapsedSeconds: job.startedAt !== undefined ? Math.round(((job.finishedAt ?? Date.now()) - job.startedAt) / 1000) : 0,
|
|
184
|
+
progress: job.progress,
|
|
185
|
+
steps: job.steps.map((s) => ({ name: s.name, status: s.status, ...(s.error !== undefined ? { error: s.error } : {}), ...(s.recoveryTool !== undefined && s.status === "failed" ? { recoveryTool: s.recoveryTool } : {}) })),
|
|
186
|
+
artifacts: job.artifacts,
|
|
187
|
+
workDir: job.workDir,
|
|
188
|
+
...(job.result !== undefined ? { result: job.result } : {}),
|
|
189
|
+
...(job.error !== undefined ? { error: job.error } : {}),
|
|
190
|
+
...(options.includeLog ? { log: job.log.slice(-40) } : {}),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function sleep(ms) {
|
|
194
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=jobs.js.map
|
package/dist/jobs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LogLevel } from "./config.js";
|
|
2
|
+
export declare function setLogLevel(level: LogLevel): void;
|
|
3
|
+
export declare const log: {
|
|
4
|
+
error: (message: string, detail?: unknown) => void;
|
|
5
|
+
warn: (message: string, detail?: unknown) => void;
|
|
6
|
+
info: (message: string, detail?: unknown) => void;
|
|
7
|
+
debug: (message: string, detail?: unknown) => void;
|
|
8
|
+
};
|
package/dist/logging.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stdout is the MCP wire when running over stdio, so every diagnostic goes to
|
|
3
|
+
* stderr. Writing a stray console.log anywhere in this server corrupts the
|
|
4
|
+
* protocol stream — always use this logger.
|
|
5
|
+
*/
|
|
6
|
+
const RANK = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
7
|
+
let threshold = "info";
|
|
8
|
+
export function setLogLevel(level) {
|
|
9
|
+
threshold = level;
|
|
10
|
+
}
|
|
11
|
+
function emit(level, message, detail) {
|
|
12
|
+
if (RANK[level] > RANK[threshold])
|
|
13
|
+
return;
|
|
14
|
+
const line = `[brainferno-mcp-bridge] ${level.toUpperCase()} ${message}`;
|
|
15
|
+
process.stderr.write(detail === undefined ? `${line}\n` : `${line} ${format(detail)}\n`);
|
|
16
|
+
}
|
|
17
|
+
function format(detail) {
|
|
18
|
+
if (detail instanceof Error)
|
|
19
|
+
return `${detail.name}: ${detail.message}`;
|
|
20
|
+
try {
|
|
21
|
+
return JSON.stringify(detail);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return String(detail);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export const log = {
|
|
28
|
+
error: (message, detail) => emit("error", message, detail),
|
|
29
|
+
warn: (message, detail) => emit("warn", message, detail),
|
|
30
|
+
info: (message, detail) => emit("info", message, detail),
|
|
31
|
+
debug: (message, detail) => emit("debug", message, detail),
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=logging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,MAAM,IAAI,GAA6B,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAEhF,IAAI,SAAS,GAAa,MAAM,CAAC;AAEjC,MAAM,UAAU,WAAW,CAAC,KAAe;IACzC,SAAS,GAAG,KAAK,CAAC;AACpB,CAAC;AAED,SAAS,IAAI,CAAC,KAAe,EAAE,OAAe,EAAE,MAAgB;IAC9D,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO;IAC1C,MAAM,IAAI,GAAG,2BAA2B,KAAK,CAAC,WAAW,EAAE,IAAI,OAAO,EAAE,CAAC;IACzE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC3F,CAAC;AAED,SAAS,MAAM,CAAC,MAAe;IAC7B,IAAI,MAAM,YAAY,KAAK;QAAE,OAAO,GAAG,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;IACxE,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,KAAK,EAAE,CAAC,OAAe,EAAE,MAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;IAC5E,IAAI,EAAE,CAAC,OAAe,EAAE,MAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC;IAC1E,IAAI,EAAE,CAAC,OAAe,EAAE,MAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC;IAC1E,KAAK,EAAE,CAAC,OAAe,EAAE,MAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;CAC7E,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BridgeServer } from "./bridge/socket.js";
|
|
3
|
+
import type { Config } from "./config.js";
|
|
4
|
+
import { AmeWebService } from "./drivers/ame-webservice.js";
|
|
5
|
+
import { IllustratorDelegate } from "./drivers/illustrator-delegate.js";
|
|
6
|
+
import { OsScriptBridge } from "./drivers/osscript.js";
|
|
7
|
+
import { JobRegistry } from "./jobs.js";
|
|
8
|
+
import { type AudioToolOptions } from "./tools/audio.js";
|
|
9
|
+
/**
|
|
10
|
+
* Everything that lives once per process and is shared by every MCP session:
|
|
11
|
+
* the panel hub, the panel-less drivers, the job registry, the Media Encoder
|
|
12
|
+
* service. An McpServer (one per transport session) is cheap and is created
|
|
13
|
+
* on top of this with {@link createMcpServer}.
|
|
14
|
+
*/
|
|
15
|
+
export interface Runtime {
|
|
16
|
+
config: Config;
|
|
17
|
+
bridge: BridgeServer;
|
|
18
|
+
/** Illustrator is driven panel-less over COM/AppleScript, not through the hub. */
|
|
19
|
+
illustratorBridge: OsScriptBridge;
|
|
20
|
+
illustratorDelegate: IllustratorDelegate;
|
|
21
|
+
jobs: JobRegistry;
|
|
22
|
+
/** Media Encoder's headless web service, started on demand. */
|
|
23
|
+
mediaEncoder: AmeWebService;
|
|
24
|
+
audio: AudioToolOptions;
|
|
25
|
+
}
|
|
26
|
+
export interface BuiltServer extends Runtime {
|
|
27
|
+
server: McpServer;
|
|
28
|
+
}
|
|
29
|
+
export declare function buildRuntime(config: Config): Runtime;
|
|
30
|
+
/**
|
|
31
|
+
* A new McpServer with every tool registered against the shared runtime.
|
|
32
|
+
* Tools are registered unconditionally: an application that has no panel
|
|
33
|
+
* connected still advertises its tools, and those tools return an actionable
|
|
34
|
+
* "not connected" error rather than vanishing from the tool list mid-session.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createMcpServer(rt: Runtime): McpServer;
|
|
37
|
+
/** Runtime plus one McpServer (the stdio session). */
|
|
38
|
+
export declare function buildServer(config: Config): BuiltServer;
|