File size: 1,557 Bytes
4e23b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 * `fileService` — process-global upload store. Mirrors
 * `agent-core-v2/app/file/fileService.ts`.
 *
 * The wire cannot carry the engine's streams (every value JSON round-trips),
 * so bytes cross base64-encoded: `save`'s `Readable` source becomes a base64
 * string as the first argument, and `get`'s result stream is buffered
 * server-side and returned as `{ meta, data }` with `data` base64-encoded.
 * The dispatcher performs the stream ⇄ base64 adaptation; the facade encodes
 * and decodes the caller's `Uint8Array`s.
 */

import { z } from 'zod';

import { noResult } from '../helpers.js';
import type { ServiceContract } from '../types.js';

export const fileMetaSchema = z.object({
  id: z.string().min(1),
  name: z.string().min(1),
  media_type: z.string().min(1),
  size: z.number().int().nonnegative(),
  created_at: z.string(),
  expires_at: z.string().optional(),
});

export const fileSaveOptionsSchema = z.object({
  name: z.string().optional(),
  mimeType: z.string().optional(),
  expiresInSec: z.number().optional(),
});

/** Wire result of `get` — the engine's `GetResult.stream` buffered to base64. */
export const fileGetResultSchema = z.object({
  meta: fileMetaSchema,
  data: z.string(),
});

export const filesContract = {
  save: {
    input: z.tuple([z.string(), z.string().min(1), fileSaveOptionsSchema]),
    output: fileMetaSchema,
  },
  get: { input: z.tuple([z.string().min(1)]), output: fileGetResultSchema },
  delete: { input: z.tuple([z.string().min(1)]), output: noResult },
} satisfies ServiceContract;