NAME

Developer::Dashboard - a local home for development work

VERSION

0.66

INTRODUCTION

Developer::Dashboard gives a developer one place to organize the moving parts of day-to-day work.

It brings together browser pages, saved notes, helper actions, collectors, prompt indicators, path aliases, open-file shortcuts, data query tools, and Docker Compose helpers so local development can stay centered around one consistent home instead of a pile of disconnected scripts and tabs.

Release tarballs contain installable runtime artifacts only; local Dist::Zilla release-builder configuration is kept out of the shipped archive. Frequently used built-in commands such as of, open-file, pjq, pyq, ptomq, and pjp are also installed as standalone executables so they can run directly without loading the full dashboard runtime. Before publishing a release, the built tarball should be smoke-tested with cpanm from the artifact itself so the shipped archive matches the fixed source tree.

It provides a small ecosystem for:

  • saved and transient dashboard pages built from the original bookmark-file shape

  • legacy bookmark syntax compatibility using the original :--------------------------------------------------------------------------------: separator plus directives such as TITLE:, STASH:, HTML:, FORM.TT:, FORM:, and CODE1:

  • Template Toolkit rendering for HTML: and FORM.TT:, with access to stash, ENV, and SYSTEM

  • legacy CODE* execution with captured STDOUT rendered into the page and captured STDERR rendered as visible errors

  • legacy-style per-page sandpit isolation so one bookmark run can share runtime variables across CODE* blocks without leaking them into later page runs

  • old-style root editor behavior with a free-form bookmark textarea when no path is provided

  • file-backed collectors and indicators

  • prompt rendering for PS1

  • project/path discovery helpers

  • a lightweight local web interface

  • action execution with trusted and safer page boundaries

  • plugin-loaded providers, path aliases, and compose overlays

  • update scripts and release packaging for CPAN distribution

Developer Dashboard is meant to become the developer's working home:

  • a local dashboard page that can hold links, notes, forms, actions, and rendered output

  • a prompt layer that shows live status for the things you care about

  • a command surface for opening files, jumping to known paths, querying data, and running repeatable local tasks

  • a configurable runtime that can adapt to each codebase without losing one familiar entrypoint

Project-specific behavior is added through configuration, startup collector definitions, saved pages, and optional plugins.

DOCUMENTATION

Main Concepts

Environment Variables

The distribution supports these compatibility-style customization variables:

  • DEVELOPER_DASHBOARD_BOOKMARKS

    Override the saved page root.

  • DEVELOPER_DASHBOARD_CHECKERS

    Filter enabled collector/checker names.

  • DEVELOPER_DASHBOARD_CONFIGS

    Override the config root.

  • DEVELOPER_DASHBOARD_STARTUP

    Override the startup collector-definition root.

User CLI Extensions

Unknown top-level subcommands can be provided by executable files under ~/.developer-dashboard/cli. For example, dashboard foobar a b will exec ~/.developer-dashboard/cli/foobar with a b as argv, while preserving stdin, stdout, and stderr.

Open File Commands

dashboard of is the shorthand name for dashboard open-file.

These commands support:

  • direct file paths

  • file:line references

  • Perl module names such as My::Module

  • Java class names such as com.example.App

  • recursive pattern searches inside a resolved directory alias or path

If VISUAL or EDITOR is set, dashboard of and dashboard open-file will exec that editor unless --print is used.

Data Query Commands

These built-in commands parse structured text and optionally extract a dotted path:

  • dashboard pjq [path] [file] for JSON

  • dashboard pyq [path] [file] for YAML

  • dashboard ptomq [path] [file] for TOML

  • dashboard pjp [path] [file] for Java properties

If the selected value is a hash or array, the command prints canonical JSON. If the selected value is a scalar, it prints the scalar plus a trailing newline.

The file path and query path are order-independent, and $d selects the whole parsed document. For example, cat file.json | dashboard pjq '$d' and dashboard pjq file.json '$d' return the same result. The same contract applies to pyq, ptomq, and pjp.

MANUAL

Installation

Install from CPAN with:

cpanm Developer::Dashboard

Or install from a checkout with:

perl Makefile.PL
make
make test
make install

Local Development

