Lumoswitch Docs
Agent setup

DeepSeek Harness setup

DeepSeek Harness (dsh) is DeepSeek's open-source Agent Harness and is currently in Developer Preview. Its plugin architecture composes the Agent, models, tools, and Web UI, and it supports custom OpenAI-compatible gateways as model providers.

Install Node.js first, then create a Lumoswitch API configuration with OpenAI-compatible output. The commands below launch the local Web UI through the official npx @deepseek-ai/dsh web entry point.

Choose a setup method

MethodBest forLocal writesStill active after exit?
One-time launchTesting a URL, Key, and model catalogTemporary Lumoswitch overlay; normal Harness runtime data remainsNo
Persistent useStarting the local Web UI from one pathNative DSH home + optional launcherYes

Neither method replaces $DSH_HOME/settings.yaml, the default profile patch, or an existing provider. Persistent setup stores the Access Key in the dedicated DSH home's owner-only .env file.

Prepare the connection values

PlaceholderValue
{{api_base_url}}OpenAI-compatible API configuration URL, including /v1
{{access_key}}The Lumoswitch Access Key selected for this configuration
{{model}}The client-facing model selected as the initial default
{{deepseek_harness_models_yaml}}Harness YAML model list generated from every downstream model and its effective reasoning capability

The console writes every downstream model into the overlay. It publishes reasoning levels only when the effective transport is known, explicitly disables reasoning controls for non-reasoning models, and does not guess for fixed or unknown capabilities.

One-time launch

Run this from the project directory that should be offered as the default workspace. The temporary directory contains only this launch's Lumoswitch overlay and is removed when the process exits:

(
  set -e
  LUMOSWITCH_DSH_TMP="$(mktemp -d "${TMPDIR:-/tmp}/lumoswitch-dsh.XXXXXX")"
  LUMOSWITCH_DSH_PATCH="$LUMOSWITCH_DSH_TMP/lumoswitch.cordis.yml"
  trap 'rm -f "$LUMOSWITCH_DSH_PATCH"; rmdir "$LUMOSWITCH_DSH_TMP" 2>/dev/null || true' EXIT
  cat > "$LUMOSWITCH_DSH_PATCH" <<'LUMOSWITCH_DSH_PATCH_EOF'
- id: agent-default-model
  config:
    provider: lumoswitch
    model: "{{model}}"
- id: llm-pi-ai
  config:
    providers:
      lumoswitch:
        displayName: Lumoswitch
        apiKeyEnv: LUMOSWITCH_DEEPSEEK_API_KEY
        api: openai-completions
        baseURL: "{{api_base_url}}"
        models:
{{deepseek_harness_models_yaml}}LUMOSWITCH_DSH_PATCH_EOF
  chmod 600 "$LUMOSWITCH_DSH_PATCH"
  env \
    LUMOSWITCH_DEEPSEEK_API_KEY="{{access_key}}" \
    npx @deepseek-ai/dsh web --patch "$LUMOSWITCH_DSH_PATCH"
)

The terminal prints the local Web UI URL, http://127.0.0.1:3080 by default. Choose the current workspace after opening it. Harness still initializes its normal profile, stores sessions, and uses the npm cache; only the Lumoswitch overlay and process-scoped Key are guaranteed to disappear on exit.

Persistent use

Run the complete command during initial setup and whenever the Lumoswitch URL, Access Key, or models change. It writes a dedicated native DSH home and optional launcher, backing up an existing copy with timestamped 0600 permissions before updating it.

set -e
LUMOSWITCH_ROOT="$HOME/.config/lumoswitch/agents/deepseek-harness"
LUMOSWITCH_ENV="$LUMOSWITCH_ROOT/env.sh"
LUMOSWITCH_LAUNCHER="$HOME/.local/bin/lumoswitch-deepseek-harness"
mkdir -p "$LUMOSWITCH_ROOT" "$(dirname "$LUMOSWITCH_LAUNCHER")"
umask 077

