@bitmagic/asset-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/forger-client.d.ts +42 -0
  2. package/dist/forger-client.d.ts.map +1 -0
  3. package/dist/forger-client.js +135 -0
  4. package/dist/forger-client.js.map +1 -0
  5. package/dist/generators/animation.d.ts +102 -0
  6. package/dist/generators/animation.d.ts.map +1 -0
  7. package/dist/generators/animation.js +234 -0
  8. package/dist/generators/animation.js.map +1 -0
  9. package/dist/generators/character.d.ts +40 -0
  10. package/dist/generators/character.d.ts.map +1 -0
  11. package/dist/generators/character.js +134 -0
  12. package/dist/generators/character.js.map +1 -0
  13. package/dist/generators/image.d.ts +43 -0
  14. package/dist/generators/image.d.ts.map +1 -0
  15. package/dist/generators/image.js +74 -0
  16. package/dist/generators/image.js.map +1 -0
  17. package/dist/generators/skybox.d.ts +18 -0
  18. package/dist/generators/skybox.d.ts.map +1 -0
  19. package/dist/generators/skybox.js +57 -0
  20. package/dist/generators/skybox.js.map +1 -0
  21. package/dist/generators/sound-effect.d.ts +31 -0
  22. package/dist/generators/sound-effect.d.ts.map +1 -0
  23. package/dist/generators/sound-effect.js +73 -0
  24. package/dist/generators/sound-effect.js.map +1 -0
  25. package/dist/http.d.ts +13 -0
  26. package/dist/http.d.ts.map +1 -0
  27. package/dist/http.js +29 -0
  28. package/dist/http.js.map +1 -0
  29. package/dist/index.d.ts +19 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +15 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/result.d.ts +6 -0
  34. package/dist/result.d.ts.map +1 -0
  35. package/dist/result.js +9 -0
  36. package/dist/result.js.map +1 -0
  37. package/dist/terminal-failure.d.ts +152 -0
  38. package/dist/terminal-failure.d.ts.map +1 -0
  39. package/dist/terminal-failure.js +203 -0
  40. package/dist/terminal-failure.js.map +1 -0
  41. package/dist/types.d.ts +89 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +2 -0
  44. package/dist/types.js.map +1 -0
  45. package/dist/validation.d.ts +9 -0
  46. package/dist/validation.d.ts.map +1 -0
  47. package/dist/validation.js +12 -0
  48. package/dist/validation.js.map +1 -0
  49. package/dist/world-patch.d.ts +25 -0
  50. package/dist/world-patch.d.ts.map +1 -0
  51. package/dist/world-patch.js +59 -0
  52. package/dist/world-patch.js.map +1 -0
  53. package/package.json +71 -0
