claude-crap 0.4.7 → 0.4.8
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/CHANGELOG.md +26 -0
- package/dist/dashboard/server.d.ts +6 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +99 -31
- package/dist/dashboard/server.js.map +1 -1
- package/dist/tests/helpers/dashboard-test-helpers.d.ts +94 -0
- package/dist/tests/helpers/dashboard-test-helpers.d.ts.map +1 -0
- package/dist/tests/helpers/dashboard-test-helpers.js +159 -0
- package/dist/tests/helpers/dashboard-test-helpers.js.map +1 -0
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/bundle/mcp-server.mjs +70 -13
- package/plugin/bundle/mcp-server.mjs.map +2 -2
- package/src/dashboard/server.ts +119 -42
- package/src/tests/dashboard-adoption.test.ts +553 -0
- package/src/tests/helpers/dashboard-test-helpers.ts +203 -0
|
@@ -7875,6 +7875,20 @@ async function buildFileDetail(input) {
|
|
|
7875
7875
|
// src/dashboard/server.ts
|
|
7876
7876
|
async function startDashboard(options) {
|
|
7877
7877
|
const { config, sarifStore, workspaceStatsProvider, logger: logger2 } = options;
|
|
7878
|
+
const pidFilePath = resolvePidFilePath(config);
|
|
7879
|
+
const adoption = await tryAdoptExisting(pidFilePath, config.dashboardPort, logger2);
|
|
7880
|
+
if (adoption) {
|
|
7881
|
+
logger2.info(
|
|
7882
|
+
{ url: adoption.url, ownerPid: adoption.pid, port: config.dashboardPort },
|
|
7883
|
+
"adopted existing claude-crap dashboard"
|
|
7884
|
+
);
|
|
7885
|
+
return {
|
|
7886
|
+
url: adoption.url,
|
|
7887
|
+
adopted: true,
|
|
7888
|
+
async close() {
|
|
7889
|
+
}
|
|
7890
|
+
};
|
|
7891
|
+
}
|
|
7878
7892
|
const publicRoot = await resolvePublicRoot(logger2);
|
|
7879
7893
|
const fastify = Fastify({
|
|
7880
7894
|
logger: false,
|
|
@@ -7927,14 +7941,35 @@ async function startDashboard(options) {
|
|
|
7927
7941
|
fastify.get("/", async (_request, reply) => {
|
|
7928
7942
|
return reply.sendFile("index.html");
|
|
7929
7943
|
});
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7944
|
+
try {
|
|
7945
|
+
await fastify.listen({ port: config.dashboardPort, host: "127.0.0.1" });
|
|
7946
|
+
} catch (err) {
|
|
7947
|
+
const code = err.code;
|
|
7948
|
+
if (code === "EADDRINUSE") {
|
|
7949
|
+
await fastify.close().catch(() => {
|
|
7950
|
+
});
|
|
7951
|
+
const raceAdoption = await tryAdoptExisting(pidFilePath, config.dashboardPort, logger2);
|
|
7952
|
+
if (raceAdoption) {
|
|
7953
|
+
logger2.info(
|
|
7954
|
+
{ url: raceAdoption.url, ownerPid: raceAdoption.pid, port: config.dashboardPort },
|
|
7955
|
+
"dashboard bind lost race, adopted concurrent owner"
|
|
7956
|
+
);
|
|
7957
|
+
return {
|
|
7958
|
+
url: raceAdoption.url,
|
|
7959
|
+
adopted: true,
|
|
7960
|
+
async close() {
|
|
7961
|
+
}
|
|
7962
|
+
};
|
|
7963
|
+
}
|
|
7964
|
+
}
|
|
7965
|
+
throw err;
|
|
7966
|
+
}
|
|
7933
7967
|
const url = `http://127.0.0.1:${config.dashboardPort}`;
|
|
7934
7968
|
logger2.info({ url, publicRoot }, "claude-crap dashboard listening");
|
|
7935
7969
|
writePidFile(pidFilePath, config.dashboardPort);
|
|
7936
7970
|
return {
|
|
7937
7971
|
url,
|
|
7972
|
+
adopted: false,
|
|
7938
7973
|
async close() {
|
|
7939
7974
|
removePidFile(pidFilePath);
|
|
7940
7975
|
await fastify.close();
|
|
@@ -8004,29 +8039,40 @@ function isPidAlive(pid) {
|
|
|
8004
8039
|
return false;
|
|
8005
8040
|
}
|
|
8006
8041
|
}
|
|
8007
|
-
async function
|
|
8008
|
-
if (!existsSync(pidFilePath)) return;
|
|
8042
|
+
async function tryAdoptExisting(pidFilePath, port, logger2) {
|
|
8043
|
+
if (!existsSync(pidFilePath)) return null;
|
|
8009
8044
|
let stale;
|
|
8010
8045
|
try {
|
|
8011
8046
|
stale = JSON.parse(readFileSync(pidFilePath, "utf8"));
|
|
8012
8047
|
} catch {
|
|
8048
|
+
logger2.info({ pidFilePath }, "corrupt dashboard pidfile, removing");
|
|
8013
8049
|
removePidFile(pidFilePath);
|
|
8014
|
-
return;
|
|
8050
|
+
return null;
|
|
8015
8051
|
}
|
|
8016
8052
|
if (!isPidAlive(stale.pid)) {
|
|
8017
|
-
logger2.info({ stalePid: stale.pid }, "stale dashboard
|
|
8053
|
+
logger2.info({ stalePid: stale.pid }, "stale dashboard pidfile (process dead), removing");
|
|
8018
8054
|
removePidFile(pidFilePath);
|
|
8019
|
-
return;
|
|
8055
|
+
return null;
|
|
8020
8056
|
}
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8057
|
+
if (stale.port !== port) {
|
|
8058
|
+
logger2.info(
|
|
8059
|
+
{ stalePort: stale.port, wantedPort: port },
|
|
8060
|
+
"dashboard pidfile points at different port, ignoring"
|
|
8061
|
+
);
|
|
8062
|
+
removePidFile(pidFilePath);
|
|
8063
|
+
return null;
|
|
8064
|
+
}
|
|
8065
|
+
const healthy = await probeDashboardHealth(port);
|
|
8066
|
+
if (healthy) {
|
|
8067
|
+
return { url: `http://127.0.0.1:${port}`, pid: stale.pid };
|
|
8068
|
+
}
|
|
8069
|
+
logger2.warn(
|
|
8070
|
+
{ stalePid: stale.pid, port },
|
|
8071
|
+
"dashboard pidfile owner is unresponsive, terminating"
|
|
8024
8072
|
);
|
|
8025
8073
|
try {
|
|
8026
8074
|
process.kill(stale.pid, "SIGTERM");
|
|
8027
8075
|
} catch {
|
|
8028
|
-
removePidFile(pidFilePath);
|
|
8029
|
-
return;
|
|
8030
8076
|
}
|
|
8031
8077
|
for (let i = 0; i < 30; i++) {
|
|
8032
8078
|
if (!isPidAlive(stale.pid)) break;
|
|
@@ -8041,6 +8087,17 @@ async function killStaleDashboard(pidFilePath, port, logger2) {
|
|
|
8041
8087
|
}
|
|
8042
8088
|
removePidFile(pidFilePath);
|
|
8043
8089
|
await new Promise((r) => setTimeout(r, 300));
|
|
8090
|
+
return null;
|
|
8091
|
+
}
|
|
8092
|
+
async function probeDashboardHealth(port) {
|
|
8093
|
+
try {
|
|
8094
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/health`, {
|
|
8095
|
+
signal: AbortSignal.timeout(500)
|
|
8096
|
+
});
|
|
8097
|
+
return res.ok;
|
|
8098
|
+
} catch {
|
|
8099
|
+
return false;
|
|
8100
|
+
}
|
|
8044
8101
|
}
|
|
8045
8102
|
async function buildComplexityReport(config, engine, logger2, exclude) {
|
|
8046
8103
|
const threshold = config.cyclomaticMax;
|