@bensandee/tooling 0.72.1 → 0.72.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -17
- package/dist/bin.mjs +69 -78
- package/package.json +2 -2
- package/tooling.schema.json +5 -5
package/README.md
CHANGED
|
@@ -475,10 +475,10 @@ env:
|
|
|
475
475
|
|
|
476
476
|
Debug output is prefixed with `[debug]` and includes exec results (exit codes, stdout/stderr), compose configuration details, container health statuses, and retry attempts.
|
|
477
477
|
|
|
478
|
-
## The agent
|
|
478
|
+
## The agent's web control panel
|
|
479
479
|
|
|
480
|
-
`agent:
|
|
481
|
-
machine, shows the
|
|
480
|
+
`agent:start --ui` serves an opt-in board that renders the `agent/<stage>[-<status>]` label state
|
|
481
|
+
machine, shows the agent's own live state, and lets you drive the transitions you would otherwise
|
|
482
482
|
apply by hand with `bst issue:set-labels` -- plus read a branch's plan and reply on its pull request
|
|
483
483
|
without leaving the page. It is off by default.
|
|
484
484
|
|
|
@@ -487,13 +487,13 @@ The problem it solves is that a parked terminal is silent: `agent/plan-ready`,
|
|
|
487
487
|
has stopped and is waiting on a person, and nothing announces them. The board's lanes are ordered by
|
|
488
488
|
what needs you, not by pipeline position.
|
|
489
489
|
|
|
490
|
-
It also closes a real gap: **the board can kill a wedged run on a headless
|
|
490
|
+
It also closes a real gap: **the board can kill a wedged run on a headless agent.** The interactive
|
|
491
491
|
`k` keypress needs a TTY, so under systemd there was previously no way to stop a stuck container
|
|
492
492
|
short of a hand-run `docker kill`.
|
|
493
493
|
|
|
494
494
|
```bash
|
|
495
495
|
# Loopback, port 4600, token minted at startup and logged once.
|
|
496
|
-
bst agent:
|
|
496
|
+
bst agent:start --ui
|
|
497
497
|
```
|
|
498
498
|
|
|
499
499
|
The startup log carries one clickable line -- open it and the token is exchanged for a session
|
|
@@ -511,7 +511,7 @@ terminator, so nothing is listening on your LAN at all and no phone is asked to
|
|
|
511
511
|
certificate:
|
|
512
512
|
|
|
513
513
|
```bash
|
|
514
|
-
bst agent:
|
|
514
|
+
bst agent:start --ui \
|
|
515
515
|
--ui-public-url https://box.tail1234.ts.net \
|
|
516
516
|
--ui-behind-tls
|
|
517
517
|
|
|
@@ -525,7 +525,7 @@ tailscale serve --bg 4600
|
|
|
525
525
|
`Host: box.tail1234.ts.net`, not `127.0.0.1:4600`, so without it **every** proxied request is
|
|
526
526
|
rejected with a bare `403`. It is also what makes the startup line print the URL you can actually
|
|
527
527
|
open.
|
|
528
|
-
- `--ui-behind-tls` marks the session cookie `Secure`. It has to be explicit: the
|
|
528
|
+
- `--ui-behind-tls` marks the session cookie `Secure`. It has to be explicit: the agent serves plain
|
|
529
529
|
HTTP and cannot see that something in front terminated TLS, and `X-Forwarded-Proto` is
|
|
530
530
|
attacker-supplied unless a trusted proxy is guaranteed -- which nothing here can guarantee.
|
|
531
531
|
|
|
@@ -541,14 +541,14 @@ There is exactly one credential to be careful with, and its lifetime is the part
|
|
|
541
541
|
|
|
542
542
|
| | Board token | Session cookie it mints |
|
|
543
543
|
| --------- | ----------------------------------------------------------------- | ----------------------- |
|
|
544
|
-
| Lifetime | the
|
|
544
|
+
| Lifetime | the agent **process** -- a service, so weeks | 12 hours |
|
|
545
545
|
| Scope | label transitions + comments + kill, every repo the bot can write | the board, nothing else |
|
|
546
546
|
| Holders | one, shared | one browser |
|
|
547
547
|
| Persisted | never | never |
|
|
548
548
|
|
|
549
|
-
The board token gates reaching the board at all, and everything it grants happens under the
|
|
549
|
+
The board token gates reaching the board at all, and everything it grants happens under the agent's
|
|
550
550
|
bot token. It is "short-lived" only relative to a config file; against the process it gates it is
|
|
551
|
-
long-lived, and rotating it means restarting the
|
|
551
|
+
long-lived, and rotating it means restarting the agent -- at which point a phone bookmark goes dead
|
|
552
552
|
and you return to the log for a fresh link. That is the accepted cost of never persisting it.
|
|
553
553
|
|
|
554
554
|
Session cookies are `HttpOnly`, `SameSite=Strict`, and expire after 12 hours. Every request is
|
|
@@ -556,7 +556,7 @@ checked against a Host-header allowlist **and** an `Origin` check; neither repla
|
|
|
556
556
|
because an `Origin` check alone does not defeat DNS rebinding.
|
|
557
557
|
|
|
558
558
|
There is no second forge credential. **Everything the board writes -- labels and comments alike --
|
|
559
|
-
is written under the
|
|
559
|
+
is written under the agent's own bot token**, so there is no operator PAT to mint and no account to
|
|
560
560
|
connect. What that costs is authorship, and the next section is about why paying it is safe.
|
|
561
561
|
|
|
562
562
|
### Reading a plan, and replying to one
|
|
@@ -574,12 +574,12 @@ can push to it, and the board will not render it.
|
|
|
574
574
|
to every later run rather than windowed). Posting takes the same two-step confirm as kill, because
|
|
575
575
|
the forge has no comment-edit API and nothing you post can be retracted from here.
|
|
576
576
|
|
|
577
|
-
The comment is **authored by the
|
|
577
|
+
The comment is **authored by the agent's bot account**, with a plain-prose suffix saying so:
|
|
578
578
|
|
|
579
579
|
```
|
|
580
580
|
Looks right, but rebase on main first.
|
|
581
581
|
|
|
582
|
-
-- posted from the agent
|
|
582
|
+
-- posted from the agent control panel
|
|
583
583
|
```
|
|
584
584
|
|
|
585
585
|
That is safe because the boundary the agent uses to tell its own words from yours is **marker-based,
|
|
@@ -596,7 +596,7 @@ that lies to the next run.
|
|
|
596
596
|
agent operation on it -- the same window the next run will actually read, computed from the same two
|
|
597
597
|
functions, so the badge cannot promise the run something different. It appears on **pull request rows
|
|
598
598
|
only**; an issue's comments are replayed to every run whole, so "new since the last run" has no
|
|
599
|
-
meaning there and a number would be a lie. The counts are fetched in the
|
|
599
|
+
meaning there and a number would be a lie. The counts are fetched in the agent's poll cycle and
|
|
600
600
|
memoized, so a row nothing has touched costs nothing and `GET /` still makes no forge call.
|
|
601
601
|
|
|
602
602
|
### What it costs, and what it does not
|
|
@@ -604,7 +604,7 @@ memoized, so a row nothing has touched costs nothing and `GET /` still makes no
|
|
|
604
604
|
Reads are cheap, not free: the publishers write a snapshot into memory and `GET /` is a synchronous
|
|
605
605
|
render off it, so **a board page load makes no forge call of its own**. What it does do is record
|
|
606
606
|
that someone is watching, which arms a refresher that drains queued actions and re-searches the
|
|
607
|
-
forge every few seconds until nobody is. That traffic is bounded by one refresher per
|
|
607
|
+
forge every few seconds until nobody is. That traffic is bounded by one refresher per agent rather
|
|
608
608
|
than by how many tabs are open, and it stops an idle window after the last reader's last request --
|
|
609
609
|
a window derived from the poll interval, never shorter than two minutes. Closing the tab does not
|
|
610
610
|
cut that short: the stream ends at once, but presence also counts that reader's last request, and
|
|
@@ -620,7 +620,7 @@ does not block. Making those git calls async is out of scope, so the board is us
|
|
|
620
620
|
occasionally waits out one clone.
|
|
621
621
|
|
|
622
622
|
An action is **queued, not applied**, when the page comes back. The board never writes a label
|
|
623
|
-
itself; the
|
|
623
|
+
itself; the agent drains queued actions -- at the top of its next claim cycle, and on the board
|
|
624
624
|
refresher's own timer while a viewer is present -- which keeps it the only writer of agent labels in
|
|
625
625
|
the process. A row says `queued` in either state, because the refresher applies it within a tick or
|
|
626
626
|
two whatever the claim cycle is doing. Each action is re-checked against live labels when it is
|
|
@@ -682,6 +682,6 @@ A service with a published port but no compose-level `healthcheck:` automaticall
|
|
|
682
682
|
|
|
683
683
|
This matters because compose publishes a port in the _host's_ network namespace, not the caller's. On a normal developer machine those are the same thing, so probing `localhost:<hostPort>` directly works fine -- but on a CI runner that shares the host's docker daemon over a mounted socket (the standard Forgejo/GitHub Actions runner shape), the job container's `localhost` is its own netns, and the published port is unreachable from it. Probing from inside the target's own namespace works in every topology, including that one.
|
|
684
684
|
|
|
685
|
-
The probe image (`curlimages/curl`) is pinned by digest, and a `docker run` exit code of `125` (Docker's convention for "the
|
|
685
|
+
The probe image (`curlimages/curl`) is pinned by digest, and a `docker run` exit code of `125` (Docker's convention for "the agent failed to start the container at all" -- missing image, pull denied, agent unreachable) fails the check immediately instead of waiting out the full timeout.
|
|
686
686
|
|
|
687
687
|
**This only applies to auto-derived checks.** A health check you configure yourself via `.tooling.json`'s `dockerCheck.healthChecks` is still probed with a direct `fetch()` from wherever `bst docker:check` runs -- an arbitrary user-supplied URL isn't necessarily compose-related at all, so making it reachable from the caller remains your responsibility.
|