Developer-Dashboard

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

`${DDDC}` paths inside the YAML itself. Project-local isolated services are
discovered from that same
`./.developer-dashboard/config/docker/<service>/...` tree.

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.

If one resolved service comes from an installed skill docker root, the
resolver also loads that skill's `<skill-root>/.env` file into the compose
environment before docker-config, addon, and mode env overlays are applied. Only skills whose compose service files actually participate are included,
disabled skills are skipped, and `<skill-root>/.env.pl` is not executed from
this compose path.

Nested skill services expand their env chain from the root
nested skill to the participating leaf service, preserving overwritten parent
keys under cumulative aliases such as `foo_VERSION` and
`foo_bar_VERSION` before the leaf value becomes the plain key. The resolver
also exports one skill-specific `<skill-name>_DDDC` variable for each
participating skill, using the leaf skill name with non-identifier characters
normalised to underscores and pointing that variable at the owning
`config/docker/` root.

Nested skill services additionally export the full
cumulative skill path alias such as `foo_bar_zzz_DDDC` for the same compose
root, while the leaf alias stays available as `zzz_DDDC`.

When one or more Compose services are explicitly selected, skill env files
are resolved only for those selected services during interpolation; unrelated
skill service folders cannot replace a selected service's same-named env key
merely because they are enumerated later. If no selected service is present
in the effective base config, or the operation selects no service, env files
for all effective base services are used.

This keeps sequential commands such
as `d2 docker compose up foo` and `d2 docker compose up bar` scoped to the
matching skill env while preserving all-service operations. 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.

### Publishing Docker Hub images

The repository's master-push workflow publishes a runnable Docker image from
the latest `master` checkout. It first runs the repository `install.sh` to
install the platform tools and Perl environment, then builds a fresh
Dist::Zilla archive from that checked-out source and installs that archive
into the image with `cpanm --notest`.

This final local-archive install deliberately
replaces the version that the bootstrap installer may have fetched from
MetaCPAN, so the image contains the current repository code.

The regular container/release gates run the full test and coverage suites
before publishing. The multi-platform image build skips rerunning those suites
under QEMU for each target architecture; the tarball itself is still installed
and its install is verified separately in a blank Docker environment.

The workflow selects the GitHub Actions environment `release`, whose
secrets `DOCKER_HUB_USER` and `DOCKER_HUB_TOKEN` provide the Docker Hub
credentials. It pushes two tags under `DOCKER_HUB_USER/developer-dashboard`:
the distribution version read from `dist.ini` (for example `5.88`) and
`latest`.

A manual workflow dispatch also builds the current `master`
branch. The image targets `linux/amd64` and
`linux/arm64`; Docker does not support a macOS-kernel image platform, while
Apple Silicon can run the Linux arm64 image using Docker Desktop's Linux VM.

The Ubuntu Linux base image is pinned by its multi-platform manifest digest,
and every GitHub Action dependency is pinned to a full commit SHA.

For example, pull the moving tag or a pinned version:

    docker pull YOUR_DOCKER_HUB_USER/developer-dashboard:latest
    docker pull YOUR_DOCKER_HUB_USER/developer-dashboard:5.88

The container entrypoint is `d2`. The versioned tag is intended for selecting
a known image build; `latest` follows successful master builds.

## Prompt Integration

Render prompt text directly:

    dashboard ps1 --jobs 2

`dashboard ps1` now follows the original `~/bin/ps1` shape more closely: a
`(YYYY-MM-DD HH:MM:SS)` timestamp prefix, dashboard status and workspace info, a
bracketed working directory, an optional jobs suffix, and a trailing
`🌿branch` marker when git metadata is available. The prompt helper reads the
branch directly from on-disk git metadata instead of shelling out to
`git branch`, so repeated prompt renders stay lightweight on slower hosts such
as iSH.

Working-directory lookup uses `Cwd::getcwd` for the ordinary physical
path; it preserves a distinct logical `$PWD` only after confirming that both
paths identify the same directory.

If the workspace workflow seeded
`WORKSPACE_REF` or the older
`TICKET_REF` into the current tmux session, `dashboard ps1` also reads that
context from tmux when the shell environment does not already export it, but it
skips that tmux probe entirely when the shell is not inside tmux. The branch marker preserves every component of a local branch such as
`team/feature`.

If HEAD is detached at a commit that matches an
`origin` remote-tracking ref, the marker shows `origin/team/feature` unless
the same-named local branch points to the same commit; only then is the
redundant `origin/` prefix omitted.

Packed and loose refs are both recognised,
while symlinked refs are not followed. When no matching remote ref exists, it
keeps the short commit id. All generated shell adapters
call this same `dashboard ps1` renderer, so the branch label is consistent in
Bash, Zsh, sh, and PowerShell.

Generate shell bootstrap:

    dashboard shell bash
    dashboard shell zsh
    dashboard shell sh

README.md  view on Meta::CPAN

       "/java" : "/app/learn.ai",
       "/v1/status" : {
          "to" : "/ajax/status",
          "type" : "json"
       },
       "/hello/world" : "/app/hello/world",
       "/main.css" : "/css/hello/world.css",
       "/hey.js" : "/js/hey/how/are/you.js",
       "/what/are/you" : "/others/hello/world/you.html"
    }

