@fungi.computer/darkroom 0.0.0-stage → 0.0.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.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +315 -2
  3. package/dist/app-media-contracts.d.ts +499 -0
  4. package/dist/app-media-contracts.js +98 -0
  5. package/dist/browser-failure.d.ts +40 -0
  6. package/dist/browser-failure.js +39 -0
  7. package/dist/browser.d.ts +2 -0
  8. package/dist/browser.js +1 -0
  9. package/dist/cloudflare/provider.d.ts +13 -0
  10. package/dist/cloudflare/provider.js +140 -0
  11. package/dist/cloudflare/reference-images.d.ts +42 -0
  12. package/dist/cloudflare/reference-images.js +102 -0
  13. package/dist/cloudflare.d.ts +6 -0
  14. package/dist/cloudflare.js +4 -0
  15. package/dist/cost-estimate.d.ts +22 -0
  16. package/dist/cost-estimate.js +76 -0
  17. package/dist/effect.d.ts +2 -0
  18. package/dist/effect.js +2 -0
  19. package/dist/execution-error.d.ts +54 -0
  20. package/dist/execution-error.js +62 -0
  21. package/dist/failure.d.ts +2 -0
  22. package/dist/failure.js +8 -0
  23. package/dist/gateway-models.d.ts +23 -0
  24. package/dist/gateway-models.js +77 -0
  25. package/dist/gateway-wire.d.ts +16 -0
  26. package/dist/gateway-wire.js +22 -0
  27. package/dist/index.d.ts +361 -0
  28. package/dist/index.js +611 -0
  29. package/dist/media-binding.d.ts +291 -0
  30. package/dist/media-binding.js +146 -0
  31. package/dist/media-generation.d.ts +10 -0
  32. package/dist/media-generation.js +110 -0
  33. package/dist/media-operation-contract.d.ts +104 -0
  34. package/dist/media-operation-contract.js +29 -0
  35. package/dist/media-operation-do.d.ts +55 -0
  36. package/dist/media-operation-do.js +105 -0
  37. package/dist/media-operation-owner.d.ts +301 -0
  38. package/dist/media-operation-owner.js +903 -0
  39. package/dist/media-operation-storage.d.ts +81 -0
  40. package/dist/media-operation-storage.js +1 -0
  41. package/dist/media-operation.d.ts +33 -0
  42. package/dist/media-operation.js +230 -0
  43. package/dist/model-catalog.d.ts +456 -0
  44. package/dist/model-catalog.js +565 -0
  45. package/dist/neurons.d.ts +4 -0
  46. package/dist/neurons.js +4 -0
  47. package/dist/provenance-bytes.d.ts +3 -0
  48. package/dist/provenance-bytes.js +15 -0
  49. package/dist/provenance-export.d.ts +23 -0
  50. package/dist/provenance-export.js +103 -0
  51. package/dist/provenance-jpeg.d.ts +3 -0
  52. package/dist/provenance-jpeg.js +76 -0
  53. package/dist/provenance-png.d.ts +1 -0
  54. package/dist/provenance-png.js +21 -0
  55. package/dist/provenance-xmp.d.ts +2 -0
  56. package/dist/provenance-xmp.js +54 -0
  57. package/dist/provenance.d.ts +20 -0
  58. package/dist/provenance.js +58 -0
  59. package/dist/server.d.ts +158 -0
  60. package/dist/server.js +207 -0
  61. package/dist/video-providers/index.d.ts +14 -0
  62. package/dist/video-providers/index.js +14 -0
  63. package/dist/video-providers/minimax.d.ts +25 -0
  64. package/dist/video-providers/minimax.js +63 -0
  65. package/dist/video-providers/reference-encoding.d.ts +4 -0
  66. package/dist/video-providers/reference-encoding.js +11 -0
  67. package/dist/video-providers/types.d.ts +28 -0
  68. package/dist/video-providers/types.js +1 -0
  69. package/dist/video-providers/unified.d.ts +18 -0
  70. package/dist/video-providers/unified.js +52 -0
  71. package/docs/api/README.md +16 -0
  72. package/docs/api/app-media-contracts.md +507 -0
  73. package/docs/api/browser.md +126 -0
  74. package/docs/api/cloudflare.md +678 -0
  75. package/docs/api/effect.md +39 -0
  76. package/docs/api/index.md +1775 -0
  77. package/docs/api/modules.md +19 -0
  78. package/docs/api/provenance-export.md +114 -0
  79. package/docs/api/server.md +1672 -0
  80. package/package.json +110 -3
