@deepseek-ai/dsh-client-file-upload 0.1.3-alpha.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.
@@ -0,0 +1,65 @@
1
+ /* Generated by @deepseek-ai/dsh-typert-generator from the Host FaceModel — do not edit. */
2
+ import { z } from 'zod'
3
+
4
+ const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema = z.intersection(z.string(), z.unknown())
5
+ const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema = z.object({
6
+ 'data': z.string().readonly(),
7
+ 'name': z.string().readonly().optional(),
8
+ })
9
+ const _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema = z.object({
10
+ 'receiptId': z.intersection(z.string(), z.unknown()).readonly(),
11
+ 'file': z.object({
12
+ 'attachmentId': z.intersection(z.string(), z.unknown()),
13
+ 'name': z.string(),
14
+ 'bytes': z.number(),
15
+ }).readonly(),
16
+ })
17
+
18
+ export const TYPERT_REMOTE = {
19
+ package: '@deepseek-ai/dsh-client-file-upload',
20
+ descriptors: [
21
+ {
22
+ id: '@deepseek-ai/dsh-client-file-upload#fileUploads/upload',
23
+ service: 'fileUploads',
24
+ namespace: 'fileUploads',
25
+ method: 'upload',
26
+ invocation: { kind: 'direct' },
27
+ scope: {
28
+ context: 'agent',
29
+ wire: 'agentId',
30
+ },
31
+ parameters: [
32
+ {
33
+ name: 'agent',
34
+ wire: 'agentId',
35
+ source: 'lookup',
36
+ lookup: 'agent',
37
+ codec: {
38
+ mode: 'strict',
39
+ typeSymbol: '@deepseek-ai/dsh-session/types#SessionId',
40
+ schema: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_0$schema,
41
+ },
42
+ },
43
+ {
44
+ name: 'request',
45
+ wire: 'request',
46
+ source: 'json',
47
+ codec: {
48
+ mode: 'strict',
49
+ typeSymbol: '@deepseek-ai/dsh-client-file-upload/types#EncodedFileUploadRequest',
50
+ schema: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_parameter_1$schema,
51
+ },
52
+ },
53
+ ],
54
+ cancellation: { parameter: 'signal' },
55
+ result: {
56
+ mode: 'strict',
57
+ typeSymbol: '@deepseek-ai/dsh-client-file-upload/types#FileUploadValue',
58
+ schema: _deepseek_ai_dsh_client_file_upload_fileUploads_upload_result$schema,
59
+ },
60
+ sourceLocation: {"file":"packages/client/file-upload/src/index.ts","line":106,"column":3},
61
+ },
62
+ ],
63
+ }
64
+
65
+ export default TYPERT_REMOTE
@@ -0,0 +1,27 @@
1
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
2
+ import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
3
+ import type { FileUploadValue } from '../types.ts';
4
+ /** Browser request body accepted by the background file-upload service. */
5
+ export type FileUploadBody = Blob | ReadableStream<Uint8Array>;
6
+ /** Monotone byte progress reported while a browser body is consumed. */
7
+ export interface FileUploadProgress {
8
+ readonly loaded: number;
9
+ readonly total?: number;
10
+ }
11
+ /** Browser upload service addressed by one Session identity. */
12
+ export interface FileUploadService {
13
+ /** Whether this page has a Host-backed background upload carrier. */
14
+ readonly available: boolean;
15
+ /**
16
+ * Store one file for a Session. Blob and stream bodies use
17
+ * the background carrier; exact bytes and fixture fallbacks use Remote.
18
+ * @param sessionId - Session that owns the staged receipt.
19
+ * @param data - browser Blob, exact bytes, or a one-shot byte stream.
20
+ * @param name - optional display name.
21
+ * @param signal - optional cancellation for the active upload.
22
+ * @param onProgress - optional byte-progress observer for background bodies.
23
+ * @returns the staged receipt and durable file reference, or a business error.
24
+ */
25
+ upload(sessionId: SessionId, data: Blob | Uint8Array | ReadableStream<Uint8Array>, name?: string, signal?: AbortSignal, onProgress?: (progress: FileUploadProgress) => void): Promise<RemoteResult<FileUploadValue>>;
26
+ }
27
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1,19 @@
1
+ /** Browser background-upload Cordis service. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import type { FileUploadService } from './contract.ts';
4
+ export type { FileUploadProgress, FileUploadService } from './contract.ts';
5
+ export type { FileUploadReceiptId, FileUploadValue } from '../types.ts';
6
+ declare module '@deepseek-ai/cordis' {
7
+ interface Context {
8
+ /** Session-addressed browser service for staged file uploads. */
9
+ fileUpload: FileUploadService;
10
+ }
11
+ }
12
+ /** The upload service uses the generated Remote fallback. */
13
+ export declare const inject: string[];
14
+ /**
15
+ * Provide the browser background-upload service.
16
+ * @param ctx - Client plugin context.
17
+ */
18
+ export declare function apply(ctx: Context): void;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,12 @@
1
+ /** Browser background-upload Cordis service. */
2
+ import { FileUploadRuntime } from "./runtime.js";
3
+ /** The upload service uses the generated Remote fallback. */
4
+ export const inject = ['remote'];
5
+ /**
6
+ * Provide the browser background-upload service.
7
+ * @param ctx - Client plugin context.
8
+ */
9
+ export function apply(ctx) {
10
+ ctx.plugin(FileUploadRuntime);
11
+ }
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,92 @@
1
+ /** Background browser upload implementation for Blob and byte-stream bodies. */
2
+ import { Service, type Context } from '@deepseek-ai/cordis';
3
+ import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
4
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
5
+ import type { FileUploadValue } from '../types.ts';
6
+ import type { FileUploadBody, FileUploadService } from './contract.ts';
7
+ interface FileUploadRequest {
8
+ readonly path: string;
9
+ readonly body: FileUploadBody;
10
+ readonly headers?: Readonly<Record<string, string>>;
11
+ readonly signal?: AbortSignal;
12
+ readonly onProgress?: (progress: {
13
+ readonly loaded: number;
14
+ readonly total?: number;
15
+ }) => void;
16
+ }
17
+ interface FileUploadResponse {
18
+ readonly status: number;
19
+ readonly body: string;
20
+ }
21
+ interface UploadWorkerStart {
22
+ readonly url: string;
23
+ readonly body: FileUploadBody;
24
+ readonly headers: Readonly<Record<string, string>>;
25
+ }
26
+ type UploadWorkerOutput = {
27
+ readonly kind: 'progress';
28
+ readonly loaded: number;
29
+ readonly total?: number;
30
+ } | {
31
+ readonly kind: 'complete';
32
+ readonly status: number;
33
+ readonly body: string;
34
+ } | {
35
+ readonly kind: 'error';
36
+ readonly message: string;
37
+ };
38
+ interface UploadWorkerScope {
39
+ onmessage: ((event: MessageEvent<UploadWorkerStart>) => void) | null;
40
+ postMessage(message: UploadWorkerOutput): void;
41
+ }
42
+ interface UploadXhr {
43
+ readonly upload: {
44
+ onprogress: ((event: ProgressEvent) => void) | null;
45
+ };
46
+ status: number;
47
+ responseText: string;
48
+ withCredentials: boolean;
49
+ onload: ((event: ProgressEvent) => void) | null;
50
+ onerror: ((event: ProgressEvent) => void) | null;
51
+ open(method: string, url: string): void;
52
+ setRequestHeader(name: string, value: string): void;
53
+ send(body: Blob): void;
54
+ }
55
+ type UploadWorkerFetch = (input: string, init: RequestInit & {
56
+ readonly duplex: 'half';
57
+ }) => Promise<Response>;
58
+ /**
59
+ * Self-contained Worker body; its string form becomes the Blob Worker source.
60
+ * @param scope - Worker global used for requests and progress messages.
61
+ * @param createXhr - XMLHttpRequest factory used for Blob progress.
62
+ * @param doFetch - Fetch carrier used for one-shot ReadableStream bodies.
63
+ */
64
+ export declare function fileUploadWorker(scope?: UploadWorkerScope, createXhr?: () => UploadXhr, doFetch?: UploadWorkerFetch): void;
65
+ /** Cordis service that owns one background carrier per upload operation. */
66
+ export declare class FileUploadRuntime extends Service implements FileUploadService {
67
+ readonly available: boolean;
68
+ private readonly transport;
69
+ /** @param ctx - providing Client context. */
70
+ constructor(ctx: Context);
71
+ /**
72
+ * Post one body with the carrier selected before Cordis boot.
73
+ * @param request - target, body, cancellation, and progress observer.
74
+ * @returns the response status and text body.
75
+ */
76
+ post(request: FileUploadRequest): Promise<FileUploadResponse>;
77
+ /**
78
+ * Store one file for a Session.
79
+ * @param sessionId - Session that owns the staged receipt.
80
+ * @param data - browser Blob, exact bytes, or a one-shot byte stream.
81
+ * @param name - optional display name.
82
+ * @param signal - optional cancellation for the active upload.
83
+ * @param onProgress - optional byte-progress observer for background bodies.
84
+ * @returns the staged receipt and durable file reference, or a business error.
85
+ */
86
+ upload(sessionId: SessionId, data: Blob | Uint8Array | ReadableStream<Uint8Array>, name?: string, signal?: AbortSignal, onProgress?: (progress: {
87
+ readonly loaded: number;
88
+ readonly total?: number;
89
+ }) => void): Promise<RemoteResult<FileUploadValue>>;
90
+ }
91
+ export {};
92
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1,270 @@
1
+ /** Background browser upload implementation for Blob and byte-stream bodies. */
2
+ import { Service } from '@deepseek-ai/cordis';
3
+ import { bytesToBase64 } from '@deepseek-ai/dsh-util-crypto';
4
+ import { RemoteError } from '@deepseek-ai/dsh-typert-protocol';
5
+ import { FILE_UPLOAD_PATH } from "../protocol.js";
6
+ /**
7
+ * Self-contained Worker body; its string form becomes the Blob Worker source.
8
+ * @param scope - Worker global used for requests and progress messages.
9
+ * @param createXhr - XMLHttpRequest factory used for Blob progress.
10
+ * @param doFetch - Fetch carrier used for one-shot ReadableStream bodies.
11
+ */
12
+ export function fileUploadWorker(scope = self, createXhr = () => new XMLHttpRequest(), doFetch = (input, init) => fetch(input, init)) {
13
+ scope.onmessage = (event) => {
14
+ const request = event.data;
15
+ if (request.body instanceof Blob) {
16
+ const xhr = createXhr();
17
+ xhr.open('POST', request.url);
18
+ xhr.withCredentials = true;
19
+ for (const [name, value] of Object.entries(request.headers))
20
+ xhr.setRequestHeader(name, value);
21
+ xhr.upload.onprogress = (progress) => {
22
+ scope.postMessage({
23
+ kind: 'progress',
24
+ loaded: progress.loaded,
25
+ ...(progress.lengthComputable ? { total: progress.total } : {}),
26
+ });
27
+ };
28
+ xhr.onload = () => {
29
+ scope.postMessage({ kind: 'complete', status: xhr.status, body: xhr.responseText });
30
+ };
31
+ xhr.onerror = () => {
32
+ scope.postMessage({ kind: 'error', message: 'background upload transport failed' });
33
+ };
34
+ xhr.send(request.body);
35
+ return;
36
+ }
37
+ if (!(request.body instanceof ReadableStream)) {
38
+ scope.postMessage({ kind: 'error', message: 'background upload worker received an invalid body' });
39
+ return;
40
+ }
41
+ const source = request.body;
42
+ void (async () => {
43
+ const reader = source.getReader();
44
+ let loaded = 0;
45
+ const body = new ReadableStream({
46
+ async pull(controller) {
47
+ const item = await reader.read();
48
+ if (item.done) {
49
+ controller.close();
50
+ return;
51
+ }
52
+ if (!(item.value instanceof Uint8Array)) {
53
+ throw new TypeError('background upload stream produced a non-Uint8Array chunk');
54
+ }
55
+ loaded += item.value.byteLength;
56
+ scope.postMessage({ kind: 'progress', loaded });
57
+ controller.enqueue(item.value);
58
+ },
59
+ async cancel(reason) {
60
+ await reader.cancel(reason);
61
+ },
62
+ });
63
+ const response = await doFetch(request.url, {
64
+ method: 'POST',
65
+ headers: request.headers,
66
+ credentials: 'include',
67
+ body,
68
+ duplex: 'half',
69
+ });
70
+ scope.postMessage({
71
+ kind: 'complete',
72
+ status: response.status,
73
+ body: await response.text(),
74
+ });
75
+ })().catch((error) => {
76
+ scope.postMessage({
77
+ kind: 'error',
78
+ message: error instanceof Error ? error.message : String(error),
79
+ });
80
+ });
81
+ };
82
+ }
83
+ /** Cordis service that owns one background carrier per upload operation. */
84
+ export class FileUploadRuntime extends Service {
85
+ available;
86
+ transport;
87
+ /** @param ctx - providing Client context. */
88
+ constructor(ctx) {
89
+ super(ctx, 'fileUpload');
90
+ const hook = globalThis.__DSH_FILE_UPLOAD__;
91
+ this.available = hook !== undefined || !isFixturePage();
92
+ this.transport = hook === undefined ? workerTransport() : customTransport(hook.fetch);
93
+ }
94
+ /**
95
+ * Post one body with the carrier selected before Cordis boot.
96
+ * @param request - target, body, cancellation, and progress observer.
97
+ * @returns the response status and text body.
98
+ */
99
+ post(request) {
100
+ if (!this.available)
101
+ return Promise.reject(new Error('background upload is unavailable in fixture mode'));
102
+ return this.transport.post(request);
103
+ }
104
+ /**
105
+ * Store one file for a Session.
106
+ * @param sessionId - Session that owns the staged receipt.
107
+ * @param data - browser Blob, exact bytes, or a one-shot byte stream.
108
+ * @param name - optional display name.
109
+ * @param signal - optional cancellation for the active upload.
110
+ * @param onProgress - optional byte-progress observer for background bodies.
111
+ * @returns the staged receipt and durable file reference, or a business error.
112
+ */
113
+ async upload(sessionId, data, name, signal, onProgress) {
114
+ if (!(data instanceof Uint8Array) && this.available) {
115
+ const query = new URLSearchParams({ sessionId });
116
+ if (name !== undefined)
117
+ query.set('name', name);
118
+ const response = await this.post({
119
+ path: `${FILE_UPLOAD_PATH}?${query.toString()}`,
120
+ body: data,
121
+ headers: { 'content-type': 'application/octet-stream' },
122
+ ...(signal === undefined ? {} : { signal }),
123
+ ...(onProgress === undefined ? {} : { onProgress }),
124
+ });
125
+ if (response.status !== 200) {
126
+ throw new Error(`file upload transport failed with HTTP ${String(response.status)}`);
127
+ }
128
+ return parseFileUploadResult(response.body);
129
+ }
130
+ if (!(data instanceof Uint8Array) && !(data instanceof Blob)) {
131
+ throw new Error('stream file upload requires a background carrier');
132
+ }
133
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(await data.arrayBuffer());
134
+ return this.ctx.remote.fileUploads.upload(sessionId, {
135
+ data: bytesToBase64(bytes),
136
+ ...(name === undefined ? {} : { name }),
137
+ }, signal);
138
+ }
139
+ }
140
+ function customTransport(customFetch) {
141
+ return {
142
+ async post(request) {
143
+ const init = {
144
+ method: 'POST',
145
+ ...(request.headers === undefined ? {} : { headers: request.headers }),
146
+ body: request.body,
147
+ ...(request.body instanceof ReadableStream ? { duplex: 'half' } : {}),
148
+ ...(request.signal === undefined ? {} : { signal: request.signal }),
149
+ };
150
+ const response = await customFetch(resolveUrl(request.path), init);
151
+ return { status: response.status, body: await response.text() };
152
+ },
153
+ };
154
+ }
155
+ function workerTransport() {
156
+ return {
157
+ post(request) {
158
+ if (typeof Worker !== 'function') {
159
+ return Promise.reject(new Error('background upload requires Web Worker support'));
160
+ }
161
+ const workerUrl = URL.createObjectURL(new Blob([
162
+ `(${fileUploadWorker.toString()})()`,
163
+ ], { type: 'text/javascript' }));
164
+ const worker = new Worker(workerUrl, { name: 'dsh-file-upload' });
165
+ URL.revokeObjectURL(workerUrl);
166
+ return new Promise((resolve, reject) => {
167
+ let settled = false;
168
+ const abort = () => {
169
+ settled = true;
170
+ worker.terminate();
171
+ request.signal?.removeEventListener('abort', abort);
172
+ reject(new DOMException('The operation was aborted.', 'AbortError'));
173
+ };
174
+ const finish = (settle) => {
175
+ if (settled)
176
+ return;
177
+ settled = true;
178
+ request.signal?.removeEventListener('abort', abort);
179
+ worker.terminate();
180
+ settle();
181
+ };
182
+ worker.onmessage = (event) => {
183
+ const output = event.data;
184
+ if (output.kind === 'progress') {
185
+ request.onProgress?.({
186
+ loaded: output.loaded,
187
+ ...(output.total === undefined ? {} : { total: output.total }),
188
+ });
189
+ }
190
+ else if (output.kind === 'complete') {
191
+ finish(() => { resolve({ status: output.status, body: output.body }); });
192
+ }
193
+ else {
194
+ finish(() => { reject(new Error(output.message)); });
195
+ }
196
+ };
197
+ worker.onerror = (event) => {
198
+ finish(() => { reject(new Error(event.message || 'background upload worker failed')); });
199
+ };
200
+ if (request.signal?.aborted === true) {
201
+ abort();
202
+ return;
203
+ }
204
+ request.signal?.addEventListener('abort', abort, { once: true });
205
+ const message = {
206
+ url: resolveUrl(request.path).href,
207
+ body: request.body,
208
+ headers: request.headers ?? {},
209
+ };
210
+ if (request.body instanceof ReadableStream)
211
+ worker.postMessage(message, [request.body]);
212
+ else
213
+ worker.postMessage(message);
214
+ });
215
+ },
216
+ };
217
+ }
218
+ function resolveUrl(path) {
219
+ const pageLocation = Reflect.get(globalThis, 'location');
220
+ const origin = typeof pageLocation === 'object' && pageLocation !== null
221
+ && 'origin' in pageLocation && typeof pageLocation.origin === 'string'
222
+ ? pageLocation.origin
223
+ : undefined;
224
+ return new URL(path, origin === undefined || origin === 'null' ? 'http://dsh.internal' : origin);
225
+ }
226
+ function isFixturePage() {
227
+ const pageLocation = Reflect.get(globalThis, 'location');
228
+ return typeof pageLocation === 'object' && pageLocation !== null
229
+ && 'search' in pageLocation && typeof pageLocation.search === 'string'
230
+ && new URLSearchParams(pageLocation.search).has('fixture');
231
+ }
232
+ function parseFileUploadResult(body) {
233
+ const value = JSON.parse(body);
234
+ if (!isRecord(value) || typeof value.ok !== 'boolean') {
235
+ throw new TypeError('file upload transport returned an invalid result');
236
+ }
237
+ if (!value.ok) {
238
+ const error = value.error;
239
+ if (!isRecord(error) || typeof error.code !== 'string'
240
+ || typeof error.message !== 'string' || !isRecord(error.details)) {
241
+ throw new TypeError('file upload transport returned an invalid failure');
242
+ }
243
+ return {
244
+ ok: false,
245
+ error: new RemoteError(error.code, error.message, error.details),
246
+ };
247
+ }
248
+ const result = value.value;
249
+ const file = isRecord(result) ? result.file : undefined;
250
+ if (!isRecord(result) || typeof result.receiptId !== 'string' || !isRecord(file)
251
+ || typeof file.attachmentId !== 'string' || typeof file.name !== 'string'
252
+ || typeof file.bytes !== 'number' || !Number.isSafeInteger(file.bytes) || file.bytes < 0) {
253
+ throw new TypeError('file upload transport returned an invalid receipt');
254
+ }
255
+ return {
256
+ ok: true,
257
+ value: {
258
+ receiptId: result.receiptId,
259
+ file: {
260
+ attachmentId: file.attachmentId,
261
+ name: file.name,
262
+ bytes: file.bytes,
263
+ },
264
+ },
265
+ };
266
+ }
267
+ function isRecord(value) {
268
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
269
+ }
270
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1,10 @@
1
+ /** Authenticated raw-byte upload route registered on the Connection fetch registry. */
2
+ import type { FileUploads } from './index.ts';
3
+ /**
4
+ * Handle one authenticated raw-byte upload.
5
+ * @param service - Host upload service receiving streamed bytes.
6
+ * @param request - authenticated HTTP request from Connection.
7
+ * @returns JSON result using HTTP status 200 after request validation.
8
+ */
9
+ export declare function handleFileUploadHttp(service: FileUploads, request: Request): Promise<Response>;
10
+ //# sourceMappingURL=http-route.d.ts.map
@@ -0,0 +1,73 @@
1
+ /** Authenticated raw-byte upload route registered on the Connection fetch registry. */
2
+ import { brandString } from '@deepseek-ai/dsh-brand';
3
+ import { remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol';
4
+ /**
5
+ * Handle one authenticated raw-byte upload.
6
+ * @param service - Host upload service receiving streamed bytes.
7
+ * @param request - authenticated HTTP request from Connection.
8
+ * @returns JSON result using HTTP status 200 after request validation.
9
+ */
10
+ export async function handleFileUploadHttp(service, request) {
11
+ if (request.method !== 'POST') {
12
+ return new Response(null, { status: 405, headers: { allow: 'POST' } });
13
+ }
14
+ const mediaType = request.headers.get('content-type')?.split(';', 1)[0]?.trim().toLowerCase();
15
+ if (mediaType !== 'application/octet-stream') {
16
+ return new Response('content type must be application/octet-stream', { status: 415 });
17
+ }
18
+ const url = new URL(request.url);
19
+ const sessionId = url.searchParams.get('sessionId');
20
+ if (sessionId === null || sessionId === '') {
21
+ return new Response('sessionId is required', { status: 400 });
22
+ }
23
+ const name = url.searchParams.get('name') ?? undefined;
24
+ let result;
25
+ try {
26
+ result = {
27
+ ok: true,
28
+ value: await service.uploadStream({
29
+ sessionId: brandString(sessionId),
30
+ data: requestBodyChunks(request.body),
31
+ signal: request.signal,
32
+ ...(name === undefined ? {} : { name }),
33
+ }),
34
+ };
35
+ }
36
+ catch (error) {
37
+ const failure = remoteErrorOf(error);
38
+ result = {
39
+ ok: false,
40
+ error: failure !== undefined
41
+ ? { code: failure.code, message: failure.message, details: failure.details }
42
+ : {
43
+ code: 'gateway/internal',
44
+ message: error instanceof Error ? error.message : String(error),
45
+ details: {},
46
+ },
47
+ };
48
+ }
49
+ return new Response(JSON.stringify(result), {
50
+ status: 200,
51
+ headers: {
52
+ 'content-type': 'application/json; charset=utf-8',
53
+ 'cache-control': 'no-store',
54
+ },
55
+ });
56
+ }
57
+ async function* requestBodyChunks(body) {
58
+ if (body === null)
59
+ return;
60
+ const reader = body.getReader();
61
+ try {
62
+ while (true) {
63
+ const chunk = await reader.read();
64
+ if (chunk.done)
65
+ return;
66
+ yield chunk.value;
67
+ }
68
+ }
69
+ finally {
70
+ reader.releaseLock();
71
+ }
72
+ }
73
+ //# sourceMappingURL=http-route.js.map
@@ -0,0 +1,84 @@
1
+ /** Host file-upload service: streamed intake and Agent-scoped staged receipts. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import type { Agent } from '@deepseek-ai/dsh-agent';
4
+ import type { FileAttachmentRef } from '@deepseek-ai/dsh-attachment';
5
+ import type { SessionId } from '@deepseek-ai/dsh-session';
6
+ import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
7
+ import type { EncodedFileUploadRequest, FileUploadReceiptId, FileUploadValue } from './types.ts';
8
+ export type * from './types.ts';
9
+ declare module '@deepseek-ai/cordis' {
10
+ interface Context {
11
+ /** Host storage and staged-receipt service for browser file uploads. */
12
+ fileUploads: FileUploads;
13
+ }
14
+ }
15
+ /** Resolve or resume the ordinary Agent that owns one Session identity. */
16
+ export type AgentResolver = (sessionId: SessionId) => Promise<Agent>;
17
+ /** Prompt receipt binding that restores its previous owners unless delivery commits it. */
18
+ export interface PromptFileBinding extends Disposable {
19
+ /** Keep the receipt bindings until queue or history observation retires them. */
20
+ commit(): void;
21
+ }
22
+ /** Host service owning upload storage and Agent-scoped staged receipts. */
23
+ export declare class FileUploads extends TypertRemoteService {
24
+ static inject: string[];
25
+ private readonly stagedFiles;
26
+ private agentResolver;
27
+ /** @param ctx - Host context carrying Agent, attachment, command, and Connection services. */
28
+ constructor(ctx: Context);
29
+ /**
30
+ * Register the ordinary-Session resolver used when a raw upload addresses a cold Session.
31
+ * @param resolve - resolver that returns the exact live Agent or throws a Remote error.
32
+ * @returns disposer removing this resolver.
33
+ */
34
+ registerAgentResolver(resolve: AgentResolver): () => void;
35
+ /**
36
+ * Persist one encoded upload and stage it under the Agent receiver selected by Typert.
37
+ * @param agent - receiving Agent resolved from the Remote Agent scope.
38
+ * @param request - canonical base64 bytes and optional display name.
39
+ * @param signal - caller cancellation before storage begins.
40
+ * @returns the staged receipt and durable file reference.
41
+ */
42
+ upload(agent: Agent, request: EncodedFileUploadRequest, signal: AbortSignal): Promise<FileUploadValue>;
43
+ /**
44
+ * Persist raw chunks for one Session without aggregating the upload.
45
+ * @param request - Session identity, ordered bytes, cancellation, and optional display name.
46
+ * @returns the staged receipt and durable file reference.
47
+ */
48
+ uploadStream(request: {
49
+ readonly sessionId: SessionId;
50
+ readonly data: AsyncIterable<Uint8Array>;
51
+ readonly signal?: AbortSignal;
52
+ readonly name?: string;
53
+ }): Promise<FileUploadValue>;
54
+ /**
55
+ * Resolve one staged receipt inside its receiving Agent scope.
56
+ * @param agent - receiving Agent.
57
+ * @param receiptId - opaque receipt minted for one completed upload.
58
+ * @returns durable file reference, or `undefined` for an unknown or foreign receipt.
59
+ */
60
+ resolve(agent: Agent, receiptId: FileUploadReceiptId): FileAttachmentRef | undefined;
61
+ /**
62
+ * Bind receipts while one prompt enters an Agent inbox.
63
+ * Disposal restores every prior binding unless the caller commits successful delivery.
64
+ * @param agent - receiving Agent.
65
+ * @param receiptIds - distinct staged receipts referenced by the prompt.
66
+ * @param requestId - prompt identity later observed in queue or history.
67
+ * @returns binding kept after commit until queue or history observation retires its receipts.
68
+ */
69
+ bindPrompt(agent: Agent, receiptIds: readonly FileUploadReceiptId[], requestId: string): PromptFileBinding;
70
+ /**
71
+ * Retire every receipt accepted by one removed queue occurrence.
72
+ * @param agent - receiving Agent.
73
+ * @param requestId - prompt identity carried by the queue occurrence.
74
+ */
75
+ retirePrompt(agent: Agent, requestId: string): void;
76
+ private commit;
77
+ private resolveAgent;
78
+ private assertAgentScope;
79
+ private assertOrdinaryAgent;
80
+ private observeSessionEvent;
81
+ private retire;
82
+ }
83
+ export default FileUploads;
84
+ //# sourceMappingURL=index.d.ts.map