When that file is present, skill pages emit the declared canonical
`ajax` path such as `/v1/status` instead of the default `/ajax/...` url,
and runtime-level aliases such as `/java` can point at normal saved bookmark
ids such as `/app/learn.ai` without treating the dot as skill notation. The
same manifest also makes the declared custom `/app`, `/js`, `/css`, and
`/others` paths requestable.

The smart longest-prefix routes remain the
parent resolvers:
`/app/example-skill/...`, `/ajax/example-skill/...`,
`/js/example-skill/...`, `/css/example-skill/...`, and
`/others/example-skill/...` are always checked first, and any declared custom
path is checked only after the normal smart route misses.

Runtime-level custom
paths from the active `config/routes.json` layer chain follow the same
fallback rule against the built-in `/app`, `/ajax`, `/js`, `/css`, and
`/others` route handlers. If neither the smart route nor the custom path
resolves, the request falls through to the normal `404` response.

Ajax custom
routes default to `json` when no explicit `type` is present, and the
optional `type` value can also be `html`,
`text`, or an arbitrary raw mime type such as
`application/vnd.example+json`. Those saved Ajax handlers run the stored file
as a real process, defaulting to Perl unless the file starts with a shebang,
and stream both `stdout` and `stderr` back to the browser as they happen.

That keeps bookmark Ajax workflows usable even while transient token URLs stay
disabled by default, and it means bookmark Ajax code can rely on normal
`print`, `warn`, `die`, `system`, and `exec` process behaviour instead of
a buffered JSON wrapper. The same layered runtime config chain and installed-skill config trees can now
ship `config/api.json` files that authorise selected `/ajax/...` routes for
machine-to-machine callers without forcing a helper login form.

The schema is a
JSON object keyed by API client name.

Each entry must provide a stored SHA-256
hex digest under `secret` plus an `ajax` array of exact saved Ajax route
paths such as `/ajax/stream.txt` or
`/ajax/example-skill/status.json`. When a non-admin remote request targets one
of those registered `/ajax/...` paths, the caller can send
`X-DD-API-Key: NAME` and `X-DD-API-Secret: RAW-SECRET`.

Developer Dashboard
hashes the raw secret with SHA-256, compares it to the stored digest, and
executes the saved Ajax handler when they match.

Missing or wrong credentials
for a registered API route return `403` with the JSON body
`{"status":"forbidden"}`.

Existing helper-session auth still works on the
same saved Ajax routes, so browser workflows and machine callers can coexist on
one handler without adding a second copy of the route. Like the rest of
`DD-OOP-LAYERS`, runtime `config/api.json` files merge from home to the
deepest active child layer, and installed skills contribute their own layered
`config/api.json` fragments for skill-local saved Ajax routes.

The built-in
`dashboard api` command is the supported way to inspect or update the writable
runtime layer for that registry.

An entry carrying `"disabled": true` (or the same flag under `_disabled`)
is dropped from the registry; a JSON literal `false` there keeps the key
visible, including values such as `"disabled": 0` or `"disabled": "no"`.

Saved bookmark Ajax handlers default to `text/plain` when no explicit
`type => ...` argument is supplied, and the generated Perl wrapper now
enables autoflush on both `STDOUT` and `STDERR` so long-running handlers
show incremental output in the browser instead of stalling behind process
buffers.

If a saved handler also needs refresh-safe process reuse, pass
`singleton => 'NAME'` in the `Ajax` helper.

The generated URL carries the singleton name, and the worker runs as
`dashboard ajax: NAME`. A refreshed request replaces the older matching
worker.

`dashboard stop` and `dashboard restart` also stop singleton Ajax workers.
Closing a browser tab sends a `pagehide` cleanup request to
`/ajax/singleton/stop?singleton=NAME`.

If `code => ...` is omitted, `Ajax(file => 'name')` targets the
existing executable at `dashboards/ajax/name` instead of rewriting it. Static files referenced by saved bookmarks are resolved from the effective
runtime public tree first and then from the saved bookmark root.

The web layer
also provides a built-in bundled `/js/jquery.js` asset that serves the local
copy of jQuery 4.0.0, with `/js/jquery-4.0.0.min.js` kept as a compatibility
alias for the same shipped payload even when no runtime file has been copied
into `dashboard/public/js` yet.

The bundled browser tab icon works the same
way: browsers request `/favicon.ico` on their own for every page load, so that
route is served outside the authorisation gate, on every trust tier and before
any helper login, from the bundled 16x16 icon. Dropping your own
`dashboard/public/others/favicon.ico` into any runtime layer overrides it.

Skills can ship the same classes of assets
under their own dashboard tree: `dashboards/ajax/*` resolves at
`/ajax/<repo-name>/...` or
`/ajax/<repo-name>/<sub-skill>/...`, and
`dashboards/public/js/*`, `dashboards/public/css/*`, and
`dashboards/public/others/*` resolve at
`/js/<repo-name>/...`, `/css/<repo-name>/...`, and
`/others/<repo-name>/...` with the same nested-skill extension,
for example `/js/<repo-name>/<sub-skill>/path/file.js`.



( run in 1.194 second using v1.01-cache-2.11-cpan-3f962061493 )