if [ -f "$LUMOSWITCH_LAUNCHER" ]; then
  LUMOSWITCH_BACKUP="$LUMOSWITCH_LAUNCHER.backup.$(date +%Y%m%d-%H%M%S)-$$"
  cp "$LUMOSWITCH_LAUNCHER" "$LUMOSWITCH_BACKUP"
  chmod 600 "$LUMOSWITCH_BACKUP"
fi

LUMOSWITCH_DSH_HOME="$LUMOSWITCH_ROOT/dsh-home"
mkdir -p "$LUMOSWITCH_DSH_HOME"
cat > "$LUMOSWITCH_DSH_HOME/cordis.patch.yml" <<'LUMOSWITCH_CONFIG_EOF'
- id: agent-default-model
  config:
    provider: lumoswitch
    model: "{{model}}"
- id: llm-pi-ai
  config:
    providers:
      lumoswitch:
        displayName: Lumoswitch
        apiKeyEnv: LUMOSWITCH_DEEPSEEK_API_KEY
        api: openai-completions
        baseURL: "{{api_base_url}}"
        models:
{{deepseek_harness_models_yaml}}LUMOSWITCH_CONFIG_EOF
cat > "$LUMOSWITCH_DSH_HOME/.env" <<'LUMOSWITCH_ENV_EOF'
LUMOSWITCH_DEEPSEEK_API_KEY={{access_key}}
LUMOSWITCH_ENV_EOF

cat > "$LUMOSWITCH_ENV" <<'LUMOSWITCH_ENV_EOF'
export DSH_HOME="$HOME/.config/lumoswitch/agents/deepseek-harness/dsh-home"
LUMOSWITCH_ENV_EOF

case "$SHELL" in
  */zsh) LUMOSWITCH_PROFILE="$HOME/.zshrc" ;;
  */bash) LUMOSWITCH_PROFILE="$HOME/.bashrc" ;;
  *) LUMOSWITCH_PROFILE="$HOME/.profile" ;;
esac
touch "$LUMOSWITCH_PROFILE"
LUMOSWITCH_PROFILE_MARKER='# Lumoswitch Agent: deepseek-harness'
LUMOSWITCH_SOURCE_LINE='[ -f "$HOME/.config/lumoswitch/agents/deepseek-harness/env.sh" ] && . "$HOME/.config/lumoswitch/agents/deepseek-harness/env.sh"'
if ! grep -Fqx "$LUMOSWITCH_SOURCE_LINE" "$LUMOSWITCH_PROFILE"; then
  printf '\n%s\n%s\n' "$LUMOSWITCH_PROFILE_MARKER" "$LUMOSWITCH_SOURCE_LINE" >> "$LUMOSWITCH_PROFILE"
fi
printf '%s\n' "$LUMOSWITCH_PROFILE" > "$LUMOSWITCH_ROOT/profile-path"

cat > "$LUMOSWITCH_LAUNCHER" <<'LUMOSWITCH_LAUNCHER_EOF'
#!/usr/bin/env bash
set -e
LUMOSWITCH_ROOT="$HOME/.config/lumoswitch/agents/deepseek-harness"
LUMOSWITCH_ENV="$LUMOSWITCH_ROOT/env.sh"
LUMOSWITCH_LAUNCHER="$HOME/.local/bin/lumoswitch-deepseek-harness"

