@hraness/slopcamera 3.2.8 → 3.3.2

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 (153) hide show
  1. package/README.md +29 -9
  2. package/apps/desktop/application/context.ts +6 -0
  3. package/apps/desktop/application/default-registry.ts +26 -0
  4. package/apps/desktop/application/operation.ts +14 -1
  5. package/apps/desktop/application/operations/index.ts +4 -0
  6. package/apps/desktop/application/operations/spatial-behavior.ts +151 -0
  7. package/apps/desktop/application/operations/spatial-direction.ts +222 -0
  8. package/apps/desktop/application/operations/spatial-rendered-audit.ts +174 -0
  9. package/apps/desktop/application/operations/spatial-review.ts +194 -0
  10. package/apps/desktop/application/operations/spatial-scene.ts +25 -1
  11. package/apps/desktop/application/registry.ts +2 -2
  12. package/apps/desktop/application/spatial-asset-admission.ts +201 -0
  13. package/apps/desktop/application/spatial-assets.ts +160 -31
  14. package/apps/desktop/application/spatial-render.ts +217 -20
  15. package/apps/desktop/application/spatial-rendered-audit.ts +273 -0
  16. package/apps/desktop/application/spatial-review.ts +361 -0
  17. package/apps/desktop/application/spatial-spz.ts +48 -3
  18. package/apps/desktop/application/spatial-world-import.ts +26 -5
  19. package/apps/desktop/application/spatial-world-metadata.ts +105 -0
  20. package/apps/desktop/cli/args.ts +514 -11
  21. package/apps/desktop/cli/capability-manifest.ts +374 -0
  22. package/apps/desktop/cli/cinema-renderer.ts +608 -0
  23. package/apps/desktop/cli/command-host-resources.ts +29 -1
  24. package/apps/desktop/cli/commands.ts +1188 -4
  25. package/apps/desktop/cli/gateway-review-provider.ts +334 -0
  26. package/apps/desktop/cli/help.ts +172 -6
  27. package/apps/desktop/cli/main.ts +3 -3
  28. package/apps/desktop/cli/portable-surface.ts +6 -1
  29. package/apps/desktop/cli/spatial-asset-service.ts +124 -0
  30. package/apps/desktop/cli/spatial-design-service.ts +237 -0
  31. package/apps/desktop/cli/spatial-generate-service.ts +370 -0
  32. package/apps/desktop/cli/spatial-scene-service.ts +271 -4
  33. package/apps/desktop/cli/workflow-code.ts +1 -1
  34. package/apps/desktop/code/semantic-builder.ts +24 -0
  35. package/apps/desktop/contracts/cinema.ts +736 -0
  36. package/apps/desktop/contracts/index.ts +1 -0
  37. package/apps/desktop/contracts/spatial-asset.ts +16 -0
  38. package/apps/desktop/contracts/spatial-world.ts +26 -3
  39. package/apps/desktop/core/cinema-plan.ts +1320 -0
  40. package/apps/desktop/core/index.ts +1 -0
  41. package/apps/desktop/core/storage.ts +75 -0
  42. package/apps/desktop/dist/cli/main.js +725 -273
  43. package/apps/desktop/html-overlay/spatial.ts +619 -57
  44. package/apps/desktop/workflows/cinematic-world.ts +127 -0
  45. package/apps/desktop/workflows/index.ts +6 -0
  46. package/dist/cli.js +25 -429
  47. package/dist/code/advanced.js +1 -269
  48. package/dist/code/index.js +16 -2239
  49. package/dist/generate.js +1 -23
  50. package/dist/host-resources.js +1 -25
  51. package/dist/index-0bbesn4m.js +35 -0
  52. package/dist/index-296qhpf2.js +3 -0
  53. package/dist/index-3cxf2c58.js +3 -0
  54. package/dist/index-77fjfg9f.js +3 -0
  55. package/dist/index-80a36bc0.js +3 -0
  56. package/dist/index-a3vvs3rf.js +3 -0
  57. package/dist/index-b3geqczn.js +5 -0
  58. package/dist/index-grk47tgs.js +3 -0
  59. package/dist/index-gs48afpt.js +7 -0
  60. package/dist/index-qry58nj2.js +10 -0
  61. package/dist/index-zhg36tf9.js +3 -0
  62. package/dist/index.js +1 -226
  63. package/dist/operations.js +1 -28
  64. package/dist/skill-install-gx7afxsd.js +2 -0
  65. package/dist/vectorize/worker.js +1 -141
  66. package/dist/workflow.js +1 -15
  67. package/docs/README.md +5 -1
  68. package/docs/studio.md +12 -0
  69. package/package.json +7 -5
  70. package/skills/slopcamera/SKILL.md +7 -1
  71. package/skills/slopcamera/references/directed-scenes.md +48 -2
  72. package/skills/slopcamera/references/gateway-media.md +15 -1
  73. package/skills/slopcamera/references/image-galleries.md +166 -0
  74. package/skills/slopcamera/references/install.md +1 -1
  75. package/skills/slopcamera/references/music-video.md +4 -0
  76. package/skills/slopcamera/references/native-studio.md +19 -0
  77. package/skills/slopcamera/references/parametric-design.md +14 -0
  78. package/skills/slopcamera/references/scene-building.md +172 -0
  79. package/skills/slopcamera/references/social-collage-banners.md +204 -0
  80. package/skills/slopcamera/references/workflows-sdk.md +3 -3
  81. package/skills/slopcamera/scripts/compose-social-collage-banner.ts +728 -0
  82. package/src/capability-manifest.ts +361 -0
  83. package/src/cli.ts +222 -4
  84. package/src/code/canonical-json.ts +9 -0
  85. package/src/generate.ts +2 -2
  86. package/src/icon.ts +1111 -0
  87. package/src/image-gallery.ts +1049 -0
  88. package/src/index.ts +11 -0
  89. package/src/mcp/boundary.ts +11 -11
  90. package/src/mcp/index.ts +3 -0
  91. package/src/mcp/server.ts +2 -1
  92. package/src/mcp/tools.ts +1649 -132
  93. package/src/mcp/types.ts +19 -0
  94. package/src/operations.ts +412 -0
  95. package/src/portable-capability-manifest.ts +67 -0
  96. package/src/scene-gallery.ts +184 -0
  97. package/src/spatial-scene/asset-admission.ts +183 -0
  98. package/src/spatial-scene/audit-rendered.ts +692 -0
  99. package/src/spatial-scene/audit.ts +617 -0
  100. package/src/spatial-scene/behavior-audit.ts +252 -0
  101. package/src/spatial-scene/behavior-bake.ts +232 -0
  102. package/src/spatial-scene/behavior-fns.ts +585 -0
  103. package/src/spatial-scene/behavior-gallery.ts +108 -0
  104. package/src/spatial-scene/behavior-stdlib.ts +323 -0
  105. package/src/spatial-scene/behavior-trace.ts +325 -0
  106. package/src/spatial-scene/behavior.ts +617 -0
  107. package/src/spatial-scene/build.ts +632 -0
  108. package/src/spatial-scene/camera-rig.ts +125 -0
  109. package/src/spatial-scene/camera-track.ts +4 -2
  110. package/src/spatial-scene/character.ts +124 -0
  111. package/src/spatial-scene/contracts.ts +75 -7
  112. package/src/spatial-scene/design-templates.ts +351 -0
  113. package/src/spatial-scene/design.ts +327 -0
  114. package/src/spatial-scene/direction-compile.ts +584 -0
  115. package/src/spatial-scene/direction.ts +122 -0
  116. package/src/spatial-scene/effects.ts +149 -0
  117. package/src/spatial-scene/evaluate.ts +109 -29
  118. package/src/spatial-scene/gallery.ts +277 -0
  119. package/src/spatial-scene/generate.ts +239 -0
  120. package/src/spatial-scene/geometry-native.ts +165 -0
  121. package/src/spatial-scene/geometry.ts +1216 -0
  122. package/src/spatial-scene/gltf.ts +600 -77
  123. package/src/spatial-scene/identity.ts +55 -2
  124. package/src/spatial-scene/index.ts +36 -0
  125. package/src/spatial-scene/inspect.ts +35 -6
  126. package/src/spatial-scene/material-lighting.ts +609 -0
  127. package/src/spatial-scene/math.ts +21 -4
  128. package/src/spatial-scene/motion-evidence.ts +88 -0
  129. package/src/spatial-scene/parametric.ts +765 -0
  130. package/src/spatial-scene/particle-preparation.ts +183 -0
  131. package/src/spatial-scene/particle.ts +195 -0
  132. package/src/spatial-scene/patch.ts +109 -15
  133. package/src/spatial-scene/performance.ts +1613 -0
  134. package/src/spatial-scene/probe.ts +137 -0
  135. package/src/spatial-scene/recipe-pack.ts +86 -0
  136. package/src/spatial-scene/render-effects.ts +240 -0
  137. package/src/spatial-scene/review.ts +639 -0
  138. package/src/spatial-scene/simulation-bake.ts +206 -0
  139. package/src/spatial-scene/simulation.ts +187 -0
  140. package/src/spatial-scene/solve.ts +366 -0
  141. package/src/spatial-scene/temporal-audit.ts +354 -0
  142. package/src/version.ts +1 -1
  143. package/dist/index-42zsesc1.js +0 -2257
  144. package/dist/index-7131pg1c.js +0 -1820
  145. package/dist/index-7308egqr.js +0 -48
  146. package/dist/index-8txs6fkn.js +0 -712
  147. package/dist/index-fava6pge.js +0 -192
  148. package/dist/index-h1k0fnjq.js +0 -2363
  149. package/dist/index-r7gdhmsp.js +0 -424
  150. package/dist/index-sh6xbav6.js +0 -1219
  151. package/dist/index-z1w83f81.js +0 -4
  152. package/dist/index-zfnddgay.js +0 -2029
  153. package/dist/skill-install-dh0ntqba.js +0 -10
