Developer-Dashboard

 view release on metacpan or  search on metacpan

lib/Developer/Dashboard.pm  view on Meta::CPAN

        "mode": "multiple",
        "multiple": 3,
        "rotation": {
          "lines": 100,
          "days": 1
        },
        "indicator": {
          "name": "foobar.indicator",
          "label": "Foobar",
          "icon": "[% a %]"
        }
      }
    ]
  }

Collector concurrency defaults are explicit:

=over 4

=item *

When C<mode> is omitted, the collector runs in C<singleton> mode.

=item *

In C<singleton> mode, the scheduler skips a due run while an older run of the
same collector is still active.

=item *

In C<multiple> mode, the scheduler still starts due runs while older runs are
active, but only until C<multiple> active runs are already in flight.

=item *

When C<mode> is C<multiple> and C<multiple> is omitted, the runtime uses
C<2>.

=item *

Collectors whose C<command> re-enters C<dashboard> or C<d2> through a shell
path now have a safety floor of C<30> seconds even if config asks for a
smaller interval, because those recursive dashboard collectors are materially
heavier than direct shell probes. Set C<allow_fast_poll> or
C<allow_fast_dashboard_poll> on that collector, or set
C<DEVELOPER_DASHBOARD_MIN_DASHBOARD_COMMAND_INTERVAL_SECONDS>, when the faster
cadence is intentional and understood.

=item *

When a collector sets C<disable =E<gt> 1> or C<"disable": true>, dashboard
will not start that collector, explicit named starts reject it, and any
already-running managed loop for that collector is stopped during the next
collector lifecycle action. Managed indicator state for that collector is
also removed instead of lingering as if it were still active.

=item *

Stopping a singleton collector loop also terminates the long-running command
currently owned by that loop, so C<dashboard stop collector foo> does not leave
the old worker command alive behind the stopped dispatcher.

=back

Collector indicators follow the collector exit code automatically: C<0>
stores an C<ok> indicator state and any non-zero exit code stores C<error>.
When C<indicator.name> is omitted, the collector name is reused
automatically. When C<indicator.label> is omitted, it defaults to that same
name. Configured collector indicators are seeded immediately, so prompt and
page status strips show them before the first collector run. Before a
collector has produced real output it appears as missing. Prompt output
renders an explicit status glyph in front of the collector icon, so
successful checks show fragments such as C<✅🔑> while failing or not-yet-run
checks show fragments such as C<🚨🔑>.
Under C<DD-OOP-LAYERS>, a deeper child layer no longer pins an inherited
collector indicator at that default C<missing> placeholder just because the
child runtime has its own F<.developer-dashboard/> folder. If the child layer
does not override that collector config and only has the placeholder state,
dashboard now falls back to the nearest inherited real collector state such
as a parent-layer C<ok> result instead of turning the same indicator red.
The top-right browser status strip now uses that same configured icon instead
of falling back to the collector name, and stale managed indicators are
removed automatically if the collector config is renamed. The browser chrome
now uses an emoji-capable font stack there as well, so UTF-8 icons such as
C<🐳> and C<💰> remain visible instead of collapsing into fallback boxes.
For TT-backed collector icons, a collector such as C<./foobar> can print
C<{"a":123}> on C<stdout>; the runner decodes that JSON into Perl data and
renders C<[% a %]> into the live icon C<123>. Later config-sync passes keep
the configured C<icon_template> metadata and the already-rendered live
C<icon>, so commands such as C<dashboard indicator list> and C<dashboard ps1>
do not revert the persisted icon back to raw C<[% ... %]> text between runs.
The blank-environment integration flow also keeps a regression for mixed
collector health: one intentionally broken Perl collector must stay red
without stopping a second healthy collector from staying green in
C<dashboard indicator list>, C<dashboard ps1>, and C</system/status>.

=head2 Docker Compose

Inspect the resolved compose stack without running Docker:

  dashboard docker compose --dry-run config

Include addons or modes:

  dashboard docker compose --addon mailhog --mode dev up -d
  dashboard docker compose config green
  dashboard docker compose config
  dashboard docker list
  dashboard docker list --disabled
  dashboard docker list --enabled
  dashboard docker disable green
  dashboard docker enable green

The resolver also supports isolated service folders without adding entries to
dashboard JSON config. If
C<./.developer-dashboard/config/docker/green/compose.yml> exists in the current
project it wins; otherwise the resolver falls back to
C<~/.developer-dashboard/config/docker/green/compose.yml>.
C<dashboard docker compose config green> or
C<dashboard docker compose up green> will pick it up automatically by
inferring service names from the passthrough compose args before the real

lib/Developer/Dashboard.pm  view on Meta::CPAN

