fix: event-loop freezes, plugin-install boot crash, tmux attach/scrollbar, Codex transcripts
- GET /api/workflows: one LEAD() window pass in a read worker (read-pool.js, workflow-stats.js); idx_events_session restored, idx_events_tool added - compat-sqlite gains function/pluck/readonly so a plugin install without a better-sqlite3 build boots again; antigravity-usage uses db.js's Database - install-hooks adds no checkout hooks while the agenthub plugin is enabled (events were doubled) - terminal attach runs the live tmux server's own binary; the pane scrollbar gets a visible 2-column style, and claude panes start with CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1 so tmux keeps history to scroll - Codex sessions' conversation in GET /api/sessions/:id/transcript - Codex/Antigravity sessions claimed by their workspace without hooks - UI-audit follow-ups, docs and OpenAPI
This commit is contained in:
+54
-13
@@ -2,7 +2,9 @@
|
||||
|
||||
/**
|
||||
* Installs Claude Code hooks that forward events to the Agent Dashboard.
|
||||
* Modifies ~/.claude/settings.json to add hook entries.
|
||||
* Modifies ~/.claude/settings.json to add hook entries — or, while the
|
||||
* `agenthub` plugin is enabled (it ships the same hooks inline), removes ours
|
||||
* instead, since two handlers post every event twice.
|
||||
*
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
@@ -31,6 +33,22 @@ function pluginBootstrapRan() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* True when settings.json enables the `agenthub` plugin, whose `plugin.json`
|
||||
* carries these same eight hooks inline — Claude Code then runs them itself,
|
||||
* and any hook-handler.js entry in settings.json is a second copy of each.
|
||||
*
|
||||
* @param {object} settings parsed settings.json
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function pluginProvidesHooks(settings) {
|
||||
const ep = settings && settings.enabledPlugins;
|
||||
if (!ep || typeof ep !== "object") return false;
|
||||
return Object.entries(ep).some(
|
||||
([k, v]) => v === true && (k === "agenthub" || k.startsWith("agenthub@"))
|
||||
);
|
||||
}
|
||||
|
||||
function envFlag(name) {
|
||||
return ["1", "true", "yes", "on"].includes(String(process.env[name] || "").toLowerCase());
|
||||
}
|
||||
@@ -143,17 +161,6 @@ function installHooks(silent = false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The `agenthub` plugin installs the same eight hooks itself. Running both means
|
||||
// every event is POSTed twice and token/cost figures double — warn loudly
|
||||
// rather than silently double-count. (`/agenthub-doctor` reports the same state.)
|
||||
if (!silent && pluginBootstrapRan()) {
|
||||
console.warn(
|
||||
"WARNING: the agenthub plugin is installed and already provides these hooks.\n" +
|
||||
" Installing them again double-counts every event. Uninstall the\n" +
|
||||
" plugin first, or skip this step. Run /agenthub-doctor to check."
|
||||
);
|
||||
}
|
||||
|
||||
let settings = {};
|
||||
if (fs.existsSync(getSettingsPath())) {
|
||||
try {
|
||||
@@ -165,6 +172,40 @@ function installHooks(silent = false) {
|
||||
}
|
||||
}
|
||||
|
||||
// The enabled plugin already runs these hooks: take our copies out instead
|
||||
// of adding them (every server start used to re-add them and double every
|
||||
// event). Other hooks stay; the file is backed up before it is written.
|
||||
if (pluginProvidesHooks(settings)) {
|
||||
let removed = 0;
|
||||
for (const [type, entries] of Object.entries(settings.hooks || {})) {
|
||||
if (!Array.isArray(entries)) continue;
|
||||
const kept = entries.filter((e) => !isOurEntry(e));
|
||||
removed += entries.length - kept.length;
|
||||
if (kept.length) settings.hooks[type] = kept;
|
||||
else delete settings.hooks[type];
|
||||
}
|
||||
if (removed) {
|
||||
fs.copyFileSync(getSettingsPath(), `${getSettingsPath()}.agenthub-bak`);
|
||||
fs.writeFileSync(getSettingsPath(), JSON.stringify(settings, null, 2) + "\n", "utf8");
|
||||
}
|
||||
if (!silent) {
|
||||
console.log(
|
||||
"The agenthub plugin is enabled and provides these hooks — nothing to install." +
|
||||
(removed ? ` Removed ${removed} duplicate entr${removed === 1 ? "y" : "ies"}.` : "")
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bootstrapped once but not enabled now (disabled or uninstalled): the
|
||||
// plugin's hooks are off, so ours are the only ones — install them.
|
||||
if (!silent && pluginBootstrapRan()) {
|
||||
console.warn(
|
||||
"Note: the agenthub plugin was installed here but is not enabled — installing\n" +
|
||||
" the checkout's hooks. Re-enabling the plugin removes them again."
|
||||
);
|
||||
}
|
||||
|
||||
if (!settings.hooks) settings.hooks = {};
|
||||
|
||||
let installed = 0;
|
||||
@@ -206,4 +247,4 @@ if (require.main === module) {
|
||||
|
||||
// isOurEntry is also used by scripts/plugin-bootstrap.js to strip hook entries
|
||||
// a previous checkout install left behind (they would double-count events).
|
||||
module.exports = { installHooks, isInsideContainer, isOurEntry };
|
||||
module.exports = { installHooks, isInsideContainer, isOurEntry, pluginProvidesHooks };
|
||||
|
||||
@@ -247,17 +247,48 @@ function systemdStatus() {
|
||||
* This checks the doctor's own PATH. A server started as a systemd `--user`
|
||||
* service can still see a different one — `GET /api/run/tmux` is the server's
|
||||
* own answer, and `agenthub doctor` reports that.
|
||||
*
|
||||
* Also WARN below tmux 3.7, the first with pane scrollbars (`tmux.js` turns
|
||||
* them on), and when a new binary still talks to an older running server — a
|
||||
* server keeps its version until every tmux session has closed. The doctor
|
||||
* only says so: it never installs or restarts tmux.
|
||||
*/
|
||||
const hasScrollbars = (version) => {
|
||||
const m = /(\d+)\.(\d+)/.exec(version || "");
|
||||
return !!m && (Number(m[1]) > 3 || (Number(m[1]) === 3 && Number(m[2]) >= 7));
|
||||
};
|
||||
|
||||
function tmuxStatus() {
|
||||
if (require("../server/lib/tmux").isTmuxAvailable()) {
|
||||
return ["ok", "tmux", "on PATH — the run console can start sessions"];
|
||||
const tmux = require("../server/lib/tmux");
|
||||
const client = tmux.clientVersion();
|
||||
if (!client) {
|
||||
return [
|
||||
"warn",
|
||||
"tmux",
|
||||
"not found — Start Run and `agenthub lanes shell` will fail (everything else is unaffected). " +
|
||||
"Install it: sudo apt install tmux / brew install tmux (Windows: run AgentHub under WSL2)",
|
||||
];
|
||||
}
|
||||
return [
|
||||
"warn",
|
||||
"tmux",
|
||||
"not found — Start Run and `agenthub lanes shell` will fail (everything else is unaffected). " +
|
||||
"Install it: sudo apt install tmux / brew install tmux (Windows: run AgentHub under WSL2)",
|
||||
];
|
||||
if (!hasScrollbars(client)) {
|
||||
return [
|
||||
"warn",
|
||||
"tmux",
|
||||
`${client} works, but terminal panes get a scrollbar only from tmux 3.7. ` +
|
||||
"Upgrade: brew upgrade tmux (macOS); on Linux, when the distro ships an older one " +
|
||||
"(Ubuntu 24.04 has 3.4), build the release from https://github.com/tmux/tmux/releases " +
|
||||
"(needs libevent-dev libncurses-dev bison: ./configure && make && sudo make install)",
|
||||
];
|
||||
}
|
||||
const server = tmux.serverVersion();
|
||||
if (server && !hasScrollbars(server)) {
|
||||
return [
|
||||
"warn",
|
||||
"tmux",
|
||||
`${client} installed, but the running tmux server is ${server} — panes get their ` +
|
||||
"scrollbar once every tmux session has closed (or after a reboot)",
|
||||
];
|
||||
}
|
||||
return ["ok", "tmux", `${client} on PATH — the run console can start sessions, with scrollbars`];
|
||||
}
|
||||
|
||||
function countLegacyHookEntries() {
|
||||
|
||||
Reference in New Issue
Block a user