@xsai/text-responses 0.0.0-stage → 1.0.0-beta.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.
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Moeru AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,25 @@
1
- # Temporary Holding Version
1
+ # @xsai/text-responses
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <!-- automd:file src="/docs/snippets/text-responses.md" -->
4
+
5
+ ```sh
6
+ pnpm add @xsai/text @xsai/text-responses
7
+ ```
8
+
9
+ ```ts
10
+ import { generateText } from '@xsai/text'
11
+ import { responses } from '@xsai/text-responses'
12
+
13
+ const model = responses({
14
+ apiKey: process.env.OPENAI_API_KEY,
15
+ baseURL: 'https://api.openai.com/v1/',
16
+ model: 'gpt-6-luna',
17
+ })
18
+
19
+ const { text } = await generateText(model, { input: 'Say hello.' })
20
+ console.log(text)
21
+ ```
22
+
23
+ <!-- /automd -->
24
+
25
+ Read the [documentation](https://xsai.js.org/text/adapters).
@@ -0,0 +1,74 @@
1
+ import { HttpOptions } from '@xsai/shared';
2
+ import { LanguageModel } from '@xsai/text';
3
+
4
+ type UrlCitationParam = {
5
+ /**
6
+ * The citation type. Always `url_citation`.
7
+ */
8
+ type: 'url_citation';
9
+ /**
10
+ * The index of the first character of the citation in the message.
11
+ */
12
+ start_index: number;
13
+ /**
14
+ * The index of the last character of the citation in the message.
15
+ */
16
+ end_index: number;
17
+ /**
18
+ * The URL of the cited resource.
19
+ */
20
+ url: string;
21
+ /**
22
+ * The title of the cited resource.
23
+ */
24
+ title: string;
25
+ };
26
+
27
+ type ResponsesAnnotation = UrlCitationParam | {
28
+ container_id: string;
29
+ end_index: number;
30
+ file_id: string;
31
+ filename: string;
32
+ index?: null | number;
33
+ start_index: number;
34
+ type: 'container_file_citation';
35
+ } | {
36
+ file_id: string;
37
+ filename: string;
38
+ index: number;
39
+ type: 'file_citation';
40
+ } | {
41
+ file_id: string;
42
+ index: number;
43
+ type: 'file_path';
44
+ };
45
+ interface ResponsesPartMetadata {
46
+ citations?: ResponsesAnnotation[];
47
+ }
48
+
49
+ interface ResponsesProviderOptions {
50
+ frequencyPenalty?: number;
51
+ include?: readonly string[];
52
+ parallelToolCalls?: boolean;
53
+ presencePenalty?: number;
54
+ /** @default `false` */
55
+ store?: boolean;
56
+ tools?: readonly ResponsesProviderTool[];
57
+ }
58
+ interface ResponsesProviderTool {
59
+ [key: string]: unknown;
60
+ type: string;
61
+ }
62
+
63
+ declare module '@xsai/text' {
64
+ interface LanguageModelProviderOptions {
65
+ responses?: ResponsesProviderOptions;
66
+ }
67
+ interface PartProviderMetadata {
68
+ responses?: ResponsesPartMetadata;
69
+ }
70
+ }
71
+ declare const responses: (options: HttpOptions) => LanguageModel;
72
+
73
+ export { responses };
74
+ export type { ResponsesAnnotation, ResponsesPartMetadata, ResponsesProviderOptions, ResponsesProviderTool };
package/dist/index.js ADDED
@@ -0,0 +1,467 @@
1
+ import { XSAIError, requestURL } from '@xsai/shared';
2
+ import { resolveSchema, WireEventStream, wireRequest } from '@xsai/text/internal';
3
+
4
+ const SUPPORTED_FORMATS = /* @__PURE__ */ new Set([
5
+ "date",
6
+ "date-time",
7
+ "duration",
8
+ "email",
9
+ "hostname",
10
+ "ipv4",
11
+ "ipv6",
12
+ "time",
13
+ "uuid"
14
+ ]);
15
+ const normalizeSchema = (schema) => {
16
+ const normalized = { ...schema };
17
+ const normalize = (schema2) => typeof schema2 === "boolean" ? schema2 : normalizeSchema(schema2);
18
+ if (normalized.$ref != null)
19
+ return { $ref: normalized.$ref };
20
+ if (normalized.type === "object" || normalized.properties != null) {
21
+ normalized.properties ??= {};
22
+ normalized.properties = Object.fromEntries(Object.entries(normalized.properties).map(([name, property]) => [name, normalize(property)]));
23
+ normalized.additionalProperties = false;
24
+ normalized.required = Object.keys(normalized.properties);
25
+ }
26
+ if (normalized.$defs != null)
27
+ normalized.$defs = Object.fromEntries(Object.entries(normalized.$defs).map(([name, definition]) => [name, normalize(definition)]));
28
+ if (normalized.definitions != null)
29
+ normalized.definitions = Object.fromEntries(Object.entries(normalized.definitions).map(([name, definition]) => [name, normalize(definition)]));
30
+ if (normalized.items != null) {
31
+ normalized.items = Array.isArray(normalized.items) ? normalized.items.map(normalize) : normalize(normalized.items);
32
+ }
33
+ if (normalized.oneOf != null) {
34
+ const oneOf = normalized.oneOf;
35
+ delete normalized.oneOf;
36
+ normalized.anyOf = normalized.anyOf == null ? oneOf : normalized.anyOf.concat(oneOf);
37
+ }
38
+ if (normalized.anyOf != null)
39
+ normalized.anyOf = normalized.anyOf.map(normalize);
40
+ delete normalized.allOf;
41
+ delete normalized.not;
42
+ delete normalized.if;
43
+ delete normalized.then;
44
+ delete normalized.else;
45
+ if (normalized.format != null && !SUPPORTED_FORMATS.has(normalized.format))
46
+ delete normalized.format;
47
+ return normalized;
48
+ };
49
+
50
+ const normalizeSchemaName = (title) => title == null || title === "" ? "output" : title.replace(/[^\w-]/g, "_").slice(0, 64);
51
+ const normalizeFormat = (outputFormat) => {
52
+ if (outputFormat == null)
53
+ return void 0;
54
+ const { schema } = resolveSchema(outputFormat);
55
+ const normalized = normalizeSchema(schema);
56
+ return {
57
+ format: {
58
+ description: normalized.description,
59
+ name: normalizeSchemaName(normalized.title),
60
+ schema: normalized,
61
+ strict: true,
62
+ type: "json_schema"
63
+ }
64
+ };
65
+ };
66
+
67
+ const normalizeInputTextPart = (part) => ({
68
+ text: part.text,
69
+ type: "input_text"
70
+ });
71
+ const normalizeOutputTextPart = (part) => ({
72
+ // TODO: Move citations to a first-class TextPart field.
73
+ // The generated OpenResponses type lists only URL citations.
74
+ annotations: part.providerMetadata?.responses?.citations,
75
+ text: part.text,
76
+ type: "output_text"
77
+ });
78
+ const normalizeImagePart = (part) => ({
79
+ detail: part.detail,
80
+ image_url: part.data.toString(),
81
+ type: "input_image"
82
+ });
83
+ const normalizeFilePart = (part) => {
84
+ const data = part.data.toString();
85
+ const protocol = URL.parse(data)?.protocol;
86
+ return protocol === "http:" || protocol === "https:" ? { file_url: data, type: "input_file" } : { file_data: data, type: "input_file" };
87
+ };
88
+ const normalizeReasoningPart$1 = (part) => {
89
+ const summary = [];
90
+ let encryptedContent;
91
+ for (const partContent of part.content) {
92
+ switch (partContent.type) {
93
+ case "encrypted":
94
+ case "redacted":
95
+ encryptedContent = partContent.text;
96
+ break;
97
+ case "summary":
98
+ summary.push({ text: partContent.text, type: "summary_text" });
99
+ break;
100
+ }
101
+ }
102
+ return {
103
+ encrypted_content: encryptedContent,
104
+ id: part.id,
105
+ summary,
106
+ type: "reasoning"
107
+ };
108
+ };
109
+ const normalizeToolCallPart = (part) => ({
110
+ arguments: part.arguments,
111
+ call_id: part.callId,
112
+ id: part.id,
113
+ name: part.name,
114
+ type: "function_call"
115
+ });
116
+ const normalizeToolResultContent = (content) => {
117
+ switch (content.type) {
118
+ case "image":
119
+ return normalizeImagePart(content);
120
+ case "text":
121
+ return normalizeInputTextPart(content);
122
+ }
123
+ };
124
+ const normalizeToolResultPart = (part) => ({
125
+ call_id: part.callId,
126
+ output: Array.isArray(part.output) ? part.output.map(normalizeToolResultContent) : part.output,
127
+ type: "function_call_output"
128
+ });
129
+ const normalizeAssistantMessage = (message, source) => {
130
+ const createMessageItem = (content2, includeId2 = true) => ({
131
+ content: content2,
132
+ id: includeId2 ? message.id : void 0,
133
+ role: "assistant",
134
+ type: "message"
135
+ });
136
+ if (typeof message.content === "string")
137
+ return [createMessageItem([{ text: message.content, type: "output_text" }])];
138
+ const items = [];
139
+ let content = [];
140
+ let includeId = true;
141
+ const flushContent = () => {
142
+ if (content.length === 0)
143
+ return;
144
+ items.push(createMessageItem(content, includeId));
145
+ content = [];
146
+ includeId = false;
147
+ };
148
+ for (const part of message.content) {
149
+ switch (part.type) {
150
+ case "provider":
151
+ if (part.source === source) {
152
+ flushContent();
153
+ items.push(part.value);
154
+ }
155
+ break;
156
+ case "reasoning":
157
+ flushContent();
158
+ items.push(normalizeReasoningPart$1(part));
159
+ break;
160
+ case "refusal":
161
+ content.push({ refusal: part.refusal, type: "refusal" });
162
+ break;
163
+ case "text":
164
+ content.push(normalizeOutputTextPart(part));
165
+ break;
166
+ case "tool-call":
167
+ flushContent();
168
+ items.push(normalizeToolCallPart(part));
169
+ break;
170
+ }
171
+ }
172
+ flushContent();
173
+ return items;
174
+ };
175
+ const normalizeUserMessage = (message) => {
176
+ const createMessageItem = (content2) => ({
177
+ content: content2,
178
+ role: "user",
179
+ type: "message"
180
+ });
181
+ if (typeof message.content === "string")
182
+ return [createMessageItem(message.content)];
183
+ const items = [];
184
+ let content = [];
185
+ const flushContent = () => {
186
+ if (content.length === 0)
187
+ return;
188
+ items.push(createMessageItem(content));
189
+ content = [];
190
+ };
191
+ for (const part of message.content) {
192
+ switch (part.type) {
193
+ case "file":
194
+ content.push(normalizeFilePart(part));
195
+ break;
196
+ case "image":
197
+ content.push(normalizeImagePart(part));
198
+ break;
199
+ case "text":
200
+ content.push(normalizeInputTextPart(part));
201
+ break;
202
+ case "tool-result":
203
+ flushContent();
204
+ items.push(normalizeToolResultPart(part));
205
+ break;
206
+ }
207
+ }
208
+ flushContent();
209
+ return items;
210
+ };
211
+ const normalizeMessage = (message, source) => {
212
+ switch (message.role) {
213
+ case "assistant":
214
+ return normalizeAssistantMessage(message, source);
215
+ case "developer":
216
+ return [{
217
+ content: typeof message.content === "string" ? message.content : message.content.map(normalizeInputTextPart),
218
+ role: "developer",
219
+ type: "message"
220
+ }];
221
+ case "system":
222
+ return [{
223
+ content: typeof message.content === "string" ? message.content : message.content.map(normalizeInputTextPart),
224
+ role: "system",
225
+ type: "message"
226
+ }];
227
+ case "user":
228
+ return normalizeUserMessage(message);
229
+ }
230
+ };
231
+ const normalizeInput = (input, source) => typeof input === "string" ? [{ content: input, role: "user", type: "message" }] : input.flatMap((message) => normalizeMessage(message, source));
232
+
233
+ const normalizeToolChoice = (toolChoice) => toolChoice == null ? void 0 : typeof toolChoice === "string" ? toolChoice : { name: toolChoice.name, type: "function" };
234
+
235
+ const normalizeTools = (tools) => tools?.map((tool) => ({
236
+ description: tool.description,
237
+ name: tool.name,
238
+ parameters: normalizeSchema(tool.inputSchema.schema),
239
+ strict: true,
240
+ type: "function"
241
+ }));
242
+ const mergeTools = (providerTools, tools) => {
243
+ const merged = [
244
+ ...providerTools ?? [],
245
+ ...normalizeTools(tools) ?? []
246
+ ];
247
+ return merged.length === 0 ? void 0 : merged;
248
+ };
249
+
250
+ const normalizeUsage = (usage) => ({
251
+ cacheReadInputTokens: usage.input_tokens_details?.cached_tokens,
252
+ inputTokens: usage.input_tokens,
253
+ outputTokens: usage.output_tokens,
254
+ reasoningTokens: usage.output_tokens_details?.reasoning_tokens,
255
+ totalTokens: usage.total_tokens
256
+ });
257
+ const normalizeToolCall = (item) => ({
258
+ arguments: item.arguments,
259
+ callId: item.call_id,
260
+ id: item.id,
261
+ name: item.name,
262
+ type: "tool-call"
263
+ });
264
+ const contentPartKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
265
+ const normalizeContentPart = (part) => {
266
+ if (part.type === "refusal")
267
+ return { refusal: part.refusal, type: "refusal" };
268
+ if (part.type === "output_text") {
269
+ const citations = part.annotations;
270
+ return {
271
+ ...citations?.length ? { providerMetadata: { responses: { citations } } } : {},
272
+ text: part.text,
273
+ type: "text"
274
+ };
275
+ }
276
+ return void 0;
277
+ };
278
+ const normalizeReasoningPart = (item) => {
279
+ const reasoningContent = [
280
+ ...(item.summary ?? []).flatMap((part) => part.type === "summary_text" ? [{ text: part.text, type: "summary" }] : []),
281
+ ...(item.content ?? []).flatMap((part) => part.type === "reasoning_text" ? [{ text: part.text, type: "text" }] : [])
282
+ ];
283
+ if (item.encrypted_content != null)
284
+ reasoningContent.push({ text: item.encrypted_content, type: "encrypted" });
285
+ return { content: reasoningContent, id: item.id, type: "reasoning" };
286
+ };
287
+ const normalizeFinishReason = (response) => {
288
+ if (response.status !== "incomplete")
289
+ return void 0;
290
+ const reason = response.incomplete_details?.reason;
291
+ switch (reason) {
292
+ case "content_filter":
293
+ return "content-filter";
294
+ case "max_output_tokens":
295
+ return "length";
296
+ case null:
297
+ case void 0:
298
+ return void 0;
299
+ default:
300
+ return reason;
301
+ }
302
+ };
303
+ const normalizeStatus = (response) => {
304
+ switch (response.status) {
305
+ case "cancelled":
306
+ return "cancelled";
307
+ case "completed":
308
+ return "completed";
309
+ case "failed":
310
+ return "failed";
311
+ case "incomplete":
312
+ return "incomplete";
313
+ default:
314
+ throw new Error(`unknown response status: ${response.status}`);
315
+ }
316
+ };
317
+ class ResponsesEventStream extends WireEventStream {
318
+ constructor(source, includeRawEvents = false) {
319
+ const normalizeOutputItem = (item, index) => {
320
+ if (item.type === "function_call_output")
321
+ return { parts: [] };
322
+ if (item.type === "function_call")
323
+ return { parts: [{ content: normalizeToolCall(item), init: { callId: item.call_id, id: item.id, name: item.name } }] };
324
+ if (item.type === "message") {
325
+ const parts = item.content.flatMap((part, contentIndex) => {
326
+ const content = normalizeContentPart(part);
327
+ return content == null ? [] : [{ content, key: contentPartKey(index, contentIndex) }];
328
+ });
329
+ return { messageId: item.id, parts };
330
+ }
331
+ if (item.type === "reasoning")
332
+ return { parts: [{ content: normalizeReasoningPart(item) }] };
333
+ return { parts: [{ content: { source, type: "provider", value: item } }] };
334
+ };
335
+ const onItemAdded = (builder, item, index) => {
336
+ const normalized = normalizeOutputItem(item, index);
337
+ for (const part of normalized.parts) {
338
+ if (part.content.type !== "provider")
339
+ builder.start(part.key ?? index, part.content.type, part.init);
340
+ }
341
+ if (normalized.messageId != null)
342
+ builder.meta({ messageId: normalized.messageId });
343
+ };
344
+ const onItemDone = (builder, item, index) => {
345
+ const normalized = normalizeOutputItem(item, index);
346
+ for (const part of normalized.parts) {
347
+ const key = part.key ?? index;
348
+ builder.start(key, part.content.type, part.init);
349
+ builder.end(key, { content: part.content });
350
+ }
351
+ return normalized;
352
+ };
353
+ const finishResponse = (builder, response) => {
354
+ const status = normalizeStatus(response);
355
+ if (response.usage != null)
356
+ builder.meta({ usage: normalizeUsage(response.usage) });
357
+ const output = response.output.map((item, index) => onItemDone(builder, item, index));
358
+ const message = {
359
+ content: output.flatMap((item) => item.parts.map((part) => part.content)),
360
+ id: output.findLast((item) => item.messageId != null)?.messageId,
361
+ role: "assistant"
362
+ };
363
+ if (status === "failed") {
364
+ const error = new XSAIError("model-error", response.error?.message ?? "response failed", {
365
+ cause: response.error ?? void 0
366
+ });
367
+ builder.fail(error, message);
368
+ return;
369
+ }
370
+ const reason = normalizeFinishReason(response) ?? (status === "completed" && message.content.some((part) => part.type === "refusal") ? "refusal" : void 0);
371
+ builder.done(status, reason, message);
372
+ };
373
+ super((event, builder) => {
374
+ switch (event.type) {
375
+ case "error":
376
+ builder.fail(new XSAIError("model-error", event.error.message, {
377
+ cause: event.error
378
+ }));
379
+ break;
380
+ case "response.completed":
381
+ case "response.failed":
382
+ case "response.incomplete":
383
+ finishResponse(builder, event.response);
384
+ break;
385
+ case "response.content_part.added": {
386
+ const content = normalizeContentPart(event.part);
387
+ if (content != null)
388
+ builder.start(contentPartKey(event.output_index, event.content_index), content.type);
389
+ break;
390
+ }
391
+ case "response.content_part.done": {
392
+ const content = normalizeContentPart(event.part);
393
+ if (content != null) {
394
+ const key = contentPartKey(event.output_index, event.content_index);
395
+ builder.start(key, content.type);
396
+ builder.end(key, { content });
397
+ }
398
+ break;
399
+ }
400
+ case "response.created":
401
+ break;
402
+ case "response.function_call_arguments.delta":
403
+ case "response.reasoning.delta":
404
+ case "response.reasoning_summary_text.delta":
405
+ builder.delta(event.output_index, event.delta);
406
+ break;
407
+ case "response.function_call_arguments.done":
408
+ break;
409
+ case "response.in_progress":
410
+ case "response.output_text.done":
411
+ case "response.queued":
412
+ case "response.reasoning.done":
413
+ case "response.reasoning_summary_part.added":
414
+ case "response.reasoning_summary_part.done":
415
+ case "response.reasoning_summary_text.done":
416
+ break;
417
+ case "response.output_item.added":
418
+ if (event.item != null)
419
+ onItemAdded(builder, event.item, event.output_index);
420
+ break;
421
+ case "response.output_item.done":
422
+ if (event.item != null)
423
+ onItemDone(builder, event.item, event.output_index);
424
+ break;
425
+ case "response.output_text.delta":
426
+ case "response.refusal.delta": {
427
+ const key = contentPartKey(event.output_index, event.content_index);
428
+ builder.start(key, event.type === "response.refusal.delta" ? "refusal" : "text");
429
+ builder.delta(key, event.delta);
430
+ break;
431
+ }
432
+ case "response.reasoning_text.delta":
433
+ builder.start(event.output_index, "reasoning");
434
+ builder.delta(event.output_index, event.delta);
435
+ break;
436
+ }
437
+ }, includeRawEvents);
438
+ }
439
+ }
440
+
441
+ const responses = (options) => async (modelOptions) => {
442
+ const source = requestURL("responses", options.baseURL).toString();
443
+ const providerOptions = modelOptions.providerOptions?.responses;
444
+ return wireRequest({
445
+ body: {
446
+ frequency_penalty: providerOptions?.frequencyPenalty,
447
+ include: providerOptions?.include,
448
+ input: normalizeInput(modelOptions.input, source),
449
+ instructions: modelOptions.instructions,
450
+ max_output_tokens: modelOptions.maxOutputTokens,
451
+ model: options.model,
452
+ parallel_tool_calls: providerOptions?.parallelToolCalls,
453
+ presence_penalty: providerOptions?.presencePenalty,
454
+ reasoning: modelOptions.reasoningEffort == null ? void 0 : { effort: modelOptions.reasoningEffort },
455
+ store: providerOptions?.store ?? false,
456
+ stream: true,
457
+ temperature: modelOptions.temperature,
458
+ text: normalizeFormat(modelOptions.outputFormat),
459
+ tool_choice: normalizeToolChoice(modelOptions.toolChoice),
460
+ tools: mergeTools(providerOptions?.tools, modelOptions.tools),
461
+ top_p: modelOptions.topP
462
+ },
463
+ path: source
464
+ }, options, modelOptions, new ResponsesEventStream(source, modelOptions.includeRawEvents));
465
+ };
466
+
467
+ export { responses };
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@xsai/text-responses",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "type": "module",
4
+ "version": "1.0.0-beta.1",
5
+ "description": "extra-small AI SDK: OpenAI Responses API model for @xsai/text.",
6
+ "author": "Moeru AI",
7
+ "license": "MIT",
8
+ "homepage": "https://xsai.js.org",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/moeru-ai/xsai.git",
12
+ "directory": "packages/text-responses"
13
+ },
14
+ "bugs": "https://github.com/moeru-ai/xsai/issues",
15
+ "keywords": [
16
+ "xsai",
17
+ "ai",
18
+ "openai",
19
+ "responses",
20
+ "responses-api"
21
+ ],
22
+ "sideEffects": false,
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "default": "./dist/index.js"
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "peerDependencies": {
34
+ "@xsai/text": "1.0.0-beta.1"
35
+ },
36
+ "dependencies": {
37
+ "@xsai/shared": "1.0.0-beta.1"
38
+ },
39
+ "devDependencies": {
40
+ "@xsai/text": "1.0.0-beta.1",
41
+ "zod": "^4.3.6"
42
+ },
43
+ "scripts": {
44
+ "build": "pkgroll --clean-dist",
45
+ "sync": "openapi-ts",
46
+ "test": "vitest run --exclude e2e/**",
47
+ "test:watch": "vitest"
48
+ },
49
+ "main": "./dist/index.js",
50
+ "types": "./dist/index.d.ts"
6
51
  }