An operator links an agent's GitHub personal access token in the swarm UI
(LinkGithubAccountForm, "link github account" on /agents). swarm-controller's
PUT /api/hives/{hive}/agents/{agent}/github-account stores it at
swarm/agents/<agent>/github-token (swarm_secret_client::github), a flat leaf
under the agent's prefix that the agent's existing read grant already covers:
no policy change, and no list grant, since there is one token per agent.
In the agent, hive-agent-github-token (oneshot + 2-minute timer, as the agent
user, under its own store certificate, ordered before hive-github-notify)
reads that path and writes <state>/github-token, 0600 and agent-owned, the
file the gh wrapper, git credential helper and hive-github-notify already
read. It replaces the file by rename only when the bytes changed and never
deletes it: a hive-written github-token stays until a token is linked in the
swarm UI. It is installed only with a store address and
services.hyperhive.agent.github.enable.
Removed: the dashboard's CR3D3NTIALS page (credentials.html/js/css, its
build entries and H0M3 tile; GITHUB was its only tab), hive-c0re's
dashboard/matrix_accounts.rs with GET/POST /api/github-account,
priv_client::write_agent_github_token, the host socket's
SetAgentGithubToken and `hivectl github set-token`, and hive-priv's
WriteAgentGithubToken with write_agent_state_file, its only caller gone.
Docs: integrations/github.md and swarm/ui.md describe the swarm path,
swarm/credentials.md gains the store-path row, and the hive UI docs,
hivectl docs and security.md's hive-priv table drop the removed pieces.
Closes #4347
68 lines
2.8 KiB
JavaScript
68 lines
2.8 KiB
JavaScript
// hive-tab-strip.js — <hive-tab-strip>, the markup-owning tab-strip custom
|
|
// element behind the logs/core/builds sub-page tabbars. Owns
|
|
// rendering the <a class="hive-tab"> markup from a declarative `tabs` list
|
|
// instead of every page hand-writing the same <nav><a>...</a></nav>
|
|
// boilerplate, then wires the existing createTabStrip() behaviour
|
|
// (@hive/shared/tabs.js) over what it just rendered — no behaviour
|
|
// duplication, this is markup only.
|
|
//
|
|
// Light-DOM (no shadow root): the existing .hive-tabbar/.hive-tab/
|
|
// .tab-count CSS (tabs.css + dashboard.css) already targets plain classes
|
|
// on the tabbar/tab elements directly, and tab *panes* stay page-owned
|
|
// content this element never touches — no slotting/scoping need the way
|
|
// <hive-menu>/<hive-side-panel> have.
|
|
//
|
|
// Host keeps its own id/class/role (same as the <nav> it replaces, so
|
|
// existing CSS selectors + #id lookups keep matching); pass a `prefix`
|
|
// attribute (e.g. `prefix="logs"`). `configure({ tabs, defaultId, onShow })`
|
|
// then renders one <a> per tab —
|
|
// <a class="hive-tab" id="${prefix}-tab-${id}" href="#${id}" role="tab"
|
|
// aria-controls="${prefix}-pane-${id}" data-tab="${id}">${label}</a>
|
|
// — same id/aria-controls scheme the hand-written markup used, so each
|
|
// page's panes (aria-labelledby="${prefix}-tab-${id}") don't need to
|
|
// change. A `badgeId` tab also gets a nested `<span class="tab-count"
|
|
// id="${badgeId}" hidden>` (matches builds.html's rebuild-queue count
|
|
// pill), then wires up createTabStrip() and returns exactly what it
|
|
// returns ({ show, active }) — every existing `.active()`/`.show(id)`
|
|
// call site keeps working unchanged. Call configure() exactly once per
|
|
// element (throws if called twice), mirroring the old call sites.
|
|
|
|
import { el } from "../dom.js";
|
|
import { createTabStrip } from "./tabs.js";
|
|
|
|
class HiveTabStrip extends HTMLElement {
|
|
configure({ tabs, defaultId, onShow }) {
|
|
if (this._api) {
|
|
throw new Error(
|
|
"hive-tab-strip: configure() called twice on the same element",
|
|
);
|
|
}
|
|
const prefix = this.getAttribute("prefix");
|
|
if (!prefix) {
|
|
throw new Error("hive-tab-strip: missing required `prefix` attribute");
|
|
}
|
|
for (const tab of tabs) {
|
|
const a = el(
|
|
"a",
|
|
{
|
|
class: "hive-tab",
|
|
id: `${prefix}-tab-${tab.id}`,
|
|
href: `#${tab.id}`,
|
|
role: "tab",
|
|
"aria-controls": `${prefix}-pane-${tab.id}`,
|
|
"data-tab": tab.id,
|
|
},
|
|
tab.label,
|
|
);
|
|
if (tab.badgeId) {
|
|
a.append(
|
|
el("span", { class: "tab-count", id: tab.badgeId, hidden: "" }),
|
|
);
|
|
}
|
|
this.append(a);
|
|
}
|
|
this._api = createTabStrip(this, { defaultId, onShow });
|
|
return this._api;
|
|
}
|
|
}
|
|
customElements.define("hive-tab-strip", HiveTabStrip);
|