@fungi.computer/darkroom 0.4.0 → 0.5.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.
package/README.md CHANGED
@@ -113,9 +113,8 @@ save fails. An ambiguous provider execution is never silently dispatched again.
113
113
 
114
114
  Declare `metering: "unmetered"` when your host pays its provider directly. A
115
115
  metered host supplies idempotent `admit` and `settle` functions plus its own
116
- `refusalMessages` for insufficient credit, daily limits and denied spending.
117
- Quotes are estimates. Your metering owner decides the customer price and
118
- settlement.
116
+ `refusalMessages` for insufficient credit and denied spending. Quotes are
117
+ estimates. Your metering owner decides the customer price and settlement.
119
118
 
120
119
  A recognized provider safety refusal returns HTTP 400 with
121
120
  `code: "SAFETY_FILTER_REFUSED"`, `stage: "provider"`, and `retryable: false`.
@@ -3,4 +3,5 @@ export { createWorkersAiMediaProvider, type MediaAiPort, } from "./cloudflare/pr
3
3
  export type { MediaAiResult } from "./index.js";
4
4
  export { createImagesReferenceFitter, createImagesViewFitter, type MediaImagePort, } from "./cloudflare/reference-images.js";
5
5
  export { MediaOperationOwner } from "./media-operation-owner.js";
6
+ export type { MediaShareSource } from "./media-operation-owner.js";
6
7
  export type { MediaOperationContext, MediaOperationStorage, MediaOperationBucket, MediaOperationObject, } from "./media-operation-storage.js";
@@ -51,7 +51,7 @@ export declare class MediaSafetyFilterRefusal extends MediaExecutionError {
51
51
  constructor(options?: MediaExecutionErrorOptions);
52
52
  }
53
53
  /** Why the host's billing owner refused to admit a new generation. */
54
- export type MediaBillingRefusalCode = "insufficient_credits" | "daily_limit_reached" | "denied";
54
+ export type MediaBillingRefusalCode = "insufficient_credits" | "denied";
55
55
  /** A definitive billing refusal before dispatch; nothing was generated or charged. */
