Commit Graph
5 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 b8fd43d7ab a chat under the lists, and the skill that makes it worth having
the layout gains a third node across the bottom, a third of the height. a
conversation reads badly in a narrow column and the two lists do not, so the
chat gets width rather than a column of its own.

it is officerdev/chat, the platform's own panel, not a simplified one of ours.
seedAppRegistry merges the platform's panels and every plugin's into one
registry, so naming it is all it takes — and a second chat maintained here
would be the one that falls behind.

the skill is deliberately NOT scoped to these routes. that was considered and
dropped once it was clear what a skill costs: compileSkills emits a name, a
description and a path, and the body is only read if the agent decides it is
relevant. two lines in every conversation. scoping would have meant a platform
change to buy back nothing.

(affordances are the opposite — loaded in full, every conversation — so this
plugin ships none. by docs/affordances.md's own test the 404 quirk does not
qualify: it CAN be fixed instead, and the platform route already fixes it.)

what the skill carries: the routes, that meta.nginx_online is the field for
"is it down", that renewal is at 30 days so under 14 is a real failure worth
raising unprompted and nothing else will raise it, that the 404 has already
been interpreted before it reaches you, and that disabling a host takes a
domain off the internet so it is confirmed and never inferred. plus what is
not built, so it does not get offered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
2026-08-25 17:17:32 +00:00
pastilhasandClaude Opus 5 3d02cb6229 saving a connection now tells the other panels
reported on the first successful save: everything still showed no data and the
form stayed open, and a manual page refresh was the only way through.

two faults, one report. the form did not close itself — nothing cleared
?config, so a save left the settings view sitting on top of a plugin that now
worked. and the two reader panels are SIBLINGS in a workspace layout: they are
mounted independently, neither owns the other, and ConnectionPanel cannot call
into them. so they went on showing the failure they had loaded back when no
connection existed.

a channel is the sanctioned answer for exactly this — docs/navigation-audit.md
puts addressable state in the URL and leaves channels for "something changed,
re-read it". nobody wants to link to the moment after a save.

the nonce is a plain module counter, copied deliberately from officerdev's
channels.ts along with the reason: setSignal(n => n + 1) applies against the
value captured at render, so two bumps in one render window both compute
snapshot + 1 and the second overwrites the first — and nobody re-reads.
Date.now() is the same bug at millisecond scale.

disconnect bumps it too. the panels must stop showing hosts they can no longer
reach.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
2026-08-25 16:58:52 +00:00
pastilhasandClaude Opus 5 83a8558e60 do not print a proxy's error page at somebody
first real test of the settings form returned openresty's 502 Bad Gateway
document, markup and all, into the panel. installing this plugin restarts
officer, and a request made inside that window reaches nginx proxy manager,
finds nothing behind it, and gets a web page back. useClient throws the
response body as the message, so the panel printed it.

worth keeping the irony: this plugin manages the proxy whose error page it
could not read.

the check is the body's SHAPE, not the status, because there is no status that
separates them — api/router.ts uses 502 for "NPM answered and refused", which
is a real answer we must not swallow. ours arrives as a sentence, theirs as a
document. gateway status AND html means the hop in front of officer answered;
html on its own still is not a sentence and says so without the tags.

all three panels go through it now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
2026-08-25 16:55:55 +00:00
pastilhasandClaude Opus 5 ee8edd3709 the settings form, and the branch that never ran
the plugin could not be configured. /_config had GET, PUT, DELETE and a test
that validates without storing, and nothing rendered any of it — the
connection had to be inserted by hand.

ConnectionPanel is both the wizard and the settings page, one component, the
same choice jellyfin's ConnectionSection makes and for the same reason: a
connection edited months later goes through exactly the path that stored the
first one. HostsPanel shows it when nothing is configured and on ?config=1,
which is in the URL so it survives a refresh. test comes before save and
stores nothing, so a wrong password cannot replace a working one. the secret
field starts empty even when one is stored, and empty means keep — that is
what lets a typo in the URL be fixed without going to find the password.

and while wiring the unconfigured case, it turned out that case never fired.

both panels read `(error as { body?: Failure }).body` to decide whether a
failure was unconfigured, unauthenticated or unreachable. useClient throws
ApiError(status, message) and has no `.body` at all, so that was always
undefined: every failure fell through to the generic message, and the careful
explanation of NPM's 404-means-credential quirk was written, shipped and
unreachable. asFailure now reads the STATUS, which the router already chooses
deliberately — 409 nothing stored, 503 no answer, 502 answered and refused.

owner-only is explained rather than reported. a member hitting 403 here reads
why: it is an admin login and NPM has no read-only role.

still not installed, still no db:push.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
2026-08-25 16:51:31 +00:00
pastilhasandClaude Fable 5 5914e96cf3 proxy-manager: nginx proxy manager in officer
reads hosts and certificates against a live NPM 2.15.1. one grantable
permission, with /_config owner-only — reading which hosts are healthy
is worth sharing, an NPM admin login is not, and NPM has no read-only
role.

the quirk that shapes the whole plugin: NPM answers an unauthenticated
read with 404, not 401, because its router only registers a resource
once a token is present. so 'not signed in' and 'no such endpoint' are
the same response, and a naive client tells you a feature is missing
when your password is wrong. the client mints a fresh token and retries
once, then reports a persistent 404 as likelyAuth.

certificates sort by expiry rather than name, which is the only ordering
that answers a question.

no create/edit/delete and no cert issuance: NPM's own UI does those and
a wrong proxy host takes a domain off the internet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 08:38:15 +00:00