feat: initial commit - Jhonny Editor

- Adicionado estrutura completa do projeto
- Configurado MCP server para Premiere Pro
- Adicionado documentação e skills
- Configurado Gitignore para o projeto
This commit is contained in:
João Henrique
2026-09-08 09:59:31 -04:00
commit b541f502ba
1507 changed files with 387650 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
import { describe, expect, it, vi } from "vitest";
import { join } from "node:path";
vi.mock("../../src/bridge/file-bridge.js", () => ({
sendCommand: vi.fn().mockResolvedValue({ success: true, data: {} }),
}));
import { sendCommand } from "../../src/bridge/file-bridge.js";
import {
AFTER_EFFECTS_BRIDGE_HELPERS,
AFTER_EFFECTS_DEFAULT_TEMP_DIR_NAME,
getAfterEffectsTempDir,
sendAfterEffectsCommand,
} from "../../src/bridge/after-effects-bridge.js";
describe("After Effects bridge", () => {
it("uses an independent bridge directory and AE-only helpers", async () => {
expect(getAfterEffectsTempDir(undefined, "D:/Temp")).toBe(join("D:/Temp", AFTER_EFFECTS_DEFAULT_TEMP_DIR_NAME));
await sendAfterEffectsCommand("return __aeResult({});", { tempDir: "D:/PremiereOnly" });
expect(sendCommand).toHaveBeenCalledWith("return __aeResult({});", expect.objectContaining({
tempDir: getAfterEffectsTempDir(),
helpers: AFTER_EFFECTS_BRIDGE_HELPERS,
}));
expect(AFTER_EFFECTS_BRIDGE_HELPERS.source).toContain("__aeResult");
expect(AFTER_EFFECTS_BRIDGE_HELPERS.source).not.toContain("__findSequence");
});
});
+349
View File
@@ -0,0 +1,349 @@
import { once } from "node:events";
import {
chmodSync,
existsSync,
mkdirSync,
readFileSync,
readdirSync,
renameSync,
statSync,
unlinkSync,
watch,
writeFileSync,
} from "node:fs";
import { WebSocket } from "ws";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { sendCommand } from "../../src/bridge/file-bridge.js";
import {
UxpWebSocketBridge,
type UxpHello,
} from "../../src/bridge/uxp-websocket-bridge.js";
vi.mock("node:fs", () => ({
existsSync: vi.fn(),
mkdirSync: vi.fn(),
writeFileSync: vi.fn(),
readFileSync: vi.fn(),
unlinkSync: vi.fn(),
readdirSync: vi.fn(),
renameSync: vi.fn(),
statSync: vi.fn(),
chmodSync: vi.fn(),
watch: vi.fn(),
}));
const fs = {
exists: vi.mocked(existsSync),
mkdir: vi.mocked(mkdirSync),
write: vi.mocked(writeFileSync),
read: vi.mocked(readFileSync),
unlink: vi.mocked(unlinkSync),
readdir: vi.mocked(readdirSync),
rename: vi.mocked(renameSync),
stat: vi.mocked(statSync),
chmod: vi.mocked(chmodSync),
watch: vi.mocked(watch),
};
const originalGetuid = Object.getOwnPropertyDescriptor(process, "getuid");
function ownedStat(mode = 0o700): ReturnType<typeof statSync> {
return {
uid: 4242,
mode,
mtimeMs: Date.now(),
} as unknown as ReturnType<typeof statSync>;
}
function usePosixProcess(uid = 4242): void {
vi.spyOn(process, "platform", "get").mockReturnValue("linux");
Object.defineProperty(process, "getuid", {
configurable: true,
value: () => uid,
});
}
function restoreGetuid(): void {
if (originalGetuid) {
Object.defineProperty(process, "getuid", originalGetuid);
} else {
delete (process as { getuid?: () => number }).getuid;
}
}
describe("file bridge uncovered security and fallback paths", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.useFakeTimers();
fs.watch.mockImplementation(() => {
throw new Error("watch unavailable");
});
fs.stat.mockReturnValue(ownedStat());
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
restoreGetuid();
});
it("rejects a pre-existing POSIX directory owned by another user", async () => {
usePosixProcess();
fs.exists.mockReturnValue(true);
fs.stat.mockReturnValue(ownedStat());
fs.stat.mockReturnValueOnce({
uid: 31337,
mode: 0o700,
mtimeMs: Date.now(),
} as unknown as ReturnType<typeof statSync>);
await expect(sendCommand("var unsafe = true;", {
tempDir: "/tmp/untrusted-bridge",
})).rejects.toThrow(/owned by uid 31337/);
});
it("restores private POSIX permissions on an owned directory", async () => {
usePosixProcess();
fs.exists.mockReturnValue(true);
fs.stat.mockReturnValueOnce(ownedStat(0o755));
fs.read.mockReturnValue('{"success":true,"data":{"repaired":true}}');
await expect(sendCommand("var repair = true;", {
tempDir: "/tmp/owned-bridge",
})).resolves.toEqual({ success: true, data: { repaired: true } });
expect(fs.chmod).toHaveBeenCalledWith("/tmp/owned-bridge", 0o700);
});
it("reports a busy Premiere operation when its heartbeat cannot be statted", async () => {
usePosixProcess();
fs.exists.mockImplementation((path) => {
const value = String(path);
if (value.includes("res_")) return false;
if (value.includes("busy_")) return true;
return true;
});
fs.stat.mockImplementation(((path) => {
if (String(path).includes("busy_")) throw new Error("heartbeat locked");
return ownedStat();
}) as typeof statSync);
const response = sendCommand("var waiting = true;", {
tempDir: "/tmp/busy-stat-error",
timeoutMs: 25,
});
await vi.advanceTimersByTimeAsync(110);
await expect(response).resolves.toMatchObject({
success: false,
error: expect.stringContaining("modal dialog"),
});
});
it("normalizes non-Error response read failures into command results", async () => {
usePosixProcess();
fs.exists.mockImplementation((path) => !String(path).includes("busy_"));
fs.read.mockImplementation(() => {
throw "response file is locked";
});
const response = sendCommand("var parse = true;", {
tempDir: "/tmp/non-error-read-failure",
timeoutMs: 25,
});
await vi.advanceTimersByTimeAsync(110);
await expect(response).resolves.toEqual({
success: false,
error: "Failed to parse response: response file is locked",
});
});
});
const TOKEN = "bridge-edge-token-at-least-16-characters";
const liveBridges: UxpWebSocketBridge[] = [];
async function startBridge(options: {
port?: number;
handshakeTimeoutMs?: number;
requestTimeoutMs?: number;
} = {}): Promise<UxpWebSocketBridge> {
const bridge = new UxpWebSocketBridge({ token: TOKEN, port: 0, ...options });
liveBridges.push(bridge);
await bridge.start();
return bridge;
}
function bridgeUrl(bridge: UxpWebSocketBridge, token = TOKEN): string {
const address = bridge.address();
return `ws://${address.host}:${address.port}${address.path}?token=${encodeURIComponent(token)}`;
}
async function connectHost(
bridge: UxpWebSocketBridge,
protocolVersion = 1,
commands: Record<string, { supported: boolean }> = { "state.get": { supported: true } },
): Promise<WebSocket> {
const client = new WebSocket(bridgeUrl(bridge));
await once(client, "open");
const connected = once(bridge, "connected");
client.send(JSON.stringify({
protocolVersion,
type: "hello",
payload: {
backend: "uxp",
protocolVersion,
commands,
},
}));
await connected;
return client;
}
afterEach(async () => {
await Promise.all(liveBridges.splice(0).map((bridge) => bridge.stop()));
});
describe("UXP WebSocket bridge rejected-message and cleanup paths", () => {
it("serves a 404 for regular HTTP traffic and ignores duplicate starts", async () => {
const bridge = await startBridge();
const listening = vi.fn();
bridge.on("listening", listening);
await bridge.start();
const address = bridge.address();
const response = await fetch(`http://${address.host}:${address.port}/not-a-websocket`);
expect(listening).not.toHaveBeenCalled();
expect(response.status).toBe(404);
await expect(response.text()).resolves.toBe("Not found");
});
it("uses configured address data before it has started", () => {
const bridge = new UxpWebSocketBridge({ token: TOKEN, port: 7911, path: "/panel" });
expect(bridge.address()).toEqual({ host: "127.0.0.1", port: 7911, path: "/panel" });
expect(bridge.getState()).toEqual({ status: "stopped", connected: false });
});
it("uses a timing-safe comparison for equal-length incorrect tokens", async () => {
const bridge = await startBridge();
const client = new WebSocket(bridgeUrl(bridge, "x".repeat(TOKEN.length)));
client.on("error", () => {});
const [, response] = await once(client, "unexpected-response");
expect(response.statusCode).toBe(401);
response.resume();
});
it("closes a host that never sends the versioned hello", async () => {
const bridge = await startBridge({ handshakeTimeoutMs: 20 });
const client = new WebSocket(bridgeUrl(bridge));
await once(client, "open");
const [code, reason] = await once(client, "close");
expect(code).toBe(1008);
expect(reason.toString()).toContain("Versioned hello required");
});
it("rejects each malformed hello shape before accepting capabilities", async () => {
const bridge = await startBridge();
const malformedMessages = [
{ protocolVersion: 1, type: "event", payload: {} },
{ protocolVersion: 1, type: "hello", payload: { backend: "cep", protocolVersion: 1, commands: {} } },
{ protocolVersion: 3, type: "hello", payload: { backend: "uxp", protocolVersion: 3, commands: {} } },
{ protocolVersion: 1, type: "hello", payload: { backend: "uxp", protocolVersion: 2, commands: {} } },
{ protocolVersion: 1, type: "hello", payload: { backend: "uxp", protocolVersion: 1 } },
{ protocolVersion: 1, type: "hello", payload: { backend: "uxp", protocolVersion: 1, commands: "invalid" } },
{ protocolVersion: 1, type: "hello", payload: { backend: "uxp", protocolVersion: 1, commands: [] } },
];
for (const message of malformedMessages) {
const client = new WebSocket(bridgeUrl(bridge));
await once(client, "open");
const closed = once(client, "close");
client.send(JSON.stringify(message));
const [code] = await closed;
expect(code).toBe(1008);
}
});
it("accepts protocol 2 and closes the connection when its protocol changes", async () => {
const bridge = await startBridge();
const client = await connectHost(bridge, 2);
expect(bridge.getState()).toMatchObject({ connected: true, protocolVersion: 2 });
const closed = once(client, "close");
client.send(JSON.stringify({ protocolVersion: 1, type: "event", payload: { kind: "wrong-version" } }));
const [code, reason] = await closed;
expect(code).toBe(1008);
expect(reason.toString()).toContain("Protocol version changed");
});
it("ignores unknown messages and reports default host command failures", async () => {
const bridge = await startBridge();
const client = await connectHost(bridge);
client.send(JSON.stringify({ protocolVersion: 1, type: "notice", payload: {} }));
client.send(JSON.stringify({
protocolVersion: 1,
type: "result",
requestId: "unknown-request",
payload: { ok: true, result: "ignored" },
}));
client.once("message", (data) => {
const command = JSON.parse(data.toString());
client.send(JSON.stringify({
protocolVersion: 1,
type: "result",
requestId: command.requestId,
payload: { ok: false },
}));
});
await expect(bridge.request("state.get")).rejects.toMatchObject({
code: "UXP_COMMAND_FAILED",
message: "UXP command 'state.get' failed",
});
expect(bridge.getState()).toMatchObject({ connected: true });
});
it("forwards client errors from an accepted connection", async () => {
const bridge = await startBridge();
await connectHost(bridge);
const emitted = once(bridge, "clientError");
const expected = new Error("client transport error");
const serverClient = (bridge as unknown as { socket: WebSocket }).socket;
serverClient.emit("error", expected);
await expect(emitted).resolves.toEqual([expected]);
});
it("rejects a request if the socket reports a send failure exactly once", async () => {
const bridge = new UxpWebSocketBridge({ token: TOKEN });
const internal = bridge as unknown as {
socket: {
readyState: number;
send: (message: string, callback: (error?: Error) => void) => void;
close: () => void;
} | null;
hello: UxpHello | null;
};
internal.socket = {
readyState: WebSocket.OPEN,
send: (_message, callback) => {
callback(new Error("socket write failed"));
callback(new Error("duplicate completion"));
},
close: vi.fn(),
};
internal.hello = {
backend: "uxp",
protocolVersion: 1,
commands: { "state.get": { supported: true } },
};
await expect(bridge.request("state.get")).rejects.toMatchObject({
code: "UXP_SEND_FAILED",
message: "socket write failed",
});
});
});
+142
View File
@@ -0,0 +1,142 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
chmodSync,
existsSync,
mkdirSync,
readFileSync,
readdirSync,
renameSync,
statSync,
unlinkSync,
watch,
writeFileSync,
} from "node:fs";
import { cleanupTempDir, sendCommand } from "../../src/bridge/file-bridge.js";
vi.mock("node:fs", () => ({
existsSync: vi.fn(),
mkdirSync: vi.fn(),
writeFileSync: vi.fn(),
readFileSync: vi.fn(),
unlinkSync: vi.fn(),
readdirSync: vi.fn(),
renameSync: vi.fn(),
statSync: vi.fn(),
chmodSync: vi.fn(),
watch: vi.fn(),
}));
const fs = {
exists: vi.mocked(existsSync),
mkdir: vi.mocked(mkdirSync),
write: vi.mocked(writeFileSync),
read: vi.mocked(readFileSync),
unlink: vi.mocked(unlinkSync),
readdir: vi.mocked(readdirSync),
rename: vi.mocked(renameSync),
stat: vi.mocked(statSync),
chmod: vi.mocked(chmodSync),
watch: vi.mocked(watch),
};
describe("file bridge fallback and cleanup branches", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.useFakeTimers();
fs.stat.mockReturnValue({
uid: typeof process.getuid === "function" ? process.getuid() : 0,
mode: 0o700,
mtimeMs: Date.now(),
} as ReturnType<typeof statSync>);
fs.watch.mockImplementation(() => { throw new Error("watch unavailable"); });
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
it("extends a busy operation, then reports a likely modal dialog", async () => {
let busy = true;
fs.exists.mockImplementation((path) => {
const value = String(path);
if (value.includes("res_")) return false;
if (value.includes("busy_")) return busy;
return true;
});
fs.stat.mockImplementation(() => ({
uid: typeof process.getuid === "function" ? process.getuid() : 0,
mode: 0o700,
mtimeMs: Date.now(),
} as ReturnType<typeof statSync>));
const response = sendCommand("var operation = true;", {
tempDir: "/tmp/busy-bridge",
timeoutMs: 100,
});
await vi.advanceTimersByTimeAsync(500);
busy = false;
await vi.advanceTimersByTimeAsync(300);
await expect(response).resolves.toMatchObject({
success: false,
error: expect.stringContaining("modal dialog"),
});
});
it("ignores unrelated watch events and disables a failed watcher", async () => {
let responseExists = false;
let change: ((event: string, filename: string | Buffer | null) => void) | undefined;
let watcherError: (() => void) | undefined;
const watcher = {
close: vi.fn(),
on: vi.fn((event: string, callback: () => void) => {
if (event === "error") watcherError = callback;
return watcher;
}),
};
fs.watch.mockImplementation(((_path, _options, callback) => {
change = callback;
return watcher;
}) as typeof watch);
fs.exists.mockImplementation((path) => String(path).includes("res_") ? responseExists : true);
fs.read.mockReturnValue('{"success":true,"data":{"watched":true}}');
const response = sendCommand("var watched = true;", { tempDir: "/tmp/watch-bridge" });
change?.("rename", null);
change?.("rename", "unrelated.json");
watcherError?.();
responseExists = true;
await vi.advanceTimersByTimeAsync(100);
await expect(response).resolves.toEqual({ success: true, data: { watched: true } });
expect(watcher.close).toHaveBeenCalled();
});
it("tolerates unlink failures and removes busy protocol files", async () => {
fs.exists.mockReturnValue(true);
fs.read.mockReturnValue('{"success":true}');
fs.unlink.mockImplementation(() => { throw new Error("locked"); });
const response = sendCommand("var cleanup = true;", { tempDir: "/tmp/cleanup-bridge" });
await expect(response).resolves.toEqual({ success: true });
fs.readdir.mockReturnValue([
"busy_1.json", "cmd_1.jsx", "res_1.json", "helpers.jsx",
] as ReturnType<typeof readdirSync>);
expect(() => cleanupTempDir({ tempDir: "/tmp/cleanup-bridge" })).not.toThrow();
expect(fs.unlink).toHaveBeenCalledWith(expect.stringContaining("busy_1.json"));
});
it("skips POSIX ownership changes on Windows while checking response bounds", async () => {
vi.spyOn(process, "platform", "get").mockReturnValue("win32");
fs.exists.mockReturnValue(true);
fs.read.mockReturnValue('{"success":true}');
await expect(sendCommand("var windows = true;", {
tempDir: "C:\\Temp\\premiere-bridge",
})).resolves.toEqual({ success: true });
expect(fs.stat).toHaveBeenCalledWith(expect.stringContaining("res_"));
expect(fs.chmod).not.toHaveBeenCalled();
});
});
+612
View File
@@ -0,0 +1,612 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import {
existsSync,
mkdirSync,
writeFileSync,
readFileSync,
unlinkSync,
readdirSync,
renameSync,
statSync,
chmodSync,
watch,
} from "node:fs";
import { dirname, join } from "node:path";
import { tmpdir } from "node:os";
import { execFileSync } from "node:child_process";
import {
getDarwinUserTempDirectory,
getTempDir,
getDefaultBridgeTempDir,
getBridgeLiveness,
MAX_BRIDGE_RESPONSE_BYTES,
sendCommand,
sendRawCommand,
cleanupTempDir,
} from "../../src/bridge/file-bridge.js";
// Mock fs module
vi.mock("node:fs", () => ({
existsSync: vi.fn(),
mkdirSync: vi.fn(),
writeFileSync: vi.fn(),
readFileSync: vi.fn(),
unlinkSync: vi.fn(),
readdirSync: vi.fn(),
renameSync: vi.fn(),
statSync: vi.fn(),
chmodSync: vi.fn(),
watch: vi.fn(() => {
throw new Error("watch unavailable in unit-test fallback");
}),
}));
vi.mock("node:child_process", () => ({
execFileSync: vi.fn(),
}));
const mockedExistsSync = vi.mocked(existsSync);
const mockedMkdirSync = vi.mocked(mkdirSync);
const mockedWriteFileSync = vi.mocked(writeFileSync);
const mockedReadFileSync = vi.mocked(readFileSync);
const mockedUnlinkSync = vi.mocked(unlinkSync);
const mockedReaddirSync = vi.mocked(readdirSync);
const mockedRenameSync = vi.mocked(renameSync);
const mockedStatSync = vi.mocked(statSync);
const mockedChmodSync = vi.mocked(chmodSync);
const mockedWatch = vi.mocked(watch);
const mockedExecFileSync = vi.mocked(execFileSync);
// ensureDir on an existing dir stat-checks ownership; default to a dir owned by us
// with safe perms so the existing tests exercise the happy path.
const myUid = typeof process.getuid === "function" ? process.getuid() : 0;
mockedStatSync.mockReturnValue({ uid: myUid, mode: 0o700 } as unknown as ReturnType<typeof statSync>);
describe("getTempDir", () => {
const originalEnv = process.env.PREMIERE_TEMP_DIR;
afterEach(() => {
if (originalEnv !== undefined) {
process.env.PREMIERE_TEMP_DIR = originalEnv;
} else {
delete process.env.PREMIERE_TEMP_DIR;
}
});
it("returns custom dir from options", () => {
expect(getTempDir({ tempDir: "/custom/dir" })).toBe("/custom/dir");
});
it("returns env var when no options", () => {
process.env.PREMIERE_TEMP_DIR = "/env/dir";
expect(getTempDir()).toBe("/env/dir");
});
it("returns env var when options have no tempDir", () => {
process.env.PREMIERE_TEMP_DIR = "/env/dir";
expect(getTempDir({})).toBe("/env/dir");
});
it("returns default when no options or env", () => {
delete process.env.PREMIERE_TEMP_DIR;
const result = getTempDir();
expect(result).toBe(join(tmpdir(), "premiere-mcp-bridge"));
});
it("uses the Darwin per-user temporary root when a GUI client omits TMPDIR", () => {
expect(getDefaultBridgeTempDir(
"darwin",
"/tmp",
() => "/var/folders/example/T",
{},
).replaceAll("\\", "/")).toBe("/var/folders/example/T/premiere-mcp-bridge");
});
it("falls back safely when the Darwin temporary-root lookup is unavailable", () => {
expect(getDefaultBridgeTempDir("darwin", "/tmp", () => null, {}).replaceAll("\\", "/"))
.toBe("/tmp/premiere-mcp-bridge");
});
it("preserves a configured Darwin temporary root", () => {
expect(getDefaultBridgeTempDir(
"darwin",
"/configured/T",
() => "/var/folders/example/T",
{ TMPDIR: "/configured/T" },
).replaceAll("\\", "/")).toBe("/configured/T/premiere-mcp-bridge");
});
it("prefers options.tempDir over env var", () => {
process.env.PREMIERE_TEMP_DIR = "/env/dir";
expect(getTempDir({ tempDir: "/custom/dir" })).toBe("/custom/dir");
});
});
describe("getDarwinUserTempDirectory", () => {
it("uses the per-user macOS temporary root reported by getconf", () => {
mockedExecFileSync.mockReturnValue("/var/folders/example/T/\n" as never);
expect(getDarwinUserTempDirectory()).toBe("/var/folders/example/T/");
expect(mockedExecFileSync).toHaveBeenCalledWith("/usr/bin/getconf", ["DARWIN_USER_TEMP_DIR"], {
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"],
});
});
it("rejects a non-absolute getconf result", () => {
mockedExecFileSync.mockReturnValue("relative-temp\n" as never);
expect(getDarwinUserTempDirectory()).toBeNull();
});
it("falls back when the getconf lookup fails", () => {
mockedExecFileSync.mockImplementation(() => { throw new Error("unavailable"); });
expect(getDarwinUserTempDirectory()).toBeNull();
});
});
describe("sendCommand", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.useFakeTimers();
mockedStatSync.mockReturnValue({ uid: myUid, mode: 0o700 } as unknown as ReturnType<typeof statSync>);
});
afterEach(() => {
vi.useRealTimers();
});
it("creates temp directory if it does not exist", async () => {
let dirCreated = false;
mockedExistsSync.mockImplementation((path) => {
const p = String(path);
// The temp dir itself does not exist (until mkdirSync is called)
if (p === "/tmp/test-bridge" && !dirCreated) return false;
if (p.includes("res_")) return true; // response exists immediately
return true;
});
mockedMkdirSync.mockImplementation(() => {
dirCreated = true;
return undefined;
});
mockedReadFileSync.mockReturnValue('{"success":true,"data":{"ok":true}}');
const promise = sendCommand("test script", { tempDir: "/tmp/test-bridge" });
await vi.advanceTimersByTimeAsync(200);
await promise;
expect(mockedMkdirSync).toHaveBeenCalledWith("/tmp/test-bridge", {
recursive: true,
mode: 0o700,
});
});
// Security: the bridge temp dir sits at a predictable, world-accessible path, and the
// CEP panel executes any cmd_*.jsx it finds there. On shared machines that dir must be
// ours and private. See the ensureDir hardening.
it("refuses to use an existing temp dir owned by another user", async () => {
if (typeof process.getuid !== "function") return; // POSIX-only guard
mockedExistsSync.mockReturnValue(true); // dir already exists
mockedStatSync.mockReturnValueOnce({
uid: process.getuid!() + 1, // someone else owns it
mode: 0o700,
} as unknown as ReturnType<typeof statSync>);
await expect(sendCommand("var x = 1;", { tempDir: "/tmp/evil-bridge" })).rejects.toThrow(
/owned by uid .* not this user/
);
});
it("clamps a group/world-accessible existing temp dir back to 0700", async () => {
if (typeof process.getuid !== "function") return;
mockedExistsSync.mockImplementation((p) => (String(p).includes("res_") ? true : true));
mockedStatSync.mockReturnValueOnce({
uid: process.getuid!(),
mode: 0o755, // ours, but world-readable
} as unknown as ReturnType<typeof statSync>);
mockedReadFileSync.mockReturnValue('{"success":true,"data":{}}');
const promise = sendCommand("var x = 1;", { tempDir: "/tmp/test-bridge" });
await vi.advanceTimersByTimeAsync(200);
await promise;
expect(mockedChmodSync).toHaveBeenCalledWith("/tmp/test-bridge", 0o700);
});
it("atomically publishes a complete command file as .jsx", async () => {
mockedExistsSync.mockImplementation((path) => {
if (String(path).includes("res_")) return true;
return true;
});
mockedReadFileSync.mockReturnValue('{"success":true,"data":{}}');
const promise = sendCommand("var x = 1;", { tempDir: "/tmp/test-bridge" });
await vi.advanceTimersByTimeAsync(200);
await promise;
const writeCall = mockedWriteFileSync.mock.calls.find(([path]) => String(path).endsWith(".jsx.staged"));
expect(writeCall).toBeDefined();
expect(String(writeCall?.[0])).toMatch(/cmd_.*\.jsx\.staged$/);
const publishedPath = mockedRenameSync.mock.calls[0]?.[1];
expect(String(publishedPath)).toMatch(/cmd_.*\.jsx$/);
// command = one-line helpers bootstrap, then the script itself
const content = String(writeCall?.[1]);
expect(content.endsWith("\nvar x = 1;")).toBe(true);
expect(content.replaceAll("\\\\", "/")).toContain('$.evalFile("/tmp/test-bridge/helpers_');
expect(writeCall?.[2]).toBe("utf-8");
});
it("returns parsed JSON response", async () => {
mockedExistsSync.mockImplementation((path) => {
if (String(path).includes("res_")) return true;
return true;
});
mockedReadFileSync.mockReturnValue('{"success":true,"data":{"version":"24.0"}}');
const promise = sendCommand("test", { tempDir: "/tmp/test-bridge" });
await vi.advanceTimersByTimeAsync(200);
const result = await promise;
expect(result).toEqual({ success: true, data: { version: "24.0" } });
});
it("rejects an oversized bridge response before reading it into memory", async () => {
mockedExistsSync.mockReturnValue(true);
mockedStatSync.mockReturnValue({
uid: myUid,
mode: 0o700,
size: MAX_BRIDGE_RESPONSE_BYTES + 1,
} as unknown as ReturnType<typeof statSync>);
await expect(sendCommand("test", { tempDir: "/tmp/test-bridge" })).resolves.toEqual({
success: false,
error: `Bridge response exceeds the ${MAX_BRIDGE_RESPONSE_BYTES}-byte limit`,
});
expect(mockedReadFileSync).not.toHaveBeenCalled();
});
it("uses distinct cryptographic IDs for concurrently-created command files", async () => {
mockedExistsSync.mockReturnValue(true);
mockedReadFileSync.mockReturnValue('{"success":true}');
await Promise.all([
sendCommand("first", { tempDir: "/tmp/test-bridge" }),
sendCommand("second", { tempDir: "/tmp/test-bridge" }),
]);
const publishedPaths = mockedRenameSync.mock.calls.map(([, target]) => String(target));
expect(publishedPaths).toHaveLength(2);
expect(new Set(publishedPaths).size).toBe(2);
expect(publishedPaths.every((path) => /cmd_[0-9a-f-]{36}\.jsx$/.test(path))).toBe(true);
});
it("attempts event-driven response watching before using the polling fallback", async () => {
mockedExistsSync.mockImplementation((path) => String(path).includes("res_"));
mockedReadFileSync.mockReturnValue('{"success":true,"data":{}}');
const promise = sendCommand("test", { tempDir: "/tmp/test-bridge" });
await vi.advanceTimersByTimeAsync(100);
await promise;
expect(mockedWatch).toHaveBeenCalledWith(
dirname(join("/tmp/test-bridge", "response.json")),
{ persistent: false },
expect.any(Function),
);
});
it("resolves immediately when the watcher reports the response file", async () => {
let responseExists = false;
let onChange: ((event: string, filename: string) => void) | undefined;
const fakeWatcher = { on: vi.fn().mockReturnThis(), close: vi.fn() };
mockedWatch.mockImplementationOnce(((_path, _options, listener) => {
onChange = listener as (event: string, filename: string) => void;
return fakeWatcher;
}) as typeof watch);
mockedExistsSync.mockImplementation((path) =>
String(path).includes("res_") ? responseExists : true,
);
mockedReadFileSync.mockReturnValue('{"success":true,"data":{"eventDriven":true}}');
const promise = sendCommand("test", { tempDir: "/tmp/test-bridge" });
const responsePath = mockedWatch.mock.calls[0]?.[0];
expect(responsePath).toBeDefined();
responseExists = true;
const commandPath = String(mockedRenameSync.mock.calls[0]?.[1]);
const responseName = commandPath.replace(/.*[\\/]+cmd_/, "res_").replace(/\.jsx$/, ".json");
onChange?.("rename", responseName);
await expect(promise).resolves.toEqual({ success: true, data: { eventDriven: true } });
expect(fakeWatcher.close).toHaveBeenCalled();
});
it("serializes concurrent bridge commands before they reach CEP", async () => {
const readyResponses = new Set<string>();
let onChange: ((event: string, filename: string) => void) | undefined;
const fakeWatcher = { on: vi.fn().mockReturnThis(), close: vi.fn() };
mockedWatch.mockImplementation(((_path, _options, listener) => {
onChange = listener as (event: string, filename: string) => void;
return fakeWatcher;
}) as typeof watch);
mockedExistsSync.mockImplementation((path) => {
const value = String(path);
return value.includes("res_") ? readyResponses.has(value) : true;
});
mockedReadFileSync.mockReturnValue('{"success":true,"data":{"shared":true}}');
const pending = [
sendCommand("first", { tempDir: "/tmp/shared-watch-bridge" }),
sendCommand("second", { tempDir: "/tmp/shared-watch-bridge" }),
];
expect(mockedWatch).toHaveBeenCalledTimes(1);
expect(mockedRenameSync).toHaveBeenCalledTimes(1);
const signalResponse = (commandPath: unknown) => {
const responsePath = String(commandPath)
.replace(/cmd_/, "res_")
.replace(/\.jsx$/, ".json");
readyResponses.add(responsePath);
onChange?.("rename", responsePath.split(/[\\/]/).pop()!);
};
signalResponse(mockedRenameSync.mock.calls[0]?.[1]);
await expect(pending[0]).resolves.toEqual({ success: true, data: { shared: true } });
await vi.advanceTimersByTimeAsync(0);
expect(mockedRenameSync).toHaveBeenCalledTimes(2);
expect(mockedWatch).toHaveBeenCalledTimes(2);
signalResponse(mockedRenameSync.mock.calls[1]?.[1]);
await expect(pending[1]).resolves.toEqual({ success: true, data: { shared: true } });
expect(fakeWatcher.close).toHaveBeenCalledTimes(2);
});
it("fails fast when a bridge directory already has the bounded command backlog", async () => {
mockedExistsSync.mockImplementation((path) => !String(path).includes("res_"));
const commands = Array.from({ length: 34 }, (_, index) => sendCommand(`queued-${index}`, {
tempDir: "/tmp/queue-capacity-bridge",
}));
await expect(commands[33]).resolves.toEqual({
success: false,
error: "Bridge command queue is full (32 waiting); retry after an active command finishes",
});
});
it("keeps polling a malformed response without resending the command", async () => {
mockedExistsSync.mockImplementation((path) => {
if (String(path).includes("res_")) return true;
if (String(path).includes("busy_")) return false;
return true;
});
mockedReadFileSync.mockReturnValue("not valid json{{{");
const promise = sendCommand("test", { tempDir: "/tmp/test-bridge", timeoutMs: 500 });
await vi.advanceTimersByTimeAsync(700);
const result = await promise;
expect(result.success).toBe(false);
expect(result.error).toContain("Failed to parse response");
expect(mockedRenameSync).toHaveBeenCalledTimes(1);
});
it("returns timeout error when response file never appears", async () => {
mockedExistsSync.mockReturnValue(false);
const promise = sendCommand("test", {
tempDir: "/tmp/test-bridge",
timeoutMs: 500,
});
await vi.advanceTimersByTimeAsync(600);
const result = await promise;
expect(result.success).toBe(false);
expect(result.error).toContain("timed out");
expect(result.error).toContain("CEP plugin");
});
it("fails health-style commands before publication when a current connector is waiting", async () => {
vi.setSystemTime(new Date(10_000));
mockedExistsSync.mockImplementation((path) => String(path).includes("bridge-heartbeat"));
mockedReadFileSync.mockReturnValue('{"protocolVersion":1,"state":"waiting"}');
mockedStatSync.mockReturnValue({
uid: myUid,
mode: 0o700,
mtimeMs: 9_500,
} as unknown as ReturnType<typeof statSync>);
await expect(sendCommand("var health = true;", {
tempDir: "/tmp/test-bridge",
failFastOnUnreadyHeartbeat: true,
})).resolves.toMatchObject({ success: false, error: expect.stringContaining("not running") });
expect(mockedRenameSync).not.toHaveBeenCalled();
});
it("fails health-style commands before publication when a known connector heartbeat is stale", async () => {
vi.setSystemTime(new Date(10_000));
mockedExistsSync.mockImplementation((path) => String(path).includes("bridge-heartbeat"));
mockedReadFileSync.mockReturnValue('{"protocolVersion":1,"state":"running"}');
mockedStatSync.mockReturnValue({
uid: myUid,
mode: 0o700,
mtimeMs: 1_000,
} as unknown as ReturnType<typeof statSync>);
await expect(sendCommand("var health = true;", {
tempDir: "/tmp/test-bridge",
failFastOnUnreadyHeartbeat: true,
})).resolves.toMatchObject({ success: false, error: expect.stringContaining("heartbeat is stale") });
expect(mockedRenameSync).not.toHaveBeenCalled();
});
it("falls back to normal command delivery when no heartbeat exists", async () => {
mockedExistsSync.mockImplementation((path) => String(path).includes("res_"));
mockedReadFileSync.mockReturnValue('{"success":true}');
await expect(sendCommand("var legacy = true;", {
tempDir: "/tmp/test-bridge",
failFastOnUnreadyHeartbeat: true,
})).resolves.toEqual({ success: true });
expect(mockedRenameSync).toHaveBeenCalled();
});
it("cleans up command and response files after success", async () => {
let responseExists = false;
mockedExistsSync.mockImplementation((path) => {
if (String(path).includes("res_")) return responseExists;
return true;
});
mockedReadFileSync.mockReturnValue('{"success":true,"data":{}}');
const promise = sendCommand("test", { tempDir: "/tmp/test-bridge" });
responseExists = true;
await vi.advanceTimersByTimeAsync(200);
await promise;
// unlinkSync should be called for both cmd and res files
expect(mockedUnlinkSync).toHaveBeenCalled();
});
it("rejects scripts containing eval()", async () => {
await expect(
sendCommand('eval("dangerous")', { tempDir: "/tmp/test-bridge" })
).rejects.toThrow("blocked pattern");
});
it("rejects scripts containing new Function()", async () => {
await expect(
sendCommand('new Function("code")', { tempDir: "/tmp/test-bridge" })
).rejects.toThrow("blocked pattern");
});
it("rejects scripts containing System.callSystem()", async () => {
await expect(
sendCommand('System.callSystem("rm -rf /")', {
tempDir: "/tmp/test-bridge",
})
).rejects.toThrow("blocked pattern");
});
it("rejects scripts exceeding 500KB", async () => {
const largeScript = "x".repeat(501 * 1024);
await expect(
sendCommand(largeScript, { tempDir: "/tmp/test-bridge" })
).rejects.toThrow("500KB size limit");
});
});
describe("getBridgeLiveness", () => {
it("reports fresh, stale, and unknown states without exposing heartbeat contents", () => {
mockedExistsSync.mockReturnValue(true);
mockedReadFileSync.mockReturnValue('{"protocolVersion":1,"state":"running"}');
mockedStatSync.mockReturnValue({ mtimeMs: 9_000 } as ReturnType<typeof statSync>);
expect(getBridgeLiveness({ tempDir: "/tmp/test-bridge" }, 10_000)).toEqual({
state: "running",
ageMs: 1_000,
});
mockedStatSync.mockReturnValue({ mtimeMs: 1_000 } as ReturnType<typeof statSync>);
expect(getBridgeLiveness({ tempDir: "/tmp/test-bridge" }, 10_000)).toEqual({
state: "stale",
ageMs: 9_000,
});
mockedReadFileSync.mockReturnValue('{"state":"running"}');
expect(getBridgeLiveness({ tempDir: "/tmp/test-bridge" }, 10_000)).toEqual({
state: "unknown",
ageMs: null,
});
// This module-level fs mock is shared with the following raw-command
// suite, which exercises the POSIX ownership guard.
mockedStatSync.mockReturnValue({
uid: myUid,
mode: 0o700,
mtimeMs: Date.now(),
} as unknown as ReturnType<typeof statSync>);
});
});
describe("sendRawCommand", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
});
it("allows eval() in raw commands", async () => {
mockedExistsSync.mockImplementation((path) => {
if (String(path).includes("res_")) return true;
return true;
});
mockedReadFileSync.mockReturnValue('{"success":true,"data":{}}');
const promise = sendRawCommand('eval("1+1")', {
tempDir: "/tmp/test-bridge",
});
await vi.advanceTimersByTimeAsync(200);
const result = await promise;
expect(result.success).toBe(true);
});
it("still enforces size limit on raw commands", async () => {
const largeScript = "x".repeat(501 * 1024);
await expect(
sendRawCommand(largeScript, { tempDir: "/tmp/test-bridge" })
).rejects.toThrow("500KB size limit");
});
});
describe("cleanupTempDir", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("removes cmd_ and res_ files", () => {
mockedExistsSync.mockReturnValue(true);
mockedReaddirSync.mockReturnValue([
"cmd_123.jsx" as any,
"cmd_124.jsx.staged" as any,
"res_123.json" as any,
"other_file.txt" as any,
]);
cleanupTempDir({ tempDir: "/tmp/test-bridge" });
// Should unlink cmd_ and res_ files but not other_file.txt
const unlinkCalls = mockedUnlinkSync.mock.calls.map((c) => String(c[0]));
expect(unlinkCalls).toContainEqual(
join("/tmp/test-bridge", "cmd_123.jsx")
);
expect(unlinkCalls).toContainEqual(
join("/tmp/test-bridge", "res_123.json")
);
expect(unlinkCalls).toContainEqual(
join("/tmp/test-bridge", "cmd_124.jsx.staged")
);
expect(unlinkCalls).not.toContainEqual(
join("/tmp/test-bridge", "other_file.txt")
);
});
it("does nothing if temp dir does not exist", () => {
mockedExistsSync.mockReturnValue(false);
cleanupTempDir({ tempDir: "/tmp/nonexistent" });
expect(mockedReaddirSync).not.toHaveBeenCalled();
});
it("handles errors gracefully", () => {
mockedExistsSync.mockReturnValue(true);
mockedReaddirSync.mockImplementation(() => {
throw new Error("permission denied");
});
// Should not throw
expect(() => cleanupTempDir({ tempDir: "/tmp/test-bridge" })).not.toThrow();
});
});
+176
View File
@@ -0,0 +1,176 @@
import { describe, it, expect } from "vitest";
import { buildScript, escapeForExtendScript, buildToolScript, getHelpersSource, buildBootstrap, helpersFileName, HELPERS_VERSION } from "../../src/bridge/script-builder.js";
import { runInNewContext } from "node:vm";
describe("buildScript", () => {
it("wraps code in an IIFE with try/catch", () => {
const result = buildScript("return __result({ ok: true });");
expect(result).toContain("(function() {");
expect(result).toContain("return __result({ ok: true });");
expect(result).toContain("} catch(e) {");
expect(result).toContain("return __error(e.toString());");
expect(result).toContain("})();");
});
it("defines helper functions in the helpers source", () => {
const result = getHelpersSource();
expect(result).toContain("var TICKS_PER_SECOND = 254016000000;");
expect(result).toContain("function __ticksToSeconds(ticks)");
expect(result).toContain("function __secondsToTicks(seconds)");
expect(result).toContain("function __ticksToTimecode(ticks, fps)");
expect(result).toContain("function __pad(n)");
expect(result).toContain("function __findSequence(idOrName)");
expect(result).toContain("function __findProjectItem(nodeIdOrName, rootItem)");
expect(result).toContain("function __findClip(nodeId)");
expect(result).toContain("function __getAllClips(seq)");
expect(result).toContain("function __jsonStringify(obj)");
expect(result).toContain("function __result(data)");
expect(result).toContain("function __error(msg)");
});
it("preserves multi-line code blocks", () => {
const code = `var x = 1;
var y = 2;
return __result({ sum: x + y });`;
const result = buildScript(code);
expect(result).toContain("var x = 1;");
expect(result).toContain("var y = 2;");
expect(result).toContain("return __result({ sum: x + y });");
});
it("handles empty code", () => {
const result = buildScript("");
expect(result).toContain("(function() {");
expect(result).toContain("})();");
});
it("returns a string", () => {
expect(typeof buildScript("")).toBe("string");
});
});
describe("buildToolScript (alias)", () => {
it("is the same function as buildScript", () => {
expect(buildToolScript).toBe(buildScript);
});
it("produces identical output to buildScript", () => {
const code = "return __result({ test: true });";
expect(buildToolScript(code)).toBe(buildScript(code));
});
});
describe("escapeForExtendScript", () => {
it("escapes backslashes", () => {
expect(escapeForExtendScript("C:\\Users\\test")).toBe("C:\\\\Users\\\\test");
});
it("escapes double quotes", () => {
expect(escapeForExtendScript('say "hello"')).toBe('say \\"hello\\"');
});
it("escapes single quotes", () => {
expect(escapeForExtendScript("it's")).toBe("it\\'s");
});
it("escapes newlines", () => {
expect(escapeForExtendScript("line1\nline2")).toBe("line1\\nline2");
});
it("escapes carriage returns", () => {
expect(escapeForExtendScript("line1\rline2")).toBe("line1\\rline2");
});
it("escapes tabs", () => {
expect(escapeForExtendScript("col1\tcol2")).toBe("col1\\tcol2");
});
it("handles empty strings", () => {
expect(escapeForExtendScript("")).toBe("");
});
it("handles strings with no special characters", () => {
expect(escapeForExtendScript("hello world")).toBe("hello world");
});
it("handles multiple escape characters in one string", () => {
const input = 'C:\\path\\to\n"file"\t\'test\'';
const result = escapeForExtendScript(input);
expect(result).toBe('C:\\\\path\\\\to\\n\\"file\\"\\t\\\'test\\\'');
});
it("handles unicode characters (passes through)", () => {
expect(escapeForExtendScript("日本語")).toBe("日本語");
});
});
describe("generated script structure", () => {
it("bootstrap loads this exact helpers version via $.evalFile", () => {
const bootstrap = buildBootstrap("/tmp/x/" + helpersFileName());
expect(bootstrap).toContain(`__HELPERS_V !== "${HELPERS_VERSION}"`);
expect(bootstrap).toContain(`$.evalFile("/tmp/x/helpers_${HELPERS_VERSION}.jsx")`);
expect(getHelpersSource()).toContain(`var __HELPERS_V = "${HELPERS_VERSION}";`);
});
it("bootstrap escapes quotes and backslashes in the helpers path", () => {
const bootstrap = buildBootstrap('C:\\temp\\he"rs.jsx');
expect(bootstrap).toContain('$.evalFile("C:\\\\temp\\\\he\\"rs.jsx")');
});
it("__findProjectItem recursively searches bins", () => {
const result = getHelpersSource();
expect(result).toContain("if (item.type === 2)");
expect(result).toContain("var found = __findProjectItem(nodeIdOrName, item);");
});
it("normalizes numeric host IDs without changing exact name matching", () => {
const result = getHelpersSource();
expect(result).toContain("var wantedId = String(idOrName);");
expect(result).toContain("String(seq.sequenceID) === wantedId || seq.name === idOrName");
expect(result).toContain("var wantedId = String(nodeIdOrName);");
expect(result).toContain("String(item.nodeId) === wantedId || item.name === nodeIdOrName");
expect(result).toContain("var wantedId = String(nodeId);");
expect(result).toContain("String(clip.nodeId) === wantedId");
});
it("__findClip searches both video and audio tracks", () => {
const result = getHelpersSource();
expect(result).toContain("seq.videoTracks.numTracks");
expect(result).toContain("seq.audioTracks.numTracks");
expect(result).toContain('trackType: "video"');
expect(result).toContain('trackType: "audio"');
});
it("__jsonStringify handles all types", () => {
const result = getHelpersSource();
expect(result).toContain('if (obj === null) return "null"');
expect(result).toContain('if (typeof obj === "string")');
expect(result).toContain('if (typeof obj === "number"');
expect(result).toContain("if (obj instanceof Array)");
expect(result).toContain('if (typeof obj === "object")');
});
});
describe("helpers execute correctly in an ES3-like engine", () => {
it("__result works when JSON is undefined (polyfill must not recurse into itself)", () => {
// Regression: __jsonStringify once delegated to JSON.stringify while the JSON
// polyfill delegated back to __jsonStringify — infinite mutual recursion that
// stack-overran the shared ExtendScript engine on every tool response.
const sandbox: Record<string, unknown> = { JSON: undefined };
const out = runInNewContext(
getHelpersSource() + '\n__result({ connected: true, nested: { n: 1, arr: [1, "a", false, null] } });',
sandbox
);
expect(out).toBe('{"success":true,"data":{"connected":true,"nested":{"n":1,"arr":[1,"a",false,null]}}}');
});
it("a stale wrapper from an older helpers version gets replaced", () => {
// Simulate a polluted long-lived engine: JSON.stringify is our old-style wrapper.
const stale = { stringify: function badWrapper(o: unknown) { return "__jsonStringify" + String(o); } };
const sandbox: Record<string, unknown> = { JSON: stale };
runInNewContext(getHelpersSource(), sandbox);
const json = sandbox.JSON as { stringify: (o: unknown) => string; __mcpPolyfill?: boolean };
expect(json.__mcpPolyfill).toBe(true);
expect(json.stringify({ ok: 1 })).toBe('{"ok":1}');
});
});