Build the distribution:

rm -f Developer-Dashboard-*.tar.gz
dzil build

Run the CLI directly from the repository:

perl -Ilib bin/dashboard init
perl -Ilib bin/dashboard auth add-user <username> <password>
perl -Ilib bin/dashboard of --print My::Module
perl -Ilib bin/dashboard open-file --print com.example.App
printf '{"alpha":{"beta":2}}' | perl -Ilib bin/dashboard pjq alpha.beta
printf 'alpha:\n  beta: 3\n' | perl -Ilib bin/dashboard pyq alpha.beta
perl -Ilib bin/dashboard update
perl -Ilib bin/dashboard serve
perl -Ilib bin/dashboard stop
perl -Ilib bin/dashboard restart

User CLI extensions can be tested from the repository too:

mkdir -p ~/.developer-dashboard/cli
printf '#!/bin/sh\ncat\n' > ~/.developer-dashboard/cli/foobar
chmod +x ~/.developer-dashboard/cli/foobar
printf 'hello\n' | perl -Ilib bin/dashboard foobar

First Run

Initialize the runtime:

dashboard init

Inspect resolved paths:

dashboard paths
dashboard path resolve bookmarks_root
dashboard path add foobar /tmp/foobar
dashboard path del foobar

Custom path aliases are stored in the global dashboard config so shell helpers such as cdr foobar and which_dir foobar keep working across sessions. When an alias points inside the current home directory, the stored config uses $HOME/... instead of a hard-coded absolute home path so a shared ~/.developer-dashboard tree remains portable across different developer accounts. Re-adding an existing alias updates it without error, and deleting a missing alias is also safe.

Render shell bootstrap:

dashboard shell bash

Resolve or open files from the CLI:

dashboard of --print My::Module
dashboard open-file --print com.example.App
dashboard open-file --print path/to/file.txt
dashboard open-file --print bookmarks welcome

Query structured files from the CLI:

printf '{"alpha":{"beta":2}}' | dashboard pjq alpha.beta
printf 'alpha:\n  beta: 3\n' | dashboard pyq alpha.beta
printf '[alpha]\nbeta = 4\n' | dashboard ptomq alpha.beta
printf 'alpha.beta=5\n' | dashboard pjp alpha.beta
dashboard pjq file.json '$d'

Start the local app:

dashboard serve

Open the root path with no bookmark path to get the free-form bookmark editor directly.

Stop the local app and collector loops:

dashboard stop

Restart the local app and configured collector loops:

dashboard restart

Create a helper login user:

dashboard auth add-user <username> <password>

Remove a helper login user:

dashboard auth remove-user helper

Helper sessions show a Logout link in the page chrome. Logging out removes both the helper session and that helper account. Helper page views also show the helper username in the top-right chrome instead of the local system account. Exact-loopback admin requests do not show a Logout link.

Working With Pages

Create a starter page document:

dashboard page new sample "Sample Page"

Save a page:

dashboard page new sample "Sample Page" | dashboard page save sample

List saved pages:

dashboard page list

Render a saved page:

dashboard page render sample

Encode and decode transient pages:

dashboard page show sample | dashboard page encode
dashboard page show sample | dashboard page encode | dashboard page decode

Run a page action:

dashboard action run system-status paths

Bookmark documents use the original separator-line format with directive headers such as TITLE:, STASH:, HTML:, FORM.TT:, FORM:, and CODE1:.

Posting a bookmark document with BOOKMARK: some-id back through the root editor now saves it to the bookmark store so /app/some-id resolves it immediately.

The browser editor highlights directive sections, HTML, CSS, JavaScript, and Perl CODE* content directly inside the editing surface rather than in a separate preview pane.

Edit and source views preserve raw Template Toolkit placeholders inside HTML: and FORM.TT: sections, so values such as [% title %] are kept in the bookmark source instead of being rewritten to rendered HTML after a browser save.

Page TITLE: values only populate the HTML <title> element. If a bookmark should show its title in the page body, add it explicitly inside HTML:, for example with [% title %].

/apps redirects to /app/index, and /app/<name> can load either a saved bookmark document or a saved ajax/url bookmark file.

Working With Collectors