list, and C<which_dir> prints the same selected target or match list without
changing directory. Bash still uses C<\j> for job counts, zsh refreshes
The shell-smoke regression coverage also compares those printed paths by
canonical identity, so macOS C</var/...> and C</private/var/...> aliases do
not fail equivalent C<pwd> / C<which_dir> checks. Bash still uses C<\j> for
job counts, zsh refreshes
C<PS1> through a C<precmd> hook with C<${#jobstates}>, POSIX C<sh> falls back
to a prompt command that does not depend on bash-only prompt escapes, and
PowerShell installs a C<prompt> function instead of using the POSIX C<PS1>
variable.

C<d2> is a real, installed short command for C<dashboard>, shipped in the same
C<bin> directory and re-execing the C<dashboard> entrypoint, so you can run
C<d2 version>, C<d2 doctor>, or C<d2 docker compose ps> without typing the full
command name -- in scripts and fresh shells as well as interactively, without
depending on the shell bootstrap.

The same generated bootstrap also wires live tab completion for C<dashboard>
and C<d2>. Bash registers C<_dashboard_complete>, zsh registers
C<_dashboard_complete_zsh>, and PowerShell registers
C<Register-ArgumentCompleter> for both command names. Completion candidates
come from the live runtime instead of a hardcoded shell list, so built-in
commands, layered custom CLI commands, and installed dotted skill commands
all show up in suggestions. For bash, the generated helper captures
completion payloads first instead of relying on process substitution, which
keeps completion responsive on macOS and inside packaged install-test shells.
The generated bootstrap also wires C<cdr>,
C<dd_cdr>, and C<which_dir> completion. The first argument suggests saved
aliases plus matching directory names beneath the current directory, and later
arguments suggest matching directory basenames beneath the resolved alias root
or current directory without crashing when one subtree is not readable.

For the POSIX shell bootstrap, the generated helper now decodes its JSON
payloads through the same Perl interpreter that generated the shell fragment
instead of a bare C<perl -MJSON::XS ...> call. That keeps C<cdr> and
C<which_dir> stable on macOS installs where C</usr/bin/perl> and a user-local
C<~/perl5> XS stack do not belong to the same Perl build. The generated
C<d2> shortcut re-enters the C<dashboard> script directly instead of
hardcoding the current Perl binary path, so the shortcut still works when the
bootstrap is loaded by a shell whose preferred Perl lives somewhere else.

On Windows, C<dashboard shell> auto-selects PowerShell by default, and
interpreter-backed runtime entrypoints such as collector C<command> strings,
trusted command actions, saved Ajax files, custom CLI commands, hook files,
and update scripts now resolve C<.ps1>, C<.cmd>, C<.bat>, and C<.pl>
runners without assuming C<sh> or C<bash>. That keeps Strawberry Perl installs
usable without requiring a Unix shell just to load the dashboard runtime.
The Windows command launcher also normalizes extensionless local C<cmd> shims
back to C<cmd.exe> so Linux, WSL, and packaging hosts that happen to expose a
helper named C<cmd> do not break the expected Windows C<.cmd> and C<.bat>
dispatch contract during cross-platform tests or tarball installs.

The repository-only Windows verification assets follow the same layered
approach: fast forced-Windows unit coverage in C<t/>, a real Strawberry Perl
host smoke in the source checkout, and a host-side rerun helper that delegates
to the QEMU launcher for release-grade Windows compatibility claims. The
supported baseline on Windows is PowerShell plus Strawberry Perl. Git Bash is
optional. Scoop is optional. They are setup helpers, not runtime requirements
for the installed C<dashboard> command. In the Dockur-backed path, the launcher
stages the Strawberry Perl MSI from the Linux host into the OEM bundle and can
keep multiple retained Windows guests alive on configurable host web/RDP ports
while it reruns the same smoke.

=head2 Browser Access Model

The browser security model follows the original local-first trust concept:

=over 4

=item *

requests from loopback with a loopback host, such as C<127.0.0.1>, C<::1>, or C<localhost>, are treated as local admin

=item *

requests from loopback with a hostname listed under C<web.ssl_subject_alt_names> are also treated as local admin

=item *

requests from non-loopback IPs are treated as helper access

=item *

outsider requests return C<401> without a login page until at least one helper user exists

=item *

after a helper user exists, outsider requests receive the helper login page

=item *

helper access requires a login backed by local file-based user and session records

=item *

helper sessions are file-backed, bound to the originating remote address, and expire automatically

=item *

helper passwords must be at least 8 characters long

=item *

state-changing requests are refused with an empty-bodied C<403> when C<Origin>
or C<Referer> names anything other than this dashboard or a trusted local
alias, on every tier

=item *

requests the browser labelled C<Sec-Fetch-Site: cross-site> or C<same-site> are
refused on B<every> method, including C<GET>, unless the accompanying
C<Origin>/C<Referer> names this dashboard or a trusted local alias

=back

This keeps the fast path for loopback-local access while making non-loopback or shared access explicit.

The two cross-site checks above sit at one choke point that runs before trust
tier classification, because the loopback-admin tier authorizes on the remote
address alone: there is no cookie in that decision, so C<SameSite=Strict>
protects nothing there and any page the operator visits would otherwise reach



( run in 1.520 second using v1.01-cache-2.11-cpan-14f38c9f855 )