From 16b59b3e51257bdc1d36d87c50e047ff4df95857 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:22:15 -0700 Subject: [PATCH 01/28] Add Completion Endpoint --- src/index.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 2379805..b0c9e02 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,7 +61,7 @@ app.listen(port, () => console.log(`Listening on PORT: ${port}`)) // app.get('/app-health-check', (_req, res) => res.sendStatus(200)) -app.post('/', async (req: Request, res: Response) => { +app.post(['/', '/chat'], async (req: Request, res: Response) => { // console.log('req.headers:', req.headers) // console.log('authorization:', req.headers.authorization) const { messages, system } = req.body @@ -84,6 +84,30 @@ app.post('/', async (req: Request, res: Response) => { pipeUIMessageStreamToResponse({ response: res, stream }) }) +app.post('/completion', async (req: Request, res: Response) => { + const { prompt, system } = req.body + console.log('prompt:', prompt.length, 'system:', system.length) + const result = streamText({ + model: model, + prompt, + system: system || process.env.COMPLETION_INSTRUCTIONS, + maxOutputTokens, + providerOptions, + onEnd({ finishReason, finalStep, text, usage }) { + console.log('finishReason:', finishReason) + console.log('usage:', usage) + console.log('reasoning:', finalStep.reasoningText) + console.log('response:', text) + }, + }) + const stream = createUIMessageStream({ + execute: ({ writer }) => { + writer.merge(toUIMessageStream({ stream: result.stream })) + }, + }) + pipeUIMessageStreamToResponse({ response: res, stream }) +}) + function corsCallback( origin: string | undefined, callback: (err: Error | null, origin?: boolean) => void, From 5161b933dd16ecd4b30be084d0f7f24dd255069c Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Tue, 14 Jul 2026 20:36:49 -0700 Subject: [PATCH 02/28] Cleanup --- src/index.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index b0c9e02..b7c8ac4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import { google } from '@ai-sdk/google' import { openai } from '@ai-sdk/openai' import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import { + consumeStream, streamText, createUIMessageStream, pipeUIMessageStreamToResponse, @@ -85,19 +86,23 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { }) app.post('/completion', async (req: Request, res: Response) => { - const { prompt, system } = req.body - console.log('prompt:', prompt.length, 'system:', system.length) + const { prompt, system, signal } = req.body + console.log('prompt:', prompt?.length, 'system:', system?.length) const result = streamText({ model: model, prompt, system: system || process.env.COMPLETION_INSTRUCTIONS, maxOutputTokens, providerOptions, - onEnd({ finishReason, finalStep, text, usage }) { - console.log('finishReason:', finishReason) - console.log('usage:', usage) + abortSignal: signal, + onError({ error }) { + console.log('error:', error) + }, + onEnd({ finalStep, finishReason, text, usage }) { console.log('reasoning:', finalStep.reasoningText) console.log('response:', text) + console.log('usage:', usage) + console.log('finishReason:', finishReason) }, }) const stream = createUIMessageStream({ @@ -105,7 +110,11 @@ app.post('/completion', async (req: Request, res: Response) => { writer.merge(toUIMessageStream({ stream: result.stream })) }, }) - pipeUIMessageStreamToResponse({ response: res, stream }) + pipeUIMessageStreamToResponse({ + response: res, + stream, + consumeSseStream: consumeStream, + }) }) function corsCallback( From 091be2300fa2e01606e58a86f24065e48c2cf7db Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Tue, 14 Jul 2026 21:57:42 -0700 Subject: [PATCH 03/28] Add /object --- src/index.ts | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index b7c8ac4..7bd2e39 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,11 +7,14 @@ import { google } from '@ai-sdk/google' import { openai } from '@ai-sdk/openai' import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import { + Output, consumeStream, + pipeTextStreamToResponse, streamText, createUIMessageStream, pipeUIMessageStreamToResponse, convertToModelMessages, + toTextStream, toUIMessageStream, } from 'ai' @@ -43,7 +46,11 @@ const maxOutputTokens = process.env.MAX_TOKENS ? Number.parseInt(process.env.MAX_TOKENS) : undefined console.log(`maxOutputTokens: ${maxOutputTokens}`) -console.log(`INSTRUCTIONS: ${process.env.INSTRUCTIONS}`) +console.log( + `CHAT_INSTRUCTIONS: ${process.env.INSTRUCTIONS || process.env.CHAT_INSTRUCTIONS}`, // NOSONAR +) +console.log(`COMPLETION_INSTRUCTIONS: ${process.env.COMPLETION_INSTRUCTIONS}`) +console.log(`OBJECT_INSTRUCTIONS: ${process.env.OBJECT_INSTRUCTIONS}`) const model = getModel() console.log(`Loaded modelId: ${model.modelId}`) @@ -74,7 +81,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { const result = streamText({ model: model, messages: modelMessages, - system: system || process.env.INSTRUCTIONS, + system: system || process.env.INSTRUCTIONS || process.env.CHAT_INSTRUCTIONS, maxOutputTokens, providerOptions, }) @@ -86,7 +93,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { }) app.post('/completion', async (req: Request, res: Response) => { - const { prompt, system, signal } = req.body + const { prompt, system } = req.body console.log('prompt:', prompt?.length, 'system:', system?.length) const result = streamText({ model: model, @@ -94,8 +101,7 @@ app.post('/completion', async (req: Request, res: Response) => { system: system || process.env.COMPLETION_INSTRUCTIONS, maxOutputTokens, providerOptions, - abortSignal: signal, - onError({ error }) { + onError(error) { console.log('error:', error) }, onEnd({ finalStep, finishReason, text, usage }) { @@ -117,6 +123,32 @@ app.post('/completion', async (req: Request, res: Response) => { }) }) +app.post('/object', async (req: Request, res: Response) => { + const { outputSchema, prompt, system } = req.body + console.log('prompt:', prompt?.length, 'system:', system?.length) + const result = streamText({ + model: model, + prompt, + system: system || process.env.OBJECT_INSTRUCTIONS, + maxOutputTokens, + providerOptions, + output: outputSchema ? Output.object({ schema: outputSchema }) : Output.json(), + onError(error) { + console.log('error:', error) + }, + onEnd({ finalStep, finishReason, text, usage }) { + console.log('reasoning:', finalStep.reasoningText) + console.log('response:', text) + console.log('usage:', usage) + console.log('finishReason:', finishReason) + }, + }) + pipeTextStreamToResponse({ + response: res, + stream: toTextStream({ stream: result.stream }), + }) +}) + function corsCallback( origin: string | undefined, callback: (err: Error | null, origin?: boolean) => void, From 23f9abb19470270fd95bcef0176eb61ae183bbd4 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:47:40 -0700 Subject: [PATCH 04/28] Update README.md --- README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++---- src/index.ts | 9 ++++++--- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 47ed6ad..0578466 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,13 @@ To get started [Setup](#setup) and [Configure](#configure) the server. No API Ke [![View Live Demo](https://img.shields.io/badge/view_live_demo-green?style=for-the-badge&logo=chatbot&logoColor=white)](https://cssnr.github.io/vitepress-chat/) -- Client: https://github.com/cssnr/vitepress-chat -- Server: https://github.com/cssnr/chat-server +- Client: +- Server: ### Features - Works with Claude, OpenAI, Gemini and OpenAI Compatible Providers +- Chat, Completion, and Object Endpoints - Live Stream Results to Client - Automatic Input Token Caching - Automatic Retry on API Failures @@ -107,7 +108,9 @@ Environment Variables. | `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | | [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | | `MAX_TOKENS` | - | Max Output Tokens | -| `INSTRUCTIONS` | - | Fallback System Instructions | +| `CHAT_INSTRUCTIONS` | - | System Instructions for Chat | +| `COMPLETION_INSTRUCTIONS` | - | System Instructions for Completion | +| `OBJECT_INSTRUCTIONS` | - | System Instructions for Object | | `AI_SDK_LOG_WARNINGS` | - | Disable SDK Warnings | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | | `PORT` | `3000` | Server Port | @@ -145,6 +148,16 @@ The value is only checked for valid JSON at startup and will fail at runtime if ## Client +### Endpoints + +| Endpoint | Method | Description | +| :------------ | :----: | :------------------------------------------------------------------------------------------------------------------------------------- | +| `/chat` | `POST` | Use with [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) and [VitePress Chat](https://cssnr.github.io/vitepress-chat/) | +| `/completion` | `POST` | Use with [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | +| `/object` | `POST` | Use with [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | + +### Chat + To send System Instructions from the client, add them to the body. ```typescript @@ -157,11 +170,46 @@ const chat = new Chat({ }) ``` +Reference: + +### Completion + +```typescript +import { useCompletion } from '@ai-sdk/react' + +const { completion, complete, isLoading, stop } = useCompletion({ + api: 'https://chat-server.cssnr.com/completion', + headers: { Authorization: 'Basic Abc123=' }, + body: { system: 'You are a helpful assistant.' }, +}) +``` + +Reference: + +### Object + +```typescript +import { useObject } from '@ai-sdk/react' + +const { object, submit } = useObject({ + api: 'https://chat-server.cssnr.com/object', + schema: z.object({ name: z.string(), age: z.number() }), + headers: { Authorization: 'Basic Abc123=' }, +}) + +submit({ + system: 'You are a helpful assistant.', + prompt: 'Extract the name and age from: John is 30 years old.', +}) +``` + +Reference: + ### VitePress Chat Plugin The client is currently available as a VitePress Plugin. -- https://github.com/cssnr/vitepress-chat +- [![View Documentation](https://img.shields.io/badge/view_documentation-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://cssnr.github.io/vitepress-chat/) diff --git a/src/index.ts b/src/index.ts index 7bd2e39..7ea1dc1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -73,7 +73,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { // console.log('req.headers:', req.headers) // console.log('authorization:', req.headers.authorization) const { messages, system } = req.body - // if (system) console.log('system:', system.substring(0, 512)) + // console.log('system:', system?.substring(0, 512)) const modelMessages = await convertToModelMessages(messages) console.log('modelMessages:', modelMessages.length) const stream = createUIMessageStream({ @@ -94,7 +94,8 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { app.post('/completion', async (req: Request, res: Response) => { const { prompt, system } = req.body - console.log('prompt:', prompt?.length, 'system:', system?.length) + console.log('prompt:', prompt?.length) + console.log('system:', system?.length) const result = streamText({ model: model, prompt, @@ -125,7 +126,9 @@ app.post('/completion', async (req: Request, res: Response) => { app.post('/object', async (req: Request, res: Response) => { const { outputSchema, prompt, system } = req.body - console.log('prompt:', prompt?.length, 'system:', system?.length) + console.log('outputSchema:', outputSchema?.length) + console.log('prompt:', prompt?.length) + console.log('system:', system?.length) const result = streamText({ model: model, prompt, From d63e304a1e4ec14775751595f9ac5c33548e19a2 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:45:52 -0700 Subject: [PATCH 05/28] Updates --- README.md | 6 +++++- src/index.ts | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0578466..7d0df3b 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ To send System Instructions from the client, add them to the body. ```typescript const chat = new Chat({ transport: new DefaultChatTransport({ - api: 'https://chat-server.cssnr.com/', + api: 'https://chat-server.cssnr.com/chat', headers: { Authorization: 'Basic Abc123=' }, body: { system: 'You are a helpful assistant.' }, }), @@ -200,6 +200,10 @@ const { object, submit } = useObject({ submit({ system: 'You are a helpful assistant.', prompt: 'Extract the name and age from: John is 30 years old.', + output: { + type: 'object', + properties: { name: { type: 'string' }, age: { type: 'number' } }, + }, }) ``` diff --git a/src/index.ts b/src/index.ts index 7ea1dc1..fe505b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import { Output, consumeStream, + jsonSchema, pipeTextStreamToResponse, streamText, createUIMessageStream, @@ -125,8 +126,8 @@ app.post('/completion', async (req: Request, res: Response) => { }) app.post('/object', async (req: Request, res: Response) => { - const { outputSchema, prompt, system } = req.body - console.log('outputSchema:', outputSchema?.length) + const { output, prompt, system } = req.body + console.log('output:', output ? 'SET' : undefined) console.log('prompt:', prompt?.length) console.log('system:', system?.length) const result = streamText({ @@ -135,7 +136,7 @@ app.post('/object', async (req: Request, res: Response) => { system: system || process.env.OBJECT_INSTRUCTIONS, maxOutputTokens, providerOptions, - output: outputSchema ? Output.object({ schema: outputSchema }) : Output.json(), + output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), onError(error) { console.log('error:', error) }, From e641616498074ef0ae70a8ff99111f4ce304ca90 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Wed, 15 Jul 2026 22:33:01 -0700 Subject: [PATCH 06/28] Cleanup --- src/index.ts | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/index.ts b/src/index.ts index fe505b8..6e16701 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,14 +7,15 @@ import { google } from '@ai-sdk/google' import { openai } from '@ai-sdk/openai' import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import { + type GenerateTextEndEvent, Output, consumeStream, + convertToModelMessages, + createUIMessageStream, jsonSchema, pipeTextStreamToResponse, - streamText, - createUIMessageStream, pipeUIMessageStreamToResponse, - convertToModelMessages, + streamText, toTextStream, toUIMessageStream, } from 'ai' @@ -76,7 +77,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { const { messages, system } = req.body // console.log('system:', system?.substring(0, 512)) const modelMessages = await convertToModelMessages(messages) - console.log('modelMessages:', modelMessages.length) + // console.log('modelMessages:', modelMessages.length) const stream = createUIMessageStream({ execute: ({ writer }) => { const result = streamText({ @@ -85,6 +86,8 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { system: system || process.env.INSTRUCTIONS || process.env.CHAT_INSTRUCTIONS, maxOutputTokens, providerOptions, + onError: onStreamError, + onEnd: onStreamEnd, }) writer.merge(toUIMessageStream({ stream: result.stream })) }, @@ -103,15 +106,8 @@ app.post('/completion', async (req: Request, res: Response) => { system: system || process.env.COMPLETION_INSTRUCTIONS, maxOutputTokens, providerOptions, - onError(error) { - console.log('error:', error) - }, - onEnd({ finalStep, finishReason, text, usage }) { - console.log('reasoning:', finalStep.reasoningText) - console.log('response:', text) - console.log('usage:', usage) - console.log('finishReason:', finishReason) - }, + onError: onStreamError, + onEnd: onStreamEnd, }) const stream = createUIMessageStream({ execute: ({ writer }) => { @@ -137,15 +133,8 @@ app.post('/object', async (req: Request, res: Response) => { maxOutputTokens, providerOptions, output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), - onError(error) { - console.log('error:', error) - }, - onEnd({ finalStep, finishReason, text, usage }) { - console.log('reasoning:', finalStep.reasoningText) - console.log('response:', text) - console.log('usage:', usage) - console.log('finishReason:', finishReason) - }, + onError: onStreamError, + onEnd: onStreamEnd, }) pipeTextStreamToResponse({ response: res, @@ -195,3 +184,14 @@ function getProviderOptions() { console.error('parsing PROVIDER_OPTIONS as JSON') } } + +function onStreamError(error: unknown) { + console.log('error:', error) +} + +function onStreamEnd({ finalStep, finishReason, text, usage }: GenerateTextEndEvent) { + console.log('reasoning:', finalStep.reasoningText) + console.log('response:', text) + console.log('usage:', usage) + console.log('finishReason:', finishReason) +} From 218b7f4cf9bb7bacbeaefee7cd6f20faf469ca51 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 11:45:33 -0700 Subject: [PATCH 07/28] Add debug --- README.md | 1 + package-lock.json | 19 +++++++++++++++++++ package.json | 2 ++ src/index.ts | 37 +++++++++++++++++++++---------------- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 7d0df3b..8d6e7cf 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Environment Variables. | `AI_SDK_LOG_WARNINGS` | - | Disable SDK Warnings | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | | `PORT` | `3000` | Server Port | +| `DEBUG` | - | Set to `app` for debug logs | You must also set the API key for the `MODEL` you select. diff --git a/package-lock.json b/package-lock.json index f49f933..6a33da0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@ai-sdk/openai-compatible": "^3.0.2", "ai": "^7.0.8", "cors": "^2.8.6", + "debug": "^4.4.3", "dotenv": "^17.4.2", "express": "^5.2.1", "picomatch": "^4.0.4" @@ -21,6 +22,7 @@ "devDependencies": { "@eslint/js": "^10.0.1", "@types/cors": "^2.8.19", + "@types/debug": "^4.1.13", "@types/express": "^5.0.6", "@types/node": "^26.0.1", "@types/picomatch": "^4.0.3", @@ -816,6 +818,16 @@ "@types/node": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -869,6 +881,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", diff --git a/package.json b/package.json index 8ab8f5a..6a81fcc 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@ai-sdk/openai-compatible": "^3.0.2", "ai": "^7.0.8", "cors": "^2.8.6", + "debug": "^4.4.3", "dotenv": "^17.4.2", "express": "^5.2.1", "picomatch": "^4.0.4" @@ -29,6 +30,7 @@ "devDependencies": { "@eslint/js": "^10.0.1", "@types/cors": "^2.8.19", + "@types/debug": "^4.1.13", "@types/express": "^5.0.6", "@types/node": "^26.0.1", "@types/picomatch": "^4.0.3", diff --git a/src/index.ts b/src/index.ts index 6e16701..feb0cad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import createDebug from 'debug' import cors from 'cors' import dotenv from 'dotenv' import pm from 'picomatch' @@ -22,9 +23,14 @@ import { dotenv.config({ path: 'settings.env' }) +console.log(`DEBUG: ${process.env.DEBUG}`) +createDebug.enable(process.env.DEBUG ?? '') +const debug = createDebug('app') +debug('debug enabled: app') + console.log(`chat-server: ${process.env.APP_VERSION}`) -console.log('MODEL:', process.env.MODEL ? 'SET' : undefined) +console.log('MODEL:', process.env.MODEL) console.log('ANTHROPIC_API_KEY:', process.env.ANTHROPIC_API_KEY ? 'SET' : undefined) console.log('OPENAI_API_KEY:', process.env.OPENAI_API_KEY ? 'SET' : undefined) console.log( @@ -72,12 +78,12 @@ app.listen(port, () => console.log(`Listening on PORT: ${port}`)) // app.get('/app-health-check', (_req, res) => res.sendStatus(200)) app.post(['/', '/chat'], async (req: Request, res: Response) => { - // console.log('req.headers:', req.headers) - // console.log('authorization:', req.headers.authorization) + // debug('req.headers:', req.headers) + // debug('authorization:', req.headers.authorization) const { messages, system } = req.body - // console.log('system:', system?.substring(0, 512)) + // debug('system:', system?.substring(0, 128)) const modelMessages = await convertToModelMessages(messages) - // console.log('modelMessages:', modelMessages.length) + debug('modelMessages:', modelMessages.length) const stream = createUIMessageStream({ execute: ({ writer }) => { const result = streamText({ @@ -92,14 +98,13 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { writer.merge(toUIMessageStream({ stream: result.stream })) }, }) - // console.log('stream:', stream) pipeUIMessageStreamToResponse({ response: res, stream }) }) app.post('/completion', async (req: Request, res: Response) => { const { prompt, system } = req.body - console.log('prompt:', prompt?.length) - console.log('system:', system?.length) + debug('prompt:', prompt?.length) + debug('system:', system?.length) const result = streamText({ model: model, prompt, @@ -123,9 +128,9 @@ app.post('/completion', async (req: Request, res: Response) => { app.post('/object', async (req: Request, res: Response) => { const { output, prompt, system } = req.body - console.log('output:', output ? 'SET' : undefined) - console.log('prompt:', prompt?.length) - console.log('system:', system?.length) + debug('output:', output?.length) + debug('prompt:', prompt?.length) + debug('system:', system?.length) const result = streamText({ model: model, prompt, @@ -186,12 +191,12 @@ function getProviderOptions() { } function onStreamError(error: unknown) { - console.log('error:', error) + console.error('error:', error) } function onStreamEnd({ finalStep, finishReason, text, usage }: GenerateTextEndEvent) { - console.log('reasoning:', finalStep.reasoningText) - console.log('response:', text) - console.log('usage:', usage) - console.log('finishReason:', finishReason) + debug('reasoning:', finalStep.reasoningText) + debug('response:', text) + debug('usage:', usage) + debug('finishReason:', finishReason) } From 078e13c5d9dd0d68acba727c83064d46a93861e8 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:26:42 -0700 Subject: [PATCH 08/28] Update README.md --- README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8d6e7cf..903b09f 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,12 @@ To get started [Setup](#setup) and [Configure](#configure) the server. No API Ke ### Features - Works with Claude, OpenAI, Gemini and OpenAI Compatible Providers -- Chat, Completion, and Object Endpoints -- Live Stream Results to Client +- Includes Chat, Completion, and Object Endpoints +- Supports Multiple Clients Simultaneously +- Live Streams the Results to the Client - Automatic Input Token Caching - Automatic Retry on API Failures - Deploy with Docker or Node -- Supports Multiple Clients Simultaneously - Plus all the [Client Features](https://github.com/cssnr/vitepress-chat?tab=readme-ov-file#features) Built with the [AI SDK](https://ai-sdk.dev/). @@ -116,6 +116,8 @@ Environment Variables. | `PORT` | `3000` | Server Port | | `DEBUG` | - | Set to `app` for debug logs | +Note: The `INSTRUCTIONS` variable also points to the `CHAT_INSTRUCTIONS` variable (recommended). + You must also set the API key for the `MODEL` you select. | Variable | Description | @@ -157,12 +159,17 @@ The value is only checked for valid JSON at startup and will fail at runtime if | `/completion` | `POST` | Use with [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | | `/object` | `POST` | Use with [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | +Note: The `/` endpoint also points to the `/chat` endpoint (recommended). + ### Chat To send System Instructions from the client, add them to the body. ```typescript -const chat = new Chat({ +import { useChat } from '@ai-sdk/vue' +import { DefaultChatTransport } from 'ai' + +const { messages, input, handleSubmit } = useChat({ transport: new DefaultChatTransport({ api: 'https://chat-server.cssnr.com/chat', headers: { Authorization: 'Basic Abc123=' }, @@ -176,7 +183,7 @@ Reference: ### Completion ```typescript -import { useCompletion } from '@ai-sdk/react' +import { useCompletion } from '@ai-sdk/vue' const { completion, complete, isLoading, stop } = useCompletion({ api: 'https://chat-server.cssnr.com/completion', @@ -190,7 +197,7 @@ Reference: ### Object ```typescript -import { useObject } from '@ai-sdk/react' +import { useObject } from '@ai-sdk/vue' const { object, submit } = useObject({ api: 'https://chat-server.cssnr.com/object', From 9cbea121307eecd30d7bd4e2adc1cec071a8c270 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:43:32 -0700 Subject: [PATCH 09/28] Update README.md --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 903b09f..73ee0b8 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ For a Portainer Deploy workflow see the [.github/workflows/deploy.yaml](https:// 💡 All variables are optional. The default `big-pickle` model works with NO API Key. -Environment Variables. +Environment Variables (can be placed in a `settings.env` file). | Variable | Default | Description | | :------------------------------------ | :--------------------------- | :---------------------------------- | @@ -161,7 +161,7 @@ The value is only checked for valid JSON at startup and will fail at runtime if Note: The `/` endpoint also points to the `/chat` endpoint (recommended). -### Chat +#### chat To send System Instructions from the client, add them to the body. @@ -180,7 +180,7 @@ const { messages, input, handleSubmit } = useChat({ Reference: -### Completion +#### completion ```typescript import { useCompletion } from '@ai-sdk/vue' @@ -194,7 +194,7 @@ const { completion, complete, isLoading, stop } = useCompletion({ Reference: -### Object +#### object ```typescript import { useObject } from '@ai-sdk/vue' @@ -215,6 +215,8 @@ submit({ }) ``` +Note: Both `system` and `output` are custom body parameters parsed by the server allowing the client to send these items. + Reference: ### VitePress Chat Plugin @@ -227,6 +229,8 @@ The client is currently available as a VitePress Plugin. ## Development +To enable debug logs set: `DEBUG=app` + This works with no configuration using the `big-pickle` model. You can set your environment variables in the `settings.env` file. If using `big-pickle` for testing it is much faster to disable reasoning. From 248c88bb325b59873b90a40ab33e5bed3c9fa71f Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:45:10 -0700 Subject: [PATCH 10/28] Update Dependencies and Workflows --- .github/workflows/build.yaml | 2 +- .github/workflows/deploy.yaml | 2 +- .github/workflows/draft.yaml | 2 +- .github/workflows/issue.yaml | 2 +- .github/workflows/labeler.yaml | 2 +- .github/workflows/lint.yaml | 4 +- package-lock.json | 268 ++++++++++++++++----------------- package.json | 22 +-- src/index.ts | 1 + 9 files changed, 153 insertions(+), 152 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fb2c838..b32ad09 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,7 +32,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: "Debug event.json" continue-on-error: true diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index c9c3c80..9d20930 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -35,7 +35,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: "Debug event.json" continue-on-error: true diff --git a/.github/workflows/draft.yaml b/.github/workflows/draft.yaml index 7f93c65..9c43df3 100644 --- a/.github/workflows/draft.yaml +++ b/.github/workflows/draft.yaml @@ -20,7 +20,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: "Draft Release Action" id: draft diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml index 1b1d2ae..e0bcf6e 100644 --- a/.github/workflows/issue.yaml +++ b/.github/workflows/issue.yaml @@ -17,7 +17,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: "Create App Token" id: app diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml index 95030e5..922b063 100644 --- a/.github/workflows/labeler.yaml +++ b/.github/workflows/labeler.yaml @@ -19,7 +19,7 @@ jobs: steps: - name: "Checkout Configs" - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: cssnr/configs ref: master diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 334bc85..5ea343e 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -19,7 +19,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: "Debug event.json" continue-on-error: true @@ -31,7 +31,7 @@ jobs: run: echo "$GITHUB_CTX" - name: "Setup Node" - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: 24 diff --git a/package-lock.json b/package-lock.json index 6a33da0..2d7de4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,40 +8,40 @@ "name": "chat-server", "version": "0.0.0", "dependencies": { - "@ai-sdk/anthropic": "^4.0.3", - "@ai-sdk/google": "^4.0.3", - "@ai-sdk/openai": "^4.0.4", - "@ai-sdk/openai-compatible": "^3.0.2", - "ai": "^7.0.8", + "@ai-sdk/anthropic": "^4.0.15", + "@ai-sdk/google": "^4.0.17", + "@ai-sdk/openai": "^4.0.15", + "@ai-sdk/openai-compatible": "^3.0.11", + "ai": "^7.0.29", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", "express": "^5.2.1", - "picomatch": "^4.0.4" + "picomatch": "^4.0.5" }, "devDependencies": { "@eslint/js": "^10.0.1", "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.0.1", + "@types/node": "^26.1.1", "@types/picomatch": "^4.0.3", - "eslint": "^10.6.0", + "eslint": "^10.7.0", "nodemon": "^3.1.14", - "prettier": "^3.9.4", - "tsx": "^4.22.4", + "prettier": "^3.9.5", + "tsx": "^4.23.1", "typescript": "^6.0.3", - "typescript-eslint": "^8.62.1" + "typescript-eslint": "^8.64.0" } }, "node_modules/@ai-sdk/anthropic": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.3.tgz", - "integrity": "sha512-USlJtQxkpbZy01evFpAOOtkELwDoWFlLrlJDM7B81KTqGVIZ60BMoEfJHZ/G4poK+t7tYFpHgDKalspc4TJKeg==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.15.tgz", + "integrity": "sha512-6iVlzqZjqqoHTmgO1WU9id/33pYykIRasWJFAMf1+d+nhju6d7566VRFsOGZ2W2GzgNtzKs8DGsYN7X5/wOGuw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2" + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10" }, "engines": { "node": ">=22" @@ -51,13 +51,13 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.6.tgz", - "integrity": "sha512-8GjssUxXeTd8tst2fYXNOFbtNNZFv3sG7aq7wKnQYrU2eB3JFR7np6o4F3Snp/fy/rJZSeH28LvjSWq5wkdL8Q==", + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.21.tgz", + "integrity": "sha512-GafyeyHFDKJjdtbyhCQ0aC2FORdyE56IxK45EZ1JLO1iVdg8XqEU3bwnRzI0+5S1XHV7W2qQsxkZnGYPbsFiXA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2", + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10", "@vercel/oidc": "3.2.0" }, "engines": { @@ -68,13 +68,13 @@ } }, "node_modules/@ai-sdk/google": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.3.tgz", - "integrity": "sha512-mTTQt9/+/traTwrM4+J3Ewea1WgQBHJC+3EVa3dqdOfN1iyOC9P76CpGCZdUyVTASfVCpUTKrgCxHUTl9S7q/A==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.17.tgz", + "integrity": "sha512-90YMrWWIz7HVqrtthaP60fqGhsTT8OXHskxJ9sPmD/bFBHqdbOBrtS9oKBWjO0XBAY8eo2Rd7Ai8zGKCTvPn9Q==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2" + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10" }, "engines": { "node": ">=22" @@ -84,13 +84,13 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.4.tgz", - "integrity": "sha512-p6VDEzEx52PIzRnFoUpiw6ABn07h4Rt+atL+M51W9SqwhselaZFRnz/pzv7LF9D1Gok5qfzOmR+JwvDDvDWS3w==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.15.tgz", + "integrity": "sha512-JpTLQp5RUbRcs5nOyPEu5NRdxZLUnD/uCyT3qzy26D+iunCeL7KJV58ER9kwisAKnTjWravfNblaQNiWr20M9A==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2" + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10" }, "engines": { "node": ">=22" @@ -100,13 +100,13 @@ } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.2.tgz", - "integrity": "sha512-C6qUMe+qSMn7HVr1a0v5H9BXYKhsT5uO4Q2VYVHYdT7johzx+d1gYgFMhkRIoheBpCl5NPhljR4I4sHMWWfz6w==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.11.tgz", + "integrity": "sha512-PKLyt5PfjV2maWJPZpm3W29vCc+BbyqSPAcghXhl+iIxzFNjpGPa6UD/8J0b9l7w/lJCZKuhcef9q+Gp5WqUvw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2" + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10" }, "engines": { "node": ">=22" @@ -116,9 +116,9 @@ } }, "node_modules/@ai-sdk/provider": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.1.tgz", - "integrity": "sha512-6p3C/vGqVIjcptBu1DnVd/BZJ2wWmV9TUv9192vT6ZvT9KNED8EwRTqyqFpoQZKgSbMDSvBSq3dqR524Nt/Crw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.3.tgz", + "integrity": "sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -128,12 +128,12 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.2.tgz", - "integrity": "sha512-EcmdjJb7yggsZPCbS3MFBpvAUnKaPW+QvanU5GzF00XCq0bqqAmvJ3MN19ejlmOETbW8sJNiq6qam48wTcbUNw==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.10.tgz", + "integrity": "sha512-uPyec0+85dwxZYXtb8qe8gCjhjDfxP4LCDo/uRQS/iG+FIgYbHPRhr/ys281udG90bTaE18+5cxWraYaf8oHCw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", + "@ai-sdk/provider": "4.0.3", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", "eventsource-parser": "^3.0.8" @@ -855,9 +855,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.2.tgz", + "integrity": "sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==", "dev": true, "license": "MIT", "dependencies": { @@ -889,9 +889,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", - "integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==", + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", "dev": true, "license": "MIT", "dependencies": { @@ -941,17 +941,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz", - "integrity": "sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz", + "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.62.1", - "@typescript-eslint/type-utils": "8.62.1", - "@typescript-eslint/utils": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/type-utils": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -964,15 +964,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.62.1", + "@typescript-eslint/parser": "^8.64.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", "dev": true, "license": "MIT", "engines": { @@ -980,16 +980,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.1.tgz", - "integrity": "sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz", + "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.62.1", - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", "debug": "^4.4.3" }, "engines": { @@ -1005,14 +1005,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.1.tgz", - "integrity": "sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.62.1", - "@typescript-eslint/types": "^8.62.1", + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", "debug": "^4.4.3" }, "engines": { @@ -1027,14 +1027,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.1.tgz", - "integrity": "sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", + "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1" + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1045,9 +1045,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz", - "integrity": "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", "dev": true, "license": "MIT", "engines": { @@ -1062,15 +1062,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.1.tgz", - "integrity": "sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz", + "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1", - "@typescript-eslint/utils": "8.62.1", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1087,9 +1087,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.1.tgz", - "integrity": "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, "license": "MIT", "engines": { @@ -1101,16 +1101,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.1.tgz", - "integrity": "sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.62.1", - "@typescript-eslint/tsconfig-utils": "8.62.1", - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1", + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1129,16 +1129,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.1.tgz", - "integrity": "sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz", + "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.62.1", - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1" + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1153,13 +1153,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.1.tgz", - "integrity": "sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", + "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/types": "8.64.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1222,14 +1222,14 @@ } }, "node_modules/ai": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.8.tgz", - "integrity": "sha512-vTEKl6fDBZ2IxBXTRaZOajf9W2Ev57Ju8iKtUvqlmDk8Z9BrEP4c22SWJsg1RcWHSFmJMSBa/s5dlUBHUq3YwA==", + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.29.tgz", + "integrity": "sha512-q+A+skhl6SyjWliU6W7zNYgDUrEk5SbNrCc5vt4S9n6+n6e7jSorDmu51hlhjDOsS7VwzWGCgbWFvvT3iomtvg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.6", - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2" + "@ai-sdk/gateway": "4.0.21", + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10" }, "engines": { "node": ">=22" @@ -1682,9 +1682,9 @@ } }, "node_modules/eslint": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz", - "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz", + "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -2180,9 +2180,9 @@ } }, "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -2627,9 +2627,9 @@ } }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "license": "MIT", "engines": { "node": ">=12" @@ -2649,9 +2649,9 @@ } }, "node_modules/prettier": { - "version": "3.9.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.4.tgz", - "integrity": "sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==", + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz", + "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==", "dev": true, "license": "MIT", "bin": { @@ -3043,9 +3043,9 @@ } }, "node_modules/tsx": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", - "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz", + "integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3120,16 +3120,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.62.1.tgz", - "integrity": "sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.64.0.tgz", + "integrity": "sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.62.1", - "@typescript-eslint/parser": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1", - "@typescript-eslint/utils": "8.62.1" + "@typescript-eslint/eslint-plugin": "8.64.0", + "@typescript-eslint/parser": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 6a81fcc..771f95e 100644 --- a/package.json +++ b/package.json @@ -16,29 +16,29 @@ "tsc": "npx tsc --noEmit" }, "dependencies": { - "@ai-sdk/anthropic": "^4.0.3", - "@ai-sdk/google": "^4.0.3", - "@ai-sdk/openai": "^4.0.4", - "@ai-sdk/openai-compatible": "^3.0.2", - "ai": "^7.0.8", + "@ai-sdk/anthropic": "^4.0.15", + "@ai-sdk/google": "^4.0.17", + "@ai-sdk/openai": "^4.0.15", + "@ai-sdk/openai-compatible": "^3.0.11", + "ai": "^7.0.29", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", "express": "^5.2.1", - "picomatch": "^4.0.4" + "picomatch": "^4.0.5" }, "devDependencies": { "@eslint/js": "^10.0.1", "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.0.1", + "@types/node": "^26.1.1", "@types/picomatch": "^4.0.3", - "eslint": "^10.6.0", + "eslint": "^10.7.0", "nodemon": "^3.1.14", - "prettier": "^3.9.4", - "tsx": "^4.22.4", + "prettier": "^3.9.5", + "tsx": "^4.23.1", "typescript": "^6.0.3", - "typescript-eslint": "^8.62.1" + "typescript-eslint": "^8.64.0" } } diff --git a/src/index.ts b/src/index.ts index feb0cad..bb805e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -81,6 +81,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { // debug('req.headers:', req.headers) // debug('authorization:', req.headers.authorization) const { messages, system } = req.body + debug('system:', system?.length) // debug('system:', system?.substring(0, 128)) const modelMessages = await convertToModelMessages(messages) debug('modelMessages:', modelMessages.length) From f8d1d69f0189b98c452283662006654d5eeb7279 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 19:59:48 -0700 Subject: [PATCH 11/28] Add Disable Endpoint Options --- AGENTS.md | 8 +++ README.md | 11 ++-- src/index.ts | 150 +++++++++++++++++++++++++++------------------------ 3 files changed, 95 insertions(+), 74 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index bb46bf5..c8cbb07 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,3 +15,11 @@ ALWAYS use the `npm run *` command | `npm run lint` | ESLint on `src/` | | `npm run tsc` | TypeScript Check Only `tsc --noEmit` | | `npm run prettier` | ALWAYS RUN AFTER EDITING FILES | + +## Endpoints + +| Endpoint | Links | +| :------------ | :-------------------------------------------------------------------------------------- | +| `/chat` | Client Docs [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) | +| `/completion` | Client Docs [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | +| `/object` | Client Docs [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | diff --git a/README.md b/README.md index 73ee0b8..7789a17 100644 --- a/README.md +++ b/README.md @@ -108,15 +108,18 @@ Environment Variables (can be placed in a `settings.env` file). | `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | | [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | | `MAX_TOKENS` | - | Max Output Tokens | -| `CHAT_INSTRUCTIONS` | - | System Instructions for Chat | -| `COMPLETION_INSTRUCTIONS` | - | System Instructions for Completion | -| `OBJECT_INSTRUCTIONS` | - | System Instructions for Object | +| `INSTRUCTIONS_CHAT` | - | System Instructions for Chat | +| `INSTRUCTIONS_COMPLETION` | - | System Instructions for Completion | +| `INSTRUCTIONS_OBJECT` | - | System Instructions for Object | +| `DISABLE_CHAT` | - | Disable the `/chat` endpoint | +| `DISABLE_COMPLETION` | - | Disable the `/completion` endpoint | +| `DISABLE_OBJECT` | - | Disable the `/object` endpoint | | `AI_SDK_LOG_WARNINGS` | - | Disable SDK Warnings | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | | `PORT` | `3000` | Server Port | | `DEBUG` | - | Set to `app` for debug logs | -Note: The `INSTRUCTIONS` variable also points to the `CHAT_INSTRUCTIONS` variable (recommended). +Note: The `INSTRUCTIONS` variable also points to the `INSTRUCTIONS_CHAT` variable (recommended). You must also set the API key for the `MODEL` you select. diff --git a/src/index.ts b/src/index.ts index bb805e7..5d080d0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,6 +30,10 @@ debug('debug enabled: app') console.log(`chat-server: ${process.env.APP_VERSION}`) +console.log('DISABLE_CHAT:', process.env.DISABLE_CHAT ? 'YES' : 'NO') +console.log('DISABLE_COMPLETION:', process.env.DISABLE_COMPLETION ? 'YES' : 'NO') +console.log('DISABLE_OBJECT:', process.env.DISABLE_OBJECT ? 'YES' : 'NO') + console.log('MODEL:', process.env.MODEL) console.log('ANTHROPIC_API_KEY:', process.env.ANTHROPIC_API_KEY ? 'SET' : undefined) console.log('OPENAI_API_KEY:', process.env.OPENAI_API_KEY ? 'SET' : undefined) @@ -55,10 +59,10 @@ const maxOutputTokens = process.env.MAX_TOKENS : undefined console.log(`maxOutputTokens: ${maxOutputTokens}`) console.log( - `CHAT_INSTRUCTIONS: ${process.env.INSTRUCTIONS || process.env.CHAT_INSTRUCTIONS}`, // NOSONAR + `INSTRUCTIONS_CHAT: ${process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT}`, // NOSONAR ) -console.log(`COMPLETION_INSTRUCTIONS: ${process.env.COMPLETION_INSTRUCTIONS}`) -console.log(`OBJECT_INSTRUCTIONS: ${process.env.OBJECT_INSTRUCTIONS}`) +console.log(`INSTRUCTIONS_COMPLETION: ${process.env.INSTRUCTIONS_COMPLETION}`) +console.log(`INSTRUCTIONS_OBJECT: ${process.env.INSTRUCTIONS_OBJECT}`) const model = getModel() console.log(`Loaded modelId: ${model.modelId}`) @@ -77,76 +81,82 @@ app.listen(port, () => console.log(`Listening on PORT: ${port}`)) // app.get('/app-health-check', (_req, res) => res.sendStatus(200)) -app.post(['/', '/chat'], async (req: Request, res: Response) => { - // debug('req.headers:', req.headers) - // debug('authorization:', req.headers.authorization) - const { messages, system } = req.body - debug('system:', system?.length) - // debug('system:', system?.substring(0, 128)) - const modelMessages = await convertToModelMessages(messages) - debug('modelMessages:', modelMessages.length) - const stream = createUIMessageStream({ - execute: ({ writer }) => { - const result = streamText({ - model: model, - messages: modelMessages, - system: system || process.env.INSTRUCTIONS || process.env.CHAT_INSTRUCTIONS, - maxOutputTokens, - providerOptions, - onError: onStreamError, - onEnd: onStreamEnd, - }) - writer.merge(toUIMessageStream({ stream: result.stream })) - }, - }) - pipeUIMessageStreamToResponse({ response: res, stream }) -}) - -app.post('/completion', async (req: Request, res: Response) => { - const { prompt, system } = req.body - debug('prompt:', prompt?.length) - debug('system:', system?.length) - const result = streamText({ - model: model, - prompt, - system: system || process.env.COMPLETION_INSTRUCTIONS, - maxOutputTokens, - providerOptions, - onError: onStreamError, - onEnd: onStreamEnd, - }) - const stream = createUIMessageStream({ - execute: ({ writer }) => { - writer.merge(toUIMessageStream({ stream: result.stream })) - }, - }) - pipeUIMessageStreamToResponse({ - response: res, - stream, - consumeSseStream: consumeStream, +if (!process.env.DISABLE_CHAT) { + app.post(['/', '/chat'], async (req: Request, res: Response) => { + // debug('req.headers:', req.headers) + // debug('authorization:', req.headers.authorization) + const { messages, system } = req.body + debug('system:', system?.length) + // debug('system:', system?.substring(0, 128)) + const modelMessages = await convertToModelMessages(messages) + debug('modelMessages:', modelMessages.length) + const stream = createUIMessageStream({ + execute: ({ writer }) => { + const result = streamText({ + model: model, + messages: modelMessages, + system: system || process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT, + maxOutputTokens, + providerOptions, + onError: onStreamError, + onEnd: onStreamEnd, + }) + writer.merge(toUIMessageStream({ stream: result.stream })) + }, + }) + pipeUIMessageStreamToResponse({ response: res, stream }) }) -}) - -app.post('/object', async (req: Request, res: Response) => { - const { output, prompt, system } = req.body - debug('output:', output?.length) - debug('prompt:', prompt?.length) - debug('system:', system?.length) - const result = streamText({ - model: model, - prompt, - system: system || process.env.OBJECT_INSTRUCTIONS, - maxOutputTokens, - providerOptions, - output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), - onError: onStreamError, - onEnd: onStreamEnd, +} + +if (!process.env.DISABLE_COMPLETION) { + app.post('/completion', async (req: Request, res: Response) => { + const { prompt, system } = req.body + debug('prompt:', prompt?.length) + debug('system:', system?.length) + const result = streamText({ + model: model, + prompt, + system: system || process.env.INSTRUCTIONS_COMPLETION, + maxOutputTokens, + providerOptions, + onError: onStreamError, + onEnd: onStreamEnd, + }) + const stream = createUIMessageStream({ + execute: ({ writer }) => { + writer.merge(toUIMessageStream({ stream: result.stream })) + }, + }) + pipeUIMessageStreamToResponse({ + response: res, + stream, + consumeSseStream: consumeStream, + }) }) - pipeTextStreamToResponse({ - response: res, - stream: toTextStream({ stream: result.stream }), +} + +if (!process.env.DISABLE_OBJECT) { + app.post('/object', async (req: Request, res: Response) => { + const { output, prompt, system } = req.body + debug('output:', output?.length) + debug('prompt:', prompt?.length) + debug('system:', system?.length) + const result = streamText({ + model: model, + prompt, + system: system || process.env.INSTRUCTIONS_OBJECT, + maxOutputTokens, + providerOptions, + output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), + onError: onStreamError, + onEnd: onStreamEnd, + }) + pipeTextStreamToResponse({ + response: res, + stream: toTextStream({ stream: result.stream }), + }) }) -}) +} function corsCallback( origin: string | undefined, From 888226e574d8b3363762c670ba00f1fe01603c4f Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 20:48:22 -0700 Subject: [PATCH 12/28] Update README.md --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7789a17..bd79901 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ To send System Instructions from the client, add them to the body. import { useChat } from '@ai-sdk/vue' import { DefaultChatTransport } from 'ai' -const { messages, input, handleSubmit } = useChat({ +const { messages, sendMessage, status, stop } = useChat({ transport: new DefaultChatTransport({ api: 'https://chat-server.cssnr.com/chat', headers: { Authorization: 'Basic Abc123=' }, @@ -193,6 +193,8 @@ const { completion, complete, isLoading, stop } = useCompletion({ headers: { Authorization: 'Basic Abc123=' }, body: { system: 'You are a helpful assistant.' }, }) + +await complete('Explain how to setup cssnr/chat-server') ``` Reference: @@ -201,20 +203,21 @@ Reference: ```typescript import { useObject } from '@ai-sdk/vue' +import { z } from 'zod' +import { zodToJsonSchema } from 'zod-to-json-schema' + +const schema = z.object({ name: z.string(), age: z.number() }) const { object, submit } = useObject({ api: 'https://chat-server.cssnr.com/object', - schema: z.object({ name: z.string(), age: z.number() }), headers: { Authorization: 'Basic Abc123=' }, + schema, }) submit({ system: 'You are a helpful assistant.', prompt: 'Extract the name and age from: John is 30 years old.', - output: { - type: 'object', - properties: { name: { type: 'string' }, age: { type: 'number' } }, - }, + output: zodToJsonSchema(schema), }) ``` From f69fec3b126ee05fbaab64deed8ae7893c3a9e6e Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 20:54:18 -0700 Subject: [PATCH 13/28] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bd79901..509a776 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ - [Setup](#setup) - [Configure](#configure) - [Client](#client) + - [Endpoints](#endpoints) - [VitePress Plugin](#vitepress-chat-plugin) - [Development](#development) - [Support](#support) From 8c080f0c290db4cc6a2f203c42fa6e2af4d5bc1e Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 21:09:54 -0700 Subject: [PATCH 14/28] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 509a776..0e6947a 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,8 @@ Note: The `/` endpoint also points to the `/chat` endpoint (recommended). #### chat +Reference: + To send System Instructions from the client, add them to the body. ```typescript @@ -182,10 +184,10 @@ const { messages, sendMessage, status, stop } = useChat({ }) ``` -Reference: - #### completion +Reference: + ```typescript import { useCompletion } from '@ai-sdk/vue' @@ -198,10 +200,10 @@ const { completion, complete, isLoading, stop } = useCompletion({ await complete('Explain how to setup cssnr/chat-server') ``` -Reference: - #### object +Reference: + ```typescript import { useObject } from '@ai-sdk/vue' import { z } from 'zod' @@ -224,8 +226,6 @@ submit({ Note: Both `system` and `output` are custom body parameters parsed by the server allowing the client to send these items. -Reference: - ### VitePress Chat Plugin The client is currently available as a VitePress Plugin. From fefc394cbcd9d21e8cf61b4355988def187b1487 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 21:50:46 -0700 Subject: [PATCH 15/28] Updates --- AGENTS.md | 15 ++++++--------- README.md | 6 +++--- package-lock.json | 16 ++++++++-------- package.json | 2 +- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c8cbb07..c861aaf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,9 +1,6 @@ # Agent Guide -Before answering any question that involves facts about ANYTHING, you MUST output at least one Read, WebFetch, or WebSearch tool call. -If your first output is text instead of a tool call, you have failed. - -- [index.ts](src/index.ts) — Single source Express.js ai-sdk server with single route `/` +- [index.ts](src/index.ts) — Single source Express.js AI-SDK server ## Commands @@ -18,8 +15,8 @@ ALWAYS use the `npm run *` command ## Endpoints -| Endpoint | Links | -| :------------ | :-------------------------------------------------------------------------------------- | -| `/chat` | Client Docs [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) | -| `/completion` | Client Docs [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | -| `/object` | Client Docs [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | +| Server Endpoint | Client | +| :-------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | +| [/chat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/create-ui-message-stream) | [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) | +| [/completion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/pipe-ui-message-stream-to-response) | [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | +| [/object](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) | [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | diff --git a/README.md b/README.md index 0e6947a..e017d54 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Built with the [AI SDK](https://ai-sdk.dev/). ## Setup -💡 The server works out-of-the box with NO environment variables. +💡 The server works out-of-the-box with NO environment variables. [![Deploy to Render](https://img.shields.io/badge/Deploy_to_Render-4351E8?style=for-the-badge&logo=render)](https://render.com/deploy?repo=https://github.com/cssnr/chat-server) @@ -150,7 +150,7 @@ PROVIDER_OPTIONS='{"openai":{"serviceTier":"flex","reasoningEffort":"low"}}' ``` You are responsible for providing valid options for the chosen model. -The SDK supports providing provider options for multiple provider simultaneously. +The SDK supports providing provider options for multiple providers simultaneously. The value is only checked for valid JSON at startup and will fail at runtime if it contains invalid options. ## Client @@ -197,7 +197,7 @@ const { completion, complete, isLoading, stop } = useCompletion({ body: { system: 'You are a helpful assistant.' }, }) -await complete('Explain how to setup cssnr/chat-server') +await complete('Explain how to set up cssnr/chat-server') ``` #### object diff --git a/package-lock.json b/package-lock.json index 2d7de4f..4f06126 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@ai-sdk/google": "^4.0.17", "@ai-sdk/openai": "^4.0.15", "@ai-sdk/openai-compatible": "^3.0.11", - "ai": "^7.0.29", + "ai": "^7.0.30", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -51,9 +51,9 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.21", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.21.tgz", - "integrity": "sha512-GafyeyHFDKJjdtbyhCQ0aC2FORdyE56IxK45EZ1JLO1iVdg8XqEU3bwnRzI0+5S1XHV7W2qQsxkZnGYPbsFiXA==", + "version": "4.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.22.tgz", + "integrity": "sha512-KdXJLa6O25fltJC/Lh8gnB1VaYMdX2mEKIalkohuItS+Ig523XxwK9e/Uuc66AvtrLIf7G51cDg3rDpP9mrxwQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", @@ -1222,12 +1222,12 @@ } }, "node_modules/ai": { - "version": "7.0.29", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.29.tgz", - "integrity": "sha512-q+A+skhl6SyjWliU6W7zNYgDUrEk5SbNrCc5vt4S9n6+n6e7jSorDmu51hlhjDOsS7VwzWGCgbWFvvT3iomtvg==", + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.30.tgz", + "integrity": "sha512-tm0gTAHSWBdDfW4P2mj+LlglGCUQTSA9ktyS2PsPgVhxNO9gYWTSnRAehiJ3h1lYsJBp1Zgbz3RH2lezJXagiw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.21", + "@ai-sdk/gateway": "4.0.22", "@ai-sdk/provider": "4.0.3", "@ai-sdk/provider-utils": "5.0.10" }, diff --git a/package.json b/package.json index 771f95e..6f6ee19 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@ai-sdk/google": "^4.0.17", "@ai-sdk/openai": "^4.0.15", "@ai-sdk/openai-compatible": "^3.0.11", - "ai": "^7.0.29", + "ai": "^7.0.30", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", From 22cd15444c95595e529162f7b5847304cc233393 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 21:53:21 -0700 Subject: [PATCH 16/28] Remove star-history --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index e017d54..bf90d6e 100644 --- a/README.md +++ b/README.md @@ -295,11 +295,3 @@ and [additional](https://cssnr.com/) open source projects. [![Ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/cssnr) For a full list of current projects visit: [https://cssnr.github.io/](https://cssnr.github.io/) - - - - - - Star History Chart - - From 538e5e91e20eb5147ad885b6256e3c8125aa3917 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 12:34:45 -0700 Subject: [PATCH 17/28] Add getBool --- .prettierrc.json | 2 +- README.md | 12 +++++---- package-lock.json | 66 +++++++++++++++++++++++------------------------ package.json | 12 ++++----- src/index.ts | 47 +++++++++++++++++---------------- 5 files changed, 72 insertions(+), 67 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index 577fddf..d7dd7f6 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/prettierrc", "semi": false, "singleQuote": true, - "printWidth": 90, + "printWidth": 94, "overrides": [ { "files": ["**/*.vue"], diff --git a/README.md b/README.md index bf90d6e..701595d 100644 --- a/README.md +++ b/README.md @@ -112,15 +112,17 @@ Environment Variables (can be placed in a `settings.env` file). | `INSTRUCTIONS_CHAT` | - | System Instructions for Chat | | `INSTRUCTIONS_COMPLETION` | - | System Instructions for Completion | | `INSTRUCTIONS_OBJECT` | - | System Instructions for Object | -| `DISABLE_CHAT` | - | Disable the `/chat` endpoint | -| `DISABLE_COMPLETION` | - | Disable the `/completion` endpoint | -| `DISABLE_OBJECT` | - | Disable the `/object` endpoint | -| `AI_SDK_LOG_WARNINGS` | - | Disable SDK Warnings | +| `DISABLE_CHAT`**¹** | - | Disable the `/chat` endpoint | +| `DISABLE_COMPLETION`**¹** | - | Disable the `/completion` endpoint | +| `DISABLE_OBJECT`**¹** | - | Disable the `/object` endpoint | +| `AI_SDK_LOG_WARNINGS`**¹** | - | Disable SDK Warnings | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | | `PORT` | `3000` | Server Port | | `DEBUG` | - | Set to `app` for debug logs | -Note: The `INSTRUCTIONS` variable also points to the `INSTRUCTIONS_CHAT` variable (recommended). +> **¹** Boolean Variables. **True** values include: `['1', 't', 'true', 'y', 'yes', 'on']` + +The `INSTRUCTIONS` variable also points to the `INSTRUCTIONS_CHAT` variable (recommended). You must also set the API key for the `MODEL` you select. diff --git a/package-lock.json b/package-lock.json index 4f06126..987787f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,11 @@ "name": "chat-server", "version": "0.0.0", "dependencies": { - "@ai-sdk/anthropic": "^4.0.15", - "@ai-sdk/google": "^4.0.17", - "@ai-sdk/openai": "^4.0.15", - "@ai-sdk/openai-compatible": "^3.0.11", - "ai": "^7.0.30", + "@ai-sdk/anthropic": "^4.0.16", + "@ai-sdk/google": "^4.0.18", + "@ai-sdk/openai": "^4.0.16", + "@ai-sdk/openai-compatible": "^3.0.12", + "ai": "^7.0.31", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -35,13 +35,13 @@ } }, "node_modules/@ai-sdk/anthropic": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.15.tgz", - "integrity": "sha512-6iVlzqZjqqoHTmgO1WU9id/33pYykIRasWJFAMf1+d+nhju6d7566VRFsOGZ2W2GzgNtzKs8DGsYN7X5/wOGuw==", + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.16.tgz", + "integrity": "sha512-vyH4D6Auih5H2xvVzzh2ep5pbdWiaV7JDC+jHUE7zZJ5Kyv0TteLav4DrOgHzRuyv8ptfUSqFF6Y8//f/Ec0fQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10" + "@ai-sdk/provider-utils": "5.0.11" }, "engines": { "node": ">=22" @@ -51,13 +51,13 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.22", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.22.tgz", - "integrity": "sha512-KdXJLa6O25fltJC/Lh8gnB1VaYMdX2mEKIalkohuItS+Ig523XxwK9e/Uuc66AvtrLIf7G51cDg3rDpP9mrxwQ==", + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.23.tgz", + "integrity": "sha512-f85diFdPMXYJpxCjOYZchMQkRH8h3r6lhK4Q2xmzJ7UA2OQ80L3W7tFu61742xGQK7zHWm5AhxYhNuc50H9SGQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10", + "@ai-sdk/provider-utils": "5.0.11", "@vercel/oidc": "3.2.0" }, "engines": { @@ -68,13 +68,13 @@ } }, "node_modules/@ai-sdk/google": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.17.tgz", - "integrity": "sha512-90YMrWWIz7HVqrtthaP60fqGhsTT8OXHskxJ9sPmD/bFBHqdbOBrtS9oKBWjO0XBAY8eo2Rd7Ai8zGKCTvPn9Q==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.18.tgz", + "integrity": "sha512-NRbXRAasXLgFLiZDTsDUiTUuQtEUDVZoqruB5Px3geltTEqOzOo2eHN5WnDp0/OgxwnrNH4olV/TVetza0PzmQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10" + "@ai-sdk/provider-utils": "5.0.11" }, "engines": { "node": ">=22" @@ -84,13 +84,13 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.15.tgz", - "integrity": "sha512-JpTLQp5RUbRcs5nOyPEu5NRdxZLUnD/uCyT3qzy26D+iunCeL7KJV58ER9kwisAKnTjWravfNblaQNiWr20M9A==", + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.16.tgz", + "integrity": "sha512-Yh+PsXaf9NbN7oA3oKwOuyjTiHMPD75phf3SqGbDNNKQ3Yj3oTntp/WhO3nCHIPA6gr5/1lyridQokmOpPf9oQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10" + "@ai-sdk/provider-utils": "5.0.11" }, "engines": { "node": ">=22" @@ -100,13 +100,13 @@ } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.11.tgz", - "integrity": "sha512-PKLyt5PfjV2maWJPZpm3W29vCc+BbyqSPAcghXhl+iIxzFNjpGPa6UD/8J0b9l7w/lJCZKuhcef9q+Gp5WqUvw==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.12.tgz", + "integrity": "sha512-tN9BUb4jUGjqtbRPUsziLxASfogLNICcq/Qrwr55vpnzKvprV6Ukl8ynED2lZaNrAHU5U4zlGnyU/iuYrjQK6g==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10" + "@ai-sdk/provider-utils": "5.0.11" }, "engines": { "node": ">=22" @@ -128,9 +128,9 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.10.tgz", - "integrity": "sha512-uPyec0+85dwxZYXtb8qe8gCjhjDfxP4LCDo/uRQS/iG+FIgYbHPRhr/ys281udG90bTaE18+5cxWraYaf8oHCw==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.11.tgz", + "integrity": "sha512-7/96wE+ZsKB35iS9ASyllrE4Ym/EolXEB7AkuJ5FI++fmS85BVTAs77890C+1Z2jwHfBKjBQSBmsliOsAh0iFQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", @@ -1222,14 +1222,14 @@ } }, "node_modules/ai": { - "version": "7.0.30", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.30.tgz", - "integrity": "sha512-tm0gTAHSWBdDfW4P2mj+LlglGCUQTSA9ktyS2PsPgVhxNO9gYWTSnRAehiJ3h1lYsJBp1Zgbz3RH2lezJXagiw==", + "version": "7.0.31", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.31.tgz", + "integrity": "sha512-pJfwKXjF5kw0rKRTePwYo60EfWb8wfzJAgf3ojln/YkOsVVKttzZAJVcRPsg37Z3a06ZdKkxX+DSrMAFlPm5Mw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.22", + "@ai-sdk/gateway": "4.0.23", "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10" + "@ai-sdk/provider-utils": "5.0.11" }, "engines": { "node": ">=22" diff --git a/package.json b/package.json index 6f6ee19..5bb4550 100644 --- a/package.json +++ b/package.json @@ -9,18 +9,18 @@ "dev": "nodemon --exec tsx src/index.ts", "lint": "npx eslint src", "prepare": "npm run build", - "prettier:": "npm run prettier:write", + "prettier": "npm run prettier:write", "prettier:check": "npx prettier --check .", "prettier:write": "npx prettier --write .", "start": "node dist/index.js", "tsc": "npx tsc --noEmit" }, "dependencies": { - "@ai-sdk/anthropic": "^4.0.15", - "@ai-sdk/google": "^4.0.17", - "@ai-sdk/openai": "^4.0.15", - "@ai-sdk/openai-compatible": "^3.0.11", - "ai": "^7.0.30", + "@ai-sdk/anthropic": "^4.0.16", + "@ai-sdk/google": "^4.0.18", + "@ai-sdk/openai": "^4.0.16", + "@ai-sdk/openai-compatible": "^3.0.12", + "ai": "^7.0.31", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", diff --git a/src/index.ts b/src/index.ts index 5d080d0..167f550 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,16 +23,16 @@ import { dotenv.config({ path: 'settings.env' }) -console.log(`DEBUG: ${process.env.DEBUG}`) +console.log('chat-server:', process.env.APP_VERSION) + +console.log('DEBUG:', process.env.DEBUG) createDebug.enable(process.env.DEBUG ?? '') const debug = createDebug('app') debug('debug enabled: app') -console.log(`chat-server: ${process.env.APP_VERSION}`) - -console.log('DISABLE_CHAT:', process.env.DISABLE_CHAT ? 'YES' : 'NO') -console.log('DISABLE_COMPLETION:', process.env.DISABLE_COMPLETION ? 'YES' : 'NO') -console.log('DISABLE_OBJECT:', process.env.DISABLE_OBJECT ? 'YES' : 'NO') +console.log('DISABLE_CHAT:', getBool(process.env.DISABLE_CHAT)) +console.log('DISABLE_COMPLETION:', getBool(process.env.DISABLE_COMPLETION)) +console.log('DISABLE_OBJECT:', getBool(process.env.DISABLE_OBJECT)) console.log('MODEL:', process.env.MODEL) console.log('ANTHROPIC_API_KEY:', process.env.ANTHROPIC_API_KEY ? 'SET' : undefined) @@ -41,12 +41,13 @@ console.log( 'GOOGLE_GENERATIVE_AI_API_KEY:', process.env.GOOGLE_GENERATIVE_AI_API_KEY ? 'SET' : undefined, ) + console.log('PROVIDER_API_KEY:', process.env.PROVIDER_API_KEY ? 'SET' : undefined) const baseURL = process.env.BASE_URL || 'https://opencode.ai/zen/v1' // NOSONAR console.log('BASE_URL:', baseURL) -if (process.env.AI_SDK_LOG_WARNINGS) globalThis.AI_SDK_LOG_WARNINGS = false -console.log('AI_SDK_LOG_WARNINGS:', process.env.AI_SDK_LOG_WARNINGS) +console.log('AI_SDK_LOG_WARNINGS:', getBool(process.env.AI_SDK_LOG_WARNINGS)) +if (!getBool(process.env.AI_SDK_LOG_WARNINGS)) globalThis.AI_SDK_LOG_WARNINGS = false const corsOrigins = process.env.CORS_ORIGINS?.split(/[, \n\r]+/) .map((s) => s.trim()) @@ -57,15 +58,14 @@ console.log('corsOrigins:', corsOrigins) const maxOutputTokens = process.env.MAX_TOKENS ? Number.parseInt(process.env.MAX_TOKENS) : undefined -console.log(`maxOutputTokens: ${maxOutputTokens}`) -console.log( - `INSTRUCTIONS_CHAT: ${process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT}`, // NOSONAR -) -console.log(`INSTRUCTIONS_COMPLETION: ${process.env.INSTRUCTIONS_COMPLETION}`) -console.log(`INSTRUCTIONS_OBJECT: ${process.env.INSTRUCTIONS_OBJECT}`) +console.log('maxOutputTokens:', maxOutputTokens) + +console.log('INSTRUCTIONS_CHAT:', process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT) // NOSONAR +console.log('INSTRUCTIONS_COMPLETION:', process.env.INSTRUCTIONS_COMPLETION) +console.log('INSTRUCTIONS_OBJECT:', process.env.INSTRUCTIONS_OBJECT) const model = getModel() -console.log(`Loaded modelId: ${model.modelId}`) +console.log('Loaded modelId:', model.modelId) const providerOptions = getProviderOptions() console.log('providerOptions:', providerOptions) @@ -74,14 +74,12 @@ const app = express() const port = process.env.PORT || 3000 // NOSONAR app.use(express.json({ limit: '10mb' })) - app.use(cors({ origin: corsCallback })) - app.listen(port, () => console.log(`Listening on PORT: ${port}`)) // app.get('/app-health-check', (_req, res) => res.sendStatus(200)) -if (!process.env.DISABLE_CHAT) { +if (!getBool(process.env.DISABLE_CHAT)) { app.post(['/', '/chat'], async (req: Request, res: Response) => { // debug('req.headers:', req.headers) // debug('authorization:', req.headers.authorization) @@ -108,7 +106,7 @@ if (!process.env.DISABLE_CHAT) { }) } -if (!process.env.DISABLE_COMPLETION) { +if (!getBool(process.env.DISABLE_COMPLETION)) { app.post('/completion', async (req: Request, res: Response) => { const { prompt, system } = req.body debug('prompt:', prompt?.length) @@ -135,7 +133,7 @@ if (!process.env.DISABLE_COMPLETION) { }) } -if (!process.env.DISABLE_OBJECT) { +if (!getBool(process.env.DISABLE_OBJECT)) { app.post('/object', async (req: Request, res: Response) => { const { output, prompt, system } = req.body debug('output:', output?.length) @@ -158,6 +156,11 @@ if (!process.env.DISABLE_OBJECT) { }) } +function getBool(value: string | undefined): boolean { + if (!value) return false + return ['1', 't', 'true', 'y', 'yes', 'on'].includes(value.trim().toLowerCase()) +} + function corsCallback( origin: string | undefined, callback: (err: Error | null, origin?: boolean) => void, @@ -196,8 +199,8 @@ function getProviderOptions() { if (!process.env.PROVIDER_OPTIONS) return try { return JSON.parse(process.env.PROVIDER_OPTIONS) - } catch { - console.error('parsing PROVIDER_OPTIONS as JSON') + } catch (e) { + console.error('error parsing PROVIDER_OPTIONS as JSON:', e) } } From 21ae90fee940179487c8d8fe4deb6718aced4a71 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 14:10:23 -0700 Subject: [PATCH 18/28] Update README.md --- README.md | 53 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 701595d..5301149 100644 --- a/README.md +++ b/README.md @@ -103,27 +103,25 @@ For a Portainer Deploy workflow see the [.github/workflows/deploy.yaml](https:// Environment Variables (can be placed in a `settings.env` file). -| Variable | Default | Description | -| :------------------------------------ | :--------------------------- | :---------------------------------- | -| `MODEL` | `big-pickle` | Model to Use | -| `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | -| [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | -| `MAX_TOKENS` | - | Max Output Tokens | -| `INSTRUCTIONS_CHAT` | - | System Instructions for Chat | -| `INSTRUCTIONS_COMPLETION` | - | System Instructions for Completion | -| `INSTRUCTIONS_OBJECT` | - | System Instructions for Object | -| `DISABLE_CHAT`**¹** | - | Disable the `/chat` endpoint | -| `DISABLE_COMPLETION`**¹** | - | Disable the `/completion` endpoint | -| `DISABLE_OBJECT`**¹** | - | Disable the `/object` endpoint | -| `AI_SDK_LOG_WARNINGS`**¹** | - | Disable SDK Warnings | -| `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | -| `PORT` | `3000` | Server Port | -| `DEBUG` | - | Set to `app` for debug logs | +| Variable | Default | Description | +| :--------------------------------------- | :--------------------------- | :---------------------------------- | +| `MODEL` | `big-pickle` | Model to Use | +| `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | +| [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | +| `MAX_TOKENS` | - | Max Output Tokens | +| [INSTRUCTIONS_CHAT](#INSTRUCTIONS) | - | System Instructions for Chat | +| [INSTRUCTIONS_COMPLETION](#INSTRUCTIONS) | - | System Instructions for Completion | +| [INSTRUCTIONS_OBJECT](#INSTRUCTIONS) | - | System Instructions for Object | +| `DISABLE_CHAT`**¹** | - | Disable the `/chat` Endpoint | +| `DISABLE_COMPLETION`**¹** | - | Disable the `/completion` Endpoint | +| `DISABLE_OBJECT`**¹** | - | Disable the `/object` Endpoint | +| `AI_SDK_LOG_WARNINGS`**¹** | - | Enable SDK Warnings Logging | +| `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | +| `PORT` | `3000` | Server Port | +| `DEBUG` | - | Set to `app` for Debug Logging | > **¹** Boolean Variables. **True** values include: `['1', 't', 'true', 'y', 'yes', 'on']` -The `INSTRUCTIONS` variable also points to the `INSTRUCTIONS_CHAT` variable (recommended). - You must also set the API key for the `MODEL` you select. | Variable | Description | @@ -135,6 +133,13 @@ You must also set the API key for the `MODEL` you select. The `PROVIDER_API_KEY` is optional for free-tier models like `big-pickle`. +#### INSTRUCTIONS + +There are mechanisms to override the instructions per-call for all clients on all endpoints. +These are used as fallback when those instructions are not sent for configurations where this is desired. + +The `INSTRUCTIONS` variable (legacy) also points to the `INSTRUCTIONS_CHAT` variable (recommended). + #### PROVIDER_OPTIONS Provider Options: @@ -165,14 +170,12 @@ The value is only checked for valid JSON at startup and will fail at runtime if | `/completion` | `POST` | Use with [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | | `/object` | `POST` | Use with [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | -Note: The `/` endpoint also points to the `/chat` endpoint (recommended). +Note: The `/` endpoint (legacy) also points to the `/chat` endpoint (recommended). #### chat Reference: -To send System Instructions from the client, add them to the body. - ```typescript import { useChat } from '@ai-sdk/vue' import { DefaultChatTransport } from 'ai' @@ -186,6 +189,8 @@ const { messages, sendMessage, status, stop } = useChat({ }) ``` +To send System Instructions from the client, add them to the body. + #### completion Reference: @@ -202,6 +207,8 @@ const { completion, complete, isLoading, stop } = useCompletion({ await complete('Explain how to set up cssnr/chat-server') ``` +To send System Instructions from the client, add them to the body. + #### object Reference: @@ -226,7 +233,9 @@ submit({ }) ``` -Note: Both `system` and `output` are custom body parameters parsed by the server allowing the client to send these items. +To send System Instructions and Output Schema from the client, add them to the body. + +Note: Both `system` and `output` are custom body parameters parsed by the server. ### VitePress Chat Plugin From 64a9ae0595588cf385e679b83e792457dc1299aa Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:20:30 -0700 Subject: [PATCH 19/28] Add DISABLE_CLIENT_INSTRUCTIONS --- README.md | 5 ++++- src/index.ts | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5301149..09071aa 100644 --- a/README.md +++ b/README.md @@ -107,14 +107,15 @@ Environment Variables (can be placed in a `settings.env` file). | :--------------------------------------- | :--------------------------- | :---------------------------------- | | `MODEL` | `big-pickle` | Model to Use | | `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | -| [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | | `MAX_TOKENS` | - | Max Output Tokens | +| [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | | [INSTRUCTIONS_CHAT](#INSTRUCTIONS) | - | System Instructions for Chat | | [INSTRUCTIONS_COMPLETION](#INSTRUCTIONS) | - | System Instructions for Completion | | [INSTRUCTIONS_OBJECT](#INSTRUCTIONS) | - | System Instructions for Object | | `DISABLE_CHAT`**¹** | - | Disable the `/chat` Endpoint | | `DISABLE_COMPLETION`**¹** | - | Disable the `/completion` Endpoint | | `DISABLE_OBJECT`**¹** | - | Disable the `/object` Endpoint | +| `DISABLE_CLIENT_INSTRUCTIONS`**¹** | - | Ignore Client System Instructions | | `AI_SDK_LOG_WARNINGS`**¹** | - | Enable SDK Warnings Logging | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | | `PORT` | `3000` | Server Port | @@ -140,6 +141,8 @@ These are used as fallback when those instructions are not sent for configuratio The `INSTRUCTIONS` variable (legacy) also points to the `INSTRUCTIONS_CHAT` variable (recommended). +To disable the clients ability to send custom instructions set `DISABLE_CLIENT_INSTRUCTIONS=true` + #### PROVIDER_OPTIONS Provider Options: diff --git a/src/index.ts b/src/index.ts index 167f550..0ddbfcb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -60,7 +60,11 @@ const maxOutputTokens = process.env.MAX_TOKENS : undefined console.log('maxOutputTokens:', maxOutputTokens) -console.log('INSTRUCTIONS_CHAT:', process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT) // NOSONAR +const disableInstructions = getBool(process.env.DISABLE_CLIENT_INSTRUCTIONS) +console.log('disableInstructions:', disableInstructions) + +process.env.INSTRUCTIONS_CHAT = process.env.INSTRUCTIONS_CHAT || process.env.INSTRUCTIONS // NOSONAR +console.log('INSTRUCTIONS_CHAT:', process.env.INSTRUCTIONS_CHAT) console.log('INSTRUCTIONS_COMPLETION:', process.env.INSTRUCTIONS_COMPLETION) console.log('INSTRUCTIONS_OBJECT:', process.env.INSTRUCTIONS_OBJECT) @@ -93,7 +97,7 @@ if (!getBool(process.env.DISABLE_CHAT)) { const result = streamText({ model: model, messages: modelMessages, - system: system || process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, maxOutputTokens, providerOptions, onError: onStreamError, @@ -114,7 +118,7 @@ if (!getBool(process.env.DISABLE_COMPLETION)) { const result = streamText({ model: model, prompt, - system: system || process.env.INSTRUCTIONS_COMPLETION, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, maxOutputTokens, providerOptions, onError: onStreamError, @@ -142,7 +146,7 @@ if (!getBool(process.env.DISABLE_OBJECT)) { const result = streamText({ model: model, prompt, - system: system || process.env.INSTRUCTIONS_OBJECT, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, maxOutputTokens, providerOptions, output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), From 295fc7c7de56bb67cfd76c93b2d34c99e1e4fd41 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:13:13 -0700 Subject: [PATCH 20/28] Remove Disable Options --- README.md | 3 -- src/index.ts | 144 ++++++++++++++++++++++++--------------------------- 2 files changed, 67 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 09071aa..1273187 100644 --- a/README.md +++ b/README.md @@ -112,9 +112,6 @@ Environment Variables (can be placed in a `settings.env` file). | [INSTRUCTIONS_CHAT](#INSTRUCTIONS) | - | System Instructions for Chat | | [INSTRUCTIONS_COMPLETION](#INSTRUCTIONS) | - | System Instructions for Completion | | [INSTRUCTIONS_OBJECT](#INSTRUCTIONS) | - | System Instructions for Object | -| `DISABLE_CHAT`**¹** | - | Disable the `/chat` Endpoint | -| `DISABLE_COMPLETION`**¹** | - | Disable the `/completion` Endpoint | -| `DISABLE_OBJECT`**¹** | - | Disable the `/object` Endpoint | | `DISABLE_CLIENT_INSTRUCTIONS`**¹** | - | Ignore Client System Instructions | | `AI_SDK_LOG_WARNINGS`**¹** | - | Enable SDK Warnings Logging | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | diff --git a/src/index.ts b/src/index.ts index 0ddbfcb..39f328b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,10 +30,6 @@ createDebug.enable(process.env.DEBUG ?? '') const debug = createDebug('app') debug('debug enabled: app') -console.log('DISABLE_CHAT:', getBool(process.env.DISABLE_CHAT)) -console.log('DISABLE_COMPLETION:', getBool(process.env.DISABLE_COMPLETION)) -console.log('DISABLE_OBJECT:', getBool(process.env.DISABLE_OBJECT)) - console.log('MODEL:', process.env.MODEL) console.log('ANTHROPIC_API_KEY:', process.env.ANTHROPIC_API_KEY ? 'SET' : undefined) console.log('OPENAI_API_KEY:', process.env.OPENAI_API_KEY ? 'SET' : undefined) @@ -83,82 +79,76 @@ app.listen(port, () => console.log(`Listening on PORT: ${port}`)) // app.get('/app-health-check', (_req, res) => res.sendStatus(200)) -if (!getBool(process.env.DISABLE_CHAT)) { - app.post(['/', '/chat'], async (req: Request, res: Response) => { - // debug('req.headers:', req.headers) - // debug('authorization:', req.headers.authorization) - const { messages, system } = req.body - debug('system:', system?.length) - // debug('system:', system?.substring(0, 128)) - const modelMessages = await convertToModelMessages(messages) - debug('modelMessages:', modelMessages.length) - const stream = createUIMessageStream({ - execute: ({ writer }) => { - const result = streamText({ - model: model, - messages: modelMessages, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, - maxOutputTokens, - providerOptions, - onError: onStreamError, - onEnd: onStreamEnd, - }) - writer.merge(toUIMessageStream({ stream: result.stream })) - }, - }) - pipeUIMessageStreamToResponse({ response: res, stream }) +app.post(['/', '/chat'], async (req: Request, res: Response) => { + // debug('req.headers:', req.headers) + // debug('authorization:', req.headers.authorization) + const { messages, system } = req.body + debug('system:', system?.length) + // debug('system:', system?.substring(0, 128)) + const modelMessages = await convertToModelMessages(messages) + debug('modelMessages:', modelMessages.length) + const stream = createUIMessageStream({ + execute: ({ writer }) => { + const result = streamText({ + model: model, + messages: modelMessages, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, + maxOutputTokens, + providerOptions, + onError: onStreamError, + onEnd: onStreamEnd, + }) + writer.merge(toUIMessageStream({ stream: result.stream })) + }, }) -} - -if (!getBool(process.env.DISABLE_COMPLETION)) { - app.post('/completion', async (req: Request, res: Response) => { - const { prompt, system } = req.body - debug('prompt:', prompt?.length) - debug('system:', system?.length) - const result = streamText({ - model: model, - prompt, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, - maxOutputTokens, - providerOptions, - onError: onStreamError, - onEnd: onStreamEnd, - }) - const stream = createUIMessageStream({ - execute: ({ writer }) => { - writer.merge(toUIMessageStream({ stream: result.stream })) - }, - }) - pipeUIMessageStreamToResponse({ - response: res, - stream, - consumeSseStream: consumeStream, - }) + pipeUIMessageStreamToResponse({ response: res, stream }) +}) + +app.post('/completion', async (req: Request, res: Response) => { + const { prompt, system } = req.body + debug('prompt:', prompt?.length) + debug('system:', system?.length) + const result = streamText({ + model: model, + prompt, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, + maxOutputTokens, + providerOptions, + onError: onStreamError, + onEnd: onStreamEnd, }) -} - -if (!getBool(process.env.DISABLE_OBJECT)) { - app.post('/object', async (req: Request, res: Response) => { - const { output, prompt, system } = req.body - debug('output:', output?.length) - debug('prompt:', prompt?.length) - debug('system:', system?.length) - const result = streamText({ - model: model, - prompt, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, - maxOutputTokens, - providerOptions, - output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), - onError: onStreamError, - onEnd: onStreamEnd, - }) - pipeTextStreamToResponse({ - response: res, - stream: toTextStream({ stream: result.stream }), - }) + const stream = createUIMessageStream({ + execute: ({ writer }) => { + writer.merge(toUIMessageStream({ stream: result.stream })) + }, }) -} + pipeUIMessageStreamToResponse({ + response: res, + stream, + consumeSseStream: consumeStream, + }) +}) + +app.post('/object', async (req: Request, res: Response) => { + const { output, prompt, system } = req.body + debug('output:', output?.length) + debug('prompt:', prompt?.length) + debug('system:', system?.length) + const result = streamText({ + model: model, + prompt, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, + maxOutputTokens, + providerOptions, + output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), + onError: onStreamError, + onEnd: onStreamEnd, + }) + pipeTextStreamToResponse({ + response: res, + stream: toTextStream({ stream: result.stream }), + }) +}) function getBool(value: string | undefined): boolean { if (!value) return false From 512527c954d6de4ff6d952e67f79d1b5ea4009d1 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 21:40:41 -0700 Subject: [PATCH 21/28] Update system to instructions --- src/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 39f328b..ea26d82 100644 --- a/src/index.ts +++ b/src/index.ts @@ -92,7 +92,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { const result = streamText({ model: model, messages: modelMessages, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, + instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, maxOutputTokens, providerOptions, onError: onStreamError, @@ -111,7 +111,7 @@ app.post('/completion', async (req: Request, res: Response) => { const result = streamText({ model: model, prompt, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, + instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, maxOutputTokens, providerOptions, onError: onStreamError, @@ -137,7 +137,7 @@ app.post('/object', async (req: Request, res: Response) => { const result = streamText({ model: model, prompt, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, + instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, maxOutputTokens, providerOptions, output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), @@ -198,11 +198,11 @@ function getProviderOptions() { } } -function onStreamError(error: unknown) { +async function onStreamError({ error }: { error: unknown }) { console.error('error:', error) } -function onStreamEnd({ finalStep, finishReason, text, usage }: GenerateTextEndEvent) { +async function onStreamEnd({ finalStep, finishReason, text, usage }: GenerateTextEndEvent) { debug('reasoning:', finalStep.reasoningText) debug('response:', text) debug('usage:', usage) From e4c38ab11b53d6ef02c33c39f8e288257cc8f00d Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 23:14:15 -0700 Subject: [PATCH 22/28] Update Client system to instructions --- .prettierrc.json | 2 +- README.md | 8 ++++---- src/index.ts | 23 +++++++++++++---------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index d7dd7f6..a1ceab5 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/prettierrc", "semi": false, "singleQuote": true, - "printWidth": 94, + "printWidth": 96, "overrides": [ { "files": ["**/*.vue"], diff --git a/README.md b/README.md index 1273187..ffcdc48 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ const { messages, sendMessage, status, stop } = useChat({ transport: new DefaultChatTransport({ api: 'https://chat-server.cssnr.com/chat', headers: { Authorization: 'Basic Abc123=' }, - body: { system: 'You are a helpful assistant.' }, + body: { instructions: 'You are a helpful assistant.' }, }), }) ``` @@ -201,7 +201,7 @@ import { useCompletion } from '@ai-sdk/vue' const { completion, complete, isLoading, stop } = useCompletion({ api: 'https://chat-server.cssnr.com/completion', headers: { Authorization: 'Basic Abc123=' }, - body: { system: 'You are a helpful assistant.' }, + body: { instructions: 'You are a helpful assistant.' }, }) await complete('Explain how to set up cssnr/chat-server') @@ -227,7 +227,7 @@ const { object, submit } = useObject({ }) submit({ - system: 'You are a helpful assistant.', + instructions: 'You are a helpful assistant.', prompt: 'Extract the name and age from: John is 30 years old.', output: zodToJsonSchema(schema), }) @@ -235,7 +235,7 @@ submit({ To send System Instructions and Output Schema from the client, add them to the body. -Note: Both `system` and `output` are custom body parameters parsed by the server. +Note: Both `instructions` and `output` are custom body parameters parsed by the server. ### VitePress Chat Plugin diff --git a/src/index.ts b/src/index.ts index ea26d82..3361912 100644 --- a/src/index.ts +++ b/src/index.ts @@ -82,9 +82,9 @@ app.listen(port, () => console.log(`Listening on PORT: ${port}`)) app.post(['/', '/chat'], async (req: Request, res: Response) => { // debug('req.headers:', req.headers) // debug('authorization:', req.headers.authorization) - const { messages, system } = req.body - debug('system:', system?.length) - // debug('system:', system?.substring(0, 128)) + const { instructions, messages, system } = req.body + debug('instructions:', (instructions || system)?.length) + // debug('instructions:', (instructions || system)?.substring(0, 128)) const modelMessages = await convertToModelMessages(messages) debug('modelMessages:', modelMessages.length) const stream = createUIMessageStream({ @@ -92,7 +92,8 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { const result = streamText({ model: model, messages: modelMessages, - instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, + instructions: + (!disableInstructions && (instructions || system)) || process.env.INSTRUCTIONS_CHAT, maxOutputTokens, providerOptions, onError: onStreamError, @@ -105,13 +106,14 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { }) app.post('/completion', async (req: Request, res: Response) => { - const { prompt, system } = req.body + const { instructions, prompt, system } = req.body + debug('instructions:', (instructions || system)?.length) debug('prompt:', prompt?.length) - debug('system:', system?.length) const result = streamText({ model: model, prompt, - instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, + instructions: + (!disableInstructions && (instructions || system)) || process.env.INSTRUCTIONS_COMPLETION, maxOutputTokens, providerOptions, onError: onStreamError, @@ -130,14 +132,15 @@ app.post('/completion', async (req: Request, res: Response) => { }) app.post('/object', async (req: Request, res: Response) => { - const { output, prompt, system } = req.body + const { instructions, output, prompt, system } = req.body + debug('instructions:', (instructions || system)?.length) debug('output:', output?.length) debug('prompt:', prompt?.length) - debug('system:', system?.length) const result = streamText({ model: model, prompt, - instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, + instructions: + (!disableInstructions && (instructions || system)) || process.env.INSTRUCTIONS_OBJECT, maxOutputTokens, providerOptions, output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), From e6775d190f4a9988e4efe5f36cf047c495ff29ee Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Sat, 18 Jul 2026 08:22:25 -0700 Subject: [PATCH 23/28] Update AGENTS.md --- AGENTS.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c861aaf..6227489 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,10 @@ # Agent Guide - [index.ts](src/index.ts) — Single source Express.js AI-SDK server +- [README.md](README.md) — Server documentation + +- [AI SDK](https://ai-sdk.dev/docs/reference) +- [Express.js](https://expressjs.com/en/5x/api/) ## Commands @@ -13,10 +17,14 @@ ALWAYS use the `npm run *` command | `npm run tsc` | TypeScript Check Only `tsc --noEmit` | | `npm run prettier` | ALWAYS RUN AFTER EDITING FILES | -## Endpoints +## Endpoints and Documentation + +| Server Endpoint | Client | +| :-------------- | :-------------------------------------------------------------------------- | +| `/chat` | [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) | +| `/completion` | [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | +| `/object` | [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | -| Server Endpoint | Client | -| :-------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | -| [/chat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/create-ui-message-stream) | [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) | -| [/completion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/pipe-ui-message-stream-to-response) | [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | -| [/object](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) | [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | +- [createUIMessageStream](https://ai-sdk.dev/docs/reference/ai-sdk-ui/create-ui-message-stream) +- [pipeUIMessageStreamToResponse](https://ai-sdk.dev/docs/reference/ai-sdk-ui/pipe-ui-message-stream-to-response) +- [streamText](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) From ff7ce9b7ee0b94d91f54d904cf5be52228d5c382 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Sun, 2 Aug 2026 13:48:53 -0700 Subject: [PATCH 24/28] Update Dependencies/Workflows --- .github/workflows/labeler.yaml | 2 +- .github/workflows/lint.yaml | 2 +- package-lock.json | 314 +++++++++++++++++---------------- package.json | 20 +-- 4 files changed, 176 insertions(+), 162 deletions(-) diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml index 922b063..c37a7bc 100644 --- a/.github/workflows/labeler.yaml +++ b/.github/workflows/labeler.yaml @@ -46,7 +46,7 @@ jobs: file: .configs/labels/labels.yaml - name: "Labeler" - uses: actions/labeler@v6 + uses: actions/labeler@v7 with: sync-labels: true configuration-path: .configs/labels/labeler.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 5ea343e..f12c0eb 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -66,7 +66,7 @@ jobs: - name: "hadolint" if: ${{ !cancelled() }} - uses: hadolint/hadolint-action@v3.3.0 + uses: hadolint/hadolint-action@v3.4.0 with: dockerfile: Dockerfile ignore: "DL3018" diff --git a/package-lock.json b/package-lock.json index 987787f..583cca6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,11 @@ "name": "chat-server", "version": "0.0.0", "dependencies": { - "@ai-sdk/anthropic": "^4.0.16", - "@ai-sdk/google": "^4.0.18", - "@ai-sdk/openai": "^4.0.16", - "@ai-sdk/openai-compatible": "^3.0.12", - "ai": "^7.0.31", + "@ai-sdk/anthropic": "^4.0.27", + "@ai-sdk/google": "^4.0.31", + "@ai-sdk/openai": "^4.0.27", + "@ai-sdk/openai-compatible": "^3.0.20", + "ai": "^7.0.48", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -24,24 +24,24 @@ "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.1.1", + "@types/node": "^26.1.2", "@types/picomatch": "^4.0.3", - "eslint": "^10.7.0", + "eslint": "^10.8.0", "nodemon": "^3.1.14", - "prettier": "^3.9.5", - "tsx": "^4.23.1", + "prettier": "^3.9.6", + "tsx": "^4.23.4", "typescript": "^6.0.3", - "typescript-eslint": "^8.64.0" + "typescript-eslint": "^8.65.0" } }, "node_modules/@ai-sdk/anthropic": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.16.tgz", - "integrity": "sha512-vyH4D6Auih5H2xvVzzh2ep5pbdWiaV7JDC+jHUE7zZJ5Kyv0TteLav4DrOgHzRuyv8ptfUSqFF6Y8//f/Ec0fQ==", + "version": "4.0.27", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.27.tgz", + "integrity": "sha512-ecrPWkYf+sHDLHErHAU/yjJfNevUzT0i6TuoQLb58oadnquSHcLQ5NMLLWjbr6d1rxYdzI7ftulIJ03VH9yM3w==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11" + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18" }, "engines": { "node": ">=22" @@ -51,13 +51,13 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.23.tgz", - "integrity": "sha512-f85diFdPMXYJpxCjOYZchMQkRH8h3r6lhK4Q2xmzJ7UA2OQ80L3W7tFu61742xGQK7zHWm5AhxYhNuc50H9SGQ==", + "version": "4.0.37", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.37.tgz", + "integrity": "sha512-YBCTsSlX0ETVsjo0ihfBOeJ3p3y1wXKXDzF9Ygp9dscUY06dzOGmyWJSGsKg+khKVArzBWUW4UCSAKms20ZDmg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11", + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18", "@vercel/oidc": "3.2.0" }, "engines": { @@ -68,13 +68,13 @@ } }, "node_modules/@ai-sdk/google": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.18.tgz", - "integrity": "sha512-NRbXRAasXLgFLiZDTsDUiTUuQtEUDVZoqruB5Px3geltTEqOzOo2eHN5WnDp0/OgxwnrNH4olV/TVetza0PzmQ==", + "version": "4.0.31", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.31.tgz", + "integrity": "sha512-AFyO4MuHryrzr/ZEMHm/dEXwqABqoi4yIa9cfZxfI4VYM+dyPeZIXU4/qjvxiD6vzbitQhYf1jutQL/d71bymw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11" + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18" }, "engines": { "node": ">=22" @@ -84,13 +84,13 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.16.tgz", - "integrity": "sha512-Yh+PsXaf9NbN7oA3oKwOuyjTiHMPD75phf3SqGbDNNKQ3Yj3oTntp/WhO3nCHIPA6gr5/1lyridQokmOpPf9oQ==", + "version": "4.0.27", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.27.tgz", + "integrity": "sha512-6MsWnbNG0ZjZgzYimNNjnTAlOFXn+jdLMFB47dDVUsRNjxdUoTSduCQPZZeTKoUl0ZNwCjsJ31of49l/t1r9MA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11" + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18" }, "engines": { "node": ">=22" @@ -100,13 +100,13 @@ } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.12.tgz", - "integrity": "sha512-tN9BUb4jUGjqtbRPUsziLxASfogLNICcq/Qrwr55vpnzKvprV6Ukl8ynED2lZaNrAHU5U4zlGnyU/iuYrjQK6g==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.20.tgz", + "integrity": "sha512-RsPg+HilsKc/d+jy4zItG7v7RJQm+caUu+iU0a5bx3eyyX5tkayMGDRklXMMMAPa4l34luzQwT3t4cmeHfdiWw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11" + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18" }, "engines": { "node": ">=22" @@ -116,9 +116,9 @@ } }, "node_modules/@ai-sdk/provider": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.3.tgz", - "integrity": "sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.4.tgz", + "integrity": "sha512-tbHKNLirllUNF3ZlkCsXnwab2ZV1Sl4b1H/Cp9ruCce15IBmskE8Gwkk0yo9xDWY+jho2of7lVXtwSsyrq7cwQ==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -128,15 +128,16 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.11.tgz", - "integrity": "sha512-7/96wE+ZsKB35iS9ASyllrE4Ym/EolXEB7AkuJ5FI++fmS85BVTAs77890C+1Z2jwHfBKjBQSBmsliOsAh0iFQ==", + "version": "5.0.18", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.18.tgz", + "integrity": "sha512-UBNCrkxS5llgN2/RXLBRkjCFTIuL6YB1Goq5c+yRecnznhtX4XPjTwLHz2hrsTltTVZ0rqVegtnwFXdPBkjDHA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider": "4.0.4", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", - "eventsource-parser": "^3.0.8" + "eventsource-parser": "^3.0.8", + "undici": "^7.28.0" }, "engines": { "node": ">=22" @@ -588,9 +589,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", "dependencies": { @@ -645,9 +646,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", - "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -855,9 +856,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.2.tgz", - "integrity": "sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.3.tgz", + "integrity": "sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==", "dev": true, "license": "MIT", "dependencies": { @@ -889,9 +890,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.1.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", - "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", "dev": true, "license": "MIT", "dependencies": { @@ -941,17 +942,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz", - "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", + "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.64.0", - "@typescript-eslint/type-utils": "8.64.0", - "@typescript-eslint/utils": "8.64.0", - "@typescript-eslint/visitor-keys": "8.64.0", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/type-utils": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -964,7 +965,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.64.0", + "@typescript-eslint/parser": "^8.65.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -980,16 +981,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz", - "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.64.0", - "@typescript-eslint/types": "8.64.0", - "@typescript-eslint/typescript-estree": "8.64.0", - "@typescript-eslint/visitor-keys": "8.64.0", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3" }, "engines": { @@ -1005,14 +1006,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", - "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.64.0", - "@typescript-eslint/types": "^8.64.0", + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", "debug": "^4.4.3" }, "engines": { @@ -1027,14 +1028,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", - "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.64.0", - "@typescript-eslint/visitor-keys": "8.64.0" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1045,9 +1046,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", - "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", "dev": true, "license": "MIT", "engines": { @@ -1062,15 +1063,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz", - "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", + "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.64.0", - "@typescript-eslint/typescript-estree": "8.64.0", - "@typescript-eslint/utils": "8.64.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1087,9 +1088,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", - "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -1101,16 +1102,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", - "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.64.0", - "@typescript-eslint/tsconfig-utils": "8.64.0", - "@typescript-eslint/types": "8.64.0", - "@typescript-eslint/visitor-keys": "8.64.0", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1129,16 +1130,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz", - "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", + "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.64.0", - "@typescript-eslint/types": "8.64.0", - "@typescript-eslint/typescript-estree": "8.64.0" + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1153,13 +1154,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", - "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1199,9 +1200,9 @@ } }, "node_modules/acorn": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", - "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", "bin": { @@ -1222,14 +1223,14 @@ } }, "node_modules/ai": { - "version": "7.0.31", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.31.tgz", - "integrity": "sha512-pJfwKXjF5kw0rKRTePwYo60EfWb8wfzJAgf3ojln/YkOsVVKttzZAJVcRPsg37Z3a06ZdKkxX+DSrMAFlPm5Mw==", + "version": "7.0.48", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.48.tgz", + "integrity": "sha512-QmqshWFEDdkFFqSgdJ4cogaoDIYaedqbv73q/NXEYNkSIocJCzXnGFVyM9lrtAGTSBfm+5hq3/292ooXJ1jDSw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.23", - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11" + "@ai-sdk/gateway": "4.0.37", + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18" }, "engines": { "node": ">=22" @@ -1343,16 +1344,16 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/braces": { @@ -1682,9 +1683,9 @@ } }, "node_modules/eslint": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz", - "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", + "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -1694,7 +1695,7 @@ "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.6.0", + "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", @@ -1718,7 +1719,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", + "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -2015,9 +2016,9 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, @@ -2373,12 +2374,16 @@ } }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", "license": "MIT", "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/merge-descriptors": { @@ -2419,13 +2424,13 @@ } }, "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -2649,9 +2654,9 @@ } }, "node_modules/prettier": { - "version": "3.9.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz", - "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -3043,9 +3048,9 @@ } }, "node_modules/tsx": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz", - "integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==", + "version": "4.23.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.4.tgz", + "integrity": "sha512-ZiUQ8oT/KzN51mJUWPqARYqwFLFJZtGZipRkw1ynHMr9vy3eU77m5yfF3Gzm6meEg/beW+lUu3fHYgskTN2oVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3120,16 +3125,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.64.0.tgz", - "integrity": "sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", + "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.64.0", - "@typescript-eslint/parser": "8.64.0", - "@typescript-eslint/typescript-estree": "8.64.0", - "@typescript-eslint/utils": "8.64.0" + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3150,6 +3155,15 @@ "dev": true, "license": "MIT" }, + "node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", diff --git a/package.json b/package.json index 5bb4550..277f8f9 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,11 @@ "tsc": "npx tsc --noEmit" }, "dependencies": { - "@ai-sdk/anthropic": "^4.0.16", - "@ai-sdk/google": "^4.0.18", - "@ai-sdk/openai": "^4.0.16", - "@ai-sdk/openai-compatible": "^3.0.12", - "ai": "^7.0.31", + "@ai-sdk/anthropic": "^4.0.27", + "@ai-sdk/google": "^4.0.31", + "@ai-sdk/openai": "^4.0.27", + "@ai-sdk/openai-compatible": "^3.0.20", + "ai": "^7.0.48", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -32,13 +32,13 @@ "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.1.1", + "@types/node": "^26.1.2", "@types/picomatch": "^4.0.3", - "eslint": "^10.7.0", + "eslint": "^10.8.0", "nodemon": "^3.1.14", - "prettier": "^3.9.5", - "tsx": "^4.23.1", + "prettier": "^3.9.6", + "tsx": "^4.23.4", "typescript": "^6.0.3", - "typescript-eslint": "^8.64.0" + "typescript-eslint": "^8.65.0" } } From ab1755f3b4decd3c3a1e53f02caf89fdc19660f4 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Sun, 2 Aug 2026 13:55:04 -0700 Subject: [PATCH 25/28] Fix hadolint DL3066 --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9c939f4..84a6176 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,7 @@ ARG VERSION="Dockerfile" ENV APP_VERSION="${VERSION}" LABEL org.opencontainers.image.version="${VERSION}" -USER node +#USER node +USER 1000 CMD ["npm", "start"] From 99029ab53f28f0d8c48338ee98ccd54549f71ff9 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Sun, 16 Aug 2026 11:56:14 -0700 Subject: [PATCH 26/28] Add Default Headers for Default Zen Provider --- .ncurc.yaml | 2 + package-lock.json | 466 ++++++++++++++++++++++---------------------- package.json | 19 +- src/index.ts | 35 +++- test/object.test.ts | 108 ++++++++++ 5 files changed, 380 insertions(+), 250 deletions(-) create mode 100644 .ncurc.yaml create mode 100644 test/object.test.ts diff --git a/.ncurc.yaml b/.ncurc.yaml new file mode 100644 index 0000000..dd1af0f --- /dev/null +++ b/.ncurc.yaml @@ -0,0 +1,2 @@ +reject: + - typescript diff --git a/package-lock.json b/package-lock.json index 583cca6..52fedc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,11 @@ "name": "chat-server", "version": "0.0.0", "dependencies": { - "@ai-sdk/anthropic": "^4.0.27", - "@ai-sdk/google": "^4.0.31", - "@ai-sdk/openai": "^4.0.27", - "@ai-sdk/openai-compatible": "^3.0.20", - "ai": "^7.0.48", + "@ai-sdk/anthropic": "^4.0.39", + "@ai-sdk/google": "^4.0.44", + "@ai-sdk/openai": "^4.0.42", + "@ai-sdk/openai-compatible": "^3.0.30", + "ai": "^7.0.66", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -24,24 +24,24 @@ "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.1.2", + "@types/node": "^26.2.0", "@types/picomatch": "^4.0.3", - "eslint": "^10.8.0", + "eslint": "^10.8.1", "nodemon": "^3.1.14", "prettier": "^3.9.6", - "tsx": "^4.23.4", + "tsx": "^4.23.12", "typescript": "^6.0.3", - "typescript-eslint": "^8.65.0" + "typescript-eslint": "^8.67.0" } }, "node_modules/@ai-sdk/anthropic": { - "version": "4.0.27", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.27.tgz", - "integrity": "sha512-ecrPWkYf+sHDLHErHAU/yjJfNevUzT0i6TuoQLb58oadnquSHcLQ5NMLLWjbr6d1rxYdzI7ftulIJ03VH9yM3w==", + "version": "4.0.39", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.39.tgz", + "integrity": "sha512-JAMGtYeEuaBzqbsPO4fkho6vQyNoVhsHASM4o59wmJRU6Vh7prjOp490Kmc7YQTY+ioU1/xYzXvWOtxZBup0Xw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18" + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27" }, "engines": { "node": ">=22" @@ -51,13 +51,13 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.37", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.37.tgz", - "integrity": "sha512-YBCTsSlX0ETVsjo0ihfBOeJ3p3y1wXKXDzF9Ygp9dscUY06dzOGmyWJSGsKg+khKVArzBWUW4UCSAKms20ZDmg==", + "version": "4.0.52", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.52.tgz", + "integrity": "sha512-SXUM8jzzuTUJRq+EOgPd5to6DSx0EKslVn+IVZHbUEX6k/3vCPNrvjckbK26HnNxHU/STxm+zTSJteqrO+7Z0w==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18", + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27", "@vercel/oidc": "3.2.0" }, "engines": { @@ -68,13 +68,13 @@ } }, "node_modules/@ai-sdk/google": { - "version": "4.0.31", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.31.tgz", - "integrity": "sha512-AFyO4MuHryrzr/ZEMHm/dEXwqABqoi4yIa9cfZxfI4VYM+dyPeZIXU4/qjvxiD6vzbitQhYf1jutQL/d71bymw==", + "version": "4.0.44", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.44.tgz", + "integrity": "sha512-bmRTDg06jQD+eX8nf214pET9+Oe8O1+lUIRGbWsGXj9IN2UJkpl1O1x7cvtiboyTtKSLvSRdVtItUfSl8sQ2GA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18" + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27" }, "engines": { "node": ">=22" @@ -84,13 +84,13 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "4.0.27", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.27.tgz", - "integrity": "sha512-6MsWnbNG0ZjZgzYimNNjnTAlOFXn+jdLMFB47dDVUsRNjxdUoTSduCQPZZeTKoUl0ZNwCjsJ31of49l/t1r9MA==", + "version": "4.0.42", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.42.tgz", + "integrity": "sha512-ZxDca6jJalYuXrIGVrw6dnkpz1Io9AWy+/b/wVWIbjigHCbd+zWLpPi8NnK0OFU+U3YCpP+KWfUvEnG5pFhltA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18" + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27" }, "engines": { "node": ">=22" @@ -100,13 +100,13 @@ } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.20.tgz", - "integrity": "sha512-RsPg+HilsKc/d+jy4zItG7v7RJQm+caUu+iU0a5bx3eyyX5tkayMGDRklXMMMAPa4l34luzQwT3t4cmeHfdiWw==", + "version": "3.0.30", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.30.tgz", + "integrity": "sha512-BB35G4fS/Ey5OHbWrVLxRLX1gkTlO+9I4YhlmdH1skNVoPaFXZlR6bQ+1C76d/ug7O6ocbIw4qcd2G4+GPdWEA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18" + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27" }, "engines": { "node": ">=22" @@ -116,9 +116,9 @@ } }, "node_modules/@ai-sdk/provider": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.4.tgz", - "integrity": "sha512-tbHKNLirllUNF3ZlkCsXnwab2ZV1Sl4b1H/Cp9ruCce15IBmskE8Gwkk0yo9xDWY+jho2of7lVXtwSsyrq7cwQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.7.tgz", + "integrity": "sha512-6or44XprPzKbr8zkmzosowSE0pxkvJcoojBL+mCZvPUt3kvXp3XSNqeVun9golb1acEfSo6yaEBRT18h2VU+1Q==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -128,12 +128,12 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "5.0.18", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.18.tgz", - "integrity": "sha512-UBNCrkxS5llgN2/RXLBRkjCFTIuL6YB1Goq5c+yRecnznhtX4XPjTwLHz2hrsTltTVZ0rqVegtnwFXdPBkjDHA==", + "version": "5.0.27", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.27.tgz", + "integrity": "sha512-EzAn4pdgG5g0xXtH6lE2zyNmfjDQIDjATkfqzuidEI35g++hh4+07vnjzkT/RmGmIClPZiRj/Q2GMPV2V7mkHw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider": "4.0.7", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", "eventsource-parser": "^3.0.8", @@ -147,9 +147,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", - "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], @@ -164,9 +164,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", - "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", - "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", - "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -215,9 +215,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", - "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -232,9 +232,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", - "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", - "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", - "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", - "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], @@ -300,9 +300,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", - "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", - "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], @@ -334,9 +334,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", - "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], @@ -351,9 +351,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", - "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], @@ -368,9 +368,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", - "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], @@ -385,9 +385,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", - "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], @@ -402,9 +402,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", - "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -419,9 +419,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", - "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], @@ -436,9 +436,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", - "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], @@ -453,9 +453,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", - "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], @@ -470,9 +470,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", - "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], @@ -487,9 +487,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", - "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -504,9 +504,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", - "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], @@ -521,9 +521,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", - "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -538,9 +538,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", - "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], @@ -555,9 +555,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", - "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], @@ -572,9 +572,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", - "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], @@ -890,9 +890,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.1.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", - "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz", + "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", "dev": true, "license": "MIT", "dependencies": { @@ -942,17 +942,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", - "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/type-utils": "8.65.0", - "@typescript-eslint/utils": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -965,7 +965,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.65.0", + "@typescript-eslint/parser": "^8.67.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -981,16 +981,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", - "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "debug": "^4.4.3" }, "engines": { @@ -1006,14 +1006,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", - "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.65.0", - "@typescript-eslint/types": "^8.65.0", + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", "debug": "^4.4.3" }, "engines": { @@ -1028,14 +1028,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", - "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0" + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1046,9 +1046,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", - "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", "dev": true, "license": "MIT", "engines": { @@ -1063,15 +1063,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", - "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1088,9 +1088,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", - "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", "dev": true, "license": "MIT", "engines": { @@ -1102,16 +1102,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", - "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.65.0", - "@typescript-eslint/tsconfig-utils": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1130,16 +1130,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", - "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0" + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1154,13 +1154,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", - "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/types": "8.67.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1223,14 +1223,14 @@ } }, "node_modules/ai": { - "version": "7.0.48", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.48.tgz", - "integrity": "sha512-QmqshWFEDdkFFqSgdJ4cogaoDIYaedqbv73q/NXEYNkSIocJCzXnGFVyM9lrtAGTSBfm+5hq3/292ooXJ1jDSw==", + "version": "7.0.66", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.66.tgz", + "integrity": "sha512-wBUyoCYF3GVr+62nelBgR8YbpTSsMZrzFyOOjiwijylNSM2TFCW35C+Pml2vc59/WLMpyhS/LWZ55M+B9DAcSg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.37", - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18" + "@ai-sdk/gateway": "4.0.52", + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27" }, "engines": { "node": ">=22" @@ -1331,9 +1331,9 @@ } }, "node_modules/body-parser/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", "license": "MIT", "engines": { "node": ">=18" @@ -1622,9 +1622,9 @@ } }, "node_modules/esbuild": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", - "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1635,32 +1635,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.1", - "@esbuild/android-arm": "0.28.1", - "@esbuild/android-arm64": "0.28.1", - "@esbuild/android-x64": "0.28.1", - "@esbuild/darwin-arm64": "0.28.1", - "@esbuild/darwin-x64": "0.28.1", - "@esbuild/freebsd-arm64": "0.28.1", - "@esbuild/freebsd-x64": "0.28.1", - "@esbuild/linux-arm": "0.28.1", - "@esbuild/linux-arm64": "0.28.1", - "@esbuild/linux-ia32": "0.28.1", - "@esbuild/linux-loong64": "0.28.1", - "@esbuild/linux-mips64el": "0.28.1", - "@esbuild/linux-ppc64": "0.28.1", - "@esbuild/linux-riscv64": "0.28.1", - "@esbuild/linux-s390x": "0.28.1", - "@esbuild/linux-x64": "0.28.1", - "@esbuild/netbsd-arm64": "0.28.1", - "@esbuild/netbsd-x64": "0.28.1", - "@esbuild/openbsd-arm64": "0.28.1", - "@esbuild/openbsd-x64": "0.28.1", - "@esbuild/openharmony-arm64": "0.28.1", - "@esbuild/sunos-x64": "0.28.1", - "@esbuild/win32-arm64": "0.28.1", - "@esbuild/win32-ia32": "0.28.1", - "@esbuild/win32-x64": "0.28.1" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/escape-html": { @@ -1683,9 +1683,9 @@ } }, "node_modules/eslint": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", - "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.1.tgz", + "integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -1847,9 +1847,9 @@ } }, "node_modules/eventsource-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", - "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -3048,9 +3048,9 @@ } }, "node_modules/tsx": { - "version": "4.23.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.4.tgz", - "integrity": "sha512-ZiUQ8oT/KzN51mJUWPqARYqwFLFJZtGZipRkw1ynHMr9vy3eU77m5yfF3Gzm6meEg/beW+lUu3fHYgskTN2oVQ==", + "version": "4.23.12", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz", + "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3098,9 +3098,9 @@ } }, "node_modules/type-is/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", "license": "MIT", "engines": { "node": ">=18" @@ -3125,16 +3125,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", - "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", + "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.65.0", - "@typescript-eslint/parser": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0" + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 277f8f9..ac7cb0b 100644 --- a/package.json +++ b/package.json @@ -13,14 +13,15 @@ "prettier:check": "npx prettier --check .", "prettier:write": "npx prettier --write .", "start": "node dist/index.js", + "test": "npx tsc --noEmit -p tsconfig.test.json && tsx --test test/*.test.ts", "tsc": "npx tsc --noEmit" }, "dependencies": { - "@ai-sdk/anthropic": "^4.0.27", - "@ai-sdk/google": "^4.0.31", - "@ai-sdk/openai": "^4.0.27", - "@ai-sdk/openai-compatible": "^3.0.20", - "ai": "^7.0.48", + "@ai-sdk/anthropic": "^4.0.39", + "@ai-sdk/google": "^4.0.44", + "@ai-sdk/openai": "^4.0.42", + "@ai-sdk/openai-compatible": "^3.0.30", + "ai": "^7.0.66", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -32,13 +33,13 @@ "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.1.2", + "@types/node": "^26.2.0", "@types/picomatch": "^4.0.3", - "eslint": "^10.8.0", + "eslint": "^10.8.1", "nodemon": "^3.1.14", "prettier": "^3.9.6", - "tsx": "^4.23.4", + "tsx": "^4.23.12", "typescript": "^6.0.3", - "typescript-eslint": "^8.65.0" + "typescript-eslint": "^8.67.0" } } diff --git a/src/index.ts b/src/index.ts index 3361912..62f9050 100644 --- a/src/index.ts +++ b/src/index.ts @@ -59,8 +59,8 @@ console.log('maxOutputTokens:', maxOutputTokens) const disableInstructions = getBool(process.env.DISABLE_CLIENT_INSTRUCTIONS) console.log('disableInstructions:', disableInstructions) -process.env.INSTRUCTIONS_CHAT = process.env.INSTRUCTIONS_CHAT || process.env.INSTRUCTIONS // NOSONAR -console.log('INSTRUCTIONS_CHAT:', process.env.INSTRUCTIONS_CHAT) +const instructionsChat = process.env.INSTRUCTIONS_CHAT || process.env.INSTRUCTIONS // NOSONAR +console.log('INSTRUCTIONS_CHAT:', instructionsChat) console.log('INSTRUCTIONS_COMPLETION:', process.env.INSTRUCTIONS_COMPLETION) console.log('INSTRUCTIONS_OBJECT:', process.env.INSTRUCTIONS_OBJECT) @@ -92,8 +92,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { const result = streamText({ model: model, messages: modelMessages, - instructions: - (!disableInstructions && (instructions || system)) || process.env.INSTRUCTIONS_CHAT, + instructions: (!disableInstructions && (instructions || system)) || instructionsChat, maxOutputTokens, providerOptions, onError: onStreamError, @@ -102,7 +101,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { writer.merge(toUIMessageStream({ stream: result.stream })) }, }) - pipeUIMessageStreamToResponse({ response: res, stream }) + return pipeUIMessageStreamToResponse({ response: res, stream }) }) app.post('/completion', async (req: Request, res: Response) => { @@ -124,7 +123,7 @@ app.post('/completion', async (req: Request, res: Response) => { writer.merge(toUIMessageStream({ stream: result.stream })) }, }) - pipeUIMessageStreamToResponse({ + return pipeUIMessageStreamToResponse({ response: res, stream, consumeSseStream: consumeStream, @@ -134,7 +133,8 @@ app.post('/completion', async (req: Request, res: Response) => { app.post('/object', async (req: Request, res: Response) => { const { instructions, output, prompt, system } = req.body debug('instructions:', (instructions || system)?.length) - debug('output:', output?.length) + // NOTE: output is an Object due to - app.use(express.json({ limit: '10mb' })) + // debug('output:', output?.length) debug('prompt:', prompt?.length) const result = streamText({ model: model, @@ -147,7 +147,10 @@ app.post('/object', async (req: Request, res: Response) => { onError: onStreamError, onEnd: onStreamEnd, }) - pipeTextStreamToResponse({ + // NOTE: pipe the raw stream - erroring the stream here would create an unhandled + // rejection (crashing the process) and pipeTextStreamToResponse already sent a 200. + // Model errors are still logged via onError and the stream simply ends. + return pipeTextStreamToResponse({ response: res, stream: toTextStream({ stream: result.stream }), }) @@ -182,11 +185,27 @@ function getModel() { if (!process.env.ANTHROPIC_API_KEY) throw new Error('Missing ANTHROPIC_API_KEY') return anthropic(process.env.MODEL) } else { + const isDefaultZen = + baseURL === 'https://opencode.ai/zen/v1' && + !process.env.MODEL && + !process.env.PROVIDER_API_KEY + debug('Applying Default Zen Headers:', isDefaultZen) const provider = createOpenAICompatible({ name: 'zen', baseURL: baseURL, apiKey: process.env.PROVIDER_API_KEY, includeUsage: true, + ...(isDefaultZen + ? { + headers: { + 'x-opencode-project': 'chat-server', + 'x-opencode-session': 'ses_chat-server', + 'x-opencode-request': 'chat-server', + 'x-opencode-client': 'opencode-tui', + 'User-Agent': 'opencode/1.14.50', + }, + } + : {}), }) return provider(process.env.MODEL || 'big-pickle') // NOSONAR } diff --git a/test/object.test.ts b/test/object.test.ts new file mode 100644 index 0000000..ea02354 --- /dev/null +++ b/test/object.test.ts @@ -0,0 +1,108 @@ +import { test } from 'node:test' +import assert from 'node:assert/strict' + +const BASE_URL = process.env.TEST_BASE_URL ?? 'http://localhost:3000' + +const nameAgeSchema = { + type: 'object', + properties: { + name: { type: 'string' }, + age: { type: 'number' }, + }, + required: ['name', 'age'], +} + +test( + 'POST /object returns a JSON object matching the provided schema', + { timeout: 60_000 }, + async () => { + const response = await fetch(`${BASE_URL}/object`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + instructions: 'You are a helpful assistant. Return only valid JSON.', + prompt: 'Extract the name and age from: John is 30 years old.', + output: nameAgeSchema, + }), + }) + + assert.equal(response.status, 200) + assert.match(response.headers.get('content-type') ?? '', /^text\/plain/) + + const body = await response.text() + assert.ok(body.length > 0, 'response body should not be empty') + + let parsed: unknown + assert.doesNotThrow(() => { + parsed = JSON.parse(body) + }) + + const object = parsed as { name?: string; age?: number } + assert.equal(typeof object.name, 'string') + assert.ok(object.name!.length > 0, 'name should not be empty') + assert.equal(typeof object.age, 'number') + assert.ok(object.age! > 0, 'age should be a positive number') + }, +) + +test( + 'POST /object without an output schema returns valid JSON', + { timeout: 60_000 }, + async () => { + const response = await fetch(`${BASE_URL}/object`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + instructions: 'You are a helpful assistant. Return only valid JSON.', + prompt: 'Return a JSON object with a single key "ok" set to true.', + }), + }) + + assert.equal(response.status, 200) + const body = await response.text() + assert.ok(body.length > 0, 'response body should not be empty') + assert.doesNotThrow(() => JSON.parse(body)) + }, +) + +test('POST /object streams the response incrementally', { timeout: 60_000 }, async () => { + const response = await fetch(`${BASE_URL}/object`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + instructions: 'You are a helpful assistant. Return only valid JSON.', + prompt: + 'Return a JSON object with a single key "list" containing an array of the numbers 1 through 20.', + output: { + type: 'object', + properties: { + list: { type: 'array', items: { type: 'number' } }, + }, + required: ['list'], + }, + }), + }) + + assert.equal(response.status, 200) + assert.ok(response.body, 'response should have a body stream') + + const chunks: string[] = [] + let text = '' + const reader = response.body!.getReader() + while (true) { + const { done, value } = await reader.read() + if (done) break + chunks.push(new TextDecoder().decode(value)) + text += chunks[chunks.length - 1] + } + + assert.ok(chunks.length >= 1, 'response should be delivered in stream chunks') + assert.ok(text.length > 0, 'streamed body should not be empty') + + let parsed: unknown + assert.doesNotThrow(() => { + parsed = JSON.parse(text) + }) + const object = parsed as { list?: unknown[] } + assert.ok(Array.isArray(object.list), 'list should be an array') +}) From 02938b0263f01f47a2cebcf74bcea47b40586ee4 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Sun, 16 Aug 2026 12:01:07 -0700 Subject: [PATCH 27/28] Add tsconfig.test.json --- tsconfig.test.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tsconfig.test.json diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..18ebdf6 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*", "test/**/*"] +} From 9aa4b3dfdcd5d7f970f47c0c6bb7695dff209d07 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 20 Aug 2026 23:01:41 -0700 Subject: [PATCH 28/28] Cleanup Custom Headers and Add PROVIDER_USER_AGENT --- README.md | 53 +++++++++++++++++++------- package-lock.json | 94 ++++++++++++++++++++++++++++------------------- package.json | 10 ++--- src/index.ts | 20 ++++------ 4 files changed, 109 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index ffcdc48..f0622ac 100644 --- a/README.md +++ b/README.md @@ -103,20 +103,21 @@ For a Portainer Deploy workflow see the [.github/workflows/deploy.yaml](https:// Environment Variables (can be placed in a `settings.env` file). -| Variable | Default | Description | -| :--------------------------------------- | :--------------------------- | :---------------------------------- | -| `MODEL` | `big-pickle` | Model to Use | -| `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | -| `MAX_TOKENS` | - | Max Output Tokens | -| [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | -| [INSTRUCTIONS_CHAT](#INSTRUCTIONS) | - | System Instructions for Chat | -| [INSTRUCTIONS_COMPLETION](#INSTRUCTIONS) | - | System Instructions for Completion | -| [INSTRUCTIONS_OBJECT](#INSTRUCTIONS) | - | System Instructions for Object | -| `DISABLE_CLIENT_INSTRUCTIONS`**¹** | - | Ignore Client System Instructions | -| `AI_SDK_LOG_WARNINGS`**¹** | - | Enable SDK Warnings Logging | -| `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | -| `PORT` | `3000` | Server Port | -| `DEBUG` | - | Set to `app` for Debug Logging | +| Variable | Default | Description | +| :------------------------------------------ | :---------------------------------- | :------------------------------------ | +| `MODEL` | `big-pickle` | Model to Use | +| `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | +| `MAX_TOKENS` | - | Max Output Tokens | +| [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | +| [PROVIDER_USER_AGENT](#PROVIDER_USER_AGENT) | [_see below_](#PROVIDER_USER_AGENT) | OpenAI Compatible Provider User-Agent | +| [INSTRUCTIONS_CHAT](#INSTRUCTIONS) | - | System Instructions for Chat | +| [INSTRUCTIONS_COMPLETION](#INSTRUCTIONS) | - | System Instructions for Completion | +| [INSTRUCTIONS_OBJECT](#INSTRUCTIONS) | - | System Instructions for Object | +| `DISABLE_CLIENT_INSTRUCTIONS`**¹** | - | Ignore Client System Instructions | +| `AI_SDK_LOG_WARNINGS`**¹** | - | Enable SDK Warnings Logging | +| `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | +| `PORT` | `3000` | Server Port | +| `DEBUG` | - | Set to `app` for Debug Logging | > **¹** Boolean Variables. **True** values include: `['1', 't', 'true', 'y', 'yes', 'on']` @@ -160,6 +161,30 @@ You are responsible for providing valid options for the chosen model. The SDK supports providing provider options for multiple providers simultaneously. The value is only checked for valid JSON at startup and will fail at runtime if it contains invalid options. +#### PROVIDER_USER_AGENT + +If BASE_URL, MODEL, and PROVIDER_API_KEY are not set (Default Zen), a custom header is set: + +```text +User-Agent: opencode/version +``` + +This header increases the rate limit for request for the free OpenCode Zen endpoint. + +To disable this without changing one of the above variables, you can set an empty value: + +```text +PROVIDER_USER_AGENT= +``` + +Otherwise, you can set a custom User-Agent (prefix) to anything you choose: + +```text +PROVIDER_USER_AGENT='my-app/1.0' +``` + +NOTE: The AI SDK appends a suffix to the UA: ` ai-sdk/provider-utils/x runtime/node` + ## Client ### Endpoints diff --git a/package-lock.json b/package-lock.json index 52fedc4..eb4d0b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,11 @@ "name": "chat-server", "version": "0.0.0", "dependencies": { - "@ai-sdk/anthropic": "^4.0.39", - "@ai-sdk/google": "^4.0.44", - "@ai-sdk/openai": "^4.0.42", - "@ai-sdk/openai-compatible": "^3.0.30", - "ai": "^7.0.66", + "@ai-sdk/anthropic": "^4.0.40", + "@ai-sdk/google": "^4.0.49", + "@ai-sdk/openai": "^4.0.45", + "@ai-sdk/openai-compatible": "^3.0.34", + "ai": "^7.0.73", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -35,13 +35,13 @@ } }, "node_modules/@ai-sdk/anthropic": { - "version": "4.0.39", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.39.tgz", - "integrity": "sha512-JAMGtYeEuaBzqbsPO4fkho6vQyNoVhsHASM4o59wmJRU6Vh7prjOp490Kmc7YQTY+ioU1/xYzXvWOtxZBup0Xw==", + "version": "4.0.40", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.40.tgz", + "integrity": "sha512-cFlCZspCUC1LGDipARsKx3+4A8c9qI+vFuG0/04Phs0deKwifNsl8wDmcU2HO31aiNR9AJgEBcvg5S00zUS70g==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.7", - "@ai-sdk/provider-utils": "5.0.27" + "@ai-sdk/provider-utils": "5.0.28" }, "engines": { "node": ">=22" @@ -51,13 +51,13 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.52", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.52.tgz", - "integrity": "sha512-SXUM8jzzuTUJRq+EOgPd5to6DSx0EKslVn+IVZHbUEX6k/3vCPNrvjckbK26HnNxHU/STxm+zTSJteqrO+7Z0w==", + "version": "4.0.59", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.59.tgz", + "integrity": "sha512-9MPyBBXU5snhuvqMa3OSOYtHP+wlyepJ+E0E+9lVNoxLGmi4sZqGeLbIxSYZeTf7qQ2xPshtfo5qDJHaOmAHvw==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.7", - "@ai-sdk/provider-utils": "5.0.27", + "@ai-sdk/provider-utils": "5.0.28", "@vercel/oidc": "3.2.0" }, "engines": { @@ -68,13 +68,13 @@ } }, "node_modules/@ai-sdk/google": { - "version": "4.0.44", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.44.tgz", - "integrity": "sha512-bmRTDg06jQD+eX8nf214pET9+Oe8O1+lUIRGbWsGXj9IN2UJkpl1O1x7cvtiboyTtKSLvSRdVtItUfSl8sQ2GA==", + "version": "4.0.49", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.49.tgz", + "integrity": "sha512-ZJMehjQNVyvLytV5cUQcTMNEG79NS+p8d2AHu+bH/Mg2M2IQ8Pueh/xQga2v85TX8BGT55m9Q01taY1fZjS4ag==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.7", - "@ai-sdk/provider-utils": "5.0.27" + "@ai-sdk/provider-utils": "5.0.28" }, "engines": { "node": ">=22" @@ -84,13 +84,13 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "4.0.42", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.42.tgz", - "integrity": "sha512-ZxDca6jJalYuXrIGVrw6dnkpz1Io9AWy+/b/wVWIbjigHCbd+zWLpPi8NnK0OFU+U3YCpP+KWfUvEnG5pFhltA==", + "version": "4.0.45", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.45.tgz", + "integrity": "sha512-F/zHXTfCaHO0Q5egf8HRdc645Mp/ofLE9NSjl5RwaXfqrqLfiLROE9xScgKf4iziElsjuQYbesPEcW37mmZy3w==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.7", - "@ai-sdk/provider-utils": "5.0.27" + "@ai-sdk/provider-utils": "5.0.28" }, "engines": { "node": ">=22" @@ -100,13 +100,13 @@ } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "3.0.30", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.30.tgz", - "integrity": "sha512-BB35G4fS/Ey5OHbWrVLxRLX1gkTlO+9I4YhlmdH1skNVoPaFXZlR6bQ+1C76d/ug7O6ocbIw4qcd2G4+GPdWEA==", + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.34.tgz", + "integrity": "sha512-KTyUuDXaHgKochdT0omCIQapXJi1nyNFnHZkGwssLergL/d0mOriJ8s5fCv7ba+IB1vpDC0LVU6BrXuHAlvctQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.7", - "@ai-sdk/provider-utils": "5.0.27" + "@ai-sdk/provider-utils": "5.0.28" }, "engines": { "node": ">=22" @@ -128,9 +128,9 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "5.0.27", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.27.tgz", - "integrity": "sha512-EzAn4pdgG5g0xXtH6lE2zyNmfjDQIDjATkfqzuidEI35g++hh4+07vnjzkT/RmGmIClPZiRj/Q2GMPV2V7mkHw==", + "version": "5.0.28", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.28.tgz", + "integrity": "sha512-TnHUyd/rCYQqHg5RuiOaz/hUql6U+kbUaBW0Rp+0N5UhnAInA9CzzV0HXvuAAPwppDsK6fAx9Rd+tRawpJ/3pg==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.7", @@ -1223,14 +1223,14 @@ } }, "node_modules/ai": { - "version": "7.0.66", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.66.tgz", - "integrity": "sha512-wBUyoCYF3GVr+62nelBgR8YbpTSsMZrzFyOOjiwijylNSM2TFCW35C+Pml2vc59/WLMpyhS/LWZ55M+B9DAcSg==", + "version": "7.0.73", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.73.tgz", + "integrity": "sha512-rm2Rk68seeyhfgXl1JA/oaHcQHL018AfgydkJWx7Bgz0wGDHyvl30SnRIU2V7ZVR60A5K0SOLyP78EZmEJ2tOw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.52", + "@ai-sdk/gateway": "4.0.59", "@ai-sdk/provider": "4.0.7", - "@ai-sdk/provider-utils": "5.0.27" + "@ai-sdk/provider-utils": "5.0.28" }, "engines": { "node": ">=22" @@ -2453,12 +2453,32 @@ "license": "MIT" }, "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/nodemon": { diff --git a/package.json b/package.json index ac7cb0b..8d003e5 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,11 @@ "tsc": "npx tsc --noEmit" }, "dependencies": { - "@ai-sdk/anthropic": "^4.0.39", - "@ai-sdk/google": "^4.0.44", - "@ai-sdk/openai": "^4.0.42", - "@ai-sdk/openai-compatible": "^3.0.30", - "ai": "^7.0.66", + "@ai-sdk/anthropic": "^4.0.40", + "@ai-sdk/google": "^4.0.49", + "@ai-sdk/openai": "^4.0.45", + "@ai-sdk/openai-compatible": "^3.0.34", + "ai": "^7.0.73", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", diff --git a/src/index.ts b/src/index.ts index 62f9050..ebd8fe8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,6 +42,9 @@ console.log('PROVIDER_API_KEY:', process.env.PROVIDER_API_KEY ? 'SET' : undefine const baseURL = process.env.BASE_URL || 'https://opencode.ai/zen/v1' // NOSONAR console.log('BASE_URL:', baseURL) +const providerUserAgent = process.env.PROVIDER_USER_AGENT?.trim() +console.log('PROVIDER_USER_AGENT:', providerUserAgent) + console.log('AI_SDK_LOG_WARNINGS:', getBool(process.env.AI_SDK_LOG_WARNINGS)) if (!getBool(process.env.AI_SDK_LOG_WARNINGS)) globalThis.AI_SDK_LOG_WARNINGS = false @@ -189,23 +192,16 @@ function getModel() { baseURL === 'https://opencode.ai/zen/v1' && !process.env.MODEL && !process.env.PROVIDER_API_KEY - debug('Applying Default Zen Headers:', isDefaultZen) + debug('Default Zen Configuration:', isDefaultZen) + // NOTE: AI SDK appends a suffix to the UA: ' ai-sdk/provider-utils/x runtime/node' + const userAgent = providerUserAgent ?? (isDefaultZen ? 'opencode/1.18.19' : undefined) + debug('User-Agent:', userAgent) const provider = createOpenAICompatible({ name: 'zen', baseURL: baseURL, apiKey: process.env.PROVIDER_API_KEY, includeUsage: true, - ...(isDefaultZen - ? { - headers: { - 'x-opencode-project': 'chat-server', - 'x-opencode-session': 'ses_chat-server', - 'x-opencode-request': 'chat-server', - 'x-opencode-client': 'opencode-tui', - 'User-Agent': 'opencode/1.14.50', - }, - } - : {}), + ...(userAgent ? { headers: { 'User-Agent': userAgent } } : {}), }) return provider(process.env.MODEL || 'big-pickle') // NOSONAR }