@hraness/slopcamera 3.4.0 → 3.6.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.
Files changed (45) hide show
  1. package/README.md +32 -19
  2. package/apps/desktop/application/operation.ts +1 -1
  3. package/apps/desktop/cli/args.ts +3 -1
  4. package/apps/desktop/cli/commands.ts +22 -1
  5. package/apps/desktop/cli/help.ts +37 -13
  6. package/apps/desktop/cli/menubar-status.ts +155 -0
  7. package/apps/desktop/cli/menubar.ts +129 -101
  8. package/apps/desktop/dist/cli/main.js +334 -312
  9. package/apps/desktop/html-overlay/catalog.ts +1 -0
  10. package/apps/desktop/html-overlay/spatial.ts +10 -6
  11. package/apps/desktop/workflows/index.ts +1 -0
  12. package/dist/cli.js +7 -8
  13. package/dist/generate.js +1 -1
  14. package/dist/{index-qtn3s421.js → index-4z9y2wa2.js} +1 -1
  15. package/dist/{index-sy1n4zfy.js → index-fevf4ppk.js} +1 -1
  16. package/dist/{index-qry58nj2.js → index-mnp0bftq.js} +2 -2
  17. package/dist/index-nxtmy18t.js +3 -0
  18. package/dist/{index-abnk4h9c.js → index-qxfexvj3.js} +2 -2
  19. package/dist/index.js +1 -1
  20. package/dist/operations.js +1 -1
  21. package/dist/vectorize/worker.js +1 -1
  22. package/dist/workflow.js +1 -1
  23. package/docs/README.md +3 -3
  24. package/examples/studio/vgpu/README.md +1 -1
  25. package/package.json +5 -6
  26. package/skills/slopcamera/SKILL.md +4 -3
  27. package/skills/slopcamera/references/brand-illustrations.md +237 -0
  28. package/skills/slopcamera/references/gateway-media.md +1 -1
  29. package/skills/slopcamera/references/install.md +10 -1
  30. package/skills/slopcamera/references/patent-drawings.md +4 -4
  31. package/skills/slopcamera/references/support.md +1 -1
  32. package/skills/slopcamera/references/vectorization.md +3 -1
  33. package/src/cli.ts +13 -10
  34. package/src/code/public-operations.ts +9 -0
  35. package/src/generate.ts +44 -1
  36. package/src/generation-pricing.ts +217 -0
  37. package/src/mcp/tools.ts +4 -0
  38. package/src/operations.ts +4 -0
  39. package/src/support.ts +7 -1
  40. package/src/vectorize/command.ts +6 -1
  41. package/src/vectorize/supervisor.ts +23 -3
  42. package/src/vectorize/tool.ts +8 -4
  43. package/src/version.ts +1 -1
  44. package/src/visual-style.ts +4 -1
  45. package/dist/index-77fjfg9f.js +0 -3
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ import{__require}from"./index-80a36bc0.js";import{createHash,randomUUID}from"crypto";import{link,rm,writeFile}from"fs/promises";import{dirname,extname,resolve}from"path";class SlopcameraCloudError extends Error{code;constructor(code,message,options){super(`[${code}] ${message}`,options);this.name="SlopcameraCloudError";this.code=code}}var slopcameraImageModelPrices=Object.freeze({"openai/gpt-image-1":Object.freeze({kind:"tokens",textInputPerMillion:5000000,outputPerMillion:40000000,worstImage:250000,source:"https://developers.openai.com/api/docs/models/gpt-image-1"}),"openai/gpt-image-1-mini":Object.freeze({kind:"tokens",textInputPerMillion:2000000,outputPerMillion:8000000,worstImage:52000,source:"https://developers.openai.com/api/docs/models/gpt-image-1-mini"}),"openai/gpt-image-1.5":Object.freeze({kind:"tokens",textInputPerMillion:5000000,outputPerMillion:32000000,worstImage:200000,source:"https://developers.openai.com/api/docs/models/gpt-image-1.5"}),"recraft/recraft-v4.1-utility":Object.freeze({kind:"image",perImage:35000,source:"https://vercel.com/ai-gateway/models/recraft-v4.1-utility"})});var slopcameraPromptTokenAllowance=64;var slopcameraCostProvider="vercel-ai-gateway";function slopcameraImageModelPrice(model){return Object.hasOwn(slopcameraImageModelPrices,model)?slopcameraImageModelPrices[model]:undefined}function tokenCost(tokens,perMillion){return Math.ceil(tokens*perMillion/1e6)}function promptBytes(prompt){return Buffer.byteLength(prompt,"utf8")}function validTokenCount(value){return typeof value==="number"&&Number.isSafeInteger(value)&&value>=0}function estimatedLines(model,prompt,price){if(price.kind==="image"){return[{provider:slopcameraCostProvider,operation:`${model} image`,microUsd:price.perImage,basis:"estimated"}]}return[{provider:slopcameraCostProvider,operation:`${model} image output`,microUsd:price.worstImage,basis:"estimated"},{provider:slopcameraCostProvider,operation:`${model} text input`,microUsd:tokenCost(promptBytes(prompt)+slopcameraPromptTokenAllowance,price.textInputPerMillion),basis:"estimated"}]}function imageCallCosts(model,prompt,usage){const price=slopcameraImageModelPrice(model);if(price===undefined){return[{provider:slopcameraCostProvider,operation:`${model} image`,microUsd:0,basis:"unknown"}]}if(price.kind==="tokens"&&usage!==undefined&&validTokenCount(usage.inputTokens)&&validTokenCount(usage.outputTokens)&&usage.outputTokens>0){return[{provider:slopcameraCostProvider,operation:`${model} image output`,microUsd:tokenCost(usage.outputTokens,price.outputPerMillion),basis:"reported"},{provider:slopcameraCostProvider,operation:`${model} text input`,microUsd:tokenCost(usage.inputTokens,price.textInputPerMillion),basis:"reported"}]}return estimatedLines(model,prompt,price)}var slopcameraGatewayApiBaseUrl="https://ai-gateway.vercel.sh/v4/ai";var slopcameraImageModels=Object.freeze(["openai/gpt-image-1.5","recraft/recraft-v4.1-utility"]);var slopcameraResponseMediaTypes=Object.freeze(["image/png","image/jpeg","image/webp"]);var slopcameraMaximumPromptBytes=32*1024;var slopcameraMaximumRawImageBytes=64*1024*1024;var defaultGenerationTimeoutMs=5*60000;var maximumGenerationTimeoutMs=30*60000;var defaultMaximumGatewayResponseBytes=96*1024*1024;function invalidArgument(message){throw new SlopcameraCloudError("INVALID_ARGUMENT",message)}function credentialValue(value){if(value.length<16||value.length>16*1024||value.trim()!==value||/[^\x21-\x7e]/u.test(value)){throw new SlopcameraCloudError("AUTHENTICATION_REQUIRED","The selected Vercel AI Gateway credential is invalid.")}return value}function environment(injected){return injected??process.env}function resolveSlopcameraGatewayCredential(injected){const values=environment(injected);if(values.AI_GATEWAY_API_KEY!==undefined){return{source:"AI_GATEWAY_API_KEY",token:credentialValue(values.AI_GATEWAY_API_KEY)}}if(values.VERCEL_OIDC_TOKEN!==undefined){return{source:"VERCEL_OIDC_TOKEN",token:credentialValue(values.VERCEL_OIDC_TOKEN)}}throw new SlopcameraCloudError("AUTHENTICATION_REQUIRED","Set AI_GATEWAY_API_KEY or run Slopcamera through `vercel env run -- \u2026` with VERCEL_OIDC_TOKEN available.")}function slopcameraGatewayCredentialStatus(injected){const values=environment(injected);if(values.AI_GATEWAY_API_KEY!==undefined){credentialValue(values.AI_GATEWAY_API_KEY);return{available:true,source:"AI_GATEWAY_API_KEY"}}if(values.VERCEL_OIDC_TOKEN!==undefined){credentialValue(values.VERCEL_OIDC_TOKEN);return{available:true,source:"VERCEL_OIDC_TOKEN"}}return{available:false,source:null}}function validateModel(value){if(typeof value!=="string"||value.length<3||value.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(value)){invalidArgument("Model must be a bounded Vercel AI Gateway provider/model id.")}return value}function validatePrompt(value){if(typeof value!=="string"||value.trim().length===0||/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u.test(value)||Buffer.byteLength(value,"utf8")>slopcameraMaximumPromptBytes){invalidArgument(`Prompt must be non-empty and no more than ${slopcameraMaximumPromptBytes} UTF-8 bytes.`)}return value}function validateTimeout(value){const timeout=value??defaultGenerationTimeoutMs;if(!Number.isSafeInteger(timeout)||timeout<1000||timeout>maximumGenerationTimeoutMs){invalidArgument(`timeoutMs must be an integer from 1000 through ${maximumGenerationTimeoutMs}.`)}return timeout}function isObject(value){return typeof value==="object"&&value!==null||typeof value==="function"}function parseFunction(value){if(typeof value!=="function"){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}return value}function disableAiSdkWarningLogging(){globalThis.AI_SDK_LOG_WARNINGS=false}function assertGenerationActive(signal){if(signal.aborted){throw new SlopcameraCloudError("GENERATION_FAILED","Vercel AI Gateway image generation was cancelled or exceeded its deadline.")}}async function loadDefaultGatewayRuntime(){let aiModule;let gatewayModule;try{[aiModule,gatewayModule]=await Promise.all([import("ai-v7"),import("@ai-sdk/gateway-v4")])}catch{throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}if(!isObject(aiModule)||!isObject(gatewayModule)){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}const generateImage=parseFunction(aiModule.generateImage);if(typeof gatewayModule.createGateway!=="function"){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}const createGateway=gatewayModule.createGateway;return{createGateway:(settings)=>{const provider=createGateway(settings);if(!isObject(provider)||typeof provider.imageModel!=="function"){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway runtime is unavailable.")}const imageModel=provider.imageModel;return{imageModel:(modelId)=>imageModel.call(provider,modelId)}},generateImage}}function gatewayUrl(input){try{if(input instanceof Request)return new URL(input.url);if(input instanceof URL)return new URL(input.href);return new URL(input,slopcameraGatewayApiBaseUrl)}catch{throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway request was rejected.")}}function canonicalGatewayInput(input,url){if(!(input instanceof Request))return url;const snapshot=new Request(input);const body=snapshot.method==="GET"||snapshot.method==="HEAD"?undefined:snapshot.body??undefined;const requestInit={headers:snapshot.headers,method:snapshot.method,signal:snapshot.signal,...body===undefined?{}:{body,duplex:"half"}};return new Request(url.href,requestInit)}function boundedResponse(response,maximumBytes){const declared=response.headers.get("content-length");if(declared!==null){const value=Number(declared);if(!Number.isSafeInteger(value)||value<0||value>maximumBytes){response.body?.cancel().catch(()=>{return});throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded response.")}}if(response.body===null)return response;const reader=response.body.getReader();let bytes=0;const body=new ReadableStream({cancel:async(reason)=>await reader.cancel(reason),pull:async(controller)=>{try{const next=await reader.read();if(next.done){reader.releaseLock();controller.close();return}bytes+=next.value.byteLength;if(bytes>maximumBytes){await reader.cancel().catch(()=>{return});controller.error(new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded response."));return}controller.enqueue(next.value)}catch{controller.error(new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded response."))}}});return new Response(body,{headers:response.headers,status:response.status,statusText:response.statusText})}function createFixedGatewayFetch(options={}){const fetchImplementation=options.fetch??globalThis.fetch;const maximumResponseBytes=options.maximumResponseBytes??defaultMaximumGatewayResponseBytes;if(!Number.isSafeInteger(maximumResponseBytes)||maximumResponseBytes<1||maximumResponseBytes>1024*1024*1024){invalidArgument("maximumResponseBytes is outside the supported range.")}const fixed=new URL(slopcameraGatewayApiBaseUrl);return async(input,init)=>{const url=gatewayUrl(input);if(url.origin!==fixed.origin||url.pathname!==fixed.pathname&&!url.pathname.startsWith(`${fixed.pathname}/`)||url.username!==""||url.password!==""){throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway request was rejected.")}const canonicalInput=canonicalGatewayInput(input,url);let response;try{response=await fetchImplementation(canonicalInput,{...init,redirect:"error"})}catch(error){if(error instanceof SlopcameraCloudError)throw error;throw new SlopcameraCloudError("GENERATION_FAILED","Vercel AI Gateway image generation failed; the request was not retried.")}if(response.redirected||response.status>=300&&response.status<400){await response.body?.cancel().catch(()=>{return});throw new SlopcameraCloudError("GENERATION_FAILED","The Vercel AI Gateway request was rejected.")}return boundedResponse(response,maximumResponseBytes)}}function combineSignals(caller,timeoutMs){const controller=new AbortController;const abort=()=>controller.abort(caller?.reason);let rejectInterruption;const interruption=new Promise((_resolve,reject)=>{rejectInterruption=reject});const rejectOnAbort=()=>{rejectInterruption?.(new SlopcameraCloudError("GENERATION_FAILED","Vercel AI Gateway image generation was cancelled or exceeded its deadline."))};controller.signal.addEventListener("abort",rejectOnAbort,{once:true});if(caller?.aborted===true)abort();caller?.addEventListener("abort",abort,{once:true});const timer=setTimeout(()=>controller.abort(),timeoutMs);return{dispose:()=>{clearTimeout(timer);caller?.removeEventListener("abort",abort);controller.signal.removeEventListener("abort",rejectOnAbort);rejectInterruption=undefined},interruption,signal:controller.signal}}function mediaType(value){if(typeof value!=="string"||!slopcameraResponseMediaTypes.includes(value)){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an unsupported image type.")}return value}function validImageBytes(bytes,type){if(bytes.byteLength<12||bytes.byteLength>slopcameraMaximumRawImageBytes){return false}if(type==="image/png"){return Buffer.from(bytes.subarray(0,8)).equals(Buffer.from([137,80,78,71,13,10,26,10]))}if(type==="image/jpeg"){return bytes[0]===255&&bytes[1]===216&&bytes.at(-2)===255&&bytes.at(-1)===217}return bytes.byteLength>=16&&Buffer.from(bytes.subarray(0,4)).toString("ascii")==="RIFF"&&Buffer.from(bytes).readUInt32LE(4)===bytes.byteLength-8&&Buffer.from(bytes.subarray(8,12)).toString("ascii")==="WEBP"&&["VP8 ","VP8L","VP8X"].includes(Buffer.from(bytes.subarray(12,16)).toString("ascii"))}function warningReceipt(value){let type="provider-warning";let detail="Provider warning";if(typeof value==="string"){detail=value}else if(isObject(value)){if(typeof value.type==="string"&&value.type.length<=64){type=value.type.replace(/[^a-z0-9._-]/giu,"-")||type}if(typeof value.message==="string")detail=value.message;else if(typeof value.details==="string")detail=value.details}return`${type} sha256:${createHash("sha256").update(detail).digest("hex")}`}function returnedUsage(value){if(!isObject(value)||!isObject(value.usage))return;const{inputTokens,outputTokens}=value.usage;return{inputTokens:typeof inputTokens==="number"?inputTokens:undefined,outputTokens:typeof outputTokens==="number"?outputTokens:undefined}}function parseResult(value,model){if(!isObject(value)||!Array.isArray(value.images)||value.images.length!==1){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway did not return exactly one image.")}const image=value.images[0];if(!isObject(image)||!(image.uint8Array instanceof Uint8Array)){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded image.")}const type=mediaType(image.mediaType);if(!validImageBytes(image.uint8Array,type)){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE","Vercel AI Gateway returned an invalid bounded image.")}const gateway=isObject(value.providerMetadata)&&isObject(value.providerMetadata.gateway)?value.providerMetadata.gateway:undefined;const foreignGenerationId=gateway!==undefined&&typeof gateway.generationId==="string"&&gateway.generationId.length>0&&gateway.generationId.length<=256&&!/[\u0000-\u001f\u007f]/u.test(gateway.generationId)?gateway.generationId:randomUUID();const requestId=`sha256:${createHash("sha256").update("slopcamera.gateway-generation-id/v1\x00").update(foreignGenerationId).digest("hex")}`;const warnings=Array.isArray(value.warnings)?value.warnings.slice(0,100).map(warningReceipt):[];return{bytes:image.uint8Array,response:{image:{base64:Buffer.from(image.uint8Array).toString("base64"),mediaType:type},model,provider:"vercel-ai-gateway",requestId,warnings}}}async function performGeneration(input,dependencies){const model=validateModel(input.model);const prompt=validatePrompt(input.prompt);const credential=resolveSlopcameraGatewayCredential(dependencies.environment);const timeout=combineSignals(input.signal,validateTimeout(input.timeoutMs));let dispatched=false;let rejected=false;let reported=false;const report=(usage)=>{if(reported||!dispatched||rejected)return;reported=true;dependencies.onProviderCost?.(imageCallCosts(model,prompt,usage))};const observedFetch=async(request,init)=>{const response=await(dependencies.fetch??globalThis.fetch)(request,init);if(response.status>=400&&response.status<500)rejected=true;return response};try{const generation=(async()=>{assertGenerationActive(timeout.signal);disableAiSdkWarningLogging();const runtime=await(dependencies.loadRuntime??loadDefaultGatewayRuntime)();assertGenerationActive(timeout.signal);const provider=runtime.createGateway({apiKey:credential.token,baseURL:slopcameraGatewayApiBaseUrl,fetch:createFixedGatewayFetch({fetch:observedFetch,...dependencies.maximumResponseBytes===undefined?{}:{maximumResponseBytes:dependencies.maximumResponseBytes}})});assertGenerationActive(timeout.signal);dispatched=true;const generated=await runtime.generateImage({abortSignal:timeout.signal,maxRetries:0,model:provider.imageModel(model),n:1,prompt});report(returnedUsage(generated));return parseResult(generated,model)})();return await Promise.race([generation,timeout.interruption])}catch(error){report(undefined);if(error instanceof SlopcameraCloudError)throw error;throw new SlopcameraCloudError("GENERATION_FAILED","Vercel AI Gateway image generation failed; the request was not retried.")}finally{timeout.dispose()}}async function generateSlopcameraImage(input,dependencies={}){return(await performGeneration(input,dependencies)).response}function expectedMediaType(outputPath){const extension=extname(outputPath).toLocaleLowerCase("en-US");if(extension===".png")return"image/png";if(extension===".jpg"||extension===".jpeg")return"image/jpeg";if(extension===".webp")return"image/webp";invalidArgument("Output path must end in .png, .jpg, .jpeg, or .webp.")}async function atomicImageWrite(outputPath,bytes){const absolutePath=resolve(outputPath);const temporaryPath=resolve(dirname(absolutePath),`.${randomUUID()}.slopcamera-generate.tmp`);try{await writeFile(temporaryPath,bytes,{flag:"wx"});await link(temporaryPath,absolutePath);return absolutePath}catch{throw new SlopcameraCloudError("OUTPUT_WRITE_FAILED","Slopcamera could not atomically write the generated image.")}finally{await rm(temporaryPath,{force:true}).catch(()=>{return})}}async function generateSlopcameraImageFile(input,dependencies={}){if(typeof input.outputPath!=="string"||input.outputPath.length<1||input.outputPath.length>4096||input.outputPath.includes("\x00")){invalidArgument("Output path must be a non-empty local path.")}const expected=expectedMediaType(input.outputPath);const generated=await performGeneration(input,dependencies);if(generated.response.image.mediaType!==expected){throw new SlopcameraCloudError("GENERATION_INVALID_RESPONSE",`Generated ${generated.response.image.mediaType} does not match the requested ${expected} output path.`)}const outputPath=await atomicImageWrite(input.outputPath,generated.bytes);return{bytes:generated.bytes.byteLength,mediaType:generated.response.image.mediaType,model:generated.response.model,outputPath,provider:generated.response.provider,requestId:generated.response.requestId,sha256:createHash("sha256").update(generated.bytes).digest("hex"),warnings:generated.response.warnings}}
3
+ export{SlopcameraCloudError,slopcameraGatewayApiBaseUrl,slopcameraImageModels,slopcameraResponseMediaTypes,slopcameraMaximumPromptBytes,slopcameraMaximumRawImageBytes,resolveSlopcameraGatewayCredential,slopcameraGatewayCredentialStatus,createFixedGatewayFetch,generateSlopcameraImage,generateSlopcameraImageFile};
@@ -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-qtn3s421.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-sy1n4zfy.js";import{VectorizeError,vectorizeHardLimits,vectorizeImage}from"./index-qry58nj2.js";import{SlopcameraCloudError,generateSlopcameraImage,generateSlopcameraImageFile,slopcameraGatewayCredentialStatus}from"./index-77fjfg9f.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-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(`
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,7 +20,7 @@ ${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.4.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("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;");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.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("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;");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&&timesUs.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(`
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{DrawingValidationError,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS,SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,SLOPCAMERA_VERSION,STUDIO_LIMITS,SlopcameraCapabilityCommandSchema,SlopcameraCapabilityEffectSchema,SlopcameraCapabilityManifestSchema,SlopcameraCapabilityModuleDeclarationSchema,SlopcameraCapabilityModuleSchema,SlopcameraCapabilityOperationSchema,SlopcameraCapabilityPreparationSchema,SlopcameraCapabilityProfileSchema,SlopcameraCapabilityQualificationSchema,SlopcameraCapabilityResourceSchema,SlopcameraCapabilityResumeSchema,SlopcameraCapabilityToolSchema,SlopcameraCapabilityWorkflowSchema,SlopcameraMcpToolRuntime,SlopcameraSceneVariantsSchema,StudioCapabilityNameSchema,StudioDigestSchema,StudioEngineOptionsSchema,StudioEngineSchema,StudioExecutionProfileSchema,StudioJobSchema,StudioOutputArtifactSchema,StudioOutputFormatSchema,StudioOutputInterpretationSchema,StudioOutputRoleSchema,StudioOutputSpecSchema,StudioPathSchema,StudioPlanSchema,StudioReceiptSchema,StudioRenderSchema,StudioRuntimeIdentitySchema,StudioSourceBundleSchema,StudioSourceSpaceSchema,VISUAL_STYLE_IDS,VISUAL_STYLE_PROFILES,VISUAL_STYLE_VARIATION_ALGORITHM,VisualStyleIdSchema,VisualStyleProfileSchema,WorkspaceBoundary,WorkspaceBoundaryError,artifactSummary,checkDiagramFile,checkDrawingFile,compileSlopcameraCapabilityManifest,createPortableSlopcameraCapabilityManifest,createVisualStyleDirection,defineSlopcameraCapabilityModule,diagramApi,drawingMinimumGlyphHeightPt,drawingPageGeometry,drawingProfile,drawingSourceByteLimit,getVisualStyleProfile,inspectStudioBundle,inspectStudioPlan,mcpMaximumRenderedPixels,mcpMaximumScale,mcpSourceByteLimit,parseDrawingSource,parseSlopcameraCapabilityManifest,parseSlopcameraSceneVariants,parseStudioJob,parseStudioPlan,parseStudioReceipt,parseStudioRuntimeIdentity,parseStudioSourceBundle,planSlopcameraSceneGallery,planStudioJob,readDiagramFile,readDrawingFile,renderDiagramFile,renderDrawing,renderDrawingFile,runMcpServer,sampleVisualStyleExposure,slopcameraApi,slopcameraMcpProtocolVersion,slopcameraMcpServerName,slopcameraMcpTools,slopcameraPortableCapabilityManifest,slopcameraSceneGalleryLimits,starterDrawingSource,studioJobSha256,studioOutputPath,studioRuntimeSha256,studioSourceBundleSha256,summarizeSceneVariantPatch,validateStudioReceipt,visualStyleFrameVariation}from"./index-abnk4h9c.js";import{bundledSkillPath,installSkill}from"./index-grk47tgs.js";import"./index-z1kd1x8j.js";import"./index-3cxf2c58.js";import{SlopcameraWorkflowError,defineSlopcameraWorkflow,runSlopcameraWorkflow}from"./index-qtn3s421.js";import{DiagramValidationError,SlopcameraOperationError,StackLayoutError,builtInIcons,composeSlopcameraImageGallery,critiqueIconRaster,executeSlopcameraOperation,executeSlopcameraOperationWithLease,extractIconLineArt,galleryCandidateId,galleryPromptFor,generateSlopcameraIcon,generateSlopcameraImageGallery,iconPromptFor,iconVisualGateProblems,isSlopcameraOperationCode,lintDiagram,parseDiagramSource,parseDiagramSpec,parseSlopcameraGalleryVary,parseSlopcameraOperationInput,planSlopcameraGallery,renderPng,renderSvg,resolveDiagramSource,resolveEdge,resolveStackLayout,searchSlopcameraOperations,serializeTldr,slopcameraGalleryAxes,slopcameraGalleryKinds,slopcameraGalleryLimits,slopcameraIconCritiqueDefaultModel,slopcameraIconCritiqueTimeoutMs,slopcameraIconDefaultInk,slopcameraIconDefaultRounds,slopcameraIconMaximumRounds,slopcameraIconPanel,slopcameraIconSubjectMaximumBytes,slopcameraOperationCodes,slopcameraOperationHostResourceClaims,slopcameraOperationRegistry,stackLayoutDefaults,withSlopcameraOperationHostAdmission}from"./index-sy1n4zfy.js";import{VTRACER_VERSION,VectorizeError,vectorizeDefaultLimits,vectorizeHardLimits,vectorizeImage,vectorizeProfileNames,vtracerReleases}from"./index-qry58nj2.js";import{SlopcameraCloudError,createFixedGatewayFetch,generateSlopcameraImage,generateSlopcameraImageFile,resolveSlopcameraGatewayCredential,slopcameraGatewayApiBaseUrl,slopcameraGatewayCredentialStatus,slopcameraImageModels,slopcameraMaximumPromptBytes,slopcameraMaximumRawImageBytes,slopcameraResponseMediaTypes}from"./index-77fjfg9f.js";import{HOST_RESOURCE_MAX_WAIT_MILLISECONDS,HostResourceError,createDefaultHostResourceCoordinator,createHostResourceCoordinator,createProcessLocalHostResourceCoordinator,defaultSlopcameraHostResourceProfile,defaultSlopcameraHostResourceStateRoot,normalizeHostResourceClaims,normalizeHostResourceProfile,slopcameraHostResourceNames}from"./index-b3geqczn.js";import"./index-80a36bc0.js";export{withSlopcameraOperationHostAdmission,vtracerReleases,visualStyleFrameVariation,vectorizeProfileNames,vectorizeImage,vectorizeHardLimits,vectorizeDefaultLimits,validateStudioReceipt,summarizeSceneVariantPatch,studioSourceBundleSha256,studioRuntimeSha256,studioOutputPath,studioJobSha256,starterDrawingSource,stackLayoutDefaults,slopcameraSceneGalleryLimits,slopcameraResponseMediaTypes,slopcameraPortableCapabilityManifest,slopcameraOperationRegistry,slopcameraOperationHostResourceClaims,slopcameraOperationCodes,slopcameraMcpTools,slopcameraMcpServerName,slopcameraMcpProtocolVersion,slopcameraMaximumRawImageBytes,slopcameraMaximumPromptBytes,slopcameraImageModels,slopcameraIconSubjectMaximumBytes,slopcameraIconPanel,slopcameraIconMaximumRounds,slopcameraIconDefaultRounds,slopcameraIconDefaultInk,slopcameraIconCritiqueTimeoutMs,slopcameraIconCritiqueDefaultModel,slopcameraHostResourceNames,slopcameraGatewayCredentialStatus,slopcameraGatewayApiBaseUrl,slopcameraGalleryLimits,slopcameraGalleryKinds,slopcameraGalleryAxes,slopcameraApi,serializeTldr,searchSlopcameraOperations,sampleVisualStyleExposure,runSlopcameraWorkflow,runMcpServer,resolveStackLayout,resolveSlopcameraGatewayCredential,resolveEdge,resolveDiagramSource,renderSvg,renderPng,renderDrawingFile,renderDrawing,renderDiagramFile,readDrawingFile,readDiagramFile,planStudioJob,planSlopcameraSceneGallery,planSlopcameraGallery,parseStudioSourceBundle,parseStudioRuntimeIdentity,parseStudioReceipt,parseStudioPlan,parseStudioJob,parseSlopcameraSceneVariants,parseSlopcameraOperationInput,parseSlopcameraGalleryVary,parseSlopcameraCapabilityManifest,parseDrawingSource,parseDiagramSpec,parseDiagramSource,normalizeHostResourceProfile,normalizeHostResourceClaims,mcpSourceByteLimit,mcpMaximumScale,mcpMaximumRenderedPixels,lintDiagram,isSlopcameraOperationCode,installSkill,inspectStudioPlan,inspectStudioBundle,iconVisualGateProblems,iconPromptFor,getVisualStyleProfile,generateSlopcameraImageGallery,generateSlopcameraImageFile,generateSlopcameraImage,generateSlopcameraIcon,galleryPromptFor,galleryCandidateId,extractIconLineArt,executeSlopcameraOperationWithLease,executeSlopcameraOperation,drawingSourceByteLimit,drawingProfile,drawingPageGeometry,drawingMinimumGlyphHeightPt,diagramApi,defineSlopcameraWorkflow,defineSlopcameraCapabilityModule,defaultSlopcameraHostResourceStateRoot,defaultSlopcameraHostResourceProfile,critiqueIconRaster,createVisualStyleDirection,createProcessLocalHostResourceCoordinator,createPortableSlopcameraCapabilityManifest,createHostResourceCoordinator,createFixedGatewayFetch,createDefaultHostResourceCoordinator,composeSlopcameraImageGallery,compileSlopcameraCapabilityManifest,checkDrawingFile,checkDiagramFile,bundledSkillPath,builtInIcons,artifactSummary,WorkspaceBoundaryError,WorkspaceBoundary,VisualStyleProfileSchema,VisualStyleIdSchema,VectorizeError,VTRACER_VERSION,VISUAL_STYLE_VARIATION_ALGORITHM,VISUAL_STYLE_PROFILES,VISUAL_STYLE_IDS,StudioSourceSpaceSchema,StudioSourceBundleSchema,StudioRuntimeIdentitySchema,StudioRenderSchema,StudioReceiptSchema,StudioPlanSchema,StudioPathSchema,StudioOutputSpecSchema,StudioOutputRoleSchema,StudioOutputInterpretationSchema,StudioOutputFormatSchema,StudioOutputArtifactSchema,StudioJobSchema,StudioExecutionProfileSchema,StudioEngineSchema,StudioEngineOptionsSchema,StudioDigestSchema,StudioCapabilityNameSchema,StackLayoutError,SlopcameraWorkflowError,SlopcameraSceneVariantsSchema,SlopcameraOperationError,SlopcameraMcpToolRuntime,SlopcameraCloudError,SlopcameraCapabilityWorkflowSchema,SlopcameraCapabilityToolSchema,SlopcameraCapabilityResumeSchema,SlopcameraCapabilityResourceSchema,SlopcameraCapabilityQualificationSchema,SlopcameraCapabilityProfileSchema,SlopcameraCapabilityPreparationSchema,SlopcameraCapabilityOperationSchema,SlopcameraCapabilityModuleSchema,SlopcameraCapabilityModuleDeclarationSchema,SlopcameraCapabilityManifestSchema,SlopcameraCapabilityEffectSchema,SlopcameraCapabilityCommandSchema,STUDIO_LIMITS,SLOPCAMERA_VERSION,SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS,HostResourceError,HOST_RESOURCE_MAX_WAIT_MILLISECONDS,DrawingValidationError,DiagramValidationError};
2
+ import{DrawingValidationError,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS,SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,SLOPCAMERA_VERSION,STUDIO_LIMITS,SlopcameraCapabilityCommandSchema,SlopcameraCapabilityEffectSchema,SlopcameraCapabilityManifestSchema,SlopcameraCapabilityModuleDeclarationSchema,SlopcameraCapabilityModuleSchema,SlopcameraCapabilityOperationSchema,SlopcameraCapabilityPreparationSchema,SlopcameraCapabilityProfileSchema,SlopcameraCapabilityQualificationSchema,SlopcameraCapabilityResourceSchema,SlopcameraCapabilityResumeSchema,SlopcameraCapabilityToolSchema,SlopcameraCapabilityWorkflowSchema,SlopcameraMcpToolRuntime,SlopcameraSceneVariantsSchema,StudioCapabilityNameSchema,StudioDigestSchema,StudioEngineOptionsSchema,StudioEngineSchema,StudioExecutionProfileSchema,StudioJobSchema,StudioOutputArtifactSchema,StudioOutputFormatSchema,StudioOutputInterpretationSchema,StudioOutputRoleSchema,StudioOutputSpecSchema,StudioPathSchema,StudioPlanSchema,StudioReceiptSchema,StudioRenderSchema,StudioRuntimeIdentitySchema,StudioSourceBundleSchema,StudioSourceSpaceSchema,VISUAL_STYLE_IDS,VISUAL_STYLE_PROFILES,VISUAL_STYLE_VARIATION_ALGORITHM,VisualStyleIdSchema,VisualStyleProfileSchema,WorkspaceBoundary,WorkspaceBoundaryError,artifactSummary,checkDiagramFile,checkDrawingFile,compileSlopcameraCapabilityManifest,createPortableSlopcameraCapabilityManifest,createVisualStyleDirection,defineSlopcameraCapabilityModule,diagramApi,drawingMinimumGlyphHeightPt,drawingPageGeometry,drawingProfile,drawingSourceByteLimit,getVisualStyleProfile,inspectStudioBundle,inspectStudioPlan,mcpMaximumRenderedPixels,mcpMaximumScale,mcpSourceByteLimit,parseDrawingSource,parseSlopcameraCapabilityManifest,parseSlopcameraSceneVariants,parseStudioJob,parseStudioPlan,parseStudioReceipt,parseStudioRuntimeIdentity,parseStudioSourceBundle,planSlopcameraSceneGallery,planStudioJob,readDiagramFile,readDrawingFile,renderDiagramFile,renderDrawing,renderDrawingFile,runMcpServer,sampleVisualStyleExposure,slopcameraApi,slopcameraMcpProtocolVersion,slopcameraMcpServerName,slopcameraMcpTools,slopcameraPortableCapabilityManifest,slopcameraSceneGalleryLimits,starterDrawingSource,studioJobSha256,studioOutputPath,studioRuntimeSha256,studioSourceBundleSha256,summarizeSceneVariantPatch,validateStudioReceipt,visualStyleFrameVariation}from"./index-qxfexvj3.js";import{bundledSkillPath,installSkill}from"./index-grk47tgs.js";import"./index-z1kd1x8j.js";import"./index-3cxf2c58.js";import{SlopcameraWorkflowError,defineSlopcameraWorkflow,runSlopcameraWorkflow}from"./index-4z9y2wa2.js";import{DiagramValidationError,SlopcameraOperationError,StackLayoutError,builtInIcons,composeSlopcameraImageGallery,critiqueIconRaster,executeSlopcameraOperation,executeSlopcameraOperationWithLease,extractIconLineArt,galleryCandidateId,galleryPromptFor,generateSlopcameraIcon,generateSlopcameraImageGallery,iconPromptFor,iconVisualGateProblems,isSlopcameraOperationCode,lintDiagram,parseDiagramSource,parseDiagramSpec,parseSlopcameraGalleryVary,parseSlopcameraOperationInput,planSlopcameraGallery,renderPng,renderSvg,resolveDiagramSource,resolveEdge,resolveStackLayout,searchSlopcameraOperations,serializeTldr,slopcameraGalleryAxes,slopcameraGalleryKinds,slopcameraGalleryLimits,slopcameraIconCritiqueDefaultModel,slopcameraIconCritiqueTimeoutMs,slopcameraIconDefaultInk,slopcameraIconDefaultRounds,slopcameraIconMaximumRounds,slopcameraIconPanel,slopcameraIconSubjectMaximumBytes,slopcameraOperationCodes,slopcameraOperationHostResourceClaims,slopcameraOperationRegistry,stackLayoutDefaults,withSlopcameraOperationHostAdmission}from"./index-fevf4ppk.js";import{VTRACER_VERSION,VectorizeError,vectorizeDefaultLimits,vectorizeHardLimits,vectorizeImage,vectorizeProfileNames,vtracerReleases}from"./index-mnp0bftq.js";import{SlopcameraCloudError,createFixedGatewayFetch,generateSlopcameraImage,generateSlopcameraImageFile,resolveSlopcameraGatewayCredential,slopcameraGatewayApiBaseUrl,slopcameraGatewayCredentialStatus,slopcameraImageModels,slopcameraMaximumPromptBytes,slopcameraMaximumRawImageBytes,slopcameraResponseMediaTypes}from"./index-nxtmy18t.js";import{HOST_RESOURCE_MAX_WAIT_MILLISECONDS,HostResourceError,createDefaultHostResourceCoordinator,createHostResourceCoordinator,createProcessLocalHostResourceCoordinator,defaultSlopcameraHostResourceProfile,defaultSlopcameraHostResourceStateRoot,normalizeHostResourceClaims,normalizeHostResourceProfile,slopcameraHostResourceNames}from"./index-b3geqczn.js";import"./index-80a36bc0.js";export{withSlopcameraOperationHostAdmission,vtracerReleases,visualStyleFrameVariation,vectorizeProfileNames,vectorizeImage,vectorizeHardLimits,vectorizeDefaultLimits,validateStudioReceipt,summarizeSceneVariantPatch,studioSourceBundleSha256,studioRuntimeSha256,studioOutputPath,studioJobSha256,starterDrawingSource,stackLayoutDefaults,slopcameraSceneGalleryLimits,slopcameraResponseMediaTypes,slopcameraPortableCapabilityManifest,slopcameraOperationRegistry,slopcameraOperationHostResourceClaims,slopcameraOperationCodes,slopcameraMcpTools,slopcameraMcpServerName,slopcameraMcpProtocolVersion,slopcameraMaximumRawImageBytes,slopcameraMaximumPromptBytes,slopcameraImageModels,slopcameraIconSubjectMaximumBytes,slopcameraIconPanel,slopcameraIconMaximumRounds,slopcameraIconDefaultRounds,slopcameraIconDefaultInk,slopcameraIconCritiqueTimeoutMs,slopcameraIconCritiqueDefaultModel,slopcameraHostResourceNames,slopcameraGatewayCredentialStatus,slopcameraGatewayApiBaseUrl,slopcameraGalleryLimits,slopcameraGalleryKinds,slopcameraGalleryAxes,slopcameraApi,serializeTldr,searchSlopcameraOperations,sampleVisualStyleExposure,runSlopcameraWorkflow,runMcpServer,resolveStackLayout,resolveSlopcameraGatewayCredential,resolveEdge,resolveDiagramSource,renderSvg,renderPng,renderDrawingFile,renderDrawing,renderDiagramFile,readDrawingFile,readDiagramFile,planStudioJob,planSlopcameraSceneGallery,planSlopcameraGallery,parseStudioSourceBundle,parseStudioRuntimeIdentity,parseStudioReceipt,parseStudioPlan,parseStudioJob,parseSlopcameraSceneVariants,parseSlopcameraOperationInput,parseSlopcameraGalleryVary,parseSlopcameraCapabilityManifest,parseDrawingSource,parseDiagramSpec,parseDiagramSource,normalizeHostResourceProfile,normalizeHostResourceClaims,mcpSourceByteLimit,mcpMaximumScale,mcpMaximumRenderedPixels,lintDiagram,isSlopcameraOperationCode,installSkill,inspectStudioPlan,inspectStudioBundle,iconVisualGateProblems,iconPromptFor,getVisualStyleProfile,generateSlopcameraImageGallery,generateSlopcameraImageFile,generateSlopcameraImage,generateSlopcameraIcon,galleryPromptFor,galleryCandidateId,extractIconLineArt,executeSlopcameraOperationWithLease,executeSlopcameraOperation,drawingSourceByteLimit,drawingProfile,drawingPageGeometry,drawingMinimumGlyphHeightPt,diagramApi,defineSlopcameraWorkflow,defineSlopcameraCapabilityModule,defaultSlopcameraHostResourceStateRoot,defaultSlopcameraHostResourceProfile,critiqueIconRaster,createVisualStyleDirection,createProcessLocalHostResourceCoordinator,createPortableSlopcameraCapabilityManifest,createHostResourceCoordinator,createFixedGatewayFetch,createDefaultHostResourceCoordinator,composeSlopcameraImageGallery,compileSlopcameraCapabilityManifest,checkDrawingFile,checkDiagramFile,bundledSkillPath,builtInIcons,artifactSummary,WorkspaceBoundaryError,WorkspaceBoundary,VisualStyleProfileSchema,VisualStyleIdSchema,VectorizeError,VTRACER_VERSION,VISUAL_STYLE_VARIATION_ALGORITHM,VISUAL_STYLE_PROFILES,VISUAL_STYLE_IDS,StudioSourceSpaceSchema,StudioSourceBundleSchema,StudioRuntimeIdentitySchema,StudioRenderSchema,StudioReceiptSchema,StudioPlanSchema,StudioPathSchema,StudioOutputSpecSchema,StudioOutputRoleSchema,StudioOutputInterpretationSchema,StudioOutputFormatSchema,StudioOutputArtifactSchema,StudioJobSchema,StudioExecutionProfileSchema,StudioEngineSchema,StudioEngineOptionsSchema,StudioDigestSchema,StudioCapabilityNameSchema,StackLayoutError,SlopcameraWorkflowError,SlopcameraSceneVariantsSchema,SlopcameraOperationError,SlopcameraMcpToolRuntime,SlopcameraCloudError,SlopcameraCapabilityWorkflowSchema,SlopcameraCapabilityToolSchema,SlopcameraCapabilityResumeSchema,SlopcameraCapabilityResourceSchema,SlopcameraCapabilityQualificationSchema,SlopcameraCapabilityProfileSchema,SlopcameraCapabilityPreparationSchema,SlopcameraCapabilityOperationSchema,SlopcameraCapabilityModuleSchema,SlopcameraCapabilityModuleDeclarationSchema,SlopcameraCapabilityManifestSchema,SlopcameraCapabilityEffectSchema,SlopcameraCapabilityCommandSchema,STUDIO_LIMITS,SLOPCAMERA_VERSION,SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS,HostResourceError,HOST_RESOURCE_MAX_WAIT_MILLISECONDS,DrawingValidationError,DiagramValidationError};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{SlopcameraOperationError,executeSlopcameraOperation,executeSlopcameraOperationWithLease,isSlopcameraOperationCode,parseSlopcameraOperationInput,searchSlopcameraOperations,slopcameraOperationCodes,slopcameraOperationHostResourceClaims,slopcameraOperationRegistry,withSlopcameraOperationHostAdmission}from"./index-sy1n4zfy.js";import"./index-qry58nj2.js";import"./index-77fjfg9f.js";import"./index-b3geqczn.js";import"./index-80a36bc0.js";export{withSlopcameraOperationHostAdmission,slopcameraOperationRegistry,slopcameraOperationHostResourceClaims,slopcameraOperationCodes,searchSlopcameraOperations,parseSlopcameraOperationInput,isSlopcameraOperationCode,executeSlopcameraOperationWithLease,executeSlopcameraOperation,SlopcameraOperationError};
2
+ import{SlopcameraOperationError,executeSlopcameraOperation,executeSlopcameraOperationWithLease,isSlopcameraOperationCode,parseSlopcameraOperationInput,searchSlopcameraOperations,slopcameraOperationCodes,slopcameraOperationHostResourceClaims,slopcameraOperationRegistry,withSlopcameraOperationHostAdmission}from"./index-fevf4ppk.js";import"./index-mnp0bftq.js";import"./index-nxtmy18t.js";import"./index-b3geqczn.js";import"./index-80a36bc0.js";export{withSlopcameraOperationHostAdmission,slopcameraOperationRegistry,slopcameraOperationHostResourceClaims,slopcameraOperationCodes,searchSlopcameraOperations,parseSlopcameraOperationInput,isSlopcameraOperationCode,executeSlopcameraOperationWithLease,executeSlopcameraOperation,SlopcameraOperationError};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{MAX_VECTORIZE_REQUEST_BYTES,MAX_VECTORIZE_RESPONSE_BYTES,VECTORIZE_WORKER_PROTOCOL,VectorizeError,configureVectorizeSharpConcurrency,forwardVectorizeWorkerTermination,vectorizeImageInProcess,withInheritedCommandFileDescriptors}from"../index-qry58nj2.js";import"../index-80a36bc0.js";import{lstat,realpath}from"fs/promises";import{tmpdir}from"os";import{basename,dirname}from"path";var errorCodes=new Set(["input_limit","invalid_input","output_limit","quality_limit","timeout","tool_download","tool_integrity","tool_platform","tool_version","trace_failed","unsafe_svg"]);forwardVectorizeWorkerTermination();await main();async function main(){let response;try{configureVectorizeSharpConcurrency();const requestText=await readBoundedInput();const request=parseRequest(requestText);const input=decodeInput(request.input);const temporaryRoot=await validateTemporaryRoot(request.temporaryRoot);const result=await withInheritedCommandFileDescriptors(request.options.inheritedFileDescriptors,async()=>await vectorizeImageInProcess(input,request.options,temporaryRoot));response={ok:true,protocol:VECTORIZE_WORKER_PROTOCOL,result}}catch(error){response={error:serializeError(error),ok:false,protocol:VECTORIZE_WORKER_PROTOCOL}}let encoded=JSON.stringify(response);if(Buffer.byteLength(encoded)>MAX_VECTORIZE_RESPONSE_BYTES){encoded=JSON.stringify({error:{code:"output_limit",details:{maximumBytes:MAX_VECTORIZE_RESPONSE_BYTES},message:"The vectorization worker response exceeds its IPC limit."},ok:false,protocol:VECTORIZE_WORKER_PROTOCOL})}await Bun.write(Bun.stdout,encoded)}async function readBoundedInput(){const reader=Bun.stdin.stream().getReader();const chunks=[];let bytes=0;try{while(true){const{done,value}=await reader.read();if(done)break;bytes+=value.byteLength;if(bytes>MAX_VECTORIZE_REQUEST_BYTES){await reader.cancel();throw new VectorizeError("input_limit","The vectorization worker request exceeds its IPC limit.",{bytes,maximumBytes:MAX_VECTORIZE_REQUEST_BYTES})}chunks.push(value)}}finally{reader.releaseLock()}return Buffer.concat(chunks,bytes).toString("utf8")}function parseRequest(text){let parsed;try{parsed=JSON.parse(text)}catch(error){throw new VectorizeError("invalid_input","The vectorization worker request is malformed.",{},{cause:error})}if(!isRecord(parsed)||parsed.protocol!==VECTORIZE_WORKER_PROTOCOL||!isRecord(parsed.input)||!isRecord(parsed.options)||typeof parsed.temporaryRoot!=="string"||parsed.temporaryRoot.length===0||typeof parsed.input.kind!=="string"||typeof parsed.input.value!=="string"||!["bytes","path"].includes(parsed.input.kind)){throw new VectorizeError("invalid_input","The vectorization worker request is invalid.")}return parsed}function decodeInput(input){if(input.kind==="path")return input.value;if(input.value.length>Math.ceil(MAX_VECTORIZE_REQUEST_BYTES/3)*4||input.value.length%4!==0){throw new VectorizeError("input_limit","Encoded raster input exceeds its IPC limit.")}const bytes=Buffer.from(input.value,"base64");if(bytes.toString("base64")!==input.value){throw new VectorizeError("invalid_input","Encoded raster input is not canonical base64.")}return bytes}async function validateTemporaryRoot(path){let realRoot;let realTemporaryDirectory;try{const resolved=await Promise.all([realpath(path),realpath(tmpdir())]);realRoot=resolved[0];realTemporaryDirectory=resolved[1];const metadata=await lstat(path);if(!metadata.isDirectory()||metadata.isSymbolicLink()||dirname(realRoot)!==realTemporaryDirectory||!/^slopcamera-vectorize-[A-Za-z0-9_-]{6,}$/u.test(basename(realRoot))){throw new VectorizeError("invalid_input","The supervisor temporary directory is invalid.")}}catch(error){if(error instanceof VectorizeError)throw error;throw new VectorizeError("invalid_input","The supervisor temporary directory could not be verified.",{},{cause:error})}return realRoot}function serializeError(error){const code=error instanceof VectorizeError&&errorCodes.has(error.code)?error.code:"trace_failed";const message=error instanceof Error?error.message:"The vectorization worker failed.";const details=error instanceof VectorizeError&&Buffer.byteLength(JSON.stringify(error.details))<=64*1024?error.details:{};return{code,details,message}}function isRecord(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}
2
+ import{MAX_VECTORIZE_REQUEST_BYTES,MAX_VECTORIZE_RESPONSE_BYTES,VECTORIZE_WORKER_PROTOCOL,VectorizeError,configureVectorizeSharpConcurrency,forwardVectorizeWorkerTermination,vectorizeImageInProcess,withInheritedCommandFileDescriptors}from"../index-mnp0bftq.js";import"../index-80a36bc0.js";import{lstat,realpath}from"fs/promises";import{tmpdir}from"os";import{basename,dirname}from"path";var errorCodes=new Set(["input_limit","invalid_input","output_limit","quality_limit","timeout","tool_download","tool_integrity","tool_platform","tool_version","trace_failed","unsafe_svg"]);forwardVectorizeWorkerTermination();await main();async function main(){let response;try{configureVectorizeSharpConcurrency();const requestText=await readBoundedInput();const request=parseRequest(requestText);const input=decodeInput(request.input);const temporaryRoot=await validateTemporaryRoot(request.temporaryRoot);const result=await withInheritedCommandFileDescriptors(request.options.inheritedFileDescriptors,async()=>await vectorizeImageInProcess(input,request.options,temporaryRoot));response={ok:true,protocol:VECTORIZE_WORKER_PROTOCOL,result}}catch(error){response={error:serializeError(error),ok:false,protocol:VECTORIZE_WORKER_PROTOCOL}}let encoded=JSON.stringify(response);if(Buffer.byteLength(encoded)>MAX_VECTORIZE_RESPONSE_BYTES){encoded=JSON.stringify({error:{code:"output_limit",details:{maximumBytes:MAX_VECTORIZE_RESPONSE_BYTES},message:"The vectorization worker response exceeds its IPC limit."},ok:false,protocol:VECTORIZE_WORKER_PROTOCOL})}await Bun.write(Bun.stdout,encoded)}async function readBoundedInput(){const reader=Bun.stdin.stream().getReader();const chunks=[];let bytes=0;try{while(true){const{done,value}=await reader.read();if(done)break;bytes+=value.byteLength;if(bytes>MAX_VECTORIZE_REQUEST_BYTES){await reader.cancel();throw new VectorizeError("input_limit","The vectorization worker request exceeds its IPC limit.",{bytes,maximumBytes:MAX_VECTORIZE_REQUEST_BYTES})}chunks.push(value)}}finally{reader.releaseLock()}return Buffer.concat(chunks,bytes).toString("utf8")}function parseRequest(text){let parsed;try{parsed=JSON.parse(text)}catch(error){throw new VectorizeError("invalid_input","The vectorization worker request is malformed.",{},{cause:error})}if(!isRecord(parsed)||parsed.protocol!==VECTORIZE_WORKER_PROTOCOL||!isRecord(parsed.input)||!isRecord(parsed.options)||typeof parsed.temporaryRoot!=="string"||parsed.temporaryRoot.length===0||typeof parsed.input.kind!=="string"||typeof parsed.input.value!=="string"||!["bytes","path"].includes(parsed.input.kind)){throw new VectorizeError("invalid_input","The vectorization worker request is invalid.")}return parsed}function decodeInput(input){if(input.kind==="path")return input.value;if(input.value.length>Math.ceil(MAX_VECTORIZE_REQUEST_BYTES/3)*4||input.value.length%4!==0){throw new VectorizeError("input_limit","Encoded raster input exceeds its IPC limit.")}const bytes=Buffer.from(input.value,"base64");if(bytes.toString("base64")!==input.value){throw new VectorizeError("invalid_input","Encoded raster input is not canonical base64.")}return bytes}async function validateTemporaryRoot(path){let realRoot;let realTemporaryDirectory;try{const resolved=await Promise.all([realpath(path),realpath(tmpdir())]);realRoot=resolved[0];realTemporaryDirectory=resolved[1];const metadata=await lstat(path);if(!metadata.isDirectory()||metadata.isSymbolicLink()||dirname(realRoot)!==realTemporaryDirectory||!/^slopcamera-vectorize-[A-Za-z0-9_-]{6,}$/u.test(basename(realRoot))){throw new VectorizeError("invalid_input","The supervisor temporary directory is invalid.")}}catch(error){if(error instanceof VectorizeError)throw error;throw new VectorizeError("invalid_input","The supervisor temporary directory could not be verified.",{},{cause:error})}return realRoot}function serializeError(error){const code=error instanceof VectorizeError&&errorCodes.has(error.code)?error.code:"trace_failed";const message=error instanceof Error?error.message:"The vectorization worker failed.";const details=error instanceof VectorizeError&&Buffer.byteLength(JSON.stringify(error.details))<=64*1024?error.details:{};return{code,details,message}}function isRecord(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}
package/dist/workflow.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{SlopcameraWorkflowError,defineSlopcameraWorkflow,runSlopcameraWorkflow}from"./index-qtn3s421.js";import"./index-sy1n4zfy.js";import"./index-qry58nj2.js";import"./index-77fjfg9f.js";import"./index-b3geqczn.js";import"./index-80a36bc0.js";export{runSlopcameraWorkflow,defineSlopcameraWorkflow,SlopcameraWorkflowError};
2
+ import{SlopcameraWorkflowError,defineSlopcameraWorkflow,runSlopcameraWorkflow}from"./index-4z9y2wa2.js";import"./index-fevf4ppk.js";import"./index-mnp0bftq.js";import"./index-nxtmy18t.js";import"./index-b3geqczn.js";import"./index-80a36bc0.js";export{runSlopcameraWorkflow,defineSlopcameraWorkflow,SlopcameraWorkflowError};
package/docs/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Slopcamera documentation
2
2
 
3
- Slopcamera retains editable source, renders local or generated media, and assembles it into inspectable video projects. Choose a guide for the work you want to do.
3
+ Slopcamera is a media studio for coding agents. It retains editable source, renders local or generated media, and assembles it into inspectable video projects. Choose a guide for the work you want to do.
4
4
 
5
- Install the [verified Slopcamera v3.3.2 release](../README.md#install-slopcamera) for the CLI, SDK, and matching Agent Skill. Each guide names its local runtimes and any later correction that requires [current source](how-to/use-current-source.md). Example helpers that import repository TypeScript also need a checkout; that requirement does not mean their CLI commands are unreleased. Historical Atet archives retain their original package and commands. The CLI also has an optional unbundled macOS menu-bar companion; the [menu-bar release contract](menubar-release.md) covers its prebuilt binary and LaunchAgent. The [capability reference](reference/capabilities.md) distinguishes them and lists local runtime requirements.
5
+ Install the [current Slopcamera release](../README.md#install-slopcamera) for the CLI, SDK, and matching Agent Skill. Each guide names its local runtimes and any correction that needs [current source](how-to/use-current-source.md). Some examples import TypeScript from the repository and need a checkout to run. Historical Atet archives retain their original package and commands. The CLI also has an optional unbundled macOS menu-bar companion; the [menu-bar release contract](menubar-release.md) covers its prebuilt binary and LaunchAgent. The [capability reference](reference/capabilities.md) distinguishes them and lists local runtime requirements.
6
6
 
7
7
  ## Learn by making something
8
8
 
@@ -13,7 +13,7 @@ Install the [verified Slopcamera v3.3.2 release](../README.md#install-slopcamera
13
13
  ## Complete a task
14
14
 
15
15
  - [Run current-source commands](how-to/use-current-source.md): build an exact checkout of Slopcamera.
16
- - [Prepare patent-style drawing sheets](how-to/patent-drawings.md): retain diagram source, check physical bounds, and render monochrome SVG sheets and a PDF; requires current source.
16
+ - [Prepare patent-style drawing sheets](how-to/patent-drawings.md): retain diagram source, check physical bounds, and render monochrome SVG sheets and a PDF.
17
17
 
18
18
  - [Render motion graphics from HTML](how-to/render-motion-graphics.md): choose among seven authoring profiles, render a graphic, and retain its source.
19
19
  - [Direct a film or animation style](how-to/direct-visual-styles.md): use reusable art direction and render the original style studies; requires current source.
@@ -39,7 +39,7 @@ node /absolute/slopcamera/examples/studio/vgpu/heat-field.mjs \
39
39
  --output /absolute/retained-media/new-energy-panel
40
40
  ```
41
41
 
42
- On Hraness hosts, wrap this native command with the installed `oompa-host-run` in the `mac-native` lane. The default output is 96 frames at 24 fps, 768×432. Optional `--frames`, `--start-frame`, `--width` and `--height` values remain within a 16:9, 1024 px, 240-frame profile. A frame's shader time is its absolute frame number divided by 24; a selected nonzero interval retains that time while filenames begin at zero.
42
+ On Hraness hosts, wrap this native command with the installed `host-run` in the `mac-native` lane. The default output is 96 frames at 24 fps, 768×432. Optional `--frames`, `--start-frame`, `--width` and `--height` values remain within a 16:9, 1024 px, 240-frame profile. A frame's shader time is its absolute frame number divided by 24; a selected nonzero interval retains that time while filenames begin at zero.
43
43
 
44
44
  Unset ambient `VGPU_*`, `NODE_OPTIONS` and `NODE_PATH` settings before invocation. The example rejects these overrides, explicitly requests Metal hardware, checks the observed hardware adapter and loaded native module, and records their identities. It permits no automatic CPU fallback as successful evidence.
45
45
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hraness/slopcamera",
3
- "version": "3.4.0",
4
- "description": "A local visual studio for coding agents: author scenes, combine generated and recorded media, and export images, diagrams, animation, and video from retained sources.",
3
+ "version": "3.6.0",
4
+ "description": "Slopcamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files it can keep revising.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "sideEffects": [
@@ -65,7 +65,6 @@
65
65
  "video-generation",
66
66
  "video-editing",
67
67
  "image-generation",
68
- "screen-recording",
69
68
  "motion-graphics",
70
69
  "captions",
71
70
  "speech-generation",
@@ -238,6 +237,7 @@
238
237
  "check:schema": "bun run ./scripts/validate-schema.ts",
239
238
  "check:skill": "bun run ./scripts/validate-skill.ts && bun test ./scripts/social-collage-compositor.test.ts && bun examples/style-portfolio/verify-animation-source.mjs",
240
239
  "check:standalone": "bun test ./scripts/standalone-support-fixture.test.ts && bun run ./scripts/check-standalone.ts",
240
+ "check:copy": "bun test ./scripts/check-copy.test.ts && bun run ./scripts/check-copy.ts",
241
241
  "test:vectorize:official": "bun run ./scripts/official-vectorizer-smoke.ts",
242
242
  "test:package": "bun run ./scripts/package-smoke.ts",
243
243
  "check:sdk": "bun run typecheck:sdk && bun run lint:sdk && bun run build:sdk && bun run test:sdk && bun run check:release-workflows && bun run check:schema && bun run check:skill",
@@ -248,7 +248,7 @@
248
248
  "kb:check": "bunx --bun github:hraness/kb#v0.15.2 check --root kb && bunx --bun github:hraness/kb#v0.15.2 agents check --root kb --repo .",
249
249
  "kb:catalog": "bunx --bun github:hraness/kb#v0.15.2 catalog --root kb",
250
250
  "check:cost-surfaces": "bun ./scripts/check-cost-surfaces.mjs",
251
- "check": "bun run check:cost-surfaces && bun run check:standalone && bun run check:sdk && bun run check:desktop && bun run check:api && bun run check:web && bun run check:standalone && bun run test:package",
251
+ "check": "bun run check:cost-surfaces && bun run check:standalone && bun run check:copy && bun run check:sdk && bun run check:desktop && bun run check:api && bun run check:web && bun run check:standalone && bun run test:package",
252
252
  "prepack": "bun run check",
253
253
  "check:effect": "bun run ./scripts/check-effect-policy.ts && bun test ./scripts/check-effect-architecture.test.ts",
254
254
  "typecheck:api": "tsc --noEmit -p apps/api/tsconfig.json",
@@ -260,7 +260,7 @@
260
260
  "dependencies": {
261
261
  "@ai-sdk/gateway-v4": "npm:@ai-sdk/gateway@^4.0.27",
262
262
  "@hraness/algal": "github:hraness/algal#c46264a",
263
- "@hraness/support-foundation": "github:hraness/support-foundation#b32c1c81bb2444f50509ed54388758ecfab1f1c0",
263
+ "@hraness/support-foundation": "github:hraness/support-foundation#8bb514d24b79dc3f305390700ae312cab88e7ad2",
264
264
  "@noble/hashes": "2.2.0",
265
265
  "@paper-design/shaders": "0.0.77",
266
266
  "@resvg/resvg-js": "2.6.2",
@@ -284,7 +284,6 @@
284
284
  "devDependencies": {
285
285
  "@antithesishq/bombadil": "0.7.2",
286
286
  "@eslint/js": "^9.39.2",
287
- "@hraness/design-kit": "github:hraness/design-kit#v0.3.0",
288
287
  "@hraness/direct": "github:hraness/direct#v0.7.20",
289
288
  "@native-sdk/cli": "0.5.3",
290
289
  "@types/bun": "^1.3.14",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: slopcamera
3
- description: Create, inspect, edit and render visual media with the Slopcamera CLI and SDK. Use for diagrams, raster-to-SVG conversion, screen or camera recordings, video editing and captions, social collage banners, music videos from authored scenes and local tracks, AI images/video/speech/transcription, Three.js scenes and world-space media, Blender rigs and simulation, CadQuery models, parametric architectural designs, Manim educational films, HTML/WGSL motion graphics, and durable agent workflows.
3
+ description: Create, inspect, edit and render visual media with the Slopcamera CLI and SDK. Use for diagrams, raster-to-SVG conversion, existing screen or camera recordings, video editing and captions, social collage banners, music videos from authored scenes and local tracks, AI images/video/speech/transcription, Three.js scenes and world-space media, Blender rigs and simulation, CadQuery models, parametric architectural designs, Manim educational films, HTML/WGSL motion graphics, and durable agent workflows.
4
4
  ---
5
5
 
6
6
  # Create visual media with Slopcamera
@@ -27,13 +27,14 @@ Slopcamera installs from its verified release archive or from source. Historical
27
27
  | --- | --- |
28
28
  | Historical film, cel animation, pixel art, classic animation styles, or a demo portfolio | [Visual style direction](references/visual-style-direction.md) |
29
29
  | Editable diagram, flowchart or visual explanation | [Diagrams](references/diagrams.md) and [visual communication](references/visual-communication.md) |
30
- | Patent-style drawing sheets, physical margins, monochrome SVGs or a drawing PDF | [Drawing sheets](references/patent-drawings.md); requires current source |
30
+ | Patent-style drawing sheets, physical margins, monochrome SVGs or a drawing PDF | [Drawing sheets](references/patent-drawings.md) |
31
31
  | Custom diagram themes, fonts or icon packages | [Customization](references/customization.md) |
32
32
  | Raster artwork converted to SVG | [Vectorization](references/vectorization.md) |
33
- | Record, clean up, caption, frame or deliver video | [Video projects](references/video-projects.md) |
33
+ | Clean up, caption, frame, or deliver existing recordings and footage | [Video projects](references/video-projects.md) |
34
34
  | Music video, dancing mascot or timed HTML scene with a local soundtrack | [Music videos](references/music-video.md) |
35
35
  | Authorized YouTube or other public web-media excerpt (`yt-dlp` / `youtube-dl`) | [Web-media excerpts](references/web-media-excerpts.md) |
36
36
  | Generate images, clips, narration or transcripts | [Gateway media](references/gateway-media.md) |
37
+ | Product mark, marketing illustration or topic icon | [Brand illustrations](references/brand-illustrations.md) |
37
38
  | Compose an editorial or social banner from generated layers, local type and diagrams | [Social collage banners](references/social-collage-banners.md) |
38
39
  | Review alternative textures, skyboxes, backdrops, design candidates or whole-scene variants | [Image galleries](references/image-galleries.md) |
39
40
  | Budgeted short clips, reviewed takes and continuity | [Directing video](references/directing-video.md) |