@hraness/slopcamera 3.2.8 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -9
- package/apps/desktop/application/context.ts +6 -0
- package/apps/desktop/application/default-registry.ts +26 -0
- package/apps/desktop/application/operation.ts +14 -1
- package/apps/desktop/application/operations/index.ts +4 -0
- package/apps/desktop/application/operations/spatial-behavior.ts +151 -0
- package/apps/desktop/application/operations/spatial-direction.ts +222 -0
- package/apps/desktop/application/operations/spatial-rendered-audit.ts +174 -0
- package/apps/desktop/application/operations/spatial-review.ts +194 -0
- package/apps/desktop/application/operations/spatial-scene.ts +25 -1
- package/apps/desktop/application/registry.ts +2 -2
- package/apps/desktop/application/spatial-asset-admission.ts +201 -0
- package/apps/desktop/application/spatial-assets.ts +160 -31
- package/apps/desktop/application/spatial-render.ts +217 -20
- package/apps/desktop/application/spatial-rendered-audit.ts +273 -0
- package/apps/desktop/application/spatial-review.ts +361 -0
- package/apps/desktop/application/spatial-spz.ts +48 -3
- package/apps/desktop/application/spatial-world-import.ts +26 -5
- package/apps/desktop/application/spatial-world-metadata.ts +105 -0
- package/apps/desktop/cli/args.ts +514 -11
- package/apps/desktop/cli/capability-manifest.ts +374 -0
- package/apps/desktop/cli/cinema-renderer.ts +608 -0
- package/apps/desktop/cli/command-host-resources.ts +29 -1
- package/apps/desktop/cli/commands.ts +1188 -4
- package/apps/desktop/cli/gateway-review-provider.ts +334 -0
- package/apps/desktop/cli/help.ts +172 -6
- package/apps/desktop/cli/main.ts +3 -3
- package/apps/desktop/cli/portable-surface.ts +6 -1
- package/apps/desktop/cli/spatial-asset-service.ts +124 -0
- package/apps/desktop/cli/spatial-design-service.ts +237 -0
- package/apps/desktop/cli/spatial-generate-service.ts +370 -0
- package/apps/desktop/cli/spatial-scene-service.ts +268 -4
- package/apps/desktop/cli/workflow-code.ts +1 -1
- package/apps/desktop/code/semantic-builder.ts +24 -0
- package/apps/desktop/contracts/cinema.ts +736 -0
- package/apps/desktop/contracts/index.ts +1 -0
- package/apps/desktop/contracts/spatial-asset.ts +16 -0
- package/apps/desktop/contracts/spatial-world.ts +26 -3
- package/apps/desktop/core/cinema-plan.ts +1320 -0
- package/apps/desktop/core/index.ts +1 -0
- package/apps/desktop/core/storage.ts +75 -0
- package/apps/desktop/dist/cli/main.js +725 -273
- package/apps/desktop/html-overlay/spatial.ts +619 -57
- package/apps/desktop/workflows/cinematic-world.ts +127 -0
- package/apps/desktop/workflows/index.ts +6 -0
- package/dist/cli.js +25 -429
- package/dist/code/advanced.js +1 -269
- package/dist/code/index.js +16 -2239
- package/dist/generate.js +1 -23
- package/dist/host-resources.js +1 -25
- package/dist/index-0bbesn4m.js +35 -0
- package/dist/index-296qhpf2.js +3 -0
- package/dist/index-3cxf2c58.js +3 -0
- package/dist/index-77fjfg9f.js +3 -0
- package/dist/index-80a36bc0.js +3 -0
- package/dist/index-a3vvs3rf.js +3 -0
- package/dist/index-b3geqczn.js +5 -0
- package/dist/index-cgpkyjqa.js +3 -0
- package/dist/index-f0ptv8dm.js +7 -0
- package/dist/index-grk47tgs.js +3 -0
- package/dist/index-qry58nj2.js +10 -0
- package/dist/index.js +1 -226
- package/dist/operations.js +1 -28
- package/dist/skill-install-gx7afxsd.js +2 -0
- package/dist/vectorize/worker.js +1 -141
- package/dist/workflow.js +1 -15
- package/docs/README.md +5 -1
- package/package.json +7 -5
- package/skills/slopcamera/SKILL.md +6 -1
- package/skills/slopcamera/references/directed-scenes.md +22 -2
- package/skills/slopcamera/references/gateway-media.md +15 -1
- package/skills/slopcamera/references/image-galleries.md +166 -0
- package/skills/slopcamera/references/install.md +1 -1
- package/skills/slopcamera/references/parametric-design.md +14 -0
- package/skills/slopcamera/references/scene-building.md +32 -0
- package/skills/slopcamera/references/social-collage-banners.md +204 -0
- package/skills/slopcamera/references/workflows-sdk.md +3 -3
- package/skills/slopcamera/scripts/compose-social-collage-banner.ts +728 -0
- package/src/capability-manifest.ts +361 -0
- package/src/cli.ts +222 -4
- package/src/code/canonical-json.ts +9 -0
- package/src/generate.ts +2 -2
- package/src/icon.ts +1111 -0
- package/src/image-gallery.ts +1049 -0
- package/src/index.ts +11 -0
- package/src/mcp/boundary.ts +11 -11
- package/src/mcp/index.ts +3 -0
- package/src/mcp/server.ts +2 -1
- package/src/mcp/tools.ts +1642 -130
- package/src/mcp/types.ts +19 -0
- package/src/operations.ts +412 -0
- package/src/portable-capability-manifest.ts +67 -0
- package/src/scene-gallery.ts +184 -0
- package/src/spatial-scene/asset-admission.ts +183 -0
- package/src/spatial-scene/audit-rendered.ts +692 -0
- package/src/spatial-scene/audit.ts +617 -0
- package/src/spatial-scene/behavior-audit.ts +252 -0
- package/src/spatial-scene/behavior-bake.ts +233 -0
- package/src/spatial-scene/behavior-fns.ts +585 -0
- package/src/spatial-scene/behavior-gallery.ts +109 -0
- package/src/spatial-scene/behavior-stdlib.ts +323 -0
- package/src/spatial-scene/behavior-trace.ts +325 -0
- package/src/spatial-scene/behavior.ts +618 -0
- package/src/spatial-scene/build.ts +632 -0
- package/src/spatial-scene/camera-rig.ts +125 -0
- package/src/spatial-scene/camera-track.ts +4 -2
- package/src/spatial-scene/character.ts +124 -0
- package/src/spatial-scene/contracts.ts +75 -7
- package/src/spatial-scene/design-templates.ts +351 -0
- package/src/spatial-scene/design.ts +327 -0
- package/src/spatial-scene/direction-compile.ts +584 -0
- package/src/spatial-scene/direction.ts +122 -0
- package/src/spatial-scene/effects.ts +149 -0
- package/src/spatial-scene/evaluate.ts +109 -29
- package/src/spatial-scene/gallery.ts +277 -0
- package/src/spatial-scene/generate.ts +239 -0
- package/src/spatial-scene/geometry-native.ts +165 -0
- package/src/spatial-scene/geometry.ts +1216 -0
- package/src/spatial-scene/gltf.ts +600 -77
- package/src/spatial-scene/identity.ts +55 -2
- package/src/spatial-scene/index.ts +36 -0
- package/src/spatial-scene/inspect.ts +35 -6
- package/src/spatial-scene/material-lighting.ts +609 -0
- package/src/spatial-scene/math.ts +21 -4
- package/src/spatial-scene/motion-evidence.ts +88 -0
- package/src/spatial-scene/parametric.ts +765 -0
- package/src/spatial-scene/particle-preparation.ts +183 -0
- package/src/spatial-scene/particle.ts +195 -0
- package/src/spatial-scene/patch.ts +109 -15
- package/src/spatial-scene/performance.ts +1613 -0
- package/src/spatial-scene/probe.ts +137 -0
- package/src/spatial-scene/recipe-pack.ts +86 -0
- package/src/spatial-scene/render-effects.ts +240 -0
- package/src/spatial-scene/review.ts +639 -0
- package/src/spatial-scene/simulation-bake.ts +206 -0
- package/src/spatial-scene/simulation.ts +187 -0
- package/src/spatial-scene/solve.ts +366 -0
- package/src/spatial-scene/temporal-audit.ts +354 -0
- package/src/version.ts +1 -1
- package/dist/index-42zsesc1.js +0 -2257
- package/dist/index-7131pg1c.js +0 -1820
- package/dist/index-7308egqr.js +0 -48
- package/dist/index-8txs6fkn.js +0 -712
- package/dist/index-fava6pge.js +0 -192
- package/dist/index-h1k0fnjq.js +0 -2363
- package/dist/index-r7gdhmsp.js +0 -424
- package/dist/index-sh6xbav6.js +0 -1219
- package/dist/index-z1w83f81.js +0 -4
- package/dist/index-zfnddgay.js +0 -2029
- package/dist/skill-install-dh0ntqba.js +0 -10
|
@@ -2,16 +2,33 @@ import { constants } from "node:fs";
|
|
|
2
2
|
import { open, realpath } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, relative, resolve } from "node:path";
|
|
4
4
|
import { canonicalJson } from "../../../src/code/canonical-json";
|
|
5
|
+
import { normalizeSpatialAuditAssetBounds, spatialEntityLocalBounds } from "../../../src/spatial-scene/audit";
|
|
5
6
|
import { createSpatialSceneStarter } from "../../../src/spatial-scene/authoring";
|
|
6
|
-
import { SPATIAL_SCENE_LIMITS } from "../../../src/spatial-scene/contracts";
|
|
7
|
-
import { parseSpatialScene, spatialSceneSha256 } from "../../../src/spatial-scene/index";
|
|
8
7
|
import { sampleSpatialCameraTrack } from "../../../src/spatial-scene/camera-track";
|
|
8
|
+
import { SPATIAL_SCENE_LIMITS, SpatialCameraIdSchema } from "../../../src/spatial-scene/contracts";
|
|
9
|
+
import { checkSpatialBehavior } from "../../../src/spatial-scene/behavior";
|
|
10
|
+
import { auditSpatialBehaviorTrace } from "../../../src/spatial-scene/behavior-audit";
|
|
11
|
+
import { bakeSpatialBehavior } from "../../../src/spatial-scene/behavior-bake";
|
|
12
|
+
import { spatialBehaviorFnSignatures } from "../../../src/spatial-scene/behavior-fns";
|
|
13
|
+
import { planSpatialBehaviorGallery, spatialBehaviorGalleryPlanSha256 } from "../../../src/spatial-scene/behavior-gallery";
|
|
14
|
+
import { parseSpatialBehaviorChannelMap, spatialBehaviorBakeSha256 } from "../../../src/spatial-scene/behavior-trace";
|
|
15
|
+
import { checkSpatialDirection, compileSpatialDirection, spatialDirectionCompilationSha256 } from "../../../src/spatial-scene/direction-compile";
|
|
16
|
+
import { createSpatialEvaluationContext, evaluateSpatialSceneInContext } from "../../../src/spatial-scene/evaluate";
|
|
17
|
+
import { planSpatialDirectionGallery, spatialGalleryPlanSha256 } from "../../../src/spatial-scene/gallery";
|
|
18
|
+
import { checkSpatialRenderEffects, planSpatialRenderEffects } from "../../../src/spatial-scene/render-effects";
|
|
19
|
+
import { SPATIAL_REVIEW_LIMITS } from "../../../src/spatial-scene/review";
|
|
20
|
+
import { bakeSpatialSimulation, spatialSimulationBakeSha256 } from "../../../src/spatial-scene/simulation-bake";
|
|
21
|
+
import { auditSpatialTemporalEvidence, SpatialTemporalContactsFileSchema, spatialTemporalAuditReportSha256 } from "../../../src/spatial-scene/temporal-audit";
|
|
22
|
+
import { parseSpatialScene, parseSpatialValue, spatialSceneSha256 } from "../../../src/spatial-scene/index";
|
|
23
|
+
import { diffSpatialScenes } from "../../../src/spatial-scene/patch";
|
|
24
|
+
import { SpatialSolveError, SpatialSolveGoalsFileSchema, solveSpatialRelations, type SpatialSolveResult } from "../../../src/spatial-scene/solve";
|
|
9
25
|
import type { ApplicationContext } from "../application/context";
|
|
10
26
|
import { createApplicationOperationRegistry } from "../application/default-registry";
|
|
11
27
|
import { planSpatialRender } from "../application/spatial-render";
|
|
12
28
|
import { createNodeBundleFileSystem } from "../core/storage";
|
|
13
29
|
import type { SpatialCliExecutionProfile, SpatialSceneCommand } from "./args";
|
|
14
|
-
import { CliError } from "./errors";
|
|
30
|
+
import { CliError, type CliErrorCode } from "./errors";
|
|
31
|
+
import { executeSpatialGenerateCommand } from "./spatial-generate-service";
|
|
15
32
|
|
|
16
33
|
/** Capture an explicit local regular file once, without following a leaf symlink. */
|
|
17
34
|
export async function readSpatialJson(path: string, maximumBytes: number = SPATIAL_SCENE_LIMITS.sourceBytes): Promise<unknown> {
|
|
@@ -54,15 +71,166 @@ function assertCameraTrackActive(signal: AbortSignal | undefined): void {
|
|
|
54
71
|
if (signal?.aborted === true) throw new CliError("cancelled", "Camera track export was cancelled.");
|
|
55
72
|
}
|
|
56
73
|
|
|
74
|
+
/** Solver codes collapse onto the CLI code set; the typed code stays in the bracketed message prefix. */
|
|
75
|
+
const SOLVE_ERROR_CODES: Readonly<Record<string, CliErrorCode>> = {
|
|
76
|
+
"invalid-data": "invalid-data",
|
|
77
|
+
"unknown-entity": "not-found",
|
|
78
|
+
"bounds-unknown": "invalid-data",
|
|
79
|
+
"relation-cycle": "conflict",
|
|
80
|
+
"duplicate-goal": "conflict",
|
|
81
|
+
"relation-failed": "invalid-data",
|
|
82
|
+
};
|
|
83
|
+
|
|
57
84
|
export async function executeSpatialSceneCommand(application: ApplicationContext, command: SpatialSceneCommand, signal?: AbortSignal): Promise<unknown> {
|
|
85
|
+
if (command.action === "generate") return executeSpatialGenerateCommand(application, command);
|
|
86
|
+
// Fail closed before touching the filesystem, renderer, or provider: review
|
|
87
|
+
// uploads the selected bounded rendered beauty frames to a vision model and
|
|
88
|
+
// requires --allow-cloud-upload on this exact invocation.
|
|
89
|
+
if (command.action === "review" && !command.allowCloudUpload) {
|
|
90
|
+
throw new CliError("authorization-required", "scene review uploads bounded rendered beauty frames to a vision model and requires --allow-cloud-upload on this invocation.", { command: "scene review" });
|
|
91
|
+
}
|
|
92
|
+
if (command.action === "behavior-audit") {
|
|
93
|
+
const bake = await readSpatialJson(resolve(application.paths.repositoryRoot, command.bake));
|
|
94
|
+
const report = auditSpatialBehaviorTrace(bake);
|
|
95
|
+
if (command.output === undefined) return report;
|
|
96
|
+
const output = resolve(application.paths.repositoryRoot, command.output);
|
|
97
|
+
await publishSpatialSource(output, report);
|
|
98
|
+
return { path: output, findings: report.findings.length, channels: report.channelCount, emitted: report.emittedCount };
|
|
99
|
+
}
|
|
58
100
|
if (command.action === "camera-track") assertCameraTrackActive(signal);
|
|
59
|
-
const sourcePath = resolve(application.paths.repositoryRoot, command.path);
|
|
101
|
+
const sourcePath = resolve(application.paths.repositoryRoot, "path" in command ? command.path : command.scene);
|
|
60
102
|
if (command.action === "init") {
|
|
61
103
|
const scene = createSpatialSceneStarter();
|
|
62
104
|
await publishSpatialSource(sourcePath, scene);
|
|
63
105
|
return { path: sourcePath, sceneSha256: spatialSceneSha256(scene) };
|
|
64
106
|
}
|
|
107
|
+
if (command.action === "review") {
|
|
108
|
+
const acknowledgedAt = application.clock.now().toISOString();
|
|
109
|
+
const authorizedApplication: ApplicationContext = {
|
|
110
|
+
...application,
|
|
111
|
+
spatialReviewAuthorization: {
|
|
112
|
+
authorize: async request => (typeof request.sceneSha256 === "string" && /^[a-f0-9]{64}$/u.test(request.sceneSha256)
|
|
113
|
+
&& SpatialCameraIdSchema.safeParse(request.cameraId).success
|
|
114
|
+
&& Array.isArray(request.timesUs)
|
|
115
|
+
&& request.timesUs.length >= 1
|
|
116
|
+
&& request.timesUs.length <= SPATIAL_REVIEW_LIMITS.frames
|
|
117
|
+
&& request.maximumFrames >= 1
|
|
118
|
+
&& request.maximumFrames <= SPATIAL_REVIEW_LIMITS.frames
|
|
119
|
+
&& request.maximumFrameBytes <= SPATIAL_REVIEW_LIMITS.pngBytes
|
|
120
|
+
&& request.maximumUploadBytes <= SPATIAL_REVIEW_LIMITS.uploadBytes
|
|
121
|
+
? { acknowledgedAt }
|
|
122
|
+
: undefined),
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
const result = await createApplicationOperationRegistry().execute({ application: authorizedApplication, abortSignal: signal ?? new AbortController().signal }, {
|
|
126
|
+
kind: "scene.review", version: 1,
|
|
127
|
+
input: {
|
|
128
|
+
source: { path: relative(application.paths.repositoryRoot, sourcePath) },
|
|
129
|
+
request: { cameraId: command.camera, ...(command.timesUs === undefined ? {} : { timesUs: [...command.timesUs] }) },
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
return result.output;
|
|
133
|
+
}
|
|
134
|
+
if (command.action === "render-audit") {
|
|
135
|
+
const result = await createApplicationOperationRegistry().execute({ application, abortSignal: signal ?? new AbortController().signal }, {
|
|
136
|
+
kind: "scene.render-audit", version: 1,
|
|
137
|
+
input: {
|
|
138
|
+
source: { path: relative(application.paths.repositoryRoot, sourcePath) },
|
|
139
|
+
request: { cameraId: command.camera, ...(command.timesUs === undefined ? {} : { timesUs: [...command.timesUs] }) },
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
return result.output;
|
|
143
|
+
}
|
|
65
144
|
const scene = parseSpatialScene(await readSpatialJson(sourcePath));
|
|
145
|
+
if (command.action === "diff") {
|
|
146
|
+
const other = parseSpatialScene(await readSpatialJson(resolve(application.paths.repositoryRoot, command.other)));
|
|
147
|
+
return { sceneSha256: spatialSceneSha256(scene), otherSha256: spatialSceneSha256(other), diff: diffSpatialScenes(scene, other) };
|
|
148
|
+
}
|
|
149
|
+
if (command.action === "solve") {
|
|
150
|
+
const goalsFile = parseSpatialValue(SpatialSolveGoalsFileSchema, await readSpatialJson(resolve(application.paths.repositoryRoot, command.goals)), "solve goals");
|
|
151
|
+
const assetBounds = command.assetBounds === undefined
|
|
152
|
+
? {}
|
|
153
|
+
: normalizeSpatialAuditAssetBounds(await readSpatialJson(resolve(application.paths.repositoryRoot, command.assetBounds)));
|
|
154
|
+
const entities = new Map(scene.entities.map(entity => [entity.entityId, entity]));
|
|
155
|
+
const cameras = new Map(scene.cameras.map(camera => [camera.cameraId, camera]));
|
|
156
|
+
const transformOverrides = new Map(
|
|
157
|
+
scene.overrides.filter(override => override.property === "transform").map(override => [override.entityId, override.value]),
|
|
158
|
+
);
|
|
159
|
+
const animatedTransforms = new Map<string, string[]>();
|
|
160
|
+
for (const channel of scene.animations) {
|
|
161
|
+
if (channel.property === "position" || channel.property === "rotation" || channel.property === "scale") {
|
|
162
|
+
animatedTransforms.set(channel.targetId, [...(animatedTransforms.get(channel.targetId) ?? []), channel.channelId]);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const referenced = new Set<string>();
|
|
166
|
+
for (const goal of goalsFile.goals) {
|
|
167
|
+
referenced.add(goal.entityKey);
|
|
168
|
+
for (const relation of goal.relations) if ("target" in relation) referenced.add(relation.target);
|
|
169
|
+
}
|
|
170
|
+
for (const goal of goalsFile.goals) {
|
|
171
|
+
const entity = entities.get(goal.entityKey);
|
|
172
|
+
if (entity === undefined) throw new CliError("not-found", `Solve goal ${goal.entityKey} is not an entity in the scene.`);
|
|
173
|
+
if (entity.origin.kind === "generated") {
|
|
174
|
+
throw new CliError("conflict", `Goal entity ${entity.entityId} is generated; edit it through declared overrides or retained output replacement.`);
|
|
175
|
+
}
|
|
176
|
+
if (entity.parentId !== null) {
|
|
177
|
+
throw new CliError("conflict", `Goal entity ${entity.entityId} is parented under ${entity.parentId}; solver bases are world transforms — pass an explicit base entry or reparent first.`);
|
|
178
|
+
}
|
|
179
|
+
if (entity.placement.kind !== "world") {
|
|
180
|
+
throw new CliError("conflict", `Goal entity ${entity.entityId} uses view placement; solver transforms are world-space.`);
|
|
181
|
+
}
|
|
182
|
+
if (transformOverrides.has(entity.entityId)) {
|
|
183
|
+
throw new CliError("conflict", `Goal entity ${entity.entityId} has a transform override; a set-transform patch would stay shadowed.`);
|
|
184
|
+
}
|
|
185
|
+
const writers = animatedTransforms.get(entity.entityId);
|
|
186
|
+
if (writers !== undefined) {
|
|
187
|
+
throw new CliError("conflict", `Goal entity ${entity.entityId} is animated (${writers.join(", ")}); a solved transform would not take effect.`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const bases: Record<string, unknown> = {};
|
|
191
|
+
for (const key of [...referenced].sort()) {
|
|
192
|
+
const entity = entities.get(key);
|
|
193
|
+
if (entity !== undefined) {
|
|
194
|
+
// Local bounds are transform-agnostic, so they seed whenever derivable;
|
|
195
|
+
// the authored transform only seeds a base when it is the world transform.
|
|
196
|
+
const explicit = goalsFile.bases?.[key]?.transform !== undefined;
|
|
197
|
+
if (!explicit && (entity.parentId !== null || entity.placement.kind !== "world")) {
|
|
198
|
+
throw new CliError("conflict", `Referenced entity ${key} is ${entity.parentId === null ? "view-placed" : `parented under ${entity.parentId}`}; solver bases are world transforms — pass an explicit base entry.`);
|
|
199
|
+
}
|
|
200
|
+
const enclosure = spatialEntityLocalBounds(entity, assetBounds);
|
|
201
|
+
bases[key] = {
|
|
202
|
+
...(explicit ? {} : { transform: transformOverrides.get(key) ?? entity.transform }),
|
|
203
|
+
...(enclosure.status === "bounded" ? { bounds: enclosure.bounds } : {}),
|
|
204
|
+
};
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const camera = cameras.get(key);
|
|
208
|
+
if (camera !== undefined) {
|
|
209
|
+
bases[key] = { transform: { position: camera.pose.position, rotation: camera.pose.rotation, scale: [1, 1, 1] } };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const [key, basePatch] of Object.entries(goalsFile.bases ?? {})) {
|
|
213
|
+
const existing = bases[key] as { transform?: unknown; bounds?: unknown } | undefined;
|
|
214
|
+
bases[key] = {
|
|
215
|
+
...(existing ?? {}),
|
|
216
|
+
...(basePatch.transform === undefined ? {} : { transform: basePatch.transform }),
|
|
217
|
+
...(basePatch.bounds === undefined ? {} : { bounds: basePatch.bounds }),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
let result: SpatialSolveResult;
|
|
221
|
+
try {
|
|
222
|
+
result = solveSpatialRelations({ goals: goalsFile.goals, bases });
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (error instanceof SpatialSolveError) {
|
|
225
|
+
throw new CliError(SOLVE_ERROR_CODES[error.code] ?? "invalid-data", `[${error.code}] ${error.message}`);
|
|
226
|
+
}
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
if (command.output === undefined) return result.patch;
|
|
230
|
+
const output = resolve(application.paths.repositoryRoot, command.output);
|
|
231
|
+
await publishSpatialSource(output, result.patch);
|
|
232
|
+
return { path: output, sceneSha256: spatialSceneSha256(scene), solvedEntities: Object.keys(result.transforms) };
|
|
233
|
+
}
|
|
66
234
|
if (command.action === "camera-track") {
|
|
67
235
|
const fence = async () => {
|
|
68
236
|
assertCameraTrackActive(signal);
|
|
@@ -78,6 +246,91 @@ export async function executeSpatialSceneCommand(application: ApplicationContext
|
|
|
78
246
|
await fence();
|
|
79
247
|
return { path: output, sceneSha256: track.sceneSha256, cameraId: track.cameraId, clock: track.clock, executed: false };
|
|
80
248
|
}
|
|
249
|
+
if (command.action === "direction-check") {
|
|
250
|
+
const direction = await readSpatialJson(resolve(application.paths.repositoryRoot, command.direction));
|
|
251
|
+
return checkSpatialDirection({ direction, scene });
|
|
252
|
+
}
|
|
253
|
+
if (command.action === "direction-plan") {
|
|
254
|
+
const direction = await readSpatialJson(resolve(application.paths.repositoryRoot, command.direction));
|
|
255
|
+
const compilation = compileSpatialDirection({ direction, scene, ...(command.camera === undefined ? {} : { cameraId: command.camera }) });
|
|
256
|
+
if (command.output === undefined) return compilation;
|
|
257
|
+
const output = resolve(application.paths.repositoryRoot, command.output);
|
|
258
|
+
await publishSpatialSource(output, compilation);
|
|
259
|
+
return { path: output, compilationSha256: spatialDirectionCompilationSha256(compilation), proposals: compilation.proposals, advisories: compilation.advisories };
|
|
260
|
+
}
|
|
261
|
+
if (command.action === "direction-gallery") {
|
|
262
|
+
const direction = await readSpatialJson(resolve(application.paths.repositoryRoot, command.direction));
|
|
263
|
+
const plan = planSpatialDirectionGallery({ axis: command.axis, direction, scene, ...(command.camera === undefined ? {} : { cameraId: command.camera }) });
|
|
264
|
+
if (command.output === undefined) return plan;
|
|
265
|
+
const output = resolve(application.paths.repositoryRoot, command.output);
|
|
266
|
+
await publishSpatialSource(output, plan);
|
|
267
|
+
return { path: output, galleryPlanSha256: spatialGalleryPlanSha256(plan), candidates: plan.candidates.map(candidate => ({ candidateId: candidate.candidateId, documentSha256: candidate.documentSha256 })) };
|
|
268
|
+
}
|
|
269
|
+
if (command.action === "effects-check") {
|
|
270
|
+
const effects = await readSpatialJson(resolve(application.paths.repositoryRoot, command.effects));
|
|
271
|
+
return checkSpatialRenderEffects({ effects, scene });
|
|
272
|
+
}
|
|
273
|
+
if (command.action === "effects-plan") {
|
|
274
|
+
const draft = await readSpatialJson(resolve(application.paths.repositoryRoot, command.draft));
|
|
275
|
+
if (typeof draft !== "object" || draft === null || Array.isArray(draft)) throw new CliError("invalid-data", "An effects draft must be a JSON object with renderPlan, particleSystems, and simulationBakes fields.");
|
|
276
|
+
const binding = planSpatialRenderEffects({ ...draft, scene });
|
|
277
|
+
if (command.output === undefined) return binding;
|
|
278
|
+
const output = resolve(application.paths.repositoryRoot, command.output);
|
|
279
|
+
await publishSpatialSource(output, binding);
|
|
280
|
+
return { path: output, documentSha256: binding.documentSha256 };
|
|
281
|
+
}
|
|
282
|
+
if (command.action === "effects-bake") {
|
|
283
|
+
const plan = await readSpatialJson(resolve(application.paths.repositoryRoot, command.simulation));
|
|
284
|
+
const result = bakeSpatialSimulation(plan);
|
|
285
|
+
const output = resolve(application.paths.repositoryRoot, command.output);
|
|
286
|
+
await publishSpatialSource(output, result.document);
|
|
287
|
+
return { path: output, documentSha256: spatialSimulationBakeSha256(result.document), receipt: result.receipt };
|
|
288
|
+
}
|
|
289
|
+
if (command.action === "behavior-check") {
|
|
290
|
+
const behavior = await readSpatialJson(resolve(application.paths.repositoryRoot, command.behavior));
|
|
291
|
+
return checkSpatialBehavior({ behavior, scene }, spatialBehaviorFnSignatures());
|
|
292
|
+
}
|
|
293
|
+
if (command.action === "behavior-bake" || command.action === "behavior-gallery") {
|
|
294
|
+
const behavior = await readSpatialJson(resolve(application.paths.repositoryRoot, command.behavior));
|
|
295
|
+
const channelMap = command.channelMap === undefined
|
|
296
|
+
? undefined
|
|
297
|
+
: parseSpatialBehaviorChannelMap(await readSpatialJson(resolve(application.paths.repositoryRoot, command.channelMap)));
|
|
298
|
+
if (command.action === "behavior-bake") {
|
|
299
|
+
const { bake } = await bakeSpatialBehavior({ behavior, scene, ...(channelMap === undefined ? {} : { channelMap }) });
|
|
300
|
+
const output = resolve(application.paths.repositoryRoot, command.output);
|
|
301
|
+
await publishSpatialSource(output, bake);
|
|
302
|
+
return {
|
|
303
|
+
path: output,
|
|
304
|
+
bakeSha256: spatialBehaviorBakeSha256(bake),
|
|
305
|
+
emitted: bake.emitted.length,
|
|
306
|
+
directives: bake.directives.length,
|
|
307
|
+
unresolvedIntents: bake.unresolvedIntents.length,
|
|
308
|
+
receipt: bake.receipt,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const plan = await planSpatialBehaviorGallery({ behavior, scene, ...(channelMap === undefined ? {} : { channelMap }) });
|
|
312
|
+
if (command.output === undefined) return plan;
|
|
313
|
+
const output = resolve(application.paths.repositoryRoot, command.output);
|
|
314
|
+
await publishSpatialSource(output, plan);
|
|
315
|
+
return { path: output, galleryPlanSha256: spatialBehaviorGalleryPlanSha256(plan), candidates: plan.candidates.map(candidate => ({ candidateId: candidate.candidateId, documentSha256: candidate.documentSha256, parameter: candidate.parameter })) };
|
|
316
|
+
}
|
|
317
|
+
if (command.action === "temporal-audit") {
|
|
318
|
+
const times = command.timesUs ?? (() => {
|
|
319
|
+
const count = Math.min(17, Math.max(2, Math.ceil(scene.durationUs / 1_000_000) + 1));
|
|
320
|
+
return Array.from({ length: count }, (_, index) => Math.floor((scene.durationUs * index) / (count - 1)));
|
|
321
|
+
})();
|
|
322
|
+
if (times.length < 2) throw new CliError("invalid-data", "scene temporal-audit requires at least two sample times.");
|
|
323
|
+
const contacts = command.contacts === undefined
|
|
324
|
+
? []
|
|
325
|
+
: parseSpatialValue(SpatialTemporalContactsFileSchema, await readSpatialJson(resolve(application.paths.repositoryRoot, command.contacts)), "temporal contacts").contacts;
|
|
326
|
+
const context = createSpatialEvaluationContext(scene);
|
|
327
|
+
const snapshots = [...times].sort((left, right) => left - right).map(timeUs => evaluateSpatialSceneInContext(context, { cameraId: command.camera, timeUs }));
|
|
328
|
+
const report = auditSpatialTemporalEvidence({
|
|
329
|
+
snapshots,
|
|
330
|
+
options: { contacts, ...(command.cutBeforeUs === undefined ? {} : { cutBeforeUs: [...command.cutBeforeUs] }) },
|
|
331
|
+
});
|
|
332
|
+
return { ...report, reportSha256: spatialTemporalAuditReportSha256(report) };
|
|
333
|
+
}
|
|
81
334
|
if (command.action === "plan" || command.action === "render") {
|
|
82
335
|
const request = bindSpatialCliExecutionProfile(await readSpatialJson(resolve(application.paths.repositoryRoot, command.request)), command.executionProfile);
|
|
83
336
|
if (command.action === "plan") return planSpatialRender(scene, request);
|
|
@@ -90,6 +343,17 @@ export async function executeSpatialSceneCommand(application: ApplicationContext
|
|
|
90
343
|
return result.output;
|
|
91
344
|
}
|
|
92
345
|
const registry = createApplicationOperationRegistry();
|
|
346
|
+
if (command.action === "audit") {
|
|
347
|
+
const result = await registry.execute({ application, abortSignal: new AbortController().signal }, {
|
|
348
|
+
kind: "scene.audit", version: 1,
|
|
349
|
+
input: {
|
|
350
|
+
scene, cameraId: command.camera,
|
|
351
|
+
...(command.timesUs === undefined ? {} : { timesUs: [...command.timesUs] }),
|
|
352
|
+
...(command.assetBounds === undefined ? {} : { assetBounds: normalizeSpatialAuditAssetBounds(await readSpatialJson(resolve(application.paths.repositoryRoot, command.assetBounds))) }),
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
return result.output;
|
|
356
|
+
}
|
|
93
357
|
const input = command.action === "patch"
|
|
94
358
|
? { scene, patch: await readSpatialJson(resolve(application.paths.repositoryRoot, command.patch)) }
|
|
95
359
|
: command.action === "evaluate" ? { scene, cameraId: command.camera, timeUs: command.timeUs } : { scene };
|
|
@@ -308,7 +308,7 @@ export function workflowCatalogDescription(workflow: BuiltInWorkflow) {
|
|
|
308
308
|
return {
|
|
309
309
|
...workflowCatalogEntry(workflow),
|
|
310
310
|
inputJsonSchema: {
|
|
311
|
-
...z.toJSONSchema(workflow.inputSchema),
|
|
311
|
+
...z.toJSONSchema(workflow.inputSchema, { io: "input" }),
|
|
312
312
|
$id: workflow.inputSchemaId,
|
|
313
313
|
},
|
|
314
314
|
} as const;
|
|
@@ -4,6 +4,12 @@ import type {
|
|
|
4
4
|
CandidateProjectRenderInput,
|
|
5
5
|
SpatialInspectInput, SpatialInspectOutput, SpatialPatchInput, SpatialPatchOutput,
|
|
6
6
|
SpatialEvaluateInput, SpatialEvaluateOutput,
|
|
7
|
+
SpatialDirectionCheckInput, SpatialDirectionCheckOutput,
|
|
8
|
+
SpatialDirectionCompileInput, SpatialDirectionCompileOutput,
|
|
9
|
+
SpatialDirectionGalleryInput, SpatialDirectionGalleryOutput,
|
|
10
|
+
SpatialEffectsCheckInput, SpatialEffectsCheckOutput,
|
|
11
|
+
SpatialEffectsPlanInput, SpatialEffectsPlanOutput,
|
|
12
|
+
SpatialTemporalAuditInput, SpatialTemporalAuditOutput,
|
|
7
13
|
BindCandidateRevisionOutput,
|
|
8
14
|
CommitProjectEditsOutput,
|
|
9
15
|
CreateCandidateRevisionOutput,
|
|
@@ -1036,6 +1042,24 @@ export class WorkflowBuilder {
|
|
|
1036
1042
|
this.#graph.operationByKind(key, { input, kind: "scene.patch", version: 1 }, options),
|
|
1037
1043
|
evaluate: (key: string, input: OperationInputValue<SpatialEvaluateInput>, options?: OperationNodeOptions): Ref<SpatialEvaluateOutput> =>
|
|
1038
1044
|
this.#graph.operationByKind(key, { input, kind: "scene.evaluate", version: 1 }, options),
|
|
1045
|
+
/**
|
|
1046
|
+
* Pure semantic planning and review operations: direction checking and
|
|
1047
|
+
* compilation, bounded galleries, render-effects binding, and sampled
|
|
1048
|
+
* temporal audit. They propose and review documents; they never mutate a
|
|
1049
|
+
* project, render media, or select a candidate.
|
|
1050
|
+
*/
|
|
1051
|
+
directionCheck: (key: string, input: OperationInputValue<SpatialDirectionCheckInput> | SpatialDirectionCheckInput, options?: OperationNodeOptions): Ref<SpatialDirectionCheckOutput> =>
|
|
1052
|
+
this.#graph.operationByKind(key, { input, kind: "scene.direction.check", version: 1 }, options),
|
|
1053
|
+
directionCompile: (key: string, input: OperationInputValue<SpatialDirectionCompileInput> | SpatialDirectionCompileInput, options?: OperationNodeOptions): Ref<SpatialDirectionCompileOutput> =>
|
|
1054
|
+
this.#graph.operationByKind(key, { input, kind: "scene.direction.compile", version: 1 }, options),
|
|
1055
|
+
directionGallery: (key: string, input: OperationInputValue<SpatialDirectionGalleryInput> | SpatialDirectionGalleryInput, options?: OperationNodeOptions): Ref<SpatialDirectionGalleryOutput> =>
|
|
1056
|
+
this.#graph.operationByKind(key, { input, kind: "scene.direction.gallery", version: 1 }, options),
|
|
1057
|
+
effectsCheck: (key: string, input: OperationInputValue<SpatialEffectsCheckInput> | SpatialEffectsCheckInput, options?: OperationNodeOptions): Ref<SpatialEffectsCheckOutput> =>
|
|
1058
|
+
this.#graph.operationByKind(key, { input, kind: "scene.effects.check", version: 1 }, options),
|
|
1059
|
+
effectsPlan: (key: string, input: OperationInputValue<SpatialEffectsPlanInput> | SpatialEffectsPlanInput, options?: OperationNodeOptions): Ref<SpatialEffectsPlanOutput> =>
|
|
1060
|
+
this.#graph.operationByKind(key, { input, kind: "scene.effects.plan", version: 1 }, options),
|
|
1061
|
+
temporalAudit: (key: string, input: OperationInputValue<SpatialTemporalAuditInput> | SpatialTemporalAuditInput, options?: OperationNodeOptions): Ref<SpatialTemporalAuditOutput> =>
|
|
1062
|
+
this.#graph.operationByKind(key, { input, kind: "scene.temporal-audit", version: 1 }, options),
|
|
1039
1063
|
});
|
|
1040
1064
|
|
|
1041
1065
|
readonly spatialProject = Object.freeze({
|