Repository navigation
weft v0.11.0 — phase 2 of the devtools plan: the weft command, weft dev, discovery, doctor
weft v0.11.0
Phase 2 of the devtools plan (start and find): the weft command,
weft dev, the port policy, discovery, the stable dev token, the
Agents page from runtime registrations, weft doctor, and the panel's
configuration ladder. Tags core/v0.11.0 (the weft.version literal
only; core's API is unchanged since 0.10.1) and v0.11.0 — a minor
bump, as studio/cmd is removed (Changed — breaking).
Added
-
Agents from runtime registrations (plan B4): Studio remembers
each runtime's registration manifest by (service, manifest hash), in
memory;/api/manifestserves it when noweft.jsonis configured
(the file still wins) and lists every source insources[]with
live; two services registering one agent name in different versions
both appear./api/metagainsmanifest_sources,capabilities_off
(why an option left a capability off) andmanifest_check.source/
differs; the Agents page, playground and debugger empty states show
the reason. -
The
weftcommand (plan B1,cmd/weft):go install github.com/weftgo/weft/cmd/weft@latestyields aweftbinary.
weft studio [--addr] [--db] [--token] [--manifest] [--open] [--no-playground]is setup B (whatstudio/cmdserved) with the
playground on (studio.Playground(true), inert until an app's
runtime connects;--no-playgroundturns it off), the manifest from
--manifest/WEFT_MANIFESTelse the nearestweft.jsonfrom the
working directory upward (one line says which, or that none was
found), and--open(default on when stdout is a terminal) opening
the UI with the token in the URL fragment. The Studio API over a
terminal:weft runs [--agent] [--since] [--failed] [--limit] [--json](GET /api/runs;--limit50 by default, and a limit
that hid runs says so on stderr),weft open <run id> [--open] [--with-token](prints the bare<url>/runs/<id>; the token goes
only to the browser, or to stdout on--with-token),weft export <run id> [--format json|jsonl|otlp]to stdout andweft export <run id> --wefttest <dir> [--test name] [--force](the wefttest fixtures
unzipped into<dir>/<name>/, wherewefttest.Replay(t, dir)reads
them; a non-empty target needs--force, which replaces its*.json
fixtures);weft doctor;weft version. The API clients never
follow a redirect. The API clients take
--url(WEFT_STUDIO_URL, defaulthttp://127.0.0.1:7331) and
--token(WEFT_STUDIO_TOKEN). Exit codes: 0, 1 a failure, 2 a
usage error. -
weft dev(plan B1.2, FEATURES D7):weft dev [studio's flags] [--no-watch] [--watch dir] [-- command…](defaultgo run .)
starts Studio in-process asweft studiodoes (same flags and port
policy) and runs the app withWEFT_ENV=dev(kept when already set
non-empty),WEFT_STUDIO_URL,WEFT_STUDIO_TOKENandWEFT_DB(the
Studio's SQLite file) set — plain variables the app can set itself.
The app runs in its own process group and restarts on a.gosave
(fsnotify, 300 ms debounce; SIGTERM, 5 s, SIGKILL); a build failure
waits for the next save;--no-watchexits with the app's code
(127 when it cannot start); a directory arriving with.gofiles
restarts too. Ctrl-C, SIGTERM and SIGHUP stop the app, then Studio
(Linux adds Pdeathsig forgo run; a SIGKILL ofweft devcan
still orphan the app's binary). Each start prints one line:
studio <url>[#token=…] · app pid <n> · runtime rt_… registered
(the token only when generated; "no runtime registered yet" after
5 s, then a later line). Reuse follows the port policy: without a
fixed token a running token-walled Studio is skipped for the next
port (plan B3's stable token changes this). The
discovery file is plan B3's.examples/studio-localruns under it:
withWEFT_STUDIO_URLset it listens on 8080, keeps its local sink
(otel.Local(""), explicit) and registers its runtime with that
Studio. -
make studio-binbuilds./weftfrom./cmd/weft(ignored by
git). -
Studio's port policy (plan B2,
internal/listen, wired into
weft studio):127.0.0.1:7331is the one default. On a busy port
the binary asksGET /api/metathere (bearer:--token/
WEFT_STUDIO_TOKENwhen set): a Studio on the same database file is
reused —studio already running at http://127.0.0.1:7331 (pid 1234), reusing, exit 0, no database or listener opened — and anything else (another
program, a Studio on another database, one whose meta this token
cannot read) moves Studio to the next free port in 7331–7340 with
one line naming the skipped address and why; the banner prints the
real address. All ten busy is exit 1 naming the range.studio.New
is unchanged: an embedded Studio is the app's own listener. -
WEFT_STUDIO_ADDR, the--addrmirror. Either pins the
address: busy is exit 1 with the address in the error — no probe, no
fallback. -
/api/metapid: the serving process's id, underdb.path's
guard (loopback with no Token, or the server token; never a panel
token). -
weft doctor [--url URL] [--token TOK](plan B5): checks a
running Studio and prints one line per check — reachable, token
accepted, the database's path and size, the content it stores,
connected runtimes (and, when none, what this shell'sWEFT_ENVand
WEFT_STUDIO_URLsay about why), the panel bundle's version, and
whetherweft.jsonis stale against the latest runs. Every line
reads a field ofGET /api/meta(internal/doctor.Linesis the
table); the flags mirrorWEFT_STUDIO_URL/WEFT_STUDIO_TOKEN. An
unreachable Studio is the first line within a 5 s timeout and exit 1;
a redirect is reported, never followed. -
weft studio --manifest path(default$WEFT_MANIFEST; the flag
wins): the app'sweft.json, read once at start and served as
studio.Manifest—/api/manifestand the doctor's weft.json check
in setup B. An unreadable file is a start error. -
/api/metaexplains itself:content(Studio's ingest policy,
"as_received", and the latest run's content mark —full,
stripped,noneorunmarked— with a note and the fix naming
otel.NoContent()orweft.Content(false)),pricing(false
until a pricing table exists),retention(null: none
configured),runtimes(runtimes holding a command stream now),
panel_version(the embedded/panel.jsstamp) and
manifest_check({agents, checked, stale}: each manifest agent's
hash against its latest run'sweft.manifest.hash;nullwithout a
manifest or for a panel token), andauth_required(whether a
studio.Tokenis configured).content.errorand
manifest_check.error(omitted when empty) report a read that
failed or a manifest that does not parse — logged through the
process'sslogdefault too — instead of reading as an empty
database. -
obsdb/sqlite:
(*DB).Path()— the database file's absolute
path (""for:memory:). -
The discovery file (plan B3,
internal/discovery):weft studio
andweft devwritestudio.json—{url, token, db, pid, started, version}, mode 0600, the bound port in it — to./.weft/when it
exists, else$XDG_RUNTIME_DIR/weft/, elseos.UserCacheDir()/weft/,
and remove it on a clean exit.otel.Install/otel.Startand
runtime.Installread it whenWEFT_STUDIO_URLis unset: an app
withdefer otel.Install()()exports to the running Studio (and its
runtime dials it) with no configuration, one INFO line naming the
Studio joined. The file is trusted only when its url is loopback, it
is fresh (pid alive, under 24 h) and, on unix, it is 0600 and the
reading user's; anything else is ignored at Debug and removed by the
next writer. A second Studio's exit restores the first's file; the
writer drops.weft/.gitignore(*) when there is none. An explicit
otel.Studio(...)orWEFT_STUDIO_URLswitches the read off;
WEFT_DISCOVERY=offturns it off;otel.NoEnv()ignores it with
the rest of the environment. -
A stable dev token per database:
<db>.tokenbeside the SQLite
file (.weft/weft.db.token), 32 random bytes base64url, 0600,
created on the first start and served by every later one, so the
token survives a restart and a second bare start's probe reuses the
running Studio (the probe sends it only to an address the user's
discovery file names).--rotate-token(weft studio,weft dev;
an action, no environment mirror) writes a new one. -
GET <base>/panel-config.json(studio):{endpoint, version, capabilities}for the devtools panel, answered to a loopback (or
AllowOrigins) Host and a same-origin,AllowOriginsor loopback
Origin only (loopback by the Host's rule —localhost,
*.localhost, 127.0.0.0/8,[::1]— even withAllowOriginsset) —
a 404 otherwise;/api/metalists the newpanel-configcapability. -
The devtools panel's configuration ladder (plan C2): each field
(endpoint,public-id,token,position,open,auto)
resolves on its own throughmount(opts)→ the<weft-devtools>
element's attributes →<meta name="weft:endpoint|public-id|token| position|open|auto">→ the panel's<script>tag (the running
classic script, else the first withdata-weft, anysrc) →
panel-config.jsonbeside the script (endpoint only, same origin
only, never a token; asked only when nothing above named an endpoint)
→ the script's own directory. A<weft-devtools>element, a
mount(opts)ordata-auto="false"with no Studio answering shows
one line,Studio not reachable at <endpoint> · retry; the dock the
script mounted by itself still removes itself silently. The rung
table is instudio/README.md.
Dependencies
github.com/fsnotify/fsnotifyv1.9.0 (framework module only, for
weft dev's watcher;coreis untouched).
Changed
runtime/examples/localserves on127.0.0.1:7331(was 7391, for
no documented reason): one default port everywhere.- The dev token is stable per database and no longer printed by
default:weft studio's banner andweft dev's one line print the
bare URL and name the token file (the stable token is the panel
tokens' signing key, like a fixed one);--openstill hands the
browser the link with the token,weft open --with-tokenprints it,
andweft dev's app still getsWEFT_STUDIO_TOKEN. Only a database
with no file (:memory:, ClickHouse) gets a per-process token,
printed as before.--token/WEFT_STUDIO_TOKENoverride as
before and never touch the file.
Changed — breaking
- The panel no longer finds its script tag by file name (the
panel(-vX)?.jspattern is gone). A tag carrying none of the panel's
data-*attributes — the bare<script type="module" src="/studio/panel.js">that relied onwindow.__WEFT__, or a
renamed or proxied bundle — is no longer found: its endpoint falls to
the page's own directory, Studio does not answer there, and the auto
dock removes itself silently. Migration: adddata-weftto the
script tag (<script type="module" src="/studio/panel.js" data-weft>), or configure a higher rung (aweft:*meta tag, the
element,mount(opts)). Tags withdata-public-id,data-endpoint
or another panel attribute keep working. A<weft-devtools>element
in your markup with no Studio answering now shows the not-reachable
line instead of an empty element. studio/cmdis removed: the setup-B binary isweft studio
(go install github.com/weftgo/weft/cmd/weft@latest), every flag
kept.studio --versionisweft version;studio doctoris
weft doctor. Scripts that rango run ./studio/cmdor
go install github.com/weftgo/weft/studio/cmd@…runweft studio./api/meta'sdbis an object:{kind, path, size}instead of
the kind string. Readdb.kindwhere you readdb.pathand
size(bytes, the WAL sidecar included) are served only to a
loopback request with nostudio.Tokenand to the server token —
omitted for panel tokens andAllowOriginshosts, and for a
database with no file.
The panel release asset: panel-v0.11.0.js (sha256 beside it).