fix: deliver WeChat pages when image_make fails

Fall back to workspace share thumbnails so already-published HTML can be
sent instead of blocking on REQUIRED_FRESH. Also allow an explicit
ALLOW_DIRECT_STABLE_RELEASE path matching the pre-2026-07-23 direct
stable packaging flow.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-28 18:19:29 +08:00
parent 7eedda092d
commit 065b1588b7
5 changed files with 245 additions and 88 deletions
+106 -13
View File
@@ -514,6 +514,84 @@ function sanitizeFreshVerification(
};
}
function collectDeliverablePageArtifacts({
artifacts = [],
messages = [],
publishDir,
buildCanonicalUrl,
} = {}) {
const restored = restoreWorkspaceArtifacts({
artifacts,
publishDir,
buildCanonicalUrl,
});
if (restored.length > 0) return restored;
const writeArtifacts = extractPublicHtmlWriteArtifacts(
messages,
{ publishDir },
);
return restoreWorkspaceArtifacts({
artifacts: writeArtifacts.map((artifact) => ({
relativePath: artifact.relativePath,
})),
publishDir,
buildCanonicalUrl,
});
}
async function acceptWorkspaceThumbnailFallback({
artifacts = [],
publishDir,
} = {}) {
const eligible = artifacts.filter((artifact) => {
const localPath = String(artifact?.localPath ?? '').trim();
return localPath && fs.existsSync(localPath) && artifact?.auxiliary !== true;
});
if (eligible.length === 0) {
return { ok: false, reason: 'missing_page_artifact', matches: [] };
}
await Promise.all(
eligible.map((artifact) =>
ensureWorkspaceHtmlThumbnail(
publishDir,
artifact.relativePath,
),
),
);
const withThumbnails = eligible.filter((artifact) =>
fs.existsSync(
path.join(
publishDir,
workspaceThumbnailRelativePath(artifact.relativePath),
),
),
);
if (withThumbnails.length !== eligible.length) {
return {
ok: false,
reason: 'workspace_thumbnail_fallback_failed',
matches: [],
artifact: eligible.find((artifact) =>
!fs.existsSync(
path.join(
publishDir,
workspaceThumbnailRelativePath(artifact.relativePath),
),
),
),
};
}
return {
ok: true,
reason: 'workspace_thumbnail_fallback',
matches: withThumbnails.map((artifact) => ({
artifact,
image: null,
cover: null,
})),
};
}
export async function ensureWechatFreshPageThumbnailsAtWorkspace({
artifacts = [],
images = [],
@@ -522,12 +600,12 @@ export async function ensureWechatFreshPageThumbnailsAtWorkspace({
buildCanonicalUrl,
repairEnabled = false,
} = {}) {
const workspaceArtifacts =
restoreWorkspaceArtifacts({
artifacts,
publishDir,
buildCanonicalUrl,
});
let workspaceArtifacts = collectDeliverablePageArtifacts({
artifacts,
messages,
publishDir,
buildCanonicalUrl,
});
let verification =
verifyFreshWechatPageThumbnails(
workspaceArtifacts,
@@ -551,20 +629,35 @@ export async function ensureWechatFreshPageThumbnailsAtWorkspace({
verification.reason,
});
if (repair.ok) {
workspaceArtifacts = restoreWorkspaceArtifacts({
artifacts: workspaceArtifacts,
publishDir,
buildCanonicalUrl,
});
verification =
verifyFreshWechatPageThumbnails(
restoreWorkspaceArtifacts({
artifacts,
publishDir,
buildCanonicalUrl,
}),
workspaceArtifacts,
images,
);
}
}
// When image_make failed or returned nothing, still deliver pages that already
// landed on disk by generating the workspace share thumbnail sidecars.
if (
!verification.ok
&& ['fresh_image_not_generated', 'missing_generated_cover', 'cover_not_from_current_run'].includes(
String(verification.reason ?? ''),
)
&& workspaceArtifacts.length > 0
) {
verification = await acceptWorkspaceThumbnailFallback({
artifacts: workspaceArtifacts,
publishDir,
});
}
if (verification.ok) {
await Promise.all(
verification.matches.map((match) =>
(verification.matches ?? []).map((match) =>
ensureWorkspaceHtmlThumbnail(
publishDir,
match.artifact.relativePath,
@@ -579,7 +672,7 @@ export async function ensureWechatFreshPageThumbnailsAtWorkspace({
),
thumbnailRelativePaths:
verification.ok
? verification.matches.map((match) =>
? (verification.matches ?? []).map((match) =>
workspaceThumbnailRelativePath(
match.artifact.relativePath,
),