fix: v2.8.10 — the server starts again on plugin installs (js-yaml is a runtime dependency)
v2.8.9 crashed at start on every plugin install with "Cannot find module 'js-yaml'": server/lib/lane-detect.js requires it, but it was only a devDependency that redoc used to pull in, and v2.8.8 dropped redoc. The plugin installs with --omit=dev. This box hid it: Debian's global /usr/share/nodejs has js-yaml. Reproduced with a pruned install and the global paths off; fixed by moving js-yaml (and argparse in the lock) to dependencies. New runtime-deps.test.js fails on any server/ require that is not a dependency.
This commit is contained in:
@@ -7239,7 +7239,7 @@ exports[`screen snapshots > Settings — about 1`] = `
|
|||||||
<span
|
<span
|
||||||
class="text-status-warning/90"
|
class="text-status-warning/90"
|
||||||
>
|
>
|
||||||
UI build v2.8.9 (rebuild client to match)
|
UI build v2.8.10 (rebuild client to match)
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
openapi: 3.0.3
|
openapi: 3.0.3
|
||||||
info:
|
info:
|
||||||
title: Agent Dashboard for Claude Code API
|
title: Agent Dashboard for Claude Code API
|
||||||
version: 2.8.9
|
version: 2.8.10
|
||||||
description: HTTP API for real-time Claude Code session monitoring, agent lifecycle tracking, analytics, pricing, hooks ingestion, and workflow intelligence.
|
description: HTTP API for real-time Claude Code session monitoring, agent lifecycle tracking, analytics, pricing, hooks ingestion, and workflow intelligence.
|
||||||
contact:
|
contact:
|
||||||
name: Nguyễn Ngọc Trí Vĩ
|
name: Nguyễn Ngọc Trí Vĩ
|
||||||
|
|||||||
Generated
+3
-5
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "agenthub",
|
"name": "agenthub",
|
||||||
"version": "2.8.9",
|
"version": "2.8.10",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "agenthub",
|
"name": "agenthub",
|
||||||
"version": "2.8.9",
|
"version": "2.8.10",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"adm-zip": "^0.6.1",
|
"adm-zip": "^0.6.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
"multer": "^2.0.0",
|
"multer": "^2.0.0",
|
||||||
"swagger-ui-express": "^5.0.1",
|
"swagger-ui-express": "^5.0.1",
|
||||||
"systray2": "^2.1.4",
|
"systray2": "^2.1.4",
|
||||||
@@ -27,7 +28,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"js-yaml": "^4.1.0",
|
|
||||||
"prettier": "^3.8.1"
|
"prettier": "^3.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -215,7 +215,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "Python-2.0"
|
"license": "Python-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/array-flatten": {
|
"node_modules/array-flatten": {
|
||||||
@@ -1099,7 +1098,6 @@
|
|||||||
"version": "4.3.2",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
|
||||||
"integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
|
"integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "agenthub",
|
"name": "agenthub",
|
||||||
"version": "2.8.9",
|
"version": "2.8.10",
|
||||||
"bin": {
|
"bin": {
|
||||||
"agenthub": "bin/agenthub.js"
|
"agenthub": "bin/agenthub.js"
|
||||||
},
|
},
|
||||||
@@ -75,6 +75,7 @@
|
|||||||
"adm-zip": "^0.6.1",
|
"adm-zip": "^0.6.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
"multer": "^2.0.0",
|
"multer": "^2.0.0",
|
||||||
"swagger-ui-express": "^5.0.1",
|
"swagger-ui-express": "^5.0.1",
|
||||||
"systray2": "^2.1.4",
|
"systray2": "^2.1.4",
|
||||||
@@ -88,7 +89,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"js-yaml": "^4.1.0",
|
|
||||||
"prettier": "^3.8.1"
|
"prettier": "^3.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* @file runtime-deps.test.js
|
||||||
|
* @description Every package the server code requires is a runtime dependency.
|
||||||
|
* A plugin install runs `npm install --omit=dev`, so a package listed only in
|
||||||
|
* devDependencies — or one that arrived only as some other package's
|
||||||
|
* dependency — is missing there. v2.8.9 crashed at start on every plugin
|
||||||
|
* install: `js-yaml` (server/lib/lane-detect.js) was a devDependency that
|
||||||
|
* `redoc` used to pull in, and dropping `redoc` took it away. Machines with
|
||||||
|
* Debian's global /usr/share/nodejs never saw it, so this checks the manifest
|
||||||
|
* instead of trusting a local `require` to fail.
|
||||||
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { describe, it } = require("node:test");
|
||||||
|
const assert = require("node:assert/strict");
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const path = require("node:path");
|
||||||
|
const { builtinModules } = require("node:module");
|
||||||
|
|
||||||
|
const ROOT = path.join(__dirname, "..", "..");
|
||||||
|
const pkg = require(path.join(ROOT, "package.json"));
|
||||||
|
|
||||||
|
// Required only behind a runtime check, never on a plain Node server.
|
||||||
|
const GUARDED = new Set(["electron"]);
|
||||||
|
|
||||||
|
function sourceFiles(dir) {
|
||||||
|
const out = [];
|
||||||
|
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||||
|
const p = path.join(dir, e.name);
|
||||||
|
if (e.isDirectory()) {
|
||||||
|
if (!["node_modules", "__tests__", "vendor"].includes(e.name)) out.push(...sourceFiles(p));
|
||||||
|
} else if (/\.(c?js|mjs)$/.test(e.name)) out.push(p);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Bare package names `file` requires, comments left out. */
|
||||||
|
function requiredPackages(file) {
|
||||||
|
const code = fs
|
||||||
|
.readFileSync(file, "utf8")
|
||||||
|
.replace(/\/\*[\s\S]*?\*\//g, "")
|
||||||
|
.replace(/(^|[^:"'`\\])\/\/.*$/gm, "$1");
|
||||||
|
const names = new Set();
|
||||||
|
for (const m of code.matchAll(/\brequire\(\s*["']([^"'./][^"']*)["']\s*\)/g)) {
|
||||||
|
const spec = m[1];
|
||||||
|
if (spec.startsWith("node:")) continue;
|
||||||
|
const name = spec.startsWith("@") ? spec.split("/").slice(0, 2).join("/") : spec.split("/")[0];
|
||||||
|
if (!builtinModules.includes(name)) names.add(name);
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("runtime dependencies", () => {
|
||||||
|
it("every package server/ requires is in dependencies or optionalDependencies", () => {
|
||||||
|
const runtime = new Set([
|
||||||
|
...Object.keys(pkg.dependencies || {}),
|
||||||
|
...Object.keys(pkg.optionalDependencies || {}),
|
||||||
|
]);
|
||||||
|
const missing = [];
|
||||||
|
for (const file of sourceFiles(path.join(ROOT, "server"))) {
|
||||||
|
for (const name of requiredPackages(file)) {
|
||||||
|
if (!runtime.has(name) && !GUARDED.has(name))
|
||||||
|
missing.push(`${path.relative(ROOT, file)}: ${name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.deepEqual(missing, []);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user