npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@opensoftware/console

v0.9.0

Published

console: the web UI of a factory, where the people on its access list follow its runs and act on them.

Readme


covers: [modules/console/src/**]

console

The web UI of a factory, where the people on its access list follow its runs and act on them. It shows every run of every foreman with its state, origin, requester, pull request and age; one run's requests, stages, gates, checks, transcripts and trace; the factory definition with a link to the file in Git, where it changes; and the self-improvement inbox of observer's open proposals. A person submits work and acts on runs and gates through its forms, signed in by its own sign-in or by a proxy in front. It keeps no state: each page asks the foreman's, intake's and gatekeeper's APIs, or the forge port's adapter, when it is opened. Temporal, the trace store and the dashboards keep their own UIs; the console links to them. Decisions: ADR 0072, ADR 0073 (the inbox).

Quick start

This quick start is scenario SCN-CONSOLE-001 and is tested on every change. It needs a running foreman (modules/foreman) with the API token dev-token, here on port 8081:

export CONSOLE_FOREMAN_URL=http://localhost:8081 CONSOLE_FOREMAN_TOKEN=dev-token
npx @opensoftware/console serve

Open http://localhost:8080. The page lists the foreman's runs, the running ones first, and reloads itself every 10 seconds.

Commands

| Command | What it does | | --- | --- | | console serve | Serve the pages below on PORT. Exits 2 without CONSOLE_FOREMAN_URL. | | console doctor [--deep] | Check the factory as Health describes, print one line per check (ok <check> or FAIL <check>: <what to do>) and the count, and exit 1 when a check fails. |

Pages

| Page | What it shows | | --- | --- | | / | The runs of every foreman: title, factory, state, source, requester, pull request and age. Runs that have not ended come first, the one that started last first, then the ended runs, the one that ended last first. A foreman that does not answer is named above the list. The page shows the first page of each foreman's runs and links "Next page" (?page=) while one has more (ADR 0115). | | /?state=<state> | Only the runs in the state running, blocked, complete, failed or cancelled, as the foremen filter them. Another state answers 400. | | /runs/<runId> | One run: its state, factory and factory version, age, pull request, trace and Temporal history; its origin, or with CONSOLE_INTAKE_URL every request of its work item; and its stages as its evidence, each with its agent ("waits" for a stage that only waits, ADR 0107), state, transcript, gate, checks and the reason it failed. A run that a person ran again names its attempt and links the run before it. 404 when no foreman knows the run. | | /work | With CONSOLE_INTAKE_URL: intake's latest work items, the one received last first, each with its origin, type, status, source, requester, run and age, also work that waits without a run, one page of intake's inbox at a time with a link "Next page" (?cursor=) while more follow. 404 without CONSOLE_INTAKE_URL. | | /factory | The factory definition in CONSOLE_FACTORY: its name, description, providers and agents with their harness and model. | | /factory/agents/<name> | One agent's configuration, and the link to the file on the branch where it changes. 404 for an agent the factory does not declare. | | /health | The checks of console doctor as a table: each check, its state and what to do. 200 when every check is ok, else 503. | | /inbox | The self-improvement inbox: the open pull requests of CONSOLE_INBOX_REPOSITORY whose branch starts with CONSOLE_INBOX_PREFIX (default observer/), newest first, each with its link, branch, author, state ("approved", "changes requested" or "needs review") and age. A forge adapter that fails is named above the list. 404 without CONSOLE_INBOX_REPOSITORY. | | /secrets | Only for an admin (Secrets): the secrets that the factory's agents declare and the names in the secret store, each "Missing", "Set", "Set outside the UI", "Not used" or "Unknown", and the form "Store a value". ?name=<name> fills the form's name. A person without the role admin gets 403. 404 in a console without roles or without OSF_OPENBAO_ADDR. | | /secrets/delete?name=<name> | Only for an admin: asks before the secret of that name is deleted, with the button "Delete" and the link "Cancel". | | /improvement | Cost and scores of the factory's runs, from the files below CONSOLE_IMPROVE_DIR: the total cost and the number of runs, each scorer with its scored runs, passes, pass rate and judge cost, and each stage of a run with its outcome and cost, linked to the run's page. 404 without CONSOLE_IMPROVE_DIR. |

The runs page / has the new look of the design system (ADR 0133): a navigation column with the factory's name, only the pages that work in this console (the runs with a count of those that wait for a person, then the "Classic pages" in the old look and the dashboards), and the account with the signed-in person, "Sign out" and the link to the other theme. On a phone the navigation folds behind "Menu". The theme is dark unless the cookie osf_theme says light; /theme?to=light&next=<path> sets it and returns to the page. The other pages keep the old look until they move one at a time.

Every page links the runs, the work items (with CONSOLE_INTAKE_URL), the factory, the health page and the dashboards in CONSOLE_LINKS. The runs pages reload themselves every CONSOLE_REFRESH seconds. Without CONSOLE_USER_HEADER the console is read-only: it has no forms, and any method but GET and HEAD answers 405. It shows what the foreman and intake show, so run it behind your sign-in proxy, such as oauth2-proxy. Every answer carries a Content-Security-Policy that allows only the console's own files and no script. The pages render on the server; the health page was the first that Astro renders, the runs page the second (ADR 0132), the others follow one at a time with the same URLs.

Actions (ADR 0089): set CONSOLE_USER_HEADER to the header in which your sign-in proxy sends the signed-in person, like X-Forwarded-Email. Then /work has the form "submit work" (title, type and description), which the console posts to intake with that person as the requester and the origin console; intake checks the fields. A form without the header answers 401, a form from another site 403, and a form that intake refuses 502 with intake's answer. Without CONSOLE_INTAKE_URL the console takes no form "submit work". The page of a failed or cancelled run has the form "run again" (ADR 0105), which the console posts to the foreman's POST /v1/runs/<runId>/retry with that person as by; it then leads to the new run's page. A retry that the foreman refuses answers with the foreman's status and its problem's detail. The page of a running or blocked run has the forms "Steer" (with a message), "Take over" (while a stage runs), "Hand back" (while a person holds a stage) and "Cancel", which the console posts to the foreman's POST /v1/runs/<runId>/signals with that person as by; the run lists them under "Actions" (ADR 0118). With CONSOLE_GATEKEEPER_URL a pending gate of a stage has the forms "Approve" and "Reject" (with a reason), which the console posts to gatekeeper's POST /v1/gates/<gateId>/decisions with that person as by: gatekeeper refuses a person who is in no group the gate waits for, so list the identity the proxy sends, like [email protected], in gatekeeper's groups. Each form leads back to the run's page, or answers with the API's status and its problem's detail. Only set the variable behind a proxy that sets the header and removes it from the browser's requests.

Cost and scores (ADR 0123): the scheduled jobs of the improvement path write run records as runlog writes them and scores as scorekit writes them, in the formats of the contracts, to one folder. Set CONSOLE_IMPROVE_DIR to it. The console reads every .json and .jsonl file below it when /improvement is opened: each invoke_agent span of a run record is one stage with its osf.cost.usd, and each score counts for its scorer, a score of 1 as a pass, with its osf.judge.cost.usd. Other records, lines that are no JSON and other files are skipped.

Several factories: list each foreman in CONSOLE_FOREMAN_URL, comma-separated, and their tokens in the same order in CONSOLE_FOREMAN_TOKEN, or one token for all.

The inbox: observer opens each proposal as a pull request from a branch observer/<problem>-<hash> (observer). The console runs the forge port's adapter in CONSOLE_FORGE_ADAPTER with prs <repo> --head <prefix> (forge port), with CONSOLE_FORGE_URL and CONSOLE_FORGE_TOKEN as its FORGE_URL and FORGE_TOKEN. A token that may read pull requests is enough. For a Forgejo, such as Codeberg, set CONSOLE_FORGE_ADAPTER=npx -y @opensoftware/forge-forgejo and its URL.

Links: CONSOLE_TRACE_URL and CONSOLE_SESSION_URL are URL templates of the trace store, such as Langfuse or Phoenix, with {runId} or {traceId}, and {sessionId}. {traceId} is the run's traceId, which a foreman with traces names (ADR 0087); Phoenix finds a trace by it. The foreman sets the run's ID as osf.run.id on every run's traces (runlog), and a stage's harness session is its transcript. CONSOLE_SPAN_URL, with {traceId} and {spanId}, makes each started stage's name a link to its span in the run's trace, such as Jaeger's /trace/{traceId}?uiFind={spanId} (ADR 0121). CONSOLE_TEMPORAL_URL links the run's history: <url>/namespaces/<CONSOLE_TEMPORAL_NAMESPACE>/workflows/<runId>.

Sign-in

The console signs people in itself when CONSOLE_SIGN_IN names the provider (ADR 0134): oidc for OpenID Connect (Google, a company's provider) or github. Every page then needs a session: a page without one sends the person to /signin and back to the page after the sign-in, a form without one answers 401, and a form whose Origin is not CONSOLE_URL answers 403. Who may sign in is the access file (CONSOLE_ACCESS, by default access.yaml beside CONSOLE_FACTORY), read again at each request: a person removed from it loses access at the next page. Only an email the provider marks as verified counts; a person not on the list reads why. Without a valid access file with an admin, nobody signs in. "Sign out" in the navigation ends the session.

# access.yaml
version: 1
people:
  - email: [email protected]
    role: admin
  - email: [email protected]
    role: member

The session is an encrypted cookie (HttpOnly, SameSite=Lax, Secure over HTTPS) that lasts CONSOLE_SESSION_HOURS; CONSOLE_SESSION_SECRET may name two keys, so a key changes without signing anyone out. The provider's callback is <CONSOLE_URL>/signin/callback. With the sign-in on, the console does not start without its provider's settings and the session key, on an address that is neither https:// nor loopback, or with CONSOLE_USER_HEADER too. Open the console on localhost (the default CONSOLE_URL): a request for another host, like 127.0.0.1, is sent there first, so the session cookie stays off the tools of the profile on 127.0.0.1. /v1/ready answers without a session whether the console reads its factory, for health checks. It answers for every host name: a container's health check asks 127.0.0.1, while a page asked at another host than CONSOLE_URL is sent there.

Whoever may write to the factory's repository can make themselves admin: write access to that repository is admin access to the factory, and the repository should be private, since the access file holds emails.

Secrets

An admin stores the values of the agents' secrets on /secrets (ADR 0137). The page needs a role, so it exists with the own sign-in or behind a sign-in proxy with an access file, and needs the factory's OpenBao in OSF_OPENBAO_ADDR; otherwise it and its forms answer 404 and name what is missing, and the navigation does not link it. Only an admin sees it in the navigation; anyone else gets 403.

The page lists, the missing ones first:

| State | When | The row says | | --- | --- | --- | | Missing | An agent declares ref:openbao/<name>, and the store has no such secret | The agents that need it, and the link "Store a value" | | Unknown | An agent declares ref:openbao/<name>, and the store did not answer | The agents that need it; the page names the store's error above the list | | Set | An agent declares it, and the store has it | The agents that use it, the version, and who stored it when (UTC, to the minute), and the link "Replace" | | Set outside the UI | An agent declares ref:env/<NAME> or ref:file/<name> | The sandbox adapter's variable OSF_SECRET_<NAME>, or the file <name> in its OSF_SECRETS_DIR | | Not used | The store has it, and no agent declares it | Who stored it when, and the link "Delete" |

The form "Store a value" has the secret's name and a password field for the value, with no autocomplete; the field is empty on every page. It posts to POST /secrets, which runs the adapter in CONSOLE_SECRETS_ADAPTER as put --by=<the person's email> -- <name> and writes the value only to the adapter's standard input. "Delete" leads to a page that asks first; its form posts to POST /secrets/delete, which runs delete -- <name>. Each form answers 303 to /secrets, or shows the page with the problem: 400 for a name the adapter refuses, 502 for a store that refuses, 413 for a form over 64 KiB. Forms follow the rules of the console's other forms: with the own sign-in a form's Origin must be CONSOLE_URL, behind a proxy a form from another site answers 403 and one without the person 401.

A value appears nowhere else: not in a page, a redirect, a header, an error or a line the console prints. The console logs no request. The adapter prints no value and names none in an error (secrets port), and the console's token cannot read one: give it the policy that secrets-openbao policy write prints.

When a new value takes effect: a sandbox's egress proxy reads the value at the first request that needs it and keeps it while the sandbox runs, so a value counts from the next sandbox that starts, like the next run's. It is sent only to the hosts that the agents which declare it may reach (their sandbox.egress). The egress proxy swaps a placeholder in an HTTP request, so a secret that is not sent in one, like an SSH key or a database password, does not work this way.

Health

console doctor and /health check, in this order (ADR 0086):

  1. Each foreman, intake and gatekeeper the console names: its GET /v1/health, one line per check the server reports (such as foreman temporal or intake nats), and whether it accepts the console's token. A server that does not answer names the variable of its URL; a refused token names the console's variable and the server's own.
  2. factory: the definition in CONSOLE_FACTORY can be read.
  3. groups, with CONSOLE_GROUPS: each group of the gatekeeper's groups file has a member who is not the example's slack:U0EXAMPLE, the member the reference groups.json ships in place of a person (ADR 0114). The check reads every group, not only the policy's approvers, and cannot tell whether a Slack ID belongs to a real person.
  4. With CONSOLE_SIGN_IN (ADR 0134): sign-in, for oidc the provider's discovery document at <CONSOLE_OIDC_ISSUER>/.well-known/openid-configuration answers; and access, the access file holds and has an admin who is not the example's [email protected], the person the reference access.yaml ships in place of one.
  5. When an agent of the factory declares a secret of OpenBao (ref:openbao/<name>, ADR 0137): secrets, the adapter in CONSOLE_SECRETS_ADAPTER answers probe and list with the store at OSF_OPENBAO_ADDR and the token OSF_OPENBAO_TOKEN, else it names the adapter's error and the three variables; secrets token, the console's token lives 7 days or more, else it warns with the time the token ends, so there is a week to give the console a new one (a token of OpenBao lives at most 32 days by default; one without an end, like the root token, never warns); and secret <name> for each declared secret, which fails when the store has no value for it and names the agents that need it.
  6. Only with console doctor --deep (ADR 0120): model <route>, one chat completion of one token per model route of the factory definition's agents through the gateway in CONSOLE_MODEL_URL, and harness, the command in CONSOLE_HARNESS_COMMAND answers ACP initialize within 10 seconds. A model call costs money, so /health and console doctor without --deep never make one. With CONSOLE_TRACES_URL also traces: one span console doctor of the service console goes by OTLP/HTTP to the collector in CONSOLE_OTLP_URL, and Jaeger's query API GET /api/traces/{traceId} must find it within 5 seconds. A collector that refuses the span names CONSOLE_OTLP_URL; a span that does not arrive names the collector's exporter and CONSOLE_TRACES_URL. With CONSOLE_SANDBOX_COMMAND also sandbox: the sandbox adapter's probe <template> example.com (sandbox port) starts a sandbox from CONSOLE_SANDBOX_TEMPLATE with no allowed host, sends one HTTPS request to example.com and removes the sandbox; it must print refused example.com. A sandbox that reaches the host names the adapter's egress proxy; one that does not start names the adapter's reason, CONSOLE_SANDBOX_COMMAND and CONSOLE_SANDBOX_TEMPLATE. With CONSOLE_NATS_URL also events: the console publishes one doctor.ping@v1 with a fresh random ID on osf.doctor.ping.v1 through JetStream, and asks each foreman of CONSOLE_FOREMAN_URL every 250 ms for GET /v1/doctor/pings/{id} until one answers 200, for 10 seconds. A NATS server that does not answer names CONSOLE_NATS_URL; one without a stream for the subject names the foreman's FOREMAN_NATS_URL, as the foreman adds the stream OSF; a ping that no foreman shows names the foreman's release and its FOREMAN_NATS_URL.

The links to the Temporal UI and the trace store are for the browser, so the console does not check them; the foreman's temporal check covers Temporal.

A server of a release before /v1/health fails its check with the advice to update it. When every check is ok, console doctor prints the count last, like all 7 checks ok. A warning prints WARN <check>: <what to do>, counts as ok, adds itself to the count, like all 5 checks ok, warnings: 1, and shows on /health as "warning" with status 200.

Configuration

| Variable | Type | Default | Meaning | | --- | --- | --- | --- | | CONSOLE_ACCESS | string | | serve, with CONSOLE_SIGN_IN: the access file, read again at each request. Default access.yaml beside CONSOLE_FACTORY. Without a valid file with an admin nobody signs in. | | CONSOLE_BRANCH | string | "main" | serve: the branch where the factory definition changes. | | CONSOLE_FACTORY | string | "factory.yaml" | serve: the factory definition that /factory shows, read-only. | | CONSOLE_FACTORY_PATH | string | "factory.yaml" | serve: the path of the factory definition in its Git repository. | | CONSOLE_FOREMAN_TOKEN | string | | serve: the foreman's FOREMAN_API_TOKEN, sent as Authorization: Bearer ; several, comma-separated, in the order of CONSOLE_FOREMAN_URL, or one for all. | | CONSOLE_FOREMAN_URL | string | | serve: the base URL of a foreman's API, or several, comma-separated, one per factory. Required. | | CONSOLE_FORGE_ADAPTER | string | "npx -y @opensoftware/forge-github" | serve: the command line of the forge port's adapter that /inbox runs with prs (ADR 0073). | | CONSOLE_FORGE_TOKEN | string | | serve: the adapter's FORGE_TOKEN; a token that may read pull requests is enough. | | CONSOLE_FORGE_URL | string | | serve: the forge's API, the adapter's FORGE_URL, like https://codeberg.org for forge-forgejo. Default: the adapter's own. | | CONSOLE_GATEKEEPER_TOKEN | string | | serve: gatekeeper's GATEKEEPER_API_TOKEN. | | CONSOLE_GATEKEEPER_URL | string | | serve: the base URL of gatekeeper's API. Set, a run's page shows each gate's state and who it waits for. | | CONSOLE_GITHUB_API_URL | string | "https://api.github.com" | serve, with CONSOLE_SIGN_IN=github: GitHub's API, from which the console reads the person's verified primary email. | | CONSOLE_GITHUB_CLIENT_ID | string | | serve, with CONSOLE_SIGN_IN=github: the client ID of the GitHub OAuth app. Its callback address is <CONSOLE_URL>/signin/callback. | | CONSOLE_GITHUB_CLIENT_SECRET | string | | serve, with CONSOLE_SIGN_IN=github: the OAuth app's client secret. | | CONSOLE_GITHUB_URL | string | "https://github.com" | serve, with CONSOLE_SIGN_IN=github: GitHub's address for the sign-in, for GitHub Enterprise Server. | | CONSOLE_GROUPS | string | | doctor and /health: the groups file of the factory's gatekeeper, like /factory/groups.json. Set, a group without a member but the example's slack:U0EXAMPLE fails the check groups. | | CONSOLE_HARNESS_COMMAND | string | | doctor --deep: the command line, run with sh -c, that starts the harness's ACP agent, like claude-code-acp; it must answer ACP initialize within 10 seconds. | | CONSOLE_IMPROVE_DIR | string | | serve: the folder the improvement jobs write run records and scores to. Set, the page /improvement shows each scorer's pass rate and judge cost and each run's stages with their cost. | | CONSOLE_INBOX_PREFIX | string | "observer/" | serve: the start of the branch names that /inbox lists. | | CONSOLE_INBOX_REPOSITORY | string | | serve: the repository of observer's pull requests, as /, like acme/factory. Set, /inbox lists its open proposals. | | CONSOLE_INTAKE_TOKEN | string | | serve: intake's INTAKE_API_TOKEN. | | CONSOLE_INTAKE_URL | string | | serve: the base URL of intake's API. Set, a run's page lists every request of its work item. | | CONSOLE_LINKS | string | | serve: the dashboards every page links, as name=url, comma-separated, like Grafana=https://grafana.acme.dev. | | CONSOLE_MODEL_TOKEN | string | | doctor --deep: the gateway's key, sent as Authorization: Bearer . | | CONSOLE_MODEL_URL | string | | doctor --deep: the OpenAI-compatible base URL of the model gateway (LiteLLM), ending in /v1; it gets one chat completion of one token per model route of the factory definition. | | CONSOLE_NATS_URL | string | | doctor --deep: the NATS server of the factory, like nats://nats:4222. Set, doctor --deep publishes one doctor.ping@v1 on osf.doctor.ping.v1 and waits 10 s for a foreman of CONSOLE_FOREMAN_URL to show it at GET /v1/doctor/pings/{id}. | | CONSOLE_OIDC_CLIENT_ID | string | | serve, with CONSOLE_SIGN_IN=oidc: the console's client at the provider. Its callback address is <CONSOLE_URL>/signin/callback. | | CONSOLE_OIDC_CLIENT_SECRET | string | | serve, with CONSOLE_SIGN_IN=oidc: the client's secret. | | CONSOLE_OIDC_ISSUER | string | | serve, with CONSOLE_SIGN_IN=oidc: the provider's issuer, whose discovery document is /.well-known/openid-configuration, like https://accounts.google.com. | | CONSOLE_OTLP_URL | string | | doctor --deep: the OTLP/HTTP base URL of the collector, like http://jaeger:4318; it gets one span "console doctor" of the service "console" at /v1/traces. | | CONSOLE_REFRESH | string | "10" | serve: the seconds after which the browser reloads the runs pages; 0 turns the reload off. | | CONSOLE_REPOSITORY_URL | string | | serve: the web URL of the factory's Git repository, like https://github.com/acme/factory. Set, the configuration pages link the file. | | CONSOLE_SANDBOX_COMMAND | string | | doctor --deep: the command line, run with sh -c, of a sandbox adapter, like sandbox-docker. Set, doctor --deep runs its probe example.com, which starts a sandbox with no allowed host, and wants "refused example.com". | | CONSOLE_SANDBOX_TEMPLATE | string | "curlimages/curl:8.17.0" | doctor --deep: the template of the sandbox probe; it needs curl. | | CONSOLE_SECRETS_ADAPTER | string | "npx -y @opensoftware/secrets-openbao" | serve, doctor and /health: the command line of the secrets port's adapter that /secrets runs with list, put and delete, and console doctor with probe and list (ADR 0137). It gets the console's environment with OSF_OPENBAO_ADDR, OSF_OPENBAO_TOKEN and OSF_OPENBAO_MOUNT, and a value only on the standard input of put. | | CONSOLE_SESSION_HOURS | number | 12 | serve, with CONSOLE_SIGN_IN: how long a session lasts, in hours. | | CONSOLE_SESSION_SECRET | string | | serve, with CONSOLE_SIGN_IN: the key of the encrypted session cookie, or several separated by commas: the first encrypts, every one decrypts, so a key changes without signing everyone out. | | CONSOLE_SESSION_URL | string | | serve: the link to a stage's transcript, with {sessionId}, like https://langfuse.acme.dev/sessions/{sessionId}. | | CONSOLE_SIGN_IN | string | | serve: switches on the console's own sign-in with this provider: oidc (OpenID Connect, like Google or a company's provider) or github (ADR 0134). Every page then needs a session, and who may sign in is the access file. Unset, the console is open and read-only, or behind the sign-in proxy of CONSOLE_USER_HEADER; both set, it does not start. | | CONSOLE_SPAN_URL | string | | serve: the link to a stage's span in the run's trace, with {traceId} and {spanId}, like http://127.0.0.1:16686/trace/{traceId}?uiFind={spanId} for Jaeger. | | CONSOLE_TEMPORAL_NAMESPACE | string | "default" | serve: the Temporal namespace of the runs, the foreman's FOREMAN_TEMPORAL_NAMESPACE. | | CONSOLE_TEMPORAL_URL | string | | serve: the base URL of the Temporal UI. Set, a run's page links its history. | | CONSOLE_TRACES_URL | string | | doctor --deep: the base URL of the trace store's Jaeger query API, like http://jaeger:16686. Set, doctor --deep sends one span to CONSOLE_OTLP_URL and waits 5 s for GET /api/traces/{traceId} to find it. | | CONSOLE_TRACE_URL | string | | serve: the link to a run's trace in the trace store, with {runId} or {traceId}, like https://langfuse.acme.dev/traces?run={runId} or Phoenix's http://127.0.0.1:6006/projects/UHJvamVjdDox/traces/{traceId}. A run without a traceId gets no link from a template with {traceId}. | | CONSOLE_URL | string | | serve: the console's own address, the source of the provider's callback address and of the same-origin rule of forms with the sign-in. Default http://localhost:. With the sign-in it must be https:// or loopback, else the console does not start. A request for another host is sent to this address. | | CONSOLE_USER_HEADER | string | | serve: the header in which the sign-in proxy sends the signed-in person, like X-Forwarded-Email. Set, the console takes actions in that person's name, such as submitting work on /work (ADR 0089); unset, it is read-only. Only set it behind a proxy that sets the header and removes it from the browser's requests. | | OSF_NOW | string | | The current time, like 2026-01-01T00:00:00Z, for the age of a run, the sign-in and the waits of console doctor --deep, which then pass at once. Default: the clock. | | OSF_OPENBAO_ADDR | string | | serve, doctor and /health: the address of the factory's OpenBao, like http://openbao:8200, for the secrets adapter. Set, an admin stores and deletes secrets on /secrets. | | OSF_OPENBAO_MOUNT | string | "secret" | serve, doctor and /health: the factory's KV version 2 mount in OpenBao, the same as its sandboxes' egress proxy reads. | | OSF_OPENBAO_TOKEN | string | | serve, doctor and /health: the console's OpenBao token, with the policy that secrets-openbao policy write prints: it writes, lists and deletes secrets and cannot read a value. console doctor warns 7 days before it ends. | | OSF_SEED | string | | doctor --deep: set, the ID of the doctor's ping comes from it instead of from chance, so a test knows it. Leave it unset in a factory. | | PORT | string | "8080" | serve: the port the web pages are served on. |

Releases

release-please opens a release pull request from the Conventional Commits under modules/console/. Merging it creates the tag console-v<version>. The workflow .github/workflows/publish-console.yml then builds the package and publishes it to npm as @opensoftware/console. When the newest tag is missing on npm, the next run of the workflow on main publishes it. To publish a tag again, run the workflow by hand with the tag. The first release is 0.1.0; until 1.0.0, a breaking change raises the minor version.

The one-time setup is the same as for contracts (contracts/README.md): the GitHub environment npm, a token only for the first version, then a trusted publisher for the workflow publish-console.yml. No credential is stored in the repository.

Scenarios

| ID | Status | Scenario | | --- | --- | --- | | SCN-CONSOLE-001 | deprecated | A new user opens the runs page of the quick start | | SCN-CONSOLE-002 | implemented | The help lists every command | | SCN-CONSOLE-003 | implemented | An unknown command shows the usage | | SCN-CONSOLE-004 | implemented | Serve without a foreman exits 2 and names the variable | | SCN-CONSOLE-005 | implemented | A run's row shows its title, source, requester, pull request and age | | SCN-CONSOLE-006 | implemented | The runs page lists the runs of two factories | | SCN-CONSOLE-007 | implemented | Runs that have not ended come first, the newest first | | SCN-CONSOLE-008 | implemented | A person filters the runs by the state "failed" | | SCN-CONSOLE-009 | implemented | The runs page links a filter for each of the five states | | SCN-CONSOLE-010 | implemented | An unknown state answers 400 and names the five states | | SCN-CONSOLE-011 | implemented | A foreman that does not answer is named, and the other foreman's runs are listed | | SCN-CONSOLE-012 | implemented | The runs page reloads itself every 10 seconds | | SCN-CONSOLE-013 | implemented | The console calls each foreman with its token | | SCN-CONSOLE-014 | implemented | A title with HTML is shown as text | | SCN-CONSOLE-015 | implemented | The run page shows the stages with their agent and state | | SCN-CONSOLE-016 | implemented | The run page links its Slack origin and its pull request | | SCN-CONSOLE-017 | implemented | With intake, the run page lists every request of the work item | | SCN-CONSOLE-018 | implemented | The run page links its trace and each stage's transcript | | SCN-CONSOLE-019 | implemented | The run page links its history in the Temporal UI | | SCN-CONSOLE-020 | implemented | The run page shows a stage's gate with its state and who it waits for | | SCN-CONSOLE-021 | implemented | The run page shows the checks a stage waits for and why a stage failed | | SCN-CONSOLE-022 | implemented | A run that no foreman knows answers 404 | | SCN-CONSOLE-023 | deprecated | The factory page lists the agents with their harness and model | | SCN-CONSOLE-024 | implemented | An agent's page is read-only and links to the file and the branch in Git | | SCN-CONSOLE-025 | implemented | An agent that the factory does not declare answers 404 | | SCN-CONSOLE-026 | implemented | A POST to the console answers 405 | | SCN-CONSOLE-027 | implemented | Every page links the dashboards in "CONSOLE_LINKS" | | SCN-CONSOLE-028 | implemented | A foreman that cannot be reached is named on the runs page | | SCN-CONSOLE-029 | implemented | An origin link that is not http or https is shown as text | | SCN-CONSOLE-030 | implemented | The inbox lists observer's open pull requests, the newest first | | SCN-CONSOLE-031 | implemented | A proposal shows its age and the state "needs review" or "approved" | | SCN-CONSOLE-032 | implemented | A proposal a reviewer asked to change shows "changes requested" | | SCN-CONSOLE-033 | implemented | The inbox lists the branches of the prefix in "CONSOLE_INBOX_PREFIX" | | SCN-CONSOLE-034 | implemented | A forge that refuses the token is named on the inbox | | SCN-CONSOLE-035 | implemented | Without a repository the inbox answers 404 and names the variable | | SCN-CONSOLE-036 | implemented | The factory page lists the agents with their harness, model and effort | | SCN-CONSOLE-037 | implemented | A healthy factory passes every check of "console doctor" | | SCN-CONSOLE-038 | implemented | "console doctor" names each failing check and what to do | | SCN-CONSOLE-039 | implemented | A foreman that does not answer is named with the variable of its URL | | SCN-CONSOLE-040 | implemented | The page "/health" shows the checks and answers 503 when one fails | | SCN-CONSOLE-041 | implemented | The run page links the run's trace by its trace ID | | SCN-CONSOLE-042 | implemented | A trace link with "{traceId}" is left out for a run without a trace ID | | SCN-CONSOLE-043 | implemented | The work page lists each work item with its status and its run | | SCN-CONSOLE-044 | implemented | Without intake the work page answers 404 and names the variable | | SCN-CONSOLE-045 | implemented | A signed-in person submits work from the work page | | SCN-CONSOLE-046 | implemented | A form without the signed-in person submits nothing | | SCN-CONSOLE-047 | implemented | Without "CONSOLE_USER_HEADER" the console stays read-only | | SCN-CONSOLE-048 | implemented | "console doctor" names a missing foreman URL, a server without "/v1/health" and a factory it cannot read | | SCN-CONSOLE-049 | implemented | A form from another site submits nothing | | SCN-CONSOLE-050 | implemented | The run page of a failed or cancelled run has the form "run again" for a signed-in person | | SCN-CONSOLE-051 | implemented | A person runs a failed run again from the run page | | SCN-CONSOLE-052 | implemented | The run page of a run that is running or complete has no form "run again" | | SCN-CONSOLE-053 | implemented | A retry the foreman refuses shows the foreman's problem | | SCN-CONSOLE-054 | implemented | The run page of a run that ran again links the run before it | | SCN-CONSOLE-055 | implemented | The run page shows a stage that only waits with "waits" instead of an agent | | SCN-CONSOLE-056 | implemented | "console doctor" fails on a group whose only member is the example's | | SCN-CONSOLE-057 | implemented | The runs page links the next page while a foreman has more runs | | SCN-CONSOLE-058 | implemented | The work page links the next page of intake's inbox | | SCN-CONSOLE-059 | implemented | "console doctor" fails on a groups file it cannot read | | SCN-CONSOLE-060 | implemented | A person steers a running run from the run page | | SCN-CONSOLE-061 | implemented | A person takes over, hands back and cancels a run from the run page | | SCN-CONSOLE-062 | implemented | A person approves and rejects a run's pending gate from the run page | | SCN-CONSOLE-063 | implemented | A decision gatekeeper refuses shows gatekeeper's problem | | SCN-CONSOLE-064 | implemented | "console doctor --deep" calls each model route once and asks the harness to initialize | | SCN-CONSOLE-065 | implemented | "console doctor --deep" names a failing model route and a harness that answers "initialize" with an error | | SCN-CONSOLE-066 | implemented | "console doctor" without "--deep" calls no model | | SCN-CONSOLE-067 | implemented | "console doctor --deep" names the variables of the gateway and the harness when they are not set | | SCN-CONSOLE-068 | implemented | "console doctor --deep" names a gateway that does not answer and a harness that ends without a word | | SCN-CONSOLE-069 | implemented | The page "/health" calls no model | | SCN-CONSOLE-070 | implemented | The run page links each stage to its span in the run's trace | | SCN-CONSOLE-080 | implemented | The run page lists the actions of a run with the person who sent each | | SCN-CONSOLE-090 | implemented | The improvement page shows each scorer's pass rate and each run's cost | | SCN-CONSOLE-091 | implemented | Without a folder the improvement page answers 404 | | SCN-CONSOLE-092 | implemented | The improvement page reads only the records of ".json" and ".jsonl" files and skips other lines | | SCN-CONSOLE-100 | implemented | "console doctor --deep" sends one span to the collector and finds it in the trace store | | SCN-CONSOLE-101 | implemented | "console doctor --deep" names the path from the collector to the trace store when the span does not arrive | | SCN-CONSOLE-102 | implemented | "console doctor --deep" names CONSOLE_OTLP_URL when no collector answers there | | SCN-CONSOLE-110 | implemented | The run page links no span of a run whose foreman names no trace | | SCN-CONSOLE-120 | implemented | "console doctor --deep" asks the sandbox adapter to probe a denied host and finds it refused | | SCN-CONSOLE-121 | implemented | "console doctor --deep" names the egress proxy when a sandbox reaches the denied host | | SCN-CONSOLE-122 | implemented | "console doctor --deep" names the adapter's reason and the variables when no sandbox starts | | SCN-CONSOLE-123 | implemented | "console doctor --deep" without "CONSOLE_SANDBOX_COMMAND" skips the sandbox | | SCN-CONSOLE-130 | implemented | "console doctor --deep" publishes a ping on NATS and finds it at the foreman | | SCN-CONSOLE-131 | implemented | "console doctor --deep" names the foreman's consumer when the ping does not reach it in 10 seconds | | SCN-CONSOLE-132 | implemented | "console doctor --deep" names the stream when no stream takes the ping | | SCN-CONSOLE-133 | implemented | "console doctor --deep" names the variable when NATS does not answer | | SCN-CONSOLE-134 | implemented | "console doctor --deep" without "CONSOLE_NATS_URL" skips the events | | SCN-CONSOLE-135 | implemented | Every page allows only the console's own files and no script | | SCN-CONSOLE-136 | implemented | The page "/health" of a healthy factory answers 200 and has the navigation of every page | | SCN-CONSOLE-137 | implemented | A person on the access list signs in and lands on the runs | | SCN-CONSOLE-138 | implemented | A person who is not on the access list is refused and reads why | | SCN-CONSOLE-139 | implemented | An email that the provider did not verify is refused | | SCN-CONSOLE-140 | implemented | After the sign-in the person comes back only to a page of the console | | SCN-CONSOLE-141 | implemented | Sign out ends the session | | SCN-CONSOLE-142 | implemented | A session past its end asks to sign in again | | SCN-CONSOLE-143 | implemented | A session of the previous key stays valid after a second key is added | | SCN-CONSOLE-144 | implemented | A person removed from the access file loses access at the next page | | SCN-CONSOLE-145 | implemented | With the sign-in a form must come from the console's own address | | SCN-CONSOLE-146 | implemented | The probe "/v1/ready" answers without a session | | SCN-CONSOLE-147 | implemented | The console with the sign-in does not start without a setting of its provider | | SCN-CONSOLE-148 | implemented | A person signs in with GitHub | | SCN-CONSOLE-149 | implemented | The console with the sign-in does not start on an address that is neither loopback nor HTTPS | | SCN-CONSOLE-150 | implemented | The console does not start when it signs people in and a sign-in proxy names the person too | | SCN-CONSOLE-151 | implemented | Behind a sign-in proxy with an access file, a person who is not in it gets 403 | | SCN-CONSOLE-152 | implemented | Behind a sign-in proxy with an access file, a person in it sees the runs | | SCN-CONSOLE-153 | implemented | The sign-in page has the look of the design system, served from the console itself | | SCN-CONSOLE-154 | implemented | Without an access file nobody can sign in, and the page names the file | | SCN-CONSOLE-155 | implemented | The probe "/v1/ready" answers 503 when the console cannot read its factory | | SCN-CONSOLE-156 | implemented | With the sign-in a form without a session answers 401 | | SCN-CONSOLE-157 | implemented | A new user opens the runs page of the quick start in the look of the design system | | SCN-CONSOLE-158 | implemented | The navigation of a console without intake, an inbox or cost and scores links only the pages that work | | SCN-CONSOLE-159 | implemented | The navigation of a console with intake, an inbox and cost and scores links their pages too | | SCN-CONSOLE-160 | implemented | The link "Runs" counts the runs that wait for a person | | SCN-CONSOLE-161 | implemented | A person switches to the light theme and back, and stays on the page | | SCN-CONSOLE-162 | implemented | The shell has a skip link to the content and its landmarks | | SCN-CONSOLE-163 | implemented | A person signs in and lands on the runs in the shell (journey) | | SCN-CONSOLE-164 | implemented | On a phone the navigation folds away behind "Menu" (journey) | | SCN-CONSOLE-165 | implemented | A page without a session redirects to the sign-in and comes back to that page | | SCN-CONSOLE-166 | implemented | A session before its end stays valid | | SCN-CONSOLE-167 | implemented | "console doctor" with the sign-in refuses an access file whose only admin is the example | | SCN-CONSOLE-168 | implemented | "console doctor" with the sign-in names a provider that does not answer | | SCN-CONSOLE-169 | implemented | "console doctor" with the sign-in of GitHub asks no OpenID Connect provider and accepts an admin beside the example | | SCN-CONSOLE-170 | implemented | "console doctor" finds the discovery of an issuer that is set with a slash at its end | | SCN-CONSOLE-171 | implemented | "console doctor" with the sign-in names the problem of an access file without an admin | | SCN-CONSOLE-172 | implemented | The probe "/v1/ready" answers a health check that asks another host than the console's address | | SCN-CONSOLE-173 | implemented | An admin stores a value for a missing secret, and the list says "Set" with the person | | SCN-CONSOLE-174 | implemented | A declared secret without a value says "Missing" and names the agents that need it | | SCN-CONSOLE-175 | implemented | A stored secret that no agent declares says "Not used" and offers "Delete" | | SCN-CONSOLE-176 | implemented | The secrets of the providers "env" and "file" are listed as set outside the UI | | SCN-CONSOLE-177 | implemented | A member gets 403 for "/secrets" and its form, and only an admin's navigation links "Secrets" | | SCN-CONSOLE-178 | implemented | In the open mode "/secrets" and its form answer 404 | | SCN-CONSOLE-179 | implemented | Behind a sign-in proxy without an access file "/secrets" answers 404, and the navigation does not link it | | SCN-CONSOLE-180 | implemented | Without a store "/secrets" answers 404 and names the variable | | SCN-CONSOLE-181 | implemented | A stored value is in no output of the console | | SCN-CONSOLE-182 | implemented | A name that the store's adapter refuses answers 400 and names the problem | | SCN-CONSOLE-183 | implemented | A form over 64 KiB answers 413 and stores nothing | | SCN-CONSOLE-184 | implemented | "Delete" asks first on a page of its own, then removes the secret | | SCN-CONSOLE-185 | implemented | A store that does not answer is named on "/secrets", and its secrets say "Unknown" | | SCN-CONSOLE-186 | implemented | "console doctor" names each secret of OpenBao that the factory declares without a value | | SCN-CONSOLE-187 | implemented | "console doctor" names a store that does not answer | | SCN-CONSOLE-188 | implemented | "console doctor" and "/health" warn when the console's token for the store ends within 7 days | | SCN-CONSOLE-189 | implemented | An admin signs in and stores a secret (journey) | | SCN-CONSOLE-190 | implemented | An adapter whose output is no JSON is named on "/secrets", and its secrets say "Unknown" |