SSD Nodes Learn 🎉 VPS ab $5.50/Monat
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-08-21

Eigenes dsh-Plugin für DeepSeek Harness erstellen

Erstellen Sie ein dsh-Plugin aus einem leeren Ordner: wichtige package.json-Felder, Patch-Datei zum Einbinden, echtes Tool und die zwei nötigen Hooks.

Was ein dsh-Plugin tatsächlich ist

Ein dsh-Plugin ist ein npm-Paket, das eine apply-Funktion exportiert und eine kleine YAML-Datei enthält, die DeepSeek Harness anweist, das Plugin zu laden. Sie müssen kein separates Plugin-SDK erlernen. dsh ist eine Cordis-Anwendung, und „alles ist ein Plugin“ ist wörtlich gemeint: Die Tool-Registry, die Agentenschleife, der Sitzungsspeicher und der Webserver sind alle Einträge im selben Plugin-Baum, dem sich Ihr Paket anschließt.

Cordis ist ein allgemeines Framework zur Komposition von Komponenten. Es wurde unabhängig entwickelt und dient seit Jahren als Grundlage des Chatbot-Frameworks Koishi. Cordis übernimmt das Laden und Entladen und löst die Abhängigkeiten zwischen Plugins auf. Das Framework kennt keine Agenten. Alles, was mit Agenten zu tun hat, kommt aus den darauf aufbauenden Harness-Paketen. Deshalb ist die unten beschriebene Plugin-Struktur so klein. Ein großer Teil der Funktionalität wird geerbt.

Ein Plugin besteht aus zwei Teilen. Der Host-Teil läuft in Node, registriert Tools und Event-Listener und kann eigene Dienste bereitstellen. Der Browser-Teil läuft innerhalb der Web-UI und registriert Schnittstellen-Slots. Das erste Plugin ist fast immer auf den Host beschränkt. Behandeln Sie den Browser-Teil daher als optional, bis Sie ihn benötigen.

Dieser Leitfaden bezieht sich auf @deepseek-ai/dsh Version 0.1.0-rc.7, den npm-Tag latest vom 19. August 2026. dsh befindet sich in der Developer Preview. In der eigenen README wird darauf hingewiesen, dass es zu inkompatiblen Änderungen kommen wird. Alle unten genannten Schlüsselnamen wurden an diesem Datum aus der Upstream-Dokumentation und dem Repository übernommen. Prüfen Sie sie erneut, bevor Sie sich darauf verlassen, da eine Preview-API Felder zwischen Release Candidates umbenennen kann. Wenn das Harness noch nicht läuft, richten Sie es zuerst mit DeepSeek Harness auf einem VPS und dem dsh-API-Key und der Modellkonfiguration ein. Kehren Sie anschließend hierher zurück.

Eine einzelne Testdatei laden, bevor Sie etwas paketieren

Zuerst zu paketieren, ist der langsamste Weg, dies zu lernen. Laden Sie eine einzelne Datei, prüfen Sie, ob die Laufzeit Ihren Code aufruft, und paketieren Sie ihn erst danach.

Erstellen Sie einen Ordner außerhalb des Checkout-Verzeichnisses des Harness und legen Sie eine Datei darin ab.

import type { Context } from '@deepseek-ai/cordis'

export const name = 'hello-plugin'

export function apply(ctx: Context) {
  console.log('[hello-plugin] plugin loaded')
}

export const name sind Metadaten zur Kennzeichnung des Plugins in Diagnosen. apply ist der vollständige Vertrag: Cordis ruft ihn einmal auf und übergibt einen auf Ihr Plugin beschränkten Kontext. Alles, was Sie in diesem Kontext registrieren, wird beim Freigeben des Plugins automatisch entfernt.

Legen Sie daneben cordis.yml an.

- insert:
    - id: hello
      name: '/absolute/path/to/scratch-plugin/hello.ts'

Starten Sie jetzt ein Profil, über dem diese Datei eingebunden wird.

dsh web --patch ./scratch-plugin/cordis.yml