if [ "$1" = "--lumoswitch-clean" ]; then
  if [ -f "$LUMOSWITCH_ROOT/profile-path" ]; then
    LUMOSWITCH_PROFILE="$(cat "$LUMOSWITCH_ROOT/profile-path")"
    if [ -f "$LUMOSWITCH_PROFILE" ]; then
      LUMOSWITCH_PROFILE_MARKER='# Lumoswitch Agent: deepseek-harness'
      LUMOSWITCH_SOURCE_LINE='[ -f "$HOME/.config/lumoswitch/agents/deepseek-harness/env.sh" ] && . "$HOME/.config/lumoswitch/agents/deepseek-harness/env.sh"'
      LUMOSWITCH_PROFILE_TMP="$LUMOSWITCH_PROFILE.lumoswitch.$$"
      awk -v marker="$LUMOSWITCH_PROFILE_MARKER" -v source="$LUMOSWITCH_SOURCE_LINE" '$0 != marker && $0 != source' "$LUMOSWITCH_PROFILE" > "$LUMOSWITCH_PROFILE_TMP"
      mv "$LUMOSWITCH_PROFILE_TMP" "$LUMOSWITCH_PROFILE"
    fi
  fi
  if [ -d "$LUMOSWITCH_ROOT" ]; then
    find "$LUMOSWITCH_ROOT" -depth \( -type f -o -type l \) -delete
    find "$LUMOSWITCH_ROOT" -depth -type d -exec rmdir {} \; 2>/dev/null || true
  fi
  find "$(dirname "$LUMOSWITCH_LAUNCHER")" -maxdepth 1 -type f -name 'lumoswitch-deepseek-harness.backup.*' -delete
  rm -f "$LUMOSWITCH_LAUNCHER"
  printf '%s\n' 'Lumoswitch native setup and optional launcher removed. Open a new terminal to refresh the environment.'
  exit 0
fi

if [ -f "$LUMOSWITCH_ENV" ]; then . "$LUMOSWITCH_ENV"; fi
exec npx @deepseek-ai/dsh web "$@"
LUMOSWITCH_LAUNCHER_EOF

chmod 600 "$LUMOSWITCH_ENV"
find "$LUMOSWITCH_ROOT" -type f -exec chmod 600 {} \;
chmod 700 "$LUMOSWITCH_LAUNCHER"
. "$LUMOSWITCH_ENV"
printf '%s\n' 'Native Lumoswitch setup saved. Future command: npx @deepseek-ai/dsh web'
printf '%s\n' 'Optional maintenance launcher: ~/.local/bin/lumoswitch-deepseek-harness'
npx @deepseek-ai/dsh web

For everyday use, run:

npx @deepseek-ai/dsh web

The invoking directory becomes the default workspace location. Web arguments remain available, for example npx @deepseek-ai/dsh web --port 3081.

Native Windows PowerShell import

These commands run natively in Windows PowerShell without WSL and use the same connection values as the macOS/Linux templates on this page.

One-time use on Windows

& {
  $ErrorActionPreference = 'Stop'
  $lumoswitchConfig = Join-Path ([IO.Path]::GetTempPath()) ('lumoswitch-' + [Guid]::NewGuid().ToString('N') + '.yml')
  $lumoswitchConfigContent = @'
- id: agent-default-model
  config:
    provider: lumoswitch
    model: "{{model}}"
- id: llm-pi-ai
  config:
    providers:
      lumoswitch:
        displayName: Lumoswitch
        apiKeyEnv: LUMOSWITCH_DEEPSEEK_API_KEY
        api: openai-completions
        baseURL: "{{api_base_url}}"
        models:
{{deepseek_harness_models_yaml}}
'@
  [IO.File]::WriteAllText($lumoswitchConfig, $lumoswitchConfigContent, [Text.UTF8Encoding]::new($false))
  $lumoswitchEnvironmentNames = @('LUMOSWITCH_DEEPSEEK_API_KEY')
  $lumoswitchPreviousEnvironment = @{}
  foreach ($lumoswitchName in $lumoswitchEnvironmentNames) {
    $lumoswitchPreviousEnvironment[$lumoswitchName] = [Environment]::GetEnvironmentVariable($lumoswitchName, 'Process')
  }
  $lumoswitchExitCode = 0
  try {
    [Environment]::SetEnvironmentVariable('LUMOSWITCH_DEEPSEEK_API_KEY', '{{access_key}}', 'Process')
    $lumoswitchArguments = @('@deepseek-ai/dsh', 'web', '--patch', $lumoswitchConfig)
    $lumoswitchExecutable = Get-Command 'npx.cmd' -CommandType Application -ErrorAction SilentlyContinue
    if ($null -eq $lumoswitchExecutable) { $lumoswitchExecutable = Get-Command 'npx' -CommandType Application -ErrorAction Stop }
    & $lumoswitchExecutable.Path @lumoswitchArguments
    if ($null -ne $LASTEXITCODE) { $lumoswitchExitCode = $LASTEXITCODE }
  } finally {
    foreach ($lumoswitchName in $lumoswitchEnvironmentNames) {
      [Environment]::SetEnvironmentVariable($lumoswitchName, $lumoswitchPreviousEnvironment[$lumoswitchName], 'Process')
    }
    Remove-Item -LiteralPath $lumoswitchConfig -Force -ErrorAction SilentlyContinue
  }
  if ($lumoswitchExitCode -ne 0) { throw 'npx exited with code ' + $lumoswitchExitCode }
}

