@hraness/slopcamera 3.2.7 → 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/NOTICE.md +1 -7
- package/PRIVACY.md +16 -7
- package/README.md +65 -20
- package/apps/desktop/README.md +14 -130
- package/apps/desktop/application/context.ts +6 -9
- package/apps/desktop/application/default-registry.ts +26 -10
- package/apps/desktop/application/operation.ts +14 -5
- package/apps/desktop/application/operations/index.ts +4 -1
- 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 +528 -97
- 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 +31 -2
- package/apps/desktop/cli/commands.ts +1215 -70
- package/apps/desktop/cli/gateway-review-provider.ts +334 -0
- package/apps/desktop/cli/help.ts +183 -23
- package/apps/desktop/cli/html-scene.ts +139 -28
- package/apps/desktop/cli/io.ts +2 -1
- package/apps/desktop/cli/main.ts +26 -50
- package/apps/desktop/cli/menubar.ts +244 -0
- package/apps/desktop/cli/music-analysis-service.ts +61 -0
- package/apps/desktop/cli/portable-surface.ts +18 -5
- 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/application-node-planner.ts +0 -47
- package/apps/desktop/code/host-source-layout.ts +46 -0
- package/apps/desktop/code/public.ts +0 -1
- package/apps/desktop/code/runtime-identity.ts +50 -8
- package/apps/desktop/code/semantic-builder.ts +24 -43
- package/apps/desktop/code/source-bundle.ts +23 -6
- package/apps/desktop/code/source-typecheck.ts +131 -43
- package/apps/desktop/code/worker-client.ts +14 -2
- 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/music-analysis.ts +124 -1
- package/apps/desktop/core/storage.ts +75 -0
- package/apps/desktop/dist/cli/main.js +737 -291
- package/apps/desktop/html-overlay/audio-reactivity.ts +127 -0
- package/apps/desktop/html-overlay/index.ts +9 -0
- package/apps/desktop/html-overlay/runtime.ts +2 -0
- package/apps/desktop/html-overlay/scene.ts +29 -0
- 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 +32 -434
- 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 -240
- 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/docs/studio.md +1 -1
- package/package.json +11 -24
- package/skills/slopcamera/SKILL.md +14 -1
- package/skills/slopcamera/references/diagrams.md +5 -14
- 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 +2 -2
- package/skills/slopcamera/references/music-video.md +23 -3
- package/skills/slopcamera/references/parametric-design.md +14 -0
- package/skills/slopcamera/references/reference-led-3d.md +1 -1
- package/skills/slopcamera/references/scene-building.md +32 -0
- package/skills/slopcamera/references/social-collage-banners.md +204 -0
- package/skills/slopcamera/references/support.md +25 -0
- package/skills/slopcamera/references/web-media-excerpts.md +120 -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 +250 -102
- 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 -23
- 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/support-completion.ts +16 -0
- package/src/support.ts +42 -0
- package/src/version.ts +1 -1
- package/apps/desktop/application/operations/recording/index.ts +0 -5
- package/apps/desktop/application/operations/recording/pause.ts +0 -34
- package/apps/desktop/application/operations/recording/resume.ts +0 -34
- package/apps/desktop/application/operations/recording/shared.ts +0 -199
- package/apps/desktop/application/operations/recording/start.ts +0 -169
- package/apps/desktop/application/operations/recording/stop.ts +0 -34
- package/apps/desktop/capture/protocol.ts +0 -689
- package/apps/desktop/cli/capture-bundle.ts +0 -1117
- package/apps/desktop/cli/recording-controller.ts +0 -1567
- package/apps/desktop/cli/recording-daemon.ts +0 -931
- package/dist/index-42zsesc1.js +0 -2257
- package/dist/index-7308egqr.js +0 -48
- package/dist/index-8txs6fkn.js +0 -712
- package/dist/index-mcy8z0br.js +0 -192
- package/dist/index-p63wavx0.js +0 -2029
- package/dist/index-r7gdhmsp.js +0 -424
- package/dist/index-rvghzt7b.js +0 -2013
- package/dist/index-sh6xbav6.js +0 -1219
- package/dist/index-z1w83f81.js +0 -4
- package/dist/index-z7239b4h.js +0 -2363
- package/dist/skill-install-dh0ntqba.js +0 -10
- package/src/desktop.ts +0 -253
package/apps/desktop/cli/args.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { CliError } from "./errors";
|
|
2
2
|
import { MAX_EVENT_QUERY_LIMIT } from "./query-limits";
|
|
3
3
|
import { STUDIO_TEMPLATES, type StudioTemplate } from "./studio-template-names";
|
|
4
|
+
import {
|
|
5
|
+
slopcameraGalleryKinds,
|
|
6
|
+
slopcameraGalleryLimits,
|
|
7
|
+
} from "../../../src/image-gallery";
|
|
4
8
|
|
|
5
9
|
export type OverlayKind = "image" | "svg" | "gif" | "video" | "emoji";
|
|
6
10
|
export type ZoomTargetKind = "rect" | "point" | "cursor" | "window" | "focused-input";
|
|
@@ -58,6 +62,12 @@ export type SpatialWorldCommand = JsonOption & {
|
|
|
58
62
|
readonly kind: "spatial-world"; readonly action: "import";
|
|
59
63
|
readonly input: string; readonly sourceRoot: string; readonly outputRoot: string;
|
|
60
64
|
};
|
|
65
|
+
export type SpatialAssetCommand = JsonOption & {
|
|
66
|
+
readonly kind: "spatial-asset"; readonly action: "admit";
|
|
67
|
+
readonly file: string; readonly output: string;
|
|
68
|
+
readonly sourceRoot?: string; readonly assetId?: string;
|
|
69
|
+
readonly metersPerUnit: number; readonly sourceUp: "x" | "y" | "z";
|
|
70
|
+
};
|
|
61
71
|
export type DirectingCommand = JsonOption & { readonly kind: "directing" } & (
|
|
62
72
|
| { readonly action: "init"; readonly path: string }
|
|
63
73
|
| { readonly action: "anchor"; readonly input: string }
|
|
@@ -69,12 +79,37 @@ export type DirectingCommand = JsonOption & { readonly kind: "directing" } & (
|
|
|
69
79
|
| { readonly action: "resume" | "cleanup"; readonly id: string; readonly attempt: string }
|
|
70
80
|
| { readonly action: "review"; readonly id: string; readonly attempt: string; readonly decision: "accepted" | "rejected"; readonly note: string }
|
|
71
81
|
);
|
|
72
|
-
export type SpatialSceneCommand = JsonOption & { readonly kind: "spatial-scene"
|
|
73
|
-
| { readonly action: "init" | "inspect" }
|
|
74
|
-
| { readonly action: "
|
|
75
|
-
| { readonly action: "
|
|
76
|
-
| { readonly action: "
|
|
77
|
-
| { readonly action: "
|
|
82
|
+
export type SpatialSceneCommand = JsonOption & { readonly kind: "spatial-scene" } & (
|
|
83
|
+
| { readonly action: "init" | "inspect"; readonly path: string }
|
|
84
|
+
| { readonly action: "diff"; readonly path: string; readonly other: string }
|
|
85
|
+
| { readonly action: "patch"; readonly path: string; readonly patch: string; readonly output: string }
|
|
86
|
+
| { readonly action: "evaluate"; readonly path: string; readonly camera: string; readonly timeUs: number }
|
|
87
|
+
| { readonly action: "audit"; readonly path: string; readonly camera: string; readonly timesUs?: readonly number[]; readonly assetBounds?: string }
|
|
88
|
+
| { readonly action: "render-audit"; readonly path: string; readonly camera: string; readonly timesUs?: readonly number[] }
|
|
89
|
+
| { readonly action: "solve"; readonly path: string; readonly goals: string; readonly output?: string; readonly assetBounds?: string }
|
|
90
|
+
| { readonly action: "review"; readonly path: string; readonly camera: string; readonly timesUs?: readonly number[]; readonly allowCloudUpload: boolean }
|
|
91
|
+
| { readonly action: "camera-track"; readonly path: string; readonly request: string; readonly output: string }
|
|
92
|
+
| { readonly action: "plan" | "render"; readonly path: string; readonly request: string; readonly assets?: string; readonly executionProfile?: SpatialCliExecutionProfile }
|
|
93
|
+
| { readonly action: "generate"; readonly module: string; readonly generatorId: string; readonly parameters?: string; readonly seed?: number; readonly into?: string; readonly output: string }
|
|
94
|
+
| { readonly action: "direction-check"; readonly direction: string; readonly scene: string }
|
|
95
|
+
| { readonly action: "direction-plan"; readonly direction: string; readonly scene: string; readonly camera?: string; readonly output?: string }
|
|
96
|
+
| { readonly action: "direction-gallery"; readonly direction: string; readonly scene: string; readonly axis: string; readonly camera?: string; readonly output?: string }
|
|
97
|
+
| { readonly action: "effects-check"; readonly effects: string; readonly scene: string }
|
|
98
|
+
| { readonly action: "effects-plan"; readonly draft: string; readonly scene: string; readonly output?: string }
|
|
99
|
+
| { readonly action: "effects-bake"; readonly simulation: string; readonly scene: string; readonly output: string }
|
|
100
|
+
| { readonly action: "behavior-check"; readonly behavior: string; readonly scene: string }
|
|
101
|
+
| { readonly action: "behavior-bake"; readonly behavior: string; readonly scene: string; readonly channelMap?: string; readonly output: string }
|
|
102
|
+
| { readonly action: "behavior-gallery"; readonly behavior: string; readonly scene: string; readonly channelMap?: string; readonly output?: string }
|
|
103
|
+
| { readonly action: "behavior-audit"; readonly bake: string; readonly output?: string }
|
|
104
|
+
| { readonly action: "temporal-audit"; readonly path: string; readonly camera: string; readonly timesUs?: readonly number[]; readonly contacts?: string; readonly cutBeforeUs?: readonly number[] }
|
|
105
|
+
);
|
|
106
|
+
export type SpatialDesignCommand = JsonOption & { readonly kind: "spatial-design" } & (
|
|
107
|
+
| { readonly action: "catalog" }
|
|
108
|
+
| { readonly action: "init"; readonly directory: string; readonly template: string }
|
|
109
|
+
| { readonly action: "inspect"; readonly path: string }
|
|
110
|
+
| { readonly action: "set"; readonly path: string; readonly parameters: string; readonly output: string }
|
|
111
|
+
| { readonly action: "compile"; readonly path: string; readonly scene?: string; readonly outputDir: string }
|
|
112
|
+
| { readonly action: "gallery"; readonly path: string; readonly scene?: string; readonly variants: string; readonly outputDir: string }
|
|
78
113
|
);
|
|
79
114
|
export type SpatialProjectCommand = JsonOption & {
|
|
80
115
|
readonly kind: "spatial-project"; readonly project: string;
|
|
@@ -89,10 +124,13 @@ export type CliCommand =
|
|
|
89
124
|
| StudioCommand
|
|
90
125
|
| DirectingCommand
|
|
91
126
|
| SpatialWorldCommand
|
|
127
|
+
| SpatialAssetCommand
|
|
92
128
|
| SpatialSceneCommand
|
|
129
|
+
| SpatialDesignCommand
|
|
93
130
|
| SpatialProjectCommand
|
|
94
131
|
| { readonly kind: "help"; readonly topic: readonly string[] }
|
|
95
132
|
| { readonly kind: "version" }
|
|
133
|
+
| ({ readonly kind: "capabilities" } & JsonOption)
|
|
96
134
|
| ({ readonly kind: "operations-list" } & JsonOption)
|
|
97
135
|
| ({ readonly kind: "operations-show"; readonly operation: string } & JsonOption)
|
|
98
136
|
| ({ readonly kind: "diagram-check"; readonly path: string } & JsonOption)
|
|
@@ -194,6 +232,29 @@ export type CliCommand =
|
|
|
194
232
|
readonly temperature: number | undefined;
|
|
195
233
|
readonly timeout: string;
|
|
196
234
|
} & JsonOption)
|
|
235
|
+
| ({
|
|
236
|
+
readonly candidatesFile: string | undefined;
|
|
237
|
+
readonly cell: number | undefined;
|
|
238
|
+
readonly count: number | undefined;
|
|
239
|
+
readonly galleryKind: string | undefined;
|
|
240
|
+
readonly kind: "ai-image-gallery";
|
|
241
|
+
readonly model: string;
|
|
242
|
+
readonly outputDir: string;
|
|
243
|
+
readonly preview: "probe" | undefined;
|
|
244
|
+
readonly subject: string;
|
|
245
|
+
readonly tiled: boolean | undefined;
|
|
246
|
+
readonly timeout: string;
|
|
247
|
+
readonly vary: string | undefined;
|
|
248
|
+
} & JsonOption)
|
|
249
|
+
| ({
|
|
250
|
+
readonly camera: string | undefined;
|
|
251
|
+
readonly cell: number | undefined;
|
|
252
|
+
readonly kind: "ai-scene-gallery";
|
|
253
|
+
readonly outputDir: string;
|
|
254
|
+
readonly scene: string;
|
|
255
|
+
readonly timeUs: number | undefined;
|
|
256
|
+
readonly variants: string;
|
|
257
|
+
} & JsonOption)
|
|
197
258
|
| ({
|
|
198
259
|
readonly allowCloudUpload: boolean;
|
|
199
260
|
readonly aspectRatio: string | undefined;
|
|
@@ -271,6 +332,8 @@ export type CliCommand =
|
|
|
271
332
|
readonly tint: number | undefined;
|
|
272
333
|
readonly videoStreamIndex: number;
|
|
273
334
|
} & JsonOption)
|
|
335
|
+
| ({ readonly kind: "menubar"; readonly action: "run" | "install" | "uninstall" | "status"; readonly mode: "foreground" | "background" } & JsonOption)
|
|
336
|
+
| ({ readonly kind: "outputs" } & JsonOption)
|
|
274
337
|
| ({ readonly kind: "recordings-list"; readonly limit: number } & JsonOption)
|
|
275
338
|
| ({ readonly kind: "projects-list"; readonly limit: number } & JsonOption)
|
|
276
339
|
| ({
|
|
@@ -385,6 +448,16 @@ export type CliCommand =
|
|
|
385
448
|
readonly project: string;
|
|
386
449
|
readonly width: number;
|
|
387
450
|
} & JsonOption)
|
|
451
|
+
| ({
|
|
452
|
+
readonly action: "init" | "check" | "plan" | "animatic" | "run" | "audit" | "gallery";
|
|
453
|
+
readonly allowPlaceholders: boolean;
|
|
454
|
+
readonly axis: string | undefined;
|
|
455
|
+
readonly dryRun: boolean;
|
|
456
|
+
readonly force: boolean;
|
|
457
|
+
readonly kind: "project-cinema";
|
|
458
|
+
readonly output: string | undefined;
|
|
459
|
+
readonly project: string;
|
|
460
|
+
} & JsonOption)
|
|
388
461
|
| ({
|
|
389
462
|
readonly apply: boolean;
|
|
390
463
|
readonly candidate: string | undefined;
|
|
@@ -490,20 +563,6 @@ export type CliCommand =
|
|
|
490
563
|
readonly recording: string;
|
|
491
564
|
readonly to: string | undefined;
|
|
492
565
|
})
|
|
493
|
-
| ({
|
|
494
|
-
readonly action: "start";
|
|
495
|
-
readonly cameraDeviceId: string | undefined;
|
|
496
|
-
readonly displays: readonly string[];
|
|
497
|
-
readonly microphone: boolean;
|
|
498
|
-
readonly microphoneDeviceId: string | undefined;
|
|
499
|
-
readonly strictInputs: boolean;
|
|
500
|
-
readonly systemAudio: boolean;
|
|
501
|
-
readonly typedText: boolean;
|
|
502
|
-
readonly webcam: boolean;
|
|
503
|
-
} & JsonOption & { readonly kind: "record" })
|
|
504
|
-
| ({ readonly action: "pause" | "resume" | "stop" | "status" } & JsonOption & {
|
|
505
|
-
readonly kind: "record";
|
|
506
|
-
})
|
|
507
566
|
| ({
|
|
508
567
|
readonly edit: EditCommand;
|
|
509
568
|
readonly fps: number | undefined;
|
|
@@ -799,22 +858,6 @@ function strictBoolean(value: string | undefined, name: string, defaults: boolea
|
|
|
799
858
|
fail(`${name} must be true or false.`);
|
|
800
859
|
}
|
|
801
860
|
|
|
802
|
-
function captureSourceId(
|
|
803
|
-
value: string | undefined,
|
|
804
|
-
name: string,
|
|
805
|
-
maximumUTF8Bytes: number,
|
|
806
|
-
): string | undefined {
|
|
807
|
-
if (value === undefined) return undefined;
|
|
808
|
-
if (
|
|
809
|
-
value.length === 0
|
|
810
|
-
|| value.includes("\0")
|
|
811
|
-
|| new TextEncoder().encode(value).byteLength > maximumUTF8Bytes
|
|
812
|
-
) {
|
|
813
|
-
fail(`${name} must be a non-empty, non-NUL source ID of at most ${maximumUTF8Bytes} UTF-8 bytes.`);
|
|
814
|
-
}
|
|
815
|
-
return value;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
861
|
function oneOf<T extends string>(
|
|
819
862
|
value: string | undefined,
|
|
820
863
|
name: string,
|
|
@@ -837,6 +880,12 @@ function exactPositionals(parsed: ParsedOptions, count: number, usage: string):
|
|
|
837
880
|
|
|
838
881
|
const JSON_SPEC = { "--json": "flag" } as const;
|
|
839
882
|
|
|
883
|
+
function parseCapabilities(argv: readonly string[]): CliCommand {
|
|
884
|
+
const parsed = parseOptions(argv, JSON_SPEC);
|
|
885
|
+
exactPositionals(parsed, 0, "slopcamera capabilities [--json]");
|
|
886
|
+
return { json: optionFlag(parsed, "--json"), kind: "capabilities" };
|
|
887
|
+
}
|
|
888
|
+
|
|
840
889
|
function parseDoctor(argv: readonly string[]): CliCommand {
|
|
841
890
|
const parsed = parseOptions(argv, JSON_SPEC);
|
|
842
891
|
exactPositionals(parsed, 0, "slopcamera doctor [--json]");
|
|
@@ -939,7 +988,10 @@ function parseAiProviderOptions(argv: readonly string[]): CliCommand {
|
|
|
939
988
|
}
|
|
940
989
|
|
|
941
990
|
function parseAiImage(argv: readonly string[]): CliCommand {
|
|
942
|
-
if (argv[0]
|
|
991
|
+
if (argv[0] === "gallery") return parseAiImageGallery(argv.slice(1));
|
|
992
|
+
if (argv[0] !== "generate") {
|
|
993
|
+
fail("Usage: slopcamera ai image <generate|gallery> --model <id> [options]");
|
|
994
|
+
}
|
|
943
995
|
const parsed = parseOptions(argv.slice(1), {
|
|
944
996
|
...JSON_SPEC,
|
|
945
997
|
"--allow-cloud-upload": "flag",
|
|
@@ -997,6 +1049,145 @@ function parseAiImage(argv: readonly string[]): CliCommand {
|
|
|
997
1049
|
};
|
|
998
1050
|
}
|
|
999
1051
|
|
|
1052
|
+
function parseAiImageGallery(argv: readonly string[]): CliCommand {
|
|
1053
|
+
const parsed = parseOptions(argv, {
|
|
1054
|
+
...JSON_SPEC,
|
|
1055
|
+
"--candidates": "value",
|
|
1056
|
+
"--cell": "value",
|
|
1057
|
+
"--count": "value",
|
|
1058
|
+
"--kind": "value",
|
|
1059
|
+
"--model": "value",
|
|
1060
|
+
"--no-tile": "flag",
|
|
1061
|
+
"--output-dir": "value",
|
|
1062
|
+
"--preview": "value",
|
|
1063
|
+
"--tile": "flag",
|
|
1064
|
+
"--timeout": "value",
|
|
1065
|
+
"--vary": "value",
|
|
1066
|
+
});
|
|
1067
|
+
const [subject] = exactPositionals(
|
|
1068
|
+
parsed,
|
|
1069
|
+
1,
|
|
1070
|
+
"slopcamera ai image gallery <subject> --model <id> --output-dir <directory> [options]",
|
|
1071
|
+
);
|
|
1072
|
+
const model = optionString(parsed, "--model");
|
|
1073
|
+
if (model === undefined) fail("--model is required.");
|
|
1074
|
+
const outputDir = optionString(parsed, "--output-dir");
|
|
1075
|
+
if (outputDir === undefined || outputDir.trim() === "") {
|
|
1076
|
+
fail("--output-dir is required.");
|
|
1077
|
+
}
|
|
1078
|
+
const galleryKind = optionString(parsed, "--kind");
|
|
1079
|
+
if (
|
|
1080
|
+
galleryKind !== undefined
|
|
1081
|
+
&& !(slopcameraGalleryKinds as readonly string[]).includes(galleryKind)
|
|
1082
|
+
) {
|
|
1083
|
+
fail(`--kind must be one of: ${slopcameraGalleryKinds.join(", ")}.`);
|
|
1084
|
+
}
|
|
1085
|
+
const count = optionalStrictInteger(optionString(parsed, "--count"), "--count");
|
|
1086
|
+
if (count !== undefined && count > slopcameraGalleryLimits.candidates) {
|
|
1087
|
+
fail(`--count must be at most ${slopcameraGalleryLimits.candidates}.`);
|
|
1088
|
+
}
|
|
1089
|
+
const cell = optionalStrictInteger(optionString(parsed, "--cell"), "--cell");
|
|
1090
|
+
if (
|
|
1091
|
+
cell !== undefined
|
|
1092
|
+
&& (
|
|
1093
|
+
cell < slopcameraGalleryLimits.cellEdgeMin
|
|
1094
|
+
|| cell > slopcameraGalleryLimits.cellEdgeMax
|
|
1095
|
+
)
|
|
1096
|
+
) {
|
|
1097
|
+
fail(
|
|
1098
|
+
`--cell must be between ${slopcameraGalleryLimits.cellEdgeMin} and ${slopcameraGalleryLimits.cellEdgeMax}.`,
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
const candidatesFile = optionString(parsed, "--candidates");
|
|
1102
|
+
const vary = optionString(parsed, "--vary");
|
|
1103
|
+
if (candidatesFile !== undefined && (vary !== undefined || count !== undefined)) {
|
|
1104
|
+
fail("--candidates is mutually exclusive with --vary and --count.");
|
|
1105
|
+
}
|
|
1106
|
+
const tile = optionFlag(parsed, "--tile");
|
|
1107
|
+
const noTile = optionFlag(parsed, "--no-tile");
|
|
1108
|
+
if (tile && noTile) fail("--tile and --no-tile are mutually exclusive.");
|
|
1109
|
+
const preview = optionString(parsed, "--preview");
|
|
1110
|
+
if (preview !== undefined && preview !== "probe") {
|
|
1111
|
+
fail("--preview must be probe.");
|
|
1112
|
+
}
|
|
1113
|
+
if (
|
|
1114
|
+
preview === "probe"
|
|
1115
|
+
&& !["texture", "skybox", "backdrop"].includes(galleryKind ?? "image")
|
|
1116
|
+
) {
|
|
1117
|
+
fail("--preview probe requires --kind texture, skybox, or backdrop.");
|
|
1118
|
+
}
|
|
1119
|
+
return {
|
|
1120
|
+
candidatesFile,
|
|
1121
|
+
cell,
|
|
1122
|
+
count,
|
|
1123
|
+
galleryKind,
|
|
1124
|
+
json: optionFlag(parsed, "--json"),
|
|
1125
|
+
kind: "ai-image-gallery",
|
|
1126
|
+
model,
|
|
1127
|
+
outputDir,
|
|
1128
|
+
preview: preview as "probe" | undefined,
|
|
1129
|
+
subject: subject!,
|
|
1130
|
+
tiled: tile ? true : noTile ? false : undefined,
|
|
1131
|
+
timeout: optionString(parsed, "--timeout") ?? "10m",
|
|
1132
|
+
vary,
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function parseAiScene(argv: readonly string[]): CliCommand {
|
|
1137
|
+
if (argv[0] !== "gallery") {
|
|
1138
|
+
fail("Usage: slopcamera ai scene gallery <scene.json> --variants <file.json> --output-dir <directory> [options]");
|
|
1139
|
+
}
|
|
1140
|
+
const parsed = parseOptions(argv.slice(1), {
|
|
1141
|
+
...JSON_SPEC,
|
|
1142
|
+
"--camera": "value",
|
|
1143
|
+
"--cell": "value",
|
|
1144
|
+
"--output-dir": "value",
|
|
1145
|
+
"--time-us": "value",
|
|
1146
|
+
"--variants": "value",
|
|
1147
|
+
});
|
|
1148
|
+
const [scene] = exactPositionals(
|
|
1149
|
+
parsed,
|
|
1150
|
+
1,
|
|
1151
|
+
"slopcamera ai scene gallery <scene.json> --variants <file.json> --output-dir <directory> [options]",
|
|
1152
|
+
);
|
|
1153
|
+
const variants = optionString(parsed, "--variants");
|
|
1154
|
+
if (variants === undefined) fail("--variants is required.");
|
|
1155
|
+
const outputDir = optionString(parsed, "--output-dir");
|
|
1156
|
+
if (outputDir === undefined || outputDir.trim() === "") {
|
|
1157
|
+
fail("--output-dir is required.");
|
|
1158
|
+
}
|
|
1159
|
+
const timeUsText = optionString(parsed, "--time-us");
|
|
1160
|
+
let timeUs: number | undefined;
|
|
1161
|
+
if (timeUsText !== undefined) {
|
|
1162
|
+
if (!/^\d+$/u.test(timeUsText) || !Number.isSafeInteger(Number(timeUsText))) {
|
|
1163
|
+
fail("--time-us must be a nonnegative integer microsecond time.");
|
|
1164
|
+
}
|
|
1165
|
+
timeUs = Number(timeUsText);
|
|
1166
|
+
}
|
|
1167
|
+
const cell = optionalStrictInteger(optionString(parsed, "--cell"), "--cell");
|
|
1168
|
+
if (
|
|
1169
|
+
cell !== undefined
|
|
1170
|
+
&& (
|
|
1171
|
+
cell < slopcameraGalleryLimits.cellEdgeMin
|
|
1172
|
+
|| cell > slopcameraGalleryLimits.cellEdgeMax
|
|
1173
|
+
)
|
|
1174
|
+
) {
|
|
1175
|
+
fail(
|
|
1176
|
+
`--cell must be between ${slopcameraGalleryLimits.cellEdgeMin} and ${slopcameraGalleryLimits.cellEdgeMax}.`,
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
return {
|
|
1180
|
+
camera: optionString(parsed, "--camera"),
|
|
1181
|
+
cell,
|
|
1182
|
+
json: optionFlag(parsed, "--json"),
|
|
1183
|
+
kind: "ai-scene-gallery",
|
|
1184
|
+
outputDir,
|
|
1185
|
+
scene: scene!,
|
|
1186
|
+
timeUs,
|
|
1187
|
+
variants,
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1000
1191
|
function parseAiVideo(argv: readonly string[]): CliCommand {
|
|
1001
1192
|
if (argv[0] !== "generate") fail("Usage: slopcamera ai video generate --model <id> [options]");
|
|
1002
1193
|
const parsed = parseOptions(argv.slice(1), {
|
|
@@ -1159,11 +1350,12 @@ function parseAi(argv: readonly string[]): CliCommand {
|
|
|
1159
1350
|
case "models": return parseAiModels(argv.slice(1));
|
|
1160
1351
|
case "provider-options": return parseAiProviderOptions(argv.slice(1));
|
|
1161
1352
|
case "image": return parseAiImage(argv.slice(1));
|
|
1353
|
+
case "scene": return parseAiScene(argv.slice(1));
|
|
1162
1354
|
case "video": return parseAiVideo(argv.slice(1));
|
|
1163
1355
|
case "speech": return parseAiSpeech(argv.slice(1));
|
|
1164
1356
|
case "transcribe": return parseAiTranscribe(argv.slice(1));
|
|
1165
1357
|
case undefined:
|
|
1166
|
-
default: fail("Usage: slopcamera ai <models|provider-options|image|video|speech|transcribe> [options]");
|
|
1358
|
+
default: fail("Usage: slopcamera ai <models|provider-options|image|scene|video|speech|transcribe> [options]");
|
|
1167
1359
|
}
|
|
1168
1360
|
}
|
|
1169
1361
|
|
|
@@ -1770,7 +1962,65 @@ function parseProject(argv: readonly string[]): CliCommand {
|
|
|
1770
1962
|
width: strictEvenPositiveInteger(optionString(parsed, "--width"), "--width", 1_920),
|
|
1771
1963
|
};
|
|
1772
1964
|
}
|
|
1773
|
-
|
|
1965
|
+
if (action === "cinema") {
|
|
1966
|
+
const cinemaAction = argv[1];
|
|
1967
|
+
if (
|
|
1968
|
+
cinemaAction !== "init"
|
|
1969
|
+
&& cinemaAction !== "check"
|
|
1970
|
+
&& cinemaAction !== "plan"
|
|
1971
|
+
&& cinemaAction !== "animatic"
|
|
1972
|
+
&& cinemaAction !== "run"
|
|
1973
|
+
&& cinemaAction !== "audit"
|
|
1974
|
+
&& cinemaAction !== "gallery"
|
|
1975
|
+
) {
|
|
1976
|
+
fail("Usage: slopcamera project cinema <init|check|plan|animatic|run|audit|gallery> <project> [options]");
|
|
1977
|
+
}
|
|
1978
|
+
const parsed = parseOptions(argv.slice(2), {
|
|
1979
|
+
...JSON_SPEC,
|
|
1980
|
+
"--allow-placeholders": "flag",
|
|
1981
|
+
"--axis": "value",
|
|
1982
|
+
"--dry-run": "flag",
|
|
1983
|
+
"--force": "flag",
|
|
1984
|
+
"--output": "value",
|
|
1985
|
+
});
|
|
1986
|
+
const [project] = exactPositionals(
|
|
1987
|
+
parsed,
|
|
1988
|
+
1,
|
|
1989
|
+
`slopcamera project cinema ${cinemaAction} <project> [options]`,
|
|
1990
|
+
);
|
|
1991
|
+
if (optionFlag(parsed, "--dry-run") && cinemaAction !== "animatic" && cinemaAction !== "run") {
|
|
1992
|
+
fail("--dry-run is valid only for project cinema animatic or run.");
|
|
1993
|
+
}
|
|
1994
|
+
if (optionFlag(parsed, "--force") && cinemaAction !== "init") {
|
|
1995
|
+
fail("--force is valid only for project cinema init.");
|
|
1996
|
+
}
|
|
1997
|
+
const output = optionString(parsed, "--output");
|
|
1998
|
+
if (output !== undefined && (cinemaAction === "init" || cinemaAction === "check" || cinemaAction === "audit")) {
|
|
1999
|
+
fail("--output is valid only for project cinema plan, animatic, run, or gallery.");
|
|
2000
|
+
}
|
|
2001
|
+
const axis = optionString(parsed, "--axis");
|
|
2002
|
+
if (cinemaAction === "gallery" && axis === undefined) {
|
|
2003
|
+
fail("project cinema gallery requires --axis <pacing|transitions|looks|audio|structure|mixed>.");
|
|
2004
|
+
}
|
|
2005
|
+
if (axis !== undefined && cinemaAction !== "gallery") {
|
|
2006
|
+
fail("--axis is valid only for project cinema gallery.");
|
|
2007
|
+
}
|
|
2008
|
+
if (optionFlag(parsed, "--allow-placeholders") && (cinemaAction === "init" || cinemaAction === "check" || cinemaAction === "audit" || cinemaAction === "gallery")) {
|
|
2009
|
+
fail("--allow-placeholders is valid only for project cinema plan, animatic, or run.");
|
|
2010
|
+
}
|
|
2011
|
+
return {
|
|
2012
|
+
action: cinemaAction,
|
|
2013
|
+
allowPlaceholders: optionFlag(parsed, "--allow-placeholders"),
|
|
2014
|
+
axis,
|
|
2015
|
+
dryRun: optionFlag(parsed, "--dry-run"),
|
|
2016
|
+
force: optionFlag(parsed, "--force"),
|
|
2017
|
+
json: optionFlag(parsed, "--json"),
|
|
2018
|
+
kind: "project-cinema",
|
|
2019
|
+
output,
|
|
2020
|
+
project: project!,
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
fail("Usage: slopcamera project <inspect|add|edit|render|cinema> ...");
|
|
1774
2024
|
}
|
|
1775
2025
|
|
|
1776
2026
|
function parseInspect(argv: readonly string[]): CliCommand {
|
|
@@ -1825,61 +2075,6 @@ function parseEvents(argv: readonly string[]): CliCommand {
|
|
|
1825
2075
|
};
|
|
1826
2076
|
}
|
|
1827
2077
|
|
|
1828
|
-
function parseRecord(argv: readonly string[]): CliCommand {
|
|
1829
|
-
const action = argv[0];
|
|
1830
|
-
if (action === "start") {
|
|
1831
|
-
const parsed = parseOptions(argv.slice(1), {
|
|
1832
|
-
...JSON_SPEC,
|
|
1833
|
-
"--camera-device": "value",
|
|
1834
|
-
"--display": "repeat",
|
|
1835
|
-
"--microphone": "value",
|
|
1836
|
-
"--microphone-device": "value",
|
|
1837
|
-
"--strict-inputs": "flag",
|
|
1838
|
-
"--system-audio": "value",
|
|
1839
|
-
"--typed-text": "value",
|
|
1840
|
-
"--webcam": "value",
|
|
1841
|
-
});
|
|
1842
|
-
exactPositionals(parsed, 0, "slopcamera record start [options]");
|
|
1843
|
-
const displays = optionStrings(parsed, "--display").map((displayId) =>
|
|
1844
|
-
captureSourceId(displayId, "--display", 64)!
|
|
1845
|
-
);
|
|
1846
|
-
if (displays.length > 16) fail("--display may be specified at most 16 times.");
|
|
1847
|
-
if (new Set(displays).size !== displays.length) fail("--display IDs must be unique.");
|
|
1848
|
-
const microphone = strictBoolean(optionString(parsed, "--microphone"), "--microphone", true);
|
|
1849
|
-
const webcam = strictBoolean(optionString(parsed, "--webcam"), "--webcam", true);
|
|
1850
|
-
const cameraDeviceId = captureSourceId(optionString(parsed, "--camera-device"), "--camera-device", 256);
|
|
1851
|
-
const microphoneDeviceId = captureSourceId(
|
|
1852
|
-
optionString(parsed, "--microphone-device"),
|
|
1853
|
-
"--microphone-device",
|
|
1854
|
-
256,
|
|
1855
|
-
);
|
|
1856
|
-
if (!webcam && cameraDeviceId !== undefined) {
|
|
1857
|
-
fail("--camera-device cannot be combined with --webcam false.");
|
|
1858
|
-
}
|
|
1859
|
-
if (!microphone && microphoneDeviceId !== undefined) {
|
|
1860
|
-
fail("--microphone-device cannot be combined with --microphone false.");
|
|
1861
|
-
}
|
|
1862
|
-
return {
|
|
1863
|
-
action,
|
|
1864
|
-
cameraDeviceId,
|
|
1865
|
-
displays,
|
|
1866
|
-
json: optionFlag(parsed, "--json"),
|
|
1867
|
-
kind: "record",
|
|
1868
|
-
microphone,
|
|
1869
|
-
microphoneDeviceId,
|
|
1870
|
-
strictInputs: optionFlag(parsed, "--strict-inputs"),
|
|
1871
|
-
systemAudio: strictBoolean(optionString(parsed, "--system-audio"), "--system-audio", true),
|
|
1872
|
-
typedText: strictBoolean(optionString(parsed, "--typed-text"), "--typed-text", false),
|
|
1873
|
-
webcam,
|
|
1874
|
-
};
|
|
1875
|
-
}
|
|
1876
|
-
if (action === "pause" || action === "resume" || action === "stop" || action === "status") {
|
|
1877
|
-
const parsed = parseOptions(argv.slice(1), JSON_SPEC);
|
|
1878
|
-
exactPositionals(parsed, 0, `slopcamera record ${action} [--json]`);
|
|
1879
|
-
return { action, json: optionFlag(parsed, "--json"), kind: "record" };
|
|
1880
|
-
}
|
|
1881
|
-
fail("Usage: slopcamera record <start|pause|resume|stop|status> [options]");
|
|
1882
|
-
}
|
|
1883
2078
|
|
|
1884
2079
|
const TIME_RANGE_SPEC = {
|
|
1885
2080
|
"--end": "value",
|
|
@@ -3073,9 +3268,77 @@ function parseSpatialWorldArgs(argv: readonly string[]): SpatialWorldCommand {
|
|
|
3073
3268
|
return fail("Usage: slopcamera scene world import --input <request.json> --source-root <directory> --output-root <artifact-directory>");
|
|
3074
3269
|
}
|
|
3075
3270
|
|
|
3076
|
-
|
|
3271
|
+
const SPATIAL_ASSET_ADMIT_USAGE = "slopcamera scene asset admit <model.glb> --output <manifest.json> [--source-root <directory>] [--asset-id <id>] [--meters-per-unit <n>] [--source-up x|y|z] [--json]";
|
|
3272
|
+
|
|
3273
|
+
function parseSpatialAssetArgs(argv: readonly string[]): SpatialAssetCommand {
|
|
3274
|
+
const action = argv[0];
|
|
3275
|
+
if (action !== "admit") return fail(`Usage: ${SPATIAL_ASSET_ADMIT_USAGE}`);
|
|
3276
|
+
const parsed = parseOptions(argv.slice(1), {
|
|
3277
|
+
...JSON_SPEC, "--source-root": "value", "--asset-id": "value",
|
|
3278
|
+
"--meters-per-unit": "value", "--source-up": "value", "--output": "value",
|
|
3279
|
+
});
|
|
3280
|
+
const [file] = exactPositionals(parsed, 1, SPATIAL_ASSET_ADMIT_USAGE);
|
|
3281
|
+
const output = optionString(parsed, "--output");
|
|
3282
|
+
if (output === undefined) fail("scene asset admit requires --output.");
|
|
3283
|
+
const assetId = optionString(parsed, "--asset-id");
|
|
3284
|
+
if (assetId !== undefined && (assetId.length > 128 || !/^asset_[a-zA-Z0-9][a-zA-Z0-9_-]*$/u.test(assetId))) {
|
|
3285
|
+
fail("--asset-id must be an asset_-prefixed identifier of at most 128 characters.");
|
|
3286
|
+
}
|
|
3287
|
+
return {
|
|
3288
|
+
kind: "spatial-asset", action, file: file!, output,
|
|
3289
|
+
...(optionString(parsed, "--source-root") === undefined ? {} : { sourceRoot: optionString(parsed, "--source-root")! }),
|
|
3290
|
+
...(assetId === undefined ? {} : { assetId }),
|
|
3291
|
+
metersPerUnit: boundedNumber(optionString(parsed, "--meters-per-unit"), "--meters-per-unit", 0.000001, 1_000_000, 1),
|
|
3292
|
+
sourceUp: oneOf(optionString(parsed, "--source-up"), "--source-up", ["x", "y", "z"], "y"),
|
|
3293
|
+
json: optionFlag(parsed, "--json"),
|
|
3294
|
+
};
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
function parseSpatialDesignArgs(argv: readonly string[]): SpatialDesignCommand {
|
|
3298
|
+
const action = argv[0];
|
|
3299
|
+
if (action === "catalog") {
|
|
3300
|
+
const parsed = parseOptions(argv.slice(1), JSON_SPEC);
|
|
3301
|
+
exactPositionals(parsed, 0, "slopcamera scene design catalog [--json]");
|
|
3302
|
+
return { kind: "spatial-design", action, json: optionFlag(parsed, "--json") };
|
|
3303
|
+
}
|
|
3304
|
+
if (action === "init") {
|
|
3305
|
+
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--template": "value" });
|
|
3306
|
+
const [directory] = exactPositionals(parsed, 1, "slopcamera scene design init <directory> --template <id> [--json]");
|
|
3307
|
+
const template = optionString(parsed, "--template");
|
|
3308
|
+
if (template === undefined) return fail("scene design init requires --template; use scene design catalog.");
|
|
3309
|
+
return { kind: "spatial-design", action, directory: directory!, template, json: optionFlag(parsed, "--json") };
|
|
3310
|
+
}
|
|
3311
|
+
if (action === "inspect" || action === "check") {
|
|
3312
|
+
const parsed = parseOptions(argv.slice(1), JSON_SPEC);
|
|
3313
|
+
const [path] = exactPositionals(parsed, 1, "slopcamera scene design inspect <design.json> [--json]");
|
|
3314
|
+
return { kind: "spatial-design", action: "inspect", path: path!, json: optionFlag(parsed, "--json") };
|
|
3315
|
+
}
|
|
3316
|
+
if (action === "set") {
|
|
3317
|
+
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--parameters": "value", "--output": "value" });
|
|
3318
|
+
const [path] = exactPositionals(parsed, 1, "slopcamera scene design set <design.json> --parameters <values.json> --output <new-design.json> [--json]");
|
|
3319
|
+
const parameters = optionString(parsed, "--parameters"), output = optionString(parsed, "--output");
|
|
3320
|
+
if (parameters === undefined || output === undefined) return fail("scene design set requires --parameters and --output.");
|
|
3321
|
+
return { kind: "spatial-design", action, path: path!, parameters, output, json: optionFlag(parsed, "--json") };
|
|
3322
|
+
}
|
|
3323
|
+
if (action === "compile" || action === "gallery") {
|
|
3324
|
+
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--scene": "value", "--output-dir": "value", ...(action === "gallery" ? { "--variants": "value" as const } : {}) });
|
|
3325
|
+
const [path] = exactPositionals(parsed, 1, `slopcamera scene design ${action} <design.json> [--scene <scene.json>] --output-dir <new-directory> [--json]`);
|
|
3326
|
+
const scene = optionString(parsed, "--scene"), outputDir = optionString(parsed, "--output-dir");
|
|
3327
|
+
if (outputDir === undefined) return fail(`scene design ${action} requires --output-dir.`);
|
|
3328
|
+
const shared = { kind: "spatial-design" as const, path: path!, ...(scene === undefined ? {} : { scene }), outputDir, json: optionFlag(parsed, "--json") };
|
|
3329
|
+
if (action === "compile") return { ...shared, action };
|
|
3330
|
+
const variants = optionString(parsed, "--variants");
|
|
3331
|
+
if (variants === undefined) return fail("scene design gallery requires --variants.");
|
|
3332
|
+
return { ...shared, action, variants };
|
|
3333
|
+
}
|
|
3334
|
+
return fail("Usage: slopcamera scene design <catalog|init|inspect|set|compile|gallery> ...");
|
|
3335
|
+
}
|
|
3336
|
+
|
|
3337
|
+
function parseSpatialSceneArgs(argv: readonly string[]): SpatialSceneCommand | SpatialDesignCommand | SpatialProjectCommand | SpatialWorldCommand | SpatialAssetCommand {
|
|
3077
3338
|
const action = argv[0];
|
|
3339
|
+
if (action === "design") return parseSpatialDesignArgs(argv.slice(1));
|
|
3078
3340
|
if (action === "world") return parseSpatialWorldArgs(argv.slice(1));
|
|
3341
|
+
if (action === "asset") return parseSpatialAssetArgs(argv.slice(1));
|
|
3079
3342
|
if (action === "project") {
|
|
3080
3343
|
const projectAction = argv[1];
|
|
3081
3344
|
if (projectAction === "snapshot") {
|
|
@@ -3105,6 +3368,11 @@ function parseSpatialSceneArgs(argv: readonly string[]): SpatialSceneCommand | S
|
|
|
3105
3368
|
const [path] = exactPositionals(parsed, 1, `slopcamera scene ${action} <scene.json> [--json]`);
|
|
3106
3369
|
return { kind: "spatial-scene", action: action === "check" ? "inspect" : action, path: path!, json: optionFlag(parsed, "--json") };
|
|
3107
3370
|
}
|
|
3371
|
+
if (action === "diff") {
|
|
3372
|
+
const parsed = parseOptions(argv.slice(1), JSON_SPEC);
|
|
3373
|
+
const [path, other] = exactPositionals(parsed, 2, "slopcamera scene diff <scene-a.json> <scene-b.json> [--json]");
|
|
3374
|
+
return { kind: "spatial-scene", action, path: path!, other: other!, json: optionFlag(parsed, "--json") };
|
|
3375
|
+
}
|
|
3108
3376
|
if (action === "patch") {
|
|
3109
3377
|
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--patch": "value", "--output": "value" });
|
|
3110
3378
|
const [path] = exactPositionals(parsed, 1, "slopcamera scene patch <scene.json> --patch <patch.json> --output <new-scene.json>");
|
|
@@ -3126,6 +3394,54 @@ function parseSpatialSceneArgs(argv: readonly string[]): SpatialSceneCommand | S
|
|
|
3126
3394
|
if (camera === undefined || time === undefined || !/^\d+$/u.test(time) || !Number.isSafeInteger(Number(time))) fail("scene evaluate requires --camera and integer --time-us.");
|
|
3127
3395
|
return { kind: "spatial-scene", action, path: path!, camera, timeUs: Number(time), json: optionFlag(parsed, "--json") };
|
|
3128
3396
|
}
|
|
3397
|
+
if (action === "audit") {
|
|
3398
|
+
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--camera": "value", "--times-us": "value", "--asset-bounds": "value" });
|
|
3399
|
+
const [path] = exactPositionals(parsed, 1, "slopcamera scene audit <scene.json> --camera <camera-id> [--times-us <csv>] [--asset-bounds <bounds-or-admission.json>] [--json]");
|
|
3400
|
+
const camera = optionString(parsed, "--camera"), times = optionString(parsed, "--times-us"), assetBounds = optionString(parsed, "--asset-bounds");
|
|
3401
|
+
if (camera === undefined) fail("scene audit requires --camera.");
|
|
3402
|
+
let timesUs: number[] | undefined;
|
|
3403
|
+
if (times !== undefined) {
|
|
3404
|
+
if (!/^\d+(,\d+)*$/u.test(times)) return fail("scene audit --times-us must be a comma-separated list of nonnegative integers.");
|
|
3405
|
+
timesUs = times.split(",").map(Number);
|
|
3406
|
+
if (timesUs.length > 64) return fail("scene audit --times-us is bounded to 64 samples.");
|
|
3407
|
+
}
|
|
3408
|
+
return { kind: "spatial-scene", action, path: path!, camera, ...(timesUs === undefined ? {} : { timesUs }), ...(assetBounds === undefined ? {} : { assetBounds }), json: optionFlag(parsed, "--json") };
|
|
3409
|
+
}
|
|
3410
|
+
if (action === "render-audit") {
|
|
3411
|
+
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--camera": "value", "--times-us": "value" });
|
|
3412
|
+
const [path] = exactPositionals(parsed, 1, "slopcamera scene render-audit <scene.json> --camera <camera-id> [--times-us <csv>] [--json]");
|
|
3413
|
+
const camera = optionString(parsed, "--camera"), times = optionString(parsed, "--times-us");
|
|
3414
|
+
if (camera === undefined) fail("scene render-audit requires --camera.");
|
|
3415
|
+
let timesUs: number[] | undefined;
|
|
3416
|
+
if (times !== undefined) {
|
|
3417
|
+
if (!/^\d+(,\d+)*$/u.test(times)) return fail("scene render-audit --times-us must be a comma-separated list of nonnegative integers.");
|
|
3418
|
+
timesUs = times.split(",").map(Number);
|
|
3419
|
+
if (timesUs.length > 64) return fail("scene render-audit --times-us is bounded to 64 samples.");
|
|
3420
|
+
}
|
|
3421
|
+
return { kind: "spatial-scene", action, path: path!, camera, ...(timesUs === undefined ? {} : { timesUs }), json: optionFlag(parsed, "--json") };
|
|
3422
|
+
}
|
|
3423
|
+
if (action === "solve") {
|
|
3424
|
+
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--goals": "value", "--output": "value", "--asset-bounds": "value" });
|
|
3425
|
+
const [path] = exactPositionals(parsed, 1, "slopcamera scene solve <scene.json> --goals <goals.json> [--output <patch.json>] [--asset-bounds <bounds-or-admission.json>] [--json]");
|
|
3426
|
+
const goals = optionString(parsed, "--goals"), output = optionString(parsed, "--output"), assetBounds = optionString(parsed, "--asset-bounds");
|
|
3427
|
+
if (goals === undefined) fail("scene solve requires --goals.");
|
|
3428
|
+
return { kind: "spatial-scene", action, path: path!, goals, ...(output === undefined ? {} : { output }), ...(assetBounds === undefined ? {} : { assetBounds }), json: optionFlag(parsed, "--json") };
|
|
3429
|
+
}
|
|
3430
|
+
if (action === "review") {
|
|
3431
|
+
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--camera": "value", "--times-us": "value", "--allow-cloud-upload": "flag" });
|
|
3432
|
+
const [path] = exactPositionals(parsed, 1, "slopcamera scene review <scene.json> --camera <camera-id> [--times-us <csv>] --allow-cloud-upload [--json]");
|
|
3433
|
+
const camera = optionString(parsed, "--camera"), times = optionString(parsed, "--times-us");
|
|
3434
|
+
if (camera === undefined) fail("scene review requires --camera.");
|
|
3435
|
+
let timesUs: number[] | undefined;
|
|
3436
|
+
if (times !== undefined) {
|
|
3437
|
+
if (!/^\d+(,\d+)*$/u.test(times)) return fail("scene review --times-us must be a comma-separated list of nonnegative integers.");
|
|
3438
|
+
timesUs = times.split(",").map(Number);
|
|
3439
|
+
if (timesUs.length > 4) return fail("scene review --times-us is bounded to 4 samples.");
|
|
3440
|
+
if (timesUs.some(value => !Number.isSafeInteger(value))) return fail("scene review --times-us values must be safe integers.");
|
|
3441
|
+
if (new Set(timesUs).size !== timesUs.length) return fail("scene review --times-us values must be unique.");
|
|
3442
|
+
}
|
|
3443
|
+
return { kind: "spatial-scene", action, path: path!, camera, ...(timesUs === undefined ? {} : { timesUs }), allowCloudUpload: optionFlag(parsed, "--allow-cloud-upload"), json: optionFlag(parsed, "--json") };
|
|
3444
|
+
}
|
|
3129
3445
|
if (action === "plan" || action === "render") {
|
|
3130
3446
|
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--request": "value", "--assets": "value", "--profile": "value" });
|
|
3131
3447
|
const [path] = exactPositionals(parsed, 1, `slopcamera scene ${action} <scene.json> --request <request.json> [--assets <bindings.json>]`);
|
|
@@ -3134,7 +3450,110 @@ function parseSpatialSceneArgs(argv: readonly string[]): SpatialSceneCommand | S
|
|
|
3134
3450
|
const executionProfile = spatialCliExecutionProfile(optionString(parsed, "--profile"));
|
|
3135
3451
|
return { kind: "spatial-scene", action, path: path!, request, ...(assets === undefined ? {} : { assets }), ...(executionProfile === undefined ? {} : { executionProfile }), json: optionFlag(parsed, "--json") };
|
|
3136
3452
|
}
|
|
3137
|
-
|
|
3453
|
+
if (action === "generate") {
|
|
3454
|
+
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--module": "value", "--generator-id": "value", "--parameters": "value", "--seed": "value", "--into": "value", "--output": "value" });
|
|
3455
|
+
exactPositionals(parsed, 0, "slopcamera scene generate --module <generator.ts> --generator-id <id> [--parameters <params.json>] [--seed <n>] [--into <scene.json>] --output <new-scene.json> [--json]");
|
|
3456
|
+
const modulePath = optionString(parsed, "--module"), generatorId = optionString(parsed, "--generator-id"), output = optionString(parsed, "--output");
|
|
3457
|
+
if (modulePath === undefined || generatorId === undefined || output === undefined) fail("scene generate requires --module, --generator-id, and --output.");
|
|
3458
|
+
const seed = optionalNonNegativeInteger(optionString(parsed, "--seed"), "--seed");
|
|
3459
|
+
if (seed !== undefined && seed > 0xffff_ffff) fail("--seed must be between 0 and 4294967295.");
|
|
3460
|
+
const parameters = optionString(parsed, "--parameters"), into = optionString(parsed, "--into");
|
|
3461
|
+
return { kind: "spatial-scene", action, module: modulePath, generatorId, output, ...(parameters === undefined ? {} : { parameters }), ...(seed === undefined ? {} : { seed }), ...(into === undefined ? {} : { into }), json: optionFlag(parsed, "--json") };
|
|
3462
|
+
}
|
|
3463
|
+
if (action === "direction") {
|
|
3464
|
+
const directionAction = argv[1];
|
|
3465
|
+
if (directionAction === "check") {
|
|
3466
|
+
const parsed = parseOptions(argv.slice(2), { ...JSON_SPEC, "--scene": "value" });
|
|
3467
|
+
const [direction] = exactPositionals(parsed, 1, "slopcamera scene direction check <direction.json> --scene <scene.json> [--json]");
|
|
3468
|
+
const scene = optionString(parsed, "--scene");
|
|
3469
|
+
if (scene === undefined) fail("scene direction check requires --scene.");
|
|
3470
|
+
return { kind: "spatial-scene", action: "direction-check", direction: direction!, scene, json: optionFlag(parsed, "--json") };
|
|
3471
|
+
}
|
|
3472
|
+
if (directionAction === "plan" || directionAction === "gallery") {
|
|
3473
|
+
const parsed = parseOptions(argv.slice(2), { ...JSON_SPEC, "--axis": "value", "--camera": "value", "--output": "value", "--scene": "value" });
|
|
3474
|
+
const [direction] = exactPositionals(parsed, 1, `slopcamera scene direction ${directionAction} <direction.json> --scene <scene.json>${directionAction === "gallery" ? " --axis <axis>" : ""} [--camera <camera-id>] [--output <out.json>] [--json]`);
|
|
3475
|
+
const scene = optionString(parsed, "--scene"), camera = optionString(parsed, "--camera"), output = optionString(parsed, "--output"), axis = optionString(parsed, "--axis");
|
|
3476
|
+
if (scene === undefined) fail(`scene direction ${directionAction} requires --scene.`);
|
|
3477
|
+
if (directionAction === "gallery" && axis === undefined) fail("scene direction gallery requires --axis.");
|
|
3478
|
+
return directionAction === "gallery"
|
|
3479
|
+
? { kind: "spatial-scene", action: "direction-gallery", direction: direction!, scene, axis: axis!, ...(camera === undefined ? {} : { camera }), ...(output === undefined ? {} : { output }), json: optionFlag(parsed, "--json") }
|
|
3480
|
+
: { kind: "spatial-scene", action: "direction-plan", direction: direction!, scene, ...(camera === undefined ? {} : { camera }), ...(output === undefined ? {} : { output }), json: optionFlag(parsed, "--json") };
|
|
3481
|
+
}
|
|
3482
|
+
fail("Usage: slopcamera scene direction <check|plan|gallery> ...");
|
|
3483
|
+
}
|
|
3484
|
+
if (action === "effects") {
|
|
3485
|
+
const effectsAction = argv[1];
|
|
3486
|
+
if (effectsAction === "check") {
|
|
3487
|
+
const parsed = parseOptions(argv.slice(2), { ...JSON_SPEC, "--scene": "value" });
|
|
3488
|
+
const [effects] = exactPositionals(parsed, 1, "slopcamera scene effects check <effects.json> --scene <scene.json> [--json]");
|
|
3489
|
+
const scene = optionString(parsed, "--scene");
|
|
3490
|
+
if (scene === undefined) fail("scene effects check requires --scene.");
|
|
3491
|
+
return { kind: "spatial-scene", action: "effects-check", effects: effects!, scene, json: optionFlag(parsed, "--json") };
|
|
3492
|
+
}
|
|
3493
|
+
if (effectsAction === "plan") {
|
|
3494
|
+
const parsed = parseOptions(argv.slice(2), { ...JSON_SPEC, "--output": "value", "--scene": "value" });
|
|
3495
|
+
const [draft] = exactPositionals(parsed, 1, "slopcamera scene effects plan <draft.json> --scene <scene.json> [--output <effects.json>] [--json]");
|
|
3496
|
+
const scene = optionString(parsed, "--scene"), output = optionString(parsed, "--output");
|
|
3497
|
+
if (scene === undefined) fail("scene effects plan requires --scene.");
|
|
3498
|
+
return { kind: "spatial-scene", action: "effects-plan", draft: draft!, scene, ...(output === undefined ? {} : { output }), json: optionFlag(parsed, "--json") };
|
|
3499
|
+
}
|
|
3500
|
+
if (effectsAction === "bake") {
|
|
3501
|
+
const parsed = parseOptions(argv.slice(2), { ...JSON_SPEC, "--output": "value", "--scene": "value" });
|
|
3502
|
+
const [simulation] = exactPositionals(parsed, 1, "slopcamera scene effects bake <simulation.json> --scene <scene.json> --output <receipt.json> [--json]");
|
|
3503
|
+
const scene = optionString(parsed, "--scene"), output = optionString(parsed, "--output");
|
|
3504
|
+
if (scene === undefined || output === undefined) fail("scene effects bake requires --scene and --output.");
|
|
3505
|
+
return { kind: "spatial-scene", action: "effects-bake", simulation: simulation!, scene, output, json: optionFlag(parsed, "--json") };
|
|
3506
|
+
}
|
|
3507
|
+
fail("Usage: slopcamera scene effects <check|plan|bake> ...");
|
|
3508
|
+
}
|
|
3509
|
+
if (action === "behavior") {
|
|
3510
|
+
const behaviorAction = argv[1];
|
|
3511
|
+
if (behaviorAction === "check") {
|
|
3512
|
+
const parsed = parseOptions(argv.slice(2), { ...JSON_SPEC, "--scene": "value" });
|
|
3513
|
+
const [behavior] = exactPositionals(parsed, 1, "slopcamera scene behavior check <behavior.json> --scene <scene.json> [--json]");
|
|
3514
|
+
const scene = optionString(parsed, "--scene");
|
|
3515
|
+
if (scene === undefined) fail("scene behavior check requires --scene.");
|
|
3516
|
+
return { kind: "spatial-scene", action: "behavior-check", behavior: behavior!, scene, json: optionFlag(parsed, "--json") };
|
|
3517
|
+
}
|
|
3518
|
+
if (behaviorAction === "bake" || behaviorAction === "gallery") {
|
|
3519
|
+
const parsed = parseOptions(argv.slice(2), { ...JSON_SPEC, "--channel-map": "value", "--output": "value", "--scene": "value" });
|
|
3520
|
+
const [behavior] = exactPositionals(parsed, 1, `slopcamera scene behavior ${behaviorAction} <behavior.json> --scene <scene.json> [--channel-map <map.json>]${behaviorAction === "bake" ? " --output <bake.json>" : " [--output <gallery.json>]"} [--json]`);
|
|
3521
|
+
const scene = optionString(parsed, "--scene"), channelMap = optionString(parsed, "--channel-map"), output = optionString(parsed, "--output");
|
|
3522
|
+
if (scene === undefined) fail(`scene behavior ${behaviorAction} requires --scene.`);
|
|
3523
|
+
if (behaviorAction === "bake" && output === undefined) fail("scene behavior bake requires --output.");
|
|
3524
|
+
const shared = { kind: "spatial-scene" as const, behavior: behavior!, scene, ...(channelMap === undefined ? {} : { channelMap }), json: optionFlag(parsed, "--json") };
|
|
3525
|
+
return behaviorAction === "bake"
|
|
3526
|
+
? { ...shared, action: "behavior-bake", output: output! }
|
|
3527
|
+
: { ...shared, action: "behavior-gallery", ...(output === undefined ? {} : { output }) };
|
|
3528
|
+
}
|
|
3529
|
+
if (behaviorAction === "audit") {
|
|
3530
|
+
const parsed = parseOptions(argv.slice(2), { ...JSON_SPEC, "--output": "value" });
|
|
3531
|
+
const [bake] = exactPositionals(parsed, 1, "slopcamera scene behavior audit <bake.json> [--output <audit.json>] [--json]");
|
|
3532
|
+
const output = optionString(parsed, "--output");
|
|
3533
|
+
return { kind: "spatial-scene", action: "behavior-audit", bake: bake!, ...(output === undefined ? {} : { output }), json: optionFlag(parsed, "--json") };
|
|
3534
|
+
}
|
|
3535
|
+
fail("Usage: slopcamera scene behavior <check|bake|gallery|audit> ...");
|
|
3536
|
+
}
|
|
3537
|
+
if (action === "temporal-audit") {
|
|
3538
|
+
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--camera": "value", "--contacts": "value", "--cut-before-us": "value", "--times-us": "value" });
|
|
3539
|
+
const [path] = exactPositionals(parsed, 1, "slopcamera scene temporal-audit <scene.json> --camera <camera-id> [--times-us <csv>] [--contacts <contacts.json>] [--cut-before-us <csv>] [--json]");
|
|
3540
|
+
const camera = optionString(parsed, "--camera"), times = optionString(parsed, "--times-us"), contacts = optionString(parsed, "--contacts"), cutBefore = optionString(parsed, "--cut-before-us");
|
|
3541
|
+
if (camera === undefined) fail("scene temporal-audit requires --camera.");
|
|
3542
|
+
let timesUs: number[] | undefined;
|
|
3543
|
+
if (times !== undefined) {
|
|
3544
|
+
if (!/^\d+(,\d+)*$/u.test(times)) return fail("scene temporal-audit --times-us must be a comma-separated list of nonnegative integers.");
|
|
3545
|
+
timesUs = times.split(",").map(Number);
|
|
3546
|
+
if (timesUs.length > 64) return fail("scene temporal-audit --times-us is bounded to 64 samples.");
|
|
3547
|
+
}
|
|
3548
|
+
let cutBeforeUs: number[] | undefined;
|
|
3549
|
+
if (cutBefore !== undefined) {
|
|
3550
|
+
if (!/^\d+(,\d+)*$/u.test(cutBefore)) return fail("scene temporal-audit --cut-before-us must be a comma-separated list of nonnegative integers.");
|
|
3551
|
+
cutBeforeUs = cutBefore.split(",").map(Number);
|
|
3552
|
+
if (cutBeforeUs.length > 64) return fail("scene temporal-audit --cut-before-us is bounded to 64 values.");
|
|
3553
|
+
}
|
|
3554
|
+
return { kind: "spatial-scene", action, path: path!, camera, ...(timesUs === undefined ? {} : { timesUs }), ...(contacts === undefined ? {} : { contacts }), ...(cutBeforeUs === undefined ? {} : { cutBeforeUs }), json: optionFlag(parsed, "--json") };
|
|
3555
|
+
}
|
|
3556
|
+
fail("Usage: slopcamera scene <init|check|inspect|diff|patch|evaluate|audit|render-audit|solve|review|camera-track|generate|plan|render|direction|effects|behavior|temporal-audit|asset|project|world> ...");
|
|
3138
3557
|
}
|
|
3139
3558
|
|
|
3140
3559
|
export function parseCliArgs(argv: readonly string[]): CliCommand {
|
|
@@ -3162,6 +3581,7 @@ export function parseCliArgs(argv: readonly string[]): CliCommand {
|
|
|
3162
3581
|
case "direct": return parseDirectingArgs(argv.slice(1));
|
|
3163
3582
|
case "scene": return parseSpatialSceneArgs(argv.slice(1));
|
|
3164
3583
|
case "operations": return parseOperations(argv.slice(1));
|
|
3584
|
+
case "capabilities": return parseCapabilities(argv.slice(1));
|
|
3165
3585
|
case "diagram": return parseDiagram(argv.slice(1));
|
|
3166
3586
|
case "image": return parseImage(argv.slice(1));
|
|
3167
3587
|
case "workflows": return parseWorkflows(argv.slice(1));
|
|
@@ -3175,7 +3595,18 @@ export function parseCliArgs(argv: readonly string[]): CliCommand {
|
|
|
3175
3595
|
case "project": return parseProject(argv.slice(1));
|
|
3176
3596
|
case "inspect": return parseInspect(argv.slice(1));
|
|
3177
3597
|
case "events": return parseEvents(argv.slice(1));
|
|
3178
|
-
case "
|
|
3598
|
+
case "menubar": {
|
|
3599
|
+
const parsed = parseOptions(argv.slice(1), { ...JSON_SPEC, "--foreground": "flag", "--background": "flag" });
|
|
3600
|
+
const positionals = parsed.positionals;
|
|
3601
|
+
if (positionals.length > 1 || (positionals[0] !== undefined && !["install", "uninstall", "status"].includes(positionals[0]))) throw new CliError("usage", "Use slopcamera menubar [--foreground|--background] | install|uninstall|status");
|
|
3602
|
+
if (positionals[0] !== undefined && (optionFlag(parsed, "--foreground") || optionFlag(parsed, "--background"))) throw new CliError("usage", "Choose a menu-bar action or a run mode, not both.");
|
|
3603
|
+
return { kind: "menubar", action: (positionals[0] as "install" | "uninstall" | "status" | undefined) ?? "run", mode: optionFlag(parsed, "--background") ? "background" : "foreground", json: optionFlag(parsed, "--json") };
|
|
3604
|
+
}
|
|
3605
|
+
case "outputs": {
|
|
3606
|
+
const parsed = parseOptions(argv.slice(1), JSON_SPEC);
|
|
3607
|
+
exactPositionals(parsed, 0, "slopcamera outputs [--json]");
|
|
3608
|
+
return { kind: "outputs", json: optionFlag(parsed, "--json") };
|
|
3609
|
+
}
|
|
3179
3610
|
case "edit": return parseEdit(argv.slice(1));
|
|
3180
3611
|
case "analyze": return parseAnalyze(argv.slice(1));
|
|
3181
3612
|
case "faces": return parseFaces(argv.slice(1));
|