@@ -0,0 +1,134 @@
1
+ import { z } from 'zod';
2
+ import { fail, ok } from '../result.js';
3
+ import { formatZodIssues } from '../validation.js';
4
+ export const characterParamsSchema = z.object({
5
+ name: z.string()
6
+ .min(1)
7
+ .regex(/^[a-zA-Z][a-zA-Z0-9_]*$/, 'Name must start with a letter, contain only letters, numbers, underscores')
8
+ .describe('Unique name for the character (e.g., "wizard", "goblin_warrior")'),
9
+ prompt: z.string()
10
+ .min(1)
11
+ .describe('Description of the character (e.g., "a wizard in a blue robe with a pointy hat")'),
12
+ applyToPlayer: z.boolean()
13
+ .optional()
14
+ .default(true)
15
+ .describe('Set this character as the PLAYER (writes worldProfileData.characterUrl and reloads). Set false for an NPC character — then pass character_url to the NPC spawn.'),
16
+ headScale: z.number()
17
+ .min(50)
18
+ .max(300)
19
+ .optional()
20
+ .default(150)
21
+ .describe('Head size as a percentage of normal (150 = chibi house style, matches the default character; 100 = realistic proportions).'),
22
+ assetId: z.string()
23
+ .optional()
24
+ .describe('Pre-generated asset id from the manifest. The character is stored in world.json assets[] under this id so code can reference it via registerNpc({ characterAssetId }).'),
25
+ }).strict();
26
+ const ENDPOINT = '/ai/v1/generate-rigged-character/jobs';
27
+ export async function generateCharacter(params, deps) {
28
+ const { forger, logger, onProgress } = deps;
29
+ const parsed = characterParamsSchema.safeParse(params);
30
+ if (!parsed.success) {
31
+ logger.warn({ err: parsed.error }, '[character] Invalid parameters');
32
+ return fail(formatZodIssues(parsed.error));
33
+ }
34
+ const input = parsed.data;
35
+ if (!forger.hasCredentials()) {
36
+ logger.error('[character] Missing Asset Forger credentials');
37
+ return fail(forger.getMissingCredentialsMessage());
38
+ }
39
+ onProgress(`Generating character: ${input.name}`);
40
+ logger.info(`[character] Starting generation for "${input.name}"`);
41
+ // Proven recipe for an engine-ready character: rerigTarget 'default' => Mixamo
42
+ // Y-up rig the engine retarget drives correctly (NEVER 'ue5' — that rig renders
43
+ // mangled), voxelize => returns the rigged voxel GLB directly (no browser
44
+ // voxelization needed).
45
+ let forgeResult;
46
+ try {
47
+ forgeResult = await forger.postJobAndWait(ENDPOINT, {
48
+ prompt: input.prompt,
49
+ rerigTarget: 'default',
50
+ voxelize: true,
51
+ voxelsTall: 80,
52
+ voxelMode: 'blended',
53
+ headScale: input.headScale,
54
+ }, 'CharacterGeneration');
55
+ }
56
+ catch (error) {
57
+ const message = error instanceof Error ? error.message : 'Unknown error occurred';
58
+ logger.error({ err: error }, '[character] Error during generation');
59
+ return fail(message);
60
+ }
61
+ // Job accepted by Asset Forger — cost incurred regardless of downstream success.
62
+ const billable = true;
63
+ // Resolve the VOXELIZED rigged variant. The AF emits the voxel as
64
+ // `output-variant-N.glb` (COLOR_0 vertex colors — matches the voxel game's art
65
+ // style). Some Asset Forger deployments (the dev one) return ONLY the textured
66
+ // low-poly mesh (`output-low.glb`) in additionalOutputFileUrls, with the voxel
67
+ // variant sitting alongside it in the same bucket dir but not listed in the
68
+ // response. So: prefer an explicit variant URL; otherwise derive it from the
69
+ // returned URL's directory and probe `output-variant-N.glb`.
70
+ const outputs = forgeResult.data?.additionalOutputFileUrls ?? [];
71
+ let characterUrl = outputs.find((u) => /output-variant-\d+\.glb/i.test(u));
72
+ const baseUrl = outputs[0] ?? forgeResult.data?.fileUrl;
73
+ if (!characterUrl && baseUrl) {
74
+ const dir = baseUrl.replace(/[^/]+$/, '');
75
+ const probes = await Promise.all([1, 2, 3, 4].map(async (n) => {
76
+ const cand = `${dir}output-variant-${n}.glb`;
77
+ try {
78
+ return (await fetch(cand, { method: 'HEAD' })).ok ? cand : null;
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }));
84
+ characterUrl = probes.filter((u) => u !== null).pop();
85
+ if (!characterUrl) {
86
+ logger.warn({ baseUrl }, '[character] No output-variant voxel found; falling back to AF-returned URL (may be a non-voxel mesh)');
87
+ characterUrl = baseUrl;
88
+ }
89
+ }
90
+ if (!characterUrl) {
91
+ return {
92
+ ...fail('Asset Forger returned no character URL. Ensure rerigTarget:"default" + voxelize:true are supported by the configured ASSET_FORGER_URL.'),
93
+ billable,
94
+ };
95
+ }
96
+ logger.info({ characterUrl, outputs }, '[character] Resolved voxel character URL');
97
+ // Store the character under its pre-minted manifest id so code can reference it
98
+ // via registerNpc({ characterAssetId }) — the engine resolves the id from
99
+ // assets[] at registration time. This is what lets the coding subagent run in
100
+ // parallel with asset generation instead of waiting for the literal GLB url.
101
+ const patches = [];
102
+ if (input.assetId) {
103
+ patches.push({
104
+ type: 'pushRoot',
105
+ path: ['assets'],
106
+ value: {
107
+ id: input.assetId,
108
+ name: input.name,
109
+ url: characterUrl,
110
+ type: 'character',
111
+ prompt: input.prompt,
112
+ source: 'generated',
113
+ },
114
+ });
115
+ }
116
+ // Apply to the player by writing worldProfileData.characterUrl AND
117
+ // useHighResCharacter:true — the default player look is the clean block
118
+ // mascot, so this flag is what makes PlayerLoader render the generated GLB as
119
+ // a highres skinned mesh on demand. The write triggers a game reload so it
120
+ // takes effect.
121
+ //
122
+ // The characterUrl write is authoritative: it is what makes the player render
123
+ // highres (a non-empty characterUrl ⇒ skinned unless useHighResCharacter:false),
124
+ // and it triggers the single reload. The useHighResCharacter flag is written
125
+ // FIRST as a best-effort marker (skipReload) so that if EITHER write fails the
126
+ // result is still consistent: flag-only with no URL → clean block; URL-only
127
+ // without the flag → still highres (the flag defaults to true when a custom
128
+ // URL is present).
129
+ if (input.applyToPlayer) {
130
+ patches.push({ type: 'set', path: ['useHighResCharacter'], value: true, skipReload: true }, { type: 'set', path: ['characterUrl'], value: characterUrl });
131
+ }
132
+ return ok(`Character "${input.name}" generated: ${characterUrl}`, patches, { assetId: input.assetId, characterUrl, billable });
133
+ }
134
+ //# sourceMappingURL=character.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"character.js","sourceRoot":"","sources":["../../src/generators/character.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod';
2
+ import type { AssetResultBase, GenerateDeps } from '../types.js';
3
+ export declare const imageAssetParamsSchema: z.ZodObject<{
4
+ name: z.ZodString;
5
+ prompt: z.ZodString;
6
+ width: z.ZodDefault<z.ZodNumber>;
7
+ height: z.ZodDefault<z.ZodNumber>;
8
+ referenceImageUrl: z.ZodOptional<z.ZodString>;
9
+ assetId: z.ZodOptional<z.ZodString>;
10
+ }, "strict", z.ZodTypeAny, {
11
+ prompt: string;
12
+ name: string;
13
+ width: number;
14
+ height: number;
15
+ referenceImageUrl?: string | undefined;
16
+ assetId?: string | undefined;
17
+ }, {
18
+ prompt: string;
19
+ name: string;
20
+ referenceImageUrl?: string | undefined;
21
+ assetId?: string | undefined;
22
+ width?: number | undefined;
23
+ height?: number | undefined;
24
+ }>;
25
+ /**
26
+ * The schema's INPUT type — callers may omit the defaulted dimensions. The
27
+ * generator parses on entry and works with the output type internally, which is
28
+ * what lets it own its own defaults instead of each host patching them in.
29
+ */
30
+ export type ImageAssetParams = z.input<typeof imageAssetParamsSchema>;
31
+ export type ImageAssetResult = AssetResultBase & {
32
+ assetId?: string;
33
+ name?: string;
34
+ url?: string;
35
+ /**
36
+ * True once the Forger has answered — the call is chargeable even when the
37
+ * response carried no URL. Left unset when the request itself threw, which is
38
+ * how the tool has always billed.
39
+ */
40
+ billable?: boolean;
41
+ };
42
+ export declare function generateImageAsset(params: ImageAssetParams, deps: GenerateDeps): Promise<ImageAssetResult>;
43
+ //# sourceMappingURL=image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/generators/image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKjE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;EAexB,CAAC;AAEZ;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAQF,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,gBAAgB,CAAC,CAsE3B"}
@@ -0,0 +1,74 @@
1
+ import { z } from 'zod';
2
+ import { fail, ok } from '../result.js';
3
+ import { fetchContentLength } from '../http.js';
4
+ import { formatZodIssues } from '../validation.js';
5
+ export const imageAssetParamsSchema = z.object({
6
+ name: z.string()
7
+ .min(1)
8
+ .regex(/^[a-zA-Z][a-zA-Z0-9_]*$/, 'Name must start with letter, contain only letters, numbers, underscores')
9
+ .describe('Unique asset name'),
10
+ prompt: z.string().min(1).describe('Text description of the image to generate'),
11
+ width: z.number().int().positive().default(512).describe('Image width in pixels'),
12
+ height: z.number().int().positive().default(512).describe('Image height in pixels'),
13
+ referenceImageUrl: z.string().optional()
14
+ .describe('Style/composition reference forwarded to the image model'),
15
+ assetId: z.string().min(1).optional()
16
+ .describe('Pre-generated asset id; minted when absent'),
17
+ }).strict();
18
+ const ENDPOINT = '/ai/v1/generate-image-as-webp-url';
19
+ export async function generateImageAsset(params, deps) {
20
+ const { forger, logger, onProgress } = deps;
21
+ const parsed = imageAssetParamsSchema.safeParse(params);
22
+ if (!parsed.success) {
23
+ logger.warn({ err: parsed.error }, '[image] Invalid parameters');
24
+ return fail(formatZodIssues(parsed.error));
25
+ }
26
+ const input = parsed.data;
27
+ if (!forger.hasCredentials()) {
28
+ logger.error('[image] Missing Asset Forger credentials');
29
+ return fail(forger.getMissingCredentialsMessage());
30
+ }
31
+ onProgress(`Generating image asset: ${input.name}`);
32
+ logger.info(`[image] Starting generation for "${input.name}"`);
33
+ let response;
34
+ try {
35
+ response = await forger.post(ENDPOINT, {
36
+ prompt: input.prompt,
37
+ height: input.height,
38
+ width: input.width,
39
+ numImages: 1,
40
+ guidanceScale: 1.0,
41
+ ...(input.referenceImageUrl ? { referenceImageUrl: input.referenceImageUrl } : {}),
42
+ }, 'image');
43
+ }
44
+ catch (error) {
45
+ const message = error instanceof Error ? error.message : 'Unknown error occurred';
46
+ logger.error({ err: error }, '[image] Error during generation');
47
+ return fail(message);
48
+ }
49
+ // The Forger answered, so this call bills from here on even if it carried no URL.
50
+ const imageUrl = response.imageUrl;
51
+ if (!imageUrl) {
52
+ return { ...fail('Asset Forger returned no image URL'), billable: true };
53
+ }
54
+ logger.info(`[image] Generated ${imageUrl}`);
55
+ // Asset Forger returns only a URL, so probe the stored file for its byte size
56
+ // (best-effort). Keeps the Assets catalog size column accurate.
57
+ const size = await fetchContentLength(imageUrl, logger);
58
+ const assetId = input.assetId ?? `img-${input.name}-${Date.now()}`;
59
+ const asset = {
60
+ id: assetId,
61
+ name: input.name,
62
+ url: imageUrl,
63
+ // The Assets-tab thumbnail is driven by `screenshotUrl`; for an image asset
64
+ // the thumbnail is the image itself. Without this, AI-generated images show
65
+ // a "No Preview" placeholder while uploaded ones get a thumbnail.
66
+ screenshotUrl: imageUrl,
67
+ type: 'image',
68
+ source: 'generated',
69
+ prompt: input.prompt,
70
+ ...(size !== undefined ? { size } : {}),
71
+ };
72
+ return ok(`Image asset "${input.name}" generated: ${imageUrl}`, [{ type: 'upsertRoot', path: ['assets'], matchKeys: ['name', 'type'], value: asset }], { assetId, name: input.name, url: imageUrl, billable: true });
73
+ }
74
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/generators/image.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod';
2
+ import type { AssetResultBase, GenerateDeps } from '../types.js';
3
+ export declare const skyboxParamsSchema: z.ZodObject<{
4
+ description: z.ZodString;
5
+ referenceImageUrl: z.ZodOptional<z.ZodString>;
6
+ }, "strict", z.ZodTypeAny, {
7
+ description: string;
8
+ referenceImageUrl?: string | undefined;
9
+ }, {
10
+ description: string;
11
+ referenceImageUrl?: string | undefined;
12
+ }>;
13
+ export type SkyboxParams = z.input<typeof skyboxParamsSchema>;
14
+ export type SkyboxResult = AssetResultBase & {
15
+ url?: string;
16
+ };
17
+ export declare function generateSkybox(params: SkyboxParams, deps: GenerateDeps): Promise<SkyboxResult>;
18
+ //# sourceMappingURL=skybox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skybox.d.ts","sourceRoot":"","sources":["../../src/generators/skybox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIjE,eAAO,MAAM,kBAAkB;;;;;;;;;EAIpB,CAAC;AAEZ,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAoB9D,wBAAsB,cAAc,CAClC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,YAAY,CAAC,CAiDvB"}
@@ -0,0 +1,57 @@
1
+ import { z } from 'zod';
2
+ import { fail, ok } from '../result.js';
3
+ import { formatZodIssues } from '../validation.js';
4
+ export const skyboxParamsSchema = z.object({
5
+ description: z.string().min(1).describe('Description of the skybox environment'),
6
+ referenceImageUrl: z.string().optional()
7
+ .describe('Style/composition reference forwarded to the image model'),
8
+ }).strict();
9
+ const SKYBOX_WIDTH = 2048;
10
+ const SKYBOX_HEIGHT = 1024;
11
+ const ENDPOINT = '/ai/v1/generate-image-as-webp-url';
12
+ function buildPrompt(environmentDescription) {
13
+ return `Create a perfectly seamless 360-degree equirectangular panoramic skybox with matching left and right edges for a stylized low-poly scene.
14
+ The scene description is: ${environmentDescription}.
15
+ CRITICAL: The left and right edges of the image MUST match perfectly to ensure seamless wrapping when viewed in 360 degrees in a Unity 3D game.
16
+ Ensure consistent lighting and color palette across the entire panorama.
17
+ Position all landmark elements and important features higher in the frame, above the horizon line, not clustered at the bottom of the image.
18
+ Design the image with horizontal symmetry and continuity to guarantee there is no visible seam when the edges connect.
19
+ The entire aesthetic should maintain clear edges and minimal texturing, matching low-poly game art while creating the impression that the viewer is looking outward from a floating platform within the skyline.`;
20
+ }
21
+ export async function generateSkybox(params, deps) {
22
+ const { forger, logger, onProgress } = deps;
23
+ const parsed = skyboxParamsSchema.safeParse(params);
24
+ if (!parsed.success) {
25
+ logger.warn({ err: parsed.error }, '[skybox] Invalid parameters');
26
+ return fail(formatZodIssues(parsed.error));
27
+ }
28
+ const input = parsed.data;
29
+ if (!forger.hasCredentials()) {
30
+ logger.error('[skybox] Missing Asset Forger credentials');
31
+ return fail(forger.getMissingCredentialsMessage());
32
+ }
33
+ onProgress(`Generating skybox: ${input.description}`);
34
+ logger.info(`[skybox] Starting generation for: "${input.description}"${input.referenceImageUrl ? ' (with reference image)' : ''}`);
35
+ try {
36
+ const response = await forger.post(ENDPOINT, {
37
+ prompt: buildPrompt(input.description),
38
+ height: SKYBOX_HEIGHT,
39
+ width: SKYBOX_WIDTH,
40
+ numImages: 1,
41
+ guidanceScale: 1.0,
42
+ ...(input.referenceImageUrl ? { referenceImageUrl: input.referenceImageUrl } : {}),
43
+ }, 'skybox');
44
+ const url = response.imageUrl;
45
+ if (!url) {
46
+ throw new Error('No image URL found in API response');
47
+ }
48
+ logger.info(`[skybox] Successfully generated skybox image URL: ${url}`);
49
+ return ok(`Skybox image generated: ${url}`, [{ type: 'set', path: ['skyboxUrl'], value: url }], { url });
50
+ }
51
+ catch (error) {
52
+ const message = error instanceof Error ? error.message : 'Unknown error occurred';
53
+ logger.error({ err: error }, '[skybox] Error during skybox generation');
54
+ return fail(message);
55
+ }
56
+ }
57
+ //# sourceMappingURL=skybox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skybox.js","sourceRoot":"","sources":["../../src/generators/skybox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ import type { AssetResultBase, GenerateDeps } from '../types.js';
3
+ export declare const soundEffectParamsSchema: z.ZodObject<{
4
+ prompt: z.ZodString;
5
+ durationSeconds: z.ZodOptional<z.ZodNumber>;
6
+ promptInfluence: z.ZodOptional<z.ZodNumber>;
7
+ loop: z.ZodOptional<z.ZodBoolean>;
8
+ assetId: z.ZodOptional<z.ZodString>;
9
+ }, "strict", z.ZodTypeAny, {
10
+ prompt: string;
11
+ durationSeconds?: number | undefined;
12
+ promptInfluence?: number | undefined;
13
+ loop?: boolean | undefined;
14
+ assetId?: string | undefined;
15
+ }, {
16
+ prompt: string;
17
+ durationSeconds?: number | undefined;
18
+ promptInfluence?: number | undefined;
19
+ loop?: boolean | undefined;
20
+ assetId?: string | undefined;
21
+ }>;
22
+ export type SoundEffectParams = z.input<typeof soundEffectParamsSchema>;
23
+ export type SoundEffectResult = AssetResultBase & {
24
+ soundEffectUrl?: string;
25
+ originalUrl?: string;
26
+ soundId?: string;
27
+ format?: string;
28
+ sizeBytes?: number;
29
+ };
30
+ export declare function generateSoundEffect(params: SoundEffectParams, deps: GenerateDeps): Promise<SoundEffectResult>;
31
+ //# sourceMappingURL=sound-effect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sound-effect.d.ts","sourceRoot":"","sources":["../../src/generators/sound-effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKjE,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;EASzB,CAAC;AAEZ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAWF,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAiE5B"}
@@ -0,0 +1,73 @@
1
+ import { z } from 'zod';
2
+ import { fail, ok } from '../result.js';
3
+ import { fetchContentLength } from '../http.js';
4
+ import { formatZodIssues } from '../validation.js';
5
+ export const soundEffectParamsSchema = z.object({
6
+ prompt: z.string().min(1).describe('Text describing the SFX to generate'),
7
+ durationSeconds: z.number().min(0.5).max(22).optional()
8
+ .describe('Length in seconds; upstream auto-picks when omitted'),
9
+ promptInfluence: z.number().min(0).max(1).optional()
10
+ .describe('How tightly to follow the prompt'),
11
+ loop: z.boolean().optional().describe('Ask upstream for a seamlessly loopable SFX'),
12
+ assetId: z.string().min(1).optional()
13
+ .describe('Pre-generated asset id; the generator id is used when absent'),
14
+ }).strict();
15
+ const ENDPOINT = '/ai/v1/generate-sound-effect-as-opus-url';
16
+ export async function generateSoundEffect(params, deps) {
17
+ const { forger, logger, onProgress } = deps;
18
+ const parsed = soundEffectParamsSchema.safeParse(params);
19
+ if (!parsed.success) {
20
+ logger.warn({ err: parsed.error }, '[sound-effect] Invalid parameters');
21
+ return fail(formatZodIssues(parsed.error));
22
+ }
23
+ const input = parsed.data;
24
+ if (!forger.hasCredentials()) {
25
+ logger.error('[sound-effect] Missing Asset Forger credentials');
26
+ return fail(forger.getMissingCredentialsMessage());
27
+ }
28
+ onProgress(`Generating sound effect: ${input.prompt}`);
29
+ logger.info(`[sound-effect] Starting generation for: "${input.prompt}"`);
30
+ const payload = { prompt: input.prompt };
31
+ if (input.durationSeconds !== undefined)
32
+ payload.durationSeconds = input.durationSeconds;
33
+ if (input.promptInfluence !== undefined)
34
+ payload.promptInfluence = input.promptInfluence;
35
+ if (input.loop !== undefined)
36
+ payload.loop = input.loop;
37
+ try {
38
+ const response = await forger.post(ENDPOINT, payload, 'sound-effect');
39
+ if (!response.soundEffectUrl) {
40
+ throw new Error('No soundEffectUrl in API response');
41
+ }
42
+ logger.info(`[sound-effect] Successfully generated: ${response.soundEffectUrl}`);
43
+ const sizeBytes = await fetchContentLength(response.soundEffectUrl, logger);
44
+ const assetId = input.assetId ?? response.soundId;
45
+ return ok(`Sound effect generated: ${response.soundEffectUrl}`, [{
46
+ type: 'pushRoot',
47
+ path: ['assets'],
48
+ value: {
49
+ id: assetId,
50
+ name: input.prompt,
51
+ url: response.soundEffectUrl,
52
+ type: 'audio',
53
+ format: response.format,
54
+ prompt: input.prompt,
55
+ source: 'generated',
56
+ size: sizeBytes ?? 0,
57
+ ...(input.loop !== undefined && { loop: input.loop }),
58
+ },
59
+ }], {
60
+ soundEffectUrl: response.soundEffectUrl,
61
+ originalUrl: response.originalUrl,
62
+ soundId: assetId,
63
+ format: response.format,
64
+ sizeBytes,
65
+ });
66
+ }
67
+ catch (error) {
68
+ const message = error instanceof Error ? error.message : 'Unknown error occurred';
69
+ logger.error({ err: error }, '[sound-effect] Error during generation');
70
+ return fail(message);
71
+ }
72
+ }
73
+ //# sourceMappingURL=sound-effect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sound-effect.js","sourceRoot":"","sources":["../../src/generators/sound-effect.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { CoreLogger } from './types.js';
2
+ /**
3
+ * Probe a remote file's byte size via a HEAD request (Content-Length header).
4
+ * Returns undefined when the request fails or the header is missing/invalid, so
5
+ * callers treat the size as unknown rather than zero. Used to size
6
+ * backend-generated assets (image/sound) whose generator API returns only a URL
7
+ * — running server-side avoids the browser CORS limits on those storage hosts.
8
+ *
9
+ * The 10s abort is load-bearing: generation has no other timeout around this
10
+ * probe, so a hung HEAD against a storage host would stall the whole call.
11
+ */
12
+ export declare function fetchContentLength(url: string, logger: CoreLogger): Promise<number | undefined>;
13
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAe7B"}
package/dist/http.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Probe a remote file's byte size via a HEAD request (Content-Length header).
3
+ * Returns undefined when the request fails or the header is missing/invalid, so
4
+ * callers treat the size as unknown rather than zero. Used to size
5
+ * backend-generated assets (image/sound) whose generator API returns only a URL
6
+ * — running server-side avoids the browser CORS limits on those storage hosts.
7
+ *
8
+ * The 10s abort is load-bearing: generation has no other timeout around this
9
+ * probe, so a hung HEAD against a storage host would stall the whole call.
10
+ */
11
+ export async function fetchContentLength(url, logger) {
12
+ try {
13
+ const response = await fetch(url, { method: 'HEAD', signal: AbortSignal.timeout(10000) });
14
+ if (!response.ok) {
15
+ logger.warn(`HEAD ${url} returned ${response.status}`);
16
+ return undefined;
17
+ }
18
+ const contentLength = response.headers.get('content-length');
19
+ if (!contentLength)
20
+ return undefined;
21
+ const bytes = parseInt(contentLength, 10);
22
+ return Number.isFinite(bytes) && bytes > 0 ? bytes : undefined;
23
+ }
24
+ catch (err) {
25
+ logger.warn({ err }, `HEAD request failed for ${url}`);
26
+ return undefined;
27
+ }
28
+ }
29
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAW,EACX,MAAkB;IAElB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,KAAM,CAAC,EAAE,CAAC,CAAC;QAC3F,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,CAAC,IAAI,CAAC,QAAQ,GAAG,aAAa,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACvD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC7D,IAAI,CAAC,aAAa;YAAE,OAAO,SAAS,CAAC;QACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QAC1C,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,2BAA2B,GAAG,EAAE,CAAC,CAAC;QACvD,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,19 @@
1
+ export type { AssetResultBase, CoreLogger, DataWorldPatch, ForgerClient, GenerateDeps, UpsertRootPatch, WorldPatch, } from './types.js';
2
+ export { buildUpsertPredicate, isUpsertRootPatch, validateWorldPatch } from './world-patch.js';
3
+ export { fail, ok } from './result.js';
4
+ export { AssetForgerClient } from './forger-client.js';
5
+ export type { AssetForgerCredentials } from './forger-client.js';
6
+ export { generateSkybox, skyboxParamsSchema } from './generators/skybox.js';
7
+ export type { SkyboxParams, SkyboxResult } from './generators/skybox.js';
8
+ export { generateSoundEffect, soundEffectParamsSchema } from './generators/sound-effect.js';
9
+ export type { SoundEffectParams, SoundEffectResult } from './generators/sound-effect.js';
10
+ export { generateImageAsset, imageAssetParamsSchema } from './generators/image.js';
11
+ export type { ImageAssetParams, ImageAssetResult } from './generators/image.js';
12
+ export { generateCharacter, characterParamsSchema } from './generators/character.js';
13
+ export type { CharacterParams, CharacterResult } from './generators/character.js';
14
+ export { generateAnimationAsset, animationParamsSchema, getConfiguredAnimationModel, ANIMATION_MODEL_TEXT_TO_MOTION_3, ANIMATION_MODEL_TEXT_TO_MOTION_2, ANIMATION_MODEL_OLDER, } from './generators/animation.js';
15
+ export type { AnimationParams, AnimationResult } from './generators/animation.js';
16
+ export { fetchContentLength } from './http.js';
17
+ export { TERMINAL_FAILURE_CODES, TERMINAL_FAILURE_VERSION, buildTerminalFailure, isTerminalFailureCode, legacyErrorTypeFor, parseTerminalFailure, } from './terminal-failure.js';
18
+ export type { BuildTerminalFailureInput, LegacyErrorType, TerminalFailure, TerminalFailureCode, TerminalFailureDetail, } from './terminal-failure.js';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,UAAU,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5E,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC5F,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACnF,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACrF,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,gCAAgC,EAChC,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAM/C,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ export { buildUpsertPredicate, isUpsertRootPatch, validateWorldPatch } from './world-patch.js';
2
+ export { fail, ok } from './result.js';
3
+ export { AssetForgerClient } from './forger-client.js';
4
+ export { generateSkybox, skyboxParamsSchema } from './generators/skybox.js';
5
+ export { generateSoundEffect, soundEffectParamsSchema } from './generators/sound-effect.js';
6
+ export { generateImageAsset, imageAssetParamsSchema } from './generators/image.js';
7
+ export { generateCharacter, characterParamsSchema } from './generators/character.js';
8
+ export { generateAnimationAsset, animationParamsSchema, getConfiguredAnimationModel, ANIMATION_MODEL_TEXT_TO_MOTION_3, ANIMATION_MODEL_TEXT_TO_MOTION_2, ANIMATION_MODEL_OLDER, } from './generators/animation.js';
9
+ export { fetchContentLength } from './http.js';
10
+ // The agent↔client terminal-failure contract. Also published as the
11
+ // `@bitmagic/asset-core/terminal-failure` subpath: this barrel eagerly pulls in
12
+ // the generators (and therefore zod, the forger client, and node http helpers),
13
+ // so browser consumers must import the subpath instead of the root.
14
+ export { TERMINAL_FAILURE_CODES, TERMINAL_FAILURE_VERSION, buildTerminalFailure, isTerminalFailureCode, legacyErrorTypeFor, parseTerminalFailure, } from './terminal-failure.js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5E,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5F,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAEnF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAErF,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,gCAAgC,EAChC,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,oEAAoE;AACpE,gFAAgF;AAChF,gFAAgF;AAChF,oEAAoE;AACpE,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { AssetResultBase, WorldPatch } from './types.js';
2
+ /** An unsuccessful result. Failures never carry world.json changes. */
3
+ export declare function fail(message: string): AssetResultBase;
4
+ /** A successful result, merging generator-specific fields onto the base shape. */
5
+ export declare function ok<T extends object = Record<string, never>>(message: string, patches: WorldPatch[], extra?: T): AssetResultBase & T;
6
+ //# sourceMappingURL=result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE9D,uEAAuE;AACvE,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAErD;AAED,kFAAkF;AAClF,wBAAgB,EAAE,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACzD,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,UAAU,EAAE,EACrB,KAAK,CAAC,EAAE,CAAC,GACR,eAAe,GAAG,CAAC,CAErB"}
package/dist/result.js ADDED
@@ -0,0 +1,9 @@
1
+ /** An unsuccessful result. Failures never carry world.json changes. */
2
+ export function fail(message) {
3
+ return { success: false, message, patches: [] };
4
+ }
5
+ /** A successful result, merging generator-specific fields onto the base shape. */
6
+ export function ok(message, patches, extra) {
7
+ return { success: true, message, patches, ...(extra ?? {}) };
8
+ }
9
+ //# sourceMappingURL=result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAEA,uEAAuE;AACvE,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAClD,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,EAAE,CAChB,OAAe,EACf,OAAqB,EACrB,KAAS;IAET,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,KAAK,IAAK,EAAQ,CAAC,EAAE,CAAC;AACtE,CAAC"}