56
56
  export declare class MediaBillingRefusal extends Error {
57
57
  /** Bounded refusal or failure reason. */
@@ -20,6 +20,8 @@ export declare class MediaOperationDO extends DurableObject<MediaOperationCloudf
20
20
  protected mediaProvider(): MediaProvider | undefined;
21
21
  /** {@inheritDoc MediaOperationOwner.read} */
22
22
  read(...input: Parameters<MediaOperationOwner["read"]>): ReturnType<MediaOperationOwner["read"]>;
23
+ /** {@inheritDoc MediaOperationOwner.readShare} */
24
+ readShare(...input: Parameters<MediaOperationOwner["readShare"]>): ReturnType<MediaOperationOwner["readShare"]>;
23
25
  /** {@inheritDoc MediaOperationOwner.preflightErasure} */
24
26
  preflightErasure(...input: Parameters<MediaOperationOwner["preflightErasure"]>): ReturnType<MediaOperationOwner["preflightErasure"]>;
25
27
  /** {@inheritDoc MediaOperationOwner.erase} */
@@ -38,6 +38,10 @@ export class MediaOperationDO extends DurableObject {
38
38
  read(...input) {
39
39
  return this.owner.read(...input);
40
40
  }
41
+ /** {@inheritDoc MediaOperationOwner.readShare} */
42
+ readShare(...input) {
43
+ return this.owner.readShare(...input);
44
+ }
41
45
  /** {@inheritDoc MediaOperationOwner.preflightErasure} */
42
46
  preflightErasure(...input) {
43
47
  return this.owner.preflightErasure(...input);
@@ -94,6 +94,31 @@ export type MediaOperationProjection = Readonly<{
94
94
  /** Last transition timestamp in milliseconds since the epoch. */
95
95
  updatedAt: number;
96
96
  }>;
97
+ /** What a host needs to serve one shared output: never references or receipts. */
98
+ export type MediaShareSource = Readonly<{
99
+ /** Retry identity of the generation; a Print's id. */
100
+ requestId: string;
101
+ /** The person whose request generated it. */
102
+ actorUserId: string;
103
+ /** Retained exact provider bytes in the host's artifacts bucket. */
104
+ outputKey: string;
105
+ /** Media type of the retained bytes. */
106
+ contentType: string;
107
+ /** Length of the retained bytes. */
108
+ bytes: number;
109
+ /** Whether the output is a still image or a video. */
110
+ kind: "image" | "video";
111
+ /** The generation prompt, as sent to the model. */
112
+ prompt: string;
113
+ /** Catalog model identifier. */
114
+ model: string;
115
+ /** Requested width in pixels. */
116
+ width: number;
117
+ /** Requested height in pixels. */
118
+ height: number;
119
+ /** Video length in whole seconds; null for an image. */
120
+ durationSeconds: number | null;
121
+ }>;
97
122
  /** An admission or claim decision with a safe operation projection. */
98
123
  export type MediaOperationDecision = Readonly<{
99
124
  /** Admission or claim outcome. */
@@ -160,6 +185,13 @@ export declare class MediaOperationOwner {
160
185
  private readRecord;
161
186
  /** Read the safe persisted operation projection, or null before admission or after erasure. */
162
187
  read(): MediaOperationProjection | null;
188
+ /**
189
+ * A settled operation's retained output and recipe, for a host that lets
190
+ * its owner share it publicly. Null before success or for another namespace,
191
+ * so a share can never reach another Team's output. References are never
192
+ * included.
193
+ */
194
+ readShare(namespace: string): MediaShareSource | null;
163
195
  /** Check host namespace, request identity and inactivity before erasure. */
164
196
  preflightErasure(namespace: string, requestId: string): MediaErasurePreflight;
165
197
  /** Erase inactive output and local storage, then retain the durable erasure marker. */
@@ -348,6 +348,32 @@ export class MediaOperationOwner {
348
348
  const record = this.readRecord();
349
349
  return record === null ? null : this.project(record);
350
350
  }
351
+ /**
352
+ * A settled operation's retained output and recipe, for a host that lets
353
+ * its owner share it publicly. Null before success or for another namespace,
354
+ * so a share can never reach another Team's output. References are never
355
+ * included.
356
+ */
357
+ readShare(namespace) {
358
+ const record = this.readRecord();
359
+ if (record?.namespace !== namespace ||
360
+ record.state !== "succeeded" ||
361
+ record.output === null)
362
+ return null;
363
+ return Object.freeze({
364
+ requestId: record.requestId,
365
+ actorUserId: record.actorUserId,
366
+ outputKey: record.output.key,
367
+ contentType: outputMime(record.output.format),
368
+ bytes: record.output.bytes,
369
+ kind: record.output.format === "mp4" ? "video" : "image",
370
+ prompt: record.input.prompt,
371
+ model: record.input.model,
372
+ width: record.input.width,
373
+ height: record.input.height,
374
+ durationSeconds: record.input.durationSeconds ?? null,
375
+ });
376
+ }
351
377
  /** Check host namespace, request identity and inactivity before erasure. */
352
378
  preflightErasure(namespace, requestId) {
353
379
  if (this.ctx.id.name !== `media:${requestId}`)
@@ -110,6 +110,43 @@ erasure.
110
110
  `string`; `bytes`: `number`; `format`: `string`; `costMilliunits`: `number`; \}
111
111
  \| `null`; `createdAt`: `number`; `updatedAt`: `number`; \}\> \| `null`
112
112
 
113
+ ##### readShare()
114
+
115
+ ```ts
116
+ readShare(...input):
117
+ | Readonly<{
118
+ requestId: string;
119
+ actorUserId: string;
120
+ outputKey: string;
121
+ contentType: string;
122
+ bytes: number;
123
+ kind: "image" | "video";
124
+ prompt: string;
125
+ model: string;
126
+ width: number;
127
+ height: number;
128
+ durationSeconds: number | null;
129
+ }>
130
+ | null;
131
+ ```
132
+
133
+ A settled operation's retained output and recipe, for a host that lets its owner
134
+ share it publicly. Null before success or for another namespace, so a share can
135
+ never reach another Team's output. References are never included.
136
+
137
+ ###### Parameters
138
+
139
+ | Parameter | Type | Description |
140
+ | ---------- | ------------ | ----------- |
141
+ | ...`input` | \[`string`\] | - |
142
+
143
+ ###### Returns
144
+
145
+ \| `Readonly`\<\{ `requestId`: `string`; `actorUserId`: `string`; `outputKey`:
146
+ `string`; `contentType`: `string`; `bytes`: `number`; `kind`: `"image"` \|
147
+ `"video"`; `prompt`: `string`; `model`: `string`; `width`: `number`; `height`:
148
+ `number`; `durationSeconds`: `number` \| `null`; \}\> \| `null`
149
+
113
150
  ##### preflightErasure()
114
151
 
115
152
  ```ts
@@ -572,6 +609,28 @@ type MediaOperationCloudflareEnvironment = MediaOperationEnvironment &
572
609
 
573
610
  Retention storage and native background provider bindings for the operation DO.
574
611
 
612
+ ---
613
+
614
+ ### MediaShareSource
615
+
616
+ ```ts
617
+ type MediaShareSource = Readonly<{
618
+ requestId: string;
619
+ actorUserId: string;
620
+ outputKey: string;
621
+ contentType: string;
622
+ bytes: number;
623
+ kind: "image" | "video";
624
+ prompt: string;
625
+ model: string;
626
+ width: number;
627
+ height: number;
628
+ durationSeconds: number | null;
629
+ }>;
630
+ ```
631
+
632
+ What a host needs to serve one shared output: never references or receipts.
633
+
575
634
  ## Functions
576
635
 
577
636
  ### createWorkersAiMediaProvider()
@@ -207,6 +207,43 @@ erasure.
207
207
  `string`; `bytes`: `number`; `format`: `string`; `costMilliunits`: `number`; \}
208
208
  \| `null`; `createdAt`: `number`; `updatedAt`: `number`; \}\> \| `null`
209
209
 
210
+ ##### readShare()
211
+
212
+ ```ts
213
+ readShare(namespace):
214
+ | Readonly<{
215
+ requestId: string;
216
+ actorUserId: string;
217
+ outputKey: string;
218
+ contentType: string;
219
+ bytes: number;
220
+ kind: "image" | "video";
221
+ prompt: string;
222
+ model: string;
223
+ width: number;
224
+ height: number;
225
+ durationSeconds: number | null;
226
+ }>
227
+ | null;
228
+ ```
229
+
230
+ A settled operation's retained output and recipe, for a host that lets its owner
231
+ share it publicly. Null before success or for another namespace, so a share can
232
+ never reach another Team's output. References are never included.
233
+
234
+ ###### Parameters
235
+
236
+ | Parameter | Type |
237
+ | ----------- | -------- |
238
+ | `namespace` | `string` |
239
+
240
+ ###### Returns
241
+
242
+ \| `Readonly`\<\{ `requestId`: `string`; `actorUserId`: `string`; `outputKey`:
243
+ `string`; `contentType`: `string`; `bytes`: `number`; `kind`: `"image"` \|
244
+ `"video"`; `prompt`: `string`; `model`: `string`; `width`: `number`; `height`:
245
+ `number`; `durationSeconds`: `number` \| `null`; \}\> \| `null`
246
+
210
247
  ##### preflightErasure()
211
248
 
212
249
  ```ts
@@ -798,8 +835,7 @@ Private cause plus an independently decoded, safe provider diagnostic.
798
835
  ### MediaBillingRefusalCode
799
836
 
800
837
  ```ts
801
- type MediaBillingRefusalCode =
802
- "insufficient_credits" | "daily_limit_reached" | "denied";
838
+ type MediaBillingRefusalCode = "insufficient_credits" | "denied";
803
839
  ```
804
840
 
805
841
  Why the host's billing owner refused to admit a new generation.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/darkroom",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "description": "Media generation, durable output retention and provenance with host-owned authority and billing",
6
6
  "type": "module",
@@ -59,7 +59,7 @@
59
59
  "exifreader": "4.46.0",
60
60
  "@fungi.computer/ts-webp": "0.1.6",
61
61
  "@xmldom/xmldom": "0.9.12",
62
- "@fungi.computer/agar": "0.6.0",
62
+ "@fungi.computer/agar": "0.7.0",
63
63
  "@fungi.computer/capnweb": "0.12.0"
64
64
  },
65
65
  "devDependencies": {