Persistent use on Windows

This command backs up existing Lumoswitch-owned files and restricts access to the configuration and launcher for the current user.

& {
  $ErrorActionPreference = 'Stop'
  $lumoswitchRoot = Join-Path $env:LOCALAPPDATA 'Lumoswitch\agents\deepseek-harness'
  $lumoswitchLauncher = Join-Path $env:LOCALAPPDATA 'Lumoswitch\bin\lumoswitch-deepseek-harness.ps1'
  $lumoswitchBackupSuffix = (Get-Date -Format 'yyyyMMdd-HHmmss') + '-' + $PID

  function Protect-LumoswitchFile([string] $Path) {
    $lumoswitchSid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
    & icacls.exe $Path '/inheritance:r' '/grant:r' ('*' + $lumoswitchSid + ':(F)') '*S-1-5-18:(F)' '*S-1-5-32-544:(F)' | Out-Null
    if ($LASTEXITCODE -ne 0) { throw "Could not restrict access to $Path" }
  }

  function Install-LumoswitchFile([string] $Path, [string] $Content) {
    [IO.Directory]::CreateDirectory((Split-Path -Parent $Path)) | Out-Null
    if (Test-Path -LiteralPath $Path) {
      $lumoswitchItem = Get-Item -LiteralPath $Path -Force
      if ($lumoswitchItem.PSIsContainer -or ($lumoswitchItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
        throw "Lumoswitch refused to replace a directory or link: $Path"
      }
      $lumoswitchBackup = $Path + '.backup.' + $lumoswitchBackupSuffix
      Copy-Item -LiteralPath $Path -Destination $lumoswitchBackup
      Protect-LumoswitchFile $lumoswitchBackup
    }
    $lumoswitchTemporary = $Path + '.' + [Guid]::NewGuid().ToString('N') + '.tmp'
    try {
      [IO.File]::WriteAllText($lumoswitchTemporary, $Content, [Text.UTF8Encoding]::new($false))
      Move-Item -LiteralPath $lumoswitchTemporary -Destination $Path -Force
      Protect-LumoswitchFile $Path
    } finally {
      Remove-Item -LiteralPath $lumoswitchTemporary -Force -ErrorAction SilentlyContinue
    }
  }

  [IO.Directory]::CreateDirectory($lumoswitchRoot) | Out-Null
  $lumoswitchDshHome = Join-Path $lumoswitchRoot 'dsh-home'
  $lumoswitchPatch = Join-Path $lumoswitchDshHome 'cordis.patch.yml'
  $lumoswitchEnv = Join-Path $lumoswitchDshHome '.env'
  $lumoswitchPatchContent = @'
- id: agent-default-model
  config:
    provider: lumoswitch
    model: "{{model}}"
- id: llm-pi-ai
  config:
    providers:
      lumoswitch:
        displayName: Lumoswitch
        apiKeyEnv: LUMOSWITCH_DEEPSEEK_API_KEY
        api: openai-completions
        baseURL: "{{api_base_url}}"
        models:
{{deepseek_harness_models_yaml}}
'@
  Install-LumoswitchFile $lumoswitchPatch $lumoswitchPatchContent
  Install-LumoswitchFile $lumoswitchEnv 'LUMOSWITCH_DEEPSEEK_API_KEY={{access_key}}'

  $lumoswitchEnvironment = [ordered]@{}
  $lumoswitchEnvironment['DSH_HOME'] = (Join-Path $lumoswitchRoot 'dsh-home')
  foreach ($lumoswitchEntry in $lumoswitchEnvironment.GetEnumerator()) {
    [Environment]::SetEnvironmentVariable($lumoswitchEntry.Key, $lumoswitchEntry.Value, 'User')
    [Environment]::SetEnvironmentVariable($lumoswitchEntry.Key, $lumoswitchEntry.Value, 'Process')
  }

  $lumoswitchLauncherContent = @'
param([Parameter(ValueFromRemainingArguments = $true)][string[]] $AgentArgs)
$ErrorActionPreference = 'Stop'
$lumoswitchRoot = Join-Path $env:LOCALAPPDATA 'Lumoswitch\agents\deepseek-harness'
$lumoswitchLauncher = Join-Path $env:LOCALAPPDATA 'Lumoswitch\bin\lumoswitch-deepseek-harness.ps1'

function Remove-LumoswitchFile([string] $Path) {
  if (-not (Test-Path -LiteralPath $Path)) { return }
  $lumoswitchItem = Get-Item -LiteralPath $Path -Force
  if ($lumoswitchItem.PSIsContainer -or ($lumoswitchItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
    throw "Lumoswitch refused to remove a directory or link: $Path"
  }
  Remove-Item -LiteralPath $Path -Force
}

if ($AgentArgs.Count -gt 0 -and $AgentArgs[0] -eq '--lumoswitch-clean') {
  foreach ($lumoswitchName in @('DSH_HOME')) {
    [Environment]::SetEnvironmentVariable($lumoswitchName, $null, 'User')
    [Environment]::SetEnvironmentVariable($lumoswitchName, $null, 'Process')
  }
  if (Test-Path -LiteralPath $lumoswitchRoot -PathType Container) {
    $lumoswitchRootItem = Get-Item -LiteralPath $lumoswitchRoot -Force
    if ($lumoswitchRootItem.Attributes -band [IO.FileAttributes]::ReparsePoint) { throw 'Lumoswitch refused to clean through a linked directory.' }
    foreach ($lumoswitchFile in @(Get-ChildItem -LiteralPath $lumoswitchRoot -File -Recurse -Force)) { Remove-LumoswitchFile $lumoswitchFile.FullName }
    foreach ($lumoswitchDirectory in @(Get-ChildItem -LiteralPath $lumoswitchRoot -Directory -Recurse -Force | Sort-Object FullName -Descending)) {
      if ($null -eq (Get-ChildItem -LiteralPath $lumoswitchDirectory.FullName -Force | Select-Object -First 1)) { Remove-Item -LiteralPath $lumoswitchDirectory.FullName -Force }
    }
    if ($null -eq (Get-ChildItem -LiteralPath $lumoswitchRoot -Force | Select-Object -First 1)) { Remove-Item -LiteralPath $lumoswitchRoot -Force }
  }
  $lumoswitchBin = Split-Path -Parent $lumoswitchLauncher
  if (Test-Path -LiteralPath $lumoswitchBin -PathType Container) {
    foreach ($lumoswitchBackup in @(Get-ChildItem -LiteralPath $lumoswitchBin -Filter 'lumoswitch-deepseek-harness.ps1.backup.*' -File -Force)) { Remove-LumoswitchFile $lumoswitchBackup.FullName }
  }
  Remove-LumoswitchFile $lumoswitchLauncher
  Write-Host 'Lumoswitch native setup and optional launcher removed. Open a new terminal to refresh the environment.'
  exit 0
}

$lumoswitchEnvironment = [ordered]@{}
$lumoswitchEnvironment['DSH_HOME'] = (Join-Path $lumoswitchRoot 'dsh-home')
foreach ($lumoswitchEntry in $lumoswitchEnvironment.GetEnumerator()) {
  [Environment]::SetEnvironmentVariable($lumoswitchEntry.Key, $lumoswitchEntry.Value, 'Process')
}
$lumoswitchExecutable = Get-Command 'npx.cmd' -CommandType Application -ErrorAction SilentlyContinue
if ($null -eq $lumoswitchExecutable) { $lumoswitchExecutable = Get-Command 'npx' -CommandType Application -ErrorAction Stop }
$lumoswitchArguments = @('@deepseek-ai/dsh', 'web')
& $lumoswitchExecutable.Path @lumoswitchArguments @AgentArgs
if ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE }
'@
  Install-LumoswitchFile $lumoswitchLauncher $lumoswitchLauncherContent
  Write-Host 'Native Lumoswitch setup saved. Future command: npx @deepseek-ai/dsh web'
  Write-Host 'Optional maintenance launcher:' $lumoswitchLauncher
  $lumoswitchExecutable = Get-Command 'npx.cmd' -CommandType Application -ErrorAction SilentlyContinue
  if ($null -eq $lumoswitchExecutable) { $lumoswitchExecutable = Get-Command 'npx' -CommandType Application -ErrorAction Stop }
  $lumoswitchArguments = @('@deepseek-ai/dsh', 'web')
  & $lumoswitchExecutable.Path @lumoswitchArguments
  if ($null -ne $LASTEXITCODE -and $LASTEXITCODE -ne 0) { throw 'npx exited with code ' + $LASTEXITCODE }
}

The import writes the Agent-supported persistent configuration and starts npx @deepseek-ai/dsh web directly. The saved Lumoswitch maintenance script is not required to start the Agent; run it with --lumoswitch-clean when you need to restore or remove this setup.

Remove the Lumoswitch setup

Windows PowerShell

Exit every Lumoswitch session for this Agent, then run the command below. The optional maintenance launcher removes the managed native configuration, restores any preserved shared file, clears Lumoswitch user environment values, and removes itself.

& {
  $lumoswitchLauncher = Join-Path $env:LOCALAPPDATA 'Lumoswitch\bin\lumoswitch-deepseek-harness.ps1'
  if (-not (Test-Path -LiteralPath $lumoswitchLauncher -PathType Leaf)) {
    throw 'The optional Lumoswitch maintenance launcher is missing. Run persistent import again before cleanup.'
  }
  & powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File $lumoswitchLauncher --lumoswitch-clean
}

macOS / Linux

The command below removes only the Agent-supported persistent configuration and optional Lumoswitch launcher, overlay, and their backups. It does not delete Harness profiles, settings, workspaces, or sessions:

"$HOME/.local/bin/lumoswitch-deepseek-harness" --lumoswitch-clean

Rotate that Access Key in Lumoswitch afterward.

Verify and troubleshoot

  • The model picker should show the Lumoswitch provider and every downstream model in the API configuration.
  • A default already saved in $DSH_HOME/settings.yaml takes precedence over the overlay's initial default. Select a Lumoswitch model in the Web UI in that case.
  • A 401 response usually means the Access Key is stale or contains extra whitespace.
  • A 404 response usually means /v1 is missing or duplicated in the Base URL.
  • If the model list is stale, copy and run the persistent command again to regenerate the dedicated overlay; the shared Harness settings do not need editing.
  • DeepSeek Harness remains in Developer Preview. If an upgrade reports a configuration-schema error, compare the command with the current provider and CLI references.

References: DeepSeek Harness repository, model-provider configuration, and dsh CLI behavior.

Maintainer publication fields
  • Platform ID: deepseek-harness
  • Display name: DeepSeek Harness
  • Downstream protocol: openai-compatible
  • commandTemplate: copy the complete One-time launch code block
  • Persistent strategy: native
  • persistentCommandTemplate: copy the complete setup command under Persistent use
  • futureCommand: npx @deepseek-ai/dsh web
  • Display order: 35
  • Last verified: 2026-08-27

On this page