A minimal Node.js CLI that calls the free Together API to run open‑source language models
Welcome to another official UVF IT guide! In this short tutorial we’ll build a tiny command‑line tool that talks to the Together Link service and returns model completions, just like the official product but stripped down to the essentials.
You’ll finish a working clone in about 45 minutes using only Node ≥ 18, npm, and the free tier of the Together API – no paid services, no databases, just pure HTTP.
We start by creating a fresh npm project so we have a package.json to track dependencies. The helper script also adds a .gitignore and installs dotenv – a tiny library that lets us load the API key from a local .env file without committing it.
Running this script once gives you a ready‑to‑code directory with Node’s native fetch available (Node ≥ 18) and a place to store secrets safely.
setup.sh
#!/usr/bin/env bash
set -e
# Create project folder if not already there
mkdir -p together-cli && cd together-cli
# Initialise npm with defaults
npm init -y > /dev/null
# Install dotenv for env file handling
npm install dotenv@latest > /dev/null
# Create .gitignore to keep .env private
cat <<'EOF' > .gitignore
node_modules/
.env
EOF
# Add starter .env template
cat <<'EOF' > .env
# Paste your free Together API key here
TOGETHER_API_KEY=YOUR_KEY_HERE
EOF
echo "Setup complete. Run 'node fetch.js' after adding your API key."Now we write the heart of the CLI: a small wrapper that reads the API key from the environment and posts a prompt to Together’s /v1/completions endpoint. The function returns the raw text so the CLI can simply console.log it.
We keep the code under 25 lines, use native fetch, and handle basic error cases so beginners can see what’s happening under the hood.
fetch.js
import { config } from 'dotenv';
config(); // load .env into process.env
const API_KEY = process.env.TOGETHER_API_KEY;
if (!API_KEY) {
console.error('❌ Missing TOGETHER_API_KEY in .env');
process.exit(1);
}
/**
* Calls Together's completions endpoint.
* @param {string} prompt - The text you want the model to continue.
* @param {string} model - Model identifier, e.g., 'togethercomputer/llama-2-7b-chat'.
* @returns {Promise<string>} - The generated completion.
*/
export async function getCompletion(prompt, model = 'togethercomputer/llama-2-7b-chat') {
const response = await fetch('https://api.together.xyz/v1/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${API_KEY}`,
},
body: JSON.stringify({
model,
prompt,
max_tokens: 64,
}),
});
if (!response.ok) {
const err = await response.text();
throw new Error(`API error ${response.status}: ${err}`);
}
const data = await response.json();
return data.choices?.[0]?.text?.trim() || '';
}
// Simple CLI usage when run directly
if (import.meta.url === process.argv[1]) {
const userPrompt = process.argv.slice(2).join(' ') || 'Hello, world!';
getCompletion(userPrompt)
.then((txt) => console.log('🤖', txt))
.catch((e) => console.error('❗', e.message));
}In this step we turn our Node script into a real command‑line tool. We read the prompt the user typed, fall back to a helpful usage message when nothing is supplied, and then hand the prompt off to the helper that talks to the Together API. Finally we print the model’s reply so the user sees the answer right in the terminal.
The code stays tiny—about fifteen lines—so it’s easy to follow and modify. By keeping the API call in a separate helper module we keep the CLI focused on input/output concerns, which makes future extensions (like adding flags) straightforward.
index.js
#!/usr/bin/env node
const { callModel } = require('./helper')
// Grab the prompt from the command line arguments
const args = process.argv.slice(2)
if (args.length === 0) {
console.error('Usage: ./index.js "your prompt"')
process.exit(1)
}
const prompt = args.join(' ')
// Optional: allow overriding the model via an env var
const model = process.env.TOGETHER_MODEL || 'togethercomputer/llama-2-7b'
callModel({ model, prompt })
.then(response => {
console.log('🤖', response)
})
.catch(err => {
console.error('Error:', err.message)
process.exit(1)
})Now that the CLI is wired up, give it a spin. Make sure the script is executable, export your Together API key, and invoke the file with a short prompt. The script will contact the free Together endpoint, wait for the response, and print it prefixed with a robot emoji for clarity.
If everything is set up correctly you’ll see a one‑line answer from the model almost instantly. This demonstrates that the minimal clone works end‑to‑end without any extra tooling.
chmod +x index.js
export TOGETHER_API_KEY=YOUR_KEY_HERE
./index.js "What is the capital of France?"Check the output you just got – it should start with the 🤖 emoji followed by a sensible answer. Once you’ve confirmed the basic flow, try swapping the model or tweaking parameters like temperature to see how the response changes.
You can experiment by setting TOGETHER_MODEL to another open model name or by editing the helper to pass additional JSON fields. These small tweaks let you explore the full power of the Together API while keeping the CLI minimal.
export TOGETHER_MODEL=togethercomputer/mpt-7b
./index.js "Tell me a short joke."| Check | Command / Why it matters |
|---|---|
| npm init succeeded and index.js is executable | ls -l index.js shows -rwx permissions |
| CLI prints usage when no argument is given | ./index.js outputs "Usage: ./index.js \"your prompt\"" |
| A real model response appears for a valid prompt | TOGETHER_API_KEY=... ./index.js "Hello" prints a line starting with 🤖 |
You now have a functional clone of Together Link’s core feature. From here you can add streaming output, support multiple models, or publish the CLI as an npm package for easy reuse.