uncloseai.com/public/languages/javascript/nodejs
russell@unturf.com 9d191f24d1 mandatory vault gate: encrypt all localStorage with password before chat
Vault password prompt is now the first thing shown when opening
uncloseai. No chat, no greeting, no data written until the user
creates or unlocks their vault. Each site has its own vault with
AES-256 encryption using password + device-specific salt.

- Add vault gate overlay blocking modal until password entered
- Remove plaintext conversation fallback from storage.js
- Route TTS mode, selected voice, language through vault
- Route custom API config reads through vault in models.js
- Add vault gate i18n strings (26 languages) explaining per-site
  encryption of chat history, API keys, and settings
- Add missing keys to migration (voice, TTS mode)
- Re-show vault gate on lock from settings
- Permacomputer headers across all source files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:38:54 -05:00
..
Dockerfile add public domain license and headers to all source files 2026-02-22 22:27:09 -05:00
package.json Reorganize project structure: move public files to public/ directory 2025-10-24 13:23:42 -04:00
README.md Reorganize project structure: move public files to public/ directory 2025-10-24 13:23:42 -04:00
uncloseai.js mandatory vault gate: encrypt all localStorage with password before chat 2026-02-23 15:38:54 -05:00

uncloseai. Node.js Client

A Node.js client library for interacting with vLLM, Ollama, and OpenAI-compatible APIs.

Features

  • 🔍 Automatic Model Discovery - Discovers available models from configured endpoints
  • 💬 Chat Completions - Both streaming and non-streaming modes
  • 🎙️ Text-to-Speech - Generate audio from text with multiple voice options
  • 🔄 Multiple Endpoints - Support for multiple model and TTS endpoints
  • 🛡️ Error Handling - Comprehensive error handling with custom exceptions
  • 📦 Zero Dependencies - Uses only Node.js built-in modules (https, http, fs)

Installation

No external dependencies required! Just copy uncloseai_lib.js to your project:

# Copy the library file
cp uncloseai_lib.js your-project/

# Or use it directly
node examples.js

Quick Start

const { uncloseai } = require('./uncloseai_lib');

// Initialize client (auto-discovers from environment variables)
const client = new uncloseai();

// Non-streaming chat
const response = await client.chat({
    model: 'auto',
    messages: [{ role: 'user', content: 'Hello!' }]
});
console.log(response.choices[0].message.content);

// Streaming chat
for await (const chunk of client.chatStream({
    model: 'auto',
    messages: [{ role: 'user', content: 'Write a story' }]
})) {
    const content = chunk.choices?.[0]?.delta?.content || '';
    process.stdout.write(content);
}

Configuration

Environment Variables

# Model endpoints (numbered 1-9999)
export MODEL_ENDPOINT_1="https://hermes.ai.unturf.com/v1"
export MODEL_ENDPOINT_2="https://qwen.ai.unturf.com/v1"

# TTS endpoints (numbered 1-9999)
export TTS_ENDPOINT_1="https://speech.ai.unturf.com/v1"

Programmatic Configuration

const client = new uncloseai({
    endpoints: ['https://api.example.com/v1'],
    ttsEndpoints: ['https://tts.example.com/v1'],
    apiKey: 'your-api-key',  // Optional
    timeout: 30000,          // Request timeout in milliseconds
    debug: true              // Enable debug logging
});

API Reference

uncloseai. Client

Main client class for interacting with AI APIs.

constructor(options)

Initialize the client.

Parameters:

  • endpoints (Array, optional): Model endpoints (auto-discovers from env if not provided)
  • ttsEndpoints (Array, optional): TTS endpoints (auto-discovers from env if not provided)
  • apiKey (String, optional): API key for authentication
  • timeout (Number): Request timeout in milliseconds (default: 30000)
  • debug (Boolean): Enable debug logging (default: false)

async listModels()

List all discovered models with their metadata.

Returns:

  • Array of objects with id, endpoint, and max_tokens

Example:

const models = await client.listModels();
console.log(models);
// [{ id: 'model-name', endpoint: 'https://...', max_tokens: 8192 }, ...]

async chat(options)

Send a non-streaming chat completion request.

Parameters:

  • messages (Array): Array of message objects with 'role' and 'content'
  • model (String): Model ID or 'auto' for first available (default: 'auto')
  • maxTokens (Number, optional): Maximum tokens to generate
  • temperature (Number): Sampling temperature 0-2 (default: 0.7)
  • topP (Number): Nucleus sampling parameter (default: 1.0)
  • Additional parameters passed to API

Returns:

  • Chat completion response object

Throws:

  • ModelNotFoundError: If model not found
  • ConnectionError: If request fails

Example:

const response = await client.chat({
    model: 'auto',
    messages: [
        { role: 'system', content: 'You are a helpful assistant.' },
        { role: 'user', content: 'What is AI?' }
    ],
    maxTokens: 100
});
console.log(response.choices[0].message.content);