Initialize example collector config:

dashboard config init

Run a collector once:

dashboard collector run example.collector

List collector status:

dashboard collector list

Collector jobs support two execution fields:

  • command runs a shell command string through sh -c

  • code runs Perl code directly inside the collector runtime

Example collector definitions:

{
  "collectors": [
    {
      "name": "shell.example",
      "command": "printf 'shell collector\n'",
      "cwd": "home",
      "interval": 60
    },
    {
      "name": "perl.example",
      "code": "print qq(perl collector\n); return 0;",
      "cwd": "home",
      "interval": 60,
      "indicator": {
        "icon": "P"
      }
    }
  ]
}

Collector indicators follow the collector exit code automatically: 0 stores an ok indicator state and any non-zero exit code stores error. When indicator.name is omitted, the collector name is reused automatically. When 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 ✅🔑 while failing or not-yet-run checks show fragments such as 🚨🔑.

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

The resolver also supports old-style isolated service folders without adding entries to dashboard JSON config. If ~/.developer-dashboard/config/docker/green/compose.yml exists, dashboard docker compose config green or dashboard docker compose up green will pick it up automatically by inferring service names from the passthrough compose args before the real docker compose command is assembled. If no service name is passed, the resolver scans isolated service folders and preloads every non-disabled folder. If a folder contains disabled.yml it is skipped. Each isolated folder contributes development.compose.yml when present, otherwise compose.yml.

During compose execution the dashboard exports DDDC as ~/.developer-dashboard/config/docker, so compose YAML can keep using ${DDDC} paths inside the YAML itself. Wrapper flags such as --service, --addon, --mode, --project, and --dry-run are consumed first, and all remaining docker compose flags such as -d and --build pass straight through to the real docker compose command. When --dry-run is omitted, the dashboard hands off with exec so the terminal sees the normal streaming output from docker compose itself instead of a dashboard JSON wrapper.

Prompt Integration

Render prompt text directly:

dashboard ps1 --jobs 2

Generate bash bootstrap:

dashboard shell bash

Browser Access Model

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

  • requests from exact 127.0.0.1 with a numeric Host of 127.0.0.1 are treated as local admin

  • requests from other IPs or from hostnames such as localhost are treated as helper access

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

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

  • helper passwords must be at least 8 characters long

This keeps the fast path for exact loopback access while making non-canonical or remote access explicit.

The editor and rendered pages also include a shared top chrome with share and source links on the left and the original status-plus-alias indicator strip on the right, refreshed from /system/status. That top-right area also includes the local username, the current host or IP link, and the current date/time in the same spirit as the old local dashboard chrome. The displayed address is discovered from the machine interfaces, preferring a VPN-style address when one is active, and the date/time is refreshed in the browser with JavaScript. The bookmark editor also follows the old auto-submit flow, so the form submits when the textarea changes and loses focus instead of showing a manual update button.

The default web bind is 0.0.0.0:7890. Trust is still decided from the request origin and host header, not from the listen address.

Runtime Lifecycle

The runtime manager follows the legacy local-service pattern:

  • dashboard serve starts the web service in the background by default

  • dashboard serve --foreground keeps the web service attached to the terminal

  • dashboard stop stops both the web service and managed collector loops

  • dashboard restart stops both, starts configured collector loops again, then starts the web service

  • web shutdown and duplicate detection do not trust pid files alone; they validate managed processes by environment marker or process title and use a pkill-style scan fallback when needed

Environment Customization

After installing with cpanm, the runtime can be customized with these environment variables:

  • DEVELOPER_DASHBOARD_BOOKMARKS

    Overrides the saved page or bookmark directory.

  • DEVELOPER_DASHBOARD_CHECKERS

    Limits enabled collector or checker jobs to a colon-separated list of names.

  • DEVELOPER_DASHBOARD_CONFIGS

    Overrides the config directory.

  • DEVELOPER_DASHBOARD_STARTUP

    Overrides the startup collector-definition directory.

Startup collector definitions are read from *.json files in DEVELOPER_DASHBOARD_STARTUP. A startup file may contain either a single collector object or an array of collector objects.

Testing And Coverage

Run the test suite:

prove -lr t