@@ -0,0 +1,291 @@
1
+ import * as z from "zod";
2
+ import * as Schema from "effect/Schema";
3
+ import { mediaRequestedImageFormatSchema, type MediaGenerationSettings } from "./index.js";
4
+ export { mediaResultSchema } from "./media-operation-contract.js";
5
+ /** Image settings and path references crossing a scoped MEDIA binding. */
6
+ export declare const mediaImageBindingInputSchema: z.ZodObject<{
7
+ kind: z.ZodLiteral<"image">;
8
+ requestId: z.ZodString;
9
+ libraryPath: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
10
+ settings: z.ZodObject<{
11
+ resolution: z.ZodOptional<z.ZodEnum<{
12
+ readonly p480: "480p";
13
+ readonly p720: "720p";
14
+ readonly p768: "768p";
15
+ readonly p1080: "1080p";
16
+ readonly p4k: "4k";
17
+ }>>;
18
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
19
+ readonly wide: "16:9";
20
+ readonly landscape: "4:3";
21
+ readonly square: "1:1";
22
+ readonly portrait: "3:4";
23
+ readonly tall: "9:16";
24
+ readonly ultrawide: "21:9";
25
+ readonly ultratall: "9:21";
26
+ }>>;
27
+ width: z.ZodNumber;
28
+ height: z.ZodNumber;
29
+ references: z.ZodOptional<z.ZodArray<z.ZodObject<{
30
+ path: z.ZodString;
31
+ role: z.ZodOptional<z.ZodEnum<{
32
+ readonly first: "first-frame";
33
+ readonly last: "last-frame";
34
+ readonly reference: "reference";
35
+ }>>;
36
+ }, z.core.$strict>>>;
37
+ model: z.ZodString;
38
+ prompt: z.ZodString;
39
+ generateAudio: z.ZodOptional<z.ZodBoolean>;
40
+ steps: z.ZodOptional<z.ZodNumber>;
41
+ }, z.core.$strict>;
42
+ outputFormat: z.ZodOptional<z.ZodEnum<{
43
+ readonly png: "png";
44
+ readonly jpeg: "jpeg";
45
+ readonly webp: "webp";
46
+ }>>;
47
+ }, z.core.$strict>;
48
+ /** Video settings and path references crossing the same scoped binding. */
49
+ export declare const mediaVideoBindingInputSchema: z.ZodObject<{
50
+ kind: z.ZodLiteral<"video">;
51
+ requestId: z.ZodString;
52
+ libraryPath: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
53
+ settings: z.ZodObject<{
54
+ model: z.ZodEnum<{
55
+ readonly seedance: "bytedance/seedance-2.0";
56
+ readonly seedanceFast: "bytedance/seedance-2.0-fast";
57
+ readonly seedanceMini: "bytedance/seedance-2.0-mini";
58
+ readonly veo: "google/veo-3.1";
59
+ readonly veoFast: "google/veo-3.1-fast";
60
+ readonly minimaxH3Max: "minimax/h3-max";
61
+ }>;
62
+ prompt: z.ZodString;
63
+ resolution: z.ZodOptional<z.ZodEnum<{
64
+ readonly p480: "480p";
65
+ readonly p720: "720p";
66
+ readonly p768: "768p";
67
+ readonly p1080: "1080p";
68
+ readonly p4k: "4k";
69
+ }>>;
70
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
71
+ readonly wide: "16:9";
72
+ readonly landscape: "4:3";
73
+ readonly square: "1:1";
74
+ readonly portrait: "3:4";
75
+ readonly tall: "9:16";
76
+ readonly ultrawide: "21:9";
77
+ readonly ultratall: "9:21";
78
+ }>>;
79
+ durationSeconds: z.ZodOptional<z.ZodNumber>;
80
+ generateAudio: z.ZodOptional<z.ZodBoolean>;
81
+ references: z.ZodOptional<z.ZodArray<z.ZodObject<{
82
+ path: z.ZodString;
83
+ role: z.ZodEnum<{
84
+ readonly first: "first-frame";
85
+ readonly last: "last-frame";
86
+ readonly reference: "reference";
87
+ }>;
88
+ }, z.core.$strict>>>;
89
+ }, z.core.$strict>;
90
+ }, z.core.$strict>;
91
+ /** One generation identity, including the App-owned folder captured for replay. */
92
+ export declare const mediaBindingGenerationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
93
+ kind: z.ZodLiteral<"image">;
94
+ requestId: z.ZodString;
95
+ libraryPath: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
96
+ settings: z.ZodObject<{
97
+ resolution: z.ZodOptional<z.ZodEnum<{
98
+ readonly p480: "480p";
99
+ readonly p720: "720p";
100
+ readonly p768: "768p";
101
+ readonly p1080: "1080p";
102
+ readonly p4k: "4k";
103
+ }>>;
104
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
105
+ readonly wide: "16:9";
106
+ readonly landscape: "4:3";
107
+ readonly square: "1:1";
108
+ readonly portrait: "3:4";
109
+ readonly tall: "9:16";
110
+ readonly ultrawide: "21:9";
111
+ readonly ultratall: "9:21";
112
+ }>>;
113
+ width: z.ZodNumber;
114
+ height: z.ZodNumber;
115
+ references: z.ZodOptional<z.ZodArray<z.ZodObject<{
116
+ path: z.ZodString;
117
+ role: z.ZodOptional<z.ZodEnum<{
118
+ readonly first: "first-frame";
119
+ readonly last: "last-frame";
120
+ readonly reference: "reference";
121
+ }>>;
122
+ }, z.core.$strict>>>;
123
+ model: z.ZodString;
124
+ prompt: z.ZodString;
125
+ generateAudio: z.ZodOptional<z.ZodBoolean>;
126
+ steps: z.ZodOptional<z.ZodNumber>;
127
+ }, z.core.$strict>;
128
+ outputFormat: z.ZodOptional<z.ZodEnum<{
129
+ readonly png: "png";
130
+ readonly jpeg: "jpeg";
131
+ readonly webp: "webp";
132
+ }>>;
133
+ }, z.core.$strict>, z.ZodObject<{
134
+ kind: z.ZodLiteral<"video">;
135
+ requestId: z.ZodString;
136
+ libraryPath: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
137
+ settings: z.ZodObject<{
138
+ model: z.ZodEnum<{
139
+ readonly seedance: "bytedance/seedance-2.0";
140
+ readonly seedanceFast: "bytedance/seedance-2.0-fast";
141
+ readonly seedanceMini: "bytedance/seedance-2.0-mini";
142
+ readonly veo: "google/veo-3.1";
143
+ readonly veoFast: "google/veo-3.1-fast";
144
+ readonly minimaxH3Max: "minimax/h3-max";
145
+ }>;
146
+ prompt: z.ZodString;
147
+ resolution: z.ZodOptional<z.ZodEnum<{
148
+ readonly p480: "480p";
149
+ readonly p720: "720p";
150
+ readonly p768: "768p";
151
+ readonly p1080: "1080p";
152
+ readonly p4k: "4k";
153
+ }>>;
154
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
155
+ readonly wide: "16:9";
156
+ readonly landscape: "4:3";
157
+ readonly square: "1:1";
158
+ readonly portrait: "3:4";
159
+ readonly tall: "9:16";
160
+ readonly ultrawide: "21:9";
161
+ readonly ultratall: "9:21";
162
+ }>>;
163
+ durationSeconds: z.ZodOptional<z.ZodNumber>;
164
+ generateAudio: z.ZodOptional<z.ZodBoolean>;
165
+ references: z.ZodOptional<z.ZodArray<z.ZodObject<{
166
+ path: z.ZodString;
167
+ role: z.ZodEnum<{
168
+ readonly first: "first-frame";
169
+ readonly last: "last-frame";
170
+ readonly reference: "reference";
171
+ }>;
172
+ }, z.core.$strict>>>;
173
+ }, z.core.$strict>;
174
+ }, z.core.$strict>], "kind">;
175
+ /** Parsed generation command consumed by either host. */
176
+ export type MediaBindingGeneration = z.output<typeof mediaBindingGenerationSchema>;
177
+ /** Maintained MEDIA method grammar; guest input carries no principal or Computer. */
178
+ export declare const mediaBindingCallSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
179
+ operation: z.ZodLiteral<"models">;
180
+ input: z.ZodObject<{}, z.core.$strict>;
181
+ }, z.core.$strict>, z.ZodObject<{
182
+ operation: z.ZodLiteral<"generate">;
183
+ input: z.ZodDiscriminatedUnion<[z.ZodObject<{
184
+ kind: z.ZodLiteral<"image">;
185
+ requestId: z.ZodString;
186
+ libraryPath: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
187
+ settings: z.ZodObject<{
188
+ resolution: z.ZodOptional<z.ZodEnum<{
189
+ readonly p480: "480p";
190
+ readonly p720: "720p";
191
+ readonly p768: "768p";
192
+ readonly p1080: "1080p";
193
+ readonly p4k: "4k";
194
+ }>>;
195
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
196
+ readonly wide: "16:9";
197
+ readonly landscape: "4:3";
198
+ readonly square: "1:1";
199
+ readonly portrait: "3:4";
200
+ readonly tall: "9:16";
201
+ readonly ultrawide: "21:9";
202
+ readonly ultratall: "9:21";
203
+ }>>;
204
+ width: z.ZodNumber;
205
+ height: z.ZodNumber;
206
+ references: z.ZodOptional<z.ZodArray<z.ZodObject<{
207
+ path: z.ZodString;
208
+ role: z.ZodOptional<z.ZodEnum<{
209
+ readonly first: "first-frame";
210
+ readonly last: "last-frame";
211
+ readonly reference: "reference";
212
+ }>>;
213
+ }, z.core.$strict>>>;
214
+ model: z.ZodString;
215
+ prompt: z.ZodString;
216
+ generateAudio: z.ZodOptional<z.ZodBoolean>;
217
+ steps: z.ZodOptional<z.ZodNumber>;
218
+ }, z.core.$strict>;
219
+ outputFormat: z.ZodOptional<z.ZodEnum<{
220
+ readonly png: "png";
221
+ readonly jpeg: "jpeg";
222
+ readonly webp: "webp";
223
+ }>>;
224
+ }, z.core.$strict>, z.ZodObject<{
225
+ kind: z.ZodLiteral<"video">;
226
+ requestId: z.ZodString;
227
+ libraryPath: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
228
+ settings: z.ZodObject<{
229
+ model: z.ZodEnum<{
230
+ readonly seedance: "bytedance/seedance-2.0";
231
+ readonly seedanceFast: "bytedance/seedance-2.0-fast";
232
+ readonly seedanceMini: "bytedance/seedance-2.0-mini";
233
+ readonly veo: "google/veo-3.1";
234
+ readonly veoFast: "google/veo-3.1-fast";
235
+ readonly minimaxH3Max: "minimax/h3-max";
236
+ }>;
237
+ prompt: z.ZodString;
238
+ resolution: z.ZodOptional<z.ZodEnum<{
239
+ readonly p480: "480p";
240
+ readonly p720: "720p";
241
+ readonly p768: "768p";
242
+ readonly p1080: "1080p";
243
+ readonly p4k: "4k";
244
+ }>>;
245
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
246
+ readonly wide: "16:9";
247
+ readonly landscape: "4:3";
248
+ readonly square: "1:1";
249
+ readonly portrait: "3:4";
250
+ readonly tall: "9:16";
251
+ readonly ultrawide: "21:9";
252
+ readonly ultratall: "9:21";
253
+ }>>;
254
+ durationSeconds: z.ZodOptional<z.ZodNumber>;
255
+ generateAudio: z.ZodOptional<z.ZodBoolean>;
256
+ references: z.ZodOptional<z.ZodArray<z.ZodObject<{
257
+ path: z.ZodString;
258
+ role: z.ZodEnum<{
259
+ readonly first: "first-frame";
260
+ readonly last: "last-frame";
261
+ readonly reference: "reference";
262
+ }>;
263
+ }, z.core.$strict>>>;
264
+ }, z.core.$strict>;
265
+ }, z.core.$strict>], "kind">;
266
+ }, z.core.$strict>], "operation">;
267
+ /** Parsed call consumed by host admission and transport. */
268
+ export type MediaBindingCall = z.output<typeof mediaBindingCallSchema>;
269
+ /** Background-generation reply emitted by the durable service. */
270
+ export declare const mediaPendingSchema: z.ZodObject<{
271
+ status: z.ZodLiteral<"pending">;
272
+ retryAfterSeconds: z.ZodNumber;
273
+ }, z.core.$strict>;
274
+ /** Parsing and model refusal stay shared; authorization remains host-owned. */
275
+ export declare function parseMediaBindingCall(raw: Schema.Json): Readonly<{
276
+ kind: "parsed";
277
+ call: MediaBindingCall;
278
+ }> | Readonly<{
279
+ kind: "refused";
280
+ result: z.core.util.JSONType;
281
+ }>;
282
+ /** Relative service input after the host attaches its trusted Computer to references. */
283
+ export type MediaBindingRequest = Readonly<{
284
+ path: string;
285
+ settings: MediaGenerationSettings;
286
+ outputFormat?: z.output<typeof mediaRequestedImageFormatSchema>;
287
+ }>;
288
+ /** Choose the same fixed filename on both hosts without a recursive App lookup. */
289
+ export declare function mediaBindingRequest(input: MediaBindingGeneration, computerId: string): MediaBindingRequest;
290
+ /** Decode bounded service replies once for both MEDIA host bindings. */
291
+ export declare function readMediaBindingReply(input: MediaBindingCall, computerId: string, response: Response): Promise<z.core.util.JSONType>;
@@ -0,0 +1,146 @@
1
+ import * as z from "zod";
2
+ import * as Schema from "effect/Schema";
3
+ import { pending } from "@fungi.computer/agar/backend-definition";
4
+ import { mediaRequestIdSchema } from "@fungi.computer/agar/media-request-id";
5
+ import { mediaLibraryPathSchema } from "@fungi.computer/agar/media-library";
6
+ import { getMediaModel, mediaGenerationSelectionSchema, mediaRequestedImageFormatSchema, mediaVideoSelectionSchema, validateMediaGenerationSelection, validateMediaVideoSelection, MediaSettingsError, readResponseBytes, } from "./index.js";
7
+ export { mediaResultSchema } from "./media-operation-contract.js";
8
+ import { mediaFailureSchema } from "./browser-failure.js";
9
+ import { appMediaBrokerFailureSchema, appMediaGenerationResultSchema, mediaModelsSchema, } from "./app-media-contracts.js";
10
+ /** The App owns its folder; the host owns Computer identity and the output filename. */
11
+ const mediaBindingLibraryPathSchema = z
12
+ .string()
13
+ .refine(Schema.is(mediaLibraryPathSchema), "Select an absolute library folder");
14
+ /** Image settings and path references crossing a scoped MEDIA binding. */
15
+ export const mediaImageBindingInputSchema = z.strictObject({
16
+ kind: z.literal("image"),
17
+ requestId: mediaRequestIdSchema,
18
+ libraryPath: mediaBindingLibraryPathSchema,
19
+ settings: mediaGenerationSelectionSchema
20
+ .omit({ durationSeconds: true })
21
+ .extend({
22
+ steps: z
23
+ .number()
24
+ .int()
25
+ .nonnegative()
26
+ .max(Number.MAX_SAFE_INTEGER)
27
+ .optional(),
28
+ })
29
+ .refine((settings) => getMediaModel(settings.model)?.kind === "image", "Select an image model from listModels"),
30
+ outputFormat: mediaRequestedImageFormatSchema.optional(),
31
+ });
32
+ /** Video settings and path references crossing the same scoped binding. */
33
+ export const mediaVideoBindingInputSchema = z.strictObject({
34
+ kind: z.literal("video"),
35
+ requestId: mediaRequestIdSchema,
36
+ libraryPath: mediaBindingLibraryPathSchema,
37
+ settings: mediaVideoSelectionSchema,
38
+ });
39
+ /** One generation identity, including the App-owned folder captured for replay. */
40
+ export const mediaBindingGenerationSchema = z.discriminatedUnion("kind", [
41
+ mediaImageBindingInputSchema,
42
+ mediaVideoBindingInputSchema,
43
+ ]);
44
+ /** Maintained MEDIA method grammar; guest input carries no principal or Computer. */
45
+ export const mediaBindingCallSchema = z.discriminatedUnion("operation", [
46
+ z.strictObject({ operation: z.literal("models"), input: z.strictObject({}) }),
47
+ z.strictObject({
48
+ operation: z.literal("generate"),
49
+ input: mediaBindingGenerationSchema,
50
+ }),
51
+ ]);
52
+ /** Background-generation reply emitted by the durable service. */
53
+ export const mediaPendingSchema = z.strictObject({
54
+ status: z.literal("pending"),
55
+ retryAfterSeconds: z.number().int().positive(),
56
+ });
57
+ /** Parsing and model refusal stay shared; authorization remains host-owned. */
58
+ export function parseMediaBindingCall(raw) {
59
+ if (new TextEncoder().encode(JSON.stringify(raw)).byteLength > 64 * 1024)
60
+ return { kind: "refused", result: { error: "too_large" } };
61
+ const parsed = mediaBindingCallSchema.safeParse(raw);
62
+ if (!parsed.success)
63
+ return { kind: "refused", result: { error: "invalid" } };
64
+ if (parsed.data.operation === "generate") {
65
+ try {
66
+ if (parsed.data.input.kind === "image")
67
+ validateMediaGenerationSelection(parsed.data.input.settings);
68
+ else
69
+ validateMediaVideoSelection(parsed.data.input.settings);
70
+ }
71
+ catch (cause) {
72
+ if (!(cause instanceof MediaSettingsError))
73
+ throw cause;
74
+ return {
75
+ kind: "refused",
76
+ result: {
77
+ error: "INVALID_ARGUMENT",
78
+ message: cause.message,
79
+ stage: "settings",
80
+ retryable: false,
81
+ },
82
+ };
83
+ }
84
+ }
85
+ return { kind: "parsed", call: parsed.data };
86
+ }
87
+ /** Choose the same fixed filename on both hosts without a recursive App lookup. */
88
+ export function mediaBindingRequest(input, computerId) {
89
+ const selected = input.kind === "image"
90
+ ? validateMediaGenerationSelection(input.settings)
91
+ : validateMediaVideoSelection(input.settings);
92
+ const { references, ...settings } = selected;
93
+ const model = getMediaModel(input.settings.model);
94
+ const format = input.kind === "video"
95
+ ? "mp4"
96
+ : (input.outputFormat ??
97
+ (model?.outputFormats.includes("png")
98
+ ? "png"
99
+ : (model?.outputFormats[0] ?? "png")));
100
+ let attached = settings;
101
+ if (references !== undefined)
102
+ attached = {
103
+ ...settings,
104
+ references: references.map((reference) => ({ ...reference, computerId })),
105
+ };
106
+ const request = {
107
+ path: `${input.libraryPath}/prints-${input.requestId}.${format}`,
108
+ settings: attached,
109
+ };
110
+ if (input.kind === "image" && input.outputFormat !== undefined)
111
+ return { ...request, outputFormat: input.outputFormat };
112
+ return request;
113
+ }
114
+ /** Decode bounded service replies once for both MEDIA host bindings. */
115
+ export async function readMediaBindingReply(input, computerId, response) {
116
+ const bytes = await readResponseBytes(new Response(response.body, { headers: response.headers }), 240 * 1024);
117
+ const value = JSON.parse(new TextDecoder().decode(bytes));
118
+ if (!response.ok) {
119
+ const failure = mediaFailureSchema.safeParse(value);
120
+ if (!failure.success)
121
+ return { error: "unavailable" };
122
+ return z.json().parse(appMediaBrokerFailureSchema.parse({
123
+ error: failure.data.code,
124
+ message: failure.data.error,
125
+ stage: failure.data.stage,
126
+ retryable: failure.data.retryable,
127
+ }));
128
+ }
129
+ if (input.operation === "models")
130
+ return z.json().parse(mediaModelsSchema.parse(value));
131
+ if (response.status === 202) {
132
+ if (input.input.kind === "image")
133
+ return { error: "unavailable" };
134
+ return z
135
+ .json()
136
+ .parse(pending(mediaPendingSchema.parse(value).retryAfterSeconds * 1000));
137
+ }
138
+ const result = appMediaGenerationResultSchema.parse(value);
139
+ if (result.destination.computerId !== computerId ||
140
+ (input.input.kind === "video"
141
+ ? result.format !== "mp4"
142
+ : input.input.outputFormat !== undefined &&
143
+ result.format !== input.input.outputFormat))
144
+ return { error: "unavailable" };
145
+ return z.json().parse(result);
146
+ }
@@ -0,0 +1,10 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { MediaSettingsError } from "./index.js";
3
+ import { MediaExecutionError } from "./execution-error.js";
4
+ import type { MediaServiceHost, MediaGenerationRequest, MediaGenerationOutcome } from "./server.js";
5
+ /**
6
+ * Validate, admit, dispatch and save one generation against `host`. Shared by
7
+ * the `/generate` Hono route and the Agent image tool so both run the exact
8
+ * same admission, billing and destination-write sequence.
9
+ */
10
+ export declare function runMediaGeneration(host: MediaServiceHost, request: MediaGenerationRequest, signal?: AbortSignal): Effect.Effect<MediaGenerationOutcome, MediaExecutionError | MediaSettingsError>;
@@ -0,0 +1,110 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { getMediaModel, validateMediaGenerationSettings, resolveMediaReferences, MediaSettingsError, } from "./index.js";
3
+ import { estimate } from "./cloudflare/provider.js";
4
+ import { executeMediaOperation } from "./media-operation.js";
5
+ import { MediaExecutionError } from "./execution-error.js";
6
+ import { mediaResultSchema } from "./media-operation-contract.js";
7
+ /**
8
+ * Validate, admit, dispatch and save one generation against `host`. Shared by
9
+ * the `/generate` Hono route and the Agent image tool so both run the exact
10
+ * same admission, billing and destination-write sequence.
11
+ */
12
+ export function runMediaGeneration(host, request, signal) {
13
+ return Effect.gen(function* () {
14
+ const settings = yield* Effect.try({
15
+ try: () => validateMediaGenerationSettings(request.settings),
16
+ catch: () => new MediaSettingsError("invalid generation settings"),
17
+ });
18
+ if (request.outputFormat !== undefined &&
19
+ !getMediaModel(settings.model)?.outputFormats.includes(request.outputFormat))
20
+ return yield* Effect.fail(new MediaSettingsError("model cannot produce the requested image format"));
21
+ const path = request.path;
22
+ const beforeAdmit = host.beforeAdmit;
23
+ if (beforeAdmit !== undefined)
24
+ yield* Effect.tryPromise({
25
+ try: () => beforeAdmit(request.requestId),
26
+ catch: (cause) => new MediaExecutionError("generation-authorization", { cause }),
27
+ });
28
+ const execution = {
29
+ operation: host.operations.getByName(`media:${request.requestId}`),
30
+ actorUserId: host.scope.actorId,
31
+ coordinates: {
32
+ namespace: host.scope.namespace,
33
+ owner: host.scope.owner,
34
+ },
35
+ requestId: request.requestId,
36
+ request: {
37
+ ...settings,
38
+ computerId: host.scope.destinationId,
39
+ path,
40
+ outputFormat: request.outputFormat,
41
+ },
42
+ metering: host.metering,
43
+ destination: {
44
+ authorize: () => host.destination.authorize(path),
45
+ write: (bytes) => host.destination.write(path, bytes),
46
+ },
47
+ generation: {
48
+ prepare: async () => {
49
+ const references = await resolveMediaReferences(settings, {
50
+ read: (reference) => {
51
+ if (host.destination.readReference === undefined)
52
+ throw new MediaSettingsError("reference images are unavailable");
53
+ return host.destination.readReference(reference);
54
+ },
55
+ });
56
+ const costMilliunits = estimate(settings, references);
57
+ // Reference preparation may suspend. This final authority check is
58
+ // the new-generation admission cutoff, before durable dispatch.
59
+ try {
60
+ await host.authorizeGeneration?.();
61
+ }
62
+ catch (cause) {
63
+ throw new MediaExecutionError("generation-authorization", {
64
+ cause,
65
+ });
66
+ }
67
+ return {
68
+ costMilliunits,
69
+ references,
70
+ run: (runSignal) => host.provider(settings, references, runSignal, request.outputFormat),
71
+ };
72
+ },
73
+ },
74
+ };
75
+ if (signal !== undefined)
76
+ execution.signal = signal;
77
+ const outcome = yield* executeMediaOperation(execution);
78
+ if (outcome.kind === "conflict")
79
+ return { kind: "conflict" };
80
+ if (outcome.kind === "accepted" ||
81
+ (outcome.kind === "pending" &&
82
+ getMediaModel(settings.model)?.kind === "video"))
83
+ // A video generation runs in the background; retry this request id.
84
+ return {
85
+ kind: "pending",
86
+ background: true,
87
+ retryAfterSeconds: 5,
88
+ };
89
+ if (outcome.kind === "pending")
90
+ return {
91
+ kind: "pending",
92
+ background: false,
93
+ retryAfterSeconds: 5,
94
+ };
95
+ if (outcome.kind === "interrupted")
96
+ return { kind: "interrupted" };
97
+ if (outcome.kind !== "succeeded")
98
+ return { kind: "unavailable" };
99
+ return {
100
+ kind: "succeeded",
101
+ bytes: outcome.bytes,
102
+ costMilliunits: outcome.costMilliunits,
103
+ path,
104
+ format: mediaResultSchema.shape.format.parse(outcome.format),
105
+ height: settings.height,
106
+ model: settings.model,
107
+ width: settings.width,
108
+ };
109
+ });
110
+ }
@@ -0,0 +1,104 @@
1
+ import * as z from "zod";
2
+ import type { MediaOperationOwner, MediaOperationCoordinates, MediaOperationRequest } from "./media-operation-owner.js";
3
+ import type { MediaUnits } from "./gateway-models.js";
4
+ import type { MediaReferenceBytes } from "./index.js";
5
+ /**
6
+ * The host's billing owner for Media. Every call is keyed by the durable
7
+ * operation id and must be idempotent: a lost acknowledgement is retried with
8
+ * the same id and must never admit, hold, or charge twice.
9
+ */
10
+ export type MediaMetering = Readonly<{
11
+ /**
12
+ * Admit one generation and hold its price before any provider dispatch.
13
+ * Throws {@link MediaBillingRefusal} for a definitive refusal. Returns the
14
+ * customer price in milliunits (1 cent = 1,000 milliunits).
15
+ */
16
+ admit: (input: Readonly<{
17
+ /** Durable operation name. */
18
+ operationId: string;
19
+ /** Provider catalog model identifier. */
20
+ model: string;
21
+ /** Billing unit and count: one image, or seconds of video. */
22
+ unit: MediaUnits["unit"];
23
+ /** Number of admitted or delivered units. */
24
+ units: number;
25
+ /** Provider cost of all units together. */
26
+ providerCostMilliunits: number;
27
+ }>) => Promise<Readonly<{
28
+ /** Host-admitted customer price. */
29
+ priceMilliunits: number;
30
+ }>>;
31
+ /** Settle delivered units: all admitted units once saved, 0 when nothing was delivered. */
32
+ settle: (input: Readonly<{
33
+ /** Durable operation name. */
34
+ operationId: string;
35
+ /** Number of admitted or delivered units. */
36
+ units: number;
37
+ }>) => Promise<void>;
38
+ }>;
39
+ /** Local or RPC methods consumed by a replay-safe generation. */
40
+ export type MediaOperation = {
41
+ [Method in "admit" | "claim" | "dispatchInBackground" | "interruptGeneration" | "readGenerated" | "recordEstimate" | "recordGenerated" | "recordSucceeded" | "recordWritten" | "recordWrittenWithoutSettlement" | "releaseEstimate" | "releaseWrite"]: (...input: Parameters<MediaOperationOwner[Method]>) => ReturnType<MediaOperationOwner[Method]> | Promise<Awaited<ReturnType<MediaOperationOwner[Method]>>>;
42
+ };
43
+ /** Admitted identity, durable operation and host effects for one generation. */
44
+ export type MediaExecution = Readonly<{
45
+ /** Durable owner methods for this generation. */
46
+ operation: MediaOperation;
47
+ /** Host-admitted actor identifier. */
48
+ actorUserId: string;
49
+ /** Host namespace and owner identity. */
50
+ coordinates: MediaOperationCoordinates;
51
+ /** Stable caller request identity reused on retries. */
52
+ requestId: string;
53
+ /** Captured generation and fixed destination. */
54
+ request: MediaOperationRequest;
55
+ /** The billing owner, or `unmetered` when the host pays its own provider. */
56
+ metering: MediaMetering | "unmetered";
57
+ /** Optional cancellation signal. */
58
+ signal?: AbortSignal;
59
+ /** Host-owned destination authorization and writing. */
60
+ destination: Readonly<{
61
+ /** Check host permission before writing or dispatching generation. */
62
+ authorize: () => Promise<void>;
63
+ /** Save exact generated bytes to the admitted destination. */
64
+ write: (bytes: Uint8Array) => Promise<void>;
65
+ }>;
66
+ /** Reference preparation and provider dispatch effects. */
67
+ generation: Readonly<{
68
+ /** Resolve references and price before provider dispatch. */
69
+ prepare: () => Promise<Readonly<{
70
+ /** Estimated provider cost in milliunits, where 1 cent is 1,000 milliunits. */
71
+ costMilliunits: number;
72
+ /** Host-authorized reference images. */
73
+ references: readonly MediaReferenceBytes[];
74
+ /** Dispatch one provider call with the prepared inputs. */
75
+ run: (signal?: AbortSignal) => Promise<Readonly<{
76
+ /** Generated output bytes. */
77
+ bytes: Uint8Array;
78
+ /** Actual output encoding. */
79
+ format: string;
80
+ }>>;
81
+ }>>;
82
+ }>;
83
+ }>;
84
+ /** Decode a completed generation, destination path and estimated or settled price. */
85
+ export declare const mediaResultSchema: z.ZodObject<{
86
+ bytes: z.ZodNumber;
87
+ costMilliunits: z.ZodNumber;
88
+ path: z.ZodString;
89
+ format: z.ZodEnum<{
90
+ /** PNG image encoding. */
91
+ readonly png: "png";
92
+ /** JPEG image encoding. */
93
+ readonly jpeg: "jpeg";
94
+ /** GIF image encoding. */
95
+ readonly gif: "gif";
96
+ /** WebP image encoding. */
97
+ readonly webp: "webp";
98
+ /** MP4 video encoding. */
99
+ readonly mp4: "mp4";
100
+ }>;
101
+ height: z.ZodNumber;
102
+ model: z.ZodString;
103
+ width: z.ZodNumber;
104
+ }, z.core.$strict>;