async *chatStream(options)

Send a streaming chat completion request.

Parameters:

  • Same as chat()

Yields:

  • Chat completion chunk objects

Throws:

  • ModelNotFoundError: If model not found
  • StreamingError: If streaming fails

Example:

for await (const chunk of client.chatStream({
    model: 'auto',
    messages: [{ role: 'user', content: 'Write a haiku' }]
})) {
    const content = chunk.choices?.[0]?.delta?.content || '';
    if (content) {
        process.stdout.write(content);
    }
}

async tts(options)

Generate speech from text.

Parameters:

  • text (String): Text to convert to speech
  • voice (String): Voice to use - alloy, echo, fable, onyx, nova, shimmer (default: 'alloy')
  • model (String): TTS model - tts-1 or tts-1-hd (default: 'tts-1')
  • Additional parameters passed to API

Returns:

  • Buffer containing audio data (MP3 format)

Throws:

  • ConnectionError: If request fails
  • uncloseaiError: If no TTS endpoints available

Example:

const audioData = await client.tts({
    text: 'Hello from uncloseai.!',
    voice: 'alloy',
    model: 'tts-1'
});

fs.writeFileSync('output.mp3', audioData);

Usage Examples

Basic Chat

const { uncloseai } = require('./uncloseai_lib');

const client = new uncloseai();

const response = await client.chat({
    model: 'auto',
    messages: [
        { role: 'system', content: 'You are a helpful assistant.' },
        { role: 'user', content: 'What is JavaScript?' }
    ],
    maxTokens: 100
});

console.log(response.choices[0].message.content);

Streaming Chat

for await (const chunk of client.chatStream({
    model: 'auto',
    messages: [{ role: 'user', content: 'Write a haiku about code' }],
    maxTokens: 100
})) {
    const content = chunk.choices?.[0]?.delta?.content || '';
    if (content) {
        process.stdout.write(content);
    }
}
console.log(); // newline

Multi-Turn Conversation

const messages = [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'What is AI?' }
];

// First response
const response1 = await client.chat({ model: 'auto', messages });
const assistantMsg = response1.choices[0].message.content;
messages.push({ role: 'assistant', content: assistantMsg });

// Follow-up question
messages.push({ role: 'user', content: 'Can you explain more?' });
const response2 = await client.chat({ model: 'auto', messages });

Text-to-Speech

const fs = require('fs');

const audioData = await client.tts({
    text: 'Hello from uncloseai.!',
    voice: 'alloy',
    model: 'tts-1'
});

fs.writeFileSync('output.mp3', audioData);

Using Specific Models

// List available models
const models = await client.listModels();
for (const model of models) {
    console.log(`${model.id} - Max tokens: ${model.max_tokens}`);
}

// Use specific model
const response = await client.chat({
    model: models[0].id,
    messages: [{ role: 'user', content: 'Hello' }]
});

Error Handling

const { uncloseai, uncloseaiError, ModelNotFoundError } = require('./uncloseai_lib');

const client = new uncloseai();

try {
    const response = await client.chat({
        model: 'non-existent-model',
        messages: [{ role: 'user', content: 'Hello' }]
    });
} catch (error) {
    if (error instanceof ModelNotFoundError) {
        console.log(`Model error: ${error.message}`);
    } else if (error instanceof uncloseaiError) {
        console.log(`API error: ${error.message}`);
    } else {
        throw error;
    }
}

Running Examples

# Set environment variables
export MODEL_ENDPOINT_1="https://hermes.ai.unturf.com/v1"
export MODEL_ENDPOINT_2="https://qwen.ai.unturf.com/v1"
export TTS_ENDPOINT_1="https://speech.ai.unturf.com/v1"

# Run example script
node examples.js

Docker Usage

# Build
docker build -t uncloseai-nodejs .

# Run examples
docker run -e MODEL_ENDPOINT_1="https://..." uncloseai-nodejs node examples.js

Compatibility

Tested with:

  • vLLM (v0.5.0+)
  • Ollama (v0.1.0+)
  • OpenAI API (compatible endpoints)

Error Types

  • uncloseaiError - Base error class for all library errors
  • ConnectionError - Network connection errors
  • ModelNotFoundError - Requested model not available
  • StreamingError - Errors during streaming requests

License

MIT License - See LICENSE file for details

Contributing

Contributions welcome! Please submit pull requests or open issues.

Support

For issues, questions, or contributions, please visit: https://github.com/yourusername/uncloseai

Changelog

v1.0.0 (2025-10-13)

  • Initial release
  • Streaming and non-streaming chat support
  • Text-to-speech generation
  • Automatic model discovery
  • Zero external dependencies
  • Comprehensive error handling