Measure library coverage with Devel::Cover:

cpanm --local-lib-contained ./.perl5 Devel::Cover
export PERL5LIB="$PWD/.perl5/lib/perl5${PERL5LIB:+:$PERL5LIB}"
export PATH="$PWD/.perl5/bin:$PATH"
cover -delete
HARNESS_PERL_SWITCHES=-MDevel::Cover prove -lr t
cover -report text -select_re '^lib/' -coverage statement -coverage subroutine

The repository target is 100% statement and subroutine coverage for lib/.

The coverage-closure suite includes managed collector loop start/stop paths under Devel::Cover, including wrapped fork coverage in t/14-coverage-closure-extra.t, so the covered run stays green without breaking TAP from daemon-style child processes.

Updating Runtime State

Run the ordered update pipeline:

dashboard update

This performs runtime bootstrap, dependency refresh, shell bootstrap generation, and collector restart orchestration.

Blank Environment Integration

Run the host-built tarball integration flow with:

integration/blank-env/run-host-integration.sh

This integration path builds the distribution tarball on the host with dzil build, starts a blank container with only that tarball mounted into it, installs the tarball with cpanm, and then exercises the installed dashboard command inside the clean Perl container.

Before uploading a release artifact, remove older tarballs first so only the current release artifact remains, then validate the exact tarball that will ship:

rm -f Developer-Dashboard-*.tar.gz
dzil build
tar -tzf Developer-Dashboard-0.58.tar.gz | grep run-host-integration.sh
cpanm /tmp/Developer-Dashboard-0.58.tar.gz -v

The harness also:

- creates a fake project wired through DEVELOPER_DASHBOARD_BOOKMARKS, DEVELOPER_DASHBOARD_CONFIGS, and DEVELOPER_DASHBOARD_STARTUP - verifies the installed CLI works against that fake project through the mounted tarball install - extracts the same tarball inside the container so dashboard update runs from artifact contents instead of the live repo - starts the installed web service - uses headless Chromium to verify the root editor, a saved fake-project bookmark page from the fake project bookmark directory, and the helper login page - verifies helper logout cleanup and runtime restart and stop behavior

FAQ

Is this tied to a specific company or codebase?

No. The core distribution is intended to be reusable for any project.

Where should project-specific behavior live?

In configuration, startup collector definitions, saved pages, and optional extensions. The core should stay generic.

Is the software spec implemented?

The current distribution implements the core runtime, page engine, action runner, plugin/provider loader, prompt and collector system, web lifecycle manager, and Docker Compose resolver described by the software spec.

What remains intentionally lightweight is breadth, not architecture:

- plugin packs are JSON-based rather than a larger CPAN plugin API - provider pages and action handlers are implemented in a compact v1 form - legacy bookmarks are supported, with Template Toolkit rendering and one clean sandpit package per page run so CODE* blocks can share state within a bookmark render without leaking runtime globals into later requests

Does it require a web framework?

No. The current distribution includes a minimal HTTP layer implemented with core Perl-oriented modules.

Why does localhost still require login?

This is intentional. The trust rule is exact and conservative: only numeric loopback on 127.0.0.1 receives local-admin treatment.

Why is the runtime file-backed?

Because prompt rendering, dashboards, and wrappers should consume prepared state quickly instead of re-running expensive checks inline.

How are CPAN releases built?

The repository is set up to build release artifacts with Dist::Zilla and upload them to PAUSE from GitHub Actions.

What JSON implementation does the project use?

The project uses JSON::XS for JSON encoding and decoding, including shell helper decoding paths.

What does the project use for command capture and HTTP clients?

The project uses Capture::Tiny for command-output capture via capture, with exit codes returned from the capture block rather than read separately. There is currently no outbound HTTP client in the core runtime, so LWP::UserAgent is not yet required by an active code path.

SEE ALSO

Developer::Dashboard::PathRegistry, Developer::Dashboard::PageStore, Developer::Dashboard::CollectorRunner, Developer::Dashboard::Prompt

AUTHOR

Developer Dashboard Contributors

LICENSE

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 516:

Non-ASCII character seen before =encoding in 'C<✅🔑>'. Assuming UTF-8