@hraness/slopcamera 3.8.1 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +14 -0
- package/README.md +14 -6
- package/apps/desktop/application/operation.ts +1 -1
- package/apps/desktop/cli/capability-manifest.ts +15 -2
- package/apps/desktop/cli/commands.ts +1 -1
- package/apps/desktop/cli/help.ts +23 -4
- package/apps/desktop/cli/portable-surface.ts +1 -0
- package/apps/desktop/cli/verbs.ts +4 -0
- package/apps/desktop/dist/cli/main.js +306 -284
- package/dist/cli.js +24 -4
- package/dist/{index-842t02pp.js → index-m6g0ca9b.js} +1 -1
- package/dist/{index-y8spxs77.js → index-mgychzh3.js} +6 -3
- package/dist/{index-6jrxaser.js → index-rxx27q8b.js} +4 -4
- package/dist/index.js +1 -1
- package/dist/operations.js +1 -1
- package/dist/workflow.js +1 -1
- package/package.json +4 -2
- package/skills/slopcamera/SKILL.md +2 -0
- package/skills/slopcamera/references/customization.md +8 -1
- package/skills/slopcamera/references/icon-place.md +76 -0
- package/skills/slopcamera/references/install.md +2 -2
- package/skills/slopcamera/references/music-video.md +10 -2
- package/skills/slopcamera/references/soundtracks.md +58 -0
- package/src/bounded-file.ts +135 -0
- package/src/cli.ts +191 -0
- package/src/icon-scene.ts +695 -0
- package/src/index.ts +2 -0
- package/src/mcp/tools.ts +311 -0
- package/src/mcp/types.ts +4 -0
- package/src/operations.ts +306 -0
- package/src/soundtrack.ts +489 -0
- package/src/version.ts +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
import{SLOPCAMERA_VERSION,VISUAL_STYLE_PROFILES,artifactSummary,checkDiagramFile,checkDrawingFile,createVisualStyleDirection,generateSlopcameraIconSet,getVisualStyleProfile,parseSlopcameraIconSetSpec,renderDiagramFile,renderDrawingFile,runMcpServer,slopcameraIconSetMaximumSetRounds,starterDrawingSource}from"./index-
|
|
3
|
+
import{SLOPCAMERA_VERSION,VISUAL_STYLE_PROFILES,artifactSummary,checkDiagramFile,checkDrawingFile,createVisualStyleDirection,generateSlopcameraIconSet,getVisualStyleProfile,parseSlopcameraIconSetSpec,renderDiagramFile,renderDrawingFile,runMcpServer,slopcameraIconSetMaximumSetRounds,starterDrawingSource}from"./index-rxx27q8b.js";import{installSkill,pathExists}from"./index-grk47tgs.js";import"./index-z1kd1x8j.js";import"./index-3cxf2c58.js";import"./index-m6g0ca9b.js";import{executeSlopcameraOperation,generateSlopcameraIcon,generateSlopcameraImageGallery,isSlopcameraOperationCode,parseSlopcameraGalleryVary,searchSlopcameraOperations,slopcameraGalleryKinds,slopcameraGalleryLimits,slopcameraIconContexts,slopcameraIconLanguages,slopcameraIconMaximumRounds,slopcameraIconPalettes,slopcameraIconSceneLimits,slopcameraOperationCodes,slopcameraSoundtrackFormats,withSlopcameraOperationHostAdmission}from"./index-mgychzh3.js";import{vectorizeImage}from"./index-mnp0bftq.js";import{generateSlopcameraImageFile,slopcameraGatewayCredentialStatus,slopcameraImageModels}from"./index-p74xy76d.js";import"./index-b3geqczn.js";import{__require}from"./index-80a36bc0.js";import{readFile,writeFile}from"fs/promises";import{dirname,resolve}from"path";function reportUsefulResult(observer){try{if(observer!==undefined)Promise.resolve(observer()).catch(()=>{return})}catch{}}import{maybeShowSupportInvitation,runSupportCommand,supportAdvancedHelp,supportHelpLine}from"@hraness/support-foundation/node";var slopcameraSupportHelpLine=()=>supportHelpLine({command:["slopcamera"]});var slopcameraSupportAdvancedHelp=()=>supportAdvancedHelp({command:["slopcamera"]});var supportProfile={id:"slopcamera",name:"Slopcamera",valueProposition:"Support ongoing development of local media tools for agents.",updates:false};function standaloneSupportEnvironment(){const env={...process.env};process.env.HRANESS_SUPPORT_AUDIENCE="off";process.env.HRANESS_SUPPORT_EMAIL="off";return env}async function runProductSupportCommand(args,options={}){const result=await runSupportCommand(supportProfile,args,{command:["slopcamera"],...options,gitEmail:false});if(result.stdout!=="")process.stdout.write(result.stdout);if(result.stderr!=="")process.stderr.write(result.stderr);return result.exitCode}async function showProductSupportInvitation(options={}){try{await maybeShowSupportInvitation(supportProfile,{usefulResult:true,command:["slopcamera"],...options,gitEmail:false})}catch{}}var slopcameraCliVersion=SLOPCAMERA_VERSION;var help=()=>`slopcamera ${slopcameraCliVersion}
|
|
4
4
|
|
|
5
5
|
Turn source material into deterministic diagrams, images, and canvas assets.
|
|
6
6
|
|
|
@@ -23,6 +23,14 @@ Usage:
|
|
|
23
23
|
slopcamera image gallery <subject> --output-dir <directory> [--kind <${slopcameraGalleryKinds.join("|")}>]
|
|
24
24
|
[--count <1-${slopcameraGalleryLimits.candidates}>] [--vary <axis[=v1,v2][;axis...]>] [--candidates <file.json>]
|
|
25
25
|
[--model <provider/model>] [--cell <${slopcameraGalleryLimits.cellEdgeMin}-${slopcameraGalleryLimits.cellEdgeMax}>] [--tile|--no-tile] [--json]
|
|
26
|
+
slopcamera image icon compose <source.json> [--output <file.json>] [--json]
|
|
27
|
+
slopcamera image icon render <source.json> --output <file.svg> [--recipe <file.json>]
|
|
28
|
+
[--size <${slopcameraIconSceneLimits.sizeMin}-${slopcameraIconSceneLimits.sizeMax}>] [--background] [--palette <${slopcameraIconPalettes.join("|")}>]
|
|
29
|
+
[--language <language>] [--json]
|
|
30
|
+
slopcamera media soundtrack compose <score> [--output <file.json>]
|
|
31
|
+
[--format <${slopcameraSoundtrackFormats.join("|")}>] [--json]
|
|
32
|
+
slopcamera media soundtrack grid <score> [--output <file.json>] [--start-us <n>]
|
|
33
|
+
[--format <${slopcameraSoundtrackFormats.join("|")}>] [--json]
|
|
26
34
|
slopcamera style list [--json]
|
|
27
35
|
slopcamera style show <id> [--json]
|
|
28
36
|
slopcamera code search [query] [--limit <number>]
|
|
@@ -70,6 +78,16 @@ measured coverage and stroke-weight spread across the set, and a
|
|
|
70
78
|
contact-sheet critique reviews the family as a unit before SVGs publish.
|
|
71
79
|
Manifests and receipts stay local.
|
|
72
80
|
|
|
81
|
+
Icon compose and render are local and deterministic: they solve and draw
|
|
82
|
+
vector icon scenes, collections, construction programs, and recipes with the
|
|
83
|
+
bundled icon.place library, emit inert SVG, and replay a recipe only when its
|
|
84
|
+
recorded digests match. Languages: ${slopcameraIconLanguages.join(", ")}.
|
|
85
|
+
|
|
86
|
+
Media soundtrack verifies a Soundfish loop or song (compose text, song text,
|
|
87
|
+
score JSON, or a Standard MIDI file) and derives its beat grid as the
|
|
88
|
+
{bpm, beatOffsetUs, beatsPerBar} music timing HTML scene requests accept, plus
|
|
89
|
+
section cue times. It renders no audio and makes no network requests.
|
|
90
|
+
|
|
73
91
|
Gallery generates several bounded candidates in parallel and composes them
|
|
74
92
|
into one labelled contact sheet plus a receipt. Use it to review texture,
|
|
75
93
|
skybox, backdrop, sprite, or design alternatives, then promote a chosen
|
|
@@ -88,12 +106,14 @@ executes workspace config or caller code, and writes protocol messages only to
|
|
|
88
106
|
stdout.
|
|
89
107
|
`;function parseArguments(args,valueOptions){const positionals=[];const options={};const flags=new Set;for(let index=0;index<args.length;index+=1){const argument=args[index];if(argument===undefined)continue;if(!argument.startsWith("--")){positionals.push(argument);continue}const name=argument.slice(2);if(!valueOptions.has(name)){flags.add(name);continue}const value=args[index+1];if(value===undefined||value.startsWith("--")){throw new Error(`--${name} requires a value`)}options[name]=value;index+=1}return{positionals,options,flags}}function requiredPositional(parsed,index,label){const value=parsed.positionals[index];if(value===undefined)throw new Error(`Missing ${label}`);return value}function requiredOption(parsed,name){const value=parsed.options[name];if(value===undefined)throw new Error(`--${name} is required`);return value}function parsePositiveInteger(value,name){if(value===undefined)return;const parsed=Number(value);if(!Number.isInteger(parsed)||parsed<1){throw new Error(`--${name} must be a positive integer`)}return parsed}function parseDuotone(value){if(value===undefined)return;const colors=value.split(",").map((color)=>color.trim());if(colors.length!==2||colors.some((color)=>!/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(color))){throw new Error("--duotone must contain two #rgb or #rrggbb colors separated by a comma")}return[colors[0],colors[1]]}function parseGalleryVary(value){if(value===undefined)return;return parseSlopcameraGalleryVary(value)}async function readGalleryCandidates(path){const text=await readFile(resolve(path),"utf8");if(Buffer.byteLength(text,"utf8")>64*1024){throw new Error("--candidates JSON must be no more than 65536 UTF-8 bytes")}try{return JSON.parse(text)}catch{throw new Error("--candidates must be valid JSON")}}function printFindings(findings){if(findings.length===0){console.log("No diagram lint findings.");return}console.warn(`${findings.length} diagram lint finding${findings.length===1?"":"s"}:`);for(const finding of findings){console.warn(` [${finding.code}] ${finding.message}`)}}var starter={$schema:"https://raw.githubusercontent.com/hraness/slopcamera/main/schema/diagram.schema.json",version:1,name:"example-flow",canvas:{width:960,height:540,padding:64},layout:{type:"stack",direction:"horizontal",gap:160,align:"center"},shapes:[{id:"source",type:"rect",width:240,height:160,label:"Source",icon:"document",tone:"blue"},{id:"result",type:"rect",width:240,height:160,label:"Result",icon:"check",tone:"green"}],edges:[{id:"source-result",from:"source",to:"result"}]};function hostAdmissionOptions(dependencies){return dependencies.hostResourceCoordinator===undefined?{}:{hostResourceCoordinator:dependencies.hostResourceCoordinator}}async function runDrawingSheets(args,dependencies){const[command,...rest]=args;if(command!=="init"&&command!=="check"&&command!=="render"){throw new Error("Use slopcamera diagram sheets init, check, or render <file.drawing.json>")}const positionals=[];const seen=new Set;let outDirectory;let json=false;for(let index=0;index<rest.length;index+=1){const argument=rest[index];if(!argument.startsWith("-")){positionals.push(argument);continue}if(argument!=="--json"&&!(command==="render"&&argument==="--out-dir")){throw new Error(`Unknown diagram sheets ${command} option: ${argument}`)}if(seen.has(argument))throw new Error(`${argument} may be supplied at most once`);seen.add(argument);if(argument==="--json"){json=true}else{const value=rest[index+1];if(value===undefined||value.startsWith("-")||value.trim().length===0){throw new Error("--out-dir requires a directory")}outDirectory=value;index+=1}}if(positionals.length!==1||positionals[0].trim().length===0){throw new Error(`slopcamera diagram sheets ${command} requires exactly one drawing file`)}const filePath=resolve(positionals[0]);if(!filePath.endsWith(".drawing.json"))throw new Error("Drawing source must end in .drawing.json");const log=dependencies.log??console.log;if(command==="init"){try{await writeFile(filePath,`${JSON.stringify(starterDrawingSource(),null,2)}
|
|
90
108
|
`,{flag:"wx"})}catch(error){if(error instanceof Error&&"code"in error&&error.code==="EEXIST"){throw new Error(`Refusing to overwrite existing file: ${filePath}`)}throw error}log(json?JSON.stringify({command:"diagram.sheets.init",source:filePath}):`Created ${filePath}`);reportUsefulResult(dependencies.onUsefulResult);return}if(command==="check"){const checks=await withSlopcameraOperationHostAdmission("slopcamera.diagram.check",async()=>await checkDrawingFile({filePath}),hostAdmissionOptions(dependencies));log(json?JSON.stringify({command:"diagram.sheets.check",source:filePath,...checks}):`Valid drawing: ${checks.sheetCount} sheet${checks.sheetCount===1?"":"s"} (${checks.paper}, ${checks.profile}).`);return}const result=await withSlopcameraOperationHostAdmission("slopcamera.diagram.render",async()=>await renderDrawingFile({filePath,...outDirectory===undefined?{}:{outDirectory}}),hostAdmissionOptions(dependencies));log(json?JSON.stringify({command:"diagram.sheets.render",artifacts:result.artifacts,checks:result.checks}):[result.artifacts.pdf,...result.artifacts.sheets,result.artifacts.receipt].join(`
|
|
91
|
-
`));reportUsefulResult(dependencies.onUsefulResult)}function canonicalArguments(args){const[surface,subcommand,...rest]=args;if(surface==="diagram"){if(subcommand==="init"||subcommand==="check"||subcommand==="render"){return[subcommand,...rest]}throw new Error("Use slopcamera diagram init, check, render, or sheets")}if(surface==="image"){if(subcommand==="vectorize"||subcommand==="generate"||subcommand==="icon"||subcommand==="gallery"){return[subcommand,...rest]}throw new Error("Use slopcamera image vectorize, generate, icon, or gallery")}if(surface==="init"||surface==="check"||surface==="render"||surface==="vectorize"||surface==="generate"){throw new Error(`The flat \`${surface}\` command moved to a namespaced Slopcamera surface.
|
|
109
|
+
`));reportUsefulResult(dependencies.onUsefulResult)}function canonicalArguments(args){const[surface,subcommand,...rest]=args;if(surface==="diagram"){if(subcommand==="init"||subcommand==="check"||subcommand==="render"){return[subcommand,...rest]}throw new Error("Use slopcamera diagram init, check, render, or sheets")}if(surface==="media"){if(subcommand==="soundtrack")return["soundtrack",...rest];throw new Error("Use slopcamera media soundtrack compose or grid")}if(surface==="image"){if(subcommand==="vectorize"||subcommand==="generate"||subcommand==="icon"||subcommand==="gallery"){return[subcommand,...rest]}throw new Error("Use slopcamera image vectorize, generate, icon, or gallery")}if(surface==="init"||surface==="check"||surface==="render"||surface==="vectorize"||surface==="generate"){throw new Error(`The flat \`${surface}\` command moved to a namespaced Slopcamera surface.
|
|
92
110
|
|
|
93
|
-
${help()}`)}return args}
|
|
111
|
+
${help()}`)}return args}function rejectUnknownCliFlags(parsed,allowed,command){const unknown=[...parsed.flags].find((flag)=>!allowed.includes(flag));if(unknown!==undefined)throw new Error(`Unknown ${command} option: --${unknown}`)}function exactlyOnePositional(parsed,usage){if(parsed.positionals.length!==1)throw new Error(`Use ${usage}`);return parsed.positionals[0]}function logOperationResult(dependencies,json,receipt,summary){(dependencies.log??console.log)(json?JSON.stringify(receipt,null,2):summary);reportUsefulResult(dependencies.onUsefulResult)}async function runIconScene(action,args,dependencies){if(action==="compose"){const usage="slopcamera image icon compose <source.json> [--output <file.json>] [--json]";const parsed2=parseArguments(args,new Set(["output"]));rejectUnknownCliFlags(parsed2,["json"],"icon compose");const sourcePath2=exactlyOnePositional(parsed2,usage);const receipt2=await executeSlopcameraOperation("slopcamera.icon.compose",{sourcePath:sourcePath2,...parsed2.options.output===undefined?{}:{outputPath:parsed2.options.output}},hostAdmissionOptions(dependencies));logOperationResult(dependencies,parsed2.flags.has("json"),receipt2,`Composed ${receipt2.form} "${receipt2.title}": ${receipt2.elementCount} element${receipt2.elementCount===1?"":"s"}, geometry ${receipt2.digests.geometry}`+`${receipt2.diagnostics.length===0?"":`, ${receipt2.diagnostics.length} diagnostic${receipt2.diagnostics.length===1?"":"s"}`}`+`${receipt2.output===null?"":` -> ${receipt2.output.path}`}`);return}const parsed=parseArguments(args,new Set(["output","recipe","size","palette","language"]));rejectUnknownCliFlags(parsed,["json","background"],"icon render");const sourcePath=exactlyOnePositional(parsed,"slopcamera image icon render <source.json> --output <file.svg> [options]");const size=parsed.options.size;if(size!==undefined&&!/^\d{1,5}$/u.test(size)){throw new Error("--size must be an integer")}const receipt=await executeSlopcameraOperation("slopcamera.icon.render",{sourcePath,outputPath:requiredOption(parsed,"output"),...parsed.options.recipe===undefined?{}:{recipePath:parsed.options.recipe},...size===undefined?{}:{size:Number(size)},...parsed.flags.has("background")?{background:true}:{},...parsed.options.palette===undefined?{}:{palette:parsed.options.palette},...parsed.options.language===undefined?{}:{language:parsed.options.language}},hostAdmissionOptions(dependencies));logOperationResult(dependencies,parsed.flags.has("json"),receipt,`Rendered ${receipt.form} "${receipt.title}"${receipt.replayed?" (replayed recipe)":""}: ${receipt.output.path} `+`(${receipt.width}x${receipt.height}, svg ${receipt.digests.svg})`+`${receipt.recipe===null?"":`
|
|
112
|
+
Recipe: ${receipt.recipe.path}`}`)}async function runSoundtrack(args,dependencies){const[action,...rest]=args;if(action==="compose"){const parsed=parseArguments(rest,new Set(["output","format"]));rejectUnknownCliFlags(parsed,["json"],"soundtrack compose");const sourcePath=exactlyOnePositional(parsed,"slopcamera media soundtrack compose <score> [--output <file.json>] [--format <format>] [--json]");const receipt=await executeSlopcameraOperation("slopcamera.soundtrack.compose",{sourcePath,...parsed.options.output===undefined?{}:{outputPath:parsed.options.output},...parsed.options.format===undefined?{}:{format:parsed.options.format}},hostAdmissionOptions(dependencies));logOperationResult(dependencies,parsed.flags.has("json"),receipt,`Verified ${receipt.kind} "${receipt.title}": ${receipt.bpm} BPM, ${receipt.beatsPerBar}/bar, ${receipt.bars} bars, `+`${(receipt.durationUs/1e6).toFixed(3)} s, digest ${receipt.digest}`+`${receipt.output===null?"":` -> ${receipt.output.path}`}`);return}if(action==="grid"){const parsed=parseArguments(rest,new Set(["output","format","start-us"]));rejectUnknownCliFlags(parsed,["json"],"soundtrack grid");const sourcePath=exactlyOnePositional(parsed,"slopcamera media soundtrack grid <score> [--output <file.json>] [--start-us <n>] [--format <format>] [--json]");const startUs=parsed.options["start-us"];if(startUs!==undefined&&!/^\d{1,10}$/u.test(startUs)){throw new Error("--start-us must be a non-negative integer")}const receipt=await executeSlopcameraOperation("slopcamera.soundtrack.grid",{sourcePath,...parsed.options.output===undefined?{}:{outputPath:parsed.options.output},...parsed.options.format===undefined?{}:{format:parsed.options.format},...startUs===undefined?{}:{startUs:Number(startUs)}},hostAdmissionOptions(dependencies));logOperationResult(dependencies,parsed.flags.has("json"),receipt,[`Beat grid for ${receipt.scoreKind} "${receipt.title}": ${JSON.stringify(receipt.music)}`,...receipt.sections.map((section)=>` ${section.index}. ${section.label} bars ${section.startBar}-${section.endBar} ${section.startUs}-${section.endUs} us`),...receipt.output===null?[]:[`Grid: ${receipt.output.path}`]].join(`
|
|
113
|
+
`));return}throw new Error("Use slopcamera media soundtrack compose or grid")}async function main(args,dependencies={}){if(args[0]==="style"){const[action,...words]=args.slice(1);const json=words.at(-1)==="--json";const positionals=json?words.slice(0,-1):words;const log=dependencies.log??console.log;if(action==="list"&&positionals.length===0){log(json?JSON.stringify({schemaVersion:1,styles:VISUAL_STYLE_PROFILES}):["Visual style foundations (authoring guidance; no effects are applied):",...VISUAL_STYLE_PROFILES.map((style)=>`${style.id} ${style.name}
|
|
94
114
|
${style.summary}`),"Use slopcamera style show <id> for palette, cadence, materials, delivery, and review criteria."].join(`
|
|
95
115
|
`));return}if(action==="show"&&positionals.length===1&&!positionals[0].startsWith("-")){let style;try{style=getVisualStyleProfile(positionals[0])}catch{throw new Error(`Unknown visual style: ${JSON.stringify(positionals[0].slice(0,128))}. Use slopcamera style list.`)}log(json?JSON.stringify({schemaVersion:1,style}):createVisualStyleDirection(style.id));return}throw new Error("Use slopcamera style list [--json] or slopcamera style show <id> [--json].")}if(args[0]==="diagram"&&args[1]==="sheets"){await runDrawingSheets(args.slice(2),dependencies);return}if(args[0]==="support"){process.exitCode=await runProductSupportCommand(args.slice(1),dependencies.supportEnvironment===undefined?{}:{env:dependencies.supportEnvironment});return}const[command,...rest]=canonicalArguments(args);if(command===undefined||command==="--help"||command==="-h"){console.log(help());return}if(command==="help"){console.log(rest[0]==="advanced"?slopcameraSupportAdvancedHelp():help());return}if(command==="version"||command==="--version"||command==="-v"){console.log(slopcameraCliVersion);return}if(command==="init"){const parsed=parseArguments(rest,new Set);const filePath=resolve(parsed.positionals[0]??"diagram.diagram.json");if(await pathExists(filePath))throw new Error(`Refusing to overwrite existing file: ${filePath}`);await writeFile(filePath,`${JSON.stringify(starter,null,2)}
|
|
96
|
-
`);console.log(`Created ${filePath}`);reportUsefulResult(dependencies.onUsefulResult);return}if(command==="check"){const parsed=parseArguments(rest,new Set(["config"]));const result=await withSlopcameraOperationHostAdmission("slopcamera.diagram.check",async()=>await checkDiagramFile({filePath:requiredPositional(parsed,0,"diagram file"),...parsed.options.config===undefined?{}:{configPath:parsed.options.config}}),hostAdmissionOptions(dependencies));console.log(`Valid diagram${result.configPath===null?"":` with ${result.configPath}`}.`);printFindings(result.findings);if(parsed.flags.has("strict")&&result.findings.length>0)process.exitCode=2;return}if(command==="render"){const parsed=parseArguments(rest,new Set(["out-dir","config","scale"]));const scale=parsed.options.scale===undefined?undefined:Number.parseFloat(parsed.options.scale);const result=await withSlopcameraOperationHostAdmission("slopcamera.diagram.render",async()=>await renderDiagramFile({filePath:requiredPositional(parsed,0,"diagram file"),...parsed.options["out-dir"]===undefined?{}:{outDirectory:parsed.options["out-dir"]},...parsed.options.config===undefined?{}:{configPath:parsed.options.config},...scale===undefined?{}:{scale}}),hostAdmissionOptions(dependencies));console.log(artifactSummary(result.artifacts));printFindings(result.findings);reportUsefulResult(dependencies.onUsefulResult);return}if(command==="vectorize"){const parsed=parseArguments(rest,new Set(["output","duotone","alpha-cutoff","timeout-ms"]));const unknownFlags=[...parsed.flags].filter((flag)=>flag!=="json");if(unknownFlags.length>0){throw new Error(`Unknown vectorize option: --${unknownFlags[0]}`)}if(parsed.positionals.length>1){throw new Error("slopcamera image vectorize accepts exactly one raster input")}const output=requiredOption(parsed,"output");if(!output.toLowerCase().endsWith(".svg")){throw new Error("--output must end in .svg")}const alphaCutoff=parsePositiveInteger(parsed.options["alpha-cutoff"],"alpha-cutoff");const timeoutMs=parsePositiveInteger(parsed.options["timeout-ms"],"timeout-ms");const duotone=parseDuotone(parsed.options.duotone);const result=await withSlopcameraOperationHostAdmission("slopcamera.image.vectorize",async(lease)=>await(dependencies.vectorize??vectorizeImage)(requiredPositional(parsed,0,"raster image"),{...alphaCutoff===undefined?{}:{alphaCutoff},...duotone===undefined?{}:{duotone},...timeoutMs===undefined?{}:{limits:{maxDurationMs:timeoutMs}},inheritedFileDescriptors:[lease.inheritedFileDescriptor],outputPath:output}),hostAdmissionOptions(dependencies));if(parsed.flags.has("json")){(dependencies.log??console.log)(JSON.stringify({...result.receipt,outputPath:result.outputPath},null,2))}else{(dependencies.log??console.log)(`Vectorized ${result.receipt.width}\xD7${result.receipt.height} with `+`${result.receipt.profile}/${result.receipt.representation}: ${result.outputPath}`)}reportUsefulResult(dependencies.onUsefulResult);return}if(command==="generate"){const parsed=parseArguments(rest,new Set(["model","output"]));const unknownFlags=[...parsed.flags].filter((flag)=>flag!=="json");if(unknownFlags.length>0){throw new Error(`Unknown generate option: --${unknownFlags[0]}`)}if(parsed.positionals.length!==1){throw new Error("slopcamera image generate accepts exactly one prompt")}const model=parsed.options.model??slopcameraImageModels[1];if(model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model)){throw new Error("--model must be a bounded Vercel AI Gateway provider/model id")}const result=await withSlopcameraOperationHostAdmission("slopcamera.image.generate",async()=>await(dependencies.generate??generateSlopcameraImageFile)({model,prompt:requiredPositional(parsed,0,"prompt"),outputPath:requiredOption(parsed,"output")}),hostAdmissionOptions(dependencies));if(parsed.flags.has("json")){(dependencies.log??console.log)(JSON.stringify(result,null,2))}else{(dependencies.log??console.log)(`Generated ${result.mediaType} with ${result.model}: ${result.outputPath} (${result.bytes} bytes, request ${result.requestId})`)}reportUsefulResult(dependencies.onUsefulResult);return}if(command==="icon"){const parsed=parseArguments(rest,new Set(["model","output","output-dir","ink","purpose","rounds","critique-model","context","candidates","set","set-rounds"]));const unknownFlags=[...parsed.flags].filter((flag)=>flag!=="json"&&flag!=="keep-raster");if(unknownFlags.length>0){throw new Error(`Unknown icon option: --${unknownFlags[0]}`)}const model=parsed.options.model??slopcameraImageModels[1];if(model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model)){throw new Error("--model must be a bounded Vercel AI Gateway provider/model id")}const critiqueModel=parsed.options["critique-model"];if(critiqueModel!==undefined&&(critiqueModel.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(critiqueModel))){throw new Error("--critique-model must be a bounded Vercel AI Gateway provider/model id")}const ink=parsed.options.ink;if(ink!==undefined&&!/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(ink)){throw new Error("--ink must be a #rgb or #rrggbb color")}const purpose=parsed.options.purpose;if(purpose!==undefined&&purpose!=="mark"&&purpose!=="illustration"){throw new Error("--purpose must be mark or illustration")}const context=parsed.options.context;if(context!==undefined&&!slopcameraIconContexts.includes(context)){throw new Error(`--context must be one of: ${slopcameraIconContexts.join(", ")}`)}const rounds=parsePositiveInteger(parsed.options.rounds,"rounds");if(rounds!==undefined&&rounds>slopcameraIconMaximumRounds){throw new Error(`--rounds must be at most ${slopcameraIconMaximumRounds}`)}const candidates=parsePositiveInteger(parsed.options.candidates,"candidates");if(candidates!==undefined&&candidates>slopcameraIconMaximumRounds){throw new Error(`--candidates must be at most ${slopcameraIconMaximumRounds}`)}const setRounds=parsePositiveInteger(parsed.options["set-rounds"],"set-rounds");if(setRounds!==undefined&&setRounds>slopcameraIconSetMaximumSetRounds){throw new Error(`--set-rounds must be at most ${slopcameraIconSetMaximumSetRounds}`)}if(parsed.options.set!==undefined){if(parsed.positionals.length!==0){throw new Error("slopcamera image icon --set takes no subject positional")}if(parsed.options.output!==undefined){throw new Error("--output cannot be combined with --set; use --output-dir")}const outputDir=requiredOption(parsed,"output-dir");const manifestPath=parsed.options.set;let manifestText;try{manifestText=await readFile(manifestPath,"utf8")}catch{throw new Error(`--set manifest could not be read: ${manifestPath}`)}if(Buffer.byteLength(manifestText,"utf8")>256*1024){throw new Error("--set manifest exceeds the 256 KiB bound")}let manifestJson;try{manifestJson=JSON.parse(manifestText)}catch{throw new Error("--set manifest must be valid JSON")}const spec=parseSlopcameraIconSetSpec(manifestJson);const result2=await withSlopcameraOperationHostAdmission("slopcamera.image.icon",async(lease)=>await(dependencies.iconSet??generateSlopcameraIconSet)({keepRaster:parsed.flags.has("keep-raster"),manifestDir:dirname(resolve(manifestPath)),model,outputDir,spec,inheritedFileDescriptors:[lease.inheritedFileDescriptor],...critiqueModel===undefined?{}:{critiqueModel},...ink===undefined?{}:{ink},...context===undefined?{}:{context},...purpose===undefined?{}:{purpose},...rounds===undefined?{}:{rounds},...candidates===undefined?{}:{candidatesPerMember:candidates},...setRounds===undefined?{}:{setRounds}}),hostAdmissionOptions(dependencies));if(parsed.flags.has("json")){(dependencies.log??console.log)(JSON.stringify(result2,null,2))}else{(dependencies.log??console.log)(`Icon set ${result2.name??"icon-set"}: ${result2.members.length} members to ${result2.outputDir} `+`(target coverage ${result2.target.coverageRatio.toFixed(3)}, `+`stroke ${result2.target.strokePx.toFixed(1)}px, ${result2.setRoundsUsed} set rounds)`)}reportUsefulResult(dependencies.onUsefulResult);return}if(parsed.positionals.length!==1){throw new Error("slopcamera image icon accepts exactly one subject")}const output=requiredOption(parsed,"output");if(!output.toLowerCase().endsWith(".svg")){throw new Error("--output must end in .svg")}const result=await withSlopcameraOperationHostAdmission("slopcamera.image.icon",async(lease)=>await(dependencies.icon??generateSlopcameraIcon)({keepRaster:parsed.flags.has("keep-raster"),model,outputPath:output,subject:requiredPositional(parsed,0,"subject"),inheritedFileDescriptors:[lease.inheritedFileDescriptor],...critiqueModel===undefined?{}:{critiqueModel},...ink===undefined?{}:{ink},...purpose===undefined?{}:{purpose},...rounds===undefined?{}:{rounds},...context===undefined?{}:{context},...candidates===undefined?{}:{candidatePool:candidates}}),hostAdmissionOptions(dependencies));if(parsed.flags.has("json")){(dependencies.log??console.log)(JSON.stringify(result,null,2))}else{const scored=result.attempts.map((attempt)=>attempt.score===undefined?`#${attempt.round}`:`#${attempt.round}=${attempt.score}`).join(" ");(dependencies.log??console.log)(`Icon ${result.svgSha256.slice(0,12)} round ${result.selectedRound}/${result.attempts.length} (${scored}): ${result.outputPath}`)}return}if(command==="gallery"){const parsed=parseArguments(rest,new Set(["model","output-dir","kind","count","vary","candidates","cell","timeout-ms"]));const unknownFlags=[...parsed.flags].filter((flag)=>flag!=="json"&&flag!=="tile"&&flag!=="no-tile");if(unknownFlags.length>0){throw new Error(`Unknown gallery option: --${unknownFlags[0]}`)}if(parsed.flags.has("tile")&&parsed.flags.has("no-tile")){throw new Error("--tile and --no-tile are mutually exclusive")}if(parsed.positionals.length!==1){throw new Error("slopcamera image gallery accepts exactly one subject")}const model=parsed.options.model??slopcameraImageModels[1];if(model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model)){throw new Error("--model must be a bounded Vercel AI Gateway provider/model id")}const kind=parsed.options.kind;if(kind!==undefined&&!slopcameraGalleryKinds.includes(kind)){throw new Error(`--kind must be one of: ${slopcameraGalleryKinds.join(", ")}`)}const count=parsePositiveInteger(parsed.options.count,"count");if(count!==undefined&&count>slopcameraGalleryLimits.candidates){throw new Error(`--count must be at most ${slopcameraGalleryLimits.candidates}`)}const cell=parsePositiveInteger(parsed.options.cell,"cell");if(cell!==undefined&&(cell<slopcameraGalleryLimits.cellEdgeMin||cell>slopcameraGalleryLimits.cellEdgeMax)){throw new Error(`--cell must be between ${slopcameraGalleryLimits.cellEdgeMin} and ${slopcameraGalleryLimits.cellEdgeMax}`)}const timeoutMs=parsePositiveInteger(parsed.options["timeout-ms"],"timeout-ms");const vary=parseGalleryVary(parsed.options.vary);const candidates=parsed.options.candidates===undefined?undefined:await readGalleryCandidates(parsed.options.candidates);if(candidates!==undefined&&!Array.isArray(candidates)){throw new Error("--candidates JSON must be an array of {id, prompt|variant} entries")}const result=await withSlopcameraOperationHostAdmission("slopcamera.image.gallery",async()=>await(dependencies.gallery??generateSlopcameraImageGallery)({subject:requiredPositional(parsed,0,"subject"),outputDir:requiredOption(parsed,"output-dir"),model,...kind===undefined?{}:{kind},...count===undefined?{}:{count},...vary===undefined?{}:{vary},...candidates===undefined?{}:{candidates},...cell===undefined?{}:{cellEdge:cell},...timeoutMs===undefined?{}:{timeoutMs},...parsed.flags.has("tile")?{tiled:true}:parsed.flags.has("no-tile")?{tiled:false}:{}}),hostAdmissionOptions(dependencies));if(parsed.flags.has("json")){(dependencies.log??console.log)(JSON.stringify(result,null,2))}else{const rows=result.candidates.map((candidate)=>candidate.status==="generated"?`#${candidate.index} ${candidate.id} ${candidate.path}`:`#${candidate.index} ${candidate.id} failed`);(dependencies.log??console.log)(`Gallery ${result.counts.generated}/${result.counts.requested} generated (${result.model}): ${result.gallery.path}
|
|
116
|
+
`);console.log(`Created ${filePath}`);reportUsefulResult(dependencies.onUsefulResult);return}if(command==="check"){const parsed=parseArguments(rest,new Set(["config"]));const result=await withSlopcameraOperationHostAdmission("slopcamera.diagram.check",async()=>await checkDiagramFile({filePath:requiredPositional(parsed,0,"diagram file"),...parsed.options.config===undefined?{}:{configPath:parsed.options.config}}),hostAdmissionOptions(dependencies));console.log(`Valid diagram${result.configPath===null?"":` with ${result.configPath}`}.`);printFindings(result.findings);if(parsed.flags.has("strict")&&result.findings.length>0)process.exitCode=2;return}if(command==="render"){const parsed=parseArguments(rest,new Set(["out-dir","config","scale"]));const scale=parsed.options.scale===undefined?undefined:Number.parseFloat(parsed.options.scale);const result=await withSlopcameraOperationHostAdmission("slopcamera.diagram.render",async()=>await renderDiagramFile({filePath:requiredPositional(parsed,0,"diagram file"),...parsed.options["out-dir"]===undefined?{}:{outDirectory:parsed.options["out-dir"]},...parsed.options.config===undefined?{}:{configPath:parsed.options.config},...scale===undefined?{}:{scale}}),hostAdmissionOptions(dependencies));console.log(artifactSummary(result.artifacts));printFindings(result.findings);reportUsefulResult(dependencies.onUsefulResult);return}if(command==="vectorize"){const parsed=parseArguments(rest,new Set(["output","duotone","alpha-cutoff","timeout-ms"]));const unknownFlags=[...parsed.flags].filter((flag)=>flag!=="json");if(unknownFlags.length>0){throw new Error(`Unknown vectorize option: --${unknownFlags[0]}`)}if(parsed.positionals.length>1){throw new Error("slopcamera image vectorize accepts exactly one raster input")}const output=requiredOption(parsed,"output");if(!output.toLowerCase().endsWith(".svg")){throw new Error("--output must end in .svg")}const alphaCutoff=parsePositiveInteger(parsed.options["alpha-cutoff"],"alpha-cutoff");const timeoutMs=parsePositiveInteger(parsed.options["timeout-ms"],"timeout-ms");const duotone=parseDuotone(parsed.options.duotone);const result=await withSlopcameraOperationHostAdmission("slopcamera.image.vectorize",async(lease)=>await(dependencies.vectorize??vectorizeImage)(requiredPositional(parsed,0,"raster image"),{...alphaCutoff===undefined?{}:{alphaCutoff},...duotone===undefined?{}:{duotone},...timeoutMs===undefined?{}:{limits:{maxDurationMs:timeoutMs}},inheritedFileDescriptors:[lease.inheritedFileDescriptor],outputPath:output}),hostAdmissionOptions(dependencies));if(parsed.flags.has("json")){(dependencies.log??console.log)(JSON.stringify({...result.receipt,outputPath:result.outputPath},null,2))}else{(dependencies.log??console.log)(`Vectorized ${result.receipt.width}\xD7${result.receipt.height} with `+`${result.receipt.profile}/${result.receipt.representation}: ${result.outputPath}`)}reportUsefulResult(dependencies.onUsefulResult);return}if(command==="generate"){const parsed=parseArguments(rest,new Set(["model","output"]));const unknownFlags=[...parsed.flags].filter((flag)=>flag!=="json");if(unknownFlags.length>0){throw new Error(`Unknown generate option: --${unknownFlags[0]}`)}if(parsed.positionals.length!==1){throw new Error("slopcamera image generate accepts exactly one prompt")}const model=parsed.options.model??slopcameraImageModels[1];if(model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model)){throw new Error("--model must be a bounded Vercel AI Gateway provider/model id")}const result=await withSlopcameraOperationHostAdmission("slopcamera.image.generate",async()=>await(dependencies.generate??generateSlopcameraImageFile)({model,prompt:requiredPositional(parsed,0,"prompt"),outputPath:requiredOption(parsed,"output")}),hostAdmissionOptions(dependencies));if(parsed.flags.has("json")){(dependencies.log??console.log)(JSON.stringify(result,null,2))}else{(dependencies.log??console.log)(`Generated ${result.mediaType} with ${result.model}: ${result.outputPath} (${result.bytes} bytes, request ${result.requestId})`)}reportUsefulResult(dependencies.onUsefulResult);return}if(command==="icon"&&(rest[0]==="compose"||rest[0]==="render")){await runIconScene(rest[0],rest.slice(1),dependencies);return}if(command==="soundtrack"){await runSoundtrack(rest,dependencies);return}if(command==="icon"){const parsed=parseArguments(rest,new Set(["model","output","output-dir","ink","purpose","rounds","critique-model","context","candidates","set","set-rounds"]));const unknownFlags=[...parsed.flags].filter((flag)=>flag!=="json"&&flag!=="keep-raster");if(unknownFlags.length>0){throw new Error(`Unknown icon option: --${unknownFlags[0]}`)}const model=parsed.options.model??slopcameraImageModels[1];if(model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model)){throw new Error("--model must be a bounded Vercel AI Gateway provider/model id")}const critiqueModel=parsed.options["critique-model"];if(critiqueModel!==undefined&&(critiqueModel.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(critiqueModel))){throw new Error("--critique-model must be a bounded Vercel AI Gateway provider/model id")}const ink=parsed.options.ink;if(ink!==undefined&&!/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(ink)){throw new Error("--ink must be a #rgb or #rrggbb color")}const purpose=parsed.options.purpose;if(purpose!==undefined&&purpose!=="mark"&&purpose!=="illustration"){throw new Error("--purpose must be mark or illustration")}const context=parsed.options.context;if(context!==undefined&&!slopcameraIconContexts.includes(context)){throw new Error(`--context must be one of: ${slopcameraIconContexts.join(", ")}`)}const rounds=parsePositiveInteger(parsed.options.rounds,"rounds");if(rounds!==undefined&&rounds>slopcameraIconMaximumRounds){throw new Error(`--rounds must be at most ${slopcameraIconMaximumRounds}`)}const candidates=parsePositiveInteger(parsed.options.candidates,"candidates");if(candidates!==undefined&&candidates>slopcameraIconMaximumRounds){throw new Error(`--candidates must be at most ${slopcameraIconMaximumRounds}`)}const setRounds=parsePositiveInteger(parsed.options["set-rounds"],"set-rounds");if(setRounds!==undefined&&setRounds>slopcameraIconSetMaximumSetRounds){throw new Error(`--set-rounds must be at most ${slopcameraIconSetMaximumSetRounds}`)}if(parsed.options.set!==undefined){if(parsed.positionals.length!==0){throw new Error("slopcamera image icon --set takes no subject positional")}if(parsed.options.output!==undefined){throw new Error("--output cannot be combined with --set; use --output-dir")}const outputDir=requiredOption(parsed,"output-dir");const manifestPath=parsed.options.set;let manifestText;try{manifestText=await readFile(manifestPath,"utf8")}catch{throw new Error(`--set manifest could not be read: ${manifestPath}`)}if(Buffer.byteLength(manifestText,"utf8")>256*1024){throw new Error("--set manifest exceeds the 256 KiB bound")}let manifestJson;try{manifestJson=JSON.parse(manifestText)}catch{throw new Error("--set manifest must be valid JSON")}const spec=parseSlopcameraIconSetSpec(manifestJson);const result2=await withSlopcameraOperationHostAdmission("slopcamera.image.icon",async(lease)=>await(dependencies.iconSet??generateSlopcameraIconSet)({keepRaster:parsed.flags.has("keep-raster"),manifestDir:dirname(resolve(manifestPath)),model,outputDir,spec,inheritedFileDescriptors:[lease.inheritedFileDescriptor],...critiqueModel===undefined?{}:{critiqueModel},...ink===undefined?{}:{ink},...context===undefined?{}:{context},...purpose===undefined?{}:{purpose},...rounds===undefined?{}:{rounds},...candidates===undefined?{}:{candidatesPerMember:candidates},...setRounds===undefined?{}:{setRounds}}),hostAdmissionOptions(dependencies));if(parsed.flags.has("json")){(dependencies.log??console.log)(JSON.stringify(result2,null,2))}else{(dependencies.log??console.log)(`Icon set ${result2.name??"icon-set"}: ${result2.members.length} members to ${result2.outputDir} `+`(target coverage ${result2.target.coverageRatio.toFixed(3)}, `+`stroke ${result2.target.strokePx.toFixed(1)}px, ${result2.setRoundsUsed} set rounds)`)}reportUsefulResult(dependencies.onUsefulResult);return}if(parsed.positionals.length!==1){throw new Error("slopcamera image icon accepts exactly one subject")}const output=requiredOption(parsed,"output");if(!output.toLowerCase().endsWith(".svg")){throw new Error("--output must end in .svg")}const result=await withSlopcameraOperationHostAdmission("slopcamera.image.icon",async(lease)=>await(dependencies.icon??generateSlopcameraIcon)({keepRaster:parsed.flags.has("keep-raster"),model,outputPath:output,subject:requiredPositional(parsed,0,"subject"),inheritedFileDescriptors:[lease.inheritedFileDescriptor],...critiqueModel===undefined?{}:{critiqueModel},...ink===undefined?{}:{ink},...purpose===undefined?{}:{purpose},...rounds===undefined?{}:{rounds},...context===undefined?{}:{context},...candidates===undefined?{}:{candidatePool:candidates}}),hostAdmissionOptions(dependencies));if(parsed.flags.has("json")){(dependencies.log??console.log)(JSON.stringify(result,null,2))}else{const scored=result.attempts.map((attempt)=>attempt.score===undefined?`#${attempt.round}`:`#${attempt.round}=${attempt.score}`).join(" ");(dependencies.log??console.log)(`Icon ${result.svgSha256.slice(0,12)} round ${result.selectedRound}/${result.attempts.length} (${scored}): ${result.outputPath}`)}return}if(command==="gallery"){const parsed=parseArguments(rest,new Set(["model","output-dir","kind","count","vary","candidates","cell","timeout-ms"]));const unknownFlags=[...parsed.flags].filter((flag)=>flag!=="json"&&flag!=="tile"&&flag!=="no-tile");if(unknownFlags.length>0){throw new Error(`Unknown gallery option: --${unknownFlags[0]}`)}if(parsed.flags.has("tile")&&parsed.flags.has("no-tile")){throw new Error("--tile and --no-tile are mutually exclusive")}if(parsed.positionals.length!==1){throw new Error("slopcamera image gallery accepts exactly one subject")}const model=parsed.options.model??slopcameraImageModels[1];if(model.length>256||!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model)){throw new Error("--model must be a bounded Vercel AI Gateway provider/model id")}const kind=parsed.options.kind;if(kind!==undefined&&!slopcameraGalleryKinds.includes(kind)){throw new Error(`--kind must be one of: ${slopcameraGalleryKinds.join(", ")}`)}const count=parsePositiveInteger(parsed.options.count,"count");if(count!==undefined&&count>slopcameraGalleryLimits.candidates){throw new Error(`--count must be at most ${slopcameraGalleryLimits.candidates}`)}const cell=parsePositiveInteger(parsed.options.cell,"cell");if(cell!==undefined&&(cell<slopcameraGalleryLimits.cellEdgeMin||cell>slopcameraGalleryLimits.cellEdgeMax)){throw new Error(`--cell must be between ${slopcameraGalleryLimits.cellEdgeMin} and ${slopcameraGalleryLimits.cellEdgeMax}`)}const timeoutMs=parsePositiveInteger(parsed.options["timeout-ms"],"timeout-ms");const vary=parseGalleryVary(parsed.options.vary);const candidates=parsed.options.candidates===undefined?undefined:await readGalleryCandidates(parsed.options.candidates);if(candidates!==undefined&&!Array.isArray(candidates)){throw new Error("--candidates JSON must be an array of {id, prompt|variant} entries")}const result=await withSlopcameraOperationHostAdmission("slopcamera.image.gallery",async()=>await(dependencies.gallery??generateSlopcameraImageGallery)({subject:requiredPositional(parsed,0,"subject"),outputDir:requiredOption(parsed,"output-dir"),model,...kind===undefined?{}:{kind},...count===undefined?{}:{count},...vary===undefined?{}:{vary},...candidates===undefined?{}:{candidates},...cell===undefined?{}:{cellEdge:cell},...timeoutMs===undefined?{}:{timeoutMs},...parsed.flags.has("tile")?{tiled:true}:parsed.flags.has("no-tile")?{tiled:false}:{}}),hostAdmissionOptions(dependencies));if(parsed.flags.has("json")){(dependencies.log??console.log)(JSON.stringify(result,null,2))}else{const rows=result.candidates.map((candidate)=>candidate.status==="generated"?`#${candidate.index} ${candidate.id} ${candidate.path}`:`#${candidate.index} ${candidate.id} failed`);(dependencies.log??console.log)(`Gallery ${result.counts.generated}/${result.counts.requested} generated (${result.model}): ${result.gallery.path}
|
|
97
117
|
`+`${rows.join(`
|
|
98
118
|
`)}
|
|
99
119
|
receipt ${result.receiptPath}`)}reportUsefulResult(dependencies.onUsefulResult);return}if(command==="code"){const[subcommand,...subcommandArgs]=rest;if(subcommand==="search"){const parsed=parseArguments(subcommandArgs,new Set(["limit"]));if(parsed.flags.size>0||parsed.positionals.length>1){throw new Error("Use slopcamera code search [query] [--limit <number>]")}const limit=parsePositiveInteger(parsed.options.limit,"limit")??slopcameraOperationCodes.length;const operations=searchSlopcameraOperations(parsed.positionals[0]??"",limit);console.log(JSON.stringify({operations},null,2));return}if(subcommand==="execute"){const parsed=parseArguments(subcommandArgs,new Set(["input"]));if(parsed.flags.size>0||parsed.positionals.length!==1){throw new Error("Use slopcamera code execute <operation> --input <JSON>")}const requestedOperation=parsed.positionals[0];const operation=isSlopcameraOperationCode(requestedOperation)?requestedOperation:undefined;if(operation===undefined){throw new Error(`Unknown Slopcamera operation code: ${requestedOperation}`)}const inputText=requiredOption(parsed,"input");if(Buffer.byteLength(inputText,"utf8")>64*1024){throw new Error("--input JSON must be no more than 65536 UTF-8 bytes")}let input;try{input=JSON.parse(inputText)}catch{throw new Error("--input must be valid JSON")}const result=await executeSlopcameraOperation(operation,input,{...hostAdmissionOptions(dependencies)});(dependencies.log??console.log)(JSON.stringify({operation,result},null,2));if(operation==="slopcamera.diagram.render"||operation==="slopcamera.image.vectorize"||operation==="slopcamera.image.generate"){reportUsefulResult(dependencies.onUsefulResult)}return}throw new Error("Use slopcamera code search [query] or slopcamera code execute <operation> --input <JSON>")}if(command==="mcp"){const parsed=parseArguments(rest,new Set(["root"]));if(parsed.positionals.length>0||parsed.flags.size>0){throw new Error("slopcamera mcp accepts only --root <workspace>")}await runMcpServer({rootDirectory:requiredOption(parsed,"root"),serverVersion:slopcameraCliVersion});return}if(command==="doctor"){console.log(`slopcamera ${slopcameraCliVersion}`);console.log(`Bun ${process.versions.bun??"not detected"}`);console.log("Headless diagram SVG/PNG/tldraw renderer ready");console.log(process.platform==="win32"?"Local raster-to-SVG vectorizer unavailable on Windows (fails closed with tool_platform)":"Local raster-to-SVG vectorizer ready without authentication (VTracer downloads on first use)");console.log("Root-relative MCP check/render/scene server ready (trusted local workspace)");const gateway=slopcameraGatewayCredentialStatus();console.log(gateway.available?`Vercel AI Gateway ready via ${gateway.source}`:"Vercel AI Gateway requires AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN");return}if(command==="skill"){const[subcommand,...subcommandArgs]=rest;if(subcommand==="path"){const{bundledSkillPath}=await import("./skill-install-gx7afxsd.js");console.log(bundledSkillPath());return}if(subcommand==="install"){const parsed=parseArguments(subcommandArgs,new Set(["target","scope","project"]));const target=parsed.options.target??"codex";const scope=parsed.options.scope??"user";if(!["codex","claude","agents"].includes(target)){throw new Error("--target must be codex, claude, or agents")}if(!["user","project"].includes(scope)){throw new Error("--scope must be user or project")}const destination=await installSkill({target,scope,...parsed.options.project===undefined?{}:{projectDirectory:parsed.options.project},force:parsed.flags.has("force")});console.log(`Installed slopcamera skill at ${destination}`);return}throw new Error("Use slopcamera skill path or install")}throw new Error(`Unknown command: ${command}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{executeSlopcameraOperationWithLease,isSlopcameraOperationCode,parseSlopcameraOperationInput,slopcameraOperationHostResourceClaims}from"./index-
|
|
2
|
+
import{executeSlopcameraOperationWithLease,isSlopcameraOperationCode,parseSlopcameraOperationInput,slopcameraOperationHostResourceClaims}from"./index-mgychzh3.js";import{createDefaultHostResourceCoordinator}from"./index-b3geqczn.js";var workflowIdPattern=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u;var workflowStepIdPattern=/^[A-Za-z0-9]+(?:[._:-][A-Za-z0-9]+)*$/u;var defaultMaximumSteps=64;var hardMaximumSteps=256;class SlopcameraWorkflowError extends Error{code;completedSteps;failedStep;constructor(code,message,options={}){super(`[${code}] ${message}`,{cause:options.cause});this.name="SlopcameraWorkflowError";this.code=code;this.completedSteps=Object.freeze([...options.completedSteps??[]].sort((left,right)=>left.index-right.index));if(options.failedStep!==undefined){this.failedStep=Object.freeze({...options.failedStep})}}}function workflowError(code,message){throw new SlopcameraWorkflowError(code,message)}function validateWorkflowId(id){if(typeof id!=="string"||id.length<1||id.length>80||!workflowIdPattern.test(id)){workflowError("INVALID_WORKFLOW","Workflow id must be 1 through 80 lowercase letters, numbers, dots, underscores, or hyphens.")}}function validateStepId(id,completedSteps=[]){if(typeof id!=="string"||id.length<1||id.length>80||!workflowStepIdPattern.test(id)){throw new SlopcameraWorkflowError("INVALID_WORKFLOW_STEP","Step id must be 1 through 80 letters, numbers, dots, underscores, colons, or hyphens.",{completedSteps})}}function defineSlopcameraWorkflow(options){if(typeof options!=="object"||options===null){workflowError("INVALID_WORKFLOW","Workflow definition must be an object.")}validateWorkflowId(options.id);if(!Number.isSafeInteger(options.version)||options.version<1){workflowError("INVALID_WORKFLOW","Workflow version must be a positive safe integer.")}if(typeof options.parseInput!=="function"||typeof options.run!=="function"){workflowError("INVALID_WORKFLOW","Workflow definition requires parseInput and run functions.")}return Object.freeze({id:options.id,version:options.version,parseInput:options.parseInput,run:options.run})}function maximumSteps(value){const resolved=value??defaultMaximumSteps;if(!Number.isSafeInteger(resolved)||resolved<1||resolved>hardMaximumSteps){workflowError("INVALID_WORKFLOW",`maximumSteps must be an integer from 1 through ${hardMaximumSteps}.`)}return resolved}function aborted(completedSteps,cause){return new SlopcameraWorkflowError("WORKFLOW_ABORTED","Workflow execution was aborted.",{cause,completedSteps})}async function runSlopcameraWorkflow(definition,value,options={}){const normalized=defineSlopcameraWorkflow(definition);const limit=maximumSteps(options.maximumSteps);const signal=options.signal??options.dependencies?.signal??new AbortController().signal;const invoked=new Set;const completed=[];const dispatched=[];let acceptingOperations=true;let nextIndex=0;if(signal.aborted)throw aborted(completed);let input;try{input=normalized.parseInput(value)}catch(cause){throw new SlopcameraWorkflowError("INVALID_WORKFLOW_INPUT","Workflow input did not satisfy its parser.",{cause})}const executor=options.executor??((code,operationInput,context2)=>executeSlopcameraOperationWithLease(code,operationInput,context2.hostResourceLease,options.dependencies));const hostResourceCoordinator=options.hostResourceCoordinator??options.dependencies?.hostResourceCoordinator??createDefaultHostResourceCoordinator();const waitTimeoutMilliseconds=options.waitTimeoutMilliseconds??options.dependencies?.waitTimeoutMilliseconds;async function dispatchOperation(id,code,operationInput){if(signal.aborted)throw aborted(completed);validateStepId(id,completed);if(!isSlopcameraOperationCode(code)){throw new SlopcameraWorkflowError("INVALID_WORKFLOW_STEP",`Workflow step ${id} names an unknown Slopcamera operation.`,{completedSteps:completed})}if(invoked.has(id)){throw new SlopcameraWorkflowError("INVALID_WORKFLOW_STEP",`Duplicate workflow step id: ${id}.`,{completedSteps:completed})}if(nextIndex>=limit){throw new SlopcameraWorkflowError("INVALID_WORKFLOW_STEP",`Workflow exceeds its ${String(limit)}-step limit.`,{completedSteps:completed})}const index=nextIndex;nextIndex+=1;invoked.add(id);let normalizedInput;try{normalizedInput=parseSlopcameraOperationInput(code,operationInput)}catch(cause){throw new SlopcameraWorkflowError("INVALID_WORKFLOW_STEP",`Workflow step ${id} has invalid input for ${code}.`,{cause,completedSteps:completed})}try{const result=await hostResourceCoordinator.withLease(slopcameraOperationHostResourceClaims(code),async(hostResourceLease)=>await executor(code,normalizedInput,{hostResourceLease,signal,stepId:id}),{signal,...waitTimeoutMilliseconds===undefined?{}:{waitTimeoutMilliseconds}});completed.push(Object.freeze({id,index,operation:code}));if(signal.aborted)throw aborted(completed);return result}catch(cause){if(cause instanceof SlopcameraWorkflowError&&cause.code==="WORKFLOW_ABORTED"){throw cause}if(signal.aborted)throw aborted(completed,cause);throw new SlopcameraWorkflowError("WORKFLOW_STEP_FAILED",`Workflow step ${id} (${code}) failed.`,{cause,completedSteps:completed,failedStep:{id,operation:code}})}}const context=Object.freeze({signal,operation(id,code,operationInput){if(!acceptingOperations){const closed=Promise.reject(new SlopcameraWorkflowError("INVALID_WORKFLOW_STEP","Workflow operations cannot start after authored workflow code has settled.",{completedSteps:completed}));closed.catch(()=>{return});return closed}const operation=dispatchOperation(id,code,operationInput);dispatched.push(operation);operation.catch(()=>{return});return operation}});let output;let runFailed=false;let runFailure;try{output=await normalized.run(context,input)}catch(cause){runFailed=true;runFailure=cause}finally{acceptingOperations=false}const operationResults=await Promise.allSettled(dispatched);if(signal.aborted)throw aborted(completed,runFailed?runFailure:undefined);if(runFailed){if(runFailure instanceof SlopcameraWorkflowError)throw runFailure;throw new SlopcameraWorkflowError("WORKFLOW_FAILED",`Workflow ${normalized.id} failed in authored code.`,{cause:runFailure,completedSteps:completed})}const operationFailure=operationResults.find((result)=>result.status==="rejected");if(operationFailure!==undefined){const cause=operationFailure.reason;if(cause instanceof SlopcameraWorkflowError)throw cause;throw new SlopcameraWorkflowError("WORKFLOW_STEP_FAILED","A dispatched workflow operation failed.",{cause,completedSteps:completed})}return Object.freeze({workflow:Object.freeze({id:normalized.id,version:normalized.version}),output,steps:Object.freeze([...completed].sort((left,right)=>left.index-right.index))})}
|
|
3
3
|
export{SlopcameraWorkflowError,defineSlopcameraWorkflow,runSlopcameraWorkflow};
|