@vgai/sdk 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/account.ts +5 -4
- package/src/cinematic/capabilities-operations.ts +3 -3
- package/src/cinematic/cue-operations.ts +11 -11
- package/src/cinematic/gsap-operations.ts +5 -5
- package/src/cinematic/index.ts +3 -3
- package/src/cinematic/preview-operations.ts +10 -10
- package/src/cinematic/preview-transport.ts +2 -2
- package/src/cinematic/render-operations.ts +15 -15
- package/src/cinematic/render-transport.ts +2 -2
- package/src/cinematic/theatre-operations.ts +17 -17
- package/src/editor/camera-operations.ts +7 -7
- package/src/editor/console-operations.ts +5 -5
- package/src/editor/hierarchy-operations.ts +5 -5
- package/src/editor/index.ts +3 -3
- package/src/editor/open-operations.ts +10 -10
- package/src/editor/screenshot-operations.ts +5 -5
- package/src/editor/selection-operations.ts +7 -7
- package/src/editor/session-operations.ts +3 -3
- package/src/editor/source-location-operations.ts +6 -6
- package/src/editor/transport.ts +24 -11
- package/src/errors.ts +8 -8
- package/src/generations.ts +12 -1
- package/src/http/http-projection.ts +26 -26
- package/src/index.ts +22 -30
- package/src/mcp/index.ts +2 -2
- package/src/mcp/mcp-projection.ts +25 -25
- package/src/mcp/mcp-server.ts +2 -2
- package/src/operations.ts +5 -5
- package/src/play/control-operations.ts +10 -10
- package/src/play/debug-command-operations.ts +12 -12
- package/src/play/index.ts +3 -3
- package/src/play/input-operations.ts +10 -10
- package/src/play/lifecycle-operations.ts +11 -11
- package/src/play/log-operations.ts +7 -7
- package/src/play/run-ticks-operations.ts +8 -8
- package/src/play/state-operations.ts +9 -9
- package/src/play/status-operations.ts +7 -7
- package/src/play/transport.ts +93 -22
- package/src/project/asset-operations.ts +8 -8
- package/src/project/component-operations.ts +10 -10
- package/src/project/discovery-operations.ts +9 -9
- package/src/project/entity-operations.ts +8 -8
- package/src/project/index.ts +4 -4
- package/src/project/input-map-operations.ts +9 -9
- package/src/project/inspection-operation.ts +3 -3
- package/src/project/manifest-operations.ts +9 -9
- package/src/project/scene-operations.ts +14 -14
- package/src/project/shared.ts +17 -17
- package/src/project-tool-catalog.ts +21 -5
- package/src/registry.ts +39 -39
- package/src/render/render-cinematic.ts +24 -1
- package/src/tools.ts +19 -12
- package/src/types.ts +4 -4
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@vgai/sdk",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.2",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
30
|
-
"@vgai/engine": "0.5.
|
|
30
|
+
"@vgai/engine": "0.5.2",
|
|
31
31
|
"playwright": "^1.58.2",
|
|
32
32
|
"zod": "^4.3.6"
|
|
33
33
|
}
|
package/src/account.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Which account backend this session is signed into: the zero-charge in-process mock control plane, or the live VGAI account service. NOT a generation switch — generation execution routes (mock/managed/byok) are chosen per call; see preferredRoute for the UI default.
|
|
4
|
+
export const AccountBackendSchema = z.enum(['mock', 'live']);
|
|
4
5
|
export const AccountPlanSchema = z.object({
|
|
5
6
|
id: z.string().min(1),
|
|
6
7
|
name: z.string().min(1),
|
|
@@ -47,10 +48,10 @@ export const AccountUsageEntrySchema = z.object({
|
|
|
47
48
|
externalId: z.string().optional(),
|
|
48
49
|
});
|
|
49
50
|
export const AccountSnapshotSchema = z.discriminatedUnion('authenticated', [
|
|
50
|
-
z.object({ authenticated: z.literal(false),
|
|
51
|
+
z.object({ authenticated: z.literal(false), backend: AccountBackendSchema }),
|
|
51
52
|
z.object({
|
|
52
53
|
authenticated: z.literal(true),
|
|
53
|
-
|
|
54
|
+
backend: AccountBackendSchema,
|
|
54
55
|
user: AccountUserSchema,
|
|
55
56
|
plan: AccountPlanSchema,
|
|
56
57
|
credits: AccountCreditsSchema,
|
|
@@ -83,7 +84,7 @@ export const ProviderCredentialTestResultSchema = z.object({
|
|
|
83
84
|
testedAt: z.string().datetime(),
|
|
84
85
|
});
|
|
85
86
|
|
|
86
|
-
export type
|
|
87
|
+
export type AccountBackend = z.infer<typeof AccountBackendSchema>;
|
|
87
88
|
export type AccountPlan = z.infer<typeof AccountPlanSchema>;
|
|
88
89
|
export type AccountCredits = z.infer<typeof AccountCreditsSchema>;
|
|
89
90
|
export type AccountSpendPolicy = z.infer<typeof AccountSpendPolicySchema>;
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
import { z } from 'zod';
|
|
30
|
-
import {
|
|
30
|
+
import { defineTool, type ToolRegistry } from '../registry.js';
|
|
31
31
|
import { checkFfmpegCapability } from '../render/capabilities/ffmpeg.js';
|
|
32
32
|
|
|
33
33
|
const BinaryCapability = z.discriminatedUnion('ok', [
|
|
@@ -76,7 +76,7 @@ const DECLARED_FORMATS = [
|
|
|
76
76
|
{ format: 'png' as const, alpha: true },
|
|
77
77
|
];
|
|
78
78
|
|
|
79
|
-
export const cinematicCapabilities =
|
|
79
|
+
export const cinematicCapabilities = defineTool({
|
|
80
80
|
name: 'cinematic.capabilities',
|
|
81
81
|
summary:
|
|
82
82
|
'Report ffmpeg/ffprobe capture capability and the render pipeline’s declared codec/format support.',
|
|
@@ -123,6 +123,6 @@ export const cinematicCapabilities = defineOperation({
|
|
|
123
123
|
});
|
|
124
124
|
|
|
125
125
|
/** Register cinematic.capabilities onto `registry`. */
|
|
126
|
-
export function registerCapabilitiesOperations(registry:
|
|
126
|
+
export function registerCapabilitiesOperations(registry: ToolRegistry): void {
|
|
127
127
|
registry.register(cinematicCapabilities);
|
|
128
128
|
}
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
import { existsSync, readFileSync } from 'node:fs';
|
|
25
25
|
import { isAbsolute, join } from 'node:path';
|
|
26
26
|
import { z } from 'zod';
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import type {
|
|
27
|
+
import { ToolError } from '../errors.js';
|
|
28
|
+
import { defineTool, type ToolErrorDefinition, type ToolRegistry } from '../registry.js';
|
|
29
|
+
import type { ToolContext } from '../types.js';
|
|
30
30
|
import { NO_ROOT_ERROR } from './theatre-operations.js';
|
|
31
31
|
|
|
32
|
-
export const CUE_FILE_NOT_FOUND_ERROR:
|
|
32
|
+
export const CUE_FILE_NOT_FOUND_ERROR: ToolErrorDefinition = {
|
|
33
33
|
code: 'FILE_NOT_FOUND',
|
|
34
34
|
summary: 'The referenced cue module file does not exist on disk.',
|
|
35
35
|
data: z.object({ path: z.string() }),
|
|
@@ -71,22 +71,22 @@ const CueSourceInput = z.object({
|
|
|
71
71
|
.describe('Base directory for a relative `path`. Defaults to ctx.projectRoot.'),
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
function resolveSource(input: z.infer<typeof CueSourceInput>, ctx:
|
|
74
|
+
function resolveSource(input: z.infer<typeof CueSourceInput>, ctx: ToolContext): string {
|
|
75
75
|
if (input.source !== undefined) return input.source;
|
|
76
76
|
if (input.path === undefined) {
|
|
77
|
-
throw new
|
|
77
|
+
throw new ToolError('BAD_INPUT', 'One of `source` or `path` must be given.', {});
|
|
78
78
|
}
|
|
79
79
|
const root = input.root ?? ctx.projectRoot;
|
|
80
80
|
const absPath = isAbsolute(input.path) ? input.path : root ? join(root, input.path) : undefined;
|
|
81
81
|
if (!absPath) {
|
|
82
|
-
throw new
|
|
82
|
+
throw new ToolError(
|
|
83
83
|
'NO_ROOT',
|
|
84
84
|
'A relative `path` was given but neither `root` nor ctx.projectRoot is available.',
|
|
85
85
|
{},
|
|
86
86
|
);
|
|
87
87
|
}
|
|
88
88
|
if (!existsSync(absPath)) {
|
|
89
|
-
throw new
|
|
89
|
+
throw new ToolError('FILE_NOT_FOUND', `File not found: ${absPath}`, { path: absPath });
|
|
90
90
|
}
|
|
91
91
|
return readFileSync(absPath, 'utf-8');
|
|
92
92
|
}
|
|
@@ -103,7 +103,7 @@ const CinematicCueListResult = z.object({
|
|
|
103
103
|
.describe('Every {id, time} pair found, in source declaration order.'),
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
export const cinematicCueList =
|
|
106
|
+
export const cinematicCueList = defineTool({
|
|
107
107
|
name: 'cinematic.cue.list',
|
|
108
108
|
summary: 'Best-effort static list of {id, time} cue entries in a TypeScript cue module.',
|
|
109
109
|
description:
|
|
@@ -137,7 +137,7 @@ const CinematicCueValidateResult = z.object({
|
|
|
137
137
|
cues: z.array(HeuristicCueSchema),
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
-
export const cinematicCueValidate =
|
|
140
|
+
export const cinematicCueValidate = defineTool({
|
|
141
141
|
name: 'cinematic.cue.validate',
|
|
142
142
|
summary:
|
|
143
143
|
'Validate unique cue IDs and ascending time ordering for the cues found by cinematic.cue.list’s ' +
|
|
@@ -192,7 +192,7 @@ export const cinematicCueValidate = defineOperation({
|
|
|
192
192
|
});
|
|
193
193
|
|
|
194
194
|
/** Register cinematic.cue.list / cinematic.cue.validate onto `registry`. */
|
|
195
|
-
export function registerCueOperations(registry:
|
|
195
|
+
export function registerCueOperations(registry: ToolRegistry): void {
|
|
196
196
|
registry.register(cinematicCueList);
|
|
197
197
|
registry.register(cinematicCueValidate);
|
|
198
198
|
}
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
import { readFileSync, statSync } from 'node:fs';
|
|
26
26
|
import { join } from 'node:path';
|
|
27
27
|
import { z } from 'zod';
|
|
28
|
-
import {
|
|
28
|
+
import { ToolError } from '../errors.js';
|
|
29
29
|
import { listProjectFiles } from '../project/shared.js';
|
|
30
|
-
import {
|
|
30
|
+
import { defineTool, type ToolRegistry } from '../registry.js';
|
|
31
31
|
import { NO_ROOT_ERROR } from './theatre-operations.js';
|
|
32
32
|
|
|
33
33
|
/** True when the file text looks like it registers a GSAP timeline for deterministic scrub/export. */
|
|
@@ -70,7 +70,7 @@ const CinematicGsapInspectResult = z.object({
|
|
|
70
70
|
.describe('Every *.ts/*.tsx file whose text imports gsap-registration, sorted by path.'),
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
export const cinematicGsapInspect =
|
|
73
|
+
export const cinematicGsapInspect = defineTool({
|
|
74
74
|
name: 'cinematic.gsap.inspect',
|
|
75
75
|
summary:
|
|
76
76
|
'Best-effort static discovery of source files that call registerGsap(...) to register a ' +
|
|
@@ -90,7 +90,7 @@ export const cinematicGsapInspect = defineOperation({
|
|
|
90
90
|
async impl(input, ctx) {
|
|
91
91
|
const root = input.root ?? ctx.projectRoot;
|
|
92
92
|
if (!root) {
|
|
93
|
-
throw new
|
|
93
|
+
throw new ToolError(
|
|
94
94
|
'NO_ROOT',
|
|
95
95
|
'Neither an explicit `root` input nor ctx.projectRoot was given.',
|
|
96
96
|
{},
|
|
@@ -121,6 +121,6 @@ export const cinematicGsapInspect = defineOperation({
|
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
/** Register cinematic.gsap.inspect onto `registry`. */
|
|
124
|
-
export function registerGsapOperations(registry:
|
|
124
|
+
export function registerGsapOperations(registry: ToolRegistry): void {
|
|
125
125
|
registry.register(cinematicGsapInspect);
|
|
126
126
|
}
|
package/src/cinematic/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* B5 — `cinematic.*` operations
|
|
3
3
|
* (docs/AI-NATIVE-AUTHORING-IMPLEMENTATION-SPEC.md §8 B5). Registered
|
|
4
|
-
* separately from B1's `
|
|
4
|
+
* separately from B1's `registerBuiltinTools` (`../operations.ts`),
|
|
5
5
|
* B2's `registerProjectOperations` (`../project/index.ts`), B3's
|
|
6
6
|
* `registerEditorOperations` (`../editor/index.ts`), and B4's
|
|
7
7
|
* `registerPlayOperations` (`../play/index.ts`) — the default `operations`
|
|
@@ -40,7 +40,7 @@ export {
|
|
|
40
40
|
} from './render-transport.js';
|
|
41
41
|
export * from './theatre-operations.js';
|
|
42
42
|
|
|
43
|
-
import type {
|
|
43
|
+
import type { ToolRegistry } from '../registry.js';
|
|
44
44
|
import { registerCapabilitiesOperations } from './capabilities-operations.js';
|
|
45
45
|
import { registerCueOperations } from './cue-operations.js';
|
|
46
46
|
import { registerGsapOperations } from './gsap-operations.js';
|
|
@@ -49,7 +49,7 @@ import { registerRenderOperations } from './render-operations.js';
|
|
|
49
49
|
import { registerTheatreOperations } from './theatre-operations.js';
|
|
50
50
|
|
|
51
51
|
/** Register every B5 `cinematic.*` operation onto `registry`. */
|
|
52
|
-
export function registerCinematicOperations(registry:
|
|
52
|
+
export function registerCinematicOperations(registry: ToolRegistry): void {
|
|
53
53
|
registerTheatreOperations(registry);
|
|
54
54
|
registerCueOperations(registry);
|
|
55
55
|
registerGsapOperations(registry);
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
import { tmpdir } from 'node:os';
|
|
15
15
|
import { join } from 'node:path';
|
|
16
16
|
import { z } from 'zod';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { ToolError } from '../errors.js';
|
|
18
|
+
import { defineTool, type ToolErrorDefinition, type ToolRegistry } from '../registry.js';
|
|
19
19
|
import {
|
|
20
20
|
createPreviewSession,
|
|
21
21
|
getCinematicOpenPreviewFn,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
stopPreviewSession,
|
|
26
26
|
} from './preview-transport.js';
|
|
27
27
|
|
|
28
|
-
const PREVIEW_SESSION_NOT_FOUND_ERROR:
|
|
28
|
+
const PREVIEW_SESSION_NOT_FOUND_ERROR: ToolErrorDefinition = {
|
|
29
29
|
code: 'PREVIEW_SESSION_NOT_FOUND',
|
|
30
30
|
summary:
|
|
31
31
|
'No open preview session with the given previewId exists (this process’s in-memory registry).',
|
|
@@ -67,7 +67,7 @@ function stateOf(session: {
|
|
|
67
67
|
function requireSession(previewId: string) {
|
|
68
68
|
const session = getPreviewSession(previewId);
|
|
69
69
|
if (!session) {
|
|
70
|
-
throw new
|
|
70
|
+
throw new ToolError(
|
|
71
71
|
'PREVIEW_SESSION_NOT_FOUND',
|
|
72
72
|
`No open preview session "${previewId}".`,
|
|
73
73
|
{ previewId },
|
|
@@ -113,7 +113,7 @@ const CinematicPreviewInput = z.object({
|
|
|
113
113
|
playbackRate: z.number().positive().optional().describe('Autoplay speed multiplier. Default 1.'),
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
export const cinematicPreview =
|
|
116
|
+
export const cinematicPreview = defineTool({
|
|
117
117
|
name: 'cinematic.preview',
|
|
118
118
|
summary:
|
|
119
119
|
'Open a headless preview session against a render-control-mode page and (by default) start playing it.',
|
|
@@ -180,7 +180,7 @@ export const cinematicPreview = defineOperation({
|
|
|
180
180
|
|
|
181
181
|
const PreviewIdInput = z.object({ previewId: z.string() });
|
|
182
182
|
|
|
183
|
-
export const cinematicPause =
|
|
183
|
+
export const cinematicPause = defineTool({
|
|
184
184
|
name: 'cinematic.pause',
|
|
185
185
|
summary: 'Stop a preview session’s autoplay loop without closing it.',
|
|
186
186
|
description:
|
|
@@ -214,7 +214,7 @@ const CinematicSeekInput = z.object({
|
|
|
214
214
|
frame: z.number().int().min(0).optional().describe('Seek to this exact frame index.'),
|
|
215
215
|
});
|
|
216
216
|
|
|
217
|
-
export const cinematicSeek =
|
|
217
|
+
export const cinematicSeek = defineTool({
|
|
218
218
|
name: 'cinematic.seek',
|
|
219
219
|
summary: 'Seek a preview session to an exact time or frame.',
|
|
220
220
|
description:
|
|
@@ -238,7 +238,7 @@ export const cinematicSeek = defineOperation({
|
|
|
238
238
|
async impl(input) {
|
|
239
239
|
const session = requireSession(input.previewId);
|
|
240
240
|
if ((input.time === undefined) === (input.frame === undefined)) {
|
|
241
|
-
throw new
|
|
241
|
+
throw new ToolError('BAD_INPUT', 'Supply exactly one of time or frame.', {});
|
|
242
242
|
}
|
|
243
243
|
const frame = input.frame ?? Math.round(input.time! * session.handle.fps);
|
|
244
244
|
await seekPreviewSession(session, frame);
|
|
@@ -250,7 +250,7 @@ export const cinematicSeek = defineOperation({
|
|
|
250
250
|
// cinematic.stop
|
|
251
251
|
// ---------------------------------------------------------------------------
|
|
252
252
|
|
|
253
|
-
export const cinematicStop =
|
|
253
|
+
export const cinematicStop = defineTool({
|
|
254
254
|
name: 'cinematic.stop',
|
|
255
255
|
summary: 'Close a preview session (its page/browser) and forget it.',
|
|
256
256
|
description:
|
|
@@ -271,7 +271,7 @@ export const cinematicStop = defineOperation({
|
|
|
271
271
|
});
|
|
272
272
|
|
|
273
273
|
/** Register cinematic.preview / .pause / .seek / .stop onto `registry`. */
|
|
274
|
-
export function registerPreviewOperations(registry:
|
|
274
|
+
export function registerPreviewOperations(registry: ToolRegistry): void {
|
|
275
275
|
registry.register(cinematicPreview);
|
|
276
276
|
registry.register(cinematicPause);
|
|
277
277
|
registry.register(cinematicSeek);
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
import type { RenderCinematicRequest } from '../render/render-cinematic.js';
|
|
44
44
|
import { openRenderCinematicPage } from '../render/render-cinematic.js';
|
|
45
|
-
import type {
|
|
45
|
+
import type { ToolContext } from '../types.js';
|
|
46
46
|
|
|
47
47
|
/** The narrow surface preview sessions need — deliberately NOT Playwright's `Page` (see module jsdoc). */
|
|
48
48
|
export interface CinematicPreviewHandle {
|
|
@@ -96,7 +96,7 @@ async function openRealPreviewHandle(
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/** Resolve the preview-page opener to use: `ctx['cinematicOpenPreviewFn']` when injected (tests), else the real launcher. */
|
|
99
|
-
export function getCinematicOpenPreviewFn(ctx:
|
|
99
|
+
export function getCinematicOpenPreviewFn(ctx: ToolContext): CinematicOpenPreviewFn {
|
|
100
100
|
const injected = ctx['cinematicOpenPreviewFn'];
|
|
101
101
|
return (injected as CinematicOpenPreviewFn | undefined) ?? openRealPreviewHandle;
|
|
102
102
|
}
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { z } from 'zod';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
19
|
+
import { ToolError } from '../errors.js';
|
|
20
|
+
import { defineTool, type ToolErrorDefinition, type ToolRegistry } from '../registry.js';
|
|
21
21
|
import {
|
|
22
22
|
RenderCinematicFfmpegError,
|
|
23
23
|
type RenderCinematicRequest,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
// Shared error definitions
|
|
35
35
|
// ---------------------------------------------------------------------------
|
|
36
36
|
|
|
37
|
-
const RENDER_REQUEST_INVALID_ERROR:
|
|
37
|
+
const RENDER_REQUEST_INVALID_ERROR: ToolErrorDefinition = {
|
|
38
38
|
code: 'RENDER_REQUEST_INVALID',
|
|
39
39
|
summary:
|
|
40
40
|
'The render request failed validation (I1 AC: "fail before launching the browser") — no job ' +
|
|
@@ -42,19 +42,19 @@ const RENDER_REQUEST_INVALID_ERROR: ErrorDefinition = {
|
|
|
42
42
|
data: z.object({ message: z.string() }),
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
const RENDER_CAPABILITY_UNAVAILABLE_ERROR:
|
|
45
|
+
const RENDER_CAPABILITY_UNAVAILABLE_ERROR: ToolErrorDefinition = {
|
|
46
46
|
code: 'RENDER_CAPABILITY_UNAVAILABLE',
|
|
47
47
|
summary: 'ffmpeg/ffprobe preflight failed (A1) — no job was created.',
|
|
48
48
|
data: z.object({ message: z.string() }),
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
const RENDER_JOB_NOT_FOUND_ERROR:
|
|
51
|
+
const RENDER_JOB_NOT_FOUND_ERROR: ToolErrorDefinition = {
|
|
52
52
|
code: 'RENDER_JOB_NOT_FOUND',
|
|
53
53
|
summary: 'No render job with the given jobId exists (this process’s in-memory job registry).',
|
|
54
54
|
data: z.object({ jobId: z.string() }),
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
const RENDER_JOB_NOT_RUNNING_ERROR:
|
|
57
|
+
const RENDER_JOB_NOT_RUNNING_ERROR: ToolErrorDefinition = {
|
|
58
58
|
code: 'RENDER_JOB_NOT_RUNNING',
|
|
59
59
|
summary: 'The job has already settled (completed/failed/cancelled) — nothing to cancel.',
|
|
60
60
|
data: z.object({ jobId: z.string(), status: z.string() }),
|
|
@@ -255,7 +255,7 @@ function toRenderCinematicRequest(
|
|
|
255
255
|
// cinematic.render
|
|
256
256
|
// ---------------------------------------------------------------------------
|
|
257
257
|
|
|
258
|
-
export const cinematicRender =
|
|
258
|
+
export const cinematicRender = defineTool({
|
|
259
259
|
name: 'cinematic.render',
|
|
260
260
|
summary: 'Render a Theatre-driven cinematic sequence to a video or frame-sequence artifact.',
|
|
261
261
|
description:
|
|
@@ -285,10 +285,10 @@ export const cinematicRender = defineOperation({
|
|
|
285
285
|
job = startRenderJob(toRenderCinematicRequest(input), renderFn);
|
|
286
286
|
} catch (err) {
|
|
287
287
|
if (err instanceof RenderCinematicRequestError) {
|
|
288
|
-
throw new
|
|
288
|
+
throw new ToolError('RENDER_REQUEST_INVALID', err.message, { message: err.message });
|
|
289
289
|
}
|
|
290
290
|
if (err instanceof RenderCinematicFfmpegError) {
|
|
291
|
-
throw new
|
|
291
|
+
throw new ToolError('RENDER_CAPABILITY_UNAVAILABLE', err.message, {
|
|
292
292
|
message: err.message,
|
|
293
293
|
});
|
|
294
294
|
}
|
|
@@ -309,7 +309,7 @@ export const cinematicRender = defineOperation({
|
|
|
309
309
|
|
|
310
310
|
const CinematicRenderStatusInput = z.object({ jobId: z.string() });
|
|
311
311
|
|
|
312
|
-
export const cinematicRenderStatus =
|
|
312
|
+
export const cinematicRenderStatus = defineTool({
|
|
313
313
|
name: 'cinematic.render.status',
|
|
314
314
|
summary: 'Poll a cinematic.render job’s progress, current frame, elapsed time, and output paths.',
|
|
315
315
|
description:
|
|
@@ -327,7 +327,7 @@ export const cinematicRenderStatus = defineOperation({
|
|
|
327
327
|
async impl(input) {
|
|
328
328
|
const job = getRenderJob(input.jobId);
|
|
329
329
|
if (!job) {
|
|
330
|
-
throw new
|
|
330
|
+
throw new ToolError('RENDER_JOB_NOT_FOUND', `No render job "${input.jobId}".`, {
|
|
331
331
|
jobId: input.jobId,
|
|
332
332
|
});
|
|
333
333
|
}
|
|
@@ -339,7 +339,7 @@ export const cinematicRenderStatus = defineOperation({
|
|
|
339
339
|
// cinematic.render.cancel
|
|
340
340
|
// ---------------------------------------------------------------------------
|
|
341
341
|
|
|
342
|
-
export const cinematicRenderCancel =
|
|
342
|
+
export const cinematicRenderCancel = defineTool({
|
|
343
343
|
name: 'cinematic.render.cancel',
|
|
344
344
|
summary: 'Cancel a running cinematic.render job.',
|
|
345
345
|
description:
|
|
@@ -361,12 +361,12 @@ export const cinematicRenderCancel = defineOperation({
|
|
|
361
361
|
async impl(input) {
|
|
362
362
|
const job = getRenderJob(input.jobId);
|
|
363
363
|
if (!job) {
|
|
364
|
-
throw new
|
|
364
|
+
throw new ToolError('RENDER_JOB_NOT_FOUND', `No render job "${input.jobId}".`, {
|
|
365
365
|
jobId: input.jobId,
|
|
366
366
|
});
|
|
367
367
|
}
|
|
368
368
|
if (job.status !== 'running') {
|
|
369
|
-
throw new
|
|
369
|
+
throw new ToolError(
|
|
370
370
|
'RENDER_JOB_NOT_RUNNING',
|
|
371
371
|
`Render job "${input.jobId}" has already settled (${job.status}) — nothing to cancel.`,
|
|
372
372
|
{ jobId: input.jobId, status: job.status },
|
|
@@ -402,7 +402,7 @@ export const cinematicRenderCancel = defineOperation({
|
|
|
402
402
|
});
|
|
403
403
|
|
|
404
404
|
/** Register cinematic.render / .status / .cancel onto `registry`. */
|
|
405
|
-
export function registerRenderOperations(registry:
|
|
405
|
+
export function registerRenderOperations(registry: ToolRegistry): void {
|
|
406
406
|
registry.register(cinematicRender);
|
|
407
407
|
registry.register(cinematicRenderStatus);
|
|
408
408
|
registry.register(cinematicRenderCancel);
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
renderCinematic,
|
|
53
53
|
resolveRenderCinematicRequest,
|
|
54
54
|
} from '../render/render-cinematic.js';
|
|
55
|
-
import type {
|
|
55
|
+
import type { ToolContext } from '../types.js';
|
|
56
56
|
|
|
57
57
|
export type CinematicRenderFn = (
|
|
58
58
|
request: RenderCinematicRequest,
|
|
@@ -60,7 +60,7 @@ export type CinematicRenderFn = (
|
|
|
60
60
|
) => Promise<RenderCinematicResult>;
|
|
61
61
|
|
|
62
62
|
/** Resolve the render function to use: `ctx['cinematicRenderFn']` when injected (tests), else the real pipeline. */
|
|
63
|
-
export function getCinematicRenderFn(ctx:
|
|
63
|
+
export function getCinematicRenderFn(ctx: ToolContext): CinematicRenderFn {
|
|
64
64
|
const injected = ctx['cinematicRenderFn'];
|
|
65
65
|
return (injected as CinematicRenderFn | undefined) ?? renderCinematic;
|
|
66
66
|
}
|
|
@@ -30,28 +30,28 @@
|
|
|
30
30
|
import { existsSync, readFileSync } from 'node:fs';
|
|
31
31
|
import { isAbsolute, join } from 'node:path';
|
|
32
32
|
import { z } from 'zod';
|
|
33
|
-
import {
|
|
33
|
+
import { ToolError } from '../errors.js';
|
|
34
34
|
import { listProjectFiles } from '../project/shared.js';
|
|
35
|
-
import {
|
|
36
|
-
import type {
|
|
35
|
+
import { defineTool, type ToolErrorDefinition, type ToolRegistry } from '../registry.js';
|
|
36
|
+
import type { ToolContext } from '../types.js';
|
|
37
37
|
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
39
|
// Shared path resolution: `root` (explicit) > `ctx.projectRoot` (fallback).
|
|
40
40
|
// ---------------------------------------------------------------------------
|
|
41
41
|
|
|
42
|
-
export const NO_ROOT_ERROR:
|
|
42
|
+
export const NO_ROOT_ERROR: ToolErrorDefinition = {
|
|
43
43
|
code: 'NO_ROOT',
|
|
44
44
|
summary: 'Neither an explicit `root` input nor ctx.projectRoot was given.',
|
|
45
45
|
data: z.object({}).describe('No additional data.'),
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
export const THEATRE_FILE_NOT_FOUND_ERROR:
|
|
48
|
+
export const THEATRE_FILE_NOT_FOUND_ERROR: ToolErrorDefinition = {
|
|
49
49
|
code: 'FILE_NOT_FOUND',
|
|
50
50
|
summary: 'The referenced Theatre project-state file does not exist on disk.',
|
|
51
51
|
data: z.object({ path: z.string() }),
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
export const INVALID_THEATRE_PROJECT_ERROR:
|
|
54
|
+
export const INVALID_THEATRE_PROJECT_ERROR: ToolErrorDefinition = {
|
|
55
55
|
code: 'INVALID_THEATRE_PROJECT',
|
|
56
56
|
summary:
|
|
57
57
|
"The file's outer shape (sheetsById/definitionVersion/revisionHistory, and — for " +
|
|
@@ -59,17 +59,17 @@ export const INVALID_THEATRE_PROJECT_ERROR: ErrorDefinition = {
|
|
|
59
59
|
data: z.object({ issues: z.array(z.string()) }),
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
export const SHEET_NOT_FOUND_ERROR:
|
|
62
|
+
export const SHEET_NOT_FOUND_ERROR: ToolErrorDefinition = {
|
|
63
63
|
code: 'SHEET_NOT_FOUND',
|
|
64
64
|
summary: 'No sheet with the given name exists in this Theatre project’s sheetsById.',
|
|
65
65
|
data: z.object({ sheetName: z.string(), availableSheets: z.array(z.string()) }),
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
/** Resolve `root` input against `ctx.projectRoot`, requiring at least one. */
|
|
69
|
-
function resolveRoot(root: string | undefined, ctx:
|
|
69
|
+
function resolveRoot(root: string | undefined, ctx: ToolContext): string {
|
|
70
70
|
const resolved = root ?? ctx.projectRoot;
|
|
71
71
|
if (!resolved) {
|
|
72
|
-
throw new
|
|
72
|
+
throw new ToolError(
|
|
73
73
|
'NO_ROOT',
|
|
74
74
|
'Neither an explicit `root` input nor ctx.projectRoot was given.',
|
|
75
75
|
{},
|
|
@@ -85,7 +85,7 @@ function resolveTheatreProjectPath(root: string, theatreProjectPath: string): st
|
|
|
85
85
|
|
|
86
86
|
function readTheatreProjectFile(absPath: string): unknown {
|
|
87
87
|
if (!existsSync(absPath)) {
|
|
88
|
-
throw new
|
|
88
|
+
throw new ToolError('FILE_NOT_FOUND', `File not found: ${absPath}`, { path: absPath });
|
|
89
89
|
}
|
|
90
90
|
return JSON.parse(readFileSync(absPath, 'utf-8'));
|
|
91
91
|
}
|
|
@@ -135,7 +135,7 @@ const CinematicProjectListResult = z.object({
|
|
|
135
135
|
),
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
export const cinematicProjectList =
|
|
138
|
+
export const cinematicProjectList = defineTool({
|
|
139
139
|
name: 'cinematic.project.list',
|
|
140
140
|
summary: 'List every committed Theatre project-state file under a directory.',
|
|
141
141
|
description:
|
|
@@ -176,7 +176,7 @@ const CinematicSheetListResult = z.object({
|
|
|
176
176
|
sheets: z.array(z.string()).describe('Every sheet name in sheetsById, sorted.'),
|
|
177
177
|
});
|
|
178
178
|
|
|
179
|
-
export const cinematicSheetList =
|
|
179
|
+
export const cinematicSheetList = defineTool({
|
|
180
180
|
name: 'cinematic.sheet.list',
|
|
181
181
|
summary: 'List every sheet name in a committed Theatre project-state file.',
|
|
182
182
|
description: 'Static, file-native (§8 B5 "list Theatre ... sheets"). Outer-shape validated only.',
|
|
@@ -194,7 +194,7 @@ export const cinematicSheetList = defineOperation({
|
|
|
194
194
|
const json = readTheatreProjectFile(absPath);
|
|
195
195
|
const parsed = TheatreProjectOuterShape.safeParse(json);
|
|
196
196
|
if (!parsed.success) {
|
|
197
|
-
throw new
|
|
197
|
+
throw new ToolError(
|
|
198
198
|
'INVALID_THEATRE_PROJECT',
|
|
199
199
|
`${absPath} does not match Theatre project-state's outer shape.`,
|
|
200
200
|
{ issues: parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`) },
|
|
@@ -236,7 +236,7 @@ const CinematicSequenceInspectResult = z.object({
|
|
|
236
236
|
),
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
-
export const cinematicSequenceInspect =
|
|
239
|
+
export const cinematicSequenceInspect = defineTool({
|
|
240
240
|
name: 'cinematic.sequence.inspect',
|
|
241
241
|
summary:
|
|
242
242
|
'Inspect one sheet’s sequence length and tracked objects in a Theatre project-state file.',
|
|
@@ -263,7 +263,7 @@ export const cinematicSequenceInspect = defineOperation({
|
|
|
263
263
|
const json = readTheatreProjectFile(absPath);
|
|
264
264
|
const parsed = TheatreProjectDeepShape.safeParse(json);
|
|
265
265
|
if (!parsed.success) {
|
|
266
|
-
throw new
|
|
266
|
+
throw new ToolError(
|
|
267
267
|
'INVALID_THEATRE_PROJECT',
|
|
268
268
|
`${absPath} does not match Theatre project-state's shape (incl. per-sheet sequence).`,
|
|
269
269
|
{ issues: parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`) },
|
|
@@ -271,7 +271,7 @@ export const cinematicSequenceInspect = defineOperation({
|
|
|
271
271
|
}
|
|
272
272
|
const sheet = parsed.data.sheetsById[input.sheetName];
|
|
273
273
|
if (!sheet) {
|
|
274
|
-
throw new
|
|
274
|
+
throw new ToolError(
|
|
275
275
|
'SHEET_NOT_FOUND',
|
|
276
276
|
`No sheet named "${input.sheetName}" in ${absPath}.`,
|
|
277
277
|
{ sheetName: input.sheetName, availableSheets: Object.keys(parsed.data.sheetsById).sort() },
|
|
@@ -299,7 +299,7 @@ export const cinematicSequenceInspect = defineOperation({
|
|
|
299
299
|
});
|
|
300
300
|
|
|
301
301
|
/** Register cinematic.project.list / cinematic.sheet.list / cinematic.sequence.inspect onto `registry`. */
|
|
302
|
-
export function registerTheatreOperations(registry:
|
|
302
|
+
export function registerTheatreOperations(registry: ToolRegistry): void {
|
|
303
303
|
registry.register(cinematicProjectList);
|
|
304
304
|
registry.register(cinematicSheetList);
|
|
305
305
|
registry.register(cinematicSequenceInspect);
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { z } from 'zod';
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
25
|
+
import { ToolError } from '../errors.js';
|
|
26
|
+
import { defineTool, type ToolRegistry } from '../registry.js';
|
|
27
27
|
import {
|
|
28
28
|
EDITOR_COMMAND_TIMEOUT_MS,
|
|
29
29
|
EDITOR_NOT_RUNNING_ERROR,
|
|
@@ -65,7 +65,7 @@ const EditorViewportCameraGetInput = z
|
|
|
65
65
|
|
|
66
66
|
const EditorViewportCameraGetResult = CameraPoseSchema.describe('Live viewport camera pose.');
|
|
67
67
|
|
|
68
|
-
export const editorViewportCameraGet =
|
|
68
|
+
export const editorViewportCameraGet = defineTool({
|
|
69
69
|
name: 'editor.viewport.camera.get',
|
|
70
70
|
summary: 'Read the viewport camera pose (position/target/fov) from a connected editor session.',
|
|
71
71
|
description:
|
|
@@ -89,7 +89,7 @@ export const editorViewportCameraGet = defineOperation({
|
|
|
89
89
|
'editor.viewport.camera.get',
|
|
90
90
|
).catch(() => undefined);
|
|
91
91
|
if (!camera) {
|
|
92
|
-
throw new
|
|
92
|
+
throw new ToolError(
|
|
93
93
|
'CAMERA_STATE_UNAVAILABLE',
|
|
94
94
|
'No browser tab has bound/reported a viewport camera pose yet.',
|
|
95
95
|
{},
|
|
@@ -124,7 +124,7 @@ const EditorViewportCameraSetInput = z
|
|
|
124
124
|
|
|
125
125
|
const EditorViewportCameraSetResult = z.object({ ok: z.literal(true) });
|
|
126
126
|
|
|
127
|
-
export const editorViewportCameraSet =
|
|
127
|
+
export const editorViewportCameraSet = defineTool({
|
|
128
128
|
name: 'editor.viewport.camera.set',
|
|
129
129
|
summary:
|
|
130
130
|
'Move the viewport camera via focus-entity, focus-selection, a view preset, or an arbitrary pose.',
|
|
@@ -155,7 +155,7 @@ export const editorViewportCameraSet = defineOperation({
|
|
|
155
155
|
error: err instanceof Error ? err.message : String(err),
|
|
156
156
|
}));
|
|
157
157
|
if (!result.ok) {
|
|
158
|
-
throw new
|
|
158
|
+
throw new ToolError('COMMAND_FAILED', result.error ?? 'camera command failed', {
|
|
159
159
|
message: result.error ?? 'camera command failed',
|
|
160
160
|
});
|
|
161
161
|
}
|
|
@@ -163,7 +163,7 @@ export const editorViewportCameraSet = defineOperation({
|
|
|
163
163
|
},
|
|
164
164
|
});
|
|
165
165
|
|
|
166
|
-
export function registerCameraOperations(registry:
|
|
166
|
+
export function registerCameraOperations(registry: ToolRegistry): void {
|
|
167
167
|
registry.register(editorViewportCameraGet);
|
|
168
168
|
registry.register(editorViewportCameraSet);
|
|
169
169
|
}
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { z } from 'zod';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import { ToolError } from '../errors.js';
|
|
19
|
+
import { defineTool, type ToolRegistry } from '../registry.js';
|
|
20
20
|
import {
|
|
21
21
|
EDITOR_NOT_RUNNING_ERROR,
|
|
22
22
|
EDITOR_READ_TIMEOUT_MS,
|
|
@@ -47,7 +47,7 @@ const EditorConsoleSubscribeResult = z.object({
|
|
|
47
47
|
.describe('Number of play-mode log entries currently buffered/persisted.'),
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
export const editorConsoleSubscribe =
|
|
50
|
+
export const editorConsoleSubscribe = defineTool({
|
|
51
51
|
name: 'editor.console.subscribe',
|
|
52
52
|
summary: "Describe how to subscribe to a connected editor session's events and logs.",
|
|
53
53
|
description:
|
|
@@ -72,7 +72,7 @@ export const editorConsoleSubscribe = defineOperation({
|
|
|
72
72
|
'editor.console.subscribe',
|
|
73
73
|
).catch(() => undefined);
|
|
74
74
|
if (!metadata) {
|
|
75
|
-
throw new
|
|
75
|
+
throw new ToolError(
|
|
76
76
|
'CONSOLE_METADATA_UNAVAILABLE',
|
|
77
77
|
'The connected editor session did not answer the log-entries read in time.',
|
|
78
78
|
{},
|
|
@@ -82,6 +82,6 @@ export const editorConsoleSubscribe = defineOperation({
|
|
|
82
82
|
},
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
-
export function registerConsoleOperations(registry:
|
|
85
|
+
export function registerConsoleOperations(registry: ToolRegistry): void {
|
|
86
86
|
registry.register(editorConsoleSubscribe);
|
|
87
87
|
}
|