Developer-Dashboard
view release on metacpan or search on metacpan
lib/Developer/Dashboard.pm view on Meta::CPAN
same dashboard ecosystem instead of in separate wrapper scripts.
=back
=head2 Environment Variables
The distribution supports these compatibility-style customization variables:
=over 4
=item * C<DEVELOPER_DASHBOARD_BOOKMARKS>
Override the saved page root.
=item * C<DEVELOPER_DASHBOARD_CHECKERS>
Filter enabled collector/checker names.
=item * C<DEVELOPER_DASHBOARD_CONFIGS>
Override the config root.
=item * C<DEVELOPER_DASHBOARD_ALLOW_TRANSIENT_URLS>
Allow browser execution of transient C</?token=...>, C</action?atoken=...>,
and older C</ajax?token=...> payloads. The default is off, so the web UI only
executes saved bookmark files unless this is set to a truthy value such as
C<1>, C<true>, C<yes>, or C<on>.
=back
=head2 Transient Web Token Policy
Transient page tokens still exist for CLI workflows such as C<dashboard page encode>
and C<dashboard page decode>, but browser routes that execute a transient payload
from C<token=> or C<atoken=> are disabled by default.
That means links such as:
=over 4
=item * C<http://127.0.0.1:7890/?token=...>
=item * C<http://127.0.0.1:7890/action?atoken=...>
=item * C<http://127.0.0.1:7890/ajax?token=...>
=back
return a C<403> unless C<DEVELOPER_DASHBOARD_ALLOW_TRANSIENT_URLS> is enabled.
Saved bookmark-file routes such as C</app/index> and
C</app/index/action/...> continue to work without that flag. Saved bookmark
editor pages also stay on their named C</app/E<lt>idE<gt>/edit> and
C</app/E<lt>idE<gt>> routes when you save from the browser, so editing an
existing bookmark file does not fall back to transient C<token=> URLs under the
default deny policy.
C<Ajax> helper calls inside saved bookmark C<CODE*> blocks should use
an explicit C<file =E<gt> 'name.json'> argument. When a saved page supplies that
name, the helper stores the Ajax Perl code under the saved dashboard ajax tree and emits a
stable saved-bookmark endpoint such as
C</ajax/name.json?type=text>. Skill pages use the same helper contract. Without
extra skill route metadata the generated saved endpoint is namespaced under the
longest matching skill route, for example
C</ajax/example-skill/name.json?type=text> or
C</ajax/example-skill/sub-skill/name.json?type=text>. The runtime config tree
and installed skills can both ship C<config/routes.json> to declare canonical
custom paths for normal saved app pages, skill-local app pages, Ajax handlers,
JavaScript assets, CSS assets, and other public assets. The schema is a JSON
object whose keys are the public custom paths and whose values are either one
smart local route string or an object with C<to> plus an optional C<type>, for
example
{
"/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
C<ajax> path such as C</v1/status> instead of the default C</ajax/...> url,
and runtime-level aliases such as C</java> can point at normal saved bookmark
ids such as C</app/learn.ai> without treating the dot as skill notation. The
same manifest also makes the declared custom C</app>, C</js>, C</css>, and
C</others> paths requestable. The smart longest-prefix routes remain the
parent resolvers:
C</app/example-skill/...>, C</ajax/example-skill/...>,
C</js/example-skill/...>, C</css/example-skill/...>, and
C</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 C<config/routes.json> layer chain follow the same
fallback rule against the built-in C</app>, C</ajax>, C</js>, C</css>, and
C</others> route handlers. If neither the smart route nor the custom path
resolves, the request falls through to the normal C<404> response. Ajax custom
routes default to C<json> when no explicit C<type> is present, and the
optional C<type> value can also be C<html>,
C<text>, or an arbitrary raw mime type such as
C<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 C<stdout> and C<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
C<print>, C<warn>, C<die>, C<system>, and C<exec> process behaviour instead of
a buffered JSON wrapper.
The same layered runtime config chain and installed-skill config trees can now
ship C<config/api.json> files that authorize selected C</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 C<secret> plus an C<ajax> array of exact saved Ajax route
paths such as C</ajax/stream.txt> or
C</ajax/example-skill/status.json>. When a non-admin remote request targets one
of those registered C</ajax/...> paths, the caller can send
C<X-DD-API-Key: NAME> and C<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 C<403> with the JSON body
C<{"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
C<DD-OOP-LAYERS>, runtime C<config/api.json> files merge from home to the
deepest active child layer, and installed skills contribute their own layered
C<config/api.json> fragments for skill-local saved Ajax routes. The built-in
C<dashboard api> command is the supported way to inspect or update the writable
runtime layer for that registry.
Saved bookmark Ajax handlers also default to C<text/plain> when no explicit
C<type =E<gt> ...> argument is supplied, and the generated Perl wrapper now
enables autoflush on both C<STDOUT> and C<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
C<singleton =E<gt> 'NAME'> in the C<Ajax> helper. The generated url then carries
that singleton name, the Perl worker runs as C<dashboard ajax: NAME>, and the
runtime terminates any older matching Perl Ajax worker before starting the
replacement stream for the refreshed browser request. Singleton-managed Ajax
workers are also terminated by C<dashboard stop> and C<dashboard restart>, and
the bookmark page now registers a C<pagehide> cleanup beacon against
C</ajax/singleton/stop?singleton=NAME> so closing the browser tab also tears
down the matching worker instead of leaving it behind.
If C<code =E<gt> ...> is omitted, C<Ajax(file =E<gt> 'name')> targets the
existing executable at C<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 C</js/jquery.js> asset that serves the local
copy of jQuery 4.0.0, with C</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 C<dashboard/public/js> yet. Skills can ship the same classes of assets
under their own dashboard tree: C<dashboards/ajax/*> resolves at
C</ajax/E<lt>repo-nameE<gt>/...> or
C</ajax/E<lt>repo-nameE<gt>/E<lt>sub-skillE<gt>/...>, and
C<dashboards/public/js/*>, C<dashboards/public/css/*>, and
C<dashboards/public/others/*> resolve at
C</js/E<lt>repo-nameE<gt>/...>, C</css/E<lt>repo-nameE<gt>/...>, and
C</others/E<lt>repo-nameE<gt>/...> with the same nested-skill extension,
for example C</js/E<lt>repo-nameE<gt>/E<lt>sub-skillE<gt>/path/file.js>. If a
request such as C</js/E<lt>repo-nameE<gt>/foo/bar.js> or
C</js/E<lt>repo-nameE<gt>/E<lt>sub-skillE<gt>/foo/bar.js> does not exist in the
skill-local public tree, the web layer falls back to the normal nested
saved-bookmark asset path C<dashboards/public/js/...> or saved Ajax file path
C<dashboards/ajax/...> instead of assuming the leading path segments must
belong to a skill.
Saved bookmark editor and view-source routes also protect literal inline
script content from breaking the browser bootstrap. If a bookmark body
contains HTML such as C</script>, the editor now escapes the inline JSON
assignment used to reload the source text, so the browser keeps the full
bookmark source inside the editor instead of spilling raw text below the page.
Saved browser workspaces can also show a request-specific token form above the
editor whenever the current request uses C<{{token}}> placeholders, carrying
those token values across matching placeholders in the same workflow so later
requests can reuse the operator-supplied values without manual copy-and-paste.
Bookmark rendering now emits saved C<set_chain_value()> bindings after
the bookmark body HTML, so pages that declare C<var endpoints = {}> and then
call helpers from C<$(document).ready(...)> receive their saved C</ajax/...>
endpoint URLs without throwing a play-route JavaScript C<ReferenceError>.
Bookmark pages now also expose
C<fetch_value(url, target, options, formatter)>,
C<stream_value(url, target, options, formatter)>, and
C<stream_data(url, target, options, formatter)> helpers so a bookmark can bind
saved Ajax endpoints into DOM targets without hand-writing the fetch and
render boilerplate. C<stream_data()> and C<stream_value()> now use
C<XMLHttpRequest> progress events for browser-visible incremental updates, so
a saved C</ajax/...> endpoint that prints early output updates the DOM before
the request finishes. Those helpers support plain text, JSON, and HTML output
modes, and the saved Ajax endpoint bindings now run after the page declares
its endpoint root object, so C<$(document).ready(...)> callbacks can call
helpers such as C<fetch_value(endpoints.foo, '#foo')> on first render.
Saved browser workspaces that render response inspection panels should place
their Response Body and Response Headers tabs below the response C<pre> box so
the main response payload stays visible while the tabbed details remain
reachable without jumping away from the current result.
=head2 User CLI Extensions
Unknown top-level subcommands can be provided by executable files under
the current working directory's F<./.developer-dashboard/cli> first, then the
nearest git-backed project runtime F<./.developer-dashboard/cli> when it is a
different directory, and then F<~/.developer-dashboard/cli>. For example,
C<dashboard foobar a b> will exec the first matching
F<cli/foobar> with C<a b> as argv, while preserving stdin, stdout, and
stderr.
A direct custom command can also be stored as an executable
F<cli/E<lt>commandE<gt>.pl>, F<cli/E<lt>commandE<gt>.py>, F<cli/E<lt>commandE<gt>.js>, F<cli/E<lt>commandE<gt>.go>,
F<cli/E<lt>commandE<gt>.java>, F<cli/E<lt>commandE<gt>.sh>,
F<cli/E<lt>commandE<gt>.bash>, F<cli/E<lt>commandE<gt>.ps1>,
F<cli/E<lt>commandE<gt>.cmd>, or F<cli/E<lt>commandE<gt>.bat>, and
C<dashboard E<lt>commandE<gt>> resolves the same logical command name to
those files.
Concrete source-backed examples:
dashboard hi
dashboard foo
If F<cli/hi.go> is executable, C<dashboard hi> runs it through C<go run>.
If F<cli/report.py> is executable, C<dashboard report> runs it through C<python>.
If F<cli/webhook.js> is executable, C<dashboard webhook> runs it through C<node>.
If F<cli/foo.java> is executable, C<dashboard foo> compiles it with C<javac>
into an isolated temp directory and then runs the declared main class with
C<java>.
If a user mistypes a command, dashboard now prints an explicit unknown-command
error together with the closest matching public command before the usual usage
summary. The same guidance also applies to dotted skill commands, so
C<dashboard alpha-skill.run-tset> suggests the nearest installed dotted skill
command instead of only dumping generic help.
C<DD-OOP-LAYERS> is now the runtime contract for the whole local ecosystem.
Starting at F<~/.developer-dashboard> and walking down through every parent
directory until the current working directory, every existing
F<.developer-dashboard/> layer participates. The deepest layer stays the write
target and the first lookup hit, but bookmarks, C<nav/*.tt>, config,
collectors, indicators, auth/session state lookups, runtime
F<local/lib/perl5>, and custom CLI hooks are all inherited across the full
chain instead of only a single project-or-home split.
Per-command hook files can live under either
F<./.developer-dashboard/cli/E<lt>commandE<gt>> or
F<./.developer-dashboard/cli/E<lt>commandE<gt>.d> in every inherited layer
from F<~/.developer-dashboard> down to the current directory. Executable files
in those directories are run in sorted filename order within each layer, with
the layers themselves running top-down from home to the deepest current layer,
non-executable files are skipped, and each hook now streams its own
C<stdout> and C<stderr> live to the terminal while still accumulating those
channels into C<RESULT> as JSON. If that JSON grows too large for a safe
C<exec()> environment, C<dashboard> spills it into C<RESULT_FILE> and
( run in 1.259 second using v1.01-cache-2.11-cpan-524268b4103 )