DeepSeek Harness setup
DeepSeek Harness (the dsh command) is a coding agent from DeepSeek with a web interface in your browser. One command below installs it, connects it to Claudexia and starts it. Harness then offers DeepSeek, GPT, Claude and other models from the Claudexia catalog.
What you need
- Node.js 24.9 or newer. Node.js is the runtime Harness runs on. Download the LTS version from nodejs.org, install it, open a new terminal window and check the version:
- A Claudexia key. Create one in the dashboard under API keys. Keys start with
sk_cdx_. No account yet? Sign up. - Money on your balance. Top up by card or with crypto, from $1. This helps when payments to the original provider do not go through in your country. Payment options are listed on Pricing.
node --versionThe output should start with v24.9 or a higher number, for example v24.11.0 or v26.1.0.
Steps
- Copy your key from API keys.
- Open a terminal. On macOS it is the Terminal app; on Windows it is PowerShell from the Start menu.
- Copy the command for your system with the Copy button and paste it into the terminal. Replace
sk_cdx_YOUR_KEYat the top of the command with your key. - Press Enter. The command installs Harness, writes the settings and starts it. A browser window opens in a minute or two.
bash <<'CLAUDEXIA_SETUP'
set -e
CLAUDEXIA_API_KEY="sk_cdx_YOUR_KEY"
if [ "${CLAUDEXIA_API_KEY#sk_cdx_}" = "YOUR_KEY" ]; then
echo "Replace sk_cdx_YOUR_KEY with your Claudexia key" >&2
exit 1
fi
if ! node -e 'const [a, b] = process.versions.node.split(".").map(Number); process.exit(a > 24 || (a === 24 && b >= 9) ? 0 : 1)'; then
echo "DeepSeek Harness needs Node.js 24.9 or newer: https://nodejs.org" >&2
exit 1
fi
npm install -g @deepseek-ai/dsh
export CLAUDEXIA_API_KEY
NODE_PATH="$(npm root -g)/@deepseek-ai/dsh/node_modules" node <<'NODE'
const fs = require('fs')
const os = require('os')
const path = require('path')
const yaml = require('js-yaml')
const home = process.env.DSH_HOME || path.join(os.homedir(), '.dsh')
const dir = path.join(home, 'profiles', 'web')
const file = path.join(dir, 'cordis.patch.yml')
fs.mkdirSync(dir, { recursive: true })
let rows = []
if (fs.existsSync(file)) {
try {
rows = yaml.load(fs.readFileSync(file, 'utf8')) || []
} catch (error) {
console.error('Cannot read ' + file + ': ' + error.message)
process.exit(1)
}
if (!Array.isArray(rows)) {
console.error(file + ' is not a list, nothing was changed')
process.exit(1)
}
fs.copyFileSync(file, file + '.bak-' + Date.now())
}
const claudexia = {
displayName: 'Claudexia',
apiKeyEnv: 'CLAUDEXIA_API_KEY',
api: 'openai-completions',
baseURL: 'https://api.claudexia.tech/v1',
models: [
{ id: 'deepseek-v4.1-flash', name: 'DeepSeek V4.1 Flash' },
{ id: 'deepseek-v4-pro', name: 'DeepSeek V4 Pro' },
{ id: 'gpt-6-sol', name: 'GPT-6 Sol' },
{ id: 'gpt-6-luna', name: 'GPT-6 Luna' },
{ id: 'claude-sonnet-5', name: 'Claude Sonnet 5' },
{ id: 'glm-5.3', name: 'GLM-5.3' },
{ id: 'qwen3.8-max', name: 'Qwen3.8 Max' },
],
}
const piAi = rows.find((row) => row && row.id === 'llm-pi-ai')
const piAiConfig = (piAi && piAi.config) || {}
const kept = rows.filter((row) => row && row.id !== 'llm-pi-ai' && row.id !== 'agent-default-model')
kept.push({ id: 'llm-pi-ai', config: { ...piAiConfig, providers: { ...(piAiConfig.providers || {}), claudexia } } })
kept.push({ id: 'agent-default-model', config: { provider: 'claudexia', model: 'deepseek-v4.1-flash' } })
fs.writeFileSync(file, yaml.dump(kept, { lineWidth: -1, noRefs: true }))
const envFile = path.join(home, '.env')
const lines = fs.existsSync(envFile) ? fs.readFileSync(envFile, 'utf8').split(/\r?\n/) : []
const rest = lines.filter((line) => line && !line.startsWith('CLAUDEXIA_API_KEY='))
rest.push('CLAUDEXIA_API_KEY=' + process.env.CLAUDEXIA_API_KEY)
fs.writeFileSync(envFile, rest.join('\n') + '\n', { mode: 0o600 })
fs.chmodSync(envFile, 0o600)
console.log('Claudexia is ready. Default model: deepseek-v4.1-flash')
NODE
dsh web
CLAUDEXIA_SETUP& {
$env:CLAUDEXIA_API_KEY = 'sk_cdx_YOUR_KEY'
if ($env:CLAUDEXIA_API_KEY.EndsWith('_YOUR_KEY')) {
Write-Host 'Replace sk_cdx_YOUR_KEY with your Claudexia key'
return
}
$nodeOk = $false
try { $nodeOk = [version](node -p 'process.versions.node') -ge [version]'24.9.0' } catch {}
if (-not $nodeOk) {
Write-Host 'DeepSeek Harness needs Node.js 24.9 or newer: https://nodejs.org'
return
}
npm.cmd install -g @deepseek-ai/dsh
if ($LASTEXITCODE -ne 0) { return }
$env:NODE_PATH = Join-Path (npm.cmd root -g) '@deepseek-ai\dsh\node_modules'
$setup = @'
const fs = require('fs')
const os = require('os')
const path = require('path')
const yaml = require('js-yaml')
const home = process.env.DSH_HOME || path.join(os.homedir(), '.dsh')
const dir = path.join(home, 'profiles', 'web')
const file = path.join(dir, 'cordis.patch.yml')
fs.mkdirSync(dir, { recursive: true })
let rows = []
if (fs.existsSync(file)) {
try {
rows = yaml.load(fs.readFileSync(file, 'utf8')) || []
} catch (error) {
console.error('Cannot read ' + file + ': ' + error.message)
process.exit(1)
}
if (!Array.isArray(rows)) {
console.error(file + ' is not a list, nothing was changed')
process.exit(1)
}
fs.copyFileSync(file, file + '.bak-' + Date.now())
}
const claudexia = {
displayName: 'Claudexia',
apiKeyEnv: 'CLAUDEXIA_API_KEY',
api: 'openai-completions',
baseURL: 'https://api.claudexia.tech/v1',
models: [
{ id: 'deepseek-v4.1-flash', name: 'DeepSeek V4.1 Flash' },
{ id: 'deepseek-v4-pro', name: 'DeepSeek V4 Pro' },
{ id: 'gpt-6-sol', name: 'GPT-6 Sol' },
{ id: 'gpt-6-luna', name: 'GPT-6 Luna' },
{ id: 'claude-sonnet-5', name: 'Claude Sonnet 5' },
{ id: 'glm-5.3', name: 'GLM-5.3' },
{ id: 'qwen3.8-max', name: 'Qwen3.8 Max' },
],
}
const piAi = rows.find((row) => row && row.id === 'llm-pi-ai')
const piAiConfig = (piAi && piAi.config) || {}
const kept = rows.filter((row) => row && row.id !== 'llm-pi-ai' && row.id !== 'agent-default-model')
kept.push({ id: 'llm-pi-ai', config: { ...piAiConfig, providers: { ...(piAiConfig.providers || {}), claudexia } } })
kept.push({ id: 'agent-default-model', config: { provider: 'claudexia', model: 'deepseek-v4.1-flash' } })
fs.writeFileSync(file, yaml.dump(kept, { lineWidth: -1, noRefs: true }))
const envFile = path.join(home, '.env')
const lines = fs.existsSync(envFile) ? fs.readFileSync(envFile, 'utf8').split(/\r?\n/) : []
const rest = lines.filter((line) => line && !line.startsWith('CLAUDEXIA_API_KEY='))
rest.push('CLAUDEXIA_API_KEY=' + process.env.CLAUDEXIA_API_KEY)
fs.writeFileSync(envFile, rest.join('\n') + '\n', { mode: 0o600 })
fs.chmodSync(envFile, 0o600)
console.log('Claudexia is ready. Default model: deepseek-v4.1-flash')
'@
$setup | node
if ($LASTEXITCODE -eq 0) { dsh.cmd web }
}What the command does: installs the @deepseek-ai/dsh package with npm, adds the Claudexia provider to ~/.dsh/profiles/web/cordis.patch.yml and saves the key to ~/.dsh/.env. Only your user can read the key file. If the settings file already exists, the command saves a copy next to it (cordis.patch.yml.bak-…) and keeps your other providers. You can run it again, for example to change the key.
Next time, start Harness from your project folder with one command:
dsh webCheck that it works
- If the browser did not open, copy the address like
http://127.0.0.1:3080/?token=…from the terminal and open it. Without thetokenpart the page answers with 401. - Choose your project folder (workspace). The input box stays disabled until you do.
- In the model list pick Claudexia and
deepseek-v4.1-flash, then type "Reply with one word: OK". If a reply comes back, it works.
Setup without the command
You can also add the provider inside Harness: Settings → Models → Add model provider → Custom model API. Provider ID: claudexia, Base URL: https://api.claudexia.tech/v1, API protocol: OpenAI Chat Completions, and paste your key into the key field. Fetch available models loads the model list.
Or write the provider into the settings file yourself. This is the same file the command writes:
- id: llm-pi-ai
config:
providers:
claudexia:
displayName: Claudexia
apiKeyEnv: CLAUDEXIA_API_KEY
api: openai-completions
baseURL: https://api.claudexia.tech/v1
models:
- id: deepseek-v4.1-flash
name: DeepSeek V4.1 Flash
- id: deepseek-v4-pro
name: DeepSeek V4 Pro
- id: gpt-6-sol
name: GPT-6 Sol
- id: gpt-6-luna
name: GPT-6 Luna
- id: claude-sonnet-5
name: Claude Sonnet 5
- id: glm-5.3
name: GLM-5.3
- id: qwen3.8-max
name: Qwen3.8 Max
- id: agent-default-model
config:
provider: claudexia
model: deepseek-v4.1-flashPut the key in ~/.dsh/.env as CLAUDEXIA_API_KEY=sk_cdx_… and restart dsh web.
Which model to pick
- For everyday work:
claude-sonnet-5in the Premium channel. On Cheap useclaude-opus-5-5. - For hard tasks:
claude-opus-5-5orgpt-6-astra. - To spend less:
deepseek-v4.1-flashorgpt-6-luna. Both cost several times less than Sonnet 5.
To add a model, append its ID to the models list in the settings file or in Settings → Models. The model line in the agent-default-model block sets the default for new sessions.
Prices and the full list are in the model catalog. GPT, DeepSeek, Gemini and other non-Claude models work only with Premium channel keys: see Channels.
Common errors
Click an error to see what to do.
Needs Node.js 24.9 or newer
Your Node.js is too old or missing. Download the LTS version from nodejs.org, install it, open a new terminal window and run the command again.
npm: EACCES or permission denied
npm cannot write to a system folder. This happens on Linux when Node.js comes from system packages. Install Node.js with the nodejs.org installer or with nvm and try again. The npm warn allow-scripts lines during install do not stop Harness from starting.
Harness fails on every request
The Harness docs suggest adding a compat block with supportsDeveloperRole: false and maxTokensField: max_tokens for third-party gateways. Add it next to baseURL in the settings file, save it and try again.
401: Invalid or missing API key
The tool sent a wrong key or no key at all. Copy the whole key from API keys: it starts with sk_cdx_ and has no spaces or line breaks. If the key was deleted or revoked, create a new one. Restart the tool after you change the key.
402: balance is empty
Your balance ran out. In the OpenAI format the error has the code insufficient_quota; in the Anthropic format it is HTTP status 402. Top up under Billing by card or with crypto, from $1. No settings need to change, just send the request again. When the balance is almost empty, requests run one at a time and you may get a 429 with the code low_balance. The code account_frozen means the account is suspended: contact support.
404: Not found or wrong address
The tool calls the wrong address. For this tool the address is https://api.claudexia.tech/v1, ending in /v1 with no slash after it. If the tool adds /v1 on its own, you end up with /v1/v1 and a 404: remove /v1 from the field. The address table for other tools is on Addresses and formats.
The tool does not see the key
Tools read their settings at startup. Quit the tool completely and open it again, and open a new terminal window. An environment variable set in one terminal window is not visible in another. Apps started from the Dock, the Start menu or a shortcut do not read ~/.zshrc or ~/.bashrc: for them, put the key in the tool settings.
Model not found
The model name must match the ID in the catalog exactly: claude-sonnet-5, not "Claude Sonnet 5". Only Claude Code understands the [1m] suffix; leave it out everywhere else.
403: model_not_in_channel
The model is not in your key channel. The Cheap channel serves Claude models only; Premium serves the whole catalog. Switch the model or the key channel: see Premium and Cheap channels.
Check the key without the tool
The command below sends one short request straight to Claudexia, without the tool. It tells you whether the problem is your key and balance or the tool settings. Replace sk_cdx_YOUR_KEY with your key. The request costs a fraction of a cent.
curl https://api.claudexia.tech/v1/chat/completions \
-H "Authorization: Bearer sk_cdx_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "claude-sonnet-5", "max_tokens": 16, "messages": [{"role": "user", "content": "Reply with OK"}]}'Invoke-RestMethod https://api.claudexia.tech/v1/chat/completions -Method Post `
-Headers @{ Authorization = 'Bearer sk_cdx_YOUR_KEY' } -ContentType 'application/json' `
-Body '{"model": "claude-sonnet-5", "max_tokens": 16, "messages": [{"role": "user", "content": "Reply with OK"}]}'You get a reply with a choices field: the key and balance are fine, so look at the tool settings. You get an error: the code and message point to the cause. They are covered above and in the error reference.
Harness is still a preview release, and its settings format may change. We checked that the command installs Harness 0.2.2-rc.1, writes a valid settings file and starts the web interface. If the settings stop loading after a Harness update, compare them with the Harness docs or contact support.