@hraness/slopcamera 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -26
- package/apps/desktop/README.md +1 -1
- package/apps/desktop/application/html-overlay-renderer.ts +6 -0
- package/apps/desktop/application/operation.ts +1 -1
- package/apps/desktop/cli/args.ts +60 -0
- package/apps/desktop/cli/command-host-resources.ts +5 -0
- package/apps/desktop/cli/commands.ts +13 -1
- package/apps/desktop/cli/help.ts +26 -4
- package/apps/desktop/cli/html-film-init.ts +158 -0
- package/apps/desktop/cli/html-film-names.ts +17 -0
- package/apps/desktop/cli/html-film.ts +471 -0
- package/apps/desktop/cli/html-overlay-renderer.ts +29 -2
- package/apps/desktop/cli/html-scene.ts +3 -3
- package/apps/desktop/cli/main.ts +1 -0
- package/apps/desktop/cli/menubar-status.ts +1 -0
- package/apps/desktop/cli/portable-surface.ts +55 -1
- package/apps/desktop/dist/cli/main.js +328 -293
- package/apps/desktop/html-overlay/film.ts +418 -0
- package/dist/cli.js +13 -2
- package/dist/generate.js +1 -1
- package/dist/{index-4z9y2wa2.js → index-842t02pp.js} +1 -1
- package/dist/{index-qxfexvj3.js → index-nv3byqa1.js} +20 -4
- package/dist/index-p74xy76d.js +3 -0
- package/dist/{index-fevf4ppk.js → index-y8spxs77.js} +10 -7
- package/dist/index.js +1 -1
- package/dist/operations.js +1 -1
- package/dist/workflow.js +1 -1
- package/docs/README.md +5 -5
- package/examples/html/launch-film/README.md +51 -0
- package/examples/html/launch-film/build.ts +158 -0
- package/examples/html/launch-film/film.css +593 -0
- package/examples/html/launch-film/film.html +85 -0
- package/examples/html/launch-film/film.js +410 -0
- package/examples/html/launch-film/film.json +63 -0
- package/examples/html/launch-film/mockups.tsx +74 -0
- package/examples/html/launch-film/timeline.ts +57 -0
- package/package.json +13 -6
- package/skills/slopcamera/SKILL.md +3 -2
- package/skills/slopcamera/references/install.md +3 -3
- package/skills/slopcamera/references/launch-films.md +154 -0
- package/skills/slopcamera/references/visual-communication.md +1 -1
- package/src/cli.ts +125 -9
- package/src/generation-pricing.ts +21 -1
- package/src/icon-set.ts +1086 -0
- package/src/icon.ts +396 -59
- package/src/index.ts +1 -0
- package/src/operations.ts +52 -0
- package/src/version.ts +1 -1
- package/dist/index-nxtmy18t.js +0 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{bundledSkillPath,installSkill,pathExists}from"./index-grk47tgs.js";import{SPATIAL_AUDIT_LIMITS,SPATIAL_SCENE_LIMITS,SPATIAL_TEMPORAL_AUDIT_LIMITS,SpatialBehaviorBakeSchema,SpatialDigestSchema,SpatialGalleryAxisSchema,SpatialPatchOperationSchema,SpatialSceneError,SpatialTemporalContactsFileSchema,applySpatialScenePatch,auditSpatialBehaviorTrace,auditSpatialScene,auditSpatialTemporalEvidence,checkSpatialBehavior,checkSpatialDirection,checkSpatialRenderEffects,compileSpatialDirection,createSpatialEvaluationContext,diffSpatialScenes,evaluateSpatialScene,evaluateSpatialSceneInContext,inspectSpatialScene,normalizeSpatialAuditAssetBounds,parseSpatialScene,parseSpatialValue,planSpatialDirectionGallery,planSpatialRenderEffects,spatialBehaviorFnSignatures,spatialSceneSha256,spatialValueSha256}from"./index-z1kd1x8j.js";import{PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS,PORTABLE_SLOPCAMERA_OPERATION_KINDS,SlopcameraCodeError,boundedCanonicalJsonSha256,canonicalJson,canonicalJsonSha256,compareUtf16Strings,createBoundedJsonValueSnapshot,deepFreezeJson}from"./index-3cxf2c58.js";import{SlopcameraWorkflowError,defineSlopcameraWorkflow,runSlopcameraWorkflow}from"./index-4z9y2wa2.js";import{DiagramValidationError,NebulaSans_Bold_default,NebulaSans_Book_default,SlopcameraOperationError,StackLayoutError,builtInIcons,executeSlopcameraOperation,generateSlopcameraIcon,generateSlopcameraImageGallery,layoutBoxContent,lintDiagram,parseDiagramSource,parseDiagramSpec,parseSlopcameraOperationInput,renderPng,renderSvg,resolveDiagramSource,resolveEdge,resolveStackLayout,sanitizeIcon,searchSlopcameraOperations,serializeTldr,slopcameraOperationCodes,slopcameraOperationRegistry,stackLayoutDefaults,withSlopcameraOperationHostAdmission}from"./index-fevf4ppk.js";import{VectorizeError,vectorizeHardLimits,vectorizeImage}from"./index-mnp0bftq.js";import{SlopcameraCloudError,generateSlopcameraImage,generateSlopcameraImageFile,slopcameraGatewayCredentialStatus}from"./index-nxtmy18t.js";import{HostResourceError,createDefaultHostResourceCoordinator}from"./index-b3geqczn.js";import{mkdir,readFile as readFile2,rename,rm,writeFile}from"fs/promises";import{basename,dirname as dirname2,join,resolve as resolve2}from"path";import{readFile}from"fs/promises";import{dirname,extname,isAbsolute,resolve}from"path";import{pathToFileURL}from"url";var configNames=[{current:"slopcamera.config.ts",retired:"diagram.config.ts"},{current:"slopcamera.config.mjs",retired:"diagram.config.mjs"},{current:"slopcamera.config.js",retired:"diagram.config.js"},{current:"slopcamera.config.json",retired:"diagram.config.json"}];function isRecord(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function parseFont(value,at){if(!isRecord(value)||typeof value.family!=="string"||value.family.trim()===""){throw new Error(`${at} must have a non-empty family`)}if(value.files!==undefined&&!Array.isArray(value.files)){throw new Error(`${at}.files must be an array`)}if(value.monoFamily!==undefined&&(typeof value.monoFamily!=="string"||value.monoFamily.trim()==="")){throw new Error(`${at}.monoFamily must be a non-empty string when present`)}const files=(value.files??[]).map((file,index)=>{if(!isRecord(file)||typeof file.path!=="string"||file.path.trim()===""){throw new Error(`${at}.files[${index}].path must be a non-empty string`)}if(file.weight!==undefined&&(typeof file.weight!=="number"||!Number.isFinite(file.weight))){throw new Error(`${at}.files[${index}].weight must be a finite number`)}if(file.style!==undefined&&file.style!=="normal"&&file.style!=="italic"){throw new Error(`${at}.files[${index}].style must be normal or italic`)}if(file.embed!==undefined&&typeof file.embed!=="boolean"){throw new Error(`${at}.files[${index}].embed must be a boolean`)}const style=file.style;return{path:file.path,...file.weight===undefined?{}:{weight:file.weight},...style===undefined?{}:{style},...file.embed===undefined?{}:{embed:file.embed}}});return{family:value.family,...value.monoFamily===undefined?{}:{monoFamily:value.monoFamily},...files.length===0?{}:{files}}}function parseIcons(value,at){if(!isRecord(value))throw new Error(`${at} must be an object`);return Object.fromEntries(Object.entries(value).map(([name,icon])=>{if(!isRecord(icon)||typeof icon.viewBox!=="string"||typeof icon.body!=="string"){throw new Error(`${at}.${name} must have string viewBox and body fields`)}return[name,sanitizeIcon({viewBox:icon.viewBox,body:icon.body})]}))}function parseTheme(value,at){if(!isRecord(value))throw new Error(`${at} must be an object`);const scalarKeys=["background","foreground","muted","stroke"];for(const key of scalarKeys){if(value[key]!==undefined&&typeof value[key]!=="string"){throw new Error(`${at}.${key} must be a CSS color string`)}}if(value.tones!==undefined&&!isRecord(value.tones)){throw new Error(`${at}.tones must be an object`)}return value}function parseConfig(value){if(!isRecord(value))throw new Error("Slopcamera config must export an object");const font=value.font===undefined?undefined:parseFont(value.font,"font");const icons=value.icons===undefined?undefined:parseIcons(value.icons,"icons");let theme;if(value.theme!==undefined){if(!isRecord(value.theme))throw new Error("theme must be an object");theme={...value.theme.light===undefined?{}:{light:parseTheme(value.theme.light,"theme.light")},...value.theme.dark===undefined?{}:{dark:parseTheme(value.theme.dark,"theme.dark")}}}return{...font===undefined?{}:{font},...icons===undefined?{}:{icons:{...builtInIcons,...icons}},...theme===undefined?{}:{theme}}}async function discoverConfig(directory){let current=null;for(const names of configNames){const candidate=resolve(directory,names.current);if(await pathExists(candidate)){current=candidate;break}}if(current!==null)return current;for(const names of configNames){const candidate=resolve(directory,names.retired);if(await pathExists(candidate)){const replacement=resolve(directory,names.current);throw new Error(`Legacy Slopcamera config found at ${candidate}. Rename it to ${replacement}; Slopcamera does not auto-load diagram.config.*.`)}}return null}async function loadDiagramConfig(options){const filePath=options.explicitPath===undefined?await discoverConfig(options.searchDirectory):resolve(options.explicitPath);if(filePath===null){return{filePath:null,baseDirectory:options.searchDirectory,value:{icons:builtInIcons}}}if(!await pathExists(filePath))throw new Error(`Config does not exist: ${filePath}`);const raw=extname(filePath)===".json"?JSON.parse(await readFile(filePath,"utf8")):(await import(`${pathToFileURL(filePath).href}?v=${Date.now()}`)).default;const value=parseConfig(raw);const baseDirectory=dirname(filePath);const font=value.font===undefined?undefined:{...value.font,...value.font.files===undefined?{}:{files:value.font.files.map((file)=>({...file,path:isAbsolute(file.path)?file.path:resolve(baseDirectory,file.path)}))}};return{filePath,baseDirectory,value:{...value,icons:{...builtInIcons,...value.icons},...font===undefined?{}:{font}}}}async function atomicWrite(filePath,data){const temporary=`${filePath}.tmp-${process.pid}-${crypto.randomUUID()}`;try{await writeFile(temporary,data);await rename(temporary,filePath)}catch(error){await rm(temporary,{force:true});throw error}}async function readDiagramFile(filePath){const absolutePath=resolve2(filePath);let parsed;try{parsed=JSON.parse(await readFile2(absolutePath,"utf8"))}catch(error){throw new Error(`Could not read diagram JSON at ${absolutePath}`,{cause:error})}return{absolutePath,spec:parseDiagramSpec(parsed)}}async function checkDiagramFile(options){const{absolutePath,spec}=await readDiagramFile(options.filePath);const config=await loadDiagramConfig({...options.configPath===undefined?{}:{explicitPath:options.configPath},searchDirectory:dirname2(absolutePath)});for(const shape of spec.shapes){if((shape.type==="rect"||shape.type==="ellipse")&&shape.icon!==undefined&&config.value.icons?.[shape.icon]===undefined){throw new Error(`Unknown icon "${shape.icon}" on shape ${shape.id}`)}}return{findings:lintDiagram(spec),configPath:config.filePath}}async function renderDiagramFile(options){const{absolutePath,spec}=await readDiagramFile(options.filePath);const outDirectory=resolve2(options.outDirectory??dirname2(absolutePath));const config=await loadDiagramConfig({...options.configPath===undefined?{}:{explicitPath:options.configPath},searchDirectory:dirname2(absolutePath)});const scale=options.scale??2;if(!Number.isFinite(scale)||scale<=0||scale>8){throw new Error("PNG scale must be greater than zero and no more than 8")}const[light,dark]=await Promise.all([renderSvg(spec,"light",config.value),renderSvg(spec,"dark",config.value)]);const[lightPng,darkPng]=[renderPng(light,config.value,scale),renderPng(dark,config.value,scale)];const artifacts={spec:absolutePath,tldr:join(outDirectory,`${spec.name}.tldr`),lightSvg:join(outDirectory,`${spec.name}.light.svg`),darkSvg:join(outDirectory,`${spec.name}.dark.svg`),lightPng:join(outDirectory,`${spec.name}.light.png`),darkPng:join(outDirectory,`${spec.name}.dark.png`)};await mkdir(outDirectory,{recursive:true});await Promise.all([atomicWrite(artifacts.tldr,serializeTldr(spec,config.value)),atomicWrite(artifacts.lightSvg,light.svg),atomicWrite(artifacts.darkSvg,dark.svg),atomicWrite(artifacts.lightPng,lightPng),atomicWrite(artifacts.darkPng,darkPng)]);return{artifacts,findings:lintDiagram(spec),configPath:config.filePath}}function artifactSummary(artifacts){return[`Rendered ${basename(artifacts.spec)}`,` ${artifacts.tldr}`,` ${artifacts.lightSvg}`,` ${artifacts.darkSvg}`,` ${artifacts.lightPng}`,` ${artifacts.darkPng}`].join(`
|
|
2
|
+
import{bundledSkillPath,installSkill,pathExists}from"./index-grk47tgs.js";import{SPATIAL_AUDIT_LIMITS,SPATIAL_SCENE_LIMITS,SPATIAL_TEMPORAL_AUDIT_LIMITS,SpatialBehaviorBakeSchema,SpatialDigestSchema,SpatialGalleryAxisSchema,SpatialPatchOperationSchema,SpatialSceneError,SpatialTemporalContactsFileSchema,applySpatialScenePatch,auditSpatialBehaviorTrace,auditSpatialScene,auditSpatialTemporalEvidence,checkSpatialBehavior,checkSpatialDirection,checkSpatialRenderEffects,compileSpatialDirection,createSpatialEvaluationContext,diffSpatialScenes,evaluateSpatialScene,evaluateSpatialSceneInContext,inspectSpatialScene,normalizeSpatialAuditAssetBounds,parseSpatialScene,parseSpatialValue,planSpatialDirectionGallery,planSpatialRenderEffects,spatialBehaviorFnSignatures,spatialSceneSha256,spatialValueSha256}from"./index-z1kd1x8j.js";import{PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS,PORTABLE_SLOPCAMERA_OPERATION_KINDS,SlopcameraCodeError,boundedCanonicalJsonSha256,canonicalJson,canonicalJsonSha256,compareUtf16Strings,createBoundedJsonValueSnapshot,deepFreezeJson}from"./index-3cxf2c58.js";import{SlopcameraWorkflowError,defineSlopcameraWorkflow,runSlopcameraWorkflow}from"./index-842t02pp.js";import{DiagramValidationError,NebulaSans_Bold_default,NebulaSans_Book_default,SlopcameraOperationError,StackLayoutError,builtInIcons,collectSlopcameraIconCandidates,executeSlopcameraOperation,extractIconLineArt,generateSlopcameraIcon,generateSlopcameraImageGallery,layoutBoxContent,lintDiagram,loadIconLanguageRuntime,measureIconCandidateMetrics,parseDiagramSource,parseDiagramSpec,parseSlopcameraOperationInput,renderIconPreview,renderPng,renderSvg,resolveDiagramSource,resolveEdge,resolveStackLayout,sanitizeIcon,searchSlopcameraOperations,serializeTldr,slopcameraIconContexts,slopcameraIconCritiqueDefaultModel,slopcameraIconCritiqueTimeoutMs,slopcameraIconDefaultInk,slopcameraIconMaximumRounds,slopcameraIconPanel,slopcameraIconSubjectMaximumBytes,slopcameraOperationCodes,slopcameraOperationRegistry,stackLayoutDefaults,withSlopcameraOperationHostAdmission,writeAtomically}from"./index-y8spxs77.js";import{VectorizeDeadline,VectorizeError,loadRaster,normalizedHexColor,resolveVectorizeLimits,vectorizeHardLimits,vectorizeImage}from"./index-mnp0bftq.js";import{SlopcameraCloudError,createFixedGatewayFetch,generateSlopcameraImage,generateSlopcameraImageFile,resolveSlopcameraGatewayCredential,slopcameraGatewayApiBaseUrl,slopcameraGatewayCredentialStatus}from"./index-p74xy76d.js";import{HostResourceError,createDefaultHostResourceCoordinator}from"./index-b3geqczn.js";import{__require}from"./index-80a36bc0.js";import{mkdir,readFile as readFile2,rename,rm,writeFile}from"fs/promises";import{basename,dirname as dirname2,join,resolve as resolve2}from"path";import{readFile}from"fs/promises";import{dirname,extname,isAbsolute,resolve}from"path";import{pathToFileURL}from"url";var configNames=[{current:"slopcamera.config.ts",retired:"diagram.config.ts"},{current:"slopcamera.config.mjs",retired:"diagram.config.mjs"},{current:"slopcamera.config.js",retired:"diagram.config.js"},{current:"slopcamera.config.json",retired:"diagram.config.json"}];function isRecord(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function parseFont(value,at){if(!isRecord(value)||typeof value.family!=="string"||value.family.trim()===""){throw new Error(`${at} must have a non-empty family`)}if(value.files!==undefined&&!Array.isArray(value.files)){throw new Error(`${at}.files must be an array`)}if(value.monoFamily!==undefined&&(typeof value.monoFamily!=="string"||value.monoFamily.trim()==="")){throw new Error(`${at}.monoFamily must be a non-empty string when present`)}const files=(value.files??[]).map((file,index)=>{if(!isRecord(file)||typeof file.path!=="string"||file.path.trim()===""){throw new Error(`${at}.files[${index}].path must be a non-empty string`)}if(file.weight!==undefined&&(typeof file.weight!=="number"||!Number.isFinite(file.weight))){throw new Error(`${at}.files[${index}].weight must be a finite number`)}if(file.style!==undefined&&file.style!=="normal"&&file.style!=="italic"){throw new Error(`${at}.files[${index}].style must be normal or italic`)}if(file.embed!==undefined&&typeof file.embed!=="boolean"){throw new Error(`${at}.files[${index}].embed must be a boolean`)}const style=file.style;return{path:file.path,...file.weight===undefined?{}:{weight:file.weight},...style===undefined?{}:{style},...file.embed===undefined?{}:{embed:file.embed}}});return{family:value.family,...value.monoFamily===undefined?{}:{monoFamily:value.monoFamily},...files.length===0?{}:{files}}}function parseIcons(value,at){if(!isRecord(value))throw new Error(`${at} must be an object`);return Object.fromEntries(Object.entries(value).map(([name,icon])=>{if(!isRecord(icon)||typeof icon.viewBox!=="string"||typeof icon.body!=="string"){throw new Error(`${at}.${name} must have string viewBox and body fields`)}return[name,sanitizeIcon({viewBox:icon.viewBox,body:icon.body})]}))}function parseTheme(value,at){if(!isRecord(value))throw new Error(`${at} must be an object`);const scalarKeys=["background","foreground","muted","stroke"];for(const key of scalarKeys){if(value[key]!==undefined&&typeof value[key]!=="string"){throw new Error(`${at}.${key} must be a CSS color string`)}}if(value.tones!==undefined&&!isRecord(value.tones)){throw new Error(`${at}.tones must be an object`)}return value}function parseConfig(value){if(!isRecord(value))throw new Error("Slopcamera config must export an object");const font=value.font===undefined?undefined:parseFont(value.font,"font");const icons=value.icons===undefined?undefined:parseIcons(value.icons,"icons");let theme;if(value.theme!==undefined){if(!isRecord(value.theme))throw new Error("theme must be an object");theme={...value.theme.light===undefined?{}:{light:parseTheme(value.theme.light,"theme.light")},...value.theme.dark===undefined?{}:{dark:parseTheme(value.theme.dark,"theme.dark")}}}return{...font===undefined?{}:{font},...icons===undefined?{}:{icons:{...builtInIcons,...icons}},...theme===undefined?{}:{theme}}}async function discoverConfig(directory){let current=null;for(const names of configNames){const candidate=resolve(directory,names.current);if(await pathExists(candidate)){current=candidate;break}}if(current!==null)return current;for(const names of configNames){const candidate=resolve(directory,names.retired);if(await pathExists(candidate)){const replacement=resolve(directory,names.current);throw new Error(`Legacy Slopcamera config found at ${candidate}. Rename it to ${replacement}; Slopcamera does not auto-load diagram.config.*.`)}}return null}async function loadDiagramConfig(options){const filePath=options.explicitPath===undefined?await discoverConfig(options.searchDirectory):resolve(options.explicitPath);if(filePath===null){return{filePath:null,baseDirectory:options.searchDirectory,value:{icons:builtInIcons}}}if(!await pathExists(filePath))throw new Error(`Config does not exist: ${filePath}`);const raw=extname(filePath)===".json"?JSON.parse(await readFile(filePath,"utf8")):(await import(`${pathToFileURL(filePath).href}?v=${Date.now()}`)).default;const value=parseConfig(raw);const baseDirectory=dirname(filePath);const font=value.font===undefined?undefined:{...value.font,...value.font.files===undefined?{}:{files:value.font.files.map((file)=>({...file,path:isAbsolute(file.path)?file.path:resolve(baseDirectory,file.path)}))}};return{filePath,baseDirectory,value:{...value,icons:{...builtInIcons,...value.icons},...font===undefined?{}:{font}}}}async function atomicWrite(filePath,data){const temporary=`${filePath}.tmp-${process.pid}-${crypto.randomUUID()}`;try{await writeFile(temporary,data);await rename(temporary,filePath)}catch(error){await rm(temporary,{force:true});throw error}}async function readDiagramFile(filePath){const absolutePath=resolve2(filePath);let parsed;try{parsed=JSON.parse(await readFile2(absolutePath,"utf8"))}catch(error){throw new Error(`Could not read diagram JSON at ${absolutePath}`,{cause:error})}return{absolutePath,spec:parseDiagramSpec(parsed)}}async function checkDiagramFile(options){const{absolutePath,spec}=await readDiagramFile(options.filePath);const config=await loadDiagramConfig({...options.configPath===undefined?{}:{explicitPath:options.configPath},searchDirectory:dirname2(absolutePath)});for(const shape of spec.shapes){if((shape.type==="rect"||shape.type==="ellipse")&&shape.icon!==undefined&&config.value.icons?.[shape.icon]===undefined){throw new Error(`Unknown icon "${shape.icon}" on shape ${shape.id}`)}}return{findings:lintDiagram(spec),configPath:config.filePath}}async function renderDiagramFile(options){const{absolutePath,spec}=await readDiagramFile(options.filePath);const outDirectory=resolve2(options.outDirectory??dirname2(absolutePath));const config=await loadDiagramConfig({...options.configPath===undefined?{}:{explicitPath:options.configPath},searchDirectory:dirname2(absolutePath)});const scale=options.scale??2;if(!Number.isFinite(scale)||scale<=0||scale>8){throw new Error("PNG scale must be greater than zero and no more than 8")}const[light,dark]=await Promise.all([renderSvg(spec,"light",config.value),renderSvg(spec,"dark",config.value)]);const[lightPng,darkPng]=[renderPng(light,config.value,scale),renderPng(dark,config.value,scale)];const artifacts={spec:absolutePath,tldr:join(outDirectory,`${spec.name}.tldr`),lightSvg:join(outDirectory,`${spec.name}.light.svg`),darkSvg:join(outDirectory,`${spec.name}.dark.svg`),lightPng:join(outDirectory,`${spec.name}.light.png`),darkPng:join(outDirectory,`${spec.name}.dark.png`)};await mkdir(outDirectory,{recursive:true});await Promise.all([atomicWrite(artifacts.tldr,serializeTldr(spec,config.value)),atomicWrite(artifacts.lightSvg,light.svg),atomicWrite(artifacts.darkSvg,dark.svg),atomicWrite(artifacts.lightPng,lightPng),atomicWrite(artifacts.darkPng,darkPng)]);return{artifacts,findings:lintDiagram(spec),configPath:config.filePath}}function artifactSummary(artifacts){return[`Rendered ${basename(artifacts.spec)}`,` ${artifacts.tldr}`,` ${artifacts.lightSvg}`,` ${artifacts.darkSvg}`,` ${artifacts.lightPng}`,` ${artifacts.darkPng}`].join(`
|
|
3
3
|
`)}import{createHash}from"crypto";import{constants}from"fs";import{mkdir as mkdir2,open,rename as rename2,rm as rm2,writeFile as writeFile2}from"fs/promises";import{dirname as dirname3,isAbsolute as isAbsolute2,join as join2,resolve as resolve3}from"path";import{fileURLToPath}from"url";import{Resvg}from"@resvg/resvg-js";function fail(message){throw new Error(`Unsupported drawing SVG: ${message}`)}var maximumSvgBytes=2*1024*1024;function attributes(value){const attrs={};let rest=value;while(rest.trim().length>0){const match=/^\s+([A-Za-z][A-Za-z0-9:-]*)="([^"<>]*)"/u.exec(rest);if(match===null)fail("malformed attribute");const name=match[1];if(Object.hasOwn(attrs,name))fail("duplicate attribute");attrs[name]=match[2];rest=rest.slice(match[0].length)}return attrs}function parseSvg(svg){if(Buffer.byteLength(svg)>maximumSvgBytes)fail("sheet exceeds byte limit");const roots=[];const stack=[];let cursor=0;let count=0;for(const token of svg.matchAll(/<[^>]*>/gu)){if(svg.slice(cursor,token.index).trim()!=="")fail("text outside a path");cursor=token.index+token[0].length;const raw=token[0];const closing=/^<\/([A-Za-z]+)>$/u.exec(raw);if(closing!==null){if(stack.pop()?.tag!==closing[1])fail("unbalanced tags");continue}const match=/^<([A-Za-z]+)([\s\S]*?)(\/?)>$/u.exec(raw);if(match===null)fail("unsupported markup");const tag=match[1];if(!["svg","defs","clipPath","g","path"].includes(tag))fail(`element ${tag}`);if(++count>50000||stack.length>32)fail("tree limit exceeded");const node={tag,attrs:attributes(match[2]),children:[]};const parent=stack.at(-1);if(parent===undefined)roots.push(node);else parent.children.push(node);if(match[3]!=="/")stack.push(node)}if(svg.slice(cursor).trim()!==""||stack.length!==0||roots.length!==1||roots[0]?.tag!=="svg")fail("invalid document");return roots[0]}function only(node,allowed){for(const key of Object.keys(node.attrs))if(!allowed.includes(key))fail(`${node.tag}.${key}`)}function number(value){if(!/^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/u.test(value))fail("invalid number");const parsed=Number(value);if(!Number.isFinite(parsed)||Math.abs(parsed)>1e7)fail("number outside bounds");return parsed}var decimal=(value)=>Number(value.toFixed(6)).toString();function pathCommands(data){const tokens=data.match(/[MLCQZ]|[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/gu)??[];if(data.replace(/[MLCQZ]|[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?|[\s,]/gu,"")!=="")fail("path command");if(tokens.length>500000)fail("path limit");const result=[];let cursor=0;let x=0;let y=0;let startX=0;let startY=0;const take=()=>{const token=tokens[cursor++];if(token===undefined)fail("truncated path");return number(token)};while(cursor<tokens.length){const command=tokens[cursor++];if(command==="M"||command==="L"){x=take();y=take();if(command==="M"){startX=x;startY=y}result.push(`${decimal(x)} ${decimal(y)} ${command==="M"?"m":"l"}`)}else if(command==="C"){const values=[take(),take(),take(),take(),take(),take()];x=values[4];y=values[5];result.push(`${values.map(decimal).join(" ")} c`)}else if(command==="Q"){const qx=take();const qy=take();const nextX=take();const nextY=take();result.push(`${[x+2*(qx-x)/3,y+2*(qy-y)/3,nextX+2*(qx-nextX)/3,nextY+2*(qy-nextY)/3,nextX,nextY].map(decimal).join(" ")} c`);x=nextX;y=nextY}else if(command==="Z"){result.push("h");x=startX;y=startY}else fail("non-absolute path command")}return result.join(`
|
|
4
4
|
`)}function color(value){if(value==="none")return;if(value===undefined||value==="#000000")return"0";if(value==="#ffffff")return"1";return fail("non-monochrome paint")}function svgStream(svg,width,height){const root=parseSvg(svg);only(root,["width","height","viewBox","xmlns"]);if(number((root.attrs.width??"").replace(/pt$/u,""))!==width||number((root.attrs.height??"").replace(/pt$/u,""))!==height||root.attrs.viewBox!==`0 0 ${width} ${height}`||root.attrs.xmlns!=="http://www.w3.org/2000/svg")fail("page geometry");const clips=new Map;for(const defs of root.children.filter((node)=>node.tag==="defs")){only(defs,[]);for(const clip of defs.children){only(clip,["id"]);if(clip.tag!=="clipPath"||clip.attrs.id===undefined||clips.has(clip.attrs.id)||clip.children.some((child)=>child.tag!=="path"))fail("clip definition");clips.set(clip.attrs.id,clip)}}const output=[`q
|
|
5
5
|
1 0 0 -1 0 ${decimal(height)} cm`];const emit=(node)=>{if(node.tag==="defs")return;if(node.tag==="g"){only(node,["transform","clip-path"]);output.push("q");if(node.attrs.transform!==undefined){const match=/^matrix\(([^)]+)\)$/u.exec(node.attrs.transform);if(match===null)fail("transform");const values=match[1].trim().split(/[\s,]+/u).map(number);if(values.length!==6)fail("matrix");output.push(`${values.map(decimal).join(" ")} cm`)}if(node.attrs["clip-path"]!==undefined){const id=/^url\(#([A-Za-z0-9_-]+)\)$/u.exec(node.attrs["clip-path"])?.[1];const clip=id===undefined?undefined:clips.get(id);if(clip===undefined)fail("missing clip");for(const path2 of clip.children){only(path2,["d","fill","stroke","fill-rule","clip-rule"]);if(path2.children.length>0)fail("path children");output.push(pathCommands(path2.attrs.d??""))}const rules=new Set(clip.children.map((path2)=>path2.attrs["clip-rule"]??path2.attrs["fill-rule"]??"nonzero"));if(rules.size!==1||!["nonzero","evenodd"].includes([...rules][0]))fail("clip rule");output.push(rules.has("evenodd")?"W* n":"W n")}node.children.forEach(emit);output.push("Q");return}if(node.tag!=="path")fail("nested non-path element");only(node,["d","fill","stroke","stroke-width","stroke-linecap","stroke-linejoin","stroke-miterlimit","fill-rule","paint-order"]);if(node.children.length>0)fail("path children");const fill=color(node.attrs.fill);const stroke=color(node.attrs.stroke??"none");const cap=["butt","round","square"].indexOf(node.attrs["stroke-linecap"]??"butt");const join2=["miter","round","bevel"].indexOf(node.attrs["stroke-linejoin"]??"miter");const strokeWidth=number(node.attrs["stroke-width"]??"1");const miter=number(node.attrs["stroke-miterlimit"]??"4");if(cap<0||join2<0||strokeWidth<=0||miter<1)fail("stroke style");const rule=node.attrs["fill-rule"]??"nonzero";if(rule!=="nonzero"&&rule!=="evenodd")fail("fill rule");const paintOrder=node.attrs["paint-order"]??"normal";if(!["normal","stroke","stroke fill","fill stroke","stroke fill markers","fill stroke markers"].includes(paintOrder))fail("paint order");output.push("q",`${strokeWidth} w`,`${cap} J`,`${join2} j`,`${miter} M`);if(fill!==undefined)output.push(`${fill} g`);if(stroke!==undefined)output.push(`${stroke} G`);const path=pathCommands(node.attrs.d??"");const fillOperator=rule==="evenodd"?"f*":"f";if(paintOrder.startsWith("stroke")&&stroke!==undefined&&fill!==undefined)output.push(path,"S",path,fillOperator);else output.push(path,fill===undefined?stroke===undefined?"n":"S":stroke===undefined?fillOperator:rule==="evenodd"?"B*":"B");output.push("Q")};root.children.forEach(emit);output.push("Q");return output.join(`
|
|
@@ -20,10 +20,26 @@ ${freeEntry}${usedEntries}trailer
|
|
|
20
20
|
startxref
|
|
21
21
|
${offset}
|
|
22
22
|
%%EOF
|
|
23
|
-
`);const bytes=Buffer.from(chunks.join(""));if(bytes.length>32*1024*1024)throw new Error("Drawing PDF exceeds output byte limit");return bytes}var SLOPCAMERA_VERSION="3.6.0";var drawingProfile="patent-line-art-v1";var drawingSourceByteLimit=1024*1024;var drawingMinimumGlyphHeightPt=3.2*72/25.4;class DrawingValidationError extends Error{constructor(message){super(`Invalid drawing source: ${message}`);this.name="DrawingValidationError"}}function fail2(message){throw new DrawingValidationError(message)}var sha256=(value)=>createHash("sha256").update(value).digest("hex");var assetPath=(asset)=>isAbsolute2(asset)?asset:fileURLToPath(new URL(asset,import.meta.url));var fontFiles=[assetPath(NebulaSans_Book_default),assetPath(NebulaSans_Bold_default)];var monochrome={background:"#ffffff",foreground:"#000000",muted:"#000000",stroke:"#000000",tones:Object.fromEntries(["neutral","blue","orange","green","red","purple","yellow"].map((tone)=>[tone,{fill:"#ffffff",stroke:"#000000",text:"#000000"}]))};var config={font:{family:"Nebula Sans",files:[{path:fontFiles[0],weight:400},{path:fontFiles[1],weight:700}]},theme:{light:monochrome}};var escapeXml=(value)=>value.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""");function drawingPageGeometry(paper){if(paper!=="us-letter"&&paper!=="a4")fail2("paper must be us-letter or a4");const width=paper==="us-letter"?612:210*72/25.4;const height=paper==="us-letter"?792:297*72/25.4;const margins={left:25*72/25.4,top:25*72/25.4,right:15*72/25.4,bottom:10*72/25.4};return{width,height,margins,numberingHeight:30,body:{x:margins.left,y:margins.top+30,width:width-margins.left-margins.right,height:height-margins.top-margins.bottom-60}}}function record(value,keys,at){if(typeof value!=="object"||value===null||Array.isArray(value))fail2(`${at} must be an object`);const result=value;for(const key of Object.keys(result))if(!keys.includes(key))fail2(`${at}.${key} is unsupported`);return result}function checkDiagramEnvelope(value){const diagram=record(value,["$schema","version","name","canvas","layout","shapes","edges"],"diagram");const bounded=(object)=>{for(const value2 of Object.values(object)){if(typeof value2==="number"&&(!Number.isFinite(value2)||Math.abs(value2)>1e4))fail2("numeric drawing values must be finite and within 10000 points");if(typeof value2==="string"&&value2.length>1024)fail2("drawing strings exceed the bounded text limit")}};bounded(diagram);bounded(record(diagram.canvas,["width","height","padding"],"canvas"));if(diagram.layout!==undefined)bounded(record(diagram.layout,["type","direction","gap","align"],"layout"));if(!Array.isArray(diagram.shapes)||diagram.shapes.length<1||diagram.shapes.length>100)fail2("each sheet needs 1 to 100 shapes");for(const value2 of diagram.shapes){const shape=record(value2,["id","type","x","y","tone","opacity","width","height","radius","icon","iconSize","strokeWidth","fill","label","labelFontSize","labelFontFamily","labelWeight","labelRows","labelRowGap","text","fontSize","fontFamily","weight","align","x2","y2"],"shape");bounded(shape);const common=["id","type","x","y","tone","opacity"];const extras=shape.type==="text"?["text","width","fontSize","fontFamily","weight","align"]:shape.type==="line"?["x2","y2","strokeWidth"]:["width","height","radius","icon","iconSize","strokeWidth","fill","label","labelFontSize","labelFontFamily","labelWeight","labelRows","labelRowGap"];record(value2,[...common,...extras],"shape");if(shape.labelRows!==undefined){if(!Array.isArray(shape.labelRows)||shape.labelRows.length>4)fail2("labelRows must be bounded");for(const row of shape.labelRows)bounded(record(row,["text","fontSize","fontFamily","weight"],"label row"))}for(const key of["label","text"])if(typeof shape[key]==="string"&&shape[key].length>500)fail2("label exceeds text limit")}if(diagram.edges!==undefined){if(!Array.isArray(diagram.edges)||diagram.edges.length>150)fail2("at most 150 edges are supported");for(const edge of diagram.edges)bounded(record(edge,["id","from","to","label","tone","start","end","bend","arrowhead","startPosition","endPosition","labelFontSize","labelFontFamily","labelWeight","labelPosition","labelOffset"],"edge"))}}function parseDrawingSource(value){const raw=record(value,["$schema","version","name","title","paper","sheets"],"drawing");if(raw.version!==1)fail2("version must be 1");if(typeof raw.name!=="string"||raw.name.length>100||!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(raw.name))fail2("name must be a bounded lowercase hyphenated name");if(raw.paper!=="us-letter"&&raw.paper!=="a4")fail2("paper must be us-letter or a4");if(raw.title!==undefined&&(typeof raw.title!=="string"||raw.title.length<1||Buffer.byteLength(raw.title)>1024||/[\u0000-\u001f]/u.test(raw.title)))fail2("title must be a bounded metadata string");if(raw.$schema!==undefined&&typeof raw.$schema!=="string")fail2("$schema must be a string");if(!Array.isArray(raw.sheets)||raw.sheets.length<1||raw.sheets.length>20)fail2("sheets must contain 1 to 20 sheets");const sheets=raw.sheets.map((sheet,index)=>{const entry=record(sheet,["figure","diagram"],`sheets[${index}]`);if(entry.figure!==index+1)fail2("figure numbers must be consecutive beginning at 1");checkDiagramEnvelope(entry.diagram);const diagram=parseDiagramSource(entry.diagram);const spec=parseDiagramSpec(diagram);if(spec.shapes.length<1||spec.shapes.length>100||(spec.edges?.length??0)>150)fail2("each sheet needs 1 to 100 shapes and at most 150 edges");return{figure:index+1,diagram}});const source={version:1,name:raw.name,paper:raw.paper,sheets,...raw.title===undefined?{}:{title:raw.title},...raw.$schema===undefined?{}:{$schema:raw.$schema}};if(Buffer.byteLength(JSON.stringify(source))>drawingSourceByteLimit)fail2("source exceeds 1MiB byte limit");return source}function renderer(svg){return new Resvg(svg,{font:{fontFiles,loadSystemFonts:false,defaultFontFamily:"Nebula Sans"},logLevel:"off"})}function bounds(engine){const box=engine.getBBox();if(box===undefined)fail2("empty drawing ink");return{x:box.x,y:box.y,width:box.width,height:box.height}}function inside(ink,box,label){const epsilon=0.01;if(ink.x<box.x-epsilon||ink.y<box.y-epsilon||ink.x+ink.width>box.x+box.width+epsilon||ink.y+ink.height>box.y+box.height+epsilon)fail2(`${label} ink extends beyond its bounds`)}async function sceneSvg(spec){return(await renderSvg(spec,"light",config,{edgeStrokeWidth:1})).svg.replace('<rect width="100%" height="100%" fill="#ffffff"/>',"")}function glyphHeight(character,fontSize,weight){const svg=`<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000"><text x="100" y="500" font-family="Nebula Sans" font-size="${fontSize}" font-weight="${weight}">${escapeXml(character)}</text></svg>`;return bounds(renderer(svg)).height}function inspectLabels(spec,glyphCache){let minimum=Infinity;const inspect=(text,fontSize,weight,family)=>{if(text.length<1||text.length>500||!Array.from(text).every((character)=>/^[A-Z0-9 \n]$/u.test(character)||".,:;()[]/+=_*'!?%-".includes(character)))fail2("visible labels must be uppercase ASCII letters, numbers, spaces, or supported punctuation; labels are never rewritten");if(family!==undefined&&family!=="default")fail2("only the bundled default font is supported");if(!Number.isFinite(fontSize)||fontSize<=0||fontSize>18)fail2("authored font size must be positive and at most 18 points, below the 20 point sheet numbering");for(const character of new Set(text.match(/[A-Z0-9]/gu)??[])){const key=`${character}:${fontSize}:${weight}`;const measured=glyphCache.get(key)??glyphHeight(character,fontSize,weight);glyphCache.set(key,measured);if(glyphCache.size>4096)fail2("glyph measurement limit exceeded");minimum=Math.min(minimum,measured);if(measured+0.001<drawingMinimumGlyphHeightPt)fail2(`glyph ${character} has ${measured.toFixed(3)}pt ink height; minimum is ${drawingMinimumGlyphHeightPt.toFixed(3)}pt (3.2mm)`)}};for(const shape of spec.shapes){if(shape.tone!==undefined&&shape.tone!=="neutral")fail2("colored tones are unsupported; author monochrome relationships explicitly");if(shape.opacity!==undefined&&shape.opacity!==1)fail2("transparency is unsupported");if(shape.type==="rect"||shape.type==="ellipse"){if(shape.icon!==undefined)fail2("icons are unsupported");if((shape.strokeWidth??2)<0.5||(shape.strokeWidth??2)>4)fail2("stroke width must be 0.5 to 4 points");for(const row of layoutBoxContent(shape).rows)inspect(row.text,row.fontSize,row.weight,row.fontFamily)}else if(shape.type==="text")inspect(shape.text,shape.fontSize??24,shape.weight??500,shape.fontFamily);else if((shape.strokeWidth??3)<0.5||(shape.strokeWidth??3)>4)fail2("stroke width must be 0.5 to 4 points")}for(const edge of spec.edges??[]){if(edge.tone!==undefined&&edge.tone!=="neutral")fail2("colored edge tones are unsupported");if(edge.label!==undefined)inspect(edge.label,edge.labelFontSize??18,edge.labelWeight??600,edge.labelFontFamily)}return minimum}async function renderDrawing(input,sourceSha256){const source=parseDrawingSource(input);const sourceDigest=sourceSha256??sha256(JSON.stringify(source));if(!/^[a-f0-9]{64}$/u.test(sourceDigest))fail2("source SHA-256 is invalid");const geometry=drawingPageGeometry(source.paper);const sheets=[];const cache=new Map;let minGlyphHeightPt=Infinity;for(const sheet of source.sheets){const spec=parseDiagramSpec(sheet.diagram);if(spec.canvas.width>geometry.body.width+0.001||spec.canvas.height>geometry.body.height+0.001)fail2(`figure ${sheet.figure} canvas exceeds ${geometry.body.width.toFixed(3)} by ${geometry.body.height.toFixed(3)}pt body; no automatic scaling is performed`);minGlyphHeightPt=Math.min(minGlyphHeightPt,inspectLabels(spec,cache));const sourceSvg=await sceneSvg(spec);inside(bounds(renderer(sourceSvg)),{x:0,y:0,width:spec.canvas.width,height:spec.canvas.height},`figure ${sheet.figure}`);for(const shape of spec.shapes){if((shape.type==="rect"||shape.type==="ellipse")&&(shape.label!==undefined||shape.labelRows!==undefined)){const inset=(shape.strokeWidth??2)/2;const single=await sceneSvg({...spec,shapes:[shape],edges:[]});inside(bounds(renderer(single)),{x:shape.x-inset,y:shape.y-inset,width:shape.width+inset*2,height:shape.height+inset*2},`shape ${shape.id} label`)}}const diagramBody=sourceSvg.replace(/^<svg[^>]*>/u,"").replace(/<\/svg>$/u,"");const x=geometry.body.x+(geometry.body.width-spec.canvas.width)/2;const counter=`${sheet.figure}/${source.sheets.length}`;const figureLabel=source.sheets.length===1?"":`<text x="${geometry.width/2}" y="${geometry.height-geometry.margins.bottom-8}" text-anchor="middle" font-size="20" font-family="Nebula Sans" font-weight="400">FIG. ${sheet.figure}</text>`;const page=`<svg xmlns="http://www.w3.org/2000/svg" width="${geometry.width}" height="${geometry.height}" viewBox="0 0 ${geometry.width} ${geometry.height}"><rect width="100%" height="100%" fill="#ffffff"/><text x="${geometry.width/2}" y="${geometry.margins.top+21}" text-anchor="middle" font-size="20" font-family="Nebula Sans" font-weight="400">${counter}</text><g transform="translate(${x} ${geometry.body.y})">${diagramBody}</g>${figureLabel}</svg>`;const outlined=renderer(page).toString().replace(/width="[^"]+" height="[^"]+" viewBox="[^"]+"/u,`width="${geometry.width}pt" height="${geometry.height}pt" viewBox="0 0 ${geometry.width} ${geometry.height}"`);if(Buffer.byteLength(outlined)>2*1024*1024)fail2("sheet SVG exceeds output byte limit");sheets.push({svg:outlined,width:geometry.width,height:geometry.height,figure:sheet.figure})}for(const character of"FIG0123456789")minGlyphHeightPt=Math.min(minGlyphHeightPt,glyphHeight(character,20,400));if(minGlyphHeightPt+0.001<drawingMinimumGlyphHeightPt)fail2("sheet numbering falls below minimum glyph height");const checks={profile:drawingProfile,paper:source.paper,sheetCount:source.sheets.length,minGlyphHeightPt,findings:[]};return{sheets,checks,pdf:drawingSheetsPdf(sheets,{title:source.title??source.name,sourceSha256:sourceDigest,rendererVersion:SLOPCAMERA_VERSION})}}async function readDrawingFile(filePath){const absolutePath=resolve3(filePath);if(!absolutePath.endsWith(".drawing.json"))fail2("source filename must end in .drawing.json");const descriptor=await open(absolutePath,constants.O_RDONLY|constants.O_NOFOLLOW|constants.O_NONBLOCK);let bytes;try{const info=await descriptor.stat();if(!info.isFile())fail2("source must be a regular file");if(info.size>drawingSourceByteLimit)fail2("source exceeds 1MiB byte limit");const buffer=Buffer.alloc(drawingSourceByteLimit+1);let cursor=0;while(cursor<buffer.length){const result=await descriptor.read(buffer,cursor,buffer.length-cursor,cursor);if(result.bytesRead===0)break;cursor+=result.bytesRead}if(cursor>drawingSourceByteLimit)fail2("source exceeds 1MiB byte limit");bytes=buffer.subarray(0,cursor)}finally{await descriptor.close()}const raw=JSON.parse(new TextDecoder("utf-8",{fatal:true}).decode(bytes));return{absolutePath,source:parseDrawingSource(raw),sourceSha256:sha256(bytes)}}async function checkDrawingFile(options){const{source,sourceSha256}=await readDrawingFile(options.filePath);return(await renderDrawing(source,sourceSha256)).checks}async function atomicWrite2(filePath,data){const temporary=`${filePath}.tmp-${process.pid}-${crypto.randomUUID()}`;try{await writeFile2(temporary,data);await rename2(temporary,filePath)}catch(error){await rm2(temporary,{force:true});throw error}}async function renderDrawingFile(options){const{absolutePath,source,sourceSha256}=await readDrawingFile(options.filePath);const result=await renderDrawing(source,sourceSha256);const outDirectory=resolve3(options.outDirectory??dirname3(absolutePath));const artifacts={source:absolutePath,pdf:join2(outDirectory,`${source.name}.pdf`),sheets:result.sheets.map((_,index)=>join2(outDirectory,`${source.name}.sheet-${String(index+1).padStart(3,"0")}.svg`)),receipt:join2(outDirectory,`${source.name}.drawing-receipt.json`)};const receipt={version:1,profile:drawingProfile,rendererVersion:SLOPCAMERA_VERSION,sourceSha256,paper:source.paper,minGlyphHeightPt:result.checks.minGlyphHeightPt,sheets:result.sheets.map((sheet)=>({figure:sheet.figure,widthPt:sheet.width,heightPt:sheet.height,sha256:sha256(sheet.svg)})),pdfSha256:sha256(result.pdf)};await mkdir2(outDirectory,{recursive:true});for(const[index,sheet]of result.sheets.entries())await atomicWrite2(artifacts.sheets[index],sheet.svg);await atomicWrite2(artifacts.pdf,result.pdf);await atomicWrite2(artifacts.receipt,`${JSON.stringify(receipt,null,2)}
|
|
23
|
+
`);const bytes=Buffer.from(chunks.join(""));if(bytes.length>32*1024*1024)throw new Error("Drawing PDF exceeds output byte limit");return bytes}var SLOPCAMERA_VERSION="3.7.0";var drawingProfile="patent-line-art-v1";var drawingSourceByteLimit=1024*1024;var drawingMinimumGlyphHeightPt=3.2*72/25.4;class DrawingValidationError extends Error{constructor(message){super(`Invalid drawing source: ${message}`);this.name="DrawingValidationError"}}function fail2(message){throw new DrawingValidationError(message)}var sha256=(value)=>createHash("sha256").update(value).digest("hex");var assetPath=(asset)=>isAbsolute2(asset)?asset:fileURLToPath(new URL(asset,import.meta.url));var fontFiles=[assetPath(NebulaSans_Book_default),assetPath(NebulaSans_Bold_default)];var monochrome={background:"#ffffff",foreground:"#000000",muted:"#000000",stroke:"#000000",tones:Object.fromEntries(["neutral","blue","orange","green","red","purple","yellow"].map((tone)=>[tone,{fill:"#ffffff",stroke:"#000000",text:"#000000"}]))};var config={font:{family:"Nebula Sans",files:[{path:fontFiles[0],weight:400},{path:fontFiles[1],weight:700}]},theme:{light:monochrome}};var escapeXml=(value)=>value.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""");function drawingPageGeometry(paper){if(paper!=="us-letter"&&paper!=="a4")fail2("paper must be us-letter or a4");const width=paper==="us-letter"?612:210*72/25.4;const height=paper==="us-letter"?792:297*72/25.4;const margins={left:25*72/25.4,top:25*72/25.4,right:15*72/25.4,bottom:10*72/25.4};return{width,height,margins,numberingHeight:30,body:{x:margins.left,y:margins.top+30,width:width-margins.left-margins.right,height:height-margins.top-margins.bottom-60}}}function record(value,keys,at){if(typeof value!=="object"||value===null||Array.isArray(value))fail2(`${at} must be an object`);const result=value;for(const key of Object.keys(result))if(!keys.includes(key))fail2(`${at}.${key} is unsupported`);return result}function checkDiagramEnvelope(value){const diagram=record(value,["$schema","version","name","canvas","layout","shapes","edges"],"diagram");const bounded=(object)=>{for(const value2 of Object.values(object)){if(typeof value2==="number"&&(!Number.isFinite(value2)||Math.abs(value2)>1e4))fail2("numeric drawing values must be finite and within 10000 points");if(typeof value2==="string"&&value2.length>1024)fail2("drawing strings exceed the bounded text limit")}};bounded(diagram);bounded(record(diagram.canvas,["width","height","padding"],"canvas"));if(diagram.layout!==undefined)bounded(record(diagram.layout,["type","direction","gap","align"],"layout"));if(!Array.isArray(diagram.shapes)||diagram.shapes.length<1||diagram.shapes.length>100)fail2("each sheet needs 1 to 100 shapes");for(const value2 of diagram.shapes){const shape=record(value2,["id","type","x","y","tone","opacity","width","height","radius","icon","iconSize","strokeWidth","fill","label","labelFontSize","labelFontFamily","labelWeight","labelRows","labelRowGap","text","fontSize","fontFamily","weight","align","x2","y2"],"shape");bounded(shape);const common=["id","type","x","y","tone","opacity"];const extras=shape.type==="text"?["text","width","fontSize","fontFamily","weight","align"]:shape.type==="line"?["x2","y2","strokeWidth"]:["width","height","radius","icon","iconSize","strokeWidth","fill","label","labelFontSize","labelFontFamily","labelWeight","labelRows","labelRowGap"];record(value2,[...common,...extras],"shape");if(shape.labelRows!==undefined){if(!Array.isArray(shape.labelRows)||shape.labelRows.length>4)fail2("labelRows must be bounded");for(const row of shape.labelRows)bounded(record(row,["text","fontSize","fontFamily","weight"],"label row"))}for(const key of["label","text"])if(typeof shape[key]==="string"&&shape[key].length>500)fail2("label exceeds text limit")}if(diagram.edges!==undefined){if(!Array.isArray(diagram.edges)||diagram.edges.length>150)fail2("at most 150 edges are supported");for(const edge of diagram.edges)bounded(record(edge,["id","from","to","label","tone","start","end","bend","arrowhead","startPosition","endPosition","labelFontSize","labelFontFamily","labelWeight","labelPosition","labelOffset"],"edge"))}}function parseDrawingSource(value){const raw=record(value,["$schema","version","name","title","paper","sheets"],"drawing");if(raw.version!==1)fail2("version must be 1");if(typeof raw.name!=="string"||raw.name.length>100||!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(raw.name))fail2("name must be a bounded lowercase hyphenated name");if(raw.paper!=="us-letter"&&raw.paper!=="a4")fail2("paper must be us-letter or a4");if(raw.title!==undefined&&(typeof raw.title!=="string"||raw.title.length<1||Buffer.byteLength(raw.title)>1024||/[\u0000-\u001f]/u.test(raw.title)))fail2("title must be a bounded metadata string");if(raw.$schema!==undefined&&typeof raw.$schema!=="string")fail2("$schema must be a string");if(!Array.isArray(raw.sheets)||raw.sheets.length<1||raw.sheets.length>20)fail2("sheets must contain 1 to 20 sheets");const sheets=raw.sheets.map((sheet,index)=>{const entry=record(sheet,["figure","diagram"],`sheets[${index}]`);if(entry.figure!==index+1)fail2("figure numbers must be consecutive beginning at 1");checkDiagramEnvelope(entry.diagram);const diagram=parseDiagramSource(entry.diagram);const spec=parseDiagramSpec(diagram);if(spec.shapes.length<1||spec.shapes.length>100||(spec.edges?.length??0)>150)fail2("each sheet needs 1 to 100 shapes and at most 150 edges");return{figure:index+1,diagram}});const source={version:1,name:raw.name,paper:raw.paper,sheets,...raw.title===undefined?{}:{title:raw.title},...raw.$schema===undefined?{}:{$schema:raw.$schema}};if(Buffer.byteLength(JSON.stringify(source))>drawingSourceByteLimit)fail2("source exceeds 1MiB byte limit");return source}function renderer(svg){return new Resvg(svg,{font:{fontFiles,loadSystemFonts:false,defaultFontFamily:"Nebula Sans"},logLevel:"off"})}function bounds(engine){const box=engine.getBBox();if(box===undefined)fail2("empty drawing ink");return{x:box.x,y:box.y,width:box.width,height:box.height}}function inside(ink,box,label){const epsilon=0.01;if(ink.x<box.x-epsilon||ink.y<box.y-epsilon||ink.x+ink.width>box.x+box.width+epsilon||ink.y+ink.height>box.y+box.height+epsilon)fail2(`${label} ink extends beyond its bounds`)}async function sceneSvg(spec){return(await renderSvg(spec,"light",config,{edgeStrokeWidth:1})).svg.replace('<rect width="100%" height="100%" fill="#ffffff"/>',"")}function glyphHeight(character,fontSize,weight){const svg=`<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000"><text x="100" y="500" font-family="Nebula Sans" font-size="${fontSize}" font-weight="${weight}">${escapeXml(character)}</text></svg>`;return bounds(renderer(svg)).height}function inspectLabels(spec,glyphCache){let minimum=Infinity;const inspect=(text,fontSize,weight,family)=>{if(text.length<1||text.length>500||!Array.from(text).every((character)=>/^[A-Z0-9 \n]$/u.test(character)||".,:;()[]/+=_*'!?%-".includes(character)))fail2("visible labels must be uppercase ASCII letters, numbers, spaces, or supported punctuation; labels are never rewritten");if(family!==undefined&&family!=="default")fail2("only the bundled default font is supported");if(!Number.isFinite(fontSize)||fontSize<=0||fontSize>18)fail2("authored font size must be positive and at most 18 points, below the 20 point sheet numbering");for(const character of new Set(text.match(/[A-Z0-9]/gu)??[])){const key=`${character}:${fontSize}:${weight}`;const measured=glyphCache.get(key)??glyphHeight(character,fontSize,weight);glyphCache.set(key,measured);if(glyphCache.size>4096)fail2("glyph measurement limit exceeded");minimum=Math.min(minimum,measured);if(measured+0.001<drawingMinimumGlyphHeightPt)fail2(`glyph ${character} has ${measured.toFixed(3)}pt ink height; minimum is ${drawingMinimumGlyphHeightPt.toFixed(3)}pt (3.2mm)`)}};for(const shape of spec.shapes){if(shape.tone!==undefined&&shape.tone!=="neutral")fail2("colored tones are unsupported; author monochrome relationships explicitly");if(shape.opacity!==undefined&&shape.opacity!==1)fail2("transparency is unsupported");if(shape.type==="rect"||shape.type==="ellipse"){if(shape.icon!==undefined)fail2("icons are unsupported");if((shape.strokeWidth??2)<0.5||(shape.strokeWidth??2)>4)fail2("stroke width must be 0.5 to 4 points");for(const row of layoutBoxContent(shape).rows)inspect(row.text,row.fontSize,row.weight,row.fontFamily)}else if(shape.type==="text")inspect(shape.text,shape.fontSize??24,shape.weight??500,shape.fontFamily);else if((shape.strokeWidth??3)<0.5||(shape.strokeWidth??3)>4)fail2("stroke width must be 0.5 to 4 points")}for(const edge of spec.edges??[]){if(edge.tone!==undefined&&edge.tone!=="neutral")fail2("colored edge tones are unsupported");if(edge.label!==undefined)inspect(edge.label,edge.labelFontSize??18,edge.labelWeight??600,edge.labelFontFamily)}return minimum}async function renderDrawing(input,sourceSha256){const source=parseDrawingSource(input);const sourceDigest=sourceSha256??sha256(JSON.stringify(source));if(!/^[a-f0-9]{64}$/u.test(sourceDigest))fail2("source SHA-256 is invalid");const geometry=drawingPageGeometry(source.paper);const sheets=[];const cache=new Map;let minGlyphHeightPt=Infinity;for(const sheet of source.sheets){const spec=parseDiagramSpec(sheet.diagram);if(spec.canvas.width>geometry.body.width+0.001||spec.canvas.height>geometry.body.height+0.001)fail2(`figure ${sheet.figure} canvas exceeds ${geometry.body.width.toFixed(3)} by ${geometry.body.height.toFixed(3)}pt body; no automatic scaling is performed`);minGlyphHeightPt=Math.min(minGlyphHeightPt,inspectLabels(spec,cache));const sourceSvg=await sceneSvg(spec);inside(bounds(renderer(sourceSvg)),{x:0,y:0,width:spec.canvas.width,height:spec.canvas.height},`figure ${sheet.figure}`);for(const shape of spec.shapes){if((shape.type==="rect"||shape.type==="ellipse")&&(shape.label!==undefined||shape.labelRows!==undefined)){const inset=(shape.strokeWidth??2)/2;const single=await sceneSvg({...spec,shapes:[shape],edges:[]});inside(bounds(renderer(single)),{x:shape.x-inset,y:shape.y-inset,width:shape.width+inset*2,height:shape.height+inset*2},`shape ${shape.id} label`)}}const diagramBody=sourceSvg.replace(/^<svg[^>]*>/u,"").replace(/<\/svg>$/u,"");const x=geometry.body.x+(geometry.body.width-spec.canvas.width)/2;const counter=`${sheet.figure}/${source.sheets.length}`;const figureLabel=source.sheets.length===1?"":`<text x="${geometry.width/2}" y="${geometry.height-geometry.margins.bottom-8}" text-anchor="middle" font-size="20" font-family="Nebula Sans" font-weight="400">FIG. ${sheet.figure}</text>`;const page=`<svg xmlns="http://www.w3.org/2000/svg" width="${geometry.width}" height="${geometry.height}" viewBox="0 0 ${geometry.width} ${geometry.height}"><rect width="100%" height="100%" fill="#ffffff"/><text x="${geometry.width/2}" y="${geometry.margins.top+21}" text-anchor="middle" font-size="20" font-family="Nebula Sans" font-weight="400">${counter}</text><g transform="translate(${x} ${geometry.body.y})">${diagramBody}</g>${figureLabel}</svg>`;const outlined=renderer(page).toString().replace(/width="[^"]+" height="[^"]+" viewBox="[^"]+"/u,`width="${geometry.width}pt" height="${geometry.height}pt" viewBox="0 0 ${geometry.width} ${geometry.height}"`);if(Buffer.byteLength(outlined)>2*1024*1024)fail2("sheet SVG exceeds output byte limit");sheets.push({svg:outlined,width:geometry.width,height:geometry.height,figure:sheet.figure})}for(const character of"FIG0123456789")minGlyphHeightPt=Math.min(minGlyphHeightPt,glyphHeight(character,20,400));if(minGlyphHeightPt+0.001<drawingMinimumGlyphHeightPt)fail2("sheet numbering falls below minimum glyph height");const checks={profile:drawingProfile,paper:source.paper,sheetCount:source.sheets.length,minGlyphHeightPt,findings:[]};return{sheets,checks,pdf:drawingSheetsPdf(sheets,{title:source.title??source.name,sourceSha256:sourceDigest,rendererVersion:SLOPCAMERA_VERSION})}}async function readDrawingFile(filePath){const absolutePath=resolve3(filePath);if(!absolutePath.endsWith(".drawing.json"))fail2("source filename must end in .drawing.json");const descriptor=await open(absolutePath,constants.O_RDONLY|constants.O_NOFOLLOW|constants.O_NONBLOCK);let bytes;try{const info=await descriptor.stat();if(!info.isFile())fail2("source must be a regular file");if(info.size>drawingSourceByteLimit)fail2("source exceeds 1MiB byte limit");const buffer=Buffer.alloc(drawingSourceByteLimit+1);let cursor=0;while(cursor<buffer.length){const result=await descriptor.read(buffer,cursor,buffer.length-cursor,cursor);if(result.bytesRead===0)break;cursor+=result.bytesRead}if(cursor>drawingSourceByteLimit)fail2("source exceeds 1MiB byte limit");bytes=buffer.subarray(0,cursor)}finally{await descriptor.close()}const raw=JSON.parse(new TextDecoder("utf-8",{fatal:true}).decode(bytes));return{absolutePath,source:parseDrawingSource(raw),sourceSha256:sha256(bytes)}}async function checkDrawingFile(options){const{source,sourceSha256}=await readDrawingFile(options.filePath);return(await renderDrawing(source,sourceSha256)).checks}async function atomicWrite2(filePath,data){const temporary=`${filePath}.tmp-${process.pid}-${crypto.randomUUID()}`;try{await writeFile2(temporary,data);await rename2(temporary,filePath)}catch(error){await rm2(temporary,{force:true});throw error}}async function renderDrawingFile(options){const{absolutePath,source,sourceSha256}=await readDrawingFile(options.filePath);const result=await renderDrawing(source,sourceSha256);const outDirectory=resolve3(options.outDirectory??dirname3(absolutePath));const artifacts={source:absolutePath,pdf:join2(outDirectory,`${source.name}.pdf`),sheets:result.sheets.map((_,index)=>join2(outDirectory,`${source.name}.sheet-${String(index+1).padStart(3,"0")}.svg`)),receipt:join2(outDirectory,`${source.name}.drawing-receipt.json`)};const receipt={version:1,profile:drawingProfile,rendererVersion:SLOPCAMERA_VERSION,sourceSha256,paper:source.paper,minGlyphHeightPt:result.checks.minGlyphHeightPt,sheets:result.sheets.map((sheet)=>({figure:sheet.figure,widthPt:sheet.width,heightPt:sheet.height,sha256:sha256(sheet.svg)})),pdfSha256:sha256(result.pdf)};await mkdir2(outDirectory,{recursive:true});for(const[index,sheet]of result.sheets.entries())await atomicWrite2(artifacts.sheets[index],sheet.svg);await atomicWrite2(artifacts.pdf,result.pdf);await atomicWrite2(artifacts.receipt,`${JSON.stringify(receipt,null,2)}
|
|
24
24
|
`);return{artifacts,checks:result.checks,receipt}}function starterDrawingSource(){return{$schema:"https://raw.githubusercontent.com/hraness/slopcamera/main/schema/drawing.schema.json",version:1,name:"sensor-control",title:"Fictional sensor and controller drawings",paper:"us-letter",sheets:[{figure:1,diagram:{version:1,name:"sensor-system",canvas:{width:480,height:500},shapes:[{id:"sensor",type:"rect",x:105,y:40,width:280,height:100,radius:0,label:"SENSOR 100",labelFontSize:16},{id:"controller",type:"rect",x:105,y:280,width:280,height:100,radius:0,label:"CONTROLLER 200",labelFontSize:16}],edges:[{id:"signal",from:"sensor",to:"controller",label:"SIGNAL",labelFontSize:16}]}},{figure:2,diagram:{version:1,name:"control-flow",canvas:{width:480,height:500},shapes:[{id:"receive",type:"rect",x:105,y:40,width:280,height:100,radius:0,label:"RECEIVE SIGNAL 210",labelFontSize:16},{id:"actuate",type:"rect",x:105,y:280,width:280,height:100,radius:0,label:"SET OUTPUT 220",labelFontSize:16}],edges:[{id:"next",from:"receive",to:"actuate"}]}}]}}import{rename as rename3,rm as rm3,writeFile as writeFile3}from"fs/promises";import{dirname as dirname5,join as join4}from"path";import{open as open2,mkdir as mkdir3,realpath,stat}from"fs/promises";import{basename as basename2,dirname as dirname4,isAbsolute as isAbsolute3,join as join3,relative,resolve as resolve4,win32}from"path";var mcpSourceByteLimit=1024*1024;class WorkspaceBoundaryError extends Error{code;constructor(code,message){super(message);this.name="WorkspaceBoundaryError";this.code=code}}function filesystemCode(error){if(typeof error==="object"&&error!==null&&"code"in error&&typeof error.code==="string"){return error.code}return}function normalizeRelativePath(value,options){if(value.length===0||value.includes("\x00")){throw new WorkspaceBoundaryError("INVALID_PATH","Path must be a non-empty root-relative path.")}if(isAbsolute3(value)||win32.isAbsolute(value)||/^[A-Za-z]:/.test(value)){throw new WorkspaceBoundaryError("INVALID_PATH","Absolute paths are not allowed.")}const segments=value.split(/[\\/]/).filter((segment)=>segment!==""&&segment!==".");if(segments.includes("..")){throw new WorkspaceBoundaryError("INVALID_PATH","Parent-directory traversal is not allowed.")}if(segments.length===0){if(!options.allowRoot){throw new WorkspaceBoundaryError("INVALID_PATH","Path must identify a file below the root.")}return{native:".",portable:"."}}return{native:segments.join("/"),portable:segments.join("/")}}function isConfined(rootDirectory,target){const fromRoot=relative(rootDirectory,target);return fromRoot===""||!fromRoot.startsWith("..")&&!isAbsolute3(fromRoot)}async function readUtf8WithCap(filePath,sourceLabel){let handle;try{handle=await open2(filePath,"r");const metadata=await handle.stat();if(!metadata.isFile()){throw new WorkspaceBoundaryError("SOURCE_NOT_FILE",`${sourceLabel} must be a regular file.`)}if(metadata.size>mcpSourceByteLimit){throw new WorkspaceBoundaryError("SOURCE_TOO_LARGE",`${sourceLabel} exceeds the ${mcpSourceByteLimit}-byte limit.`)}const buffer=Buffer.allocUnsafe(mcpSourceByteLimit+1);let bytesRead=0;while(bytesRead<=mcpSourceByteLimit){const next=await handle.read(buffer,bytesRead,mcpSourceByteLimit+1-bytesRead,null);if(next.bytesRead===0)break;bytesRead+=next.bytesRead}if(bytesRead>mcpSourceByteLimit){throw new WorkspaceBoundaryError("SOURCE_TOO_LARGE",`${sourceLabel} exceeds the ${mcpSourceByteLimit}-byte limit.`)}try{return new TextDecoder("utf-8",{fatal:true}).decode(buffer.subarray(0,bytesRead))}catch{throw new WorkspaceBoundaryError("SOURCE_ENCODING",`${sourceLabel} must contain valid UTF-8.`)}}catch(error){if(error instanceof WorkspaceBoundaryError)throw error;const code=filesystemCode(error);if(code==="ENOENT"){throw new WorkspaceBoundaryError("SOURCE_NOT_FOUND",`${sourceLabel} does not exist.`)}throw new WorkspaceBoundaryError("FILESYSTEM_ERROR",`${sourceLabel} could not be read.`)}finally{await handle?.close()}}class WorkspaceBoundary{rootDirectory;constructor(rootDirectory){this.rootDirectory=rootDirectory}static async create(rootDirectory){let resolvedRoot;try{resolvedRoot=await realpath(resolve4(rootDirectory));if(!(await stat(resolvedRoot)).isDirectory()){throw new WorkspaceBoundaryError("OUTPUT_NOT_DIRECTORY","MCP root must be a directory.")}}catch(error){if(error instanceof WorkspaceBoundaryError)throw error;throw new WorkspaceBoundaryError("FILESYSTEM_ERROR","MCP root could not be opened.")}return new WorkspaceBoundary(resolvedRoot)}assertConfined(target){if(!isConfined(this.rootDirectory,target)){throw new WorkspaceBoundaryError("PATH_OUTSIDE_ROOT","Path resolves outside the MCP root.")}}toRelativePath(absolutePath){this.assertConfined(absolutePath);const fromRoot=relative(this.rootDirectory,absolutePath);return fromRoot===""?".":fromRoot.split("\\").join("/")}async readSource(value,sourceLabel="Diagram source"){const normalized=normalizeRelativePath(value,{allowRoot:false});const lexicalPath=resolve4(this.rootDirectory,normalized.native);this.assertConfined(lexicalPath);let canonicalPath;try{canonicalPath=await realpath(lexicalPath)}catch(error){if(filesystemCode(error)==="ENOENT"){throw new WorkspaceBoundaryError("SOURCE_NOT_FOUND",`${sourceLabel} does not exist.`)}throw new WorkspaceBoundaryError("FILESYSTEM_ERROR",`${sourceLabel} could not be resolved.`)}this.assertConfined(canonicalPath);return{absolutePath:canonicalPath,relativePath:this.toRelativePath(canonicalPath),text:await readUtf8WithCap(canonicalPath,sourceLabel)}}async resolveInputFile(value,maximumBytes){const normalized=normalizeRelativePath(value,{allowRoot:false});const lexicalPath=resolve4(this.rootDirectory,normalized.native);this.assertConfined(lexicalPath);let canonicalPath;try{canonicalPath=await realpath(lexicalPath);this.assertConfined(canonicalPath);const metadata=await stat(canonicalPath);if(!metadata.isFile()){throw new WorkspaceBoundaryError("SOURCE_NOT_FILE","Input must be a regular file.")}if(!Number.isSafeInteger(maximumBytes)||maximumBytes<1||metadata.size>maximumBytes){throw new WorkspaceBoundaryError("SOURCE_TOO_LARGE",`Input exceeds the ${maximumBytes}-byte limit.`)}}catch(error){if(error instanceof WorkspaceBoundaryError)throw error;if(filesystemCode(error)==="ENOENT"){throw new WorkspaceBoundaryError("SOURCE_NOT_FOUND","Input does not exist.")}throw new WorkspaceBoundaryError("FILESYSTEM_ERROR","Input could not be resolved.")}return{absolutePath:canonicalPath,relativePath:this.toRelativePath(canonicalPath)}}async prepareOutputFile(value){const normalized=normalizeRelativePath(value,{allowRoot:false});const fileName=basename2(normalized.native);if(fileName==="."||fileName===".."||fileName.length===0){throw new WorkspaceBoundaryError("INVALID_PATH","Output path must identify a file below the root.")}const directory=await this.prepareOutputDirectory(dirname4(normalized.native));const absolutePath=join3(directory.absolutePath,fileName);this.assertConfined(absolutePath);return{absolutePath,relativePath:this.toRelativePath(absolutePath)}}async prepareOutputDirectory(value){const normalized=normalizeRelativePath(value,{allowRoot:true});const lexicalPath=resolve4(this.rootDirectory,normalized.native);this.assertConfined(lexicalPath);let ancestor=lexicalPath;for(;;){try{const canonicalAncestor=await realpath(ancestor);this.assertConfined(canonicalAncestor);break}catch(error){if(error instanceof WorkspaceBoundaryError)throw error;if(filesystemCode(error)!=="ENOENT"){throw new WorkspaceBoundaryError("FILESYSTEM_ERROR","Output directory could not be resolved.")}const parent=dirname4(ancestor);if(parent===ancestor){throw new WorkspaceBoundaryError("PATH_OUTSIDE_ROOT","Output directory resolves outside the MCP root.")}ancestor=parent}}try{await mkdir3(lexicalPath,{recursive:true});const canonicalPath=await realpath(lexicalPath);this.assertConfined(canonicalPath);if(!(await stat(canonicalPath)).isDirectory()){throw new WorkspaceBoundaryError("OUTPUT_NOT_DIRECTORY","Output path must be a directory.")}return{absolutePath:canonicalPath,relativePath:this.toRelativePath(canonicalPath)}}catch(error){if(error instanceof WorkspaceBoundaryError)throw error;throw new WorkspaceBoundaryError("FILESYSTEM_ERROR","Output directory could not be created.")}}}var mcpMaximumScale=4;var mcpMaximumRenderedPixels=16777216;var mcpMaximumShapes=64;var mcpMaximumEdges=128;var mcpMaximumReturnedFindings=40;var mcpMaximumReturnedEntities=256;var mcpMaximumReturnedAuditSamples=8192;var mcpMaximumReturnedDiffEntries=1024;var defaultScale=2;var maximumShapeIdsPerFinding=12;var builtInConfig=Object.freeze({icons:builtInIcons});var findingSchema={type:"object",additionalProperties:false,required:["code","message","shapeIds"],properties:{code:{type:"string"},message:{type:"string"},shapeIds:{type:"array",items:{type:"string"}}}};var scenePathSchema={type:"string",description:"Root-relative path to a spatial scene JSON source (1 MiB maximum)."};var sceneCameraIdSchema={type:"string",maxLength:128,pattern:"^camera_[a-zA-Z0-9][a-zA-Z0-9_-]*$",description:"Identifier of a camera declared by the scene (camera_\u2026)."};var entityTruncationSummarySchema={type:"object",additionalProperties:false,required:["entityCount","returnedEntityCount","entitiesTruncated"],properties:{entityCount:{type:"integer",minimum:0},returnedEntityCount:{type:"integer",minimum:0},entitiesTruncated:{type:"boolean"}}};function deepFreeze(value){if(typeof value!=="object"||value===null||Object.isFrozen(value)){return value}for(const nested of Object.values(value))deepFreeze(nested);return Object.freeze(value)}var slopcameraMcpTools=deepFreeze([{name:"check_diagram",title:"Check diagram",description:"Parse and lint one root-relative Slopcamera diagram source without changing files. Uses only built-in icons and themes.",inputSchema:{type:"object",additionalProperties:false,required:["path"],properties:{path:{type:"string",description:"Root-relative path to a diagram JSON source (1 MiB maximum)."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","findings","summary"],properties:{ok:{const:true},source:{type:"string"},findings:{type:"array",items:findingSchema},summary:{type:"object",additionalProperties:false,required:["shapeCount","edgeCount","findingCount","returnedFindingCount","findingsTruncated"],properties:{shapeCount:{type:"integer",minimum:0},edgeCount:{type:"integer",minimum:0},findingCount:{type:"integer",minimum:0},returnedFindingCount:{type:"integer",minimum:0},findingsTruncated:{type:"boolean"}}}}},annotations:{title:"Check diagram",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"render_diagram",title:"Render diagram",description:"Render one root-relative Slopcamera diagram source with built-in icons and themes, overwriting its paired .tldr, light/dark SVG, and light/dark PNG artifacts.",inputSchema:{type:"object",additionalProperties:false,required:["path"],properties:{path:{type:"string",description:"Root-relative path to a diagram JSON source (1 MiB maximum)."},out_dir:{type:"string",description:"Optional root-relative output directory. Defaults to the source directory."},scale:{type:"number",exclusiveMinimum:0,maximum:mcpMaximumScale,default:defaultScale,description:"PNG scale. The scaled canvas may contain at most 16,777,216 pixels."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","scale","findings","artifacts","summary"],properties:{ok:{const:true},source:{type:"string"},scale:{type:"number"},findings:{type:"array",items:findingSchema},artifacts:{type:"object",additionalProperties:false,required:["tldr","lightSvg","darkSvg","lightPng","darkPng"],properties:{tldr:{type:"string"},lightSvg:{type:"string"},darkSvg:{type:"string"},lightPng:{type:"string"},darkPng:{type:"string"}}},summary:{type:"object",additionalProperties:false,required:["shapeCount","edgeCount","findingCount","returnedFindingCount","findingsTruncated"],properties:{shapeCount:{type:"integer",minimum:0},edgeCount:{type:"integer",minimum:0},findingCount:{type:"integer",minimum:0},returnedFindingCount:{type:"integer",minimum:0},findingsTruncated:{type:"boolean"}}}}},annotations:{title:"Render diagram",readOnlyHint:false,destructiveHint:true,idempotentHint:true,openWorldHint:false}},{name:"search_slopcamera",title:"Search Slopcamera operations",description:"Search the fixed semantic Slopcamera operation registry by bounded text. This never executes code or changes files.",inputSchema:{type:"object",additionalProperties:false,properties:{query:{type:"string",maxLength:200,description:"Optional terms matched against operation codes and descriptions."},limit:{type:"integer",minimum:1,maximum:20,default:4}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","operations"],properties:{ok:{const:true},operations:{type:"array",maxItems:20,items:{type:"object",required:["code","title","description","execution","authentication","inputSchema"]}}}},annotations:{title:"Search Slopcamera operations",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"execute_slopcamera",title:"Execute Slopcamera operation",description:"Execute one exact operation code with typed JSON input. Never accepts or evaluates source code. Local paths remain confined to the configured workspace root.",inputSchema:{type:"object",additionalProperties:false,required:["operation","input"],properties:{operation:{type:"string",enum:slopcameraOperationCodes},input:{type:"object",description:"Typed input matching the selected operation's registry schema."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","operation","result"],properties:{ok:{const:true},operation:{type:"string",enum:slopcameraOperationCodes},result:{type:"object"}}},annotations:{title:"Execute Slopcamera operation",readOnlyHint:false,destructiveHint:true,idempotentHint:false,openWorldHint:true}},{name:"check_scene",title:"Check scene",description:"Parse and validate one root-relative Slopcamera spatial scene JSON source without changing files.",inputSchema:{type:"object",additionalProperties:false,required:["path"],properties:{path:scenePathSchema}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","sceneId","sceneSha256","summary"],properties:{ok:{const:true},source:{type:"string"},sceneId:{type:"string"},sceneSha256:{type:"string"},summary:{type:"object",additionalProperties:false,required:["durationUs","entityCount","cameraCount","assetCount","animationCount","generatorCount","overrideCount"],properties:{durationUs:{type:"integer",minimum:0},entityCount:{type:"integer",minimum:0},cameraCount:{type:"integer",minimum:0},assetCount:{type:"integer",minimum:0},animationCount:{type:"integer",minimum:0},generatorCount:{type:"integer",minimum:0},overrideCount:{type:"integer",minimum:0}}}}},annotations:{title:"Check scene",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"inspect_scene",title:"Inspect scene",description:"Snapshot one root-relative spatial scene's entities, editable controls, placements, assets, and generators without changing files.",inputSchema:{type:"object",additionalProperties:false,required:["path"],properties:{path:scenePathSchema}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","inspection","summary"],properties:{ok:{const:true},source:{type:"string"},inspection:{type:"object"},summary:entityTruncationSummarySchema}},annotations:{title:"Inspect scene",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"audit_scene",title:"Audit scene",description:"Sample one root-relative spatial scene against a camera and report per-entity geometric coverage, visibility findings, and bounds gaps. Optional asset_bounds accepts a Record<assetId,{min,max}> map, a slopcamera.spatial-asset-admission document, a {manifest,facts} pair, or an array of those.",inputSchema:{type:"object",additionalProperties:false,required:["path","camera_id"],properties:{path:scenePathSchema,camera_id:sceneCameraIdSchema,times_us:{type:"array",items:{type:"integer",minimum:0,maximum:SPATIAL_SCENE_LIMITS.durationUs},minItems:1,maxItems:SPATIAL_AUDIT_LIMITS.samples,description:"Optional sample times in microseconds. Defaults to evenly spaced coverage of the scene duration."},asset_bounds:{description:"Optional decoded scene-space asset bounds: a Record<assetId,{min,max}> map, a slopcamera.spatial-asset-admission document, a {manifest,facts} pair, or an array of those documents."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","report","summary"],properties:{ok:{const:true},source:{type:"string"},report:{type:"object"},summary:{type:"object",additionalProperties:false,required:["findingCount","returnedFindingCount","findingsTruncated","entityCount","returnedEntityCount","entitiesTruncated"],properties:{findingCount:{type:"integer",minimum:0},returnedFindingCount:{type:"integer",minimum:0},findingsTruncated:{type:"boolean"},entityCount:{type:"integer",minimum:0},returnedEntityCount:{type:"integer",minimum:0},entitiesTruncated:{type:"boolean"}}}}},annotations:{title:"Audit scene",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"diff_scenes",title:"Diff scenes",description:"Compare two root-relative spatial scene JSON sources and report added, removed, and changed collection entries without changing files.",inputSchema:{type:"object",additionalProperties:false,required:["path","other"],properties:{path:scenePathSchema,other:{type:"string",description:"Root-relative path to the second spatial scene JSON source (1 MiB maximum)."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","other","sceneSha256","otherSha256","diff","summary"],properties:{ok:{const:true},source:{type:"string"},other:{type:"string"},sceneSha256:{type:"string"},otherSha256:{type:"string"},diff:{type:"array"},summary:{type:"object",additionalProperties:false,required:["entryCount","returnedEntryCount","diffTruncated"],properties:{entryCount:{type:"integer",minimum:0},returnedEntryCount:{type:"integer",minimum:0},diffTruncated:{type:"boolean"}}}}},annotations:{title:"Diff scenes",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"evaluate_scene",title:"Evaluate scene",description:"Evaluate one root-relative spatial scene at one camera and integer microsecond time into an immutable snapshot without changing files.",inputSchema:{type:"object",additionalProperties:false,required:["path","camera_id","time_us"],properties:{path:scenePathSchema,camera_id:sceneCameraIdSchema,time_us:{type:"integer",minimum:0,maximum:SPATIAL_SCENE_LIMITS.durationUs,description:"Evaluation time in integer microseconds."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","snapshot","summary"],properties:{ok:{const:true},source:{type:"string"},snapshot:{type:"object"},summary:entityTruncationSummarySchema}},annotations:{title:"Evaluate scene",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"check_scene_direction",title:"Check scene direction",description:"Validate one authored direction document against one root-relative spatial scene: stale project digests, unresolved entity references, and intervals outside scene duration report as errors without compiling or changing files.",inputSchema:{type:"object",additionalProperties:false,required:["scene","direction"],properties:{scene:{...scenePathSchema,description:"Root-relative path to the spatial scene JSON source (1 MiB maximum)."},direction:{type:"string",description:"Root-relative path to the slopcamera.spatial-direction JSON document."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","report","summary"],properties:{ok:{const:true},source:{type:"string"},report:{type:"object"},summary:{type:"object",additionalProperties:false,required:["findingCount","returnedFindingCount","findingsTruncated","errorCount","warningCount"],properties:{findingCount:{type:"integer",minimum:0},returnedFindingCount:{type:"integer",minimum:0},findingsTruncated:{type:"boolean"},errorCount:{type:"integer",minimum:0},warningCount:{type:"integer",minimum:0}}}}},annotations:{title:"Check scene direction",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"plan_scene_direction",title:"Plan scene direction",description:"Compile one authored direction document into a deterministic, unverified proposal document \u2014 performance actions, camera rigs, shots and looks bound to the actual scene digest. Proposals are advisory only; nothing is applied to the scene.",inputSchema:{type:"object",additionalProperties:false,required:["scene","direction"],properties:{scene:{...scenePathSchema,description:"Root-relative path to the spatial scene JSON source (1 MiB maximum)."},direction:{type:"string",description:"Root-relative path to the slopcamera.spatial-direction JSON document."},camera_id:{...sceneCameraIdSchema,description:"Optional camera used to resolve coverage; defaults to the sole or first sorted scene camera."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","compilation","summary"],properties:{ok:{const:true},source:{type:"string"},compilation:{type:"object"},summary:{type:"object",additionalProperties:false,required:["advisoryCount","unresolvedIntentCount","proposalCounts"],properties:{advisoryCount:{type:"integer",minimum:0},unresolvedIntentCount:{type:"integer",minimum:0},proposalCounts:{type:"object",additionalProperties:false,required:["performance","cameraRigs","shots","lookIntents"],properties:{performance:{type:"integer",minimum:0},cameraRigs:{type:"integer",minimum:0},shots:{type:"integer",minimum:0},lookIntents:{type:"integer",minimum:0}}}}}}},annotations:{title:"Plan scene direction",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"plan_scene_gallery",title:"Plan scene gallery",description:"Compile a bounded set of single-axis direction variants into separate content-addressed candidate compilations plus a preview-reel plan. The planner never selects or promotes a candidate.",inputSchema:{type:"object",additionalProperties:false,required:["scene","direction","axis"],properties:{scene:{...scenePathSchema,description:"Root-relative path to the spatial scene JSON source (1 MiB maximum)."},direction:{type:"string",description:"Root-relative path to the slopcamera.spatial-direction JSON document."},axis:{type:"string",enum:[...SpatialGalleryAxisSchema.options],description:"Variation axis for the bounded candidate set."},camera_id:{...sceneCameraIdSchema,description:"Optional camera used to resolve coverage."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","gallery","summary"],properties:{ok:{const:true},source:{type:"string"},gallery:{type:"object"},summary:{type:"object",additionalProperties:false,required:["axis","candidateCount","selected"],properties:{axis:{type:"string"},candidateCount:{type:"integer",minimum:0},selected:{const:false}}}}},annotations:{title:"Plan scene gallery",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"check_scene_effects",title:"Check scene effects",description:"Validate one slopcamera.spatial-render-effects document or {document, documentSha256} binding against one scene: scene-digest bindings, entity and asset closure, and nested plan/particle/simulation digests report as findings before any host work.",inputSchema:{type:"object",additionalProperties:false,required:["scene","effects"],properties:{scene:{...scenePathSchema,description:"Root-relative path to the spatial scene JSON source (1 MiB maximum)."},effects:{type:"string",description:"Root-relative path to the render-effects document or binding JSON."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","report","summary"],properties:{ok:{const:true},source:{type:"string"},report:{type:"object"},summary:{type:"object",additionalProperties:false,required:["findingCount","returnedFindingCount","findingsTruncated","errorCount","warningCount"],properties:{findingCount:{type:"integer",minimum:0},returnedFindingCount:{type:"integer",minimum:0},findingsTruncated:{type:"boolean"},errorCount:{type:"integer",minimum:0},warningCount:{type:"integer",minimum:0}}}}},annotations:{title:"Check scene effects",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"plan_scene_effects",title:"Plan scene effects",description:"Assemble a canonical {document, documentSha256} render-effects binding for one scene from a draft JSON naming renderPlan, particleSystems and simulationBakes. Entity and asset closure rejects before the document is formed; the scene digest binds here, never by caller assertion.",inputSchema:{type:"object",additionalProperties:false,required:["scene","draft"],properties:{scene:{...scenePathSchema,description:"Root-relative path to the spatial scene JSON source (1 MiB maximum)."},draft:{type:"string",description:"Root-relative path to the effects draft JSON ({renderPlan, particleSystems?, simulationBakes?})."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","binding","summary"],properties:{ok:{const:true},source:{type:"string"},binding:{type:"object"},summary:{type:"object",additionalProperties:false,required:["documentSha256","particleSystemCount","simulationBakeCount"],properties:{documentSha256:{type:"string"},particleSystemCount:{type:"integer",minimum:0},simulationBakeCount:{type:"integer",minimum:0}}}}},annotations:{title:"Plan scene effects",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"check_scene_behavior",title:"Check scene behavior",description:"Validate one slopcamera.spatial-behavior document against one scene: bake-safe organism profile (input/const/fn/repeat/each/organism cells only), manifest-closure digests, entity and scene-digest bindings, channel declarations, fn-catalog resolution, wiring, and budget bounds report as findings before any host work.",inputSchema:{type:"object",additionalProperties:false,required:["scene","behavior"],properties:{scene:{...scenePathSchema,description:"Root-relative path to the spatial scene JSON source (1 MiB maximum)."},behavior:{type:"string",description:"Root-relative path to the spatial behavior document JSON."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","report","summary"],properties:{ok:{const:true},source:{type:"string"},report:{type:"object"},summary:{type:"object",additionalProperties:false,required:["findingCount","returnedFindingCount","findingsTruncated","errorCount","warningCount"],properties:{findingCount:{type:"integer",minimum:0},returnedFindingCount:{type:"integer",minimum:0},findingsTruncated:{type:"boolean"},errorCount:{type:"integer",minimum:0},warningCount:{type:"integer",minimum:0}}}}},annotations:{title:"Check scene behavior",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"audit_scene_behavior",title:"Audit scene behavior trace",description:"Analyze a baked behavior trace for pathological patterns: state thrash (rapid transitions), exact periodicity (trivially cyclic channels), dead channels (constant-valued), and unreachable states (declared in transitions but never visited). Advisory findings for gallery review \u2014 never selects or promotes.",inputSchema:{type:"object",additionalProperties:false,required:["bake"],properties:{bake:{type:"string",description:"Root-relative path to the behavior bake JSON (slopcamera.spatial-behavior-bake)."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","report","summary"],properties:{ok:{const:true},source:{type:"string"},report:{type:"object"},summary:{type:"object",additionalProperties:false,required:["findingCount","channelCount","emittedCount"],properties:{findingCount:{type:"integer",minimum:0},channelCount:{type:"integer",minimum:0},emittedCount:{type:"integer",minimum:0}}}}},annotations:{title:"Audit scene behavior trace",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}},{name:"audit_scene_temporal",title:"Audit scene temporal evidence",description:"Evaluate one root-relative spatial scene at bounded sample times under one camera and report deterministic temporal findings: visibility flicker, transform discontinuity, foot-slide against declared contact windows, camera acceleration/jerk/angular velocity, exposure and focus jumps, and resource spikes.",inputSchema:{type:"object",additionalProperties:false,required:["path","camera_id"],properties:{path:scenePathSchema,camera_id:sceneCameraIdSchema,times_us:{type:"array",items:{type:"integer",minimum:0,maximum:SPATIAL_SCENE_LIMITS.durationUs},minItems:2,maxItems:SPATIAL_TEMPORAL_AUDIT_LIMITS.samples,description:"Optional sample times in microseconds (2\u2013256). Defaults to evenly spaced coverage of the scene duration."},contacts:{type:"string",description:"Optional root-relative path to a {contacts: [...]} ground-contact evidence JSON."},cut_before_us:{type:"array",items:{type:"integer",minimum:0,maximum:SPATIAL_SCENE_LIMITS.durationUs},maxItems:SPATIAL_TEMPORAL_AUDIT_LIMITS.cutBoundaries,description:"Optional intentional cut boundaries; discontinuities exactly at a cut are not findings."}}},outputSchema:{type:"object",additionalProperties:false,required:["ok","source","report","summary"],properties:{ok:{const:true},source:{type:"string"},report:{type:"object"},summary:{type:"object",additionalProperties:false,required:["findingCount","returnedFindingCount","findingsTruncated","sampleCount"],properties:{findingCount:{type:"integer",minimum:0},returnedFindingCount:{type:"integer",minimum:0},findingsTruncated:{type:"boolean"},sampleCount:{type:"integer",minimum:2}}}}},annotations:{title:"Audit scene temporal evidence",readOnlyHint:true,destructiveHint:false,idempotentHint:true,openWorldHint:false}}]);class ToolFailure extends Error{code;issues;constructor(code,message,issues){super(message);this.name="ToolFailure";this.code=code;if(issues!==undefined)this.issues=issues}}function isRecord2(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function safeFragment(value,maximumLength=160){return value.replace(/[\u0000-\u001f\u007f]/g," ").replace(/\s+/g," ").trim().slice(0,maximumLength)}function safeIssues(issues){return issues.slice(0,24).map((issue)=>safeFragment(issue,240))}function rejectUnknownKeys(value,allowed){const unknown=Object.keys(value).filter((key)=>!allowed.has(key));if(unknown.length>0){throw new ToolFailure("INVALID_ARGUMENTS",`Unsupported argument: ${safeFragment(unknown[0]??"unknown")}.`)}}function parsePath(value){if(typeof value!=="string"||value.length===0){throw new ToolFailure("INVALID_ARGUMENTS","path must be a non-empty root-relative string.")}if(!value.toLowerCase().endsWith(".diagram.json")){throw new ToolFailure("INVALID_ARGUMENTS","path must end in .diagram.json.")}return value}function parseCheckArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["path"]));return{path:parsePath(value.path)}}function parseRenderArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["path","out_dir","scale"]));const outDirectory=value.out_dir;if(outDirectory!==undefined&&(typeof outDirectory!=="string"||outDirectory.length===0)){throw new ToolFailure("INVALID_ARGUMENTS","out_dir must be a non-empty root-relative string when present.")}const scale=value.scale??defaultScale;if(typeof scale!=="number"||!Number.isFinite(scale)||scale<=0||scale>mcpMaximumScale){throw new ToolFailure("RENDER_LIMIT",`scale must be greater than zero and no more than ${mcpMaximumScale}.`)}return{path:parsePath(value.path),...outDirectory===undefined?{}:{outDirectory},scale}}function parseSearchArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["query","limit"]));const query=value.query??"";const limit=value.limit??slopcameraOperationCodes.length;if(typeof query!=="string"||query.length>200||/[\u0000-\u001f\u007f]/u.test(query)||!Number.isInteger(limit)||limit<1||limit>20){throw new ToolFailure("INVALID_ARGUMENTS","query must be a bounded string and limit must be an integer from 1 through 20.")}return{query,limit}}function parseExecuteArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["operation","input"]));if(typeof value.operation!=="string"||!slopcameraOperationCodes.includes(value.operation)||!isRecord2(value.input)){throw new ToolFailure("INVALID_ARGUMENTS","operation must be an exact Slopcamera operation code and input must be an object.")}return{operation:value.operation,input:value.input}}var sceneCameraIdPattern=/^camera_[a-zA-Z0-9][a-zA-Z0-9_-]*$/u;function parseScenePath(value,label){if(typeof value!=="string"||value.length===0){throw new ToolFailure("INVALID_ARGUMENTS",`${label} must be a non-empty root-relative string.`)}if(!value.toLowerCase().endsWith(".json")){throw new ToolFailure("INVALID_ARGUMENTS",`${label} must end in .json.`)}return value}function parseSceneCameraId(value){if(typeof value!=="string"||value.length>128||!sceneCameraIdPattern.test(value)){throw new ToolFailure("INVALID_ARGUMENTS","camera_id must be a scene camera identifier (camera_ followed by letters, digits, _ or -).")}return value}function parseSceneSourceArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["path"]));return{path:parseScenePath(value.path,"path")}}function parseAuditSceneArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["path","camera_id","times_us","asset_bounds"]));let timesUs;if(value.times_us!==undefined){if(!Array.isArray(value.times_us)||value.times_us.length===0||value.times_us.length>SPATIAL_AUDIT_LIMITS.samples||value.times_us.some((timeUs)=>!Number.isSafeInteger(timeUs)||timeUs<0||timeUs>SPATIAL_SCENE_LIMITS.durationUs)){throw new ToolFailure("INVALID_ARGUMENTS",`times_us must be an array of 1\u2013${SPATIAL_AUDIT_LIMITS.samples} integer microsecond times within the scene duration bound.`)}timesUs=[...value.times_us]}return{path:parseScenePath(value.path,"path"),cameraId:parseSceneCameraId(value.camera_id),...timesUs===undefined?{}:{timesUs},..."asset_bounds"in value?{assetBounds:value.asset_bounds}:{}}}function parseDiffScenesArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["path","other"]));return{path:parseScenePath(value.path,"path"),other:parseScenePath(value.other,"other")}}function parseEvaluateSceneArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["path","camera_id","time_us"]));const timeUs=value.time_us;if(typeof timeUs!=="number"||!Number.isSafeInteger(timeUs)||timeUs<0||timeUs>SPATIAL_SCENE_LIMITS.durationUs){throw new ToolFailure("INVALID_ARGUMENTS",`time_us must be an integer microsecond time from 0 through ${SPATIAL_SCENE_LIMITS.durationUs}.`)}return{path:parseScenePath(value.path,"path"),cameraId:parseSceneCameraId(value.camera_id),timeUs}}function parseSceneDirectionArguments(value,requireAxis){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["scene","direction","camera_id","axis"]));if(requireAxis&&!SpatialGalleryAxisSchema.safeParse(value.axis).success){throw new ToolFailure("INVALID_ARGUMENTS",`axis must be one of ${SpatialGalleryAxisSchema.options.join(", ")}.`)}if(!requireAxis&&value.axis!==undefined){throw new ToolFailure("INVALID_ARGUMENTS","axis is only valid for plan_scene_gallery.")}return{scene:parseScenePath(value.scene,"scene"),direction:parseScenePath(value.direction,"direction"),...value.camera_id===undefined?{}:{cameraId:parseSceneCameraId(value.camera_id)},...value.axis===undefined?{}:{axis:value.axis}}}function parseSceneEffectsArguments(value,effectsKey){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["scene",effectsKey]));return{scene:parseScenePath(value.scene,"scene"),effects:parseScenePath(value[effectsKey],effectsKey)}}function parseSceneBehaviorArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["scene","behavior"]));return{scene:parseScenePath(value.scene,"scene"),behavior:parseScenePath(value.behavior,"behavior")}}function parseSceneBehaviorAuditArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["bake"]));return{bake:parseScenePath(value.bake,"bake")}}function parseSceneTemporalArguments(value){if(!isRecord2(value)){throw new ToolFailure("INVALID_ARGUMENTS","Tool arguments must be an object.")}rejectUnknownKeys(value,new Set(["path","camera_id","times_us","contacts","cut_before_us"]));const boundedTimes=(key,maximum)=>{const entries=value[key];if(entries===undefined)return;if(!Array.isArray(entries)||entries.length===0||entries.length>maximum||entries.some((timeUs)=>!Number.isSafeInteger(timeUs)||timeUs<0||timeUs>SPATIAL_SCENE_LIMITS.durationUs)){throw new ToolFailure("INVALID_ARGUMENTS",`${key} must be an array of 1\u2013${maximum} integer microsecond times within the scene duration bound.`)}return[...entries]};const timesUs=boundedTimes("times_us",SPATIAL_TEMPORAL_AUDIT_LIMITS.samples);const cutBeforeUs=boundedTimes("cut_before_us",SPATIAL_TEMPORAL_AUDIT_LIMITS.cutBoundaries);if(timesUs!==undefined&×Us.length<2){throw new ToolFailure("INVALID_ARGUMENTS","times_us must contain at least 2 sample times.")}return{path:parseScenePath(value.path,"path"),cameraId:parseSceneCameraId(value.camera_id),...timesUs===undefined?{}:{timesUs},...value.contacts===undefined?{}:{contacts:parseScenePath(value.contacts,"contacts")},...cutBeforeUs===undefined?{}:{cutBeforeUs}}}function assertBuiltInIcons(spec){for(const shape of spec.shapes){if((shape.type==="rect"||shape.type==="ellipse")&&shape.icon!==undefined&&!Object.hasOwn(builtInIcons,shape.icon)){throw new ToolFailure("UNKNOWN_ICON",`Shape ${safeFragment(shape.id)} requests unavailable built-in icon ${safeFragment(shape.icon)}.`)}}}function assertComplexityLimits(spec){const edgeCount=spec.edges?.length??0;if(spec.shapes.length>mcpMaximumShapes||edgeCount>mcpMaximumEdges){throw new ToolFailure("COMPLEXITY_LIMIT",`Diagram may contain at most ${mcpMaximumShapes} shapes and ${mcpMaximumEdges} edges in MCP mode.`)}}function assertRawComplexityLimits(value){if(!isRecord2(value))return;const shapeCount=Array.isArray(value.shapes)?value.shapes.length:0;const edgeCount=Array.isArray(value.edges)?value.edges.length:0;if(shapeCount>mcpMaximumShapes||edgeCount>mcpMaximumEdges){throw new ToolFailure("COMPLEXITY_LIMIT",`Diagram may contain at most ${mcpMaximumShapes} shapes and ${mcpMaximumEdges} edges in MCP mode.`)}}function assertRenderLimits(spec,scale){const scaledWidth=spec.canvas.width*scale;const scaledHeight=spec.canvas.height*scale;const pixels=Math.ceil(scaledWidth)*Math.ceil(scaledHeight);if(!Number.isFinite(pixels)||scaledWidth<1||scaledHeight<1||pixels>mcpMaximumRenderedPixels){throw new ToolFailure("RENDER_LIMIT",`Scaled canvas must be at least 1 pixel on each axis and no more than ${mcpMaximumRenderedPixels.toLocaleString("en-US")} pixels total.`)}}function boundedSlice(items,maximum){return items.length>maximum?items.slice(0,maximum):items}function publicFinding(finding){return{code:safeFragment(finding.code,64),message:safeFragment(finding.message,240),shapeIds:finding.shapeIds.slice(0,maximumShapeIdsPerFinding).map((shapeId)=>safeFragment(shapeId,120))}}function publicFindings(findings){return findings.slice(0,mcpMaximumReturnedFindings).map(publicFinding)}function diagramSummary(spec,findingCount,returnedFindingCount){return{shapeCount:spec.shapes.length,edgeCount:spec.edges?.length??0,findingCount,returnedFindingCount,findingsTruncated:returnedFindingCount<findingCount}}function successResult(text,structuredContent){return{content:[{type:"text",text}],structuredContent}}function failureResult(error){let code="INTERNAL_ERROR";let message="The tool failed safely.";let issues;if(error instanceof ToolFailure){code=error.code;message=safeFragment(error.message,320);issues=error.issues}else if(error instanceof WorkspaceBoundaryError){code=error.code;message=safeFragment(error.message,320)}else if(error instanceof SlopcameraCloudError){code=error.code;message=safeFragment(error.message.replace(/^\[[A-Z_]+\]\s*/u,""),320)}else if(error instanceof SlopcameraOperationError){code=error.code;message=safeFragment(error.message.replace(/^\[[A-Z_]+\]\s*/u,""),320)}else if(error instanceof VectorizeError){code=`VECTORIZE_${error.code.toUpperCase()}`;message="Local vectorization failed safely."}else if(error instanceof DiagramValidationError){code="INVALID_DIAGRAM";message="Diagram source did not pass validation.";issues=safeIssues(error.issues)}else if(error instanceof SlopcameraCodeError){code=error.code.toUpperCase().replace(/-/g,"_");message=safeFragment(error.message,320)}else if(error instanceof HostResourceError){code=`HOST_RESOURCE_${error.code}`;message="Host-resource admission failed safely."}else if(typeof error==="object"&&error!==null&&"issues"in error&&Array.isArray(error.issues)&&error.issues.every((issue)=>typeof issue==="string")){code="INVALID_LAYOUT";message="Diagram layout could not be resolved.";issues=safeIssues(error.issues)}const issueText=issues===undefined||issues.length===0?"":`
|
|
25
25
|
${issues.map((issue)=>`- ${issue}`).join(`
|
|
26
26
|
`)}`;return{content:[{type:"text",text:`[${code}] ${message}${issueText}`}],isError:true}}function portableDirectory(filePath){const separator=filePath.lastIndexOf("/");return separator===-1?".":filePath.slice(0,separator)}async function atomicOverwrite(filePath,data){const temporaryPath=join4(dirname5(filePath),`.${crypto.randomUUID()}.slopcamera-mcp.tmp`);try{await writeFile3(temporaryPath,data,{flag:"wx"});try{await rename3(temporaryPath,filePath)}catch(error){const code=typeof error==="object"&&error!==null&&"code"in error&&typeof error.code==="string"?error.code:undefined;if(code!=="EEXIST"&&code!=="EPERM")throw error;await rm3(filePath,{force:true});await rename3(temporaryPath,filePath)}}finally{await rm3(temporaryPath,{force:true})}}async function loadDiagram(boundary,path){const source=await boundary.readSource(path);let parsed;try{parsed=JSON.parse(source.text)}catch{throw new ToolFailure("INVALID_JSON","Diagram source is not valid JSON.")}assertRawComplexityLimits(parsed);const spec=parseDiagramSpec(parsed);assertComplexityLimits(spec);assertBuiltInIcons(spec);return{source,spec}}async function loadScene(boundary,path){const source=await boundary.readSource(path,"Scene source");const parsed=parseJsonText(source.text);try{return{source,scene:parseSpatialScene(parsed)}}catch(error){if(error instanceof SpatialSceneError){const where=error.path==="scene"?"":` at ${error.path}`;throw new ToolFailure("INVALID_SCENE",`Scene source did not pass validation${where}: ${safeFragment(error.message,240)}`)}throw error}}function parseJsonText(text){try{return JSON.parse(text)}catch{throw new ToolFailure("INVALID_JSON","Source is not valid JSON.")}}async function loadJson(boundary,path,label){const source=await boundary.readSource(path,label);return{source,value:parseJsonText(source.text)}}class SlopcameraMcpToolRuntime{boundary;generateDependencies;hostResourceCoordinator;renderQueue=Promise.resolve();constructor(boundary,generateDependencies,hostResourceCoordinator){this.boundary=boundary;this.generateDependencies=generateDependencies;this.hostResourceCoordinator=hostResourceCoordinator}static async create(rootDirectory,generateDependencies={},hostResourceCoordinator){return new SlopcameraMcpToolRuntime(await WorkspaceBoundary.create(rootDirectory),generateDependencies,hostResourceCoordinator??createDefaultHostResourceCoordinator())}async withHostAdmission(operation,callback){return await withSlopcameraOperationHostAdmission(operation,callback,{hostResourceCoordinator:this.hostResourceCoordinator})}async withSceneAdmission(callback){return await this.hostResourceCoordinator.withLease([{resource:"cpu",amount:1},{resource:"local-io",amount:1}],async(lease)=>{await lease.assertOwned();return await callback(lease)})}enqueueRender(operation){const result=this.renderQueue.then(operation,operation);this.renderQueue=result.then(()=>{return},()=>{return});return result}async call(name,argumentsValue){try{if(name==="check_diagram"){const options=parseCheckArguments(argumentsValue);return await this.withHostAdmission("slopcamera.diagram.check",async()=>await this.check(options))}if(name==="render_diagram"){const options=parseRenderArguments(argumentsValue);return await this.enqueueRender(async()=>await this.withHostAdmission("slopcamera.diagram.render",async()=>await this.render(options)))}if(name==="search_slopcamera"){const options=parseSearchArguments(argumentsValue);const operations=searchSlopcameraOperations(options.query,options.limit);return successResult(`Found ${operations.length} Slopcamera operation${operations.length===1?"":"s"}.`,{ok:true,operations})}if(name==="execute_slopcamera"){const options=parseExecuteArguments(argumentsValue);return await this.execute(options)}if(name==="check_scene"){const options=parseSceneSourceArguments(argumentsValue);return await this.withSceneAdmission(async()=>await this.checkScene(options))}if(name==="inspect_scene"){const options=parseSceneSourceArguments(argumentsValue);return await this.withSceneAdmission(async()=>await this.inspectScene(options))}if(name==="audit_scene"){const options=parseAuditSceneArguments(argumentsValue);return await this.withSceneAdmission(async()=>await this.auditScene(options))}if(name==="diff_scenes"){const options=parseDiffScenesArguments(argumentsValue);return await this.withSceneAdmission(async()=>await this.diffScenes(options))}if(name==="evaluate_scene"){const options=parseEvaluateSceneArguments(argumentsValue);return await this.withSceneAdmission(async()=>await this.evaluateScene(options))}if(name==="check_scene_direction"){const options=parseSceneDirectionArguments(argumentsValue,false);return await this.withSceneAdmission(async()=>await this.checkSceneDirection(options))}if(name==="plan_scene_direction"){const options=parseSceneDirectionArguments(argumentsValue,false);return await this.withSceneAdmission(async()=>await this.planSceneDirection(options))}if(name==="plan_scene_gallery"){const options=parseSceneDirectionArguments(argumentsValue,true);return await this.withSceneAdmission(async()=>await this.planSceneGallery(options))}if(name==="check_scene_effects"){const options=parseSceneEffectsArguments(argumentsValue,"effects");return await this.withSceneAdmission(async()=>await this.checkSceneEffects(options))}if(name==="plan_scene_effects"){const options=parseSceneEffectsArguments(argumentsValue,"draft");return await this.withSceneAdmission(async()=>await this.planSceneEffects(options))}if(name==="check_scene_behavior"){const options=parseSceneBehaviorArguments(argumentsValue);return await this.withSceneAdmission(async()=>await this.checkSceneBehavior(options))}if(name==="audit_scene_behavior"){const options=parseSceneBehaviorAuditArguments(argumentsValue);return await this.withSceneAdmission(async()=>await this.auditSceneBehavior(options))}if(name==="audit_scene_temporal"){const options=parseSceneTemporalArguments(argumentsValue);return await this.withSceneAdmission(async()=>await this.auditSceneTemporal(options))}throw new ToolFailure("UNKNOWN_TOOL","Requested tool is not available.")}catch(error){return failureResult(error)}}wrapSemanticResult(operation,result){if(result.isError===true)return result;return{content:result.content,structuredContent:{ok:true,operation,result:result.structuredContent??{}}}}async execute(options){if(options.operation==="slopcamera.diagram.check"){const input=parseSlopcameraOperationInput(options.operation,options.input);return this.wrapSemanticResult(options.operation,await this.withHostAdmission(options.operation,async()=>await this.check({path:input.path})))}if(options.operation==="slopcamera.diagram.render"){const input=parseSlopcameraOperationInput(options.operation,options.input);return this.enqueueRender(async()=>await this.withHostAdmission(options.operation,async()=>this.wrapSemanticResult(options.operation,await this.render({path:input.path,...input.outDirectory===undefined?{}:{outDirectory:input.outDirectory},scale:input.scale??defaultScale}))))}if(options.operation==="slopcamera.image.vectorize"){const input=parseSlopcameraOperationInput(options.operation,options.input);return this.enqueueRender(async()=>await this.withHostAdmission(options.operation,async(lease)=>{const source=await this.boundary.resolveInputFile(input.inputPath,vectorizeHardLimits.maxInputBytes);const output=await this.boundary.prepareOutputFile(input.outputPath);const result=await vectorizeImage(source.absolutePath,{outputPath:output.absolutePath,...input.duotone===undefined?{}:{duotone:input.duotone},...input.alphaCutoff===undefined?{}:{alphaCutoff:input.alphaCutoff},...input.timeoutMs===undefined?{}:{limits:{maxDurationMs:input.timeoutMs}},inheritedFileDescriptors:[lease.inheritedFileDescriptor]});return successResult(`Executed ${options.operation}: ${output.relativePath}`,{ok:true,operation:options.operation,result:{inputPath:source.relativePath,outputPath:output.relativePath,receipt:result.receipt}})}))}if(options.operation==="slopcamera.image.gallery"){const input=parseSlopcameraOperationInput(options.operation,options.input);return await this.withHostAdmission(options.operation,async()=>{const output=await this.boundary.prepareOutputDirectory(input.outputDir);const receipt=await generateSlopcameraImageGallery({...input,outputDir:output.absolutePath},this.generateDependencies);return successResult(`Executed ${options.operation}: ${receipt.counts.generated}/${receipt.counts.requested} candidates in ${output.relativePath}.`,{ok:true,operation:options.operation,result:{...receipt,outputDir:output.relativePath}})})}return await this.withHostAdmission(options.operation,async()=>{const input=parseSlopcameraOperationInput(options.operation,options.input);const output=await this.boundary.prepareOutputFile(input.outputPath);const generated=await generateSlopcameraImageFile({...input,outputPath:output.absolutePath},this.generateDependencies);return successResult(`Executed ${options.operation}: ${output.relativePath} (request ${safeFragment(generated.requestId,256)}).`,{ok:true,operation:options.operation,result:{bytes:generated.bytes,mediaType:generated.mediaType,model:generated.model,outputPath:output.relativePath,provider:generated.provider,requestId:generated.requestId,sha256:generated.sha256,warnings:generated.warnings}})})}async check(options){const{source,spec}=await loadDiagram(this.boundary,options.path);const allFindings=lintDiagram(spec);const findings=publicFindings(allFindings);const summary=diagramSummary(spec,allFindings.length,findings.length);const text=allFindings.length===0?`Checked ${source.relativePath}: no findings.`:`Checked ${source.relativePath}: ${allFindings.length} finding${allFindings.length===1?"":"s"}; ${findings.length} returned in structured content${findings.length<allFindings.length?" (truncated)":""}.`;return successResult(text,{ok:true,source:source.relativePath,findings,summary})}async render(options){const{source,spec}=await loadDiagram(this.boundary,options.path);assertRenderLimits(spec,options.scale);const outputDirectory=await this.boundary.prepareOutputDirectory(options.outDirectory??portableDirectory(source.relativePath));const tldr=serializeTldr(spec,builtInConfig);const[light,dark]=await Promise.all([renderSvg(spec,"light",builtInConfig),renderSvg(spec,"dark",builtInConfig)]);const lightPng=renderPng(light,builtInConfig,options.scale);const darkPng=renderPng(dark,builtInConfig,options.scale);const absoluteArtifacts={spec:source.absolutePath,tldr:join4(outputDirectory.absolutePath,`${spec.name}.tldr`),lightSvg:join4(outputDirectory.absolutePath,`${spec.name}.light.svg`),darkSvg:join4(outputDirectory.absolutePath,`${spec.name}.dark.svg`),lightPng:join4(outputDirectory.absolutePath,`${spec.name}.light.png`),darkPng:join4(outputDirectory.absolutePath,`${spec.name}.dark.png`)};await Promise.all([atomicOverwrite(absoluteArtifacts.tldr,tldr),atomicOverwrite(absoluteArtifacts.lightSvg,light.svg),atomicOverwrite(absoluteArtifacts.darkSvg,dark.svg),atomicOverwrite(absoluteArtifacts.lightPng,lightPng),atomicOverwrite(absoluteArtifacts.darkPng,darkPng)]);const artifacts={tldr:this.boundary.toRelativePath(absoluteArtifacts.tldr),lightSvg:this.boundary.toRelativePath(absoluteArtifacts.lightSvg),darkSvg:this.boundary.toRelativePath(absoluteArtifacts.darkSvg),lightPng:this.boundary.toRelativePath(absoluteArtifacts.lightPng),darkPng:this.boundary.toRelativePath(absoluteArtifacts.darkPng)};const allFindings=lintDiagram(spec);const findings=publicFindings(allFindings);const summary=diagramSummary(spec,allFindings.length,findings.length);const text=[`Rendered ${source.relativePath} with built-in assets:`,...Object.values(artifacts).map((artifact)=>`- ${artifact}`)].join(`
|
|
27
27
|
`);return successResult(text,{ok:true,source:source.relativePath,scale:options.scale,findings,artifacts,summary})}async checkScene(options){const{source,scene}=await loadScene(this.boundary,options.path);const summary={durationUs:scene.durationUs,entityCount:scene.entities.length,cameraCount:scene.cameras.length,assetCount:scene.assets.length,animationCount:scene.animations.length,generatorCount:scene.generators.length,overrideCount:scene.overrides.length};return successResult(`Checked ${source.relativePath}: scene ${safeFragment(scene.sceneId,128)} is valid (${summary.entityCount} entities, ${summary.cameraCount} cameras).`,{ok:true,source:source.relativePath,sceneId:scene.sceneId,sceneSha256:spatialValueSha256(scene),summary})}async inspectScene(options){const{source,scene}=await loadScene(this.boundary,options.path);const inspection=inspectSpatialScene(scene);const entities=boundedSlice(inspection.entities,mcpMaximumReturnedEntities);const summary={entityCount:inspection.entities.length,returnedEntityCount:entities.length,entitiesTruncated:entities.length<inspection.entities.length};return successResult(`Inspected ${source.relativePath}: ${summary.entityCount} entities${summary.entitiesTruncated?`, first ${summary.returnedEntityCount} returned in structured content`:""}.`,{ok:true,source:source.relativePath,inspection:{...inspection,entities},summary})}async auditScene(options){const{source,scene}=await loadScene(this.boundary,options.path);const report=auditSpatialScene(scene,{cameraId:options.cameraId,...options.timesUs===undefined?{}:{timesUs:options.timesUs},...options.assetBounds===undefined?{}:{assetBounds:normalizeSpatialAuditAssetBounds(options.assetBounds)}});let sampleBudget=mcpMaximumReturnedAuditSamples;const entities=[];for(const entity of report.entities){if(entities.length>=mcpMaximumReturnedEntities||entity.samples.length>sampleBudget){break}sampleBudget-=entity.samples.length;entities.push(entity)}const findings=boundedSlice(report.findings,mcpMaximumReturnedFindings);const findingCount=report.findings.length+report.omittedFindings;const summary={findingCount,returnedFindingCount:findings.length,findingsTruncated:findings.length<findingCount,entityCount:report.entities.length,returnedEntityCount:entities.length,entitiesTruncated:entities.length<report.entities.length};return successResult(`Audited ${source.relativePath} under ${options.cameraId}: ${findingCount} finding${findingCount===1?"":"s"} across ${report.entities.length} entities and ${report.timesUs.length} samples${summary.findingsTruncated||summary.entitiesTruncated?" (truncated)":""}.`,{ok:true,source:source.relativePath,report:{...report,entities,findings},summary})}async diffScenes(options){const{source,scene}=await loadScene(this.boundary,options.path);const other=await loadScene(this.boundary,options.other);const entries=diffSpatialScenes(scene,other.scene);const diff=boundedSlice(entries,mcpMaximumReturnedDiffEntries);const summary={entryCount:entries.length,returnedEntryCount:diff.length,diffTruncated:diff.length<entries.length};return successResult(`Diffed ${source.relativePath} against ${other.source.relativePath}: ${summary.entryCount} entr${summary.entryCount===1?"y":"ies"}${summary.diffTruncated?`, first ${summary.returnedEntryCount} returned in structured content`:""}.`,{ok:true,source:source.relativePath,other:other.source.relativePath,sceneSha256:spatialValueSha256(scene),otherSha256:spatialValueSha256(other.scene),diff,summary})}async evaluateScene(options){const{source,scene}=await loadScene(this.boundary,options.path);const snapshot=evaluateSpatialScene(scene,{timeUs:options.timeUs,cameraId:options.cameraId});const entities=boundedSlice(snapshot.entities,mcpMaximumReturnedEntities);const summary={entityCount:snapshot.entities.length,returnedEntityCount:entities.length,entitiesTruncated:entities.length<snapshot.entities.length};return successResult(`Evaluated ${source.relativePath} at timeUs ${options.timeUs} under ${options.cameraId}: ${summary.entityCount} entities${summary.entitiesTruncated?`, first ${summary.returnedEntityCount} returned in structured content`:""}.`,{ok:true,source:source.relativePath,snapshot:{...snapshot,entities},summary})}async checkSceneDirection(options){const{source,scene}=await loadScene(this.boundary,options.scene);const direction=await loadJson(this.boundary,options.direction,"Direction source");const report=checkSpatialDirection({direction:direction.value,scene});const findings=boundedSlice(report.findings,mcpMaximumReturnedFindings);const summary={findingCount:report.findings.length,returnedFindingCount:findings.length,findingsTruncated:findings.length<report.findings.length,errorCount:report.counts.errors,warningCount:report.counts.warnings};return successResult(`Checked direction ${direction.source.relativePath} against ${source.relativePath}: ${summary.findingCount} finding${summary.findingCount===1?"":"s"} (${summary.errorCount} errors).`,{ok:summary.errorCount===0,source:source.relativePath,report:{...report,findings},summary})}async planSceneDirection(options){const{source,scene}=await loadScene(this.boundary,options.scene);const direction=await loadJson(this.boundary,options.direction,"Direction source");const compilation=compileSpatialDirection({direction:direction.value,scene,...options.cameraId===undefined?{}:{cameraId:options.cameraId}});const summary={advisoryCount:compilation.advisories.length,unresolvedIntentCount:compilation.unresolvedIntents.length,proposalCounts:{performance:compilation.proposals.performance.length,cameraRigs:compilation.proposals.cameraRigs.length,shots:compilation.proposals.shots.length,lookIntents:compilation.proposals.lookIntents.length}};return successResult(`Compiled direction ${direction.source.relativePath}: ${compilation.proposals.shots.length} shot proposals, ${compilation.advisories.length} advisories; all proposals remain unverified.`,{ok:true,source:source.relativePath,compilation,summary})}async planSceneGallery(options){const{source,scene}=await loadScene(this.boundary,options.scene);const direction=await loadJson(this.boundary,options.direction,"Direction source");const gallery=planSpatialDirectionGallery({axis:options.axis,direction:direction.value,scene,...options.cameraId===undefined?{}:{cameraId:options.cameraId}});const summary={axis:gallery.axis,candidateCount:gallery.candidates.length,selected:false};return successResult(`Planned a ${gallery.axis} gallery from ${direction.source.relativePath}: ${gallery.candidates.length} content-addressed candidates; none selected.`,{ok:true,source:source.relativePath,gallery,summary})}async checkSceneEffects(options){const{source,scene}=await loadScene(this.boundary,options.scene);const effects=await loadJson(this.boundary,options.effects,"Effects source");const report=checkSpatialRenderEffects({effects:effects.value,scene});const findings=boundedSlice(report.findings,mcpMaximumReturnedFindings);const summary={findingCount:report.findings.length,returnedFindingCount:findings.length,findingsTruncated:findings.length<report.findings.length,errorCount:report.counts.errors,warningCount:report.counts.warnings};return successResult(`Checked effects ${effects.source.relativePath} against ${source.relativePath}: ${summary.findingCount} finding${summary.findingCount===1?"":"s"} (${summary.errorCount} errors).`,{ok:summary.errorCount===0,source:source.relativePath,report:{...report,findings},summary})}async planSceneEffects(options){const{source,scene}=await loadScene(this.boundary,options.scene);const draft=await loadJson(this.boundary,options.effects,"Effects draft");if(!isRecord2(draft.value)){throw new ToolFailure("INVALID_ARGUMENTS","Effects draft must be a JSON object with renderPlan, particleSystems and simulationBakes fields.")}const binding=planSpatialRenderEffects({...draft.value,scene});const summary={documentSha256:binding.documentSha256,particleSystemCount:binding.document.particleSystems.length,simulationBakeCount:binding.document.simulationBakes.length};return successResult(`Bound effects draft ${draft.source.relativePath} to scene ${summary.documentSha256.slice(0,16)}\u2026: ${summary.particleSystemCount} particle systems, ${summary.simulationBakeCount} simulation bakes.`,{ok:true,source:source.relativePath,binding,summary})}async checkSceneBehavior(options){const{source,scene}=await loadScene(this.boundary,options.scene);const behavior=await loadJson(this.boundary,options.behavior,"Behavior source");const report=checkSpatialBehavior({behavior:behavior.value,scene},spatialBehaviorFnSignatures());const findings=boundedSlice(report.findings,mcpMaximumReturnedFindings);const summary={findingCount:report.findings.length,returnedFindingCount:findings.length,findingsTruncated:findings.length<report.findings.length,errorCount:report.counts.errors,warningCount:report.counts.warnings};return successResult(`Checked behavior ${behavior.source.relativePath} against ${source.relativePath}: ${summary.findingCount} finding${summary.findingCount===1?"":"s"} (${summary.errorCount} errors).`,{ok:summary.errorCount===0,source:source.relativePath,report:{...report,findings},summary})}async auditSceneBehavior(options){const bake=await loadJson(this.boundary,options.bake,"Behavior bake source");const document=parseSpatialValue(SpatialBehaviorBakeSchema,bake.value,"behavior bake");const report=auditSpatialBehaviorTrace({behaviorSha256:document.behaviorSha256,emittedSha256:document.receipt.emittedSha256,emitted:document.emitted,rangeUs:document.rangeUs});const findings=boundedSlice(report.findings,mcpMaximumReturnedFindings);const summary={findingCount:report.findings.length,channelCount:report.channelCount,emittedCount:report.emittedCount};return successResult(`Audited behavior bake ${bake.source.relativePath}: ${summary.findingCount} finding${summary.findingCount===1?"":"s"} across ${summary.channelCount} channel${summary.channelCount===1?"":"s"}.`,{ok:true,source:bake.source.relativePath,report:{...report,findings},summary})}async auditSceneTemporal(options){const{source,scene}=await loadScene(this.boundary,options.path);const times=options.timesUs??(()=>{const count=Math.min(17,Math.max(2,Math.ceil(scene.durationUs/1e6)+1));return Array.from({length:count},(_value,index)=>Math.floor(scene.durationUs*index/(count-1)))})();const contacts=options.contacts===undefined?[]:parseSpatialValue(SpatialTemporalContactsFileSchema,(await loadJson(this.boundary,options.contacts,"Contacts source")).value,"temporal contacts").contacts;const context=createSpatialEvaluationContext(scene);const snapshots=[...times].sort((left,right)=>left-right).map((timeUs)=>evaluateSpatialSceneInContext(context,{cameraId:options.cameraId,timeUs}));const report=auditSpatialTemporalEvidence({snapshots,options:{contacts,...options.cutBeforeUs===undefined?{}:{cutBeforeUs:[...options.cutBeforeUs]}}});const findings=boundedSlice(report.findings,mcpMaximumReturnedFindings);const findingCount=report.findings.length+report.omittedFindings;const summary={findingCount,returnedFindingCount:findings.length,findingsTruncated:findings.length<findingCount,sampleCount:report.sampleCount};return successResult(`Audited ${source.relativePath} over ${report.sampleCount} samples under ${options.cameraId}: ${findingCount} temporal finding${findingCount===1?"":"s"}${summary.findingsTruncated?" (truncated)":""}.`,{ok:true,source:source.relativePath,report:{...report,findings},summary})}}var slopcameraMcpProtocolVersion="2025-11-25";var slopcameraMcpServerName="hraness-slopcamera";var maximumMessageBytes=1024*1024;function isRecord3(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function isJsonRpcId(value){return typeof value==="string"||typeof value==="number"&&Number.isSafeInteger(value)}function isInitializeParams(value){return isRecord3(value)&&typeof value.protocolVersion==="string"&&isRecord3(value.capabilities)&&isRecord3(value.clientInfo)&&typeof value.clientInfo.name==="string"&&typeof value.clientInfo.version==="string"}function parseRequest(value){if(!isRecord3(value)||value.jsonrpc!=="2.0"||typeof value.method!=="string"||value.method.length===0||"id"in value&&!isJsonRpcId(value.id)){throw new Error("invalid request")}return{jsonrpc:"2.0",..."id"in value?{id:value.id}:{},method:value.method,..."params"in value?{params:value.params}:{}}}function success(id,result){return{jsonrpc:"2.0",id,result}}function failure(id,code,message){return{jsonrpc:"2.0",id,error:{code,message}}}function parseToolCall(params){if(!isRecord3(params)||typeof params.name!=="string"||params.arguments!==undefined&&!isRecord3(params.arguments)){throw new Error("invalid params")}const unknownKeys=Object.keys(params).filter((key)=>key!=="name"&&key!=="arguments");if(unknownKeys.length>0)throw new Error("invalid params");return{name:params.name,argumentsValue:params.arguments??{}}}class SlopcameraMcpSession{runtime;serverVersion;state="new";constructor(runtime,serverVersion){this.runtime=runtime;this.serverVersion=serverVersion}async handle(value){let request;try{request=parseRequest(value)}catch{return failure(null,-32600,"Invalid Request")}const notification=request.id===undefined;if(request.method==="notifications/initialized"){if(!notification){return failure(request.id,-32600,"Invalid Request")}if(this.state==="initializing")this.state="ready";return null}if(notification)return null;const id=request.id;if(request.method==="initialize"){if(this.state!=="new"||!isInitializeParams(request.params)){return failure(id,-32602,"Invalid initialize parameters")}this.state="initializing";return success(id,{protocolVersion:slopcameraMcpProtocolVersion,capabilities:{tools:{listChanged:false}},serverInfo:{name:slopcameraMcpServerName,version:this.serverVersion},instructions:"Use check_diagram/render_diagram, the read-only scene tools (check_scene, inspect_scene, audit_scene, diff_scenes, evaluate_scene), or search_slopcamera followed by execute_slopcamera with an exact registry code and typed JSON. Local paths are root-relative; source code is never accepted or evaluated."})}if(this.state!=="ready"){return failure(id,-32002,"Server is not initialized")}if(request.method==="ping")return success(id,{});if(request.method==="tools/list"){if(request.params!==undefined&&(!isRecord3(request.params)||Object.keys(request.params).length>0)){return failure(id,-32602,"Invalid tools/list parameters")}return success(id,{tools:slopcameraMcpTools})}if(request.method==="tools/call"){try{const toolCall=parseToolCall(request.params);if(!slopcameraMcpTools.some((tool)=>tool.name===toolCall.name)){return failure(id,-32602,"Unknown tool")}return success(id,await this.runtime.call(toolCall.name,toolCall.argumentsValue))}catch{return failure(id,-32602,"Invalid tools/call parameters")}}return failure(id,-32601,"Method not found")}}async function defaultWriteLine(line){await new Promise((resolve5,reject)=>{process.stdout.write(`${line}
|
|
28
|
-
`,(error)=>{if(error===null||error===undefined)resolve5();else reject(error)})})}function defaultInput(){return process.stdin}async function emitResponse(writeLine,response){await writeLine(JSON.stringify(response))}async function processLine(line,session,writeLine){if(line.byteLength===0)return;let value;try{const text=new TextDecoder("utf-8",{fatal:true}).decode(line);if(text.trim()==="")return;value=JSON.parse(text)}catch{await emitResponse(writeLine,failure(null,-32700,"Parse error"));return}if(Array.isArray(value)){await emitResponse(writeLine,failure(null,-32600,"Invalid Request"));return}const response=await session.handle(value);if(response!==null)await emitResponse(writeLine,response)}async function runMcpServer(options={}){const runtime=await SlopcameraMcpToolRuntime.create(options.rootDirectory??process.cwd(),options.generateDependencies,options.hostResourceCoordinator);const session=new SlopcameraMcpSession(runtime,options.serverVersion??SLOPCAMERA_VERSION);const writeLine=options.writeLine??defaultWriteLine;let buffered=Buffer.alloc(0);for await(const chunk of options.input??defaultInput()){const bytes=typeof chunk==="string"?Buffer.from(chunk,"utf8"):Buffer.from(chunk);buffered=Buffer.concat([buffered,bytes]);if(buffered.byteLength>maximumMessageBytes&&!buffered.includes(10)){buffered=Buffer.alloc(0);await emitResponse(writeLine,failure(null,-32700,"Parse error"));continue}for(;;){const newline=buffered.indexOf(10);if(newline===-1)break;let line=buffered.subarray(0,newline);buffered=buffered.subarray(newline+1);if(line.at(-1)===13)line=line.subarray(0,-1);if(line.byteLength>maximumMessageBytes){await emitResponse(writeLine,failure(null,-32700,"Parse error"))}else{await processLine(line,session,writeLine)}}}if(buffered.byteLength>0){if(buffered.byteLength>maximumMessageBytes){await emitResponse(writeLine,failure(null,-32700,"Parse error"))}else{await processLine(buffered,session,writeLine)}}}import{z}from"zod";var SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS=Object.freeze({bytes:4*1024*1024,commands:256,evidence:64,modules:64,operations:512,profiles:128,requirements:32,tools:128,workflows:128});var identifier=z.string().min(1).max(192).regex(/^[a-z][a-z0-9]*(?:[-_.:/@][a-z0-9]+)*$/u);var commandName=z.string().min(1).max(128).regex(/^[a-z][a-z0-9-]*(?: [a-z][a-z0-9-]*){0,3}$/u);var digest=z.string().regex(/^[a-f0-9]{64}$/u);var positiveSafeInteger=z.number().int().safe().positive();var nonnegativeSafeInteger=z.number().int().safe().nonnegative();var SlopcameraCapabilityEffectSchema=z.enum(["pure","local-read","local-derived-write","project-mutation","paid-cloud","live-control"]);var SlopcameraCapabilityResumeSchema=z.enum(["deterministic","verified-receipt","recoverable-transaction","ambiguous-after-dispatch","non-resumable-live"]);var SlopcameraCapabilityResourceSchema=z.enum(["cpu","local-io","ffmpeg","vision","whisper","network","paid-call","project-render","project-publication","output-publication","capture-device","browser"]);var SlopcameraCapabilityPreparationSchema=z.enum(["project-state","recording-metadata","screen-capture","camera","microphone","system-audio","typed-text","window-metadata","local-media","provider-options"]);var SlopcameraCapabilityOperationSchema=z.strictObject({cache:z.enum(["none","exact-run","content-addressed"]),cancellable:z.boolean(),effect:SlopcameraCapabilityEffectSchema,inputSchemaId:identifier,key:z.string().min(3).max(256).regex(/^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*@[1-9][0-9]*$/u),kind:z.string().min(3).max(192).regex(/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*(?:\.[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*)+$/u),lifecycle:z.enum(["pure","local-artifact","project-transaction","paid-dispatch","live-control"]),maxDurationMs:positiveSafeInteger,maxFanOut:nonnegativeSafeInteger,maxInputBytes:nonnegativeSafeInteger,maxOutputBytes:nonnegativeSafeInteger,outputSchemaId:identifier,preparation:z.array(SlopcameraCapabilityPreparationSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.requirements),resources:z.array(z.strictObject({amount:positiveSafeInteger,resource:SlopcameraCapabilityResourceSchema})).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.requirements),resume:SlopcameraCapabilityResumeSchema,version:positiveSafeInteger}).superRefine((operation,context)=>{if(operation.key!==`${operation.kind}@${operation.version}`){context.addIssue({code:"custom",path:["key"],message:"Operation key must match kind and version."})}if(new Set(operation.preparation).size!==operation.preparation.length){context.addIssue({code:"custom",path:["preparation"],message:"Operation preparation entries must be unique."})}const resources=operation.resources.map((claim)=>claim.resource);if(new Set(resources).size!==resources.length){context.addIssue({code:"custom",path:["resources"],message:"Operation resource claims must be unique by resource."})}});var SlopcameraCapabilityWorkflowSchema=z.strictObject({id:identifier,inputSchemaId:identifier,key:z.string().min(3).max(256).regex(/^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*@[1-9][0-9]*$/u),version:positiveSafeInteger}).superRefine((workflow,context)=>{if(workflow.key!==`${workflow.id}@${workflow.version}`){context.addIssue({code:"custom",path:["key"],message:"Workflow key must match id and version."})}});var SlopcameraCapabilityToolSchema=z.strictObject({destructive:z.boolean(),idempotent:z.boolean(),name:z.string().min(1).max(128).regex(/^[a-z][a-z0-9_]*$/u),openWorld:z.boolean(),readOnly:z.boolean()}).superRefine((tool,context)=>{if(tool.readOnly&&tool.destructive){context.addIssue({code:"custom",message:"A read-only tool cannot be destructive."})}});var SlopcameraCapabilityCommandSchema=z.strictObject({effect:SlopcameraCapabilityEffectSchema,name:commandName});var SlopcameraCapabilityQualificationSchema=z.strictObject({evidence:z.array(z.string().min(1).max(512)).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.evidence),level:z.enum(["unit","fixture","browser","native","provider"]),status:z.enum(["qualified","conditional","not-qualified"])}).superRefine((qualification,context)=>{if(qualification.status!=="not-qualified"&&qualification.evidence.length===0){context.addIssue({code:"custom",path:["evidence"],message:"Qualified or conditional profiles require evidence references."})}});var SlopcameraCapabilityProfileSchema=z.strictObject({id:identifier,kind:z.enum(["renderer","native-adapter","provider","authoring","interchange"]),qualification:SlopcameraCapabilityQualificationSchema,requirements:z.array(z.strictObject({description:z.string().min(1).max(256),id:identifier,optional:z.boolean()})).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.requirements)});var SlopcameraCapabilityModuleDeclarationSchema=z.strictObject({commands:z.array(SlopcameraCapabilityCommandSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.commands),description:z.string().min(1).max(1024),moduleId:z.string().min(1).max(128).regex(/^slopcamera\.capability\.[a-z][a-z0-9-]*$/u),operationKeys:z.array(z.string().min(3).max(256)).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.operations),profiles:z.array(SlopcameraCapabilityProfileSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.profiles),stability:z.enum(["stable","experimental"]),title:z.string().min(1).max(160),toolNames:z.array(z.string().min(1).max(128)).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.tools),trust:z.enum(["portable","local-effects","trusted-authoring","native-adapter","paid-provider"]),version:positiveSafeInteger,workflowKeys:z.array(z.string().min(3).max(256)).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.workflows)}).superRefine((module,context)=>{if(module.commands.length+module.operationKeys.length+module.profiles.length+module.toolNames.length+module.workflowKeys.length===0){context.addIssue({code:"custom",message:"A capability module must expose at least one command, operation, profile, tool, or workflow."})}for(const[name,values]of[["operationKeys",module.operationKeys],["toolNames",module.toolNames],["workflowKeys",module.workflowKeys]]){if(new Set(values).size!==values.length){context.addIssue({code:"custom",path:[name],message:`${name} must be unique within a capability module.`})}}});var SlopcameraCapabilityModuleSchema=z.strictObject({commands:z.array(SlopcameraCapabilityCommandSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.commands),description:z.string().min(1).max(1024),moduleId:z.string().min(1).max(128).regex(/^slopcamera\.capability\.[a-z][a-z0-9-]*$/u),operations:z.array(SlopcameraCapabilityOperationSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.operations),profiles:z.array(SlopcameraCapabilityProfileSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.profiles),stability:z.enum(["stable","experimental"]),title:z.string().min(1).max(160),tools:z.array(SlopcameraCapabilityToolSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.tools),trust:z.enum(["portable","local-effects","trusted-authoring","native-adapter","paid-provider"]),version:positiveSafeInteger,workflows:z.array(SlopcameraCapabilityWorkflowSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.workflows)}).superRefine((module,context)=>{const identities=[["commands",module.commands.map((value)=>value.name)],["operations",module.operations.map((value)=>value.key)],["profiles",module.profiles.map((value)=>value.id)],["tools",module.tools.map((value)=>value.name)],["workflows",module.workflows.map((value)=>value.key)]];if(identities.every(([,values])=>values.length===0)){context.addIssue({code:"custom",message:"A capability module must expose at least one command, operation, profile, tool, or workflow."})}for(const[name,values]of identities){if(new Set(values).size!==values.length)context.addIssue({code:"custom",path:[name],message:`${name} must be unique within a capability module.`})}});var manifestBodyBaseSchema=z.strictObject({host:z.enum(["portable","local"]),kind:z.literal("slopcamera.capability-manifest"),modules:z.array(SlopcameraCapabilityModuleSchema).min(1).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.modules),schemaVersion:z.literal(1),toolVersion:z.string().min(1).max(128)});function validateManifestOwnership(manifest,context){const ownership=new Map;for(const[moduleIndex,module]of manifest.modules.entries()){for(const identity of[module.moduleId,...module.commands.map((value)=>`command:${value.name}`),...module.operations.map((value)=>`operation:${value.key}`),...module.profiles.map((value)=>`profile:${value.id}`),...module.tools.map((value)=>`tool:${value.name}`),...module.workflows.map((value)=>`workflow:${value.key}`)]){const owner=ownership.get(identity);if(owner!==undefined)context.addIssue({code:"custom",path:["modules",moduleIndex],message:`${identity} is owned by both ${owner} and ${module.moduleId}.`});ownership.set(identity,module.moduleId)}if(manifest.host==="portable"&&module.trust!=="portable"){context.addIssue({code:"custom",path:["modules",moduleIndex,"trust"],message:"Portable manifests may contain only portable capability modules."})}}}var manifestBodySchema=manifestBodyBaseSchema.superRefine(validateManifestOwnership);var SlopcameraCapabilityManifestSchema=manifestBodyBaseSchema.extend({manifestSha256:digest}).superRefine(validateManifestOwnership);var compilerInputSchema=z.strictObject({host:z.enum(["portable","local"]),modules:z.array(SlopcameraCapabilityModuleDeclarationSchema).min(1).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.modules),operations:z.array(SlopcameraCapabilityOperationSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.operations),tools:z.array(SlopcameraCapabilityToolSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.tools),toolVersion:z.string().min(1).max(128),workflows:z.array(SlopcameraCapabilityWorkflowSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.workflows)});function compareBy(key){return(left,right)=>left[key].localeCompare(right[key])}function indexed(values,key,label){const result=new Map;for(const value of values){const identity=key(value);if(result.has(identity))throw new TypeError(`Duplicate ${label}: ${identity}.`);result.set(identity,value)}return result}function assertExactOwnership(label,available,owners){for(const key of available.keys()){if(!owners.has(key))throw new TypeError(`Unowned ${label}: ${key}.`)}for(const key of owners.keys()){if(!available.has(key))throw new TypeError(`Unknown ${label} declared by a capability module: ${key}.`)}}function defineSlopcameraCapabilityModule(input){return deepFreezeJson(SlopcameraCapabilityModuleDeclarationSchema.parse(createBoundedJsonValueSnapshot(input,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.bytes,"capability module").value))}function compileSlopcameraCapabilityManifest(input){const parsed=compilerInputSchema.parse(createBoundedJsonValueSnapshot(input,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.bytes,"capability manifest compiler input").value);const operations=indexed(parsed.operations,(value)=>value.key,"operation inventory key");const workflows=indexed(parsed.workflows,(value)=>value.key,"workflow inventory key");const tools=indexed(parsed.tools,(value)=>value.name,"tool inventory name");const operationOwners=new Map;const workflowOwners=new Map;const toolOwners=new Map;const moduleIds=new Set;const commandOwners=new Map;const profileOwners=new Map;const claim=(owners,key,moduleId,label)=>{const owner=owners.get(key);if(owner!==undefined)throw new TypeError(`${label} ${key} is owned by both ${owner} and ${moduleId}.`);owners.set(key,moduleId)};const modules=[...parsed.modules].sort(compareBy("moduleId")).map((module)=>{if(moduleIds.has(module.moduleId))throw new TypeError(`Duplicate capability module: ${module.moduleId}.`);moduleIds.add(module.moduleId);for(const command of module.commands)claim(commandOwners,command.name,module.moduleId,"Command");for(const profile of module.profiles)claim(profileOwners,profile.id,module.moduleId,"Profile");const resolvedOperations=module.operationKeys.map((key)=>{claim(operationOwners,key,module.moduleId,"Operation");const value=operations.get(key);if(value===undefined)throw new TypeError(`Unknown operation declared by ${module.moduleId}: ${key}.`);return value}).sort(compareBy("key"));const resolvedWorkflows=module.workflowKeys.map((key)=>{claim(workflowOwners,key,module.moduleId,"Workflow");const value=workflows.get(key);if(value===undefined)throw new TypeError(`Unknown workflow declared by ${module.moduleId}: ${key}.`);return value}).sort(compareBy("key"));const resolvedTools=module.toolNames.map((name)=>{claim(toolOwners,name,module.moduleId,"Tool");const value=tools.get(name);if(value===undefined)throw new TypeError(`Unknown tool declared by ${module.moduleId}: ${name}.`);return value}).sort(compareBy("name"));const{operationKeys:_operationKeys,toolNames:_toolNames,workflowKeys:_workflowKeys,...metadata}=module;return{...metadata,commands:[...module.commands].sort(compareBy("name")),operations:resolvedOperations,profiles:[...module.profiles].sort(compareBy("id")),tools:resolvedTools,workflows:resolvedWorkflows}});assertExactOwnership("operation",operations,operationOwners);assertExactOwnership("workflow",workflows,workflowOwners);assertExactOwnership("tool",tools,toolOwners);const body=manifestBodySchema.parse({host:parsed.host,kind:"slopcamera.capability-manifest",modules,schemaVersion:1,toolVersion:parsed.toolVersion});return deepFreezeJson(SlopcameraCapabilityManifestSchema.parse({...body,manifestSha256:canonicalJsonSha256(body)}))}function parseSlopcameraCapabilityManifest(input){const manifest=SlopcameraCapabilityManifestSchema.parse(createBoundedJsonValueSnapshot(input,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.bytes,"capability manifest").value);const{manifestSha256,...body}=manifest;const actual=canonicalJsonSha256(body);if(actual!==manifestSha256)throw new TypeError(`Capability manifest digest mismatch: expected ${manifestSha256}, received ${actual}.`);return deepFreezeJson(manifest)}var SLOPCAMERA_PORTABLE_CAPABILITY_VERSION="slopcamera-portable-v1";var portableModule=defineSlopcameraCapabilityModule({commands:[],description:"The fixed portable diagram and image operation projection for Bun SDK consumers.",moduleId:"slopcamera.capability.portable-diagram-image",operationKeys:PORTABLE_SLOPCAMERA_OPERATION_KINDS.map((kind)=>`${kind}@2`),profiles:[{id:"portable-bun-v1",kind:"authoring",qualification:{evidence:["src/code/public-operations.ts"],level:"unit",status:"qualified"},requirements:[{description:"Bun 1.3.14 or newer.",id:"bun",optional:false}]}],stability:"stable",title:"Portable diagrams and images",toolNames:[],trust:"portable",version:1,workflowKeys:[]});function operationProjection(kind){const operation=PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS[kind];return{cache:operation.policy.cache,cancellable:operation.policy.cancellable,effect:operation.policy.effect,inputSchemaId:operation.inputSchemaId,key:`${operation.kind}@${operation.version}`,kind:operation.kind,lifecycle:operation.lifecycle,maxDurationMs:operation.policy.maxDurationMs,maxFanOut:operation.policy.maxFanOut,maxInputBytes:operation.policy.maxInputBytes,maxOutputBytes:operation.policy.maxOutputBytes,outputSchemaId:operation.outputSchemaId,preparation:[...operation.policy.preparation],resources:operation.policy.resources.map((claim)=>({...claim})),resume:operation.policy.resume,version:operation.version}}function createPortableSlopcameraCapabilityManifest(){return compileSlopcameraCapabilityManifest({host:"portable",modules:[portableModule],operations:PORTABLE_SLOPCAMERA_OPERATION_KINDS.map(operationProjection),tools:[],toolVersion:SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,workflows:[]})}var slopcameraPortableCapabilityManifest=createPortableSlopcameraCapabilityManifest();import{z as z2}from"zod";var slopcameraSceneGalleryLimits=Object.freeze({variants:16,variantsBytes:1024*1024,idEdge:64,labelEdge:256,summaryEdge:256});var variantId=z2.string().min(1).max(slopcameraSceneGalleryLimits.idEdge).regex(/^[a-z0-9][a-z0-9-]*$/u,"Variant ids are lowercase slugs like dusk or golden-hour-2.");var SceneVariantPatchSchema=z2.strictObject({kind:z2.literal("slopcamera.spatial-scene-patch"),schemaVersion:z2.literal(1),expectedSceneSha256:SpatialDigestSchema.optional(),operations:z2.array(SpatialPatchOperationSchema).min(1).max(SPATIAL_SCENE_LIMITS.patchOperations)});var SceneGalleryVariantSchema=z2.strictObject({id:variantId,label:z2.string().min(1).max(slopcameraSceneGalleryLimits.labelEdge).optional(),patch:SceneVariantPatchSchema});var SlopcameraSceneVariantsSchema=z2.strictObject({kind:z2.literal("slopcamera.scene-variants"),schemaVersion:z2.literal(1),variants:z2.array(SceneGalleryVariantSchema).min(1).max(slopcameraSceneGalleryLimits.variants)});function summarizeSceneVariantPatch(operations){const parts=operations.map((operation)=>{const target=("entityId"in operation?operation.entityId:undefined)??("camera"in operation?operation.camera.cameraId:undefined)??("channel"in operation?operation.channel.channelId:undefined)??("asset"in operation?operation.asset.assetId:undefined)??("generator"in operation?operation.generator.generatorId:undefined)??("entity"in operation?operation.entity.entityId:undefined)??("override"in operation?operation.override.entityId:undefined);return target===undefined?operation.kind:`${operation.kind} ${target}`});const summary=parts.join("; ");return summary.length<=slopcameraSceneGalleryLimits.summaryEdge?summary:`${summary.slice(0,slopcameraSceneGalleryLimits.summaryEdge-1)}\u2026`}function parseSlopcameraSceneVariants(input){const value=createBoundedJsonValueSnapshot(input,slopcameraSceneGalleryLimits.variantsBytes,"scene variants").value;const document=parseSpatialValue(SlopcameraSceneVariantsSchema,value,"scene variants");const ids=new Set;for(const variant of document.variants){if(ids.has(variant.id)){throw new SpatialSceneError("invalid-data",`Duplicate scene variant id ${variant.id}.`,"variants")}ids.add(variant.id)}return document.variants}function planSlopcameraSceneGallery(input){const scene=parseSpatialScene(input.scene);const baseSceneSha256=spatialSceneSha256(scene);const specs=parseSlopcameraSceneVariants(input.variants);const variants=specs.map((spec,offset)=>{const expected=spec.patch.expectedSceneSha256??baseSceneSha256;if(expected!==baseSceneSha256){throw new SpatialSceneError("conflict",`Variant ${spec.id} was authored against a different scene revision.`,"variants")}const patch={...spec.patch,expectedSceneSha256:expected};const result=applySpatialScenePatch(scene,patch);return{diff:result.diff,id:spec.id,index:offset+1,label:spec.label??spec.id,patchSha256:spatialValueSha256(patch),prompt:summarizeSceneVariantPatch(patch.operations),scene:result.scene,sceneSha256:result.sceneSha256}});return{baseSceneSha256,scene,variants}}import{z as z4}from"zod";import{z as z3}from"zod";var STUDIO_LIMITS=Object.freeze({documentBytes:32*1024*1024,documentDepth:32,documentValues:500000,sourceFiles:512,sourceBytes:4*1024**3,outputSpecifications:32,outputFiles:25000,outputBytes:64*1024**3,timeoutSeconds:6*60*60,frames:25000,frameIndexExclusive:1e6,dimension:8192,pixels:33554432,parameterBytes:256*1024,parameterDepth:16,parameterValues:20000});function studioDocument(schema,name){return z3.preprocess((value)=>value===undefined?undefined:createBoundedJsonValueSnapshot(value,STUDIO_LIMITS.documentBytes,name,{maximumDepth:STUDIO_LIMITS.documentDepth,maximumValues:STUDIO_LIMITS.documentValues}).value,schema)}function parseStudioValue(schema,input){try{return deepFreezeJson(schema.parse(input))}catch(error){if(error instanceof SlopcameraCodeError)throw error;throw new SlopcameraCodeError("invalid-data",error instanceof z3.ZodError?error.issues[0]?.message??"Invalid studio document.":"Invalid studio document.")}}function studioHash(domain,value){return boundedCanonicalJsonSha256({domain,value},{maximumBytes:STUDIO_LIMITS.documentBytes,maximumDepth:STUDIO_LIMITS.documentDepth+2,maximumValues:STUDIO_LIMITS.documentValues+4})}var studioCompare=compareUtf16Strings;function studioRequire(condition,message){if(!condition)throw new SlopcameraCodeError("invalid-data",message)}function pathKey(path){return path.toLowerCase()}function assertDistinctPaths(paths){const names=new Set(paths.map(pathKey));studioRequire(names.size===paths.length,"Studio paths collide.");for(const name of names){for(let offset=name.indexOf("/");offset!==-1;offset=name.indexOf("/",offset+1)){studioRequire(!names.has(name.slice(0,offset)),"Studio paths use a file as an ancestor.")}}}var StudioDigestSchema=z4.string().regex(/^[a-f0-9]{64}$/u);var StudioEngineSchema=z4.enum(["blender","manim","cadquery"]);var StudioPathSchema=z4.string().min(1).max(1024).refine((path)=>path.normalize("NFC")===path&&!path.startsWith("/")&&!/[\\:\u0000-\u001f\u007f]/u.test(path)&&path.split("/").every((part)=>part!==""&&part!=="."&&part!==".."&&!/[. ]$/u.test(part)),"Studio paths must be normalized, contained POSIX-relative names.");var identifier2=z4.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u);var stableId=z4.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/u);var sourceFile=z4.strictObject({path:StudioPathSchema,sha256:StudioDigestSchema,bytes:z4.number().int().safe().nonnegative().max(STUDIO_LIMITS.sourceBytes)});var sourceBundleShape=z4.strictObject({kind:z4.literal("slopcamera.studio-source-bundle"),schemaVersion:z4.literal(1),engine:StudioEngineSchema,entrypoint:z4.discriminatedUnion("kind",[z4.strictObject({kind:z4.literal("python"),path:StudioPathSchema.refine((path)=>path.endsWith(".py"))}),z4.strictObject({kind:z4.literal("blend"),path:StudioPathSchema.refine((path)=>path.endsWith(".blend"))})]),files:z4.array(sourceFile).min(1).max(STUDIO_LIMITS.sourceFiles)}).superRefine((value,context)=>{const issue=(message)=>context.addIssue({code:"custom",message});try{assertDistinctPaths(value.files.map((file)=>file.path))}catch{issue("Source paths must be distinct and cannot have file ancestors.")}if(value.files.reduce((total,file)=>total+file.bytes,0)>STUDIO_LIMITS.sourceBytes)issue("Source bundle exceeds four GiB.");if(!value.files.some((file)=>file.path===value.entrypoint.path&&file.bytes>0))issue("Entrypoint must name a declared nonempty source file.");if(value.entrypoint.kind==="blend"&&value.engine!=="blender")issue("Blend entrypoints require Blender.")});var sourceBundle=sourceBundleShape.transform((value)=>({...value,files:[...value.files].sort((a,b)=>studioCompare(a.path,b.path))})).pipe(sourceBundleShape);var StudioSourceBundleSchema=studioDocument(sourceBundle,"studio source bundle");var parseStudioSourceBundle=(input)=>parseStudioValue(StudioSourceBundleSchema,input);var StudioSourceSpaceSchema=z4.strictObject({units:z4.enum(["meters","millimeters","centimeters"]),upAxis:z4.enum(["x","y","z"]),handedness:z4.enum(["right","left"])});var StudioOutputRoleSchema=z4.enum(["native-source","model","beauty","auxiliary","simulation-cache","audio"]);var StudioOutputFormatSchema=z4.enum(["py","blend","usd","usda","usdc","glb","step","png","exr","mp4","mov","webm","wav","flac","mp3","cache"]);var raster=z4.strictObject({kind:z4.literal("raster"),colorSpace:z4.enum(["srgb","linear-rec709","data","unspecified"]),alpha:z4.enum(["opaque","straight","premultiplied","none"]),dataType:z4.enum(["uint8","uint16","float16","float32"]),channels:z4.array(z4.string().regex(/^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u)).min(1).max(32),semantic:z4.enum(["color","depth","normal","object-id","mask","custom"]),unit:z4.enum(["unitless","meters","millimeters","centimeters"])}).superRefine((value,context)=>{if(new Set(value.channels).size!==value.channels.length)context.addIssue({code:"custom",message:"Raster channels must be unique."});if(value.semantic!=="color"&&value.colorSpace!=="data")context.addIssue({code:"custom",message:"Non-color raster passes must declare data color space."});if(value.semantic==="color"&&value.colorSpace==="data")context.addIssue({code:"custom",message:"Color output cannot declare data color space."});if(value.semantic!=="depth"&&value.unit!=="unitless")context.addIssue({code:"custom",message:"Only depth passes declare distance units."})});var StudioOutputInterpretationSchema=z4.discriminatedUnion("kind",[raster,z4.strictObject({kind:z4.literal("model"),sourceSpace:StudioSourceSpaceSchema}),z4.strictObject({kind:z4.literal("native-source")}),z4.strictObject({kind:z4.literal("cache"),semantics:z4.literal("opaque-native")}),z4.strictObject({kind:z4.literal("audio"),sampleRate:z4.number().int().min(8000).max(384000),channels:z4.number().int().min(1).max(32)})]);var outputCommon={id:stableId,role:StudioOutputRoleSchema,format:StudioOutputFormatSchema,interpretation:StudioOutputInterpretationSchema};var pattern=z4.string().max(1024).refine((value)=>value.split("%06d").length===2&&!value.replace("%06d","").includes("%")&&StudioPathSchema.safeParse(value.replace("%06d","000000")).success,"Sequence paths require exactly one %06d placeholder in a safe relative path.");function compatibleOutput(value){if(value.role==="native-source")return["py","blend"].includes(value.format)&&value.interpretation.kind==="native-source";if(value.role==="model")return["blend","usd","usda","usdc","glb","step"].includes(value.format)&&value.interpretation.kind==="model";if(value.role==="simulation-cache")return value.format==="cache"&&value.interpretation.kind==="cache";if(value.role==="audio")return["wav","flac","mp3"].includes(value.format)&&value.interpretation.kind==="audio";return["png","exr","mp4","mov","webm"].includes(value.format)&&value.interpretation.kind==="raster"&&(value.role!=="beauty"||value.interpretation.semantic==="color")}var StudioOutputSpecSchema=z4.discriminatedUnion("kind",[z4.strictObject({...outputCommon,kind:z4.literal("file"),path:StudioPathSchema}),z4.strictObject({...outputCommon,kind:z4.literal("sequence"),pathPattern:pattern}),z4.strictObject({...outputCommon,kind:z4.literal("directory"),path:StudioPathSchema})]).superRefine((value,context)=>{const issue=(message)=>context.addIssue({code:"custom",message});if(!compatibleOutput(value))issue("Output role, format, and interpretation disagree.");if(value.kind==="directory"!==(value.format==="cache"))issue("Only native cache outputs use directory declarations.");if(value.kind==="sequence"&&!["png","exr"].includes(value.format))issue("Numbered sequences support PNG or EXR only.");if(value.kind!=="directory"){const path=value.kind==="sequence"?value.pathPattern:value.path;if(!path.endsWith(`.${value.format}`)&&!(value.format==="step"&&path.endsWith(".stp")))issue("Output extension must agree with its declared format.")}if(value.interpretation.kind==="raster"){if(value.format==="png"&&!["uint8","uint16"].includes(value.interpretation.dataType))issue("PNG declares integer sample data.");if(value.format==="exr"&&!["float16","float32"].includes(value.interpretation.dataType))issue("The admitted EXR profile declares floating point sample data.")}});var frameRateShape=z4.strictObject({numerator:z4.number().int().min(1).max(1e6),denominator:z4.number().int().min(1).max(1e6)}).refine((value)=>value.numerator/value.denominator<=240,"Studio cadence exceeds 240 fps.");var frameRate=frameRateShape.transform((value)=>{let{numerator:a,denominator:b}=value;while(b!==0){const remainder=a%b;a=b;b=remainder}return{numerator:value.numerator/a,denominator:value.denominator/a}}).pipe(frameRateShape);var StudioRenderSchema=z4.strictObject({width:z4.number().int().min(1).max(STUDIO_LIMITS.dimension),height:z4.number().int().min(1).max(STUDIO_LIMITS.dimension),frameRate,startFrame:z4.number().int().nonnegative().max(STUDIO_LIMITS.frameIndexExclusive-1),endFrameExclusive:z4.number().int().positive().max(STUDIO_LIMITS.frameIndexExclusive)}).refine((value)=>value.endFrameExclusive>value.startFrame&&value.endFrameExclusive-value.startFrame<=STUDIO_LIMITS.frames&&value.width*value.height<=STUDIO_LIMITS.pixels,"Render requires a bounded, nonempty half-open frame interval and pixel area.");var StudioEngineOptionsSchema=z4.discriminatedUnion("engine",[z4.strictObject({engine:z4.literal("blender"),renderer:z4.enum(["cycles","eevee"]),device:z4.enum(["cpu","gpu"]),samples:z4.number().int().min(1).max(4096),transparent:z4.boolean(),viewTransform:z4.enum(["AgX","Standard"]),denoise:z4.boolean(),seed:z4.number().int().min(0).max(4294967295)}),z4.strictObject({engine:z4.literal("manim"),scene:identifier2,renderer:z4.literal("cairo"),transparent:z4.boolean()}),z4.strictObject({engine:z4.literal("cadquery"),exportVariable:identifier2,tolerance:z4.number().finite().positive().max(1),angularTolerance:z4.number().finite().positive().max(Math.PI)})]);var parameters=z4.preprocess((value)=>value===undefined?undefined:createBoundedJsonValueSnapshot(value,STUDIO_LIMITS.parameterBytes,"studio parameters",{maximumDepth:STUDIO_LIMITS.parameterDepth,maximumValues:STUDIO_LIMITS.parameterValues}).value,z4.record(z4.string(),z4.unknown()));var StudioExecutionProfileSchema=z4.strictObject({trust:z4.literal("trusted-current-user"),isolation:z4.literal("none"),hermetic:z4.literal(false)});var jobShape=z4.strictObject({kind:z4.literal("slopcamera.studio-job"),schemaVersion:z4.literal(1),jobId:z4.string().regex(/^studio_[a-zA-Z0-9][a-zA-Z0-9_-]{0,120}$/u),bundleSha256:StudioDigestSchema,stage:z4.enum(["build","bake","render"]),parameters,engine:StudioEngineOptionsSchema,render:StudioRenderSchema.optional(),outputs:z4.array(StudioOutputSpecSchema).min(1).max(STUDIO_LIMITS.outputSpecifications),limits:z4.strictObject({timeoutSeconds:z4.number().int().min(1).max(STUDIO_LIMITS.timeoutSeconds),maximumOutputBytes:z4.number().int().safe().min(1).max(STUDIO_LIMITS.outputBytes),maximumOutputFiles:z4.number().int().min(1).max(STUDIO_LIMITS.outputFiles)}),execution:StudioExecutionProfileSchema}).superRefine((value,context)=>{if(value.render===undefined&&(value.stage==="render"&&value.engine.engine!=="cadquery"||value.outputs.some((output)=>output.kind==="sequence"||output.interpretation.kind==="raster")))context.addIssue({code:"custom",message:"Raster outputs, render stage, and numbered sequences require explicit dimensions and a frame interval."});if(new Set(value.outputs.map((output)=>output.id)).size!==value.outputs.length)context.addIssue({code:"custom",message:"Output IDs must be unique."});const count=value.outputs.reduce((sum,output)=>sum+(output.kind==="sequence"&&value.render!==undefined?value.render.endFrameExclusive-value.render.startFrame:1),0);if(count>value.limits.maximumOutputFiles)context.addIssue({code:"custom",message:"Declared outputs exceed the job's output-file limit."})});var job=jobShape.transform((value)=>({...value,outputs:[...value.outputs].sort((a,b)=>studioCompare(a.id,b.id))})).pipe(jobShape);var StudioJobSchema=studioDocument(job,"studio job");var parseStudioJob=(input)=>parseStudioValue(StudioJobSchema,input);var StudioCapabilityNameSchema=z4.enum(["python-authoring","blend-authoring","build","bake","render","gpu-render","image-sequence","beauty-video","model-export","auxiliary-passes","native-cache","audio-output"]);var capability=z4.strictObject({name:StudioCapabilityNameSchema,support:z4.enum(["available","unavailable","unverified"]),evidence:z4.enum(["probe","qualification"]),receiptSha256:StudioDigestSchema.optional()}).refine((value)=>value.evidence!=="qualification"||value.receiptSha256!==undefined,"Qualification evidence requires a retained receipt digest.");var runtimeShape=z4.strictObject({kind:z4.literal("slopcamera.studio-runtime"),schemaVersion:z4.literal(1),engine:StudioEngineSchema,tool:z4.strictObject({name:z4.string().min(1).max(128),version:z4.string().min(1).max(512),executableSha256:StudioDigestSchema}),driverSha256:StudioDigestSchema,environment:z4.strictObject({fingerprintSha256:StudioDigestSchema,evidence:z4.literal("observed-package-environment"),hermetic:z4.literal(false)}),capabilities:z4.array(capability).max(12)}).refine((value)=>new Set(value.capabilities.map((item)=>item.name)).size===value.capabilities.length,"Runtime capability names must be unique.");var runtime=runtimeShape.transform((value)=>({...value,capabilities:[...value.capabilities].sort((a,b)=>studioCompare(a.name,b.name))})).pipe(runtimeShape);var StudioRuntimeIdentitySchema=studioDocument(runtime,"studio runtime identity");var parseStudioRuntimeIdentity=(input)=>parseStudioValue(StudioRuntimeIdentitySchema,input);var StudioOutputArtifactSchema=z4.strictObject({outputId:stableId,path:StudioPathSchema,sha256:StudioDigestSchema,bytes:z4.number().int().safe().positive().max(STUDIO_LIMITS.outputBytes),role:StudioOutputRoleSchema,format:StudioOutputFormatSchema,frame:z4.number().int().nonnegative().max(STUDIO_LIMITS.frameIndexExclusive-1).optional()});import{z as z5}from"zod";var studioSourceBundleSha256=(input)=>studioHash("slopcamera.studio-source-bundle/v1",parseStudioSourceBundle(input));var studioJobSha256=(input)=>studioHash("slopcamera.studio-job/v1",parseStudioJob(input));var studioRuntimeSha256=(input)=>studioHash("slopcamera.studio-runtime/v1",parseStudioRuntimeIdentity(input));function studioOutputPath(output,frame){if(output.kind==="sequence"){studioRequire(Number.isInteger(frame)&&frame>=0&&frame<STUDIO_LIMITS.frameIndexExclusive,"Sequence output requires an admitted frame index.");return output.pathPattern.replace("%06d",String(frame).padStart(6,"0"))}studioRequire(frame===undefined,"Only sequence outputs have a frame index.");return output.path}function declaration(job2){const files=[];const directories=[];for(const output of job2.outputs){if(output.kind==="directory")directories.push({id:output.id,path:output.path});else if(output.kind==="file")files.push({id:output.id,path:output.path});else{studioRequire(job2.render!==undefined,"Sequence requires a render interval.");for(let frame=job2.render.startFrame;frame<job2.render.endFrameExclusive;frame++)files.push({id:output.id,path:studioOutputPath(output,frame),frame})}}studioRequire(files.length+directories.length<=job2.limits.maximumOutputFiles,"Declared outputs exceed the output-file budget.");assertDistinctPaths([...files,...directories].map((item)=>item.path));return{files,directories}}function requiredCapabilities(bundle,job2){const values=new Set([bundle.entrypoint.kind==="blend"?"blend-authoring":"python-authoring",job2.stage]);if(job2.engine.engine==="blender"&&job2.engine.device==="gpu"&&job2.stage==="render")values.add("gpu-render");for(const output of job2.outputs){if(output.kind==="sequence")values.add("image-sequence");if(output.role==="beauty"&&["mov","mp4","webm"].includes(output.format))values.add("beauty-video");if(output.role==="model")values.add("model-export");if(output.role==="auxiliary")values.add("auxiliary-passes");if(output.role==="simulation-cache")values.add("native-cache");if(output.role==="audio")values.add("audio-output")}return[...values].sort(studioCompare)}function derivePlan(input){const{bundle,job:job2,runtime:runtime2}=input;const bundleSha256=studioSourceBundleSha256(bundle),jobSha256=studioJobSha256(job2);studioRequire(bundleSha256===job2.bundleSha256,"Job does not bind the exact source bundle.");studioRequire(bundle.engine===job2.engine.engine&&(runtime2===undefined||runtime2.engine===bundle.engine),"Bundle, job, and runtime must use one engine.");studioRequire(bundle.entrypoint.kind!=="blend"||job2.stage==="render","Blend entrypoints support render stage only; explicit Python authoring owns builds and bakes.");const declared=declaration(job2),required=requiredCapabilities(bundle,job2);const capabilityChecks=required.map((name)=>({name,support:runtime2===undefined?"unbound":runtime2.capabilities.find((item)=>item.name===name)?.support??"unverified"}));const readiness=runtime2===undefined?"runtime-unbound":capabilityChecks.some((item)=>item.support==="unavailable")?"capability-unavailable":capabilityChecks.some((item)=>item.support==="unverified")?"capability-unverified":"authorization-required";const body={kind:"slopcamera.studio-plan",schemaVersion:1,bundle,job:job2,...runtime2===undefined?{}:{runtime:runtime2,runtimeSha256:studioRuntimeSha256(runtime2)},bundleSha256,jobSha256,sourceBytes:bundle.files.reduce((total,file)=>total+file.bytes,0),frameCount:job2.render===undefined?0:job2.render.endFrameExclusive-job2.render.startFrame,outputCount:{minimum:declared.files.length+declared.directories.length,maximum:declared.directories.length===0?declared.files.length:job2.limits.maximumOutputFiles},requiredCapabilities:required,capabilityChecks,readiness};return{...body,planSha256:studioHash("slopcamera.studio-plan/v1",body)}}var plan=z5.strictObject({kind:z5.literal("slopcamera.studio-plan"),schemaVersion:z5.literal(1),bundle:StudioSourceBundleSchema,job:StudioJobSchema,runtime:StudioRuntimeIdentitySchema.optional(),runtimeSha256:StudioDigestSchema.optional(),bundleSha256:StudioDigestSchema,jobSha256:StudioDigestSchema,planSha256:StudioDigestSchema,sourceBytes:z5.number().int().safe().nonnegative().max(STUDIO_LIMITS.sourceBytes),frameCount:z5.number().int().nonnegative().max(STUDIO_LIMITS.frames),outputCount:z5.strictObject({minimum:z5.number().int().positive().max(STUDIO_LIMITS.outputFiles),maximum:z5.number().int().positive().max(STUDIO_LIMITS.outputFiles)}),requiredCapabilities:z5.array(StudioCapabilityNameSchema).min(1).max(12),capabilityChecks:z5.array(z5.strictObject({name:StudioCapabilityNameSchema,support:z5.enum(["available","unavailable","unverified","unbound"])})).min(1).max(12),readiness:z5.enum(["runtime-unbound","capability-unavailable","capability-unverified","authorization-required"])}).superRefine((value,context)=>{try{const derived=derivePlan({bundle:value.bundle,job:value.job,...value.runtime===undefined?{}:{runtime:value.runtime}});if(canonicalJson(derived)!==canonicalJson(value))context.addIssue({code:"custom",message:"Studio plan differs from its canonical source, job, runtime, or admission derivation."})}catch(error){context.addIssue({code:"custom",message:error instanceof Error?error.message:"Invalid studio plan."})}});var StudioPlanSchema=studioDocument(plan,"studio plan");var parseStudioPlan=(input)=>parseStudioValue(StudioPlanSchema,input);function planStudioJob(input){const captured=parseStudioValue(studioDocument(z5.strictObject({bundle:StudioSourceBundleSchema,job:StudioJobSchema,runtime:StudioRuntimeIdentitySchema.optional()}),"studio planning input"),input);return parseStudioPlan(derivePlan(captured))}var failure2=z5.strictObject({code:z5.enum(["subprocess","cancelled","deadline","validation","custody","publication","unavailable"]),message:z5.string().min(1).max(2048)});var receiptCommon={kind:z5.literal("slopcamera.studio-receipt"),schemaVersion:z5.literal(1),jobId:z5.string().regex(/^studio_[a-zA-Z0-9][a-zA-Z0-9_-]{0,120}$/u),attemptId:z5.string().regex(/^attempt_[a-zA-Z0-9][a-zA-Z0-9_-]{0,120}$/u),planSha256:StudioDigestSchema,bundleSha256:StudioDigestSchema,jobSha256:StudioDigestSchema,runtime:StudioRuntimeIdentitySchema,runtimeSha256:StudioDigestSchema,startedAt:z5.iso.datetime({offset:true}),finishedAt:z5.iso.datetime({offset:true}),outputs:z5.array(StudioOutputArtifactSchema).max(STUDIO_LIMITS.outputFiles)};var receiptShape=z5.discriminatedUnion("state",[z5.strictObject({...receiptCommon,state:z5.literal("succeeded"),custody:z5.literal("closed"),exitCode:z5.literal(0)}),z5.strictObject({...receiptCommon,state:z5.literal("failed"),custody:z5.literal("closed"),exitCode:z5.number().int().min(-255).max(255).nullable(),failure:failure2}),z5.strictObject({...receiptCommon,state:z5.literal("unknown-custody"),custody:z5.literal("unknown"),exitCode:z5.number().int().min(-255).max(255).nullable(),failure:failure2})]).superRefine((value,context)=>{if(studioRuntimeSha256(value.runtime)!==value.runtimeSha256)context.addIssue({code:"custom",message:"Receipt runtime digest differs from its evidence."});if(Date.parse(value.finishedAt)<Date.parse(value.startedAt))context.addIssue({code:"custom",message:"Receipt finishes before it starts."});try{assertDistinctPaths(value.outputs.map((item)=>item.path))}catch{context.addIssue({code:"custom",message:"Receipt output paths collide."})}if(value.outputs.reduce((sum,item)=>sum+item.bytes,0)>STUDIO_LIMITS.outputBytes)context.addIssue({code:"custom",message:"Receipt exceeds the global output byte bound."})});var receipt=receiptShape.transform((value)=>({...value,outputs:[...value.outputs].sort((a,b)=>studioCompare(a.path,b.path))})).pipe(receiptShape);var StudioReceiptSchema=studioDocument(receipt,"studio receipt");var parseStudioReceipt=(input)=>parseStudioValue(StudioReceiptSchema,input);function validateStudioReceipt(input){const captured=parseStudioValue(studioDocument(z5.strictObject({plan:StudioPlanSchema,receipt:StudioReceiptSchema}),"studio receipt validation input"),input);const{plan:plan2,receipt:receipt2}=captured;studioRequire(receipt2.jobId===plan2.job.jobId&&receipt2.planSha256===plan2.planSha256&&receipt2.jobSha256===plan2.jobSha256&&receipt2.bundleSha256===plan2.bundleSha256,"Receipt identity differs from the exact planned job.");studioRequire(plan2.runtime!==undefined&&receipt2.runtimeSha256===plan2.runtimeSha256&&canonicalJson(receipt2.runtime)===canonicalJson(plan2.runtime),"Execution receipt requires the exact planned runtime binding.");if(receipt2.state==="succeeded")studioRequire(plan2.readiness==="authorization-required","A successful receipt requires available observed runtime capabilities; consent remains host-owned.");studioRequire(receipt2.outputs.length<=plan2.job.limits.maximumOutputFiles&&receipt2.outputs.reduce((total,output)=>total+output.bytes,0)<=plan2.job.limits.maximumOutputBytes,"Receipt exceeds the planned output budget.");const declared=declaration(plan2.job),expected=new Map(declared.files.map((file)=>[file.path,file]));const counts=new Map;for(const artifact of receipt2.outputs){const specification=plan2.job.outputs.find((output)=>output.id===artifact.outputId);studioRequire(specification!==undefined,"Receipt contains an undeclared output ID.");studioRequire(artifact.role===specification.role&&artifact.format===specification.format,"Receipt output role or format differs from its declaration.");if(specification.kind==="directory"){studioRequire(artifact.path.startsWith(`${specification.path}/`)&&artifact.frame===undefined,"Cache artifact must be a file inside its declared directory.")}else{const wanted=expected.get(artifact.path);studioRequire(wanted!==undefined&&wanted.id===artifact.outputId&&artifact.frame===wanted.frame,"Receipt contains an undeclared file or incorrect frame index.");expected.delete(artifact.path)}counts.set(artifact.outputId,(counts.get(artifact.outputId)??0)+1)}if(receipt2.state==="succeeded"){studioRequire(expected.size===0&&declared.directories.every((directory)=>(counts.get(directory.id)??0)>0),"Successful receipt must cover every declared file, frame, and cache directory.")}return deepFreezeJson(receipt2)}function inspectStudioBundle(input){const bundle=parseStudioSourceBundle(input);return deepFreezeJson({engine:bundle.engine,entrypoint:bundle.entrypoint,bundleSha256:studioSourceBundleSha256(bundle),files:bundle.files.length,sourceBytes:bundle.files.reduce((total,file)=>total+file.bytes,0),executed:false,dependencyDiscovery:"explicit-files-only"})}function inspectStudioPlan(input){const plan2=parseStudioPlan(input);return deepFreezeJson({jobId:plan2.job.jobId,stage:plan2.job.stage,engine:plan2.job.engine.engine,planSha256:plan2.planSha256,sourceBytes:plan2.sourceBytes,frameCount:plan2.frameCount,outputCount:plan2.outputCount,readiness:plan2.readiness,capabilityChecks:plan2.capabilityChecks,execution:plan2.job.execution,limits:plan2.job.limits,executed:false,outputs:plan2.job.outputs.map((output)=>({id:output.id,role:output.role,format:output.format,kind:output.kind,path:output.kind==="sequence"?output.pathPattern:output.path}))})}import{z as z6}from"zod";var VISUAL_STYLE_IDS=Object.freeze(["silent-actuality","noir-35mm","documentary-16mm","super8-color","theatrical-cel","watercolor-storybook","ink-sketch","rubber-hose","midcentury-limited","cut-paper","stopmotion-clay","pixel-art","rotoscope","engraving","math-explainer","isometric-design","clean-motion"]);var VisualStyleIdSchema=z6.enum(VISUAL_STYLE_IDS);var DirectionTextSchema=z6.string().trim().min(1).max(512);var DirectionListSchema=z6.array(DirectionTextSchema).min(1).max(12).readonly();var ColorSchema=z6.string().regex(/^#[0-9a-f]{6}$/u);var UnitSchema=z6.number().finite().min(0).max(1);var VisualStyleProfileSchema=z6.strictObject({id:VisualStyleIdSchema,name:z6.string().trim().min(1).max(80),family:z6.enum(["historical-film","drawn-animation","tactile-animation","digital-animation","explanation"]),summary:DirectionTextSchema,palette:z6.strictObject({background:ColorSchema,ink:ColorSchema,paper:ColorSchema,primary:ColorSchema,secondary:ColorSchema,accent:ColorSchema}).readonly(),cadence:z6.strictObject({fps:z6.number().int().min(1).max(120),exposureFrames:z6.number().int().min(1).max(12),cameraExposureFrames:z6.number().int().min(1).max(12),shutterAngle:z6.number().finite().min(0).max(360)}).readonly(),delivery:z6.strictObject({width:z6.number().int().min(1080).max(8192).multipleOf(2),height:z6.number().int().min(1080).max(8192).multipleOf(2),sampling:z6.enum(["smooth","nearest"]),logicalGrid:z6.strictObject({width:z6.number().int().min(16).max(1024),height:z6.number().int().min(16).max(1024)}).readonly().optional()}).superRefine((delivery,context)=>{const grid=delivery.logicalGrid;if(delivery.sampling==="nearest"){if(grid===undefined||delivery.width%grid.width!==0||delivery.height%grid.height!==0||delivery.width/grid.width!==delivery.height/grid.height){context.addIssue({code:"custom",message:"Nearest sampling requires a logical grid with one exact integer scale on both axes."})}}else if(grid!==undefined){context.addIssue({code:"custom",message:"A logical pixel grid requires nearest sampling."})}}).readonly(),shape:DirectionListSchema,material:DirectionListSchema,camera:DirectionListSchema,finishing:z6.strictObject({grain:UnitSchema,grainSizePxAt1080:z6.number().finite().min(0).max(8),halation:UnitSchema,gateWeavePxAt1080:z6.number().finite().min(0).max(4),vignette:UnitSchema,saturation:z6.number().finite().min(0).max(2),contrast:z6.number().finite().min(0.5).max(2)}).readonly(),avoid:DirectionListSchema,acceptance:DirectionListSchema}).readonly();var cleanFinish=Object.freeze({grain:0,grainSizePxAt1080:0,halation:0,gateWeavePxAt1080:0,vignette:0,saturation:1,contrast:1});var cinema=Object.freeze({fps:24,exposureFrames:1,cameraExposureFrames:1,shutterAngle:180});var onTwos=Object.freeze({fps:24,exposureFrames:2,cameraExposureFrames:1,shutterAngle:0});var uhd=Object.freeze({width:3840,height:2160,sampling:"smooth"});var academy=Object.freeze({width:2880,height:2160,sampling:"smooth"});function profile(value){return VisualStyleProfileSchema.parse(value)}var VISUAL_STYLE_PROFILES=Object.freeze([profile({id:"silent-actuality",name:"Silent actuality",family:"historical-film",summary:"An observed street or everyday action reconstructed with early monochrome photography, a restrained camera, and a coherent period world.",palette:{background:"#171714",ink:"#080909",paper:"#e2dfce",primary:"#a7a797",secondary:"#666b63",accent:"#c6c3ad"},cadence:{fps:24,exposureFrames:1,cameraExposureFrames:1,shutterAngle:180},delivery:academy,shape:["Stage a deep street with foreground, middle distance, and a readable vanishing point.","Choose one place and decade; match silhouettes of buildings, signs, transport, and clothing to that brief."],material:["Use rough masonry, imperfect glazing, worn painted timber, and matte clothing; build surface detail before aging the image.","Separate sky, pale facades, dark coats, and street into readable monochrome values."],camera:["Use an eye-level locked tripod or a very slow mechanically plausible pan; let the world supply movement.","Keep the 4:3 gate visible through composition; introduce only low-amplitude frame-wise registration variation."],finishing:{grain:0.16,grainSizePxAt1080:1.4,halation:0.04,gateWeavePxAt1080:0.65,vignette:0.18,saturation:0,contrast:1.08},avoid:["Do not equate historical film with sepia, a black border, or repeated scratch overlays.","Avoid glossy CGI, contemporary props, drone paths, fake archive dates, and unqualified documentary claims."],acceptance:["With texture disabled, the world and framing still read as the selected period.","Moving subjects remain legible; grain changes per exposed frame, while scratches and weave do not overpower the action.","Review full-resolution surfaces and motion; label the result as an authored reconstruction, not found footage."]}),profile({id:"noir-35mm",name:"35 mm noir",family:"historical-film",summary:"A dramatic monochrome city or interior organized by motivated pools of light, negative space, and strong depth.",palette:{background:"#101316",ink:"#040608",paper:"#f1efe5",primary:"#aeb5b7",secondary:"#475159",accent:"#d6d4c9"},cadence:cinema,delivery:academy,shape:["Use one strong silhouette, diagonal blocking, and a deep foreground-to-background relationship.","Let window mullions, stairs, awnings, and practical lights produce motivated graphic shapes."],material:["Give masonry, wet pavement, wool, skin, and glass distinct tonal responses.","Protect luminous highlights while holding enough shadow detail to read the principal action."],camera:["Use a deliberate dolly, fixed composition, or motivated low angle; decide the point of attention before moving.","Let occlusion and lighting reveal the subject instead of adding random camera shake."],finishing:{grain:0.08,grainSizePxAt1080:0.9,halation:0.08,gateWeavePxAt1080:0.15,vignette:0.16,saturation:0,contrast:1.2},avoid:["Do not crush every shadow or turn wet streets into mirror-like plastic.","Avoid neon cyberpunk color, arbitrary fog, or broad blur that erases the staged action."],acceptance:["The eye finds the subject in a thumbnail and still sees the intended shadow detail at full size.","Light sources explain the shadows and reflections; the complete motion preserves this logic."]}),profile({id:"documentary-16mm",name:"16 mm documentary",family:"historical-film",summary:"A patient observational view with plausible handheld weight, tactile surfaces, restrained color, and the texture of a small film gauge.",palette:{background:"#343731",ink:"#202824",paper:"#d8d2af",primary:"#a7996e",secondary:"#66807c",accent:"#b86644"},cadence:cinema,delivery:academy,shape:["Stage a specific ordinary activity with incidental depth and asymmetry.","Resolve period street furniture, signage, vehicles, and building details before styling."],material:["Use natural light, restrained saturation, rough plaster, faded paint, and uneven reflectance.","Keep highlight roll-off soft and retain believable material scale in close and distant surfaces."],camera:["Use a human-height view, slow reframing, and very small smooth operator drift.","Pick a plausible lens and viewpoint; foreground parallax must follow camera translation."],finishing:{grain:0.13,grainSizePxAt1080:1.3,halation:0.07,gateWeavePxAt1080:0.3,vignette:0.12,saturation:0.72,contrast:0.96},avoid:["Avoid pristine game-engine materials, evenly spaced procedural city blocks, and modern billboard typography in a period scene.","Do not simulate handheld photography with frantic random movement or apply grain that swims independently of frames."],acceptance:["The untreated scene has convincing period objects, human scale, lighting, and material variation.","Camera drift feels operated, the principal activity stays readable, and small highlights do not clip broadly."]}),profile({id:"super8-color",name:"Super 8 color memory",family:"historical-film",summary:"An intimate small-gauge color scene with warm highlights, gentle exposure breathing, and grounded everyday details.",palette:{background:"#302c28",ink:"#263333",paper:"#ead5a4",primary:"#c59a60",secondary:"#698c80",accent:"#bd6650"},cadence:{fps:18,exposureFrames:1,cameraExposureFrames:1,shutterAngle:180},delivery:academy,shape:["Favor intimate framing, simple gestures, and irregular lived-in surroundings.","Choose one dominant warm/cool relationship and reserve bright color for a small focal object."],material:["Use fabric, foliage, skin, glass, and paint with visible but gentle surface detail.","Keep a soft tonal shoulder and restrained shadow color; emulate a chosen look without claiming an exact film-stock match."],camera:["Keep movement light and human, with short settled moments between reframes.","Author a complete first and last composition; use exposure breathing sparingly and continuously."],finishing:{grain:0.18,grainSizePxAt1080:1.8,halation:0.12,gateWeavePxAt1080:0.55,vignette:0.17,saturation:0.84,contrast:0.93},avoid:["Avoid looping light leaks, damaged-film overlays in every frame, and extreme orange skin.","Do not confuse a 4K delivery raster with invented high-frequency detail inside the small-gauge image."],acceptance:["The frame reads as an intimate photograph before grain is added.","The 18 fps delivery has intentional motion cadence, retained subject detail, and no sudden exposure flashes."]}),profile({id:"theatrical-cel",name:"Theatrical cel animation",family:"drawn-animation",summary:"Late-1980s theatrical anime craft: dimensional layouts, intricate painted environments, decisive ink contours, and controlled cel shadows.",palette:{background:"#101e2c",ink:"#111626",paper:"#c1d3d0",primary:"#396275",secondary:"#9b745e",accent:"#ec593f"},cadence:onTwos,delivery:uhd,shape:["Construct convincing perspective and a clear silhouette with designed mechanical and architectural details.","Separate painted background planes from animated foreground cels; reserve high-detail edges for the focal region."],material:["Use varied ink weight, two or three purposeful shadow shapes, and opaque painted color.","Give background paintings richer texture and atmospheric depth while keeping moving cels clean."],camera:["Use a composed layout, measured multiplane translation, and a clear action axis.","Hold character drawings on twos by default; reserve ones, smears, or impact drawings for specifically authored actions."],finishing:{grain:0.025,grainSizePxAt1080:0.6,halation:0.045,gateWeavePxAt1080:0,vignette:0.04,saturation:0.95,contrast:1.06},avoid:["Avoid generic glossy 3D with a black outline, arbitrary neon, and uniformly detailed noise.","Do not copy a film's characters, logos, or specific shot; translate the production principles into an original scene."],acceptance:["Silhouette, perspective, ink, cel-shadow grouping, and painted depth remain convincing without grain.","Camera motion stays smooth while exposed character poses hold intentionally; motion does not slide joints or contours."]}),profile({id:"watercolor-storybook",name:"Watercolor storybook",family:"drawn-animation",summary:"A luminous painted illustration brought to life through selective movement, pigment pooling, and breathing paper space.",palette:{background:"#eee8d6",ink:"#575b57",paper:"#fff8e6",primary:"#709487",secondary:"#8ba9b6",accent:"#c67757"},cadence:onTwos,delivery:uhd,shape:["Compose broad washed silhouettes, irregular contours, and generous unpainted paper.","Design a limited set of focal edges; allow distant forms to dissolve into larger color masses."],material:["Create translucent overlapping washes with edge pooling and subtle fixed paper tooth.","Bind pigment texture to the painted object rather than regenerating all texture on every frame."],camera:["Use a quiet tableau or shallow multiplane drift.","Animate one narrative gesture at a time with soft anticipation and a settled pause."],finishing:{...cleanFinish,grain:0.025,grainSizePxAt1080:0.6,saturation:0.75},avoid:["Avoid wet-looking plastic, uniform Gaussian blur, and a noise filter that obscures the drawing.","Do not let paper texture crawl or painted edges shimmer between held drawings."],acceptance:["Paper, transparent wash, pooled pigment, and focal edge hierarchy are individually visible.","Texture follows objects and motion leaves the composition calm and readable."]}),profile({id:"ink-sketch",name:"Living ink sketch",family:"drawn-animation",summary:"An expressive drawing with intentional line economy, selective hatching, and restrained redraw variation.",palette:{background:"#eee9dc",ink:"#282c2d",paper:"#fbf5e5",primary:"#566565",secondary:"#acaaa0",accent:"#b86742"},cadence:{...onTwos,exposureFrames:3},delivery:uhd,shape:["Establish contour, gesture, and hierarchy before adding crosshatching.","Let open contours and deliberate omissions carry light; vary line weight by depth and emphasis."],material:["Use a small set of pressure-sensitive strokes with directionally consistent hatching.","Register each redraw to the same underlying shape so line boil does not deform the subject."],camera:["Keep a stable drawing surface or make one legible pan across the sheet.","Use held poses, brief drawing reveals, and small secondary gestures."],finishing:{...cleanFinish,saturation:0.25},avoid:["Avoid vector-perfect uniform outlines, evenly applied scribble noise, and permanent jitter of every point.","Do not add decorative hatching over labels or important negative space."],acceptance:["The gesture reads at thumbnail size and line hierarchy remains clear at full resolution.","Redraw variation is subtle and stable within each exposure; no unintended frame-to-frame crawling."]}),profile({id:"rubber-hose",name:"Rubber-hose cartoon",family:"drawn-animation",summary:"Elastic early-cartoon movement with clean silhouettes, rhythmic arcs, anticipation, and appealing graphic staging.",palette:{background:"#d5cdbb",ink:"#252523",paper:"#f4eddc",primary:"#5f655d",secondary:"#96998a",accent:"#b28355"},cadence:onTwos,delivery:academy,shape:["Build readable bean-shaped torsos, curved hose limbs, and large simple hands and feet.","Stage clear silhouettes with ample space for arcs, squash, stretch, and overlap."],material:["Use opaque flat ink and warm paper-like backgrounds with sparse decorative detail.","Keep line weight and facial registration stable as the body deforms."],camera:["Favor a fixed theatrical view and whole-body acting.","Author anticipation, action, overshoot, and settle; preserve foot contact during planted poses."],finishing:{grain:0.04,grainSizePxAt1080:0.8,halation:0,gateWeavePxAt1080:0.08,vignette:0.06,saturation:0.15,contrast:1.05},avoid:["Avoid sliding feet, unexplained limb length changes, and constant sine-wave movement without acting beats.","Use original characters and props rather than familiar cartoon mascots."],acceptance:["Anticipation, elastic action, follow-through, and a settled pose can be identified in the complete clip.","Feet and hands make intentional contact, and facial features remain readable through deformation."]}),profile({id:"midcentury-limited",name:"Mid-century limited animation",family:"drawn-animation",summary:"Modernist graphic staging with asymmetric shapes, flat color, spare backgrounds, and selective expressive movement.",palette:{background:"#e7dab6",ink:"#263739",paper:"#f6edd5",primary:"#467f82",secondary:"#c79744",accent:"#cf6049"},cadence:{...onTwos,exposureFrames:3},delivery:uhd,shape:["Design large asymmetric color blocks and stylized angular silhouettes.","Use negative space and economical drawn details to direct attention."],material:["Favor matte gouache-like flat fields with slight fixed paper texture.","Keep a few controlled overlaps and color relationships rather than realistic shading."],camera:["Treat the frame as a designed poster; use purposeful pans or held compositions.","Animate the expressive part while holding the body or background; time actions around clear pauses."],finishing:{...cleanFinish,saturation:0.88},avoid:["Avoid gradients on every shape, generic geometric confetti, and continual motion of every object.","Limited animation still needs poses, timing, and staging; do not substitute random bobbing."],acceptance:["The still frame reads as a coherent graphic design with a dominant focal shape.","Selective motion clarifies the action and the held regions feel intentional."]}),profile({id:"cut-paper",name:"Layered cut paper",family:"tactile-animation",summary:"A handmade collage with meaningful overlaps, irregular cut edges, restrained paper relief, and mechanical articulation.",palette:{background:"#dad2b9",ink:"#374a43",paper:"#f6edce",primary:"#597d66",secondary:"#d2a45b",accent:"#bf5d45"},cadence:onTwos,delivery:uhd,shape:["Build a small number of nested, readable paper silhouettes and visible articulation pivots.","Let overlapping layers explain the spatial construction."],material:["Use fixed fiber texture, small edge irregularities, and soft contact shadows proportional to paper thickness.","Keep lighting direction consistent across every layer."],camera:["Use a mostly frontal rostrum view with restrained depth changes.","Move paper pieces around authored pivots and stepped poses; keep texture attached to each piece."],finishing:{...cleanFinish,saturation:0.84},avoid:["Avoid inflated bevels, thick plastic shadows, sliding textures, and smooth stretchy limbs.","Do not make the whole scene look like a flat vector image with one global drop shadow."],acceptance:["Separate paper layers, their contact shadows, and edge character are readable.","Articulation, occlusion, and paper texture remain consistent throughout motion."]}),profile({id:"stopmotion-clay",name:"Clay stop motion",family:"tactile-animation",summary:"A miniature tactile set with shaped clay, practical lighting, intentional pose increments, and believable physical contact.",palette:{background:"#283f47",ink:"#22333a",paper:"#edd6a9",primary:"#719b92",secondary:"#d8a451",accent:"#c16b54"},cadence:{...onTwos,cameraExposureFrames:2},delivery:uhd,shape:["Design a strong hand-shaped silhouette with slightly irregular proportions and tangible seams.","Stage a small set whose miniature scale is supported by prop size and limited depth."],material:["Use soft diffuse clay with sparse tool marks and local roughness variation.","Create contact shadows and controlled depth of field; avoid uniformly shiny surfaces."],camera:["Use a physically plausible fixed miniature camera or slow incremented move.","Author weight shifts and stepped poses, preserving planted contacts and deliberate squash."],finishing:{...cleanFinish,grain:0.018,grainSizePxAt1080:0.6,vignette:0.05,saturation:0.86},avoid:["Avoid frictionless interpolation, floating props, animated texture noise, and glossy toy materials.","Do not call procedural wobble a physical simulation or claim a practical shoot."],acceptance:["Clay surface, contact, miniature lighting, and stepped posing all contribute to the treatment.","No planted foot slides, prop intersections, or focus changes conceal the principal action."]}),profile({id:"pixel-art",name:"Authored pixel art",family:"digital-animation",summary:"A deliberately drawn low-resolution world with a limited palette, designed pixel clusters, discrete poses, and exact integer enlargement.",palette:{background:"#182633",ink:"#111c2d",paper:"#e6dcc1",primary:"#5c9c83",secondary:"#d2aa63",accent:"#d96658"},cadence:{...onTwos,cameraExposureFrames:2},delivery:{...uhd,sampling:"nearest",logicalGrid:{width:320,height:180}},shape:["Draw on the declared 320 by 180 logical grid with readable clusters and deliberate stair-step diagonals.","Separate foreground sprites, midground tiles, and distant parallax planes with value and palette."],material:["Use a bounded palette with authored highlight and shadow ramps.","Place dithering only where it explains a gradient or material; preserve crisp one-pixel accents."],camera:["Quantize sprite positions and camera translation to logical pixels.","Use authored pose changes and integer parallax offsets; export with a single nearest-neighbor integer scale."],finishing:cleanFinish,avoid:["Avoid anti-aliasing, postprocess blur, noninteger scaling, smooth subpixel translation, and a pixelation filter over an unrelated image.","Do not add film grain or screen simulation by default; those are separate authored treatments."],acceptance:["Each logical pixel maps to an equal, crisp 12 by 12 output block in the recommended 3840 by 2160 delivery.","Palette, sprite clusters, occlusion, and pose changes read clearly throughout the full clip."]}),profile({id:"rotoscope",name:"Painterly rotoscope",family:"drawn-animation",summary:"Naturalistic human movement translated into stable painted masses, expressive contour, and deliberately simplified light.",palette:{background:"#243747",ink:"#172934",paper:"#e7d8bc",primary:"#798b82",secondary:"#b08a69",accent:"#dc8561"},cadence:onTwos,delivery:uhd,shape:["Preserve anatomical landmarks, weight, and the readable gesture from an authorized reference or deliberately authored motion.","Simplify the figure into consistent large painted masses instead of tracing every visible texture."],material:["Keep controlled contour variation and two or three light groups attached to the subject.","Let brush texture follow body surfaces and maintain facial landmark identity."],camera:["Use a stable action axis and clear full-body or close gesture framing.","Retain natural timing and planted contact; author redraw variation per exposure rather than per display refresh."],finishing:{...cleanFinish,grain:0.012,grainSizePxAt1080:0.5,saturation:0.78},avoid:["Avoid edge-detection filters, flickering facial features, crawling paint, and rubbery anatomical drift.","Do not describe a scene as reference-traced unless an actual authorized reference was used."],acceptance:["Weight shifts and gestures remain plausible, with stable anatomy and consistent light grouping.","Contours and texture hold coherently between exposures without accidental shimmer."]}),profile({id:"engraving",name:"Engraved illustration",family:"drawn-animation",summary:"A precise monochrome illustration whose curved hatching, contour hierarchy, and open paper explain form.",palette:{background:"#e8dfc8",ink:"#25322f",paper:"#f6ecd2",primary:"#52645b",secondary:"#a5ab92",accent:"#9b653d"},cadence:cinema,delivery:uhd,shape:["Use a clear structural outline and reserve fine marks for material and curvature.","Organize hatching into form-following bands with intentional light gaps."],material:["Render crisp fine ink with sparse paper texture and deliberate crosshatch density.","Keep line spacing wide enough to survive the actual delivery raster and video encoding."],camera:["Use a slow explanatory reveal, rotation, or camera move with stable line registration.","If the underlying form moves, move its hatch coordinates with it rather than sampling new screen-space noise."],finishing:{...cleanFinish,saturation:0.08},avoid:["Avoid uniform screen-space hatch overlays, moire, crushed dense line fields, and illegible decorative labels.","Do not present an artistic mechanical drawing as dimensionally verified engineering."],acceptance:["Hatch direction describes form and fine lines remain distinct in a full-resolution delivery frame.","Motion does not create moire or crawl that overwhelms the subject."]}),profile({id:"math-explainer",name:"Mathematical explanation",family:"explanation",summary:"A lucid visual argument built from persistent objects, purposeful color, geometric construction, and time to understand each step.",palette:{background:"#101c2a",ink:"#e0e8ed",paper:"#f4f1de",primary:"#59b6d5",secondary:"#e4bf63",accent:"#df7e82"},cadence:{fps:30,exposureFrames:1,cameraExposureFrames:1,shutterAngle:0},delivery:uhd,shape:["Choose one mathematical claim; construct the relevant objects before transforming or comparing them.","Use persistent object identities, meaningful axes, clear labels, and whitespace around the argument."],material:["Use clean vector strokes, restrained fills, and consistent notation with semantic colors.","Keep line weight, label size, and contrast readable at the intended viewing size."],camera:["Hold a stable coordinate system unless a move directly explains the mathematics.","Sequence setup, construction, transformation, comparison, and conclusion with deliberate pauses."],finishing:cleanFinish,avoid:["Avoid decorative equations, unsupported numeric claims, arbitrary camera spins, and simultaneous unrelated reveals.","Do not substitute a familiar presenter's mascot or identity for the clarity of the explanation."],acceptance:["The illustrated relationship can be checked from the authored geometry or data.","Each color keeps its meaning, labels remain legible, and each logical step has a readable settled state."]}),profile({id:"isometric-design",name:"Isometric design study",family:"digital-animation",summary:"An orthographic object or system study with consistent construction, restrained materials, and diagram-like spatial clarity.",palette:{background:"#e6e5db",ink:"#2c4147",paper:"#f7f2df",primary:"#7b9c93",secondary:"#d1ad66",accent:"#c9674d"},cadence:cinema,delivery:uhd,shape:["Use one consistent orthographic projection and a clear assembled silhouette.","Make repeated parts and physical relationships coherent; reserve detail for the function being explained."],material:["Assign a small palette of believable matte, metal, glass, and rubber responses where appropriate.","Use soft contact shadows and explicit edges to separate adjacent parts."],camera:["Hold the projection and composition during assembly or an exploded view.","Move components along meaningful axes and include a settled assembled and separated state."],finishing:cleanFinish,avoid:["Avoid inconsistent vanishing points, floating unconnected parts, excessive bevels, and candy-like materials on every surface.","Do not imply fabrication readiness without dimensional and engineering validation."],acceptance:["Part relationships remain legible during every movement, with no accidental intersection or cropped component.","The final composition has coherent scale, contact, projection, and material distinction."]}),profile({id:"clean-motion",name:"Editorial motion graphics",family:"digital-animation",summary:"Precise typography, controlled hierarchy, and economical choreography organized around a clear editorial message.",palette:{background:"#15232e",ink:"#f2eee1",paper:"#f8f4e9",primary:"#7cafb1",secondary:"#d4b87b",accent:"#dc795b"},cadence:{fps:30,exposureFrames:1,cameraExposureFrames:1,shutterAngle:0},delivery:uhd,shape:["Establish one typographic hierarchy, a consistent grid, and a small set of geometric devices.","Use exact supplied copy and arrange it to read in the intended viewing order."],material:["Use restrained flat color, carefully weighted rules, and crisp type at the final output size.","Reserve texture and gradients for an authored editorial purpose."],camera:["Treat the screen as a composition; use position, scale, and opacity only where they clarify hierarchy.","Separate entrance, readable hold, transition, and exit; stagger related objects rather than animating everything at once."],finishing:cleanFinish,avoid:["Avoid decorative dashboard cards, indiscriminate bounce, tiny type, and transitions that obscure supplied text.","Do not introduce slogans, data, or claims absent from the brief."],acceptance:["Exact copy reads comfortably in the delivery frame and remains visible long enough to understand.","Every move has a purpose, spacing stays controlled, and the first and last frames are intentionally composed."]})]);var profilesById=new Map(VISUAL_STYLE_PROFILES.map((item)=>[item.id,item]));function getVisualStyleProfile(id){return profilesById.get(VisualStyleIdSchema.parse(id))}function createVisualStyleDirection(id){const style=getVisualStyleProfile(id);const grid=style.delivery.logicalGrid;return[`${style.name}: ${style.summary}`,`Palette: ${Object.entries(style.palette).map(([role,color2])=>`${role} ${color2}`).join(", ")}.`,`Shape and staging: ${style.shape.join(" ")}`,`Materials and light: ${style.material.join(" ")}`,`Camera and motion: ${style.camera.join(" ")}`,`Cadence: ${style.cadence.fps} fps; subject exposure ${style.cadence.exposureFrames} frame(s); camera exposure ${style.cadence.cameraExposureFrames} frame(s); shutter angle ${style.cadence.shutterAngle} degrees.`,`Finishing targets: ${Object.entries(style.finishing).map(([name,value])=>`${name}=${value}`).join(", ")}. These are authoring controls, not a film-stock calibration.`,`Recommended delivery: ${style.delivery.width} by ${style.delivery.height}, ${style.delivery.sampling} sampling.${grid===undefined?"":` Author at ${grid.width} by ${grid.height} logical pixels and enlarge by exactly ${style.delivery.width/grid.width}.`}`,`Avoid: ${style.avoid.join(" ")}`,`Review actual frames and motion: ${style.acceptance.join(" ")}`].join(`
|
|
29
|
-
|
|
28
|
+
`,(error)=>{if(error===null||error===undefined)resolve5();else reject(error)})})}function defaultInput(){return process.stdin}async function emitResponse(writeLine,response){await writeLine(JSON.stringify(response))}async function processLine(line,session,writeLine){if(line.byteLength===0)return;let value;try{const text=new TextDecoder("utf-8",{fatal:true}).decode(line);if(text.trim()==="")return;value=JSON.parse(text)}catch{await emitResponse(writeLine,failure(null,-32700,"Parse error"));return}if(Array.isArray(value)){await emitResponse(writeLine,failure(null,-32600,"Invalid Request"));return}const response=await session.handle(value);if(response!==null)await emitResponse(writeLine,response)}async function runMcpServer(options={}){const runtime=await SlopcameraMcpToolRuntime.create(options.rootDirectory??process.cwd(),options.generateDependencies,options.hostResourceCoordinator);const session=new SlopcameraMcpSession(runtime,options.serverVersion??SLOPCAMERA_VERSION);const writeLine=options.writeLine??defaultWriteLine;let buffered=Buffer.alloc(0);for await(const chunk of options.input??defaultInput()){const bytes=typeof chunk==="string"?Buffer.from(chunk,"utf8"):Buffer.from(chunk);buffered=Buffer.concat([buffered,bytes]);if(buffered.byteLength>maximumMessageBytes&&!buffered.includes(10)){buffered=Buffer.alloc(0);await emitResponse(writeLine,failure(null,-32700,"Parse error"));continue}for(;;){const newline=buffered.indexOf(10);if(newline===-1)break;let line=buffered.subarray(0,newline);buffered=buffered.subarray(newline+1);if(line.at(-1)===13)line=line.subarray(0,-1);if(line.byteLength>maximumMessageBytes){await emitResponse(writeLine,failure(null,-32700,"Parse error"))}else{await processLine(line,session,writeLine)}}}if(buffered.byteLength>0){if(buffered.byteLength>maximumMessageBytes){await emitResponse(writeLine,failure(null,-32700,"Parse error"))}else{await processLine(buffered,session,writeLine)}}}import{z}from"zod";var SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS=Object.freeze({bytes:4*1024*1024,commands:256,evidence:64,modules:64,operations:512,profiles:128,requirements:32,tools:128,workflows:128});var identifier=z.string().min(1).max(192).regex(/^[a-z][a-z0-9]*(?:[-_.:/@][a-z0-9]+)*$/u);var commandName=z.string().min(1).max(128).regex(/^[a-z][a-z0-9-]*(?: [a-z][a-z0-9-]*){0,3}$/u);var digest=z.string().regex(/^[a-f0-9]{64}$/u);var positiveSafeInteger=z.number().int().safe().positive();var nonnegativeSafeInteger=z.number().int().safe().nonnegative();var SlopcameraCapabilityEffectSchema=z.enum(["pure","local-read","local-derived-write","project-mutation","paid-cloud","live-control"]);var SlopcameraCapabilityResumeSchema=z.enum(["deterministic","verified-receipt","recoverable-transaction","ambiguous-after-dispatch","non-resumable-live"]);var SlopcameraCapabilityResourceSchema=z.enum(["cpu","local-io","ffmpeg","vision","whisper","network","paid-call","project-render","project-publication","output-publication","capture-device","browser"]);var SlopcameraCapabilityPreparationSchema=z.enum(["project-state","recording-metadata","screen-capture","camera","microphone","system-audio","typed-text","window-metadata","local-media","provider-options"]);var SlopcameraCapabilityOperationSchema=z.strictObject({cache:z.enum(["none","exact-run","content-addressed"]),cancellable:z.boolean(),effect:SlopcameraCapabilityEffectSchema,inputSchemaId:identifier,key:z.string().min(3).max(256).regex(/^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*@[1-9][0-9]*$/u),kind:z.string().min(3).max(192).regex(/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*(?:\.[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*)+$/u),lifecycle:z.enum(["pure","local-artifact","project-transaction","paid-dispatch","live-control"]),maxDurationMs:positiveSafeInteger,maxFanOut:nonnegativeSafeInteger,maxInputBytes:nonnegativeSafeInteger,maxOutputBytes:nonnegativeSafeInteger,outputSchemaId:identifier,preparation:z.array(SlopcameraCapabilityPreparationSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.requirements),resources:z.array(z.strictObject({amount:positiveSafeInteger,resource:SlopcameraCapabilityResourceSchema})).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.requirements),resume:SlopcameraCapabilityResumeSchema,version:positiveSafeInteger}).superRefine((operation,context)=>{if(operation.key!==`${operation.kind}@${operation.version}`){context.addIssue({code:"custom",path:["key"],message:"Operation key must match kind and version."})}if(new Set(operation.preparation).size!==operation.preparation.length){context.addIssue({code:"custom",path:["preparation"],message:"Operation preparation entries must be unique."})}const resources=operation.resources.map((claim)=>claim.resource);if(new Set(resources).size!==resources.length){context.addIssue({code:"custom",path:["resources"],message:"Operation resource claims must be unique by resource."})}});var SlopcameraCapabilityWorkflowSchema=z.strictObject({id:identifier,inputSchemaId:identifier,key:z.string().min(3).max(256).regex(/^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*@[1-9][0-9]*$/u),version:positiveSafeInteger}).superRefine((workflow,context)=>{if(workflow.key!==`${workflow.id}@${workflow.version}`){context.addIssue({code:"custom",path:["key"],message:"Workflow key must match id and version."})}});var SlopcameraCapabilityToolSchema=z.strictObject({destructive:z.boolean(),idempotent:z.boolean(),name:z.string().min(1).max(128).regex(/^[a-z][a-z0-9_]*$/u),openWorld:z.boolean(),readOnly:z.boolean()}).superRefine((tool,context)=>{if(tool.readOnly&&tool.destructive){context.addIssue({code:"custom",message:"A read-only tool cannot be destructive."})}});var SlopcameraCapabilityCommandSchema=z.strictObject({effect:SlopcameraCapabilityEffectSchema,name:commandName});var SlopcameraCapabilityQualificationSchema=z.strictObject({evidence:z.array(z.string().min(1).max(512)).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.evidence),level:z.enum(["unit","fixture","browser","native","provider"]),status:z.enum(["qualified","conditional","not-qualified"])}).superRefine((qualification,context)=>{if(qualification.status!=="not-qualified"&&qualification.evidence.length===0){context.addIssue({code:"custom",path:["evidence"],message:"Qualified or conditional profiles require evidence references."})}});var SlopcameraCapabilityProfileSchema=z.strictObject({id:identifier,kind:z.enum(["renderer","native-adapter","provider","authoring","interchange"]),qualification:SlopcameraCapabilityQualificationSchema,requirements:z.array(z.strictObject({description:z.string().min(1).max(256),id:identifier,optional:z.boolean()})).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.requirements)});var SlopcameraCapabilityModuleDeclarationSchema=z.strictObject({commands:z.array(SlopcameraCapabilityCommandSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.commands),description:z.string().min(1).max(1024),moduleId:z.string().min(1).max(128).regex(/^slopcamera\.capability\.[a-z][a-z0-9-]*$/u),operationKeys:z.array(z.string().min(3).max(256)).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.operations),profiles:z.array(SlopcameraCapabilityProfileSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.profiles),stability:z.enum(["stable","experimental"]),title:z.string().min(1).max(160),toolNames:z.array(z.string().min(1).max(128)).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.tools),trust:z.enum(["portable","local-effects","trusted-authoring","native-adapter","paid-provider"]),version:positiveSafeInteger,workflowKeys:z.array(z.string().min(3).max(256)).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.workflows)}).superRefine((module,context)=>{if(module.commands.length+module.operationKeys.length+module.profiles.length+module.toolNames.length+module.workflowKeys.length===0){context.addIssue({code:"custom",message:"A capability module must expose at least one command, operation, profile, tool, or workflow."})}for(const[name,values]of[["operationKeys",module.operationKeys],["toolNames",module.toolNames],["workflowKeys",module.workflowKeys]]){if(new Set(values).size!==values.length){context.addIssue({code:"custom",path:[name],message:`${name} must be unique within a capability module.`})}}});var SlopcameraCapabilityModuleSchema=z.strictObject({commands:z.array(SlopcameraCapabilityCommandSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.commands),description:z.string().min(1).max(1024),moduleId:z.string().min(1).max(128).regex(/^slopcamera\.capability\.[a-z][a-z0-9-]*$/u),operations:z.array(SlopcameraCapabilityOperationSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.operations),profiles:z.array(SlopcameraCapabilityProfileSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.profiles),stability:z.enum(["stable","experimental"]),title:z.string().min(1).max(160),tools:z.array(SlopcameraCapabilityToolSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.tools),trust:z.enum(["portable","local-effects","trusted-authoring","native-adapter","paid-provider"]),version:positiveSafeInteger,workflows:z.array(SlopcameraCapabilityWorkflowSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.workflows)}).superRefine((module,context)=>{const identities=[["commands",module.commands.map((value)=>value.name)],["operations",module.operations.map((value)=>value.key)],["profiles",module.profiles.map((value)=>value.id)],["tools",module.tools.map((value)=>value.name)],["workflows",module.workflows.map((value)=>value.key)]];if(identities.every(([,values])=>values.length===0)){context.addIssue({code:"custom",message:"A capability module must expose at least one command, operation, profile, tool, or workflow."})}for(const[name,values]of identities){if(new Set(values).size!==values.length)context.addIssue({code:"custom",path:[name],message:`${name} must be unique within a capability module.`})}});var manifestBodyBaseSchema=z.strictObject({host:z.enum(["portable","local"]),kind:z.literal("slopcamera.capability-manifest"),modules:z.array(SlopcameraCapabilityModuleSchema).min(1).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.modules),schemaVersion:z.literal(1),toolVersion:z.string().min(1).max(128)});function validateManifestOwnership(manifest,context){const ownership=new Map;for(const[moduleIndex,module]of manifest.modules.entries()){for(const identity of[module.moduleId,...module.commands.map((value)=>`command:${value.name}`),...module.operations.map((value)=>`operation:${value.key}`),...module.profiles.map((value)=>`profile:${value.id}`),...module.tools.map((value)=>`tool:${value.name}`),...module.workflows.map((value)=>`workflow:${value.key}`)]){const owner=ownership.get(identity);if(owner!==undefined)context.addIssue({code:"custom",path:["modules",moduleIndex],message:`${identity} is owned by both ${owner} and ${module.moduleId}.`});ownership.set(identity,module.moduleId)}if(manifest.host==="portable"&&module.trust!=="portable"){context.addIssue({code:"custom",path:["modules",moduleIndex,"trust"],message:"Portable manifests may contain only portable capability modules."})}}}var manifestBodySchema=manifestBodyBaseSchema.superRefine(validateManifestOwnership);var SlopcameraCapabilityManifestSchema=manifestBodyBaseSchema.extend({manifestSha256:digest}).superRefine(validateManifestOwnership);var compilerInputSchema=z.strictObject({host:z.enum(["portable","local"]),modules:z.array(SlopcameraCapabilityModuleDeclarationSchema).min(1).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.modules),operations:z.array(SlopcameraCapabilityOperationSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.operations),tools:z.array(SlopcameraCapabilityToolSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.tools),toolVersion:z.string().min(1).max(128),workflows:z.array(SlopcameraCapabilityWorkflowSchema).max(SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.workflows)});function compareBy(key){return(left,right)=>left[key].localeCompare(right[key])}function indexed(values,key,label){const result=new Map;for(const value of values){const identity=key(value);if(result.has(identity))throw new TypeError(`Duplicate ${label}: ${identity}.`);result.set(identity,value)}return result}function assertExactOwnership(label,available,owners){for(const key of available.keys()){if(!owners.has(key))throw new TypeError(`Unowned ${label}: ${key}.`)}for(const key of owners.keys()){if(!available.has(key))throw new TypeError(`Unknown ${label} declared by a capability module: ${key}.`)}}function defineSlopcameraCapabilityModule(input){return deepFreezeJson(SlopcameraCapabilityModuleDeclarationSchema.parse(createBoundedJsonValueSnapshot(input,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.bytes,"capability module").value))}function compileSlopcameraCapabilityManifest(input){const parsed=compilerInputSchema.parse(createBoundedJsonValueSnapshot(input,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.bytes,"capability manifest compiler input").value);const operations=indexed(parsed.operations,(value)=>value.key,"operation inventory key");const workflows=indexed(parsed.workflows,(value)=>value.key,"workflow inventory key");const tools=indexed(parsed.tools,(value)=>value.name,"tool inventory name");const operationOwners=new Map;const workflowOwners=new Map;const toolOwners=new Map;const moduleIds=new Set;const commandOwners=new Map;const profileOwners=new Map;const claim=(owners,key,moduleId,label)=>{const owner=owners.get(key);if(owner!==undefined)throw new TypeError(`${label} ${key} is owned by both ${owner} and ${moduleId}.`);owners.set(key,moduleId)};const modules=[...parsed.modules].sort(compareBy("moduleId")).map((module)=>{if(moduleIds.has(module.moduleId))throw new TypeError(`Duplicate capability module: ${module.moduleId}.`);moduleIds.add(module.moduleId);for(const command of module.commands)claim(commandOwners,command.name,module.moduleId,"Command");for(const profile of module.profiles)claim(profileOwners,profile.id,module.moduleId,"Profile");const resolvedOperations=module.operationKeys.map((key)=>{claim(operationOwners,key,module.moduleId,"Operation");const value=operations.get(key);if(value===undefined)throw new TypeError(`Unknown operation declared by ${module.moduleId}: ${key}.`);return value}).sort(compareBy("key"));const resolvedWorkflows=module.workflowKeys.map((key)=>{claim(workflowOwners,key,module.moduleId,"Workflow");const value=workflows.get(key);if(value===undefined)throw new TypeError(`Unknown workflow declared by ${module.moduleId}: ${key}.`);return value}).sort(compareBy("key"));const resolvedTools=module.toolNames.map((name)=>{claim(toolOwners,name,module.moduleId,"Tool");const value=tools.get(name);if(value===undefined)throw new TypeError(`Unknown tool declared by ${module.moduleId}: ${name}.`);return value}).sort(compareBy("name"));const{operationKeys:_operationKeys,toolNames:_toolNames,workflowKeys:_workflowKeys,...metadata}=module;return{...metadata,commands:[...module.commands].sort(compareBy("name")),operations:resolvedOperations,profiles:[...module.profiles].sort(compareBy("id")),tools:resolvedTools,workflows:resolvedWorkflows}});assertExactOwnership("operation",operations,operationOwners);assertExactOwnership("workflow",workflows,workflowOwners);assertExactOwnership("tool",tools,toolOwners);const body=manifestBodySchema.parse({host:parsed.host,kind:"slopcamera.capability-manifest",modules,schemaVersion:1,toolVersion:parsed.toolVersion});return deepFreezeJson(SlopcameraCapabilityManifestSchema.parse({...body,manifestSha256:canonicalJsonSha256(body)}))}function parseSlopcameraCapabilityManifest(input){const manifest=SlopcameraCapabilityManifestSchema.parse(createBoundedJsonValueSnapshot(input,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS.bytes,"capability manifest").value);const{manifestSha256,...body}=manifest;const actual=canonicalJsonSha256(body);if(actual!==manifestSha256)throw new TypeError(`Capability manifest digest mismatch: expected ${manifestSha256}, received ${actual}.`);return deepFreezeJson(manifest)}import{createHash as createHash2}from"crypto";import{readFile as readFile3}from"fs/promises";import{resolve as resolve5}from"path";import{z as z2}from"zod";var slopcameraIconSetMaximumMembers=24;var slopcameraIconSetMaximumSetRounds=4;var slopcameraIconSetDefaultSetRounds=3;var slopcameraIconSetDefaultPool=2;var slopcameraIconSetMaximumNameBytes=120;var slopcameraIconSetSlugPattern=/^[a-z0-9][a-z0-9-]{0,62}$/u;var iconSetSheetCellEdge=288;var iconSetSheetGutter=24;var iconSetSheetColumns=4;var iconSetCoverageTolerance=0.4;var iconSetStrokeTolerance=0.4;var iconSetCoverageFloor=0.06;var iconSetStrokeFloorPx=4;var SET_CRITIQUE_SYSTEM=`You are a strict design reviewer judging whether a set of product illustrations belongs to one visual family.
|
|
29
|
+
|
|
30
|
+
The attached contact sheet shows every selected member of the set in row-major reading order, one subject per cell, in the order the message lists their slugs.
|
|
31
|
+
|
|
32
|
+
Intended family contract:
|
|
33
|
+
- Simple orthographic isometric projection in exactly one ink color.
|
|
34
|
+
- Uniform medium-weight structural strokes with at most three large filled planes.
|
|
35
|
+
- No hairlines, hatching, texture, tiny repeated detail, shading, gradients, shadows, text, borders, or background objects.
|
|
36
|
+
|
|
37
|
+
Judge the family as a whole, not each icon in isolation: matching stroke weight, matching ink density and level of detail, consistent projection angle and margins, and no member that looks drawn by a different hand. An icon that is individually fine but visibly sparser, denser, thinner-stroked, or more detailed than the family fails the set.
|
|
38
|
+
|
|
39
|
+
Return pass only when the full sheet reads as one family and every member still depicts its subject. For each member that breaks the family, give concrete problems and a promptFix describing how its image prompt should change.`;var INLINE_SET_CRITIQUE_CLAUSE=`
|
|
40
|
+
|
|
41
|
+
Context note: this is an inline family \u2014 every member renders beside text at about 32 to 48 pixels. Judge each cell as a small chip: bold flat or slightly dimensional pictograms satisfy the contract when their silhouettes stay recognizable at that size. Do not demand isometric depth or the detail level of a larger card illustration; consistency of ink, stroke weight, and mass balance across the sheet is what makes them one family.`;function invalidArgument(message){throw new SlopcameraCloudError("INVALID_ARGUMENT",message)}function isRecord4(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function parseSetMember(value,index){if(!isRecord4(value)){invalidArgument(`Icon set member ${index} must be an object.`)}const slug=value.slug;if(typeof slug!=="string"||!slopcameraIconSetSlugPattern.test(slug)){invalidArgument(`Icon set member ${index} slug must be lowercase kebab-case, at most 63 characters.`)}const subject=value.subject;if(typeof subject!=="string"||subject.trim().length===0||Buffer.byteLength(subject,"utf8")>slopcameraIconSubjectMaximumBytes){invalidArgument(`Icon set member "${slug}" needs a bounded non-empty subject.`)}const purpose=value.purpose;if(purpose!==undefined&&purpose!=="illustration"&&purpose!=="mark"){invalidArgument(`Icon set member "${slug}" purpose must be illustration or mark.`)}const context=value.context;if(context!==undefined&&(typeof context!=="string"||!slopcameraIconContexts.includes(context))){invalidArgument(`Icon set member "${slug}" context must be one of: ${slopcameraIconContexts.join(", ")}.`)}return{...context===undefined?{}:{context},...purpose===undefined?{}:{purpose},slug,subject:subject.trim()}}function parseSlopcameraIconSetSpec(value){if(!isRecord4(value)){invalidArgument("Icon set manifest must be a JSON object.")}const name=value.name;if(name!==undefined&&(typeof name!=="string"||name.trim().length===0||Buffer.byteLength(name,"utf8")>slopcameraIconSetMaximumNameBytes||/[/\\\u0000-\u001f\u007f]/u.test(name))){invalidArgument("Icon set name must be a bounded portable label.")}const ink=value.ink;if(ink!==undefined&&(typeof ink!=="string"||!/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(ink))){invalidArgument("Icon set ink must be a #rgb or #rrggbb color.")}const context=value.context;if(context!==undefined&&(typeof context!=="string"||!slopcameraIconContexts.includes(context))){invalidArgument(`Icon set context must be one of: ${slopcameraIconContexts.join(", ")}.`)}const members=value.members;if(!Array.isArray(members)||members.length===0){invalidArgument("Icon set members must be a non-empty array.")}if(members.length>slopcameraIconSetMaximumMembers){invalidArgument(`Icon sets are bounded to ${slopcameraIconSetMaximumMembers} members.`)}const parsed=members.map((member,index)=>parseSetMember(member,index));const seen=new Set;for(const member of parsed){if(seen.has(member.slug)){invalidArgument(`Icon set slug "${member.slug}" is duplicated.`)}seen.add(member.slug)}const references=value.references;const parsedReferences=[];if(references!==undefined){if(!Array.isArray(references)||references.length>8){invalidArgument("Icon set references must be an array of at most 8 entries.")}for(const[index,reference]of references.entries()){if(!isRecord4(reference)){invalidArgument(`Icon set reference ${index} must be an object.`)}const slug=reference.slug;if(typeof slug!=="string"||!slopcameraIconSetSlugPattern.test(slug)){invalidArgument(`Icon set reference ${index} slug must be lowercase kebab-case, at most 63 characters.`)}if(seen.has(slug)){invalidArgument(`Icon set slug "${slug}" is duplicated.`)}seen.add(slug);const svg=reference.svg;if(typeof svg!=="string"||svg.length<1||svg.length>4096||svg.includes("\x00")||!svg.toLowerCase().endsWith(".svg")){invalidArgument(`Icon set reference "${slug}" needs a bounded .svg path.`)}parsedReferences.push({slug,svg})}}return{...context===undefined?{}:{context},...ink===undefined?{}:{ink},members:parsed,...name===undefined?{}:{name:name.trim()},...parsedReferences.length===0?{}:{references:parsedReferences}}}var iconSetCritiqueSchema=z2.object({familyProblems:z2.array(z2.string()),members:z2.array(z2.object({pass:z2.boolean(),problems:z2.array(z2.string()),promptFix:z2.string(),slug:z2.string()})),pass:z2.boolean()});function boundedText(value,maximum){return value.replace(/[\u0000-\u001f\u007f]/gu," ").slice(0,maximum)}function parseIconSetCritique(output,resolvedModel,memberSlugs){const parsed=iconSetCritiqueSchema.safeParse(output);if(!parsed.success){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","The icon-set critique returned an invalid bounded object.")}const members=[];const seen=new Set;for(const member of parsed.data.members){if(!memberSlugs.has(member.slug)||seen.has(member.slug))continue;seen.add(member.slug);members.push({pass:member.pass,problems:member.problems.slice(0,8).map((problem)=>boundedText(problem,300)),promptFix:boundedText(member.promptFix,2000),slug:member.slug})}return{familyProblems:parsed.data.familyProblems.slice(0,8).map((problem)=>boundedText(problem,300)),members,pass:parsed.data.pass,resolvedModel}}async function critiqueSlopcameraIconSet(input,dependencies){const runtime=await(dependencies.loadLanguageRuntime??loadIconLanguageRuntime)();const controller=new AbortController;const abort=()=>controller.abort(input.signal?.reason);input.signal?.addEventListener("abort",abort,{once:true});if(input.signal?.aborted===true)abort();const timer=setTimeout(()=>controller.abort(),slopcameraIconCritiqueTimeoutMs);try{const apiKey=resolveSlopcameraGatewayCredential(dependencies.environment).token;const gateway=runtime.createGateway({apiKey,baseURL:slopcameraGatewayApiBaseUrl,fetch:createFixedGatewayFetch({...dependencies.fetch===undefined?{}:{fetch:dependencies.fetch},maximumResponseBytes:8*1024*1024})});const output=runtime.Output.object({description:"One bounded family-consistency critique for a rendered icon set contact sheet.",name:"slopcamera_icon_set_critique",schema:iconSetCritiqueSchema});const result=await runtime.generateText({abortSignal:controller.signal,maxOutputTokens:4096,maxRetries:0,messages:[{content:[{text:`Ink: ${input.ink}
|
|
42
|
+
`+(input.context===undefined?"":`Context: ${input.context}
|
|
43
|
+
`)+`Members in row-major order: ${input.members.join(", ")}
|
|
44
|
+
`+"Judge this contact sheet against the family contract.",type:"text"},{data:input.png,mediaType:"image/png",type:"file"}],role:"user"}],model:gateway.languageModel(input.model),output,providerOptions:{gateway:{disallowPromptTraining:true,tags:["slopcamera","icon-set-critique","v1"],zeroDataRetention:true}},system:input.context==="inline"?SET_CRITIQUE_SYSTEM+INLINE_SET_CRITIQUE_CLAUSE:SET_CRITIQUE_SYSTEM,temperature:0});return parseIconSetCritique(isRecord4(result)?result.output:undefined,resolvedModelId(isRecord4(result)?result.response:undefined),new Set(input.members))}catch(error){if(error instanceof SlopcameraCloudError)throw error;throw new SlopcameraCloudError("GENERATION_FAILED","The icon-set critique request failed; it was not retried.",{cause:error})}finally{clearTimeout(timer);input.signal?.removeEventListener("abort",abort)}}function resolvedModelId(value){const modelId=isRecord4(value)?value.modelId:undefined;return typeof modelId==="string"&&modelId.length>0&&modelId.length<=256?modelId:null}async function composeIconSetSheet(members){const sharp=(await import("sharp")).default;const cell=iconSetSheetCellEdge;const gutter=iconSetSheetGutter;const columns=Math.min(iconSetSheetColumns,members.length);const rows=Math.ceil(members.length/columns);const width=columns*cell+(columns+1)*gutter;const height=rows*cell+(rows+1)*gutter;const[panelRed,panelGreen,panelBlue]=[247,248,251];const composites=[];for(let index=0;index<members.length;index+=1){const member=members[index];const rendered=await sharp(Buffer.from(member.svg),{density:96,failOn:"error",limitInputPixels:vectorizeHardLimits.maxDecodedPixels}).resize(cell,cell,{fit:"inside"}).flatten({background:slopcameraIconPanel}).png({compressionLevel:9}).toBuffer();const column=index%columns;const row=Math.floor(index/columns);composites.push({input:rendered,left:gutter+column*(cell+gutter),top:gutter+row*(cell+gutter)})}return Uint8Array.from(await sharp({create:{background:{b:panelBlue,g:panelGreen,r:panelRed},channels:3,height,width}}).composite(composites).png({compressionLevel:9}).toBuffer())}function medianMetric(values){const sorted=[...values].sort((left,right)=>left-right);const middle=Math.floor(sorted.length/2);return sorted.length%2===1?sorted[middle]:(sorted[middle-1]+sorted[middle])/2}function coverageToleranceBand(target){const span=Math.max(iconSetCoverageFloor,target*iconSetCoverageTolerance);return[target-span,target+span]}function strokeToleranceBand(target){const span=Math.max(iconSetStrokeFloorPx,target*iconSetStrokeTolerance);return[target-span,target+span]}function familyDistance(metrics,target){const coverageSpan=Math.max(iconSetCoverageFloor,target.coverageRatio*iconSetCoverageTolerance);const strokeSpan=Math.max(iconSetStrokeFloorPx,target.strokePx*iconSetStrokeTolerance);return Math.abs(metrics.coverageRatio-target.coverageRatio)/coverageSpan+Math.abs(metrics.strokePx-target.strokePx)/strokeSpan}function pickMemberCandidate(eligible,target){const quality=(record2)=>record2.critique===null||record2.critique===undefined?-1:record2.critique.score*(record2.critique.pass?1:0.5);const ranked=[...eligible].sort((left,right)=>{if(target!==null){const distance=familyDistance(left.metrics,target)-familyDistance(right.metrics,target);if(distance!==0)return distance}return quality(right)-quality(left)||right.round-left.round});return ranked[0]}function jointSelect(lanes,anchors){const picks=new Map(lanes.map((lane)=>[lane.input.slug,pickMemberCandidate(lane.eligible,null)]));for(let pass=0;pass<4;pass+=1){let changed=false;for(const lane of lanes){const siblings=[...lanes.filter((candidate)=>candidate!==lane).map((candidate)=>picks.get(candidate.input.slug).metrics),...anchors.map((anchor)=>anchor.metrics)];const own=picks.get(lane.input.slug);const target2=siblings.length===0?{coverageRatio:own.metrics.coverageRatio,strokePx:own.metrics.strokePx}:{coverageRatio:medianMetric(siblings.map((metrics)=>metrics.coverageRatio)),strokePx:medianMetric(siblings.map((metrics)=>metrics.strokePx))};const pick=pickMemberCandidate(lane.eligible,target2);if(pick!==own){picks.set(lane.input.slug,pick);changed=true}}if(!changed)break}const familyMetrics=[...[...picks.values()].map((record2)=>record2.metrics),...anchors.map((anchor)=>anchor.metrics)];const target={coverageRatio:medianMetric(familyMetrics.map((metrics)=>metrics.coverageRatio)),strokePx:medianMetric(familyMetrics.map((metrics)=>metrics.strokePx))};return{picks,target}}function outOfFamilyMembers(lanes,picks,target){const coverageBand=coverageToleranceBand(target.coverageRatio);const strokeBand=strokeToleranceBand(target.strokePx);const outliers=[];for(const lane of lanes){const pick=picks.get(lane.input.slug);if(pick===undefined)continue;if(pick.metrics.coverageRatio<coverageBand[0]||pick.metrics.coverageRatio>coverageBand[1]||pick.metrics.strokePx<strokeBand[0]||pick.metrics.strokePx>strokeBand[1]){outliers.push(lane.input.slug)}}return outliers}function directionalFamilyFeedback(metrics,target,familyProblems){const fixes=[];if(metrics.coverageRatio<target.coverageRatio){fixes.push("increase visual density to match the rest of the set: slightly more structural linework or one more filled plane")}else if(metrics.coverageRatio>target.coverageRatio){fixes.push("reduce visual density to match the rest of the set: fewer planes and less interior detail")}if(metrics.strokePx<target.strokePx){fixes.push("use thicker uniform strokes like the rest of the set")}else if(metrics.strokePx>target.strokePx){fixes.push("use thinner uniform strokes like the rest of the set")}const prefix=fixes.length===0?[]:[`Match the family: ${fixes.join("; ")}.`];return[...prefix,...familyProblems].filter((part)=>part.length>0).join(" ")}async function generateSlopcameraIconSet(input,dependencies={}){if(typeof input.outputDir!=="string"||input.outputDir.length<1||input.outputDir.length>4096||input.outputDir.includes("\x00")){invalidArgument("outputDir must be a bounded local directory path.")}const rounds=input.rounds??2;if(!Number.isInteger(rounds)||rounds<1||rounds>slopcameraIconMaximumRounds){invalidArgument(`rounds must be an integer from 1 through ${slopcameraIconMaximumRounds}.`)}const requestedPool=input.candidatesPerMember??slopcameraIconSetDefaultPool;if(!Number.isInteger(requestedPool)||requestedPool<1||requestedPool>slopcameraIconMaximumRounds){invalidArgument(`candidatesPerMember must be an integer from 1 through ${slopcameraIconMaximumRounds}.`)}const poolSize=Math.min(requestedPool,rounds);const setRounds=input.setRounds??slopcameraIconSetDefaultSetRounds;if(!Number.isInteger(setRounds)||setRounds<1||setRounds>slopcameraIconSetMaximumSetRounds){invalidArgument(`setRounds must be an integer from 1 through ${slopcameraIconSetMaximumSetRounds}.`)}const ink=normalizedHexColor(input.ink??input.spec.ink??slopcameraIconDefaultInk);const model=input.model??"recraft/recraft-v4.1-utility";const critiqueModel=input.critiqueModel??slopcameraIconCritiqueDefaultModel;const critiqueEnabled=rounds>1||input.critiqueModel!==undefined;const collect=dependencies.collectCandidates??collectSlopcameraIconCandidates;const composeSheet=dependencies.composeSheet??composeIconSetSheet;const setCritique=dependencies.setCritique??((critiqueInput)=>critiqueSlopcameraIconSet(critiqueInput,dependencies));const lanes=input.spec.members.map((member)=>({attempts:[],context:member.context??input.spec.context??input.context??undefined,eligible:[],input:member,purpose:member.purpose??input.purpose??"illustration",regenerated:0}));const anchors=[];const limits=resolveVectorizeLimits({});for(const reference of input.spec.references??[]){const referencePath=resolve5(input.manifestDir??".",reference.svg);let svg;try{svg=await readFile3(referencePath,"utf8")}catch{throw new SlopcameraCloudError("INVALID_ARGUMENT",`Icon set reference "${reference.slug}" could not be read: ${reference.svg}.`)}if(Buffer.byteLength(svg,"utf8")>262144){invalidArgument(`Icon set reference "${reference.slug}" exceeds 256 KiB.`)}const png=dependencies.rasterize===undefined?await renderIconPreview(svg,"illustration"):await dependencies.rasterize(svg);const raster=await loadRaster(png,limits,new VectorizeDeadline(limits.maxDurationMs));const extraction=extractIconLineArt(raster.pixels,raster.width,raster.height,{hardEdges:true,ink});const pathCount=(svg.match(/<path/gu)??[]).length;anchors.push({metrics:measureIconCandidateMetrics(extraction,svg,pathCount),slug:reference.slug,svg})}const collectForLane=async(lane,initialFeedback)=>{const collected=await collect({candidatePool:poolSize,...lane.context===undefined?{}:{context:lane.context},...input.critiqueModel===undefined?{}:{critiqueModel:input.critiqueModel},requireCritiquePass:false,...initialFeedback===undefined?{}:{initialFeedback},...input.inheritedFileDescriptors===undefined?{}:{inheritedFileDescriptors:input.inheritedFileDescriptors},ink,model,purpose:lane.purpose,rounds,...input.signal===undefined?{}:{signal:input.signal},subject:lane.input.subject},dependencies);lane.attempts.push(...collected.attempts);lane.eligible.push(...collected.eligible)};for(const lane of lanes){try{await collectForLane(lane)}catch(error){const detail=error instanceof Error?` Last error: ${error.message}`:"";throw new SlopcameraCloudError("GENERATION_FAILED",`Icon set member "${lane.input.slug}" produced no gated candidates.${detail}`,{cause:error})}if(lane.eligible.length===0){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE",`Icon set member "${lane.input.slug}" produced no gate-passing candidates.`)}}const setCritiques=[];let setCritiqueErrors=0;let setRoundsUsed=0;for(let setRound=1;setRound<=setRounds;setRound+=1){setRoundsUsed=setRound;const{picks:picks2,target:target2}=jointSelect(lanes,anchors);const outliers=outOfFamilyMembers(lanes,picks2,target2);const fixes=new Map;if(outliers.length>0){for(const slug of outliers){const pick=picks2.get(slug);fixes.set(slug,directionalFamilyFeedback(pick.metrics,target2,[]))}}else if(critiqueEnabled){const sheet=await composeSheet([...lanes.map((lane)=>({slug:lane.input.slug,svg:picks2.get(lane.input.slug).candidate.svg})),...anchors.map((anchor)=>({slug:anchor.slug,svg:anchor.svg}))]);let verdict;try{verdict=await setCritique({...input.spec.context===undefined?{}:{context:input.spec.context},ink,members:lanes.map((lane)=>lane.input.slug),model:critiqueModel,png:sheet,...input.signal===undefined?{}:{signal:input.signal}})}catch{setCritiqueErrors+=1;continue}setCritiques.push(verdict);if(verdict.pass&&verdict.members.every((member)=>member.pass)){break}for(const member of verdict.members){if(member.pass)continue;const fix=[member.promptFix,...member.problems].filter((part)=>part.length>0).join("; ");fixes.set(member.slug,fix||"match the rest of the set")}if(fixes.size===0&&verdict.familyProblems.length===0){break}if(fixes.size===0){const worst=[...picks2.entries()].sort((left,right)=>familyDistance(right[1].metrics,target2)-familyDistance(left[1].metrics,target2))[0];fixes.set(worst[0],directionalFamilyFeedback(worst[1].metrics,target2,verdict.familyProblems))}}else{break}if(setRound===setRounds)break;for(const[slug,fix]of fixes){const lane=lanes.find((candidate)=>candidate.input.slug===slug);lane.regenerated+=1;try{await collectForLane(lane,fix)}catch{}}}const{picks,target}=jointSelect(lanes,anchors);const finalOutliers=outOfFamilyMembers(lanes,picks,target);const unresolvedCritique=setCritiques.length>0&&!setCritiques[setCritiques.length-1].pass?setCritiques[setCritiques.length-1].members.filter((member)=>!member.pass).map((member)=>member.slug):[];const outputDir=resolve5(input.outputDir);const buildReceipt=()=>{const memberReceipts=lanes.map((lane)=>{const pick=picks.get(lane.input.slug);return{attempts:lane.attempts,candidates:lane.eligible.map((record2)=>({metrics:record2.metrics,round:record2.round,...record2.critique===null?{}:{pass:record2.critique.pass,score:record2.critique.score},selected:record2===pick})),...lane.context===undefined?{}:{context:lane.context},metrics:pick.metrics,outputPath:`${outputDir}/${lane.input.slug}.svg`,purpose:lane.purpose,regenerated:lane.regenerated,slug:lane.input.slug,subject:lane.input.subject,svgSha256:createHash2("sha256").update(pick.candidate.svg).digest("hex")}});const receipt2={...input.spec.context===undefined&&input.context===undefined?{}:{context:input.spec.context??input.context},ink,members:memberReceipts,model,...input.spec.name===undefined?{}:{name:input.spec.name},outputDir,receiptVersion:1,setCritiques,...setCritiqueErrors===0?{}:{setCritiqueErrors},setRoundsUsed,target};return receipt2};if(finalOutliers.length>0||unresolvedCritique.length>0){const unresolved=[...new Set([...finalOutliers,...unresolvedCritique])];await writeAtomically(`${outputDir}/${input.spec.name??"icon-set"}.failed.receipt.json`,JSON.stringify({...buildReceipt(),unresolvedMembers:unresolved},null,2));throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE",`The icon set did not converge inside ${setRounds} set rounds; unresolved members: ${unresolved.join(", ")}. Diagnostic receipt: ${outputDir}/${input.spec.name??"icon-set"}.failed.receipt.json`)}for(const lane of lanes){const pick=picks.get(lane.input.slug);await writeAtomically(`${outputDir}/${lane.input.slug}.svg`,pick.candidate.svg);if(input.keepRaster===true){await writeAtomically(`${outputDir}/${lane.input.slug}.lineart.png`,pick.candidate.png)}}const receipt=buildReceipt();await writeAtomically(`${outputDir}/${input.spec.name??"icon-set"}.receipt.json`,JSON.stringify(receipt,null,2));return receipt}var SLOPCAMERA_PORTABLE_CAPABILITY_VERSION="slopcamera-portable-v1";var portableModule=defineSlopcameraCapabilityModule({commands:[],description:"The fixed portable diagram and image operation projection for Bun SDK consumers.",moduleId:"slopcamera.capability.portable-diagram-image",operationKeys:PORTABLE_SLOPCAMERA_OPERATION_KINDS.map((kind)=>`${kind}@2`),profiles:[{id:"portable-bun-v1",kind:"authoring",qualification:{evidence:["src/code/public-operations.ts"],level:"unit",status:"qualified"},requirements:[{description:"Bun 1.3.14 or newer.",id:"bun",optional:false}]}],stability:"stable",title:"Portable diagrams and images",toolNames:[],trust:"portable",version:1,workflowKeys:[]});function operationProjection(kind){const operation=PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS[kind];return{cache:operation.policy.cache,cancellable:operation.policy.cancellable,effect:operation.policy.effect,inputSchemaId:operation.inputSchemaId,key:`${operation.kind}@${operation.version}`,kind:operation.kind,lifecycle:operation.lifecycle,maxDurationMs:operation.policy.maxDurationMs,maxFanOut:operation.policy.maxFanOut,maxInputBytes:operation.policy.maxInputBytes,maxOutputBytes:operation.policy.maxOutputBytes,outputSchemaId:operation.outputSchemaId,preparation:[...operation.policy.preparation],resources:operation.policy.resources.map((claim)=>({...claim})),resume:operation.policy.resume,version:operation.version}}function createPortableSlopcameraCapabilityManifest(){return compileSlopcameraCapabilityManifest({host:"portable",modules:[portableModule],operations:PORTABLE_SLOPCAMERA_OPERATION_KINDS.map(operationProjection),tools:[],toolVersion:SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,workflows:[]})}var slopcameraPortableCapabilityManifest=createPortableSlopcameraCapabilityManifest();import{z as z3}from"zod";var slopcameraSceneGalleryLimits=Object.freeze({variants:16,variantsBytes:1024*1024,idEdge:64,labelEdge:256,summaryEdge:256});var variantId=z3.string().min(1).max(slopcameraSceneGalleryLimits.idEdge).regex(/^[a-z0-9][a-z0-9-]*$/u,"Variant ids are lowercase slugs like dusk or golden-hour-2.");var SceneVariantPatchSchema=z3.strictObject({kind:z3.literal("slopcamera.spatial-scene-patch"),schemaVersion:z3.literal(1),expectedSceneSha256:SpatialDigestSchema.optional(),operations:z3.array(SpatialPatchOperationSchema).min(1).max(SPATIAL_SCENE_LIMITS.patchOperations)});var SceneGalleryVariantSchema=z3.strictObject({id:variantId,label:z3.string().min(1).max(slopcameraSceneGalleryLimits.labelEdge).optional(),patch:SceneVariantPatchSchema});var SlopcameraSceneVariantsSchema=z3.strictObject({kind:z3.literal("slopcamera.scene-variants"),schemaVersion:z3.literal(1),variants:z3.array(SceneGalleryVariantSchema).min(1).max(slopcameraSceneGalleryLimits.variants)});function summarizeSceneVariantPatch(operations){const parts=operations.map((operation)=>{const target=("entityId"in operation?operation.entityId:undefined)??("camera"in operation?operation.camera.cameraId:undefined)??("channel"in operation?operation.channel.channelId:undefined)??("asset"in operation?operation.asset.assetId:undefined)??("generator"in operation?operation.generator.generatorId:undefined)??("entity"in operation?operation.entity.entityId:undefined)??("override"in operation?operation.override.entityId:undefined);return target===undefined?operation.kind:`${operation.kind} ${target}`});const summary=parts.join("; ");return summary.length<=slopcameraSceneGalleryLimits.summaryEdge?summary:`${summary.slice(0,slopcameraSceneGalleryLimits.summaryEdge-1)}\u2026`}function parseSlopcameraSceneVariants(input){const value=createBoundedJsonValueSnapshot(input,slopcameraSceneGalleryLimits.variantsBytes,"scene variants").value;const document=parseSpatialValue(SlopcameraSceneVariantsSchema,value,"scene variants");const ids=new Set;for(const variant of document.variants){if(ids.has(variant.id)){throw new SpatialSceneError("invalid-data",`Duplicate scene variant id ${variant.id}.`,"variants")}ids.add(variant.id)}return document.variants}function planSlopcameraSceneGallery(input){const scene=parseSpatialScene(input.scene);const baseSceneSha256=spatialSceneSha256(scene);const specs=parseSlopcameraSceneVariants(input.variants);const variants=specs.map((spec,offset)=>{const expected=spec.patch.expectedSceneSha256??baseSceneSha256;if(expected!==baseSceneSha256){throw new SpatialSceneError("conflict",`Variant ${spec.id} was authored against a different scene revision.`,"variants")}const patch={...spec.patch,expectedSceneSha256:expected};const result=applySpatialScenePatch(scene,patch);return{diff:result.diff,id:spec.id,index:offset+1,label:spec.label??spec.id,patchSha256:spatialValueSha256(patch),prompt:summarizeSceneVariantPatch(patch.operations),scene:result.scene,sceneSha256:result.sceneSha256}});return{baseSceneSha256,scene,variants}}import{z as z5}from"zod";import{z as z4}from"zod";var STUDIO_LIMITS=Object.freeze({documentBytes:32*1024*1024,documentDepth:32,documentValues:500000,sourceFiles:512,sourceBytes:4*1024**3,outputSpecifications:32,outputFiles:25000,outputBytes:64*1024**3,timeoutSeconds:6*60*60,frames:25000,frameIndexExclusive:1e6,dimension:8192,pixels:33554432,parameterBytes:256*1024,parameterDepth:16,parameterValues:20000});function studioDocument(schema,name){return z4.preprocess((value)=>value===undefined?undefined:createBoundedJsonValueSnapshot(value,STUDIO_LIMITS.documentBytes,name,{maximumDepth:STUDIO_LIMITS.documentDepth,maximumValues:STUDIO_LIMITS.documentValues}).value,schema)}function parseStudioValue(schema,input){try{return deepFreezeJson(schema.parse(input))}catch(error){if(error instanceof SlopcameraCodeError)throw error;throw new SlopcameraCodeError("invalid-data",error instanceof z4.ZodError?error.issues[0]?.message??"Invalid studio document.":"Invalid studio document.")}}function studioHash(domain,value){return boundedCanonicalJsonSha256({domain,value},{maximumBytes:STUDIO_LIMITS.documentBytes,maximumDepth:STUDIO_LIMITS.documentDepth+2,maximumValues:STUDIO_LIMITS.documentValues+4})}var studioCompare=compareUtf16Strings;function studioRequire(condition,message){if(!condition)throw new SlopcameraCodeError("invalid-data",message)}function pathKey(path){return path.toLowerCase()}function assertDistinctPaths(paths){const names=new Set(paths.map(pathKey));studioRequire(names.size===paths.length,"Studio paths collide.");for(const name of names){for(let offset=name.indexOf("/");offset!==-1;offset=name.indexOf("/",offset+1)){studioRequire(!names.has(name.slice(0,offset)),"Studio paths use a file as an ancestor.")}}}var StudioDigestSchema=z5.string().regex(/^[a-f0-9]{64}$/u);var StudioEngineSchema=z5.enum(["blender","manim","cadquery"]);var StudioPathSchema=z5.string().min(1).max(1024).refine((path)=>path.normalize("NFC")===path&&!path.startsWith("/")&&!/[\\:\u0000-\u001f\u007f]/u.test(path)&&path.split("/").every((part)=>part!==""&&part!=="."&&part!==".."&&!/[. ]$/u.test(part)),"Studio paths must be normalized, contained POSIX-relative names.");var identifier2=z5.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u);var stableId=z5.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/u);var sourceFile=z5.strictObject({path:StudioPathSchema,sha256:StudioDigestSchema,bytes:z5.number().int().safe().nonnegative().max(STUDIO_LIMITS.sourceBytes)});var sourceBundleShape=z5.strictObject({kind:z5.literal("slopcamera.studio-source-bundle"),schemaVersion:z5.literal(1),engine:StudioEngineSchema,entrypoint:z5.discriminatedUnion("kind",[z5.strictObject({kind:z5.literal("python"),path:StudioPathSchema.refine((path)=>path.endsWith(".py"))}),z5.strictObject({kind:z5.literal("blend"),path:StudioPathSchema.refine((path)=>path.endsWith(".blend"))})]),files:z5.array(sourceFile).min(1).max(STUDIO_LIMITS.sourceFiles)}).superRefine((value,context)=>{const issue=(message)=>context.addIssue({code:"custom",message});try{assertDistinctPaths(value.files.map((file)=>file.path))}catch{issue("Source paths must be distinct and cannot have file ancestors.")}if(value.files.reduce((total,file)=>total+file.bytes,0)>STUDIO_LIMITS.sourceBytes)issue("Source bundle exceeds four GiB.");if(!value.files.some((file)=>file.path===value.entrypoint.path&&file.bytes>0))issue("Entrypoint must name a declared nonempty source file.");if(value.entrypoint.kind==="blend"&&value.engine!=="blender")issue("Blend entrypoints require Blender.")});var sourceBundle=sourceBundleShape.transform((value)=>({...value,files:[...value.files].sort((a,b)=>studioCompare(a.path,b.path))})).pipe(sourceBundleShape);var StudioSourceBundleSchema=studioDocument(sourceBundle,"studio source bundle");var parseStudioSourceBundle=(input)=>parseStudioValue(StudioSourceBundleSchema,input);var StudioSourceSpaceSchema=z5.strictObject({units:z5.enum(["meters","millimeters","centimeters"]),upAxis:z5.enum(["x","y","z"]),handedness:z5.enum(["right","left"])});var StudioOutputRoleSchema=z5.enum(["native-source","model","beauty","auxiliary","simulation-cache","audio"]);var StudioOutputFormatSchema=z5.enum(["py","blend","usd","usda","usdc","glb","step","png","exr","mp4","mov","webm","wav","flac","mp3","cache"]);var raster=z5.strictObject({kind:z5.literal("raster"),colorSpace:z5.enum(["srgb","linear-rec709","data","unspecified"]),alpha:z5.enum(["opaque","straight","premultiplied","none"]),dataType:z5.enum(["uint8","uint16","float16","float32"]),channels:z5.array(z5.string().regex(/^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u)).min(1).max(32),semantic:z5.enum(["color","depth","normal","object-id","mask","custom"]),unit:z5.enum(["unitless","meters","millimeters","centimeters"])}).superRefine((value,context)=>{if(new Set(value.channels).size!==value.channels.length)context.addIssue({code:"custom",message:"Raster channels must be unique."});if(value.semantic!=="color"&&value.colorSpace!=="data")context.addIssue({code:"custom",message:"Non-color raster passes must declare data color space."});if(value.semantic==="color"&&value.colorSpace==="data")context.addIssue({code:"custom",message:"Color output cannot declare data color space."});if(value.semantic!=="depth"&&value.unit!=="unitless")context.addIssue({code:"custom",message:"Only depth passes declare distance units."})});var StudioOutputInterpretationSchema=z5.discriminatedUnion("kind",[raster,z5.strictObject({kind:z5.literal("model"),sourceSpace:StudioSourceSpaceSchema}),z5.strictObject({kind:z5.literal("native-source")}),z5.strictObject({kind:z5.literal("cache"),semantics:z5.literal("opaque-native")}),z5.strictObject({kind:z5.literal("audio"),sampleRate:z5.number().int().min(8000).max(384000),channels:z5.number().int().min(1).max(32)})]);var outputCommon={id:stableId,role:StudioOutputRoleSchema,format:StudioOutputFormatSchema,interpretation:StudioOutputInterpretationSchema};var pattern=z5.string().max(1024).refine((value)=>value.split("%06d").length===2&&!value.replace("%06d","").includes("%")&&StudioPathSchema.safeParse(value.replace("%06d","000000")).success,"Sequence paths require exactly one %06d placeholder in a safe relative path.");function compatibleOutput(value){if(value.role==="native-source")return["py","blend"].includes(value.format)&&value.interpretation.kind==="native-source";if(value.role==="model")return["blend","usd","usda","usdc","glb","step"].includes(value.format)&&value.interpretation.kind==="model";if(value.role==="simulation-cache")return value.format==="cache"&&value.interpretation.kind==="cache";if(value.role==="audio")return["wav","flac","mp3"].includes(value.format)&&value.interpretation.kind==="audio";return["png","exr","mp4","mov","webm"].includes(value.format)&&value.interpretation.kind==="raster"&&(value.role!=="beauty"||value.interpretation.semantic==="color")}var StudioOutputSpecSchema=z5.discriminatedUnion("kind",[z5.strictObject({...outputCommon,kind:z5.literal("file"),path:StudioPathSchema}),z5.strictObject({...outputCommon,kind:z5.literal("sequence"),pathPattern:pattern}),z5.strictObject({...outputCommon,kind:z5.literal("directory"),path:StudioPathSchema})]).superRefine((value,context)=>{const issue=(message)=>context.addIssue({code:"custom",message});if(!compatibleOutput(value))issue("Output role, format, and interpretation disagree.");if(value.kind==="directory"!==(value.format==="cache"))issue("Only native cache outputs use directory declarations.");if(value.kind==="sequence"&&!["png","exr"].includes(value.format))issue("Numbered sequences support PNG or EXR only.");if(value.kind!=="directory"){const path=value.kind==="sequence"?value.pathPattern:value.path;if(!path.endsWith(`.${value.format}`)&&!(value.format==="step"&&path.endsWith(".stp")))issue("Output extension must agree with its declared format.")}if(value.interpretation.kind==="raster"){if(value.format==="png"&&!["uint8","uint16"].includes(value.interpretation.dataType))issue("PNG declares integer sample data.");if(value.format==="exr"&&!["float16","float32"].includes(value.interpretation.dataType))issue("The admitted EXR profile declares floating point sample data.")}});var frameRateShape=z5.strictObject({numerator:z5.number().int().min(1).max(1e6),denominator:z5.number().int().min(1).max(1e6)}).refine((value)=>value.numerator/value.denominator<=240,"Studio cadence exceeds 240 fps.");var frameRate=frameRateShape.transform((value)=>{let{numerator:a,denominator:b}=value;while(b!==0){const remainder=a%b;a=b;b=remainder}return{numerator:value.numerator/a,denominator:value.denominator/a}}).pipe(frameRateShape);var StudioRenderSchema=z5.strictObject({width:z5.number().int().min(1).max(STUDIO_LIMITS.dimension),height:z5.number().int().min(1).max(STUDIO_LIMITS.dimension),frameRate,startFrame:z5.number().int().nonnegative().max(STUDIO_LIMITS.frameIndexExclusive-1),endFrameExclusive:z5.number().int().positive().max(STUDIO_LIMITS.frameIndexExclusive)}).refine((value)=>value.endFrameExclusive>value.startFrame&&value.endFrameExclusive-value.startFrame<=STUDIO_LIMITS.frames&&value.width*value.height<=STUDIO_LIMITS.pixels,"Render requires a bounded, nonempty half-open frame interval and pixel area.");var StudioEngineOptionsSchema=z5.discriminatedUnion("engine",[z5.strictObject({engine:z5.literal("blender"),renderer:z5.enum(["cycles","eevee"]),device:z5.enum(["cpu","gpu"]),samples:z5.number().int().min(1).max(4096),transparent:z5.boolean(),viewTransform:z5.enum(["AgX","Standard"]),denoise:z5.boolean(),seed:z5.number().int().min(0).max(4294967295)}),z5.strictObject({engine:z5.literal("manim"),scene:identifier2,renderer:z5.literal("cairo"),transparent:z5.boolean()}),z5.strictObject({engine:z5.literal("cadquery"),exportVariable:identifier2,tolerance:z5.number().finite().positive().max(1),angularTolerance:z5.number().finite().positive().max(Math.PI)})]);var parameters=z5.preprocess((value)=>value===undefined?undefined:createBoundedJsonValueSnapshot(value,STUDIO_LIMITS.parameterBytes,"studio parameters",{maximumDepth:STUDIO_LIMITS.parameterDepth,maximumValues:STUDIO_LIMITS.parameterValues}).value,z5.record(z5.string(),z5.unknown()));var StudioExecutionProfileSchema=z5.strictObject({trust:z5.literal("trusted-current-user"),isolation:z5.literal("none"),hermetic:z5.literal(false)});var jobShape=z5.strictObject({kind:z5.literal("slopcamera.studio-job"),schemaVersion:z5.literal(1),jobId:z5.string().regex(/^studio_[a-zA-Z0-9][a-zA-Z0-9_-]{0,120}$/u),bundleSha256:StudioDigestSchema,stage:z5.enum(["build","bake","render"]),parameters,engine:StudioEngineOptionsSchema,render:StudioRenderSchema.optional(),outputs:z5.array(StudioOutputSpecSchema).min(1).max(STUDIO_LIMITS.outputSpecifications),limits:z5.strictObject({timeoutSeconds:z5.number().int().min(1).max(STUDIO_LIMITS.timeoutSeconds),maximumOutputBytes:z5.number().int().safe().min(1).max(STUDIO_LIMITS.outputBytes),maximumOutputFiles:z5.number().int().min(1).max(STUDIO_LIMITS.outputFiles)}),execution:StudioExecutionProfileSchema}).superRefine((value,context)=>{if(value.render===undefined&&(value.stage==="render"&&value.engine.engine!=="cadquery"||value.outputs.some((output)=>output.kind==="sequence"||output.interpretation.kind==="raster")))context.addIssue({code:"custom",message:"Raster outputs, render stage, and numbered sequences require explicit dimensions and a frame interval."});if(new Set(value.outputs.map((output)=>output.id)).size!==value.outputs.length)context.addIssue({code:"custom",message:"Output IDs must be unique."});const count=value.outputs.reduce((sum,output)=>sum+(output.kind==="sequence"&&value.render!==undefined?value.render.endFrameExclusive-value.render.startFrame:1),0);if(count>value.limits.maximumOutputFiles)context.addIssue({code:"custom",message:"Declared outputs exceed the job's output-file limit."})});var job=jobShape.transform((value)=>({...value,outputs:[...value.outputs].sort((a,b)=>studioCompare(a.id,b.id))})).pipe(jobShape);var StudioJobSchema=studioDocument(job,"studio job");var parseStudioJob=(input)=>parseStudioValue(StudioJobSchema,input);var StudioCapabilityNameSchema=z5.enum(["python-authoring","blend-authoring","build","bake","render","gpu-render","image-sequence","beauty-video","model-export","auxiliary-passes","native-cache","audio-output"]);var capability=z5.strictObject({name:StudioCapabilityNameSchema,support:z5.enum(["available","unavailable","unverified"]),evidence:z5.enum(["probe","qualification"]),receiptSha256:StudioDigestSchema.optional()}).refine((value)=>value.evidence!=="qualification"||value.receiptSha256!==undefined,"Qualification evidence requires a retained receipt digest.");var runtimeShape=z5.strictObject({kind:z5.literal("slopcamera.studio-runtime"),schemaVersion:z5.literal(1),engine:StudioEngineSchema,tool:z5.strictObject({name:z5.string().min(1).max(128),version:z5.string().min(1).max(512),executableSha256:StudioDigestSchema}),driverSha256:StudioDigestSchema,environment:z5.strictObject({fingerprintSha256:StudioDigestSchema,evidence:z5.literal("observed-package-environment"),hermetic:z5.literal(false)}),capabilities:z5.array(capability).max(12)}).refine((value)=>new Set(value.capabilities.map((item)=>item.name)).size===value.capabilities.length,"Runtime capability names must be unique.");var runtime=runtimeShape.transform((value)=>({...value,capabilities:[...value.capabilities].sort((a,b)=>studioCompare(a.name,b.name))})).pipe(runtimeShape);var StudioRuntimeIdentitySchema=studioDocument(runtime,"studio runtime identity");var parseStudioRuntimeIdentity=(input)=>parseStudioValue(StudioRuntimeIdentitySchema,input);var StudioOutputArtifactSchema=z5.strictObject({outputId:stableId,path:StudioPathSchema,sha256:StudioDigestSchema,bytes:z5.number().int().safe().positive().max(STUDIO_LIMITS.outputBytes),role:StudioOutputRoleSchema,format:StudioOutputFormatSchema,frame:z5.number().int().nonnegative().max(STUDIO_LIMITS.frameIndexExclusive-1).optional()});import{z as z6}from"zod";var studioSourceBundleSha256=(input)=>studioHash("slopcamera.studio-source-bundle/v1",parseStudioSourceBundle(input));var studioJobSha256=(input)=>studioHash("slopcamera.studio-job/v1",parseStudioJob(input));var studioRuntimeSha256=(input)=>studioHash("slopcamera.studio-runtime/v1",parseStudioRuntimeIdentity(input));function studioOutputPath(output,frame){if(output.kind==="sequence"){studioRequire(Number.isInteger(frame)&&frame>=0&&frame<STUDIO_LIMITS.frameIndexExclusive,"Sequence output requires an admitted frame index.");return output.pathPattern.replace("%06d",String(frame).padStart(6,"0"))}studioRequire(frame===undefined,"Only sequence outputs have a frame index.");return output.path}function declaration(job2){const files=[];const directories=[];for(const output of job2.outputs){if(output.kind==="directory")directories.push({id:output.id,path:output.path});else if(output.kind==="file")files.push({id:output.id,path:output.path});else{studioRequire(job2.render!==undefined,"Sequence requires a render interval.");for(let frame=job2.render.startFrame;frame<job2.render.endFrameExclusive;frame++)files.push({id:output.id,path:studioOutputPath(output,frame),frame})}}studioRequire(files.length+directories.length<=job2.limits.maximumOutputFiles,"Declared outputs exceed the output-file budget.");assertDistinctPaths([...files,...directories].map((item)=>item.path));return{files,directories}}function requiredCapabilities(bundle,job2){const values=new Set([bundle.entrypoint.kind==="blend"?"blend-authoring":"python-authoring",job2.stage]);if(job2.engine.engine==="blender"&&job2.engine.device==="gpu"&&job2.stage==="render")values.add("gpu-render");for(const output of job2.outputs){if(output.kind==="sequence")values.add("image-sequence");if(output.role==="beauty"&&["mov","mp4","webm"].includes(output.format))values.add("beauty-video");if(output.role==="model")values.add("model-export");if(output.role==="auxiliary")values.add("auxiliary-passes");if(output.role==="simulation-cache")values.add("native-cache");if(output.role==="audio")values.add("audio-output")}return[...values].sort(studioCompare)}function derivePlan(input){const{bundle,job:job2,runtime:runtime2}=input;const bundleSha256=studioSourceBundleSha256(bundle),jobSha256=studioJobSha256(job2);studioRequire(bundleSha256===job2.bundleSha256,"Job does not bind the exact source bundle.");studioRequire(bundle.engine===job2.engine.engine&&(runtime2===undefined||runtime2.engine===bundle.engine),"Bundle, job, and runtime must use one engine.");studioRequire(bundle.entrypoint.kind!=="blend"||job2.stage==="render","Blend entrypoints support render stage only; explicit Python authoring owns builds and bakes.");const declared=declaration(job2),required=requiredCapabilities(bundle,job2);const capabilityChecks=required.map((name)=>({name,support:runtime2===undefined?"unbound":runtime2.capabilities.find((item)=>item.name===name)?.support??"unverified"}));const readiness=runtime2===undefined?"runtime-unbound":capabilityChecks.some((item)=>item.support==="unavailable")?"capability-unavailable":capabilityChecks.some((item)=>item.support==="unverified")?"capability-unverified":"authorization-required";const body={kind:"slopcamera.studio-plan",schemaVersion:1,bundle,job:job2,...runtime2===undefined?{}:{runtime:runtime2,runtimeSha256:studioRuntimeSha256(runtime2)},bundleSha256,jobSha256,sourceBytes:bundle.files.reduce((total,file)=>total+file.bytes,0),frameCount:job2.render===undefined?0:job2.render.endFrameExclusive-job2.render.startFrame,outputCount:{minimum:declared.files.length+declared.directories.length,maximum:declared.directories.length===0?declared.files.length:job2.limits.maximumOutputFiles},requiredCapabilities:required,capabilityChecks,readiness};return{...body,planSha256:studioHash("slopcamera.studio-plan/v1",body)}}var plan=z6.strictObject({kind:z6.literal("slopcamera.studio-plan"),schemaVersion:z6.literal(1),bundle:StudioSourceBundleSchema,job:StudioJobSchema,runtime:StudioRuntimeIdentitySchema.optional(),runtimeSha256:StudioDigestSchema.optional(),bundleSha256:StudioDigestSchema,jobSha256:StudioDigestSchema,planSha256:StudioDigestSchema,sourceBytes:z6.number().int().safe().nonnegative().max(STUDIO_LIMITS.sourceBytes),frameCount:z6.number().int().nonnegative().max(STUDIO_LIMITS.frames),outputCount:z6.strictObject({minimum:z6.number().int().positive().max(STUDIO_LIMITS.outputFiles),maximum:z6.number().int().positive().max(STUDIO_LIMITS.outputFiles)}),requiredCapabilities:z6.array(StudioCapabilityNameSchema).min(1).max(12),capabilityChecks:z6.array(z6.strictObject({name:StudioCapabilityNameSchema,support:z6.enum(["available","unavailable","unverified","unbound"])})).min(1).max(12),readiness:z6.enum(["runtime-unbound","capability-unavailable","capability-unverified","authorization-required"])}).superRefine((value,context)=>{try{const derived=derivePlan({bundle:value.bundle,job:value.job,...value.runtime===undefined?{}:{runtime:value.runtime}});if(canonicalJson(derived)!==canonicalJson(value))context.addIssue({code:"custom",message:"Studio plan differs from its canonical source, job, runtime, or admission derivation."})}catch(error){context.addIssue({code:"custom",message:error instanceof Error?error.message:"Invalid studio plan."})}});var StudioPlanSchema=studioDocument(plan,"studio plan");var parseStudioPlan=(input)=>parseStudioValue(StudioPlanSchema,input);function planStudioJob(input){const captured=parseStudioValue(studioDocument(z6.strictObject({bundle:StudioSourceBundleSchema,job:StudioJobSchema,runtime:StudioRuntimeIdentitySchema.optional()}),"studio planning input"),input);return parseStudioPlan(derivePlan(captured))}var failure2=z6.strictObject({code:z6.enum(["subprocess","cancelled","deadline","validation","custody","publication","unavailable"]),message:z6.string().min(1).max(2048)});var receiptCommon={kind:z6.literal("slopcamera.studio-receipt"),schemaVersion:z6.literal(1),jobId:z6.string().regex(/^studio_[a-zA-Z0-9][a-zA-Z0-9_-]{0,120}$/u),attemptId:z6.string().regex(/^attempt_[a-zA-Z0-9][a-zA-Z0-9_-]{0,120}$/u),planSha256:StudioDigestSchema,bundleSha256:StudioDigestSchema,jobSha256:StudioDigestSchema,runtime:StudioRuntimeIdentitySchema,runtimeSha256:StudioDigestSchema,startedAt:z6.iso.datetime({offset:true}),finishedAt:z6.iso.datetime({offset:true}),outputs:z6.array(StudioOutputArtifactSchema).max(STUDIO_LIMITS.outputFiles)};var receiptShape=z6.discriminatedUnion("state",[z6.strictObject({...receiptCommon,state:z6.literal("succeeded"),custody:z6.literal("closed"),exitCode:z6.literal(0)}),z6.strictObject({...receiptCommon,state:z6.literal("failed"),custody:z6.literal("closed"),exitCode:z6.number().int().min(-255).max(255).nullable(),failure:failure2}),z6.strictObject({...receiptCommon,state:z6.literal("unknown-custody"),custody:z6.literal("unknown"),exitCode:z6.number().int().min(-255).max(255).nullable(),failure:failure2})]).superRefine((value,context)=>{if(studioRuntimeSha256(value.runtime)!==value.runtimeSha256)context.addIssue({code:"custom",message:"Receipt runtime digest differs from its evidence."});if(Date.parse(value.finishedAt)<Date.parse(value.startedAt))context.addIssue({code:"custom",message:"Receipt finishes before it starts."});try{assertDistinctPaths(value.outputs.map((item)=>item.path))}catch{context.addIssue({code:"custom",message:"Receipt output paths collide."})}if(value.outputs.reduce((sum,item)=>sum+item.bytes,0)>STUDIO_LIMITS.outputBytes)context.addIssue({code:"custom",message:"Receipt exceeds the global output byte bound."})});var receipt=receiptShape.transform((value)=>({...value,outputs:[...value.outputs].sort((a,b)=>studioCompare(a.path,b.path))})).pipe(receiptShape);var StudioReceiptSchema=studioDocument(receipt,"studio receipt");var parseStudioReceipt=(input)=>parseStudioValue(StudioReceiptSchema,input);function validateStudioReceipt(input){const captured=parseStudioValue(studioDocument(z6.strictObject({plan:StudioPlanSchema,receipt:StudioReceiptSchema}),"studio receipt validation input"),input);const{plan:plan2,receipt:receipt2}=captured;studioRequire(receipt2.jobId===plan2.job.jobId&&receipt2.planSha256===plan2.planSha256&&receipt2.jobSha256===plan2.jobSha256&&receipt2.bundleSha256===plan2.bundleSha256,"Receipt identity differs from the exact planned job.");studioRequire(plan2.runtime!==undefined&&receipt2.runtimeSha256===plan2.runtimeSha256&&canonicalJson(receipt2.runtime)===canonicalJson(plan2.runtime),"Execution receipt requires the exact planned runtime binding.");if(receipt2.state==="succeeded")studioRequire(plan2.readiness==="authorization-required","A successful receipt requires available observed runtime capabilities; consent remains host-owned.");studioRequire(receipt2.outputs.length<=plan2.job.limits.maximumOutputFiles&&receipt2.outputs.reduce((total,output)=>total+output.bytes,0)<=plan2.job.limits.maximumOutputBytes,"Receipt exceeds the planned output budget.");const declared=declaration(plan2.job),expected=new Map(declared.files.map((file)=>[file.path,file]));const counts=new Map;for(const artifact of receipt2.outputs){const specification=plan2.job.outputs.find((output)=>output.id===artifact.outputId);studioRequire(specification!==undefined,"Receipt contains an undeclared output ID.");studioRequire(artifact.role===specification.role&&artifact.format===specification.format,"Receipt output role or format differs from its declaration.");if(specification.kind==="directory"){studioRequire(artifact.path.startsWith(`${specification.path}/`)&&artifact.frame===undefined,"Cache artifact must be a file inside its declared directory.")}else{const wanted=expected.get(artifact.path);studioRequire(wanted!==undefined&&wanted.id===artifact.outputId&&artifact.frame===wanted.frame,"Receipt contains an undeclared file or incorrect frame index.");expected.delete(artifact.path)}counts.set(artifact.outputId,(counts.get(artifact.outputId)??0)+1)}if(receipt2.state==="succeeded"){studioRequire(expected.size===0&&declared.directories.every((directory)=>(counts.get(directory.id)??0)>0),"Successful receipt must cover every declared file, frame, and cache directory.")}return deepFreezeJson(receipt2)}function inspectStudioBundle(input){const bundle=parseStudioSourceBundle(input);return deepFreezeJson({engine:bundle.engine,entrypoint:bundle.entrypoint,bundleSha256:studioSourceBundleSha256(bundle),files:bundle.files.length,sourceBytes:bundle.files.reduce((total,file)=>total+file.bytes,0),executed:false,dependencyDiscovery:"explicit-files-only"})}function inspectStudioPlan(input){const plan2=parseStudioPlan(input);return deepFreezeJson({jobId:plan2.job.jobId,stage:plan2.job.stage,engine:plan2.job.engine.engine,planSha256:plan2.planSha256,sourceBytes:plan2.sourceBytes,frameCount:plan2.frameCount,outputCount:plan2.outputCount,readiness:plan2.readiness,capabilityChecks:plan2.capabilityChecks,execution:plan2.job.execution,limits:plan2.job.limits,executed:false,outputs:plan2.job.outputs.map((output)=>({id:output.id,role:output.role,format:output.format,kind:output.kind,path:output.kind==="sequence"?output.pathPattern:output.path}))})}import{z as z7}from"zod";var VISUAL_STYLE_IDS=Object.freeze(["silent-actuality","noir-35mm","documentary-16mm","super8-color","theatrical-cel","watercolor-storybook","ink-sketch","rubber-hose","midcentury-limited","cut-paper","stopmotion-clay","pixel-art","rotoscope","engraving","math-explainer","isometric-design","clean-motion"]);var VisualStyleIdSchema=z7.enum(VISUAL_STYLE_IDS);var DirectionTextSchema=z7.string().trim().min(1).max(512);var DirectionListSchema=z7.array(DirectionTextSchema).min(1).max(12).readonly();var ColorSchema=z7.string().regex(/^#[0-9a-f]{6}$/u);var UnitSchema=z7.number().finite().min(0).max(1);var VisualStyleProfileSchema=z7.strictObject({id:VisualStyleIdSchema,name:z7.string().trim().min(1).max(80),family:z7.enum(["historical-film","drawn-animation","tactile-animation","digital-animation","explanation"]),summary:DirectionTextSchema,palette:z7.strictObject({background:ColorSchema,ink:ColorSchema,paper:ColorSchema,primary:ColorSchema,secondary:ColorSchema,accent:ColorSchema}).readonly(),cadence:z7.strictObject({fps:z7.number().int().min(1).max(120),exposureFrames:z7.number().int().min(1).max(12),cameraExposureFrames:z7.number().int().min(1).max(12),shutterAngle:z7.number().finite().min(0).max(360)}).readonly(),delivery:z7.strictObject({width:z7.number().int().min(1080).max(8192).multipleOf(2),height:z7.number().int().min(1080).max(8192).multipleOf(2),sampling:z7.enum(["smooth","nearest"]),logicalGrid:z7.strictObject({width:z7.number().int().min(16).max(1024),height:z7.number().int().min(16).max(1024)}).readonly().optional()}).superRefine((delivery,context)=>{const grid=delivery.logicalGrid;if(delivery.sampling==="nearest"){if(grid===undefined||delivery.width%grid.width!==0||delivery.height%grid.height!==0||delivery.width/grid.width!==delivery.height/grid.height){context.addIssue({code:"custom",message:"Nearest sampling requires a logical grid with one exact integer scale on both axes."})}}else if(grid!==undefined){context.addIssue({code:"custom",message:"A logical pixel grid requires nearest sampling."})}}).readonly(),shape:DirectionListSchema,material:DirectionListSchema,camera:DirectionListSchema,finishing:z7.strictObject({grain:UnitSchema,grainSizePxAt1080:z7.number().finite().min(0).max(8),halation:UnitSchema,gateWeavePxAt1080:z7.number().finite().min(0).max(4),vignette:UnitSchema,saturation:z7.number().finite().min(0).max(2),contrast:z7.number().finite().min(0.5).max(2)}).readonly(),avoid:DirectionListSchema,acceptance:DirectionListSchema}).readonly();var cleanFinish=Object.freeze({grain:0,grainSizePxAt1080:0,halation:0,gateWeavePxAt1080:0,vignette:0,saturation:1,contrast:1});var cinema=Object.freeze({fps:24,exposureFrames:1,cameraExposureFrames:1,shutterAngle:180});var onTwos=Object.freeze({fps:24,exposureFrames:2,cameraExposureFrames:1,shutterAngle:0});var uhd=Object.freeze({width:3840,height:2160,sampling:"smooth"});var academy=Object.freeze({width:2880,height:2160,sampling:"smooth"});function profile(value){return VisualStyleProfileSchema.parse(value)}var VISUAL_STYLE_PROFILES=Object.freeze([profile({id:"silent-actuality",name:"Silent actuality",family:"historical-film",summary:"An observed street or everyday action reconstructed with early monochrome photography, a restrained camera, and a coherent period world.",palette:{background:"#171714",ink:"#080909",paper:"#e2dfce",primary:"#a7a797",secondary:"#666b63",accent:"#c6c3ad"},cadence:{fps:24,exposureFrames:1,cameraExposureFrames:1,shutterAngle:180},delivery:academy,shape:["Stage a deep street with foreground, middle distance, and a readable vanishing point.","Choose one place and decade; match silhouettes of buildings, signs, transport, and clothing to that brief."],material:["Use rough masonry, imperfect glazing, worn painted timber, and matte clothing; build surface detail before aging the image.","Separate sky, pale facades, dark coats, and street into readable monochrome values."],camera:["Use an eye-level locked tripod or a very slow mechanically plausible pan; let the world supply movement.","Keep the 4:3 gate visible through composition; introduce only low-amplitude frame-wise registration variation."],finishing:{grain:0.16,grainSizePxAt1080:1.4,halation:0.04,gateWeavePxAt1080:0.65,vignette:0.18,saturation:0,contrast:1.08},avoid:["Do not equate historical film with sepia, a black border, or repeated scratch overlays.","Avoid glossy CGI, contemporary props, drone paths, fake archive dates, and unqualified documentary claims."],acceptance:["With texture disabled, the world and framing still read as the selected period.","Moving subjects remain legible; grain changes per exposed frame, while scratches and weave do not overpower the action.","Review full-resolution surfaces and motion; label the result as an authored reconstruction, not found footage."]}),profile({id:"noir-35mm",name:"35 mm noir",family:"historical-film",summary:"A dramatic monochrome city or interior organized by motivated pools of light, negative space, and strong depth.",palette:{background:"#101316",ink:"#040608",paper:"#f1efe5",primary:"#aeb5b7",secondary:"#475159",accent:"#d6d4c9"},cadence:cinema,delivery:academy,shape:["Use one strong silhouette, diagonal blocking, and a deep foreground-to-background relationship.","Let window mullions, stairs, awnings, and practical lights produce motivated graphic shapes."],material:["Give masonry, wet pavement, wool, skin, and glass distinct tonal responses.","Protect luminous highlights while holding enough shadow detail to read the principal action."],camera:["Use a deliberate dolly, fixed composition, or motivated low angle; decide the point of attention before moving.","Let occlusion and lighting reveal the subject instead of adding random camera shake."],finishing:{grain:0.08,grainSizePxAt1080:0.9,halation:0.08,gateWeavePxAt1080:0.15,vignette:0.16,saturation:0,contrast:1.2},avoid:["Do not crush every shadow or turn wet streets into mirror-like plastic.","Avoid neon cyberpunk color, arbitrary fog, or broad blur that erases the staged action."],acceptance:["The eye finds the subject in a thumbnail and still sees the intended shadow detail at full size.","Light sources explain the shadows and reflections; the complete motion preserves this logic."]}),profile({id:"documentary-16mm",name:"16 mm documentary",family:"historical-film",summary:"A patient observational view with plausible handheld weight, tactile surfaces, restrained color, and the texture of a small film gauge.",palette:{background:"#343731",ink:"#202824",paper:"#d8d2af",primary:"#a7996e",secondary:"#66807c",accent:"#b86644"},cadence:cinema,delivery:academy,shape:["Stage a specific ordinary activity with incidental depth and asymmetry.","Resolve period street furniture, signage, vehicles, and building details before styling."],material:["Use natural light, restrained saturation, rough plaster, faded paint, and uneven reflectance.","Keep highlight roll-off soft and retain believable material scale in close and distant surfaces."],camera:["Use a human-height view, slow reframing, and very small smooth operator drift.","Pick a plausible lens and viewpoint; foreground parallax must follow camera translation."],finishing:{grain:0.13,grainSizePxAt1080:1.3,halation:0.07,gateWeavePxAt1080:0.3,vignette:0.12,saturation:0.72,contrast:0.96},avoid:["Avoid pristine game-engine materials, evenly spaced procedural city blocks, and modern billboard typography in a period scene.","Do not simulate handheld photography with frantic random movement or apply grain that swims independently of frames."],acceptance:["The untreated scene has convincing period objects, human scale, lighting, and material variation.","Camera drift feels operated, the principal activity stays readable, and small highlights do not clip broadly."]}),profile({id:"super8-color",name:"Super 8 color memory",family:"historical-film",summary:"An intimate small-gauge color scene with warm highlights, gentle exposure breathing, and grounded everyday details.",palette:{background:"#302c28",ink:"#263333",paper:"#ead5a4",primary:"#c59a60",secondary:"#698c80",accent:"#bd6650"},cadence:{fps:18,exposureFrames:1,cameraExposureFrames:1,shutterAngle:180},delivery:academy,shape:["Favor intimate framing, simple gestures, and irregular lived-in surroundings.","Choose one dominant warm/cool relationship and reserve bright color for a small focal object."],material:["Use fabric, foliage, skin, glass, and paint with visible but gentle surface detail.","Keep a soft tonal shoulder and restrained shadow color; emulate a chosen look without claiming an exact film-stock match."],camera:["Keep movement light and human, with short settled moments between reframes.","Author a complete first and last composition; use exposure breathing sparingly and continuously."],finishing:{grain:0.18,grainSizePxAt1080:1.8,halation:0.12,gateWeavePxAt1080:0.55,vignette:0.17,saturation:0.84,contrast:0.93},avoid:["Avoid looping light leaks, damaged-film overlays in every frame, and extreme orange skin.","Do not confuse a 4K delivery raster with invented high-frequency detail inside the small-gauge image."],acceptance:["The frame reads as an intimate photograph before grain is added.","The 18 fps delivery has intentional motion cadence, retained subject detail, and no sudden exposure flashes."]}),profile({id:"theatrical-cel",name:"Theatrical cel animation",family:"drawn-animation",summary:"Late-1980s theatrical anime craft: dimensional layouts, intricate painted environments, decisive ink contours, and controlled cel shadows.",palette:{background:"#101e2c",ink:"#111626",paper:"#c1d3d0",primary:"#396275",secondary:"#9b745e",accent:"#ec593f"},cadence:onTwos,delivery:uhd,shape:["Construct convincing perspective and a clear silhouette with designed mechanical and architectural details.","Separate painted background planes from animated foreground cels; reserve high-detail edges for the focal region."],material:["Use varied ink weight, two or three purposeful shadow shapes, and opaque painted color.","Give background paintings richer texture and atmospheric depth while keeping moving cels clean."],camera:["Use a composed layout, measured multiplane translation, and a clear action axis.","Hold character drawings on twos by default; reserve ones, smears, or impact drawings for specifically authored actions."],finishing:{grain:0.025,grainSizePxAt1080:0.6,halation:0.045,gateWeavePxAt1080:0,vignette:0.04,saturation:0.95,contrast:1.06},avoid:["Avoid generic glossy 3D with a black outline, arbitrary neon, and uniformly detailed noise.","Do not copy a film's characters, logos, or specific shot; translate the production principles into an original scene."],acceptance:["Silhouette, perspective, ink, cel-shadow grouping, and painted depth remain convincing without grain.","Camera motion stays smooth while exposed character poses hold intentionally; motion does not slide joints or contours."]}),profile({id:"watercolor-storybook",name:"Watercolor storybook",family:"drawn-animation",summary:"A luminous painted illustration brought to life through selective movement, pigment pooling, and breathing paper space.",palette:{background:"#eee8d6",ink:"#575b57",paper:"#fff8e6",primary:"#709487",secondary:"#8ba9b6",accent:"#c67757"},cadence:onTwos,delivery:uhd,shape:["Compose broad washed silhouettes, irregular contours, and generous unpainted paper.","Design a limited set of focal edges; allow distant forms to dissolve into larger color masses."],material:["Create translucent overlapping washes with edge pooling and subtle fixed paper tooth.","Bind pigment texture to the painted object rather than regenerating all texture on every frame."],camera:["Use a quiet tableau or shallow multiplane drift.","Animate one narrative gesture at a time with soft anticipation and a settled pause."],finishing:{...cleanFinish,grain:0.025,grainSizePxAt1080:0.6,saturation:0.75},avoid:["Avoid wet-looking plastic, uniform Gaussian blur, and a noise filter that obscures the drawing.","Do not let paper texture crawl or painted edges shimmer between held drawings."],acceptance:["Paper, transparent wash, pooled pigment, and focal edge hierarchy are individually visible.","Texture follows objects and motion leaves the composition calm and readable."]}),profile({id:"ink-sketch",name:"Living ink sketch",family:"drawn-animation",summary:"An expressive drawing with intentional line economy, selective hatching, and restrained redraw variation.",palette:{background:"#eee9dc",ink:"#282c2d",paper:"#fbf5e5",primary:"#566565",secondary:"#acaaa0",accent:"#b86742"},cadence:{...onTwos,exposureFrames:3},delivery:uhd,shape:["Establish contour, gesture, and hierarchy before adding crosshatching.","Let open contours and deliberate omissions carry light; vary line weight by depth and emphasis."],material:["Use a small set of pressure-sensitive strokes with directionally consistent hatching.","Register each redraw to the same underlying shape so line boil does not deform the subject."],camera:["Keep a stable drawing surface or make one legible pan across the sheet.","Use held poses, brief drawing reveals, and small secondary gestures."],finishing:{...cleanFinish,saturation:0.25},avoid:["Avoid vector-perfect uniform outlines, evenly applied scribble noise, and permanent jitter of every point.","Do not add decorative hatching over labels or important negative space."],acceptance:["The gesture reads at thumbnail size and line hierarchy remains clear at full resolution.","Redraw variation is subtle and stable within each exposure; no unintended frame-to-frame crawling."]}),profile({id:"rubber-hose",name:"Rubber-hose cartoon",family:"drawn-animation",summary:"Elastic early-cartoon movement with clean silhouettes, rhythmic arcs, anticipation, and appealing graphic staging.",palette:{background:"#d5cdbb",ink:"#252523",paper:"#f4eddc",primary:"#5f655d",secondary:"#96998a",accent:"#b28355"},cadence:onTwos,delivery:academy,shape:["Build readable bean-shaped torsos, curved hose limbs, and large simple hands and feet.","Stage clear silhouettes with ample space for arcs, squash, stretch, and overlap."],material:["Use opaque flat ink and warm paper-like backgrounds with sparse decorative detail.","Keep line weight and facial registration stable as the body deforms."],camera:["Favor a fixed theatrical view and whole-body acting.","Author anticipation, action, overshoot, and settle; preserve foot contact during planted poses."],finishing:{grain:0.04,grainSizePxAt1080:0.8,halation:0,gateWeavePxAt1080:0.08,vignette:0.06,saturation:0.15,contrast:1.05},avoid:["Avoid sliding feet, unexplained limb length changes, and constant sine-wave movement without acting beats.","Use original characters and props rather than familiar cartoon mascots."],acceptance:["Anticipation, elastic action, follow-through, and a settled pose can be identified in the complete clip.","Feet and hands make intentional contact, and facial features remain readable through deformation."]}),profile({id:"midcentury-limited",name:"Mid-century limited animation",family:"drawn-animation",summary:"Modernist graphic staging with asymmetric shapes, flat color, spare backgrounds, and selective expressive movement.",palette:{background:"#e7dab6",ink:"#263739",paper:"#f6edd5",primary:"#467f82",secondary:"#c79744",accent:"#cf6049"},cadence:{...onTwos,exposureFrames:3},delivery:uhd,shape:["Design large asymmetric color blocks and stylized angular silhouettes.","Use negative space and economical drawn details to direct attention."],material:["Favor matte gouache-like flat fields with slight fixed paper texture.","Keep a few controlled overlaps and color relationships rather than realistic shading."],camera:["Treat the frame as a designed poster; use purposeful pans or held compositions.","Animate the expressive part while holding the body or background; time actions around clear pauses."],finishing:{...cleanFinish,saturation:0.88},avoid:["Avoid gradients on every shape, generic geometric confetti, and continual motion of every object.","Limited animation still needs poses, timing, and staging; do not substitute random bobbing."],acceptance:["The still frame reads as a coherent graphic design with a dominant focal shape.","Selective motion clarifies the action and the held regions feel intentional."]}),profile({id:"cut-paper",name:"Layered cut paper",family:"tactile-animation",summary:"A handmade collage with meaningful overlaps, irregular cut edges, restrained paper relief, and mechanical articulation.",palette:{background:"#dad2b9",ink:"#374a43",paper:"#f6edce",primary:"#597d66",secondary:"#d2a45b",accent:"#bf5d45"},cadence:onTwos,delivery:uhd,shape:["Build a small number of nested, readable paper silhouettes and visible articulation pivots.","Let overlapping layers explain the spatial construction."],material:["Use fixed fiber texture, small edge irregularities, and soft contact shadows proportional to paper thickness.","Keep lighting direction consistent across every layer."],camera:["Use a mostly frontal rostrum view with restrained depth changes.","Move paper pieces around authored pivots and stepped poses; keep texture attached to each piece."],finishing:{...cleanFinish,saturation:0.84},avoid:["Avoid inflated bevels, thick plastic shadows, sliding textures, and smooth stretchy limbs.","Do not make the whole scene look like a flat vector image with one global drop shadow."],acceptance:["Separate paper layers, their contact shadows, and edge character are readable.","Articulation, occlusion, and paper texture remain consistent throughout motion."]}),profile({id:"stopmotion-clay",name:"Clay stop motion",family:"tactile-animation",summary:"A miniature tactile set with shaped clay, practical lighting, intentional pose increments, and believable physical contact.",palette:{background:"#283f47",ink:"#22333a",paper:"#edd6a9",primary:"#719b92",secondary:"#d8a451",accent:"#c16b54"},cadence:{...onTwos,cameraExposureFrames:2},delivery:uhd,shape:["Design a strong hand-shaped silhouette with slightly irregular proportions and tangible seams.","Stage a small set whose miniature scale is supported by prop size and limited depth."],material:["Use soft diffuse clay with sparse tool marks and local roughness variation.","Create contact shadows and controlled depth of field; avoid uniformly shiny surfaces."],camera:["Use a physically plausible fixed miniature camera or slow incremented move.","Author weight shifts and stepped poses, preserving planted contacts and deliberate squash."],finishing:{...cleanFinish,grain:0.018,grainSizePxAt1080:0.6,vignette:0.05,saturation:0.86},avoid:["Avoid frictionless interpolation, floating props, animated texture noise, and glossy toy materials.","Do not call procedural wobble a physical simulation or claim a practical shoot."],acceptance:["Clay surface, contact, miniature lighting, and stepped posing all contribute to the treatment.","No planted foot slides, prop intersections, or focus changes conceal the principal action."]}),profile({id:"pixel-art",name:"Authored pixel art",family:"digital-animation",summary:"A deliberately drawn low-resolution world with a limited palette, designed pixel clusters, discrete poses, and exact integer enlargement.",palette:{background:"#182633",ink:"#111c2d",paper:"#e6dcc1",primary:"#5c9c83",secondary:"#d2aa63",accent:"#d96658"},cadence:{...onTwos,cameraExposureFrames:2},delivery:{...uhd,sampling:"nearest",logicalGrid:{width:320,height:180}},shape:["Draw on the declared 320 by 180 logical grid with readable clusters and deliberate stair-step diagonals.","Separate foreground sprites, midground tiles, and distant parallax planes with value and palette."],material:["Use a bounded palette with authored highlight and shadow ramps.","Place dithering only where it explains a gradient or material; preserve crisp one-pixel accents."],camera:["Quantize sprite positions and camera translation to logical pixels.","Use authored pose changes and integer parallax offsets; export with a single nearest-neighbor integer scale."],finishing:cleanFinish,avoid:["Avoid anti-aliasing, postprocess blur, noninteger scaling, smooth subpixel translation, and a pixelation filter over an unrelated image.","Do not add film grain or screen simulation by default; those are separate authored treatments."],acceptance:["Each logical pixel maps to an equal, crisp 12 by 12 output block in the recommended 3840 by 2160 delivery.","Palette, sprite clusters, occlusion, and pose changes read clearly throughout the full clip."]}),profile({id:"rotoscope",name:"Painterly rotoscope",family:"drawn-animation",summary:"Naturalistic human movement translated into stable painted masses, expressive contour, and deliberately simplified light.",palette:{background:"#243747",ink:"#172934",paper:"#e7d8bc",primary:"#798b82",secondary:"#b08a69",accent:"#dc8561"},cadence:onTwos,delivery:uhd,shape:["Preserve anatomical landmarks, weight, and the readable gesture from an authorized reference or deliberately authored motion.","Simplify the figure into consistent large painted masses instead of tracing every visible texture."],material:["Keep controlled contour variation and two or three light groups attached to the subject.","Let brush texture follow body surfaces and maintain facial landmark identity."],camera:["Use a stable action axis and clear full-body or close gesture framing.","Retain natural timing and planted contact; author redraw variation per exposure rather than per display refresh."],finishing:{...cleanFinish,grain:0.012,grainSizePxAt1080:0.5,saturation:0.78},avoid:["Avoid edge-detection filters, flickering facial features, crawling paint, and rubbery anatomical drift.","Do not describe a scene as reference-traced unless an actual authorized reference was used."],acceptance:["Weight shifts and gestures remain plausible, with stable anatomy and consistent light grouping.","Contours and texture hold coherently between exposures without accidental shimmer."]}),profile({id:"engraving",name:"Engraved illustration",family:"drawn-animation",summary:"A precise monochrome illustration whose curved hatching, contour hierarchy, and open paper explain form.",palette:{background:"#e8dfc8",ink:"#25322f",paper:"#f6ecd2",primary:"#52645b",secondary:"#a5ab92",accent:"#9b653d"},cadence:cinema,delivery:uhd,shape:["Use a clear structural outline and reserve fine marks for material and curvature.","Organize hatching into form-following bands with intentional light gaps."],material:["Render crisp fine ink with sparse paper texture and deliberate crosshatch density.","Keep line spacing wide enough to survive the actual delivery raster and video encoding."],camera:["Use a slow explanatory reveal, rotation, or camera move with stable line registration.","If the underlying form moves, move its hatch coordinates with it rather than sampling new screen-space noise."],finishing:{...cleanFinish,saturation:0.08},avoid:["Avoid uniform screen-space hatch overlays, moire, crushed dense line fields, and illegible decorative labels.","Do not present an artistic mechanical drawing as dimensionally verified engineering."],acceptance:["Hatch direction describes form and fine lines remain distinct in a full-resolution delivery frame.","Motion does not create moire or crawl that overwhelms the subject."]}),profile({id:"math-explainer",name:"Mathematical explanation",family:"explanation",summary:"A lucid visual argument built from persistent objects, purposeful color, geometric construction, and time to understand each step.",palette:{background:"#101c2a",ink:"#e0e8ed",paper:"#f4f1de",primary:"#59b6d5",secondary:"#e4bf63",accent:"#df7e82"},cadence:{fps:30,exposureFrames:1,cameraExposureFrames:1,shutterAngle:0},delivery:uhd,shape:["Choose one mathematical claim; construct the relevant objects before transforming or comparing them.","Use persistent object identities, meaningful axes, clear labels, and whitespace around the argument."],material:["Use clean vector strokes, restrained fills, and consistent notation with semantic colors.","Keep line weight, label size, and contrast readable at the intended viewing size."],camera:["Hold a stable coordinate system unless a move directly explains the mathematics.","Sequence setup, construction, transformation, comparison, and conclusion with deliberate pauses."],finishing:cleanFinish,avoid:["Avoid decorative equations, unsupported numeric claims, arbitrary camera spins, and simultaneous unrelated reveals.","Do not substitute a familiar presenter's mascot or identity for the clarity of the explanation."],acceptance:["The illustrated relationship can be checked from the authored geometry or data.","Each color keeps its meaning, labels remain legible, and each logical step has a readable settled state."]}),profile({id:"isometric-design",name:"Isometric design study",family:"digital-animation",summary:"An orthographic object or system study with consistent construction, restrained materials, and diagram-like spatial clarity.",palette:{background:"#e6e5db",ink:"#2c4147",paper:"#f7f2df",primary:"#7b9c93",secondary:"#d1ad66",accent:"#c9674d"},cadence:cinema,delivery:uhd,shape:["Use one consistent orthographic projection and a clear assembled silhouette.","Make repeated parts and physical relationships coherent; reserve detail for the function being explained."],material:["Assign a small palette of believable matte, metal, glass, and rubber responses where appropriate.","Use soft contact shadows and explicit edges to separate adjacent parts."],camera:["Hold the projection and composition during assembly or an exploded view.","Move components along meaningful axes and include a settled assembled and separated state."],finishing:cleanFinish,avoid:["Avoid inconsistent vanishing points, floating unconnected parts, excessive bevels, and candy-like materials on every surface.","Do not imply fabrication readiness without dimensional and engineering validation."],acceptance:["Part relationships remain legible during every movement, with no accidental intersection or cropped component.","The final composition has coherent scale, contact, projection, and material distinction."]}),profile({id:"clean-motion",name:"Editorial motion graphics",family:"digital-animation",summary:"Precise typography, controlled hierarchy, and economical choreography organized around a clear editorial message.",palette:{background:"#15232e",ink:"#f2eee1",paper:"#f8f4e9",primary:"#7cafb1",secondary:"#d4b87b",accent:"#dc795b"},cadence:{fps:30,exposureFrames:1,cameraExposureFrames:1,shutterAngle:0},delivery:uhd,shape:["Establish one typographic hierarchy, a consistent grid, and a small set of geometric devices.","Use exact supplied copy and arrange it to read in the intended viewing order."],material:["Use restrained flat color, carefully weighted rules, and crisp type at the final output size.","Reserve texture and gradients for an authored editorial purpose."],camera:["Treat the screen as a composition; use position, scale, and opacity only where they clarify hierarchy.","Separate entrance, readable hold, transition, and exit; stagger related objects rather than animating everything at once."],finishing:cleanFinish,avoid:["Avoid decorative dashboard cards, indiscriminate bounce, tiny type, and transitions that obscure supplied text.","Do not introduce slogans, data, or claims absent from the brief."],acceptance:["Exact copy reads comfortably in the delivery frame and remains visible long enough to understand.","Every move has a purpose, spacing stays controlled, and the first and last frames are intentionally composed."]})]);var profilesById=new Map(VISUAL_STYLE_PROFILES.map((item)=>[item.id,item]));function getVisualStyleProfile(id){return profilesById.get(VisualStyleIdSchema.parse(id))}function createVisualStyleDirection(id){const style=getVisualStyleProfile(id);const grid=style.delivery.logicalGrid;return[`${style.name}: ${style.summary}`,`Palette: ${Object.entries(style.palette).map(([role,color2])=>`${role} ${color2}`).join(", ")}.`,`Shape and staging: ${style.shape.join(" ")}`,`Materials and light: ${style.material.join(" ")}`,`Camera and motion: ${style.camera.join(" ")}`,`Cadence: ${style.cadence.fps} fps; subject exposure ${style.cadence.exposureFrames} frame(s); camera exposure ${style.cadence.cameraExposureFrames} frame(s); shutter angle ${style.cadence.shutterAngle} degrees.`,`Finishing targets: ${Object.entries(style.finishing).map(([name,value])=>`${name}=${value}`).join(", ")}. These are authoring controls, not a film-stock calibration.`,`Recommended delivery: ${style.delivery.width} by ${style.delivery.height}, ${style.delivery.sampling} sampling.${grid===undefined?"":` Author at ${grid.width} by ${grid.height} logical pixels and enlarge by exactly ${style.delivery.width/grid.width}.`}`,`Avoid: ${style.avoid.join(" ")}`,`Review actual frames and motion: ${style.acceptance.join(" ")}`].join(`
|
|
45
|
+
`)}var MAX_STYLE_TIME_US=3600000000;function checkInteger(value,name,maximum){if(!Number.isSafeInteger(value)||value<0||value>maximum){throw new RangeError(`${name} must be an integer from 0 through ${maximum}.`)}}function sampleVisualStyleExposure(timeUs,id){checkInteger(timeUs,"timeUs",MAX_STYLE_TIME_US);const{fps,exposureFrames,cameraExposureFrames}=getVisualStyleProfile(id).cadence;const frame=Math.floor(timeUs*fps/1e6)||0;const exposureIndex=Math.floor(frame/exposureFrames);const cameraFrame=Math.floor(frame/cameraExposureFrames)*cameraExposureFrames;return Object.freeze({frame,exposureIndex,exposureTimeUs:Math.ceil(exposureIndex*exposureFrames*1e6/fps),cameraTimeUs:Math.ceil(cameraFrame*1e6/fps)})}var VISUAL_STYLE_VARIATION_ALGORITHM="slopcamera.visual-style.fnv1a-avalanche-v1";function visualStyleFrameVariation(seed,frame,channel){checkInteger(seed,"seed",4294967295);checkInteger(frame,"frame",432000);if(typeof channel!=="string"||channel.length<1||channel.length>128||!/^[a-zA-Z0-9][a-zA-Z0-9._:/-]*$/u.test(channel)){throw new TypeError("channel must be 1\u2013128 ASCII letters, digits, dots, underscores, colons, slashes, or hyphens, beginning with a letter or digit.")}let value=(2166136261^seed)>>>0;for(let index=0;index<4;index+=1){value=Math.imul(value^frame>>>index*8&255,16777619)>>>0}for(let index=0;index<channel.length;index+=1){value=Math.imul(value^channel.charCodeAt(index),16777619)>>>0}value=Math.imul(value^value>>>16,2246822507)>>>0;value=Math.imul(value^value>>>13,3266489909)>>>0;return((value^value>>>16)>>>0)/4294967296}var slopcameraApi=Object.freeze({artifactSummary,builtInIcons,bundledSkillPath,checkDiagramFile,checkDrawingFile,drawingPageGeometry,DrawingValidationError,defineSlopcameraWorkflow,DiagramValidationError,generateSlopcameraImage,generateSlopcameraImageFile,generateSlopcameraImageGallery,generateSlopcameraIcon,slopcameraGatewayCredentialStatus,slopcameraMcpProtocolVersion,slopcameraMcpServerName,slopcameraMcpTools,slopcameraOperationRegistry,SlopcameraMcpToolRuntime,installSkill,lintDiagram,mcpMaximumRenderedPixels,mcpMaximumScale,mcpSourceByteLimit,parseDiagramSource,parseDiagramSpec,parseDrawingSource,readDiagramFile,readDrawingFile,renderDiagramFile,renderDrawing,renderDrawingFile,renderPng,renderSvg,resolveEdge,resolveDiagramSource,resolveStackLayout,runSlopcameraWorkflow,runMcpServer,searchSlopcameraOperations,serializeTldr,stackLayoutDefaults,starterDrawingSource,StackLayoutError,SlopcameraWorkflowError,vectorizeImage,WorkspaceBoundary,WorkspaceBoundaryError,executeSlopcameraOperation});var diagramApi=slopcameraApi;export{readDiagramFile,checkDiagramFile,renderDiagramFile,artifactSummary,SLOPCAMERA_VERSION,drawingProfile,drawingSourceByteLimit,drawingMinimumGlyphHeightPt,DrawingValidationError,drawingPageGeometry,parseDrawingSource,renderDrawing,readDrawingFile,checkDrawingFile,renderDrawingFile,starterDrawingSource,mcpSourceByteLimit,WorkspaceBoundaryError,WorkspaceBoundary,mcpMaximumScale,mcpMaximumRenderedPixels,slopcameraMcpTools,SlopcameraMcpToolRuntime,slopcameraMcpProtocolVersion,slopcameraMcpServerName,runMcpServer,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS,SlopcameraCapabilityEffectSchema,SlopcameraCapabilityResumeSchema,SlopcameraCapabilityResourceSchema,SlopcameraCapabilityPreparationSchema,SlopcameraCapabilityOperationSchema,SlopcameraCapabilityWorkflowSchema,SlopcameraCapabilityToolSchema,SlopcameraCapabilityCommandSchema,SlopcameraCapabilityQualificationSchema,SlopcameraCapabilityProfileSchema,SlopcameraCapabilityModuleDeclarationSchema,SlopcameraCapabilityModuleSchema,SlopcameraCapabilityManifestSchema,defineSlopcameraCapabilityModule,compileSlopcameraCapabilityManifest,parseSlopcameraCapabilityManifest,slopcameraIconSetMaximumMembers,slopcameraIconSetMaximumSetRounds,slopcameraIconSetDefaultSetRounds,slopcameraIconSetDefaultPool,slopcameraIconSetMaximumNameBytes,slopcameraIconSetSlugPattern,parseSlopcameraIconSetSpec,critiqueSlopcameraIconSet,generateSlopcameraIconSet,SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,createPortableSlopcameraCapabilityManifest,slopcameraPortableCapabilityManifest,slopcameraSceneGalleryLimits,SlopcameraSceneVariantsSchema,summarizeSceneVariantPatch,parseSlopcameraSceneVariants,planSlopcameraSceneGallery,STUDIO_LIMITS,StudioDigestSchema,StudioEngineSchema,StudioPathSchema,StudioSourceBundleSchema,parseStudioSourceBundle,StudioSourceSpaceSchema,StudioOutputRoleSchema,StudioOutputFormatSchema,StudioOutputInterpretationSchema,StudioOutputSpecSchema,StudioRenderSchema,StudioEngineOptionsSchema,StudioExecutionProfileSchema,StudioJobSchema,parseStudioJob,StudioCapabilityNameSchema,StudioRuntimeIdentitySchema,parseStudioRuntimeIdentity,StudioOutputArtifactSchema,studioSourceBundleSha256,studioJobSha256,studioRuntimeSha256,studioOutputPath,StudioPlanSchema,parseStudioPlan,planStudioJob,StudioReceiptSchema,parseStudioReceipt,validateStudioReceipt,inspectStudioBundle,inspectStudioPlan,VISUAL_STYLE_IDS,VisualStyleIdSchema,VisualStyleProfileSchema,VISUAL_STYLE_PROFILES,getVisualStyleProfile,createVisualStyleDirection,sampleVisualStyleExposure,VISUAL_STYLE_VARIATION_ALGORITHM,visualStyleFrameVariation,slopcameraApi,diagramApi};
|