@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
package/src/mcp/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { SlopcameraGenerateDependencies } from "../generate.js"
2
+ import type { HostResourceCoordinator } from "../host-resources.js"
2
3
 
3
4
  export type JsonRpcId = string | number
4
5
  export type JsonRpcResponseId = JsonRpcId | null
@@ -37,6 +38,19 @@ export interface McpToolDefinition {
37
38
  | "render_diagram"
38
39
  | "search_slopcamera"
39
40
  | "execute_slopcamera"
41
+ | "check_scene"
42
+ | "inspect_scene"
43
+ | "audit_scene"
44
+ | "diff_scenes"
45
+ | "evaluate_scene"
46
+ | "check_scene_direction"
47
+ | "plan_scene_direction"
48
+ | "plan_scene_gallery"
49
+ | "check_scene_effects"
50
+ | "plan_scene_effects"
51
+ | "check_scene_behavior"
52
+ | "audit_scene_behavior"
53
+ | "audit_scene_temporal"
40
54
  readonly title: string
41
55
  readonly description: string
42
56
  readonly inputSchema: Readonly<Record<string, unknown>>
@@ -68,4 +82,9 @@ export interface McpServerOptions {
68
82
  readonly serverVersion?: string
69
83
  /** Injectable Gateway runtime, environment, and fixed-origin fetch seams. */
70
84
  readonly generateDependencies?: SlopcameraGenerateDependencies
85
+ /**
86
+ * Injectable host-resource admission coordinator. Defaults to the
87
+ * machine-global coordinator; tests inject a process-local stand-in.
88
+ */
89
+ readonly hostResourceCoordinator?: HostResourceCoordinator
71
90
  }
package/src/operations.ts CHANGED
@@ -14,6 +14,19 @@ import {
14
14
  type SlopcameraGenerateDependencies,
15
15
  type SlopcameraImageModel,
16
16
  } from "./generate.js"
17
+ import {
18
+ generateSlopcameraIcon,
19
+ slopcameraIconMaximumRounds,
20
+ slopcameraIconSubjectMaximumBytes,
21
+ type SlopcameraIconReceipt,
22
+ } from "./icon.js"
23
+ import {
24
+ generateSlopcameraImageGallery,
25
+ slopcameraGalleryAxes,
26
+ slopcameraGalleryKinds,
27
+ slopcameraGalleryLimits,
28
+ type SlopcameraGalleryReceipt,
29
+ } from "./image-gallery.js"
17
30
  import {
18
31
  vectorizeImage,
19
32
  type VectorizeReceipt,
@@ -30,6 +43,8 @@ export const slopcameraOperationCodes = [
30
43
  "slopcamera.diagram.render",
31
44
  "slopcamera.image.vectorize",
32
45
  "slopcamera.image.generate",
46
+ "slopcamera.image.icon",
47
+ "slopcamera.image.gallery",
33
48
  ] as const
34
49
 
35
50
  export type SlopcameraOperationCode = (typeof slopcameraOperationCodes)[number]
@@ -209,6 +224,157 @@ export const slopcameraOperationRegistry: readonly SlopcameraOperationDescriptor
209
224
  retry: "never",
210
225
  },
211
226
  },
