mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
reland(sidecar): converge product clients on package boundary (#7636)
* reland(sidecar): converge product clients after weekly release
Restore the sidecar convergence from #7244 after the release-scheduling revert in #7634. This tree matches the previously reviewed and cross-platform QA-accepted implementation at d83d4e032f.
* fix(sidecar): clarify identity and cache witnesses
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* fix(packaged): probe cross-source desktop owners
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* fix(sidecar): preserve cross-source launch ownership
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* test(pack): assert cross-source Windows launch ownership
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* fix(sidecar): align HyperFrames with client boundary
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* fix(e2e): keep HyperFrames fixture in helper lane
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* docs(sidecar): restore review and handoff contracts
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* fix(sidecar): restore cache and daemon boundaries
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* fix(pack): restore sourcemaps and Linux smoke discovery
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* fix(sidecar): make payload handoff generation-atomic
* fix(sidecar): preserve packaged action and mode parity
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* test(pack): align Linux cleanup resource count
Generated-By: looper 0.11.2 (runner=fixer, agent=codex)
* fix(packaged): preserve headless sidecar mode
* test: normalize source line endings
* fix(daemon): launch Codex cmd shims on Windows
---------
Co-authored-by: Looper <looper@noreply.github.com>
This commit is contained in:
@@ -7,71 +7,51 @@ import { fileURLToPath } from 'node:url';
|
||||
|
||||
import ffmpegInstaller from '@ffmpeg-installer/ffmpeg';
|
||||
import ffprobeInstaller from '@ffprobe-installer/ffprobe';
|
||||
import { PNG } from 'pngjs';
|
||||
import {
|
||||
createJsonIpcServer,
|
||||
resolveAppIpcPath,
|
||||
convergeSidecarLaunch,
|
||||
stopSidecar,
|
||||
} from '@open-design/sidecar';
|
||||
import {
|
||||
APP_KEYS,
|
||||
OPEN_DESIGN_SIDECAR_CONTRACT,
|
||||
SIDECAR_MESSAGES,
|
||||
normalizeDesktopSidecarMessage,
|
||||
type DesktopRenderFramesInput,
|
||||
SIDECAR_MODES,
|
||||
SIDECAR_SOURCES,
|
||||
} from '@open-design/sidecar-proto';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import { createSmokeSuite } from '@/vitest/suite';
|
||||
|
||||
const odBin = fileURLToPath(new URL('../../apps/daemon/bin/od.mjs', import.meta.url));
|
||||
const desktopFixture = fileURLToPath(new URL('../lib/desktop/hyperframes-sidecar.ts', import.meta.url));
|
||||
|
||||
describe('HyperFrames bundled runtime end-to-end', () => {
|
||||
test('[P0] real od media generate renders MP4 through the daemon-owned HyperFrames runtime', async () => {
|
||||
const suite = await createSmokeSuite('hyperframes-runtime-render');
|
||||
const desktopIpc = resolveAppIpcPath({
|
||||
const desktopStamp = {
|
||||
app: APP_KEYS.DESKTOP,
|
||||
contract: OPEN_DESIGN_SIDECAR_CONTRACT,
|
||||
channel: 'local',
|
||||
mode: SIDECAR_MODES.DEV,
|
||||
namespace: suite.namespace,
|
||||
});
|
||||
let receivedFrameDocument = '';
|
||||
const framePng = solidPng(320, 180);
|
||||
const desktop = await createJsonIpcServer({
|
||||
socketPath: desktopIpc,
|
||||
handler: async (message) => {
|
||||
const request = normalizeDesktopSidecarMessage(message);
|
||||
if (request.type === SIDECAR_MESSAGES.STATUS) {
|
||||
return {
|
||||
capabilities: { frameRenderer: true },
|
||||
pid: process.pid,
|
||||
state: 'running',
|
||||
updatedAt: new Date().toISOString(),
|
||||
url: null,
|
||||
windowVisible: false,
|
||||
};
|
||||
}
|
||||
if (request.type === SIDECAR_MESSAGES.RENDER_FRAMES) {
|
||||
const input = request.input as DesktopRenderFramesInput;
|
||||
receivedFrameDocument = input.html;
|
||||
await mkdir(input.outputDir, { recursive: true });
|
||||
for (let frame = 0; frame < 3; frame += 1) {
|
||||
await writeFile(
|
||||
join(input.outputDir, `frame-${String(frame).padStart(8, '0')}.png`),
|
||||
framePng,
|
||||
);
|
||||
}
|
||||
return {
|
||||
duration: 0.1,
|
||||
fps: 30,
|
||||
frameCount: 3,
|
||||
framePattern: join(input.outputDir, 'frame-%08d.png'),
|
||||
height: 180,
|
||||
ok: true,
|
||||
width: 320,
|
||||
};
|
||||
}
|
||||
throw new Error(`unexpected desktop request: ${request.type}`);
|
||||
source: SIDECAR_SOURCES.TOOLS_DEV,
|
||||
};
|
||||
const capturedFrameDocument = join(suite.dataDir, 'captured-frame-document.html');
|
||||
await convergeSidecarLaunch({
|
||||
args: ['--import', 'tsx', desktopFixture],
|
||||
command: process.execPath,
|
||||
env: {
|
||||
...process.env,
|
||||
OD_TEST_CAPTURED_FRAME_DOCUMENT: capturedFrameDocument,
|
||||
OD_TEST_SIDECAR_DATA_ROOT: suite.dataDir,
|
||||
OD_TEST_SIDECAR_RUNTIME_ROOT: join(suite.dataDir, 'runtime'),
|
||||
},
|
||||
});
|
||||
logFd: null,
|
||||
resources: {
|
||||
dataRoot: suite.dataDir,
|
||||
ownerPid: null,
|
||||
port: 0,
|
||||
runtimeRoot: join(suite.dataDir, 'runtime'),
|
||||
},
|
||||
stamp: desktopStamp,
|
||||
}, { stabilityMs: 100, timeoutMs: 10_000 });
|
||||
|
||||
try {
|
||||
await suite.with.env(
|
||||
@@ -148,27 +128,16 @@ window.__timelines.main={duration:function(){return .1},seek:function(){}};
|
||||
]);
|
||||
expect(outputStat.size).toBe(payload.file?.size);
|
||||
expect(bytes.subarray(4, 8).toString('ascii')).toBe('ftyp');
|
||||
expect(receivedFrameDocument).toContain('window.__odFrameRenderer');
|
||||
expect(await readFile(capturedFrameDocument, 'utf8')).toContain('window.__odFrameRenderer');
|
||||
});
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
await desktop.close();
|
||||
await stopSidecar(desktopStamp);
|
||||
}
|
||||
}, 180_000);
|
||||
});
|
||||
|
||||
function solidPng(width: number, height: number): Buffer {
|
||||
const image = new PNG({ width, height });
|
||||
for (let offset = 0; offset < image.data.length; offset += 4) {
|
||||
image.data[offset] = 16;
|
||||
image.data[offset + 1] = 37;
|
||||
image.data[offset + 2] = 63;
|
||||
image.data[offset + 3] = 255;
|
||||
}
|
||||
return PNG.sync.write(image);
|
||||
}
|
||||
|
||||
async function runOd(
|
||||
daemonUrl: string,
|
||||
args: string[],
|
||||
|
||||
@@ -185,55 +185,41 @@ async function loadPackagedSidecarsModule(): Promise<PackagedSidecarsModule> {
|
||||
}
|
||||
|
||||
const FAKE_SIDECAR_SOURCE = String.raw`
|
||||
import { mkdir, rm, writeFile } from "node:fs/promises";
|
||||
import { createServer } from "node:net";
|
||||
import { dirname, join } from "node:path";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
const appArgument = process.argv.find((argument) => argument.startsWith("--od-stamp-app="));
|
||||
const app = appArgument == null ? null : appArgument.slice("--od-stamp-app=".length);
|
||||
const socketPath = process.env.OD_SIDECAR_IPC_PATH;
|
||||
if (app == null || socketPath == null) throw new Error("missing sidecar identity");
|
||||
if (app === "daemon") {
|
||||
const dataRoot = process.env.OD_DATA_DIR;
|
||||
if (dataRoot == null) throw new Error("missing daemon data root");
|
||||
await mkdir(dataRoot, { recursive: true });
|
||||
await writeFile(
|
||||
join(dataRoot, "captured-daemon-env.json"),
|
||||
JSON.stringify({ telemetryRelayUrl: process.env.OPEN_DESIGN_TELEMETRY_RELAY_URL ?? null }),
|
||||
);
|
||||
}
|
||||
if (!socketPath.startsWith("\\\\.\\pipe\\")) {
|
||||
await mkdir(dirname(socketPath), { recursive: true });
|
||||
await rm(socketPath, { force: true });
|
||||
}
|
||||
const server = createServer((socket) => {
|
||||
let buffer = "";
|
||||
socket.on("data", (chunk) => {
|
||||
buffer += chunk.toString("utf8");
|
||||
const newline = buffer.indexOf("\n");
|
||||
if (newline < 0) return;
|
||||
const request = JSON.parse(buffer.slice(0, newline));
|
||||
const shutdown = request.type === "shutdown";
|
||||
const result = request.type === "status"
|
||||
? {
|
||||
pid: process.pid,
|
||||
state: "running",
|
||||
updatedAt: new Date().toISOString(),
|
||||
url: app === "daemon" ? "http://127.0.0.1:43101" : "http://127.0.0.1:43102",
|
||||
}
|
||||
: { accepted: true };
|
||||
socket.end(JSON.stringify({ ok: true, result }) + "\n", () => {
|
||||
if (shutdown) server.close(() => process.exit(0));
|
||||
});
|
||||
});
|
||||
import { SidecarFactory } from ${JSON.stringify(new URL("../../packages/sidecar/dist/index.mjs", import.meta.url).href)};
|
||||
|
||||
const client = SidecarFactory.create({
|
||||
handlers: {
|
||||
"register-web-url": () => ({ accepted: true }),
|
||||
},
|
||||
lifecycle: {
|
||||
async start(resources) {
|
||||
const app = client.stamp.app;
|
||||
if (app === "daemon") {
|
||||
await mkdir(resources.dataRoot, { recursive: true });
|
||||
await writeFile(
|
||||
join(resources.dataRoot, "captured-daemon-env.json"),
|
||||
JSON.stringify({ telemetryRelayUrl: process.env.OPEN_DESIGN_TELEMETRY_RELAY_URL ?? null }),
|
||||
);
|
||||
}
|
||||
return {
|
||||
pid: process.pid,
|
||||
state: "running",
|
||||
updatedAt: new Date().toISOString(),
|
||||
url: app === "daemon" ? "http://127.0.0.1:43101" : "http://127.0.0.1:43102",
|
||||
};
|
||||
},
|
||||
status(runtime) {
|
||||
return runtime;
|
||||
},
|
||||
async stop() {},
|
||||
},
|
||||
});
|
||||
await new Promise((resolve, reject) => {
|
||||
server.once("error", reject);
|
||||
server.listen(socketPath, resolve);
|
||||
});
|
||||
for (const signal of ["SIGINT", "SIGTERM"]) {
|
||||
process.on(signal, () => server.close(() => process.exit(0)));
|
||||
}
|
||||
|
||||
await client.start();
|
||||
await client.waitUntilStopped();
|
||||
`;
|
||||
|
||||
function fakePackagedConfig(root: string, testCase: PlatformCase): PackagedConfigLike {
|
||||
|
||||
Reference in New Issue
Block a user