Wenn dsh nicht in Ihrem PATH enthalten ist, erledigt npx @deepseek-ai/dsh web --patch ./scratch-plugin/cordis.yml dieselbe Aufgabe. Über diesen npx-Weg kann jedoch ein älterer Release Candidate aus dem Cache verwendet werden und nicht die Version, die in dieser Anleitung beschrieben wird. Wenn das Harness ein dokumentiertes Flag direkt ablehnt, arbeiten Sie zunächst die Fehlerbehebungen für Installations- und Versionsfehler von dsh durch, bevor Sie an Ihrer eigenen Datei zweifeln. Im Terminal, in dem dsh gestartet wurde, sollte [hello-plugin] plugin loaded erscheinen. Wenn nichts angezeigt wird, wurde die Zeile nicht aufgelöst.

Das Feld name akzeptiert einen npm-Paketnamen oder einen Dateisystempfad. In der Dokumentation des Upstream-Projekts steht, dass der Pfad absolut sein muss. Ein relativer Wert für ./hello.ts ist die erste Ursache, die Sie prüfen sollten, wenn ein Testplugin keine Ausgabe erzeugt. Die zweite Ursache ist die Dateierweiterung. Der dokumentierte Ablauf wird als pnpm dsh web --patch ... aus einem Clone des Harness-Repositorys ausgeführt. Dort werden TypeScript-Einträge über tsx geladen. Wenn Sie dsh über npm installiert haben, verweisen Sie in der Zeile auf reines JavaScript oder erstellen Sie die Datei zuerst.

--patch ist ein Launcher-Flag. Seine Overlay-Konfiguration wird zuletzt angewendet, nach jedem Bundle und nach Ihrem eigenen Profil-Patch. Eine Test-Overlay-Konfiguration hat daher immer Vorrang. Genau das benötigen Sie während der Entwicklung.

Das kleinste nützliche Tool schreiben

Eine Logzeile belegt, dass das Plugin geladen wird. Ein Tool belegt, dass das Plugin Bestandteil des Agents ist.

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'greet-tool'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'greet',
    description: 'Greet someone by name.',
    parameters: {
      name: { type: 'string', required: true, description: 'The name to greet' },
    },
    output: {
      schema: { type: 'string' },
      render: (_args, value) => [{ type: 'text', text: value }],
    },
    async execute(args) {
      return `Hello, ${args.name}!`
    },
  }))
}

export const inject = ['tools'] ist die Zeile, die häufig fehlt. Einträge in einer Cordis-Konfiguration werden gleichzeitig gestartet. Die Position einer Zeile in der Datei sagt daher nichts über die Ladereihenfolge aus. Die Reihenfolge ergibt sich aus den deklarierten Abhängigkeiten. inject weist Cordis an, zu warten, bis ctx.tools vorhanden ist, bevor es apply aufruft. Ohne diese Angabe kann Ihr Code zu einem Zeitpunkt ausgeführt werden, zu dem die Registry noch nicht vorhanden ist und keine Registrierung möglich ist.

Der restliche Teil des Objekts definiert den Vertrag, den das Modell sieht. parameters ist das Argument-Schema, und execute erhält Argumente, die bereits anhand dieses Schemas geparst wurden. output.schema beschreibt den Wert, den execute zurückgibt, während render diesen Wert in die Inhaltsblöcke umwandelt, die das Modell liest. Die Trennung dieser beiden Aspekte ermöglicht es der Schnittstelle, etwas anderes anzuzeigen, als das Modell liest.

Starten Sie das Profil und bitten Sie den Assistenten, jemanden mit Namen zu begrüßen. Die Antwort wird über Ihr execute zurückgegeben. Die Registrierung über ctx ist reversibel. Beim Freigeben des Plugins wird das Tool daher automatisch ausgetragen. Für Ressourcen, die Cordis nicht kennen kann, etwa einen Socket oder einen Dateideskriptor, rufen Sie ctx.effect() auf und übergeben Sie ihm eine Freigabefunktion.

Die beiden Erweiterungspunkte, die ein erstes Plugin tatsächlich berührt

Die vollständige Liste der Schnittstellen ist lang. Zwei davon decken fast jedes erste Plugin ab.

Conversation-Events sind der dauerhafte, protokollierte Datenstrom. Die Namen sind session/event, turn/start, turn/end, step/start, step/end, user/message, assistant/message, assistant/chunk, tool/call und tool/result. Sie binden einen gewöhnlichen Listener ein.

ctx.on('tool/call', (payload) => {
  console.log('[my-plugin] tool/call', JSON.stringify(payload))
})

