Mitwirken
Für Entwickler. Wähle im Entwicklerhandbuch zwischen API-Anbindung, App-Veröffentlichung und Projektarbeit.
Diese Seite ist für Menschen, die OpenBot selbst ändern. Sie zeigt dir, wie du deinen Computer einrichtest, den Server und die Prüfungen startest und dich im Code zurechtfindest.
Einrichten
Section titled “Einrichten”Du brauchst devenv. devenv gibt dir genau die Werkzeuge für
das Projekt: Node.js, pnpm, PostgreSQL und mehr. Du brauchst auch
Docker. Die Datenbank und der
Objektspeicher für die Entwicklung laufen in Docker, und mit Docker baust und
prüfst du die Images. devenv gibt dir die Befehlszeilen-Werkzeuge von Docker.
Dein Computer braucht nur einen laufenden Docker-Dienst. Siehe
Betrieb. Führe jeden Befehl über
devenv shell -- <command> aus. So nutzt du immer diese Werkzeuge.
Den Code holen
Section titled “Den Code holen”git clone <repository-url> openbotcd openbotLokale Einstellungen erstellen
Section titled “Lokale Einstellungen erstellen”devenv shell -- pnpm dev:setupdev:setup erstellt .env.development mit einem neuen, zufälligen geheimen
Schlüssel in OPENBOT_SECRET_KEYS. Wenn deine .env.development aus einer
älteren Version stammt, lösche sie und führe dev:setup noch einmal aus. Du
kannst auch die Entwicklungs-Datenbank zurücksetzen. Siehe
Konfiguration.
Den Entwicklungsserver starten
Section titled “Den Entwicklungsserver starten”devenv shell -- pnpm devpnpm dev führt devenv up aus. Das macht diese Schritte:
- Es startet PostgreSQL 18 auf
127.0.0.1, mit dem Benutzeropenbot_dev. Es läuft in Docker, mit demselben Image wie die Compose-Datei für die Produktion. Seine Daten liegen im Docker-Volumeopenbot-dev-postgres. Um die Datenbank zurückzusetzen, stoppepnpm devund führedocker volume rm openbot-dev-postgresaus. - Es startet RustFS, den Objektspeicher für Dateien, auf
127.0.0.1. Es läuft auch in Docker. Seine Daten liegen im Docker-Volumeopenbot-dev-rustfs. Um die gespeicherten Dateien zu löschen, stoppepnpm devund führedocker volume rm openbot-dev-rustfsaus. - Es installiert die Pakete mit
pnpm install --frozen-lockfile. - Es baut OpenBot und die Pakete, die OpenBot braucht.
- Es führt die Datenbank-Migrationen aus.
- Es startet den Entwicklungsserver unter
http://localhost. Der Server schreibt die Plattform-Einstellungen für diesen Origin selbst. Darum gibt es keinen Einrichtungsschritt.
devenv wählt für jeden Dienst einen freien Port. Es beginnt bei einem
Basis-Port: 5433 für PostgreSQL, 9000 für RustFS und 3000 für OpenBot. Es
zeigt die Ports in seiner Prozessliste. Der Proxy von devenv gibt OpenBot die
feste Adresse http://localhost auf Port 80, egal welchen Port OpenBot bekommt.
Unter Linux fragt der Proxy beim ersten Mal mit sudo nach deinem Passwort,
damit er auf Port 80 lauschen kann.
Wenn du eine Datei in packages/*/src änderst, startet devenv OpenBot neu.
Der Entwicklungsserver nutzt ein lokales Testkonto, Test-Models und eine Test-App. Kein externer Dienst ist nötig. Wie du dich anmeldest und ein User Token erstellst, steht unter Schnellstart.
Prüfungen
Section titled “Prüfungen”| Befehl | Was er tut |
|---|---|
devenv shell -- pnpm install |
Installiert alle Pakete. |
devenv shell -- pnpm build |
Baut jedes Paket mit tsdown. |
devenv shell -- pnpm typecheck |
Prüft die Typen jedes Pakets und die Effect-Regeln. |
devenv shell -- pnpm rules |
Prüft nur die Effect-Regeln von scripts/effect-rules.ts. |
devenv shell -- pnpm -r test |
Führt die Tests jedes Pakets aus. |
devenv shell -- pnpm test |
Führt die Tests aller Pakete in einem Vitest-Lauf aus, ohne Build und Typprüfung. |
devenv shell -- pnpm --filter @openbot-build/core test |
Führt die Tests eines Pakets aus. |
devenv shell -- treefmt |
Formatiert den Code mit Prettier und nixfmt. |
Führe pnpm build vor pnpm typecheck aus. Pakete importieren einander aus
ihren gebauten Dateien.
Tests nutzen Vitest mit
@effect/vitest. Ein Test, der
einen Effect ausführt, nutzt it.effect. Er gibt dem Test eine Test-Uhr: Die
Zeit läuft nur weiter, wenn der Test sie weiterstellt. Ein Test, der echte Zeit
braucht, zum Beispiel einer mit einem echten Server, nutzt it.live. Tests von
OpenBot starten ihren eigenen kurzlebigen PostgreSQL-Server mit den Werkzeugen
aus devenv. Sie brauchen keine Geheimnisse und keine externen Dienste. GitHub
Actions führt dieselben Befehle build, typecheck und test aus. Siehe
Betrieb.
Aufbau des Repositorys
Section titled “Aufbau des Repositorys”| Pfad | Was er enthält |
|---|---|
packages/ |
Die Pakete des Workspaces. Siehe Pakete. |
packages/docs/ |
Diese Doku-Seite. |
packages/template/ |
Ein Startpunkt für ein neues Paket. Er gehört nicht zum Workspace. |
protocol-test/ |
Der öffentliche Testdienst für das App Protocol. Siehe Protokolltest. |
deploy/ |
Dateien, um OpenBot in Betrieb zu nehmen. Jedes Ziel hat einen eigenen Ordner. Siehe Betrieb. |
deploy/docker/ |
Das Dockerfile, die Bake-Datei, Compose-Dateien, die Vorlage für die Produktivumgebung und die Image-Prüfungen. |
.env.development.example |
Die Vorlage für .env.development. |
patches/ |
Korrekturen für genaue Versionen externer Pakete. |
references/ |
Quellcode anderer Open-Source-Projekte, als Git-Submodule. Nur zum Lesen. |
devenv.nix |
Die Entwicklungswerkzeuge, Einstellungen und Prozesse. |
Der Ordner references/ gehört nicht zum Build. Um diesen Code zu lesen, hole
ihn mit git submodule update --init.
Für ein neues Paket kopierst du packages/template/ nach packages/<name>/.
Ändere dann seinen Namen, seinen Einstiegspunkt und seine Abhängigkeiten.
Die Doku-Seite
Section titled “Die Doku-Seite”Die Doku-Seite ist eine Astro-Seite, gebaut mit Starlight. Der Build erzeugt nur statische Dateien. Das Logo in der Kopfzeile führt zu openbot.build. Starte sie mit:
devenv shell -- pnpm --filter @openbot-build/docs devÖffne dann http://localhost:4321. Das englische Handbuch beginnt unter /,
das chinesische unter /zh-cn/ und das deutsche unter /de/.
Pfad in packages/docs/ |
Was es ist |
|---|---|
content/docs/*.mdx |
Die Seiten. Der Dateiname ist die URL: deploy.mdx ist /deploy. |
content/docs/<locale>/ |
Die Übersetzungen: zh-cn für vereinfachtes Chinesisch und de für Deutsch. |
astro.config.ts |
Die Einstellungen der Seite und die Reihenfolge der Seiten in der Seitenleiste. |
src/components/ |
Die Komponenten, die Seiten ohne Import nutzen können: Callout, Steps und mehr. |
src/i18n.ts |
Die drei Sprachen der Doku. |
src/pages/openapi.json.ts |
Liefert openapi.json unter /openapi.json aus, damit man es herunterladen kann. |
scripts/api.ts |
Erzeugt openapi.json aus dem API-Vertrag. |
Die API-Referenz unter /api entsteht aus openapi.json, mit dem Plugin
starlight-openapi. Schreibe sie
nicht von Hand. Das Skript läuft vor jedem dev und build. Wenn du den
API-Vertrag in @openbot-build/api änderst, ändert sich auch die Referenz. Wenn
du dem Vertrag eine Gruppe hinzufügst, gib ihr in scripts/api.ts einen Titel
und eine Beschreibung. Bis du das tust, bricht das Skript mit einem Fehler ab.
Der Build erzeugt auch /llms.txt und /llms-full.txt. Sie enthalten die Doku
als reinen Text für KI-Werkzeuge. Um zu prüfen, dass jede Seite gebaut wird,
führe devenv shell -- pnpm --filter @openbot-build/docs build aus.
Jeder Push auf main veröffentlicht die Seite auf
docs.openbot.build. Siehe
Die Doku-Seite veröffentlichen.
Projektregeln
Section titled “Projektregeln”Für einen Leserkreis schreiben
Section titled “Für einen Leserkreis schreiben”Das Benutzerhandbuch erklärt Arbeit mit einem vorhandenen Client oder Channel. Nenne Aufgabe, Ergebnis und Hilfe. Installation, API-Felder und Quellcode gehören nicht auf Benutzerseiten.
Das Administrationshandbuch erklärt Einrichtung und Betrieb. Nenne die nötigen Organisationsrechte, Plattformrechte oder den Serverzugriff. Client-Verträge, App-Veröffentlichung und Quellcodeänderungen gehören ins Entwicklerhandbuch. Verlinke eine andere Rolle, wenn die Aufgabe an sie übergeht.
Halte Code und Felddetails an einer Stelle. Übersetzungen behalten die gleichen Abschnitte und verlinken ihre eigene Sprache. Die erzeugte API-Referenz bleibt auf Englisch.
Basic English
Section titled “Basic English”Schreibe allen Text in Basic English (Grundenglisch). Das gilt für Doku, Code-Kommentare, Fehlermeldungen, Logs, Prompts und Test-Texte. Nutze, wo es geht, die 850 Wörter von Basic English und dazu die Fachwörter, die du brauchst. Nutze kurze Sätze. Erkläre jeden Fachbegriff, wenn du ihn zum ersten Mal nutzt.
Englisch ist die Quelle für allen Text. Die Doku gibt es auch auf vereinfachtem
Chinesisch und auf Deutsch. Wenn du eine englische Seite änderst, ändere ihre
Übersetzungen in derselben Änderung. Eine Übersetzung behält dieselben
Überschriften in derselben Reihenfolge. Ihre Links führen zu Seiten in ihrer
eigenen Sprache, zum Beispiel /de/deploy. Die API-Referenz gibt es nur auf
Englisch.
Headless
Section titled “Headless”OpenBot hat keine Benutzeroberfläche. Füge dem Repository keine Seiten,
Frontends oder Client-Apps hinzu. Die Doku-Seite ist die einzige Ausnahme. Jede
neue Funktion beginnt als Operation im Vertrag @openbot-build/api. Danach
behandelt der Server sie. Jede Operation muss mit einem User Token und mit dem
Anmelde-Cookie funktionieren. Sie muss auch sagen, welche Aktion auf welcher
Ressource sie braucht, mit einer Annotation Required oder GroupRequired. Die
Helfer own, organization, channel und channels in
packages/api/src/Authorization.ts erstellen die Annotationen. Eine Anforderung
darf nur einen Pfadparameter nennen, den ihr Endpunkt hat. Ein Test in
packages/api prüft das. Siehe Berechtigungen.
Der Code für die Autorisierung kommt aus der Bibliothek
ability, dem npm-Paket @nmnmcc/ability.
Version 3.1.0 steht fest in package.json. HTTP und Speicherung nutzen
JSON-Text für Policies. Diese Version exportiert die Fehlerklassen direkt in den
Typdeklarationen. So kann TypeScript die Deklarationen von OpenBot ohne lokalen
Patch erzeugen.
Änderungen an Aufrufern und Daten
Section titled “Änderungen an Aufrufern und Daten”Frage zuerst den Nutzer, wenn eine Änderung Arbeit für alte Aufrufer, Clients oder gespeicherte Daten braucht. Wähle nicht selbst zwischen einem Bruch und dem Behalten des alten Verhaltens.
Ändere nie die Basismigration. Erstelle für jede Änderung am Datenbankschema
eine neue Migration mit devenv shell -- drizzle-kit generate --name <change>.
Bestehende Datenbanken müssen aktualisiert werden können.
Erkläre eine Umgehung für einen Fehler in einem externen Paket oder Werkzeug mit einem Kommentar direkt am Code.
Eine UUID ist eine ID mit 16 Bytes. Nutze UUID v7, wenn die Zeitfolge
gespeicherten Einträgen oder Indizes hilft. Erzeuge sie mit
Crypto.randomUUIDv7() aus Effect. Nutze das Format bytes, wenn der nächste
Schritt Bytes braucht. Behalte UUID v4 für zufällige Geheimnisse und für kurze
Zeit genutzte IDs, denen eine Zeitfolge nicht hilft.
UUID v7 beginnt mit der Zeit in Millisekunden. IDs aus derselben Millisekunde haben eine zufällige Reihenfolge. Behalte Zeitfelder und Folgenummern, wenn die genaue Reihenfolge zählt. Siehe RFC 9562.
Speichere UUIDs als Bytes, wenn die Daten es erlauben. Nutze in PostgreSQL den
eigenen
Typ uuid. Er
speichert 16 Bytes und nimmt v4 und v7 an. APIs können weiter UUID-Text nutzen.
Wandle gemischte IDs, externe IDs oder IDs mit Präfix nicht direkt in uuid um.
Prüfe vorher alte Werte, Verweise und die Nutzung in verschlüsselten Daten.
Erstelle eine neue Migration und teste sie mit alten v4-Werten.
Zuerst Effect-Daten
Section titled “Zuerst Effect-Daten”Nutze die unveränderlichen Datentypen von Effect, zum Beispiel HashMap,
Chunk und Option. Nutze die veränderlichen Typen von JavaScript nur, wenn du
sie wirklich brauchst.
Typen zuerst
Section titled “Typen zuerst”Schreibe die Typen vor dem Code, der die Arbeit ausführt. Der Vertrag eines Dienstes nennt Eingaben, Ausgaben, Fehler und Abhängigkeiten.
- Definiere Werte der Domäne und externe Daten mit Schema. Teile den Typ eines
Schemas mit
typeof Name.Type. Kopiere seine Felder nicht in einen anderen Typ. - Gib jedem
Context.Serviceein festes Interface oder einen Objekttyp vormake. Leite den Diensttyp nicht mitEffect.Success<typeof make>oderReturnType<typeof make>aus der Implementierung ab. - Deklariere Methoden als
Effect.Effect<Value, Error, Needs>oder typisierten Stream. Nutze benannte Fehler. Behalte generische Fehler alsE, nicht alsanyoderunknown. Hole Abhängigkeiten beim Aufbau des Dienstes, außer dem Kontext, den der Aufrufer liefert. - Prüfe die Implementierung mit
Service.ofund baue dann den Layer. Lokale Hilfsfunktionen dürfen Typen ableiten. Gemeinsame Typen dürfen andere feste Verträge nutzen, aber kein abgeleitetes Ergebnis eines Konstruktors.
Die Prüfung service-contract folgt auch importierten Typ-Aliasen. Sie stoppt
Dienste, deren Typ aus make kommt. Der Compiler lehnt auch any oder
unknown in Fehlern und Abhängigkeiten, unsichere Effect-Typumwandlungen,
fehlende Dienste und das Bereitstellen von Diensten außerhalb erlaubter Grenzen
ab. Tests dürfen ihre eigenen Layer bereitstellen.
Effect-Regeln
Section titled “Effect-Regeln”Diese Prüfung prüft auch Modulgrenzen und Typ-Imports. domain darf core
nutzen, aber keine Adapter für HTTP, SQL oder Workflows. core und protocol
bleiben frei von Produktpaketen. Ein Modul in openbot darf den Ordner app
für den Aufbau nicht importieren. Pakete nutzen einander über öffentliche
Eingänge.
Geschäftscode und Speicher dürfen keine HTTP-Verträge importieren. Das gilt auch für PostgreSQL-Adapter, Einstellungen, Modelle, Konten, Rollen und SSO. Diese Module nutzen Werte und Fehler aus der Domäne. Die API nutzt dieselben Definitionen und fügt HTTP-Statuscodes hinzu. Nur HTTP-Handler, die Abbildung von Antworten und der Aufbau der Anwendung dürfen HTTP-Verträge importieren.
Geschäftscode darf keine HTTP-Antwortabbildungen importieren. Gemeinsame Fehlerabbildungen nutzen Domänenfehler. HTTP-Handler fügen die Antwortform hinzu. App-Adapter fügen Protokolldaten an ihrer Grenze hinzu.
Server-Arbeit nutzt die gemeinsame Ausführungs-Engine. Die Prüfung verbietet
lokales ChatRuntime sowie Operations.make und Operations.layer im Server.
Nur die Ausführungs-Engine, der Ressourcentransport und der Aufbau der Anwendung
dürfen Effect-Cluster- oder RPC-Module importieren. Domänencode darf keine
Ausführungs- oder Speicheradapter aus core nutzen. Die Prüfung erkennt auch
Import-Aliase und Unterpfade von Paketen.
OpenBot ist mit Effect geschrieben. Zwei Prüfungen
halten den Code im Stil von Effect. pnpm typecheck führt beide aus, und GitHub
Actions führt pnpm typecheck bei jeder Änderung aus.
Die erste Prüfung ist der Effect Language Service. Er ist ein Plugin des
TypeScript-Compilers und in tsconfig.base.json eingerichtet. Seine Regeln sind
Fehler, also hält tsc bei ihnen an. Unter anderem verlangen sie:
- die Module
Array,StringundRecordvon Effect statt der nativen Methoden, importiert alsArr,StrundRec; ClockundDateTimefür Zeit, die DiensteRandomundCryptofür Zufallswerte und IDs undConfigfür Umgebungsvariablen;HttpClientstattfetchund die DiensteFileSystem,PathundChildProcessstatt der Node.js-Module;- Effect Schema für JSON und für Daten von außen, dekodiert über Effect und
nicht mit den
*Sync-Funktionen; Schema.TaggedErrorfür Fehler und eine Brand an jeder Schema-Klasse;Effect.genundEffect.fnstattasync-Funktionen undnew Promise;it.effect,it.liveundit.layeraus@effect/vitestin Tests.
Drei Regeln sind aus. apiStabilityLeak und unstableApiUsage sind aus, weil
der API-Vertrag auf effect/http-api aufbaut, das Effect als instabil markiert.
missingPipeableSignature ist aus, weil eine pipe-fähige Form jeder
exportierten Funktion einer Bibliothek hilft, nicht einer Anwendung.
Die zweite Prüfung ist scripts/effect-rules.ts. Sie liest jede
TypeScript-Datei und prüft die Regeln, die der Language Service nicht sieht:
| Regel | Was sie verhindert |
|---|---|
service-contract |
Ein aus dem Konstruktor abgeleiteter Diensttyp, auch über importierte Aliase. Schreibe zuerst den Vertrag. |
blind-error-map |
Effect.mapError(() => ...), Effect.orElseFail(() => ...) und catch: () => ..., die die Ursache eines Fehlers verwerfen. Ein Handler, der mit seinem Namen übergeben wird, zählt auch, wenn dieselbe Datei ihn als Funktion ohne Parameter deklariert. |
throw |
throw. Scheitere mit einem typisierten Fehler, nutze Effect.die für eine verletzte Regel des Codes oder assert.fail in einem Test. |
run-outside-main |
Effect.runPromise und die anderen run-Funktionen außerhalb eines Einstiegspunkts (einer *Main.ts-Datei). Das gilt auch für runForkWith; ein Callback von außen nutzt eine FiberSet-Runtime mit Scope. |
fork-daemon |
Effect.forkDetach. Forke eine Fiber in einem Scope oder halte sie in einem FiberSet oder FiberMap. |
promise-then |
Promise-Ketten mit .then. Mach aus dem Promise einen Effect. |
directive-reason |
Eine Regel-Direktive ohne Grund. |
Manche Stellen müssen nativ bleiben, weil eine API von außen es verlangt oder
weil Effect keine API dafür hat. Beispiele sind ein Drizzle-Callback, der ein
Date zurückgeben muss, ein Promise-Callback eines SDK und HMAC-Signaturen.
Dann schalte die Regel für eine Zeile aus und gib den Grund nach -- an:
// @effect-diagnostics-next-line globalDate:off -- Drizzle calls this outside Effect and needs a Date.// @effect-rules-next-line throw -- The SDK reads a thrown error as a failed call.Eine Direktive ist nie ein Weg, Arbeit zu sparen. Tests sind Einstiegspunkte,
daher ist strictEffectProvide in test/-Ordnern aus.
Kleine, echte Änderungen
Section titled “Kleine, echte Änderungen”Baue, was echten Nutzen bringt. Halte jede Änderung fokussiert und teste die Teile, die wirklich kaputtgehen können.
Die vollständigen Regeln für Mitwirkende stehen in AGENTS.md im Hauptordner
des Repositorys.
Lizenz
Section titled “Lizenz”OpenBot nutzt die GNU Affero General Public License, nur Version 3
(AGPL-3.0-only). Der vollständige Text steht in der Datei LICENSE im
Hauptordner des Repositorys. Wenn du eine Änderung einreichst, stimmst du zu,
dass sie dieselbe Lizenz nutzt.
Das Client-Paket nutzt MIT. Dies gilt auch für den darin gebündelten
OpenBot-Code, wie in packages/client/LICENSE angegeben. Andere Pakete behalten
AGPL. Der Client bietet den API-Vertrag unter @openbot-build/client/Api und
Chat-Schemas unter @openbot-build/client/Core. Apps brauchen keine weiteren
OpenBot-Pakete.
Paket-Releases
Section titled “Paket-Releases”CI baut den Code, prüft Typen und führt Tests aus. Erst danach veröffentlicht es npm-Pakete in GitHub Packages. Alle acht OpenBot-Release-Pakete haben die gleiche Version. Der Client behält MIT. Die anderen Pakete nutzen AGPL-3.0-only.
- Ein Push auf
maingibt0.0.0-commit-<voller-Hash>mit dem Tagsha-<voller-Hash>frei.latestbleibt gleich. - Ein Tag wie
v0.1.0gibt0.1.0frei und setztlatest. - Pull Requests bauen und prüfen Pakete, geben sie aber nicht frei.
devenv shell -- pnpm packages:pack legt lokale Archive in .release an. Mit
RELEASE_REF=v0.1.0 prüfst du ein Versions-Release. Interne Pakete nutzen die
gleiche genaue Version. Danach werden die Quelldateien wiederhergestellt; die
Workspace-Lockdatei bleibt gleich. Ein erneuter Upload überspringt gleiche
Inhalte. Andere Inhalte unter der gleichen Version führen zu einem Fehler.
Gib Repositories, die diese Pakete nutzen, in GitHub Packages
Actions-Lesezugriff auf die Pakete. Die Workflows nutzen GITHUB_TOKEN mit
packages: read. Bei getrennten Zugangsdaten nutze das Secret
PACKAGES_READ_TOKEN mit einem classic PAT und read:packages. Die
Veröffentlichung nutzt GITHUB_TOKEN mit packages: write des
Quell-Repositorys. Auch lokale Installationen öffentlicher Pakete brauchen einen
Token. Siehe Client-Setup.