227
+ {
228
+ code: "slopcamera.image.icon",
229
+ title: "Generate line-art icon",
230
+ description:
231
+ "Generate one isometric line-art SVG icon: a style-locked Vercel AI Gateway raster normalized to canonical ink-on-transparent pixels, traced locally, and optionally critiqued by a vision model across bounded rounds.",
232
+ execution: "gateway",
233
+ authentication: "environment",
234
+ destructive: true,
235
+ idempotent: false,
236
+ inputSchema: {
237
+ type: "object",
238
+ additionalProperties: false,
239
+ required: ["subject", "outputPath"],
240
+ properties: {
241
+ subject: {
242
+ type: "string",
243
+ minLength: 1,
244
+ maxLength: slopcameraIconSubjectMaximumBytes,
245
+ },
246
+ outputPath: pathSchema,
247
+ model: modelSchema,
248
+ critiqueModel: modelSchema,
249
+ ink: {
250
+ type: "string",
251
+ pattern: "^#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$",
252
+ },
253
+ purpose: { type: "string", enum: ["mark", "illustration"] },
254
+ rounds: {
255
+ type: "integer",
256
+ minimum: 1,
257
+ maximum: slopcameraIconMaximumRounds,
258
+ },
259
+ keepRaster: { type: "boolean" },
260
+ },
261
+ },
262
+ resources: [
263
+ { resource: "cpu", amount: 1 },
264
+ { resource: "local-io", amount: 1 },
265
+ { resource: "network", amount: 1 },
266
+ { resource: "paid-call", amount: 1 },
267
+ ],
268
+ transport: {
269
+ method: "POST",
270
+ authority: "https://ai-gateway.vercel.sh/v4/ai",
271
+ authorization: "bearer",
272
+ retry: "never",
273
+ },
274
+ },
275
+ {
276
+ code: "slopcamera.image.gallery",
277
+ title: "Generate image gallery",
278
+ description:
279
+ "Generate several bounded Vercel AI Gateway image candidates in parallel and compose a labelled contact sheet with per-candidate provenance for explicit review and selection.",
280
+ execution: "gateway",
281
+ authentication: "environment",
282
+ destructive: true,
283
+ idempotent: false,
284
+ inputSchema: {
285
+ type: "object",
286
+ additionalProperties: false,
287
+ required: ["subject", "outputDir"],
288
+ properties: {
289
+ subject: {
290
+ type: "string",
291
+ minLength: 1,
292
+ maxLength: slopcameraGalleryLimits.subjectBytes,
293
+ },
294
+ outputDir: pathSchema,
295
+ kind: { type: "string", enum: [...slopcameraGalleryKinds] },
296
+ model: modelSchema,
297
+ count: {
298
+ type: "integer",
299
+ minimum: 1,
300
+ maximum: slopcameraGalleryLimits.candidates,
301
+ },
302
+ vary: {
303
+ type: "array",
304
+ maxItems: slopcameraGalleryAxes.length,
305
+ items: {
306
+ anyOf: [
307
+ { type: "string", enum: [...slopcameraGalleryAxes] },
308
+ {
309
+ type: "object",
310
+ additionalProperties: false,
311
+ required: ["axis"],
312
+ properties: {
313
+ axis: { type: "string", enum: [...slopcameraGalleryAxes] },
314
+ values: {
315
+ type: "array",
316
+ minItems: 1,
317
+ maxItems: slopcameraGalleryLimits.candidates,
318
+ items: {
319
+ type: "string",
320
+ minLength: 1,
321
+ maxLength: slopcameraGalleryLimits.variantBytes,
322
+ },
323
+ },
324
+ },
325
+ },
326
+ ],
327
+ },
328
+ },
329
+ candidates: {
330
+ type: "array",
331
+ minItems: 1,
332
+ maxItems: slopcameraGalleryLimits.candidates,
333
+ items: {
334
+ type: "object",
335
+ additionalProperties: false,
336
+ required: ["id"],
337
+ properties: {
338
+ id: {
339
+ type: "string",
340
+ minLength: 1,
341
+ maxLength: slopcameraGalleryLimits.idLength,
342
+ },
343
+ prompt: {
344
+ type: "string",
345
+ minLength: 1,
346
+ maxLength: slopcameraGalleryLimits.candidatePromptBytes,
347
+ },
348
+ variant: {
349
+ type: "string",
350
+ minLength: 1,
351
+ maxLength: slopcameraGalleryLimits.variantBytes,
352
+ },
353
+ },
354
+ },
355
+ },
356
+ cellEdge: {
357
+ type: "integer",
358
+ minimum: slopcameraGalleryLimits.cellEdgeMin,
359
+ maximum: slopcameraGalleryLimits.cellEdgeMax,
360
+ },
361
+ tiled: { type: "boolean" },
362
+ timeoutMs: { type: "integer", minimum: 1_000, maximum: 30 * 60_000 },
363
+ },
364
+ },
365
+ resources: [
366
+ { resource: "cpu", amount: 1 },
367
+ { resource: "local-io", amount: 1 },
368
+ { resource: "network", amount: 1 },
369
+ { resource: "paid-call", amount: 1 },
370
+ ],
371
+ transport: {
372
+ method: "POST",
373
+ authority: "https://ai-gateway.vercel.sh/v4/ai",
374
+ authorization: "bearer",
375
+ retry: "never",
376
+ },
377
+ },
212
378
  ] satisfies readonly SlopcameraOperationDescriptor[])
