342 lines
25 KiB
Markdown
342 lines
25 KiB
Markdown
# Werkregie-Dashboard
|
||
|
||
A web app showing what the coding agents and the build pipeline are doing right now
|
||
and what happened recently — read-only, links back to the tools.
|
||
|
||
| Source | Data | Shown |
|
||
|---|---|---|
|
||
| Claude Code (Desktop and terminal) | `~/.config/Claude/claude-code-sessions/**.json`, `~/.claude/sessions/<pid>.json`, the end of `~/.claude/projects/*/<session>.jsonl` | busy / waiting / closed sessions, resume command, sessions waiting for the user |
|
||
| Codex (ChatGPT Desktop, IDE, CLI) | `~/.codex/state_5.sqlite` (read-only), rollout files | threads, running turns, resume command |
|
||
| Werkator, any number of instances | `/api/repos`, `/api/repos/{repo}/builds/history`, `/api/system`, `/api/system/history` | running and finished builds, without failures a newer build of the branch superseded; `@ai-review` builds as werkrevisor; the host's load |
|
||
| werkrevisor's statistics service | `/api/stats` | review costs of 24 hours and 7 days |
|
||
| Gitea, any number of instances | `/api/v1` | open PRs with commit statuses (Werkator, werkrevisor run and verdict), own activity, merges of the configured authors |
|
||
| Matrix, with the user's own account | client-server API through Trixnity (`de.connect2x.trixnity`) | direct messages, mentions in every room, every message of configured rooms |
|
||
|
||
The page follows Werkator's design (`werkregie.css` mirrors `werkator.css`: IBM Plex, petrol accent, light/dark with a toggle).
|
||
It opens on the overview, which answers two questions first: is every source connected, and where must the user act?
|
||
|
||
- The connection bar shows each tool once, as its logo with the worst status of its sources;
|
||
a logo of several sources shows a second dot behind it, with the best status.
|
||
A tap or click on the logo opens a menu of its instances and relays, each with its state and link.
|
||
- The page asks the server every 5 s while it is visible, at once when it becomes visible, returns from the back/forward cache
|
||
or the network comes back, and after a failure again after 1 s, at most 3 times; a request that hangs fails after 10 s.
|
||
On the desktop the Claude logo opens Claude Desktop's sessions waiting for the user (`claude://code/needs-input`),
|
||
and a session of Claude Desktop opens there (`claude://code/continue?session=…`).
|
||
On a phone the logo opens `https://claude.ai/code`, and a session with Remote Control opens its address there,
|
||
which the Claude app takes over; a session without Remote Control has no link there.
|
||
Codex has no such link yet: the ChatGPT app on Linux takes `codex://threads/…` but does not act on it.
|
||
- *Du bist dran* groups what asks something of the user: Absichern, Festgefahren, Nacharbeiten, Freigeben, Mergen, Antworten.
|
||
Absichern holds a vulnerable dependency on main: a failed run of Werkator's nightly build `vulnerability-data`,
|
||
with the vulnerabilities of its OWASP Dependency-Check report.
|
||
Antworten holds the Matrix messages to the user, the Claude Code sessions that wait for the user,
|
||
and the user's own PRs that another human commented.
|
||
- Beside it: the load of each Werkator host, Werkator's builds of 24 hours, werkrevisor's costs,
|
||
and per Gitea the merges into the default branch of 30 days, 12 weeks or 12 months, stacked by author,
|
||
as pull requests or as changed lines; the legend names the mean and the median of the lines per pull request.
|
||
A Werkator panel charts 24 hours of its 3 most critical metrics,
|
||
out of CPU, RAM, each disk, load, swap, CPU wait, memory wait and disk wait.
|
||
Before its verdict it shows the running builds of its slots, e.g. `1/2`, coloured as on Werkator's tile "Builds".
|
||
- Below it: what runs and which sessions wait, as cards; the history of 24 hours as a lane per tool.
|
||
|
||
The tabs *Pull Requests*, *Sessions*, *Aufgaben*, *Nachrichten*, *Verlauf* and *Quellen* hold the full lists, with the filters;
|
||
the address keeps the tab, e.g. `#prs`.
|
||
On a phone the tabs are a bar at the bottom.
|
||
The page speaks German or English (DE/EN in the header); the browser keeps the choice.
|
||
Texts that the sources compose, e.g. Gitea activity titles, stay as the server writes them.
|
||
The logos in `static/logos/` are the tools' own; the black ones turn white in the dark mode.
|
||
|
||
The page installs as an app (PWA): `manifest.webmanifest`, icons and the service worker `sw.js` in `static/`.
|
||
The service worker keeps the page, styles and fonts for a start without network, fetching them anew first whenever it can;
|
||
it never touches `/api/`.
|
||
The page itself keeps the last state of `/api/dashboard` in the browser's `localStorage` (`werkregie-last`):
|
||
when the server does not answer, it shows that state under an amber banner with its age.
|
||
Demo data is never kept, and forgetting the access token deletes the state.
|
||
The PNG icons are rendered from the SVGs, e.g. `inkscape icon-maskable.svg -w 512 -h 512 -o icon-maskable-512.png`.
|
||
|
||
## Modes
|
||
|
||
One program, three ways to run it:
|
||
|
||
```
|
||
┌──────────── Linux machine with coding agents ─────────────┐
|
||
│ Claude Code, Codex ──► werkregie-dashboard relay ──┐ │
|
||
└─────────────────────────────────────────────────────┼─────┘
|
||
HTTPS POST every 5 s, │ outgoing only:
|
||
bearer token ▼ nothing reaches in
|
||
┌──────────── host, e.g. Hostsharing Managed Webspace ──────┐
|
||
browser ─►│ Apache (.htaccess) ─► werkregie-dashboard server ◄── Werkator, Gitea
|
||
(cookie) └───────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
| Mode | Reads | Serves |
|
||
|---|---|---|
|
||
| `local` (default) | Claude Code, Codex, Werkator, Gitea | `127.0.0.1` only, no token |
|
||
| `server` | Werkator, Gitea, and whatever the relays report | real data only with the access token, invented demo data without |
|
||
| `relay` | Claude Code, Codex | nothing: posts its state to `relay.serverUrl` |
|
||
| `watch-pr URL` | one pull request of a Gitea in `giteas` | nothing: prints one line when its coding agent must act, then ends |
|
||
|
||
- **Access token:** the server creates it on its first start in `<stateDir>/access-token` (mode 0600);
|
||
`werkregie-dashboard token` prints it, deleting the file rotates it.
|
||
The browser enters it with the key button and keeps it as the `werkregie_token` cookie;
|
||
relays send it as `Authorization: Bearer`. The relay endpoint does not accept the cookie,
|
||
so no other site can make a logged-in browser post to it.
|
||
- **Silent relays:** after `server.relayStaleSeconds` (30) the relay's chip turns yellow with "zuletzt vor …",
|
||
and the indicator in the header shows e.g. "think still" in amber instead of "live", also on a phone;
|
||
after `server.relayGoneSeconds` (120) its running and open sessions move to the history.
|
||
A server without any relay shows a failing source "Relays" and "kein Relay" in the header.
|
||
- **Retired relays:** a relay that reports no more stays as stale until it is forgotten:
|
||
`werkregie-dashboard forget-relay NAME` on the server host asks the running server (with the token from its state),
|
||
or edits `relays.json` when no server runs. A relay still running simply reports again.
|
||
- **State:** the last report of every relay is kept in `<stateDir>/relays.json`, so a restarted server is filled at once.
|
||
`stateDir` defaults to `${XDG_STATE_HOME:-~/.local/state}/werkregie`.
|
||
- **Journal:** every minute the server, or the local mode, records the work of each day in `<stateDir>/journal/YYYY-MM-DD.json`:
|
||
the active time of each Claude Code session and Codex thread, and the items of all sources, sessions apart.
|
||
A recorded span or item stays when its source forgets it; days older than `journal.keepDays` (400) are deleted.
|
||
The relay reads the active time from the time stamps in the transcripts, `claude.projectsDir` and Codex' rollout files;
|
||
entries apart by at most `journal.gapMinutes` (15) count as one span, a longer pause splits it.
|
||
`journal.zone` (default the machine's zone) sets where a day begins.
|
||
`GET /api/journal/YYYY-MM-DD` returns a day, on a server only with the access token.
|
||
- **Werkjournal proposal:** the tab *Journal* proposes the Werkjournal entries of a picked day, to correct, copy or download
|
||
as `werkjournal-YYYY-MM-DD.json`, Werkjournal's import file of schema version 1.
|
||
`journal.customers` names the Werkjournal customers and their projects, each with its `repositories` (without owner),
|
||
`taiga` project slugs, Matrix `rooms` (ID or name) and `paths` of working directories without a repository;
|
||
work that no project names goes to `journal.fallback`, else it stays a hint.
|
||
There is one entry per Taiga story and day where a branch, a title or a commit message names a Taiga ID
|
||
(`Taiga#US-591`, `Taiga#591`, `Taiga591`, `US-591`, `TG-591`); Taiga resolves it, a task to its story.
|
||
Else there is one entry per project and day.
|
||
The minutes are the union of the sessions' active spans and of 15 min before each action in Gitea, Taiga and Matrix;
|
||
builds, reviews and pull requests are evidence only.
|
||
A minute in which several entries are active counts once, shared equally among them.
|
||
They are rounded half up to whole hours; a story below 30 min joins the entry of its project without story,
|
||
less than 30 min after that is a hint, and a day over 12 h gives from its largest entry.
|
||
Ids and descriptions derive from the day's data, so importing the same file twice adds nothing in Werkjournal.
|
||
With `journal.summary`, a chat model through OpenRouter as `claude.summary` (endpoint, `model`, `apiKey` or `apiKeyEnv`),
|
||
each entry of the file also gets an AI summary of its evidence; the switch "Regeln | KI" in the tab picks the text.
|
||
A summary is kept by the hash of its evidence in `<stateDir>/journal-summaries.json`, 400 days after its last use,
|
||
so the same day gives the same text, after a restart too; while the model fails, the entry keeps the rule text.
|
||
`GET /api/journal/YYYY-MM-DD/proposal` returns the proposal, on a server only with the access token.
|
||
|
||
## Run locally
|
||
|
||
```bash
|
||
./gradlew installDist
|
||
build/install/werkregie-dashboard/bin/werkregie-dashboard # → http://localhost:4747/
|
||
```
|
||
|
||
or `./gradlew run`. To start it with the session, see `packaging/werkregie-dashboard.service`.
|
||
|
||
`./gradlew build` needs Node 18 or newer: `jsTest` tests the JavaScript of the page with `node --test`,
|
||
without npm packages (ADR 0002).
|
||
The tests lie in `src/test/js/`; a tested function moves out of `index.html` into a script of its own, e.g. `static/merges.js`.
|
||
|
||
## Server on a host (Hostsharing Managed Webspace)
|
||
|
||
The same shape as Werkator's: a runtime bundle with its own JRE, a systemd user service on the
|
||
port of the booked "eigener Serverdienst", and the platform's Apache as TLS-terminating proxy via `.htaccess`.
|
||
|
||
1. Build the bundle on a Linux x86_64 machine (the JRE is Temurin 25, built against an old glibc; werkregie ADR 0001):
|
||
```bash
|
||
./gradlew runtimeBundle # → build/distributions/werkregie-dashboard-runtime-linux-x64.tar.gz
|
||
```
|
||
2. On the host, unpack it to `~/opt` and write the configuration, see [dashboard.server.example.yml](dashboard.server.example.yml)
|
||
(`port` is the assigned port, `server.publicBaseUrl` the domain).
|
||
3. Generate and install the unit and the `.htaccess`; the command prints the remaining steps:
|
||
```bash
|
||
~/opt/werkregie-dashboard/bin/werkregie-dashboard init server ~/werkregie-init
|
||
```
|
||
4. `~/opt/werkregie-dashboard/bin/werkregie-dashboard token` prints the access token for the browser and the relays.
|
||
|
||
To update: stop the service, unpack the new bundle over `~/opt/werkregie-dashboard`, start the service.
|
||
|
||
### Deployment by Werkator
|
||
|
||
Werkator's build `deploy` follows each green build of `main`:
|
||
[packaging/werkregie-deploy](packaging/werkregie-deploy) builds the runtime bundle of the tested commit
|
||
and sends it over one ssh connection to [packaging/werkregie-deploy-receive](packaging/werkregie-deploy-receive).
|
||
The receiver checks that the new launcher prints its version, keeps the running runtime as `werkregie-dashboard.prev`, swaps and restarts,
|
||
and waits up to 120 s until `/api/dashboard` answers with the new version; otherwise it puts the previous runtime back.
|
||
When the launcher fails or the new version does not answer, the build fails, and the service keeps running.
|
||
|
||
The repository says what the build does; the host decides when, where and with which key.
|
||
The setup, once, by the human:
|
||
|
||
1. As the user of the server, install the receiver: `~/bin/werkregie-deploy-receive`, mode 0755.
|
||
2. As the user of Werkator, create a key without passphrase for the build
|
||
in the home that the build sees: natively `~/.ssh/`, in the werkdock sandbox `.git/werkator/buildenv/home/.ssh/`.
|
||
Name the server there as the alias that the script connects to,
|
||
and add its host key to `known_hosts`, e.g. by `ssh-keyscan mih09.hostsharing.net`, after comparing the fingerprint:
|
||
```
|
||
Host werkregie-deploy
|
||
HostName mih09.hostsharing.net
|
||
User mih09-werkregie
|
||
IdentityFile ~/.ssh/werkregie-deploy
|
||
IdentitiesOnly yes
|
||
```
|
||
3. Add the key to `~/.ssh/authorized_keys` of the server's user, restricted to the receiver:
|
||
```
|
||
command="/absolute/path/bin/werkregie-deploy-receive",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAA… werkregie-deploy
|
||
```
|
||
4. In `.git/werkator/.werkator.yml` of Werkator's checkout of werkregie, add the trigger; it is pinned there:
|
||
```yaml
|
||
builds:
|
||
deploy:
|
||
trigger:
|
||
afterSuccessOf: default
|
||
branches: ["main"]
|
||
```
|
||
|
||
## Relay on a machine with coding agents
|
||
|
||
Configuration as in [dashboard.relay.example.yml](dashboard.relay.example.yml) — `relay.serverUrl` and the token,
|
||
best in a file of its own (`relay.tokenFile`, mode 0600). Then:
|
||
|
||
```bash
|
||
build/install/werkregie-dashboard/bin/werkregie-dashboard init relay ~/werkregie-init # prints how to enable the unit
|
||
```
|
||
|
||
## Matrix
|
||
|
||
Werkregie reads Matrix as a device of the user's own account,
|
||
so that end-to-end encrypted rooms are readable once the device has their keys.
|
||
It runs in the modes `local` and `relay`, never on the server: the device keys stay on the user's machine.
|
||
|
||
1. Configure `matrix.homeserver`; there is no default.
|
||
2. Stop the relay or the local mode, then sign in; it asks for user and password:
|
||
```bash
|
||
werkregie-dashboard matrix login
|
||
```
|
||
3. Verify the device with the recovery key, formerly "Security Key", or the security phrase;
|
||
this also unlocks the key backup, so that older encrypted messages become readable:
|
||
```bash
|
||
werkregie-dashboard matrix verify
|
||
```
|
||
4. Start the relay or the local mode again; it decrypts the rows shown as "verschlüsselt" once more.
|
||
|
||
The session and the keys live in `<stateDir>/matrix/` (mode 0700):
|
||
an H2 database `client.mv.db` and the shown rows in `messages.json`.
|
||
To sign in anew, delete that directory and remove the device "Werkregie <host>" in Element.
|
||
A relay reports only the start of each text, at most 80 characters, to the server.
|
||
One process per machine runs the client, the one that holds `sync.lock`:
|
||
a relay and the local mode share the state directory, and two clients with the same keys would spoil each other's sessions.
|
||
The other process shows the same messages from `messages.json` and takes over when the first one ends.
|
||
Until the device has the keys, an encrypted message shows as "verschlüsselt", with room, sender and time.
|
||
Each direct chat and configured room keeps its last message in the panel, however old.
|
||
A message the user has not read yet is marked "Neu"; read ones are subdued.
|
||
Read means up to the later of the user's read receipts and Element's read marker `m.fully_read`,
|
||
since an Element without read receipts only moves its marker.
|
||
A click opens the message by `matrix.link`: `matrix.to` (default) in the browser,
|
||
`io.element.desktop` in Element Desktop, `element` in its nightly build; kept messages take the setting, too.
|
||
On a phone the page links to matrix.to, which the Element app takes over:
|
||
unlike Android, a Linux desktop has no app that takes `https://matrix.to` from the browser.
|
||
|
||
### Reactions
|
||
|
||
An agent marks a message of the user with a reaction, e.g. an announced pull request once it is approved:
|
||
|
||
```bash
|
||
werkregie-dashboard matrix react --room "HSAdmin NG" --contains /pulls/329 ✅️
|
||
```
|
||
|
||
The target is the newest own message among the last 500 events of the room whose text contains `--contains`,
|
||
not followed by a letter or a digit, so `/pulls/329` finds no `/pulls/3290`.
|
||
`--room` takes the room's ID or its name.
|
||
The command prints one line:
|
||
|
||
- `REACTED <message> <reaction>`; without `<reaction>` when the client has no event ID for it yet;
|
||
- `PRESENT <message>` when the user reacted with that key already, the variation selector U+FE0F aside;
|
||
- `NO-MESSAGE`, with exit code 1.
|
||
|
||
A failure ends with exit code 3, a wrong call with 2.
|
||
It sends an encrypted `m.reaction` in an encrypted room, as the device of `matrix login`.
|
||
A running relay or local mode sends the reaction with its own client, so it keeps running:
|
||
the process that holds `sync.lock` serves the Unix domain socket `<stateDir>/matrix/control.sock`,
|
||
which takes only a reaction, and `matrix react` asks it first.
|
||
When nobody answers there, the command takes `sync.lock` and opens a client of its own, for about 50 s.
|
||
A process that holds the lock but does not answer, e.g. a relay of an older version or one that is just starting,
|
||
makes the command fail; restart it with `systemctl --user restart werkregie-relay`.
|
||
|
||
## Configuration
|
||
|
||
`~/.config/werkregie/dashboard.yml`, or `--config FILE`, or `WERKREGIE_DASHBOARD_CONFIG`.
|
||
See [dashboard.example.yml](dashboard.example.yml) for the local mode; without a file only Claude Code and Codex are shown.
|
||
An entry this version does not know is named on stderr and skipped, so that a newer version may add entries before its deployment.
|
||
|
||
- **Claude Code:** a session that is not busy is marked 💬 *Claude fragt* when it waits for the user:
|
||
*Frage offen* or *Dialog offen* by its process file, *Frage offen* by an unanswered AskUserQuestion in its transcript,
|
||
*wartet auf dich* when Jev rates Claude's last text at least `claude.jev.threshold` (default 0.75).
|
||
Only with `claude.jev` does the last text, at most 3000 characters, go to Jev, once per message;
|
||
its key comes from `apiKey` or `apiKeyEnv`, e.g. `TYPESAFE_API_KEY` in the relay's `dashboard.env`.
|
||
An archived session carries no marker.
|
||
✕ on a session in *Du bist dran* dismisses it until the session changes, e.g. with a new message;
|
||
the server keeps the dismissals in `dismissed.json` of its state directory, for all devices,
|
||
and takes them only with the access token as bearer header.
|
||
The other rows show ✕ dimmed, so that the controls align; its tooltip says why the row cannot be dismissed.
|
||
The tooltip of its row in "Du bist dran" shows the user's last input and Claude's question:
|
||
the open AskUserQuestion, else Claude's last text.
|
||
The last input is a prompt or the answers to an AskUserQuestion, searched up to 8 MiB back in the transcript.
|
||
On a phone, which has no tooltip, the info symbol ⓘ of the row shows both texts below its title.
|
||
- **Session of a pull request:** each pull request, in its panel and in *Du bist dran*, shows the session that works on it.
|
||
That is the session that named its address last, in any checkout: in a shell command, e.g. `watch-pr` or a call of Gitea's API,
|
||
in the output of a command that names this pull request alone, e.g. Gitea's answer to opening it, or in a text of Claude.
|
||
A session whose command named it beats one that only wrote about it, e.g. a session that reports on the work of others.
|
||
The user's texts, files and messages to other sessions do not count; `http`, `https`, Gitea's API and `/issues/` are one address.
|
||
Without such a session, the pull request shows the session that last worked on its head branch, in a checkout of its repository,
|
||
by the `gitBranch` of the transcript's lines, other than a detached `HEAD`.
|
||
The relay reads each transcript from where it stopped, at first only its last 32 MiB, and keeps the last 20 pull requests per session.
|
||
Its button opens the session, as the session's own link does; under *Festgefahren* the copy symbol beside it copies a message
|
||
for the session, e.g. to rebase onto `main` after a conflict.
|
||
Each non-archived session counts, older than the window too; a relay older than 0.47.0 reports none,
|
||
one older than werkregie PR#98 no addresses.
|
||
A text over 160 characters is shortened by `claude.summary`, by default `anthropic/claude-haiku-5.5` through OpenRouter,
|
||
with its key from `apiKeyEnv`, e.g. `OPENROUTER_API_KEY` in the relay's `dashboard.env`;
|
||
each text costs one call, since the summaries stay in `summaries.json` in the state directory for a day after their last use.
|
||
Without `claude.summary`, or while the model fails, such a text is cut.
|
||
Both texts go with the relay's report to the server.
|
||
- **Werkator:** private repositories are redacted for anonymous readers.
|
||
`controlToken` (or `controlTokenEnv`) is sent as the `werkator_token` cookie, the way Werkator's own pages do;
|
||
`tools/remote --env-file .env.<instance> werkator control-token` prints it.
|
||
- **werkrevisor:** `werkrevisors` with `name` and `url` of the statistics service; its costs are read anonymously.
|
||
- **Gitea:** a token with read scopes, as `token`, `tokenEnv` or `teaLogin` (a login of `~/.config/tea/config.yml`, by name or user).
|
||
`pullRequests: mine` lists PRs created by, assigned to or awaiting review from the user, `all` every open PR the user can see.
|
||
A PR is the user's own (its author is the token's user), an agent's (its author is in `agents`) or a colleague's.
|
||
Each PR is marked by what it asks of whom; the panel sorts by these markers, in this order:
|
||
⚠ *Festgefahren* (werkrevisor escalated, RED for an hour without a new commit, ERROR, a run without verdict,
|
||
or an agent's PR in conflict for 30 min without a new commit since `main` left its base; never a colleague's PR),
|
||
✋ *Dein Approval* (an agent's or a colleague's PR with a final ORANGE, then YELLOW, then without werkrevisor 2 min after its last change;
|
||
a review requested from the user counts once werkrevisor's result is there, also after an approval of the user),
|
||
➜ *Mergen* (the user's own PR with the official approvals and green checks that the branch protection of its base asks for,
|
||
with "hinter main: rebasen, dann mergen" when its base moved on; without a branch protection, approved by another human;
|
||
or an agent's PR approved by anyone but its author, with all checks green; in each case no automerge scheduled), 🔧 *Nacharbeit* (conflict or red build on the user's own PR),
|
||
✍ *Kommentar* (another human commented on the user's own PR, or reviewed it with comments or requested changes,
|
||
after its last commit and the user's last comment; `reviewBots` do not count),
|
||
⏳ *Wartet auf Review* (the user's own PR waits for another human),
|
||
⚙ *Agent arbeitet* (instead of *Dein Approval* while an agent's session named the PR's URL in a shell command, e.g. `watch-pr`,
|
||
or wrote on the PR's branch, within 10 min; a text about the PR does not count), ✓ *Von dir approved*.
|
||
Unmarked PRs follow, drafts come last; drafts carry no marker.
|
||
Within each group the PR that waits longest comes first.
|
||
`reviewBots` (default `[ai-reviewer]`) names accounts whose approval is no human's.
|
||
`agents` (default `[claude-code]`) names the logins of the coding agents, ignoring case;
|
||
a colleague merges and rebases a PR of its own, so it shows neither *Mergen* nor *Festgefahren*.
|
||
`mergeAuthors`, e.g. `[mi, claude-code]`, names the logins whose merges into the default branch the chart counts,
|
||
by default the token's user; `[]` shows no chart.
|
||
A pull request counts by its author on the day of its merge, in the time zone of the browser; a week starts on Monday.
|
||
The merges are read every 10 min; each merged pull request is asked once for its base branch and its changed lines,
|
||
the added plus the deleted ones of the whole diff.
|
||
|
||
- **Taiga:** `taskboards` with `type: taiga` list the open user stories, tasks and issues assigned to the user
|
||
in the configured `projects`, in the panel *Aufgaben*.
|
||
There is no default instance or project.
|
||
The API address comes from the field `api` of `<url>/conf.json`, as for Taiga's own web app, else `<url>/api/v1`;
|
||
`apiUrl` overrides it.
|
||
Werkregie logs in with the user and the password of the variable `passwordEnv` and logs in again when the token expires;
|
||
on a server the variable stands in `dashboard.env`, in the local mode it has to be in the environment.
|
||
`statuses` names the most important statuses first, within one the latest change first;
|
||
a list in one place holds equal names of different projects, e.g. `[In Arbeit, In Progress]`.
|
||
An open status named nowhere comes first, marked *Status unbekannt*, so that it gets added.
|
||
`hiddenStatuses`, e.g. `[Erledigt, Verworfen]`, are hidden until the panel's count shows them.
|
||
Each status carries its colour from Taiga.
|
||
|
||
Local sources refresh every 2–3 s, Werkator every 30 s (three repositories at a time, its lines every 5 min),
|
||
Gitea every 30 s (its merges every 10 min), Taiga every 60 s, werkrevisor every 2 min;
|
||
cached data is served at once while a due source refreshes in the background.
|