
Research
Supply Chain Attack on Axios Pulls Malicious Dependency from npm
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.
@routstr/client-sdk
Advanced tools
TypeScript SDK for interacting with the Routstr decentralized AI network
A TypeScript SDK for interacting with the Routstr decentralized AI network. Provides automatic Cashu token management, privacy through routing, and simple authentication via Nostr.
npm install @routstr/client-sdk
import { RoutstrClient } from '@routstr/client-sdk';
// Initialize client with your Nostr private key
const client = new RoutstrClient({
nsec: 'nsec1your_private_key_here'
});
// Initialize and start chatting
await client.init();
const response = await client.chat(
'Hello! Explain quantum computing in simple terms.',
undefined, // Use default model
{ temperature: 0.7 }
);
console.log(response);
The SDK uses Nostr for authentication. You need a Nostr private key in nsec format:
import { RoutstrClient, RoutstrConfig } from '@routstr/client-sdk';
const config: RoutstrConfig = {
nsec: 'nsec1your_private_key_here',
mintUrl: 'https://mint.minibits.cash/Bitcoin', // Optional: custom Cashu mint
baseUrl: 'https://api.routstr.com/', // Optional: custom API endpoint
defaultModel: 'qwen/qwen3-14b' // Optional: preferred model
};
const client = new RoutstrClient(config);
const balance = await client.getBalance();
console.log(`Total: ${balance.total} sats`);
console.log(`In Proofs: ${balance.proofs} sats`);
console.log(`In API Tokens: ${balance.api} sats`);
// Create a Lightning invoice
const invoice = await client.createInvoice(1000); // 1000 sats
if (invoice.success) {
console.log('Pay this invoice:', invoice.invoice);
// Check if invoice was paid
const paid = await client.checkInvoice(invoice.quoteId!);
console.log('Invoice paid:', paid.paid);
}
const result = await client.importCashuToken('cashuAeyJ0b2tlbiI6W3sibWludCI6...');
if (result.success) {
console.log(`Imported ${result.amount} sats`);
}
const response = await client.chat(
'What is Bitcoin?',
'qwen/qwen3-14b', // Optional: specific model
{
systemPrompt: 'You are a helpful crypto expert.',
temperature: 0.7,
maxTokens: 500
}
);
import { ChatMessage } from '@routstr/client-sdk';
const messages: ChatMessage[] = [
{
role: 'system',
content: 'You are a helpful assistant.'
},
{
role: 'user',
content: 'Explain machine learning basics.'
}
];
const response = await client.chatCompletion({
model: 'qwen/qwen3-14b',
messages,
temperature: 0.7,
max_tokens: 1000
});
console.log(response.choices[0].message.content);
await client.streamChatCompletion(
{
model: 'qwen/qwen3-14b',
messages: [
{ role: 'user', content: 'Tell me a long story about space exploration.' }
]
},
{
onToken: (token) => {
process.stdout.write(token); // Print each token as it arrives
},
onComplete: (fullResponse) => {
console.log('\nStream completed!');
},
onError: (error) => {
console.error('Stream error:', error);
}
}
);
Send images along with text:
import { ChatMessage, MessageContent } from '@routstr/client-sdk';
const messages: ChatMessage[] = [
{
role: 'user',
content: [
{
type: 'text',
text: 'What do you see in this image?'
},
{
type: 'image_url',
image_url: {
url: 'data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=='
}
}
]
}
];
const response = await client.chatCompletion({
model: 'gpt-4-vision-preview',
messages
});
await client.init(); // Fetches available models
const models = client.getModels();
models.forEach(model => {
console.log(`${model.name}: ${model.sats_pricing.max_cost} sats max`);
});
const model = client.getModel('qwen/qwen3-14b');
if (model) {
console.log(`Context length: ${model.context_length}`);
console.log(`Modality: ${model.architecture.modality}`);
console.log(`Cost: ${model.sats_pricing.completion} sats/token`);
}
import {
isInsufficientBalanceError,
isNetworkError,
ValidationError
} from '@routstr/client-sdk';
try {
const response = await client.chat('Hello!');
} catch (error) {
if (isInsufficientBalanceError(error)) {
console.log('Please add more funds:', error.message);
} else if (isNetworkError(error)) {
console.log('Network issue:', error.message);
} else if (error instanceof ValidationError) {
console.log('Configuration error:', error.message);
} else {
console.log('Unknown error:', error);
}
}
const history = client.getTransactionHistory();
history.forEach(tx => {
const date = new Date(tx.timestamp).toLocaleString();
console.log(`${tx.type}: ${tx.amount} sats on ${date}`);
});
client.updateConfig({
baseUrl: 'https://my-custom-routstr-node.com/',
defaultModel: 'anthropic/claude-3-sonnet'
});
const config = client.getConfig();
console.log('Current base URL:', config.baseUrl);
console.log('Default model:', config.defaultModel);
// Get your Nostr public key
const pubkey = client.getPublicKey();
console.log('Your pubkey:', pubkey);
// Get formatted npub
const npub = client.getNpub();
console.log('Your npub:', npub);
// Get display format
const formatted = client.getFormattedPublicKey();
console.log('Formatted:', formatted);
Send Cashu tokens privately to other Nostr users using encrypted gift wraps:
// Wrap a Cashu token for another user
const result = await client.wrapCashuToken(
cashuTokenString,
recipientPublicKey, // hex format
'Here are some AI credits!' // optional note
);
if (result.success) {
// Send the wrapped event via Nostr relay
console.log('Gift wrapped!', result.event);
}
// Unwrap a received gift (from Nostr relay)
const unwrapResult = await client.unwrapCashuToken(giftEvent);
if (unwrapResult.success && unwrapResult.gift) {
console.log('Gift received:', unwrapResult.gift.note);
// Import the token
const imported = await client.importCashuToken(unwrapResult.gift.token);
console.log(`Added ${imported.amount} sats to wallet`);
}
// Get stored wrapped tokens (gifts you've sent)
const sentGifts = client.getStoredWrappedTokens();
console.log(`You've sent ${sentGifts.length} gifts`);
// Validate gift wrap events
const isValid = client.isValidCashuGiftWrap(event);
console.log('Valid gift:', isValid);
// Remove old wrapped token from storage
client.removeWrappedToken(eventId);
Main client class for interacting with Routstr.
new RoutstrClient(config: RoutstrConfig)
init() - Initialize client and fetch available modelschat(message, model?, options?) - Simple chat methodchatCompletion(request) - Full chat completionstreamChatCompletion(request, callbacks?) - Streaming chatgetBalance() - Get current balancegetModels() - Get available modelsgetModel(id) - Get specific modelcreateInvoice(amount) - Create Lightning invoicecheckInvoice(quoteId) - Check invoice payment statusimportCashuToken(token) - Import Cashu tokengetTransactionHistory() - Get transaction historyNIP-60 Gift Wrap Methods:
wrapCashuToken(token, recipientPubkey, note?) - Wrap token for private transferunwrapCashuToken(event) - Unwrap received giftgetStoredWrappedTokens() - Get sent gifts historyremoveWrappedToken(eventId) - Remove stored wrapped tokenisValidCashuGiftWrap(event) - Validate gift wrap eventKey TypeScript interfaces:
interface RoutstrConfig {
nsec: string;
mintUrl?: string;
baseUrl?: string;
defaultModel?: string;
}
interface ChatMessage {
role: 'system' | 'user' | 'assistant';
content: string | MessageContent[];
}
interface Balance {
proofs: number;
api: number;
total: number;
}
Check the examples/ directory for complete working examples:
basic-usage.ts - Getting startedstreaming-chat.ts - Real-time streamingadvanced-usage.ts - Advanced featurescomplete-integration.ts - Full workflownip60-gift-wrap.ts - NIP-60 Gift Wrap protocol for private token transfersnpm run build
npm run dev
npm run lint
npm run format
MIT
For issues and questions:
Contributions welcome! Please read our contributing guidelines and submit pull requests.
FAQs
TypeScript SDK for interacting with the Routstr decentralized AI network
The npm package @routstr/client-sdk receives a total of 0 weekly downloads. As such, @routstr/client-sdk popularity was classified as not popular.
We found that @routstr/client-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.

Security News
TeamPCP is partnering with ransomware group Vect to turn open source supply chain attacks on tools like Trivy and LiteLLM into large-scale ransomware operations.