213
379
 
214
380
  export interface CheckSlopcameraOperationInput {
@@ -234,11 +400,37 @@ export interface GenerateSlopcameraOperationInput {
234
400
  readonly outputPath: string
235
401
  }
236
402
 
403
+ export interface IconSlopcameraOperationInput {
404
+ readonly subject: string
405
+ readonly outputPath: string
406
+ readonly model?: SlopcameraImageModel
407
+ readonly critiqueModel?: string
408
+ readonly ink?: string
409
+ readonly purpose?: "illustration" | "mark"
410
+ readonly rounds?: number
411
+ readonly keepRaster?: boolean
412
+ }
413
+
414
+ export interface GallerySlopcameraOperationInput {
415
+ readonly subject: string
416
+ readonly outputDir: string
417
+ readonly kind?: string
418
+ readonly model?: SlopcameraImageModel
419
+ readonly count?: number
420
+ readonly vary?: readonly unknown[]
421
+ readonly candidates?: readonly unknown[]
422
+ readonly cellEdge?: number
423
+ readonly tiled?: boolean
424
+ readonly timeoutMs?: number
425
+ }
426
+
237
427
  export interface SlopcameraOperationInputMap {
238
428
  readonly "slopcamera.diagram.check": CheckSlopcameraOperationInput
239
429
  readonly "slopcamera.diagram.render": RenderSlopcameraOperationInput
240
430
  readonly "slopcamera.image.vectorize": VectorizeSlopcameraOperationInput
241
431
  readonly "slopcamera.image.generate": GenerateSlopcameraOperationInput
432
+ readonly "slopcamera.image.icon": IconSlopcameraOperationInput
433
+ readonly "slopcamera.image.gallery": GallerySlopcameraOperationInput
242
434
  }
243
435
 
244
436
  export interface SlopcameraOperationResultMap {
@@ -256,6 +448,8 @@ export interface SlopcameraOperationResultMap {
256
448
  readonly receipt: VectorizeReceipt
257
449
  }
258
450
  readonly "slopcamera.image.generate": GeneratedSlopcameraImageFile
451
+ readonly "slopcamera.image.icon": SlopcameraIconReceipt
452
+ readonly "slopcamera.image.gallery": SlopcameraGalleryReceipt
259
453
  }
260
454
 
261
455
  function operationFailure(message: string): never {
@@ -401,6 +595,190 @@ function parseGenerate(value: unknown): GenerateSlopcameraOperationInput {
401
595
  }
402
596
  }
403
597
 
598
+ function parseIcon(value: unknown): IconSlopcameraOperationInput {
599
+ const input = record(value, [
600
+ "subject",
601
+ "outputPath",
602
+ "model",
603
+ "critiqueModel",
604
+ "ink",
605
+ "rounds",
606
+ "keepRaster",
607
+ ])
608
+ if (
609
+ typeof input.subject !== "string" ||
610
+ input.subject.trim().length < 1 ||
611
+ /[\u0000-\u001f\u007f]/u.test(input.subject) ||
612
+ Buffer.byteLength(input.subject, "utf8") > slopcameraIconSubjectMaximumBytes
613
+ ) {
614
+ operationFailure(
615
+ `subject must be non-empty and no more than ${slopcameraIconSubjectMaximumBytes} UTF-8 bytes.`,
616
+ )
617
+ }
618
+ const outputPath = pathValue(input.outputPath, "outputPath")
619
+ if (!outputPath.toLowerCase().endsWith(".svg")) {
620
+ operationFailure("outputPath must end in .svg.")
621
+ }
622
+ for (const name of ["model", "critiqueModel"] as const) {
623
+ const model = input[name]
624
+ if (
625
+ model !== undefined &&
626
+ (typeof model !== "string" ||
627
+ model.length > 256 ||
628
+ !/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(model))
629
+ ) {
630
+ operationFailure(`${name} must be a bounded Vercel AI Gateway provider/model id.`)
631
+ }
632
+ }
633
+ if (
634
+ input.ink !== undefined &&
635
+ (typeof input.ink !== "string" ||
636
+ !/^#[a-f0-9]{3}(?:[a-f0-9]{3})?$/iu.test(input.ink))
637
+ ) {
638
+ operationFailure("ink must be a #rgb or #rrggbb color.")
639
+ }
640
+ const rounds = input.rounds
641
+ if (
642
+ rounds !== undefined &&
643
+ (!Number.isInteger(rounds) ||
644
+ (rounds as number) < 1 ||
645
+ (rounds as number) > slopcameraIconMaximumRounds)
646
+ ) {
647
+ operationFailure(
648
+ `rounds must be an integer from 1 through ${slopcameraIconMaximumRounds}.`,
649
+ )
650
+ }
651
+ if (
652
+ input.keepRaster !== undefined &&
653
+ typeof input.keepRaster !== "boolean"
654
+ ) {
655
+ operationFailure("keepRaster must be a boolean.")
656
+ }
657
+ return {
658
+ subject: input.subject,
659
+ outputPath,
660
+ ...(input.model === undefined
661
+ ? {}
662
+ : { model: input.model as SlopcameraImageModel }),
663
+ ...(input.critiqueModel === undefined
664
+ ? {}
665
+ : { critiqueModel: input.critiqueModel as string }),
666
+ ...(input.ink === undefined ? {} : { ink: input.ink as string }),
667
+ ...(rounds === undefined ? {} : { rounds: rounds as number }),
668
+ ...(input.keepRaster === undefined
669
+ ? {}
670
+ : { keepRaster: input.keepRaster as boolean }),
671
+ }
672
+ }
673
+
674
+ function parseGallery(value: unknown): GallerySlopcameraOperationInput {
675
+ const input = record(value, [
676
+ "subject",
677
+ "outputDir",
678
+ "kind",
679
+ "model",
680
+ "count",
681
+ "vary",
682
+ "candidates",
683
+ "cellEdge",
684
+ "tiled",
685
+ "timeoutMs",
686
+ ])
687
+ if (
688
+ typeof input.subject !== "string" ||
689
+ input.subject.trim().length < 1 ||
690
+ /[\u0000-\u001f\u007f]/u.test(input.subject) ||
691
+ Buffer.byteLength(input.subject, "utf8") > slopcameraGalleryLimits.subjectBytes
692
+ ) {
693
+ operationFailure(
694
+ `subject must be non-empty and no more than ${slopcameraGalleryLimits.subjectBytes} UTF-8 bytes.`,
695
+ )
696
+ }
697
+ if (
698
+ input.kind !== undefined &&
699
+ (typeof input.kind !== "string" || !slopcameraGalleryKinds.includes(input.kind as never))
700
+ ) {
701
+ operationFailure(`kind must be one of: ${slopcameraGalleryKinds.join(", ")}.`)
702
+ }
703
+ if (
704
+ input.model !== undefined &&
705
+ (typeof input.model !== "string" ||
706
+ input.model.length > 256 ||
707
+ !/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._:-]*$/iu.test(input.model))
708
+ ) {
709
+ operationFailure("model must be a bounded Vercel AI Gateway provider/model id.")
710
+ }
711
+ if (
712
+ input.count !== undefined &&
713
+ (!Number.isInteger(input.count) ||
714
+ (input.count as number) < 1 ||
715
+ (input.count as number) > slopcameraGalleryLimits.candidates)
716
+ ) {
717
+ operationFailure(`count must be an integer from 1 through ${slopcameraGalleryLimits.candidates}.`)
718
+ }
719
+ if (
720
+ input.vary !== undefined &&
721
+ (!Array.isArray(input.vary) || input.vary.length < 1 || input.vary.length > slopcameraGalleryAxes.length)
722
+ ) {
723
+ operationFailure(`vary must be a list of at most ${slopcameraGalleryAxes.length} axes.`)
724
+ }
725
+ if (
726
+ input.candidates !== undefined &&
727
+ (!Array.isArray(input.candidates) ||
728
+ input.candidates.length < 1 ||
729
+ input.candidates.length > slopcameraGalleryLimits.candidates)
730
+ ) {
731
+ operationFailure(`candidates must contain 1 through ${slopcameraGalleryLimits.candidates} entries.`)
732
+ }
733
+ if (
734
+ input.vary !== undefined &&
735
+ input.candidates !== undefined
736
+ ) {
737
+ operationFailure("vary and candidates are mutually exclusive.")
738
+ }
739
+ if (
740
+ input.count !== undefined &&
741
+ input.candidates !== undefined
742
+ ) {
743
+ operationFailure("count and candidates are mutually exclusive.")
744
+ }
745
+ const cellEdge = input.cellEdge
746
+ if (
747
+ cellEdge !== undefined &&
748
+ (!Number.isInteger(cellEdge) ||
749
+ (cellEdge as number) < slopcameraGalleryLimits.cellEdgeMin ||
750
+ (cellEdge as number) > slopcameraGalleryLimits.cellEdgeMax)
751
+ ) {
752
+ operationFailure(
753
+ `cellEdge must be an integer from ${slopcameraGalleryLimits.cellEdgeMin} through ${slopcameraGalleryLimits.cellEdgeMax}.`,
754
+ )
755
+ }
756
+ const timeoutMs = input.timeoutMs
757
+ if (
758
+ timeoutMs !== undefined &&
759
+ (!Number.isInteger(timeoutMs) ||
760
+ (timeoutMs as number) < 1_000 ||
761
+ (timeoutMs as number) > 30 * 60_000)
762
+ ) {
763
+ operationFailure("timeoutMs must be an integer from 1000 through 1800000.")
764
+ }
765
+ if (input.tiled !== undefined && typeof input.tiled !== "boolean") {
766
+ operationFailure("tiled must be a boolean when set.")
767
+ }
768
+ return {
769
+ subject: input.subject as string,
770
+ outputDir: pathValue(input.outputDir, "outputDir"),
771
+ ...(input.kind === undefined ? {} : { kind: input.kind as string }),
772
+ ...(input.model === undefined ? {} : { model: input.model as SlopcameraImageModel }),
773
+ ...(input.count === undefined ? {} : { count: input.count as number }),
774
+ ...(input.vary === undefined ? {} : { vary: input.vary as readonly unknown[] }),
775
+ ...(input.candidates === undefined ? {} : { candidates: input.candidates as readonly unknown[] }),
776
+ ...(cellEdge === undefined ? {} : { cellEdge: cellEdge as number }),
777
+ ...(input.tiled === undefined ? {} : { tiled: input.tiled as boolean }),
778
+ ...(timeoutMs === undefined ? {} : { timeoutMs: timeoutMs as number }),
779
+ }
780
+ }
781
+
404
782
  export function parseSlopcameraOperationInput<C extends SlopcameraOperationCode>(
405
783
  code: C,
406
784
  input: unknown,
@@ -414,6 +792,10 @@ export function parseSlopcameraOperationInput<C extends SlopcameraOperationCode>
414
792
  return parseVectorize(input) as SlopcameraOperationInputMap[C]
415
793
  case "slopcamera.image.generate":
416
794
  return parseGenerate(input) as SlopcameraOperationInputMap[C]
795
+ case "slopcamera.image.icon":
796
+ return parseIcon(input) as SlopcameraOperationInputMap[C]
797
+ case "slopcamera.image.gallery":
798
+ return parseGallery(input) as SlopcameraOperationInputMap[C]
417
799
  default:
418
800
  throw new SlopcameraOperationError(
419
801
  "INVALID_OPERATION",
@@ -698,6 +1080,36 @@ async function executeSlopcameraOperationUncoordinated<
698
1080
  dependencies,
699
1081
  )) as SlopcameraOperationResultMap[C]
700
1082
  }