Geben Sie die Payload einmal aus und lesen Sie sie. Übernehmen Sie Feldnamen der Payload nicht aus irgendeiner Anleitung, auch nicht aus dieser, weil sich die Struktur der Payload bei einer Preview-API am häufigsten ändert.

Der zweite Erweiterungspunkt ist die Waterfall-Kette. agent/pre-step, agent/request, agent/request-error, llm/stream und die tools/*-Events sind Waterfalls, und ein Waterfall-Listener hat eine andere Signatur. Er erhält einen next-Callback. Die Kette wird nur fortgesetzt, wenn er diesen aufruft.

ctx.on('agent/request', async (payload, next) => {
  const startedAt = Date.now()
  const downstream = await next()
  console.log('[my-plugin] model request took', Date.now() - startedAt, 'ms')
  return downstream
})

Wenn Sie await next() vergessen, haben Sie keinen Hook hinzugefügt. Sie haben den Modellaufruf durch nichts ersetzt, und der Agent stoppt an dieser Stelle, weil das vorzeitige Beenden das vorgesehene Verhalten eines Gateway-Plugins ist, das eine Anfrage absichtlich ablehnt. Dieser eine Unterschied verursacht die meisten Probleme beim ersten Plugin. Schreiben Sie den Aufruf von next(), bevor Sie irgendetwas darum herum schreiben.

agent/request umschließt den Modellaufruf selbst. Seine Payload enthält den Agenten, der den Aufruf ausführt, die Nummer des geöffneten Turns, den Schritt, zu dem die Anfrage gehört, und das Abbruchsignal dieses Turns. Dadurch ist dies die richtige Schnittstelle für einen Request-Logger oder einen Rate-Limiter. Die tools/*-Waterfalls haben eine Ebene darunter dieselbe Struktur. tools/pre-execute erlaubt, verweigert oder fordert vor dem Dispatch eine Genehmigung an. tools/execute umschließt den Dispatch. tools/post-execute kann das normalisierte Ergebnis ersetzen oder blockieren. tools/result beobachtet nur das unveränderliche Ergebnis.

Als Bundle für die Installation durch andere bereitstellen

Ein Bundle ist ein npm-Paket, dessen package.json ein Feld dsh.bundle enthält, das auf seine Patch-Datei verweist. Diese Deklaration ist der einzige Unterschied zwischen einer temporären Datei und etwas, das installiert werden kann.

{
  "name": "dsh-plugin-hello",
  "version": "0.1.0",
  "type": "module",
  "main": "lib/index.js",
  "files": ["lib", "cordis.patch.yml", "README.md", "LICENSE"],
  "engines": { "node": "^22.19 || >=24", "dsh": ">=0.1.0-rc.6" },
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } },
  "keywords": ["dsh-plugin", "deepseek-harness"],
  "scripts": { "build": "tsdown", "prepare": "pnpm run build" },
  "exports": {
    ".": { "types": "./lib/index.d.ts", "default": "./lib/index.js" },
    "./cordis.patch.yml": "./cordis.patch.yml",
    "./package.json": "./package.json"
  }
}

Das danebenstehende cordis.patch.yml ist kurz.

- insert:
    - id: dsh-plugin-hello
      name: dsh-plugin-hello

Die Zeile name enthält den Paketnamen. Daher müssen diese beiden Zeichenfolgen übereinstimmen. Die Zeile id ist das Ziel, auf das eine spätere Ebene verweist, wenn ein Benutzer Ihre Konfiguration überschreibt. Wählen Sie daher einen stabilen Wert und verwenden Sie ihn nie für ein anderes Plugin.

files muss cordis.patch.yml auflisten. Wenn dieser Eintrag fehlt, enthält das veröffentlichte Tarball einen dsh.bundle.patch, der auf eine Datei verweist, die nie in das Paket aufgenommen wurde. Das Paket wird dann zwar installiert, trägt aber nichts zum Baum bei.

Installieren Sie das Paket aus dem Verzeichnis, das Ihren Plugin-Ordner enthält, in ein Profil.

dsh plugin --profile demo add ./dsh-plugin-hello
dsh --profile demo --dump-config
dsh --profile demo

dsh plugin --profile <name> leitet die übrigen Argumente innerhalb des Profilverzeichnisses an pnpm weiter. Daher verhalten sich add und remove wie bei pnpm. Deinstallieren Sie das Paket mit dsh plugin --profile demo remove dsh-plugin-hello. Die Profile web und headless werden bei der ersten Verwendung automatisch aus den mitgelieferten Vorlagen erstellt. Jeder andere Profilname muss über dsh plugin erstellt werden.

Warum Ihre Zeile im zusammengesetzten Baum fehlt

Die Zusammensetzung beginnt mit einer leeren Eintragsliste und fügt die Layer in einer festen Reihenfolge hinzu. Zuerst kommt jedes im Profil unter dsh.profile.bundles aufgeführte Bundle, und zwar in der angegebenen Reihenfolge. Danach folgt das eigene cordis.patch.yml des Profils. Anschließend kommt $DSH_HOME/cordis.patch.yml. Danach wird ein gegebenenfalls über die Befehlszeile angegebenes --patch-Overlay hinzugefügt. Spätere Layer ersetzen frühere Zeilen anhand ihrer ID.

Profile liegen unter $DSH_HOME/profiles/<name>. Ein Profilverzeichnis enthält ein package.json mit dem dsh.profile-Manifest und seiner geordneten bundles-Liste sowie die eigene Patch-Datei des Benutzers. Bundle-Namen werden zuerst aus der dsh-Installation und anschließend aus dem node_modules des Profils aufgelöst. Dort legt pnpm ein Plugin außerhalb des Verzeichnisbaums ab.

dsh --profile demo --dump-config gibt den vollständig zusammengesetzten Baum aus, ohne etwas zu starten. Diese Ausgabe bildet die Trennlinie bei der Fehlersuche. Fehlt Ihre Zeilen-ID, liegt das Problem in der Zusammensetzung: Ein Name lässt sich nicht auflösen oder eine Patch-Datei wurde nie gepackt. Ist die Zeile vorhanden und passiert trotzdem nichts, liegt das Problem in Ihrem Code. Beantworten Sie zuerst diese Frage. Dadurch entfällt der größte Teil des Rätselns.

Wo Ladefehler tatsächlich sichtbar werden

Ein Fehler innerhalb von apply ist deutlich sichtbar. Der Prozess beendet sich mit dieser Ausnahme, und Sie erhalten einen Stacktrace, der auf Ihre eigene Zeile verweist.

Fehler bei der Auflösung bleiben dagegen unauffällig. Der Loader meldet ein Modul, das er nicht auflösen kann, über den Cordis-Logger, statt den Prozess zu beenden. Im vorgelagerten Tutorial wird darauf hingewiesen, dass diese Meldungen beim Start verloren gehen können, weil sie ausgegeben werden, bevor Konsolen-Exporter eingebunden sind. Ein Tippfehler im Pfad sieht daher genau wie ein Plugin aus, das geladen wurde, aber nichts tut. Deshalb lohnt es sich, die Prüfung --dump-config oben auszuführen, bevor Sie den Code untersuchen.

Fügen Sie während der Entwicklung einen console.log als erste Anweisung in apply ein. Wenn er fehlt, erkennen Sie, welche Hälfte des Problems vorliegt. Später können Sie ihn ohne weitere Auswirkungen löschen. Führen Sie das Harness auf einem Server während der Entwicklung im Vordergrund aus, statt es unter einem Service-Manager zu starten. So gelangt die Ausgabe des Loaders direkt in Ihr Terminal und nicht in ein Journal, das Sie erst aufrufen und lesen müssen.

Iterieren, ohne alles neu zu starten

Die ehrliche Antwort für den Host-Teil lautet heute: Sie starten den Prozess neu. Das Webanwendungs-Bundle wird mit deaktivierter gemeinsamer Hot-Module-Reload-Funktion ausgeliefert. In der Datei steht ein Hinweis, dass sie wieder aktiviert wird, sobald der Reload-Lebenszyklus getestet wurde. Die Reload-Kette der Client-Seite ist immer eingebunden, bleibt aber inaktiv, bis ein Build-Watcher die Client-Bundles neu schreibt. Daher bewirkt sie auch für Ihren Node-Teil nichts.

Machen Sie den Neustart schnell, statt einem Reload nachzujagen, den es noch nicht gibt. Halten Sie das Plugin in einer Datei. Laden Sie es mit --patch, statt es in einem Profil zu installieren. Dadurch liegen weder ein Build-Schritt noch ein pnpm-Schritt zwischen einer Änderung und dem Start. Registrieren Sie alles über ctx, damit ein Neustart kein doppeltes Tool und keinen veralteten Listener zurücklässt. Verpacken Sie alles, was Sie selbst allokieren, in ctx.effect() mit einem echten Disposer. Das typische Symptom eines fehlenden Disposers ist, dass der zweite Start an einem Port fehlschlägt, den der erste Start noch belegt.

Wenn Sie gegen einen Harness entwickeln, der auf einem Server und nicht auf Ihrem Laptop läuft, ändert sich daran nichts. Die Bindung der Web-UI ist jedoch relevant. Die Loopback-Bindung an Port 3080 erklärt, warum die Seite nicht automatisch geöffnet wird und was Sie dagegen tun können.

Die Browser-Seite und wie viel Vertrauen angemessen ist

Fügen Sie dies nur hinzu, wenn Ihr Plugin eine eigene Benutzeroberfläche benötigt. Sie wird im selben dsh-Feld wie das Bundle deklariert.

{
  "dsh": {
    "client": {
      "platform": "web",
      "inject": [],
      "external": [],
      "immediately": false
    }
  },
  "exports": {
    ".": "./src/index.ts",
    "./client": "./src/client/apply.ts",
    "./package.json": "./package.json"
  }
}

"platform": "web" ist erforderlich. Der Scanner bricht ab, wenn das Paket keinen ./client-Export enthält. Deshalb ist die Export-Zuordnung Bestandteil des Manifests und keine optionale Vereinfachung. Der Client-Einstiegspunkt erhält den Cordis-Context, erweitert um den Typ der Client-Laufzeit. Jede Registrierung erfolgt innerhalb von apply über ctx.slots.register. Seiteneffekte auf Modulebene sind dort nicht zulässig.

import type { Context } from 'cordis'
import type { DshClientContext } from '@deepseek-ai/dsh-client-runtime'

export async function apply(ctx: Context & DshClientContext) {
  ctx.slots.register({ name: 'domain.entry.slot' }, MyComponent)
}

Zwei Details sollten Sie vor Beginn kennen. inject im Client-Manifest dient der Dokumentation und nicht der Ablaufsteuerung. Es erfasst Abhängigkeiten auf Paketebene und legt nicht die Aktivierungsreihenfolge fest. Unter external deklarieren Sie Modulanforderungen außerhalb der Basiskonfiguration. Diese werden materialisiert, bevor Ihr Plugin sie anfordert. Dieser Bereich der Vorschau ändert sich am schnellsten. Lesen Sie deshalb am Tag der Implementierung packages/client/AGENTS.md im Repository der Testumgebung und nicht an dem Tag, an dem Sie eine Anleitung dazu lesen.

Veröffentlichen Sie das Plugin und geben Sie an, worauf es zugreift

Wenn Sie das Thema dsh-plugin zu einem GitHub-Repository hinzufügen, erscheint es in der Liste, die Nutzer bei der Suche nach Plugins durchsuchen. Damit beanspruchen Sie das Vertrauen anderer und übernehmen entsprechende Pflichten. Diese Pflichten entsprechen genau den Prüfpunkten aus unserem Leitfaden zur Prüfung eines dsh-Plugins vor der Installation. Deshalb lässt sich das Plugin am einfachsten veröffentlichen, wenn Sie diese Checkliste direkt abarbeiten.

  • Fixieren Sie Ihre Abhängigkeiten. Ein Caret-Bereich bei einer transitiven Abhängigkeit kann dazu führen, dass ein Paket, das letzte Woche noch sicher war, diese Woche anderen Code ausführt. Genau auf diesem Mechanismus beruhen npm-Lieferkettenangriffe auf einen Server.
  • Geben Sie im Manifest an, worauf Sie zugreifen. Ihre inject-Liste ist eine ehrliche, maschinenlesbare Zusammenfassung der Harness-Dienste, die Sie verwenden. Ein Reviewer kann sie in wenigen Sekunden prüfen und sich daraus ein Urteil bilden.
  • Keine stillen Netzwerkaufrufe. Wenn ein Tool eine API aufruft, nennen Sie den Host in der README und machen Sie den Endpunkt konfigurierbar. Ein Plugin, das einen nicht erwähnten Server kontaktiert, wird von den zuständigen Prüfern aus der Liste entfernt.
  • Halten Sie files knapp. Wenn Sie einen vollständigen Arbeitsordner veröffentlichen, kann versehentlich eine Datei mit Zugangsdaten in die Registry gelangen.
  • Stellen Sie Git-Installern ein prepare-Skript bereit, das ohne Annahmen über reine Entwicklungsabhängigkeiten erstellt werden kann. Geben Sie in der README an, dass sie diesen Build in der pnpm-workspace.yaml ihres Profils auf die Allowlist setzen müssen.
  • Versehen Sie die README mit dem Datum des Release Candidates, gegen den Sie das Plugin erstellt und getestet haben. Nutzer einer Vorschau-API müssen wissen, welche Version Sie verwendet haben.

Um zu sehen, wie ein fertiges Plugin von außen aussieht, lesen Sie die empfehlenswerten dsh-Plugins und achten Sie darauf, welche Informationen die jeweilige README vor der Installation liefert. Wenn Sie Erweiterungen für einen anderen Agenten geschrieben haben, ist der Aufbau von Claude-Code-Plugins ein nützlicher Vergleich. Der Harness stellt Ihnen einen Live-Objektgraphen und eine reversible Registrierung bereit. Das bietet mehr Möglichkeiten als ein Manifest mit Dateien, bringt aber auch mehr Verantwortung mit sich.

FAQ

Muss ich nach npm veröffentlichen, um ein dsh-Plugin zu schreiben?

Nein. Ein Dateisystempfad in einem cordis.yml-Overlay, der mit dsh web --patch ./scratch-plugin/cordis.yml geladen wird, reicht aus, um eigenen Code innerhalb des Harness auszuführen. Der Pfad muss absolut sein. Eine Paketierung ist nur relevant, wenn jemand anderes das Plugin installiert. Auch dann können Sie mit dsh plugin --profile demo add ./my-plugin einen lokalen Ordner installieren, um die paketierte Form zu testen, ohne eine Registry zu verwenden.

Warum wird mein Plugin geladen, aber das Tool erscheint nie?

Führen Sie zuerst dsh --profile demo --dump-config aus. Wenn Ihre Zeilen-ID in dieser Ausgabe fehlt, wurde das Plugin nie eingebunden. Die Ursache liegt dann in der Komposition und nicht im Code. Wenn die Zeile vorhanden ist, prüfen Sie export const inject = ['tools']. Einträge in einer Cordis-Konfiguration werden gleichzeitig gestartet. Die Reihenfolge der Dateien bestimmt daher nicht die Ladereihenfolge. Ohne diese Deklaration wartet Cordis nicht auf die Tool-Registry. Ihr apply kann dann zu einem Zeitpunkt ausgeführt werden, an dem ctx.tools für die Registrierung noch nicht verfügbar ist.

Was ist der Unterschied zwischen cordis.yml und cordis.patch.yml?

cordis.yml enthält eine vollständige Liste von Einträgen. cordis.patch.yml ist eine Ebene, die auf eine solche Liste angewendet wird. Sie zielt anhand der IDs auf Zeilen, um neue Zeilen einzufügen oder eine vorhandene Konfiguration zu ersetzen. Ein Bundle verweist über dsh.bundle.patch in package.json auf seine eigene Patch-Datei. Die Ebenen werden in einer festen Reihenfolge angewendet: zuerst jedes Bundle in der im Profil angegebenen Reihenfolge, danach die Patch-Datei des Profils, anschließend $DSH_HOME/cordis.patch.yml und zuletzt jedes --patch-Overlay. Spätere Ebenen haben Vorrang.

Kann ich ein dsh-Plugin während der Laufzeit des Agents per Hot Reload neu laden?

Für den Host-Teil im Webprofil ist das ab 0.1.0-rc.7 nicht möglich. Dieses Bundle liefert die gemeinsame Zeile für Hot Module Reloading deaktiviert aus. Im betreffenden File steht, dass sie zurückkehrt, sobald ihr Reload-Lebenszyklus getestet wurde. Planen Sie stattdessen einen schnellen Neustart ein: Verwenden Sie eine einzelne Datei, die über --patch ohne Build-Schritt geladen wird, und führen Sie jede Registrierung über ctx aus, damit nichts aus einem Lauf in den nächsten übergeht. Verwenden Sie ctx.effect() mit einem Disposer für Ressourcen, die Cordis nicht selbst bereinigen kann.