在 Makelore 构建并预检静态发布产物

This commit is contained in:
2026-08-12 21:19:39 +08:00
parent 3a80625fe2
commit 5b44864265
26 changed files with 1275 additions and 217 deletions

View File

@@ -19,6 +19,7 @@ import { AgentBrowserCdpGuard } from './cdp-guard';
import { AgentBrowserEventBuffer } from './event-buffer';
import { AgentBrowserFault } from './fault';
import { AgentBrowserPayloadStore } from './payload-store';
import { startStaticReleaseServer, type StaticArtifactSnapshot } from '../services/static-release-server';
const CDP_PROTOCOL_VERSION = '1.3';
const INLINE_RESULT_BYTES = 64 * 1024;
@@ -59,6 +60,7 @@ const PUBLISH_PREFLIGHT_INSPECTION = `(() => {
};
})()`;
const SAFE_PREFLIGHT_MESSAGES = {
PUBLISH_PREFLIGHT_UNAVAILABLE: '暂时无法启动作品检查,请稍后重试。',
PREVIEW_REQUIRED: '请先在 Makelore 内置浏览器中打开当前项目预览。',
PUBLISH_PREFLIGHT_LOAD_FAILED: '作品主页无法打开。',
PUBLISH_PREFLIGHT_RUNTIME_ERROR: '作品打开时发生了运行错误。',
@@ -202,6 +204,28 @@ export class AgentBrowserModule {
return { ok: true };
}
async preflightStaticArtifact(snapshot: StaticArtifactSnapshot): Promise<{ ok: true }> {
this.assertAvailable();
const deadline = Date.now() + PUBLISH_PREFLIGHT_TIMEOUT_MS;
let artifactServer: Awaited<ReturnType<typeof startStaticReleaseServer>>;
try {
artifactServer = await beforePublishPreflightDeadline(
startStaticReleaseServer(snapshot),
deadline,
);
} catch {
throw new PublishPreflightFault('PUBLISH_PREFLIGHT_UNAVAILABLE');
}
try {
for (const viewport of PUBLISH_PREFLIGHT_VIEWPORTS) {
await this.preflightStaticViewport(artifactServer.entryUrl, viewport, deadline);
}
return { ok: true };
} finally {
await beforePublishPreflightDeadline(artifactServer.close(), deadline).catch(() => undefined);
}
}
async getSnapshot(projectPath?: string): Promise<AgentBrowserSnapshot> {
if (!this.record) return this.closedSnapshot();
if (projectPath) this.assertProject(this.record, projectPath);
@@ -495,6 +519,7 @@ export class AgentBrowserModule {
const previewOrigin = new URL(targetUrl).origin;
let view: AgentBrowserViewPort | null = null;
let releaseOriginRestriction: (() => void) | null = null;
let contentLoadStarted = false;
let runtimeError = false;
const onDebuggerMessage: PortListener = (_event, methodValue, paramsValue) => {
if (typeof methodValue !== 'string') return;
@@ -569,13 +594,14 @@ export class AgentBrowserModule {
}),
deadline,
);
contentLoadStarted = true;
await beforePublishPreflightDeadline(view.webContents.loadURL(targetUrl), deadline);
await beforePublishPreflightDeadline(delay(PUBLISH_PREFLIGHT_SETTLE_MS), deadline);
const inspected = await beforePublishPreflightDeadline(
view.webContents.executeJavaScript(PUBLISH_PREFLIGHT_INSPECTION),
deadline,
);
if (new URL(view.webContents.getURL()).origin !== previewOrigin) {
if (view.webContents.getURL() !== targetUrl) {
throw new PublishPreflightFault('PUBLISH_PREFLIGHT_LOAD_FAILED');
}
if (runtimeError) {
@@ -596,7 +622,11 @@ export class AgentBrowserModule {
} catch (error) {
if (error instanceof PublishPreflightFault) throw error;
throw new PublishPreflightFault(
isTimeoutError(error) ? 'PUBLISH_PREFLIGHT_TIMEOUT' : 'PUBLISH_PREFLIGHT_LOAD_FAILED',
isTimeoutError(error)
? 'PUBLISH_PREFLIGHT_TIMEOUT'
: contentLoadStarted
? 'PUBLISH_PREFLIGHT_LOAD_FAILED'
: 'PUBLISH_PREFLIGHT_UNAVAILABLE',
);
} finally {
bestEffortCleanup(() => view?.webContents.debugger.removeListener('message', onDebuggerMessage));