1083
+ case "slopcamera.image.icon": {
1084
+ const options = input as IconSlopcameraOperationInput
1085
+ return (await generateSlopcameraIcon(
1086
+ {
1087
+ ...options,
1088
+ ...(dependencies.signal === undefined
1089
+ ? {}
1090
+ : { signal: dependencies.signal }),
1091
+ ...(dependencies.inheritedFileDescriptors === undefined
1092
+ ? {}
1093
+ : {
1094
+ inheritedFileDescriptors:
1095
+ dependencies.inheritedFileDescriptors,
1096
+ }),
1097
+ },
1098
+ dependencies,
1099
+ )) as SlopcameraOperationResultMap[C]
1100
+ }
1101
+ case "slopcamera.image.gallery": {
1102
+ const options = input as GallerySlopcameraOperationInput
1103
+ return (await generateSlopcameraImageGallery(
1104
+ {
1105
+ ...options,
1106
+ ...(dependencies.signal === undefined
1107
+ ? {}
1108
+ : { signal: dependencies.signal }),
1109
+ },
1110
+ dependencies,
1111
+ )) as SlopcameraOperationResultMap[C]
1112
+ }
701
1113
  default:
702
1114
  throw new SlopcameraOperationError(
703
1115
  "INVALID_OPERATION",
@@ -0,0 +1,67 @@
1
+ import {
2
+ compileSlopcameraCapabilityManifest,
3
+ defineSlopcameraCapabilityModule,
4
+ type SlopcameraCapabilityManifest,
5
+ type SlopcameraCapabilityOperation,
6
+ } from "./capability-manifest.js"
7
+ import {
8
+ PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS,
9
+ PORTABLE_SLOPCAMERA_OPERATION_KINDS,
10
+ type PortableSlopcameraOperationKind,
11
+ } from "./code/public-operations.js"
12
+
13
+ export const SLOPCAMERA_PORTABLE_CAPABILITY_VERSION = "slopcamera-portable-v1" as const
14
+
15
+ const portableModule = defineSlopcameraCapabilityModule({
16
+ commands: [],
17
+ description: "The fixed portable diagram and image operation projection for Bun SDK consumers.",
18
+ moduleId: "slopcamera.capability.portable-diagram-image",
19
+ operationKeys: PORTABLE_SLOPCAMERA_OPERATION_KINDS.map(kind => `${kind}@2`),
20
+ profiles: [{
21
+ id: "portable-bun-v1",
22
+ kind: "authoring",
23
+ qualification: { evidence: ["src/code/public-operations.ts"], level: "unit", status: "qualified" },
24
+ requirements: [{ description: "Bun 1.3.14 or newer.", id: "bun", optional: false }],
25
+ }],
26
+ stability: "stable",
27
+ title: "Portable diagrams and images",
28
+ toolNames: [],
29
+ trust: "portable",
30
+ version: 1,
31
+ workflowKeys: [],
32
+ })
33
+
34
+ function operationProjection(kind: PortableSlopcameraOperationKind): SlopcameraCapabilityOperation {
35
+ const operation = PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS[kind]
36
+ return {
37
+ cache: operation.policy.cache,
38
+ cancellable: operation.policy.cancellable,
39
+ effect: operation.policy.effect,
40
+ inputSchemaId: operation.inputSchemaId,
41
+ key: `${operation.kind}@${operation.version}`,
42
+ kind: operation.kind,
43
+ lifecycle: operation.lifecycle,
44
+ maxDurationMs: operation.policy.maxDurationMs,
45
+ maxFanOut: operation.policy.maxFanOut,
46
+ maxInputBytes: operation.policy.maxInputBytes,
47
+ maxOutputBytes: operation.policy.maxOutputBytes,
48
+ outputSchemaId: operation.outputSchemaId,
49
+ preparation: [...operation.policy.preparation],
50
+ resources: operation.policy.resources.map(claim => ({ ...claim })),
51
+ resume: operation.policy.resume,
52
+ version: operation.version,
53
+ }
54
+ }
55
+
56
+ export function createPortableSlopcameraCapabilityManifest(): SlopcameraCapabilityManifest {
57
+ return compileSlopcameraCapabilityManifest({
58
+ host: "portable",
59
+ modules: [portableModule],
60
+ operations: PORTABLE_SLOPCAMERA_OPERATION_KINDS.map(operationProjection),
61
+ tools: [],
62
+ toolVersion: SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,
63
+ workflows: [],
64
+ })
65
+ }
66
+
67
+ export const slopcameraPortableCapabilityManifest = createPortableSlopcameraCapabilityManifest()