// @ts-nocheck import path from 'node:path'; const MANIFEST_VERSION = 1; const MAX_TITLE_LENGTH = 200; const MAX_ENTRY_LENGTH = 260; const MAX_SOURCE_SKILL_ID_LENGTH = 128; const MAX_DESIGN_SYSTEM_ID_LENGTH = 128; const MAX_SUPPORTING_FILE_LENGTH = 260; const MAX_SUPPORTING_FILES = 128; const MAX_METADATA_BYTES = 16 * 1024; const ALLOWED_KINDS = new Set([ 'html', 'deck', 'react-component', 'markdown-document', 'svg', 'diagram', 'code-snippet', 'mini-app', 'design-system', ]); const ALLOWED_RENDERERS = new Set([ 'html', 'deck-html', 'react-component', 'markdown', 'svg', 'diagram', 'code', 'mini-app', 'design-system', ]); const ALLOWED_EXPORTS = new Set(['html', 'pdf', 'zip', 'pptx', 'jsx', 'md', 'svg', 'txt']); const ALLOWED_STATUS = new Set(['streaming', 'complete', 'error']); function isPlainObject(value) { if (!value || typeof value !== 'object' || Array.isArray(value)) return false; const proto = Object.getPrototypeOf(value); return proto === Object.prototype || proto === null; } function validateBoundedString(value, field, maxLen, { allowEmpty = false } = {}) { if (typeof value !== 'string') return `${field} must be a string`; if (!allowEmpty && value.length === 0) return `${field} is required`; if (value.length > maxLen) return `${field} exceeds max length (${maxLen})`; return null; } function validateSupportingPath(value) { if (typeof value !== 'string') return 'supportingFiles entries must be strings'; if (value.length === 0) return 'supportingFiles entries cannot be empty'; if (value.length > MAX_SUPPORTING_FILE_LENGTH) { return `supportingFiles entries exceed max length (${MAX_SUPPORTING_FILE_LENGTH})`; } if (/^[A-Za-z]:/.test(value) || value.startsWith('/')) { return 'supportingFiles cannot contain absolute paths'; } if (value.includes('\u0000')) return 'supportingFiles cannot contain null bytes'; const normalized = value.replace(/\\/g, '/'); if (normalized.includes('..')) return 'supportingFiles cannot contain traversal segments'; const parts = normalized.split('/').filter(Boolean); if (parts.length === 0 || parts.some((p) => p === '.' || p === '..')) { return 'supportingFiles cannot contain traversal segments'; } return null; } export function validateArtifactManifestInput(manifest, entry) { if (manifest == null) return { ok: true, value: null }; if (!isPlainObject(manifest)) { return { ok: false, error: 'artifactManifest must be an object' }; } const kindErr = validateBoundedString(manifest.kind, 'artifactManifest.kind', 64); if (kindErr) return { ok: false, error: kindErr }; if (!ALLOWED_KINDS.has(manifest.kind)) { return { ok: false, error: 'artifactManifest.kind is not allowed' }; } const rendererErr = validateBoundedString(manifest.renderer, 'artifactManifest.renderer', 64); if (rendererErr) return { ok: false, error: rendererErr }; if (!ALLOWED_RENDERERS.has(manifest.renderer)) { return { ok: false, error: 'artifactManifest.renderer is not allowed' }; } if (!Array.isArray(manifest.exports) || manifest.exports.length === 0) { return { ok: false, error: 'artifactManifest.exports must be a non-empty array' }; } for (const exp of manifest.exports) { if (typeof exp !== 'string') { return { ok: false, error: 'artifactManifest.exports must contain strings' }; } if (!ALLOWED_EXPORTS.has(exp)) { return { ok: false, error: `artifactManifest.exports contains unsupported value: ${exp}` }; } } if (manifest.status !== undefined) { if (typeof manifest.status !== 'string') { return { ok: false, error: 'artifactManifest.status must be a string' }; } if (!ALLOWED_STATUS.has(manifest.status)) { return { ok: false, error: 'artifactManifest.status is not allowed' }; } } if (manifest.supportingFiles !== undefined) { if (!Array.isArray(manifest.supportingFiles)) { return { ok: false, error: 'artifactManifest.supportingFiles must be an array' }; } if (manifest.supportingFiles.length > MAX_SUPPORTING_FILES) { return { ok: false, error: `artifactManifest.supportingFiles exceeds max items (${MAX_SUPPORTING_FILES})`, }; } for (const rel of manifest.supportingFiles) { const relErr = validateSupportingPath(rel); if (relErr) return { ok: false, error: relErr }; } } if (manifest.title !== undefined) { const titleErr = validateBoundedString( manifest.title, 'artifactManifest.title', MAX_TITLE_LENGTH, { allowEmpty: false }, ); if (titleErr) return { ok: false, error: titleErr }; } if (manifest.sourceSkillId !== undefined) { const skillErr = validateBoundedString( manifest.sourceSkillId, 'artifactManifest.sourceSkillId', MAX_SOURCE_SKILL_ID_LENGTH, { allowEmpty: true }, ); if (skillErr) return { ok: false, error: skillErr }; } if (manifest.designSystemId !== undefined && manifest.designSystemId !== null) { const dsErr = validateBoundedString( manifest.designSystemId, 'artifactManifest.designSystemId', MAX_DESIGN_SYSTEM_ID_LENGTH, { allowEmpty: true }, ); if (dsErr) return { ok: false, error: dsErr }; } if (manifest.metadata !== undefined) { if (!isPlainObject(manifest.metadata)) { return { ok: false, error: 'artifactManifest.metadata must be a plain object' }; } const serialized = JSON.stringify(manifest.metadata); if (typeof serialized !== 'string') { return { ok: false, error: 'artifactManifest.metadata must be JSON-serializable' }; } if (Buffer.byteLength(serialized, 'utf8') > MAX_METADATA_BYTES) { return { ok: false, error: `artifactManifest.metadata exceeds max size (${MAX_METADATA_BYTES} bytes)`, }; } } const safeEntry = typeof entry === 'string' ? entry : ''; if (!safeEntry || safeEntry.length > MAX_ENTRY_LENGTH) { return { ok: false, error: `artifact entry exceeds max length (${MAX_ENTRY_LENGTH})` }; } return { ok: true, value: sanitizeManifest(manifest, safeEntry) }; } export function sanitizeManifest(manifest, entry) { const now = new Date().toISOString(); return { version: MANIFEST_VERSION, kind: manifest.kind, title: manifest.title || entry, entry, renderer: manifest.renderer, status: ALLOWED_STATUS.has(manifest.status) ? manifest.status : 'complete', exports: manifest.exports, supportingFiles: Array.isArray(manifest.supportingFiles) ? manifest.supportingFiles.map((x) => x.replace(/\\/g, '/')) : undefined, createdAt: typeof manifest.createdAt === 'string' ? manifest.createdAt : now, updatedAt: now, sourceSkillId: manifest.sourceSkillId, designSystemId: manifest.designSystemId ?? undefined, metadata: manifest.metadata, }; } export function parsePersistedManifest(raw, fallbackEntry) { try { const parsed = JSON.parse(raw); if (!parsed || parsed.version !== MANIFEST_VERSION) return null; const entry = typeof parsed.entry === 'string' && parsed.entry ? parsed.entry : fallbackEntry; const result = validateArtifactManifestInput(parsed, entry); return result.ok ? result.value : null; } catch { return null; } } export function inferLegacyManifest(entry) { const lower = entry.toLowerCase(); const ext = path.extname(lower); // NOTE: This duplicate heuristic must stay in sync with // src/artifacts/manifest.ts::inferLegacyManifest() until frontend+daemon // inference is moved to a shared runtime-safe module. const isDeck = ext === '.html' && (lower.includes('deck') || lower.includes('slides') || lower.includes('pitch')); if (ext === '.html' || ext === '.htm') { return { version: MANIFEST_VERSION, kind: isDeck ? 'deck' : 'html', title: entry, entry, renderer: isDeck ? 'deck-html' : 'html', status: 'complete', exports: isDeck ? ['html', 'pdf', 'pptx', 'zip'] : ['html', 'pdf', 'zip'], metadata: { inferred: true }, }; } if (ext === '.md') { return { version: MANIFEST_VERSION, kind: 'markdown-document', title: entry, entry, renderer: 'markdown', status: 'complete', exports: ['md', 'html', 'pdf', 'zip'], metadata: { inferred: true }, }; } if (ext === '.svg') { return { version: MANIFEST_VERSION, kind: 'svg', title: entry, entry, renderer: 'svg', status: 'complete', exports: ['svg', 'zip'], metadata: { inferred: true }, }; } return null; }