@@ -0,0 +1,127 @@
1
+ import { z } from "zod";
2
+
3
+ import {
4
+ spatialSceneSha256,
5
+ SpatialSceneV1Schema,
6
+ } from "../../../src/spatial-scene";
7
+ import { SpatialDirectionSchema } from "../../../src/spatial-scene/direction";
8
+ import { SpatialTemporalAuditOptionsSchema } from "../../../src/spatial-scene/temporal-audit";
9
+ import { SpatialRecipePackSchema } from "../../../src/spatial-scene/recipe-pack";
10
+ import { SpatialRenderRequestSchema, type SpatialRenderRequest } from "../application/spatial-render";
11
+ import type { SpatialEffectsPlanInput } from "../application/operations/spatial-direction";
12
+ import { MediaArtifactRequestSchema } from "../application/operations/media/shared";
13
+ import type { OperationInputValue } from "../code/contracts";
14
+ import { defineWorkflow } from "../code/public";
15
+
16
+ export const CinematicWorldInputSchema = z.strictObject({
17
+ /** The declarative pack an agent authors: direction, axes, previews, audit. */
18
+ pack: SpatialRecipePackSchema,
19
+ /** The admitted scene the pack binds by `sceneSha256`. */
20
+ scene: SpatialSceneV1Schema,
21
+ /** Repository artifact for the same scene, used only by render nodes. */
22
+ source: MediaArtifactRequestSchema,
23
+ }).superRefine((input, context) => {
24
+ if (input.pack.sceneSha256 !== spatialSceneSha256(input.scene)) {
25
+ context.addIssue({
26
+ code: "custom",
27
+ message: "Recipe pack sceneSha256 does not match the admitted scene.",
28
+ path: ["pack", "sceneSha256"],
29
+ });
30
+ }
31
+ input.pack.previews.forEach((preview, index) => {
32
+ const parsed = SpatialRenderRequestSchema.safeParse(preview.request);
33
+ if (!parsed.success) {
34
+ context.addIssue({
35
+ code: "custom",
36
+ message: `Preview ${preview.name} request is not a valid spatial render request.`,
37
+ path: ["pack", "previews", index, "request"],
38
+ });
39
+ }
40
+ });
41
+ });
42
+
43
+ /**
44
+ * The complete agent-directed planning and review loop for one admitted scene:
45
+ * inspect the world, check and compile the authored direction, plan a bounded
46
+ * gallery per requested axis, optionally bind declared effects into each
47
+ * preview render, and audit sampled temporal evidence. Every proposal stays
48
+ * `verified: false`; selection and promotion are separate explicit operations
49
+ * outside this workflow.
50
+ */
51
+ export const cinematicWorld = defineWorkflow({
52
+ id: "cinematic-world",
53
+ inputSchema: CinematicWorldInputSchema,
54
+ inputSchemaId: "slopcamera.workflow.cinematic-world.input/v1",
55
+ version: 1,
56
+ build(workflow, input) {
57
+ const sceneSha256 = input.pack.sceneSha256;
58
+ /** Parsed documents enter operation input positions unchanged; refs may substitute any position. */
59
+ const scene = input.scene as unknown as OperationInputValue<z.infer<typeof SpatialSceneV1Schema>>;
60
+ const direction = input.pack.direction as unknown as OperationInputValue<z.infer<typeof SpatialDirectionSchema>>;
61
+ const planning = workflow.namespace("planning");
62
+ const inspect = planning.scene.inspect("inspect", { scene });
63
+ const directionCheck = planning.scene.directionCheck("direction-check", {
64
+ direction,
65
+ scene,
66
+ });
67
+ const compilation = planning.scene.directionCompile("direction-compile", {
68
+ direction,
69
+ scene,
70
+ ...(input.pack.cameraId === undefined ? {} : { cameraId: input.pack.cameraId }),
71
+ });
72
+ const galleries = workflow.namespace("galleries");
73
+ const gallery = Object.fromEntries(input.pack.axes.map(axis => [axis, galleries.scene.directionGallery(axis, {
74
+ axis,
75
+ direction,
76
+ scene,
77
+ ...(input.pack.cameraId === undefined ? {} : { cameraId: input.pack.cameraId }),
78
+ })]));
79
+ const audit = input.pack.temporalAudit === undefined || input.pack.cameraId === undefined
80
+ ? undefined
81
+ : planning.scene.temporalAudit("temporal-audit", {
82
+ cameraId: input.pack.cameraId,
83
+ options: {
84
+ contacts: input.pack.temporalAudit.contacts,
85
+ cutBeforeUs: input.pack.temporalAudit.cutBeforeUs,
86
+ } as unknown as OperationInputValue<z.infer<typeof SpatialTemporalAuditOptionsSchema>>,
87
+ scene,
88
+ ...(input.pack.temporalAudit.timesUs === undefined
89
+ ? {} : { timesUs: input.pack.temporalAudit.timesUs }),
90
+ });
91
+ const previews = workflow.namespace("previews");
92
+ const renders = input.pack.previews.map(preview => {
93
+ const request = SpatialRenderRequestSchema.parse(preview.request) as unknown as OperationInputValue<SpatialRenderRequest>;
94
+ const branch = previews.namespace(preview.name);
95
+ const effects = input.pack.effects === undefined ? undefined : branch.scene.effectsPlan("effects", {
96
+ particleSystems: input.pack.effects.particleSystems,
97
+ renderPlan: input.pack.effects.renderPlan,
98
+ scene,
99
+ simulationBakes: input.pack.effects.simulationBakes,
100
+ } as unknown as OperationInputValue<SpatialEffectsPlanInput>);
101
+ const render = branch.scene.render("render", {
102
+ request: {
103
+ ...request,
104
+ ...(effects === undefined ? {} : { effects }),
105
+ },
106
+ sceneSha256,
107
+ source: input.source,
108
+ });
109
+ return {
110
+ effects,
111
+ name: preview.name,
112
+ render,
113
+ };
114
+ });
115
+ return {
116
+ compilation,
117
+ directionCheck,
118
+ galleries: gallery,
119
+ inspect,
120
+ previews: Object.fromEntries(renders.map(candidate => [candidate.name, {
121
+ ...(candidate.effects === undefined ? {} : { effects: candidate.effects }),
122
+ render: candidate.render,
123
+ }])),
124
+ ...(audit === undefined ? {} : { temporalAudit: audit }),
125
+ };
126
+ },
127
+ });
@@ -8,6 +8,7 @@ import {
8
8
  } from "../code/define-workflow";
9
9
  import type { WorkflowOutputValue } from "../code/contracts";
10
10
  import { chapteredDemo } from "./chaptered-demo";
11
+ import { cinematicWorld } from "./cinematic-world";
11
12
  import { directedScene } from "./scene-direction";
12
13
  import { creativeIteration } from "./creative-iteration";
13
14
  import { creativeSelection } from "./creative-selection";
@@ -52,6 +53,10 @@ export const BUILT_IN_WORKFLOWS: readonly BuiltInWorkflow[] = Object.freeze([
52
53
  description: "Render a prepared immutable spatial scene composition with exact frame timing and retained project audio.",
53
54
  title: "Directed scene",
54
55
  }),
56
+ catalogEntry(cinematicWorld, {
57
+ description: "Compile authored direction for one admitted scene into proposals, bounded galleries, effect-bound previews, and a temporal audit — selection and promotion stay explicit.",
58
+ title: "Cinematic world",
59
+ }),
55
60
  catalogEntry(chapteredDemo, {
56
61
  description: "Freeze the current chapter, overlay, and media composition and render an exact video.",
57
62
  title: "Chaptered demo",
@@ -84,6 +89,7 @@ export function builtInWorkflow(id: string): BuiltInWorkflow | undefined {
84
89
 
85
90
  export {
86
91
  chapteredDemo,
92
+ cinematicWorld,
87
93
  directedScene,
88
94
  creativeIteration,
89
95
  creativeSelection,
package/dist/cli.js CHANGED
@@ -1,95 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import {
4
- SLOPCAMERA_VERSION,
5
- artifactSummary,
6
- checkDiagramFile,
7
- renderDiagramFile,
8
- runMcpServer
9
- } from "./index-7131pg1c.js";
10
- import {
11
- installSkill,
12
- pathExists
13
- } from "./index-7308egqr.js";
14
- import"./index-8txs6fkn.js";
15
- import"./index-fava6pge.js";
16
- import {
17
- executeSlopcameraOperation,
18
- isSlopcameraOperationCode,
19
- searchSlopcameraOperations,
20
- slopcameraOperationCodes,
21
- withSlopcameraOperationHostAdmission
22
- } from "./index-h1k0fnjq.js";
23
- import {
24
- vectorizeImage
25
- } from "./index-zfnddgay.js";
26
- import {
27
- generateSlopcameraImageFile,
28
- slopcameraGatewayCredentialStatus,
29
- slopcameraImageModels
30
- } from "./index-r7gdhmsp.js";
31
- import"./index-sh6xbav6.js";
32
- import {
33
- __require
34
- } from "./index-z1w83f81.js";
35
-
36
- // src/cli.ts
37
- import { writeFile } from "fs/promises";
38
- import { resolve } from "path";
39
-
40
- // src/support-completion.ts
41
- function reportUsefulResult(observer) {
42
- try {
43
- if (observer !== undefined)
44
- Promise.resolve(observer()).catch(() => {
45
- return;
46
- });
47
- } catch {}
48
- }
49
-
50
- // src/support.ts
51
- import {
52
- maybeShowSupportInvitation,
53
- runSupportCommand
54
- } from "@hraness/support-foundation/node";
55
- var supportProfile = {
56
- id: "slopcamera",
57
- name: "Slopcamera",
58
- valueProposition: "Support ongoing development of local visual tools for agents.",
59
- updates: false
60
- };
61
- function standaloneSupportEnvironment() {
62
- const env = { ...process.env };
63
- process.env.HRANESS_SUPPORT_AUDIENCE = "off";
64
- process.env.HRANESS_SUPPORT_EMAIL = "off";
65
- return env;
66
- }
67
- async function runProductSupportCommand(args, options = {}) {
68
- const result = await runSupportCommand(supportProfile, args, {
69
- command: ["slopcamera"],
70
- ...options,
71
- gitEmail: false
72
- });
73
- if (result.stdout !== "")
74
- process.stdout.write(result.stdout);
75
- if (result.stderr !== "")
76
- process.stderr.write(result.stderr);
77
- return result.exitCode;
78
- }
79
- async function showProductSupportInvitation(options = {}) {
80
- try {
81
- await maybeShowSupportInvitation(supportProfile, {
82
- usefulResult: true,
83
- command: ["slopcamera"],
84
- ...options,
85
- gitEmail: false
86
- });
87
- } catch {}
88
- }
89
-
90
- // src/cli.ts
91
- var slopcameraCliVersion = SLOPCAMERA_VERSION;
92
- var help = `slopcamera ${slopcameraCliVersion}
3
+ import{SLOPCAMERA_VERSION,artifactSummary,checkDiagramFile,renderDiagramFile,runMcpServer}from"./index-gs48afpt.js";import{installSkill,pathExists}from"./index-grk47tgs.js";import"./index-zhg36tf9.js";import"./index-3cxf2c58.js";import"./index-296qhpf2.js";import{executeSlopcameraOperation,generateSlopcameraIcon,generateSlopcameraImageGallery,isSlopcameraOperationCode,parseSlopcameraGalleryVary,searchSlopcameraOperations,slopcameraGalleryKinds,slopcameraGalleryLimits,slopcameraIconMaximumRounds,slopcameraOperationCodes,withSlopcameraOperationHostAdmission}from"./index-0bbesn4m.js";import{vectorizeImage}from"./index-qry58nj2.js";import{generateSlopcameraImageFile,slopcameraGatewayCredentialStatus,slopcameraImageModels}from"./index-77fjfg9f.js";import"./index-b3geqczn.js";import{__require}from"./index-80a36bc0.js";import{readFile,writeFile}from"fs/promises";import{resolve}from"path";function reportUsefulResult(observer){try{if(observer!==undefined)Promise.resolve(observer()).catch(()=>{return})}catch{}}import{maybeShowSupportInvitation,runSupportCommand}from"@hraness/support-foundation/node";var supportProfile={id:"slopcamera",name:"Slopcamera",valueProposition:"Support ongoing development of local visual 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}
93
4
 
94
5
  Turn source material into deterministic diagrams, images, and canvas assets.
95
6
 
@@ -99,6 +10,12 @@ Usage:
99
10
  slopcamera diagram render <file> [--out-dir <directory>] [--config <file>] [--scale <number>]
100
11
  slopcamera image vectorize <image> --output <file.svg> [--json] [--duotone <#rgb,#rgb>]
101
12
  slopcamera image generate <prompt> --output <file.png|jpg|webp> [--model <provider/model>] [--json]
13
+ slopcamera image icon <subject> --output <file.svg> [--purpose <mark|illustration>]
14
+ [--model <provider/model>] [--ink <#rgb|#rrggbb>] [--rounds <1-${slopcameraIconMaximumRounds}>]
15
+ [--critique-model <provider/model>] [--keep-raster] [--json]
16
+ slopcamera image gallery <subject> --output-dir <directory> [--kind <${slopcameraGalleryKinds.join("|")}>]
17
+ [--count <1-${slopcameraGalleryLimits.candidates}>] [--vary <axis[=v1,v2][;axis...]>] [--candidates <file.json>]
18
+ [--model <provider/model>] [--cell <${slopcameraGalleryLimits.cellEdgeMin}-${slopcameraGalleryLimits.cellEdgeMax}>] [--tile|--no-tile] [--json]
102
19
  slopcamera code search [query] [--limit <number>]
103
20
  slopcamera code execute <operation> --input <JSON>
104
21
  slopcamera mcp --root <workspace>
@@ -127,6 +44,17 @@ Set AI_GATEWAY_API_KEY, or run through \`vercel env run -- \u2026\` so
127
44
  VERCEL_OIDC_TOKEN is available. Slopcamera never stores or prints the token.
128
45
  PNG, JPEG, and WebP responses are signature-checked and published atomically.
129
46
 
47
+ Icon produces isometric line-art SVG: a style-locked Gateway raster is
48
+ normalized to canonical ink-on-transparent pixels, traced by the local
49
+ vectorizer, and (when --rounds exceeds 1) critiqued by a vision model whose
50
+ feedback revises the prompt for the next attempt. Only Slopcamera's own
51
+ generated output is uploaded for critique \u2014 never user media.
52
+
53
+ Gallery generates several bounded candidates in parallel and composes them
54
+ into one labelled contact sheet plus a receipt. Use it to review texture,
55
+ skybox, backdrop, sprite, or design alternatives, then promote a chosen
56
+ candidate file explicitly \u2014 nothing is applied automatically.
57
+
130
58
  Optional support: after useful work, agents can read slopcamera support protocol --json.
131
59
  Discovery uses stderr without claiming an invitation; HRANESS_SUPPORT_AUDIENCE=off disables it.
132
60
  No feature requires payment. Imported CLI/SDK calls and probes stay quiet.
@@ -138,344 +66,12 @@ MCP preserves root-relative check_diagram/render_diagram and adds closed
138
66
  search_slopcamera/execute_slopcamera registry tools. It uses built-in assets, never
139
67
  executes workspace config or caller code, and writes protocol messages only to
140
68
  stdout.
141
- `;
142
- function parseArguments(args, valueOptions) {
143
- const positionals = [];
144
- const options = {};
145
- const flags = new Set;
146
- for (let index = 0;index < args.length; index += 1) {
147
- const argument = args[index];
148
- if (argument === undefined)
149
- continue;
150
- if (!argument.startsWith("--")) {
151
- positionals.push(argument);
152
- continue;
153
- }
154
- const name = argument.slice(2);
155
- if (!valueOptions.has(name)) {
156
- flags.add(name);
157
- continue;
158
- }
159
- const value = args[index + 1];
160
- if (value === undefined || value.startsWith("--")) {
161
- throw new Error(`--${name} requires a value`);
162
- }
163
- options[name] = value;
164
- index += 1;
165
- }
166
- return { positionals, options, flags };
167
- }
168
- function requiredPositional(parsed, index, label) {
169
- const value = parsed.positionals[index];
170
- if (value === undefined)
171
- throw new Error(`Missing ${label}`);
172
- return value;
173
- }
174
- function requiredOption(parsed, name) {
175
- const value = parsed.options[name];
176
- if (value === undefined)
177
- throw new Error(`--${name} is required`);
178
- return value;
179
- }
180
- function parsePositiveInteger(value, name) {
181
- if (value === undefined)
182
- return;
183
- const parsed = Number(value);
184
- if (!Number.isInteger(parsed) || parsed < 1) {
185
- throw new Error(`--${name} must be a positive integer`);
186
- }
187
- return parsed;
188
- }
189
- function parseDuotone(value) {
190
- if (value === undefined)
191
- return;
192
- const colors = value.split(",").map((color) => color.trim());
193
- if (colors.length !== 2 || colors.some((color) => !/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(color))) {
194
- throw new Error("--duotone must contain two #rgb or #rrggbb colors separated by a comma");
195
- }
196
- return [colors[0], colors[1]];
197
- }
198
- function printFindings(findings) {
199
- if (findings.length === 0) {
200
- console.log("No diagram lint findings.");
201
- return;
202
- }
203
- console.warn(`${findings.length} diagram lint finding${findings.length === 1 ? "" : "s"}:`);
204
- for (const finding of findings) {
205
- console.warn(` [${finding.code}] ${finding.message}`);
206
- }
207
- }
208
- var starter = {
209
- $schema: "https://raw.githubusercontent.com/hraness/slopcamera/main/schema/diagram.schema.json",
210
- version: 1,
211
- name: "example-flow",
212
- canvas: { width: 960, height: 540, padding: 64 },
213
- layout: { type: "stack", direction: "horizontal", gap: 160, align: "center" },
214
- shapes: [
215
- {
216
- id: "source",
217
- type: "rect",
218
- width: 240,
219
- height: 160,
220
- label: "Source",
221
- icon: "document",
222
- tone: "blue"
223
- },
224
- {
225
- id: "result",
226
- type: "rect",
227
- width: 240,
228
- height: 160,
229
- label: "Result",
230
- icon: "check",
231
- tone: "green"
232
- }
233
- ],
234
- edges: [{ id: "source-result", from: "source", to: "result" }]
235
- };
236
- function hostAdmissionOptions(dependencies) {
237
- return dependencies.hostResourceCoordinator === undefined ? {} : { hostResourceCoordinator: dependencies.hostResourceCoordinator };
238
- }
239
- function canonicalArguments(args) {
240
- const [surface, subcommand, ...rest] = args;
241
- if (surface === "diagram") {
242
- if (subcommand === "init" || subcommand === "check" || subcommand === "render") {
243
- return [subcommand, ...rest];
244
- }
245
- throw new Error("Use slopcamera diagram init, check, or render");
246
- }
247
- if (surface === "image") {
248
- if (subcommand === "vectorize" || subcommand === "generate") {
249
- return [subcommand, ...rest];
250
- }
251
- throw new Error("Use slopcamera image vectorize or generate");
252
- }
253
- if (surface === "init" || surface === "check" || surface === "render" || surface === "vectorize" || surface === "generate") {
254
- throw new Error(`The flat \`${surface}\` command moved to a namespaced Slopcamera surface.
69
+ `;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}}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, or render")}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.
255
70
 
256
- ${help}`);
257
- }
258
- return args;
259
- }
260
- async function main(args, dependencies = {}) {
261
- if (args[0] === "support") {
262
- process.exitCode = await runProductSupportCommand(args.slice(1), dependencies.supportEnvironment === undefined ? {} : { env: dependencies.supportEnvironment });
263
- return;
264
- }
265
- const [command, ...rest] = canonicalArguments(args);
266
- if (command === undefined || command === "help" || command === "--help" || command === "-h") {
267
- console.log(help);
268
- return;
269
- }
270
- if (command === "version" || command === "--version" || command === "-v") {
271
- console.log(slopcameraCliVersion);
272
- return;
273
- }
274
- if (command === "init") {
275
- const parsed = parseArguments(rest, new Set);
276
- const filePath = resolve(parsed.positionals[0] ?? "diagram.diagram.json");
277
- if (await pathExists(filePath))
278
- throw new Error(`Refusing to overwrite existing file: ${filePath}`);
279
- await writeFile(filePath, `${JSON.stringify(starter, null, 2)}
280
- `);
281
- console.log(`Created ${filePath}`);
282
- reportUsefulResult(dependencies.onUsefulResult);
283
- return;
284
- }
285
- if (command === "check") {
286
- const parsed = parseArguments(rest, new Set(["config"]));
287
- const result = await withSlopcameraOperationHostAdmission("slopcamera.diagram.check", async () => await checkDiagramFile({
288
- filePath: requiredPositional(parsed, 0, "diagram file"),
289
- ...parsed.options.config === undefined ? {} : { configPath: parsed.options.config }
290
- }), hostAdmissionOptions(dependencies));
291
- console.log(`Valid diagram${result.configPath === null ? "" : ` with ${result.configPath}`}.`);
292
- printFindings(result.findings);
293
- if (parsed.flags.has("strict") && result.findings.length > 0)
294
- process.exitCode = 2;
295
- return;
296
- }
297
- if (command === "render") {
298
- const parsed = parseArguments(rest, new Set(["out-dir", "config", "scale"]));
299
- const scale = parsed.options.scale === undefined ? undefined : Number.parseFloat(parsed.options.scale);
300
- const result = await withSlopcameraOperationHostAdmission("slopcamera.diagram.render", async () => await renderDiagramFile({
301
- filePath: requiredPositional(parsed, 0, "diagram file"),
302
- ...parsed.options["out-dir"] === undefined ? {} : { outDirectory: parsed.options["out-dir"] },
303
- ...parsed.options.config === undefined ? {} : { configPath: parsed.options.config },
304
- ...scale === undefined ? {} : { scale }
305
- }), hostAdmissionOptions(dependencies));
306
- console.log(artifactSummary(result.artifacts));
307
- printFindings(result.findings);
308
- reportUsefulResult(dependencies.onUsefulResult);
309
- return;
310
- }
311
- if (command === "vectorize") {
312
- const parsed = parseArguments(rest, new Set(["output", "duotone", "alpha-cutoff", "timeout-ms"]));
313
- const unknownFlags = [...parsed.flags].filter((flag) => flag !== "json");
314
- if (unknownFlags.length > 0) {
315
- throw new Error(`Unknown vectorize option: --${unknownFlags[0]}`);
316
- }
317
- if (parsed.positionals.length > 1) {
318
- throw new Error("slopcamera image vectorize accepts exactly one raster input");
319
- }
320
- const output = requiredOption(parsed, "output");
321
- if (!output.toLowerCase().endsWith(".svg")) {
322
- throw new Error("--output must end in .svg");
323
- }
324
- const alphaCutoff = parsePositiveInteger(parsed.options["alpha-cutoff"], "alpha-cutoff");
325
- const timeoutMs = parsePositiveInteger(parsed.options["timeout-ms"], "timeout-ms");
326
- const duotone = parseDuotone(parsed.options.duotone);
327
- const result = await withSlopcameraOperationHostAdmission("slopcamera.image.vectorize", async (lease) => await (dependencies.vectorize ?? vectorizeImage)(requiredPositional(parsed, 0, "raster image"), {
328
- ...alphaCutoff === undefined ? {} : { alphaCutoff },
329
- ...duotone === undefined ? {} : { duotone },
330
- ...timeoutMs === undefined ? {} : { limits: { maxDurationMs: timeoutMs } },
331
- inheritedFileDescriptors: [lease.inheritedFileDescriptor],
332
- outputPath: output
333
- }), hostAdmissionOptions(dependencies));
334
- if (parsed.flags.has("json")) {
335
- (dependencies.log ?? console.log)(JSON.stringify({ ...result.receipt, outputPath: result.outputPath }, null, 2));
336
- } else {
337
- (dependencies.log ?? console.log)(`Vectorized ${result.receipt.width}\xD7${result.receipt.height} with ` + `${result.receipt.profile}/${result.receipt.representation}: ${result.outputPath}`);
338
- }
339
- reportUsefulResult(dependencies.onUsefulResult);
340
- return;
341
- }
342
- if (command === "generate") {
343
- const parsed = parseArguments(rest, new Set(["model", "output"]));
344
- const unknownFlags = [...parsed.flags].filter((flag) => flag !== "json");
345
- if (unknownFlags.length > 0) {
346
- throw new Error(`Unknown generate option: --${unknownFlags[0]}`);
347
- }
348
- if (parsed.positionals.length !== 1) {
349
- throw new Error("slopcamera image generate accepts exactly one prompt");
350
- }
351
- const model = parsed.options.model ?? slopcameraImageModels[1];
352
- if (model.length > 256 || !/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model)) {
353
- throw new Error("--model must be a bounded Vercel AI Gateway provider/model id");
354
- }
355
- const result = await withSlopcameraOperationHostAdmission("slopcamera.image.generate", async () => await (dependencies.generate ?? generateSlopcameraImageFile)({
356
- model,
357
- prompt: requiredPositional(parsed, 0, "prompt"),
358
- outputPath: requiredOption(parsed, "output")
359
- }), hostAdmissionOptions(dependencies));
360
- if (parsed.flags.has("json")) {
361
- (dependencies.log ?? console.log)(JSON.stringify(result, null, 2));
362
- } else {
363
- (dependencies.log ?? console.log)(`Generated ${result.mediaType} with ${result.model}: ${result.outputPath} (${result.bytes} bytes, request ${result.requestId})`);
364
- }
365
- reportUsefulResult(dependencies.onUsefulResult);
366
- return;
367
- }
368
- if (command === "code") {
369
- const [subcommand, ...subcommandArgs] = rest;
370
- if (subcommand === "search") {
371
- const parsed = parseArguments(subcommandArgs, new Set(["limit"]));
372
- if (parsed.flags.size > 0 || parsed.positionals.length > 1) {
373
- throw new Error("Use slopcamera code search [query] [--limit <number>]");
374
- }
375
- const limit = parsePositiveInteger(parsed.options.limit, "limit") ?? slopcameraOperationCodes.length;
376
- const operations = searchSlopcameraOperations(parsed.positionals[0] ?? "", limit);
377
- console.log(JSON.stringify({ operations }, null, 2));
378
- return;
379
- }
380
- if (subcommand === "execute") {
381
- const parsed = parseArguments(subcommandArgs, new Set(["input"]));
382
- if (parsed.flags.size > 0 || parsed.positionals.length !== 1) {
383
- throw new Error("Use slopcamera code execute <operation> --input <JSON>");
384
- }
385
- const requestedOperation = parsed.positionals[0];
386
- const operation = isSlopcameraOperationCode(requestedOperation) ? requestedOperation : undefined;
387
- if (operation === undefined) {
388
- throw new Error(`Unknown Slopcamera operation code: ${requestedOperation}`);
389
- }
390
- const inputText = requiredOption(parsed, "input");
391
- if (Buffer.byteLength(inputText, "utf8") > 64 * 1024) {
392
- throw new Error("--input JSON must be no more than 65536 UTF-8 bytes");
393
- }
394
- let input;
395
- try {
396
- input = JSON.parse(inputText);
397
- } catch {
398
- throw new Error("--input must be valid JSON");
399
- }
400
- const result = await executeSlopcameraOperation(operation, input, {
401
- ...hostAdmissionOptions(dependencies)
402
- });
403
- (dependencies.log ?? console.log)(JSON.stringify({ operation, result }, null, 2));
404
- if (operation === "slopcamera.diagram.render" || operation === "slopcamera.image.vectorize" || operation === "slopcamera.image.generate") {
405
- reportUsefulResult(dependencies.onUsefulResult);
406
- }
407
- return;
408
- }
409
- throw new Error("Use slopcamera code search [query] or slopcamera code execute <operation> --input <JSON>");
410
- }
411
- if (command === "mcp") {
412
- const parsed = parseArguments(rest, new Set(["root"]));
413
- if (parsed.positionals.length > 0 || parsed.flags.size > 0) {
414
- throw new Error("slopcamera mcp accepts only --root <workspace>");
415
- }
416
- await runMcpServer({
417
- rootDirectory: requiredOption(parsed, "root"),
418
- serverVersion: slopcameraCliVersion
419
- });
420
- return;
421
- }
422
- if (command === "doctor") {
423
- console.log(`slopcamera ${slopcameraCliVersion}`);
424
- console.log(`Bun ${process.versions.bun ?? "not detected"}`);
425
- console.log("Headless diagram SVG/PNG/tldraw renderer ready");
426
- 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)");
427
- console.log("Root-relative MCP check/render server ready (trusted local workspace)");
428
- const gateway = slopcameraGatewayCredentialStatus();
429
- console.log(gateway.available ? `Vercel AI Gateway ready via ${gateway.source}` : "Vercel AI Gateway requires AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN");
430
- return;
431
- }
432
- if (command === "skill") {
433
- const [subcommand, ...subcommandArgs] = rest;
434
- if (subcommand === "path") {
435
- const { bundledSkillPath } = await import("./skill-install-dh0ntqba.js");
436
- console.log(bundledSkillPath());
437
- return;
438
- }
439
- if (subcommand === "install") {
440
- const parsed = parseArguments(subcommandArgs, new Set(["target", "scope", "project"]));
441
- const target = parsed.options.target ?? "codex";
442
- const scope = parsed.options.scope ?? "user";
443
- if (!["codex", "claude", "agents"].includes(target)) {
444
- throw new Error("--target must be codex, claude, or agents");
445
- }
446
- if (!["user", "project"].includes(scope)) {
447
- throw new Error("--scope must be user or project");
448
- }
449
- const destination = await installSkill({
450
- target,
451
- scope,
452
- ...parsed.options.project === undefined ? {} : { projectDirectory: parsed.options.project },
453
- force: parsed.flags.has("force")
454
- });
455
- console.log(`Installed slopcamera skill at ${destination}`);
456
- return;
457
- }
458
- throw new Error("Use slopcamera skill path or install");
459
- }
460
- throw new Error(`Unknown command: ${command}
71
+ ${help}`)}return args}async function main(args,dependencies={}){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==="--help"||command==="-h"){console.log(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)}
72
+ `);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","ink","purpose","rounds","critique-model"]));const unknownFlags=[...parsed.flags].filter((flag)=>flag!=="json"&&flag!=="keep-raster");if(unknownFlags.length>0){throw new Error(`Unknown icon option: --${unknownFlags[0]}`)}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 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 rounds=parsePositiveInteger(parsed.options.rounds,"rounds");if(rounds!==undefined&&rounds>slopcameraIconMaximumRounds){throw new Error(`--rounds must be at most ${slopcameraIconMaximumRounds}`)}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}}),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}
73
+ `+`${rows.join(`
74
+ `)}
75
+ 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}
461
76
 
462
- ${help}`);
463
- }
464
- if (import.meta.main) {
465
- try {
466
- const env = standaloneSupportEnvironment();
467
- let usefulResult = false;
468
- await main(process.argv.slice(2), { supportEnvironment: env, onUsefulResult: () => {
469
- usefulResult = true;
470
- } });
471
- if (usefulResult && (process.exitCode ?? 0) === 0)
472
- await showProductSupportInvitation({ env });
473
- } catch (error) {
474
- console.error(error instanceof Error ? error.message : String(error));
475
- process.exitCode = 1;
476
- }
477
- }
478
- export {
479
- slopcameraCliVersion,
480
- main
481
- };
77
+ ${help}`)}if(import.meta.main){try{const env=standaloneSupportEnvironment();let usefulResult=false;await main(process.argv.slice(2),{supportEnvironment:env,onUsefulResult:()=>{usefulResult=true}});if(usefulResult&&(process.exitCode??0)===0)await showProductSupportInvitation({env})}catch(error){console.error(error instanceof Error?error.message:String(error));process.exitCode=1}}export{slopcameraCliVersion,main};