hotsheet 0.17.0-beta.21 → 0.17.0-beta.23
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/dist/channel.js +119 -4
- package/dist/cli.js +16 -5
- package/dist/client/app.global.js +113 -113
- package/package.json +2 -2
package/dist/channel.js
CHANGED
|
@@ -34708,6 +34708,40 @@ function slugifyDataDir(dataDir2) {
|
|
|
34708
34708
|
return slug !== "" ? slug : "project";
|
|
34709
34709
|
}
|
|
34710
34710
|
|
|
34711
|
+
// src/channelLog.ts
|
|
34712
|
+
import { appendFileSync, renameSync, statSync } from "fs";
|
|
34713
|
+
var CHANNEL_LOG_MAX_BYTES = 1048576;
|
|
34714
|
+
function createChannelLogger(logPath) {
|
|
34715
|
+
let injectedBlankLine = false;
|
|
34716
|
+
return {
|
|
34717
|
+
log(event, details) {
|
|
34718
|
+
try {
|
|
34719
|
+
const size = safeStatSize(logPath);
|
|
34720
|
+
if (size >= CHANNEL_LOG_MAX_BYTES) {
|
|
34721
|
+
try {
|
|
34722
|
+
renameSync(logPath, `${logPath}.old`);
|
|
34723
|
+
} catch {
|
|
34724
|
+
}
|
|
34725
|
+
}
|
|
34726
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
34727
|
+
const detailsText = details === void 0 || details === "" ? "" : ` ${details}`;
|
|
34728
|
+
const prefix = !injectedBlankLine && size > 0 ? "\n" : "";
|
|
34729
|
+
injectedBlankLine = true;
|
|
34730
|
+
appendFileSync(logPath, `${prefix}[${ts}] [pid ${process.pid}] ${event}:${detailsText}
|
|
34731
|
+
`, "utf-8");
|
|
34732
|
+
} catch {
|
|
34733
|
+
}
|
|
34734
|
+
}
|
|
34735
|
+
};
|
|
34736
|
+
}
|
|
34737
|
+
function safeStatSize(path) {
|
|
34738
|
+
try {
|
|
34739
|
+
return statSync(path).size;
|
|
34740
|
+
} catch {
|
|
34741
|
+
return 0;
|
|
34742
|
+
}
|
|
34743
|
+
}
|
|
34744
|
+
|
|
34711
34745
|
// src/channelPermissions.ts
|
|
34712
34746
|
var PERMISSION_TTL_MS = 12e4;
|
|
34713
34747
|
var queue = [];
|
|
@@ -34731,6 +34765,48 @@ function clearAllPermissions() {
|
|
|
34731
34765
|
queue.length = 0;
|
|
34732
34766
|
}
|
|
34733
34767
|
|
|
34768
|
+
// src/channelStdioWatcher.ts
|
|
34769
|
+
function installStdioDisconnectHandler(opts) {
|
|
34770
|
+
const { stdin, stdout, onDisconnect, log } = opts;
|
|
34771
|
+
let fired = false;
|
|
34772
|
+
const fire = (reason) => {
|
|
34773
|
+
if (fired) return;
|
|
34774
|
+
fired = true;
|
|
34775
|
+
if (log !== void 0) {
|
|
34776
|
+
log(`stdio disconnected (${reason}) \u2014 triggering channel-server cleanup`);
|
|
34777
|
+
}
|
|
34778
|
+
try {
|
|
34779
|
+
onDisconnect(reason);
|
|
34780
|
+
} catch (err) {
|
|
34781
|
+
if (log !== void 0) {
|
|
34782
|
+
log(`onDisconnect threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
34783
|
+
}
|
|
34784
|
+
}
|
|
34785
|
+
};
|
|
34786
|
+
const onStdinEnd = () => {
|
|
34787
|
+
fire("stdin-end");
|
|
34788
|
+
};
|
|
34789
|
+
const onStdinClose = () => {
|
|
34790
|
+
fire("stdin-close");
|
|
34791
|
+
};
|
|
34792
|
+
const onStdoutError = (err) => {
|
|
34793
|
+
if (err.code === "EPIPE" || err.code === "ECONNRESET" || err.code === "ECANCELED") {
|
|
34794
|
+
fire("stdout-error");
|
|
34795
|
+
}
|
|
34796
|
+
};
|
|
34797
|
+
stdin.on("end", onStdinEnd);
|
|
34798
|
+
stdin.on("close", onStdinClose);
|
|
34799
|
+
stdout.on("error", onStdoutError);
|
|
34800
|
+
let disposed = false;
|
|
34801
|
+
return () => {
|
|
34802
|
+
if (disposed) return;
|
|
34803
|
+
disposed = true;
|
|
34804
|
+
stdin.off("end", onStdinEnd);
|
|
34805
|
+
stdin.off("close", onStdinClose);
|
|
34806
|
+
stdout.off("error", onStdoutError);
|
|
34807
|
+
};
|
|
34808
|
+
}
|
|
34809
|
+
|
|
34734
34810
|
// src/channel.ts
|
|
34735
34811
|
var CHANNEL_VERSION = 7;
|
|
34736
34812
|
var dataDir = ".hotsheet";
|
|
@@ -34744,6 +34820,8 @@ for (let i = 0; i < args.length; i++) {
|
|
|
34744
34820
|
var portFile = join3(dataDir, "channel-port");
|
|
34745
34821
|
var serverSlug = slugifyDataDir(dataDir);
|
|
34746
34822
|
var serverName = `hotsheet-channel-${serverSlug}`;
|
|
34823
|
+
var channelLog = createChannelLogger(join3(dataDir, "mcp.log"));
|
|
34824
|
+
channelLog.log("process-start", `argv=${process.argv.slice(2).join(" ")} dataDir=${dataDir} serverName=${serverName}`);
|
|
34747
34825
|
var mcp = new Server(
|
|
34748
34826
|
{ name: serverName, version: "0.1.0" },
|
|
34749
34827
|
{
|
|
@@ -34774,10 +34852,25 @@ mcp.setRequestHandler(ListToolsRequestSchema, () => {
|
|
|
34774
34852
|
});
|
|
34775
34853
|
mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
34776
34854
|
const { name, arguments: args2 } = request.params;
|
|
34855
|
+
channelLog.log("tools/call", `name=${name}`);
|
|
34777
34856
|
const result = await callTool(name, args2 ?? {}, dataDir);
|
|
34778
34857
|
return result;
|
|
34779
34858
|
});
|
|
34780
34859
|
await mcp.connect(new StdioServerTransport());
|
|
34860
|
+
channelLog.log("mcp-connect", "StdioServerTransport ready");
|
|
34861
|
+
installStdioDisconnectHandler({
|
|
34862
|
+
stdin: process.stdin,
|
|
34863
|
+
stdout: process.stdout,
|
|
34864
|
+
log: (msg) => {
|
|
34865
|
+
process.stderr.write(`${serverName}: ${msg}
|
|
34866
|
+
`);
|
|
34867
|
+
channelLog.log("stdio-watcher", msg);
|
|
34868
|
+
},
|
|
34869
|
+
onDisconnect: (reason) => {
|
|
34870
|
+
channelLog.log("disconnect", `reason=${reason} \u2014 invoking cleanup`);
|
|
34871
|
+
void cleanup(`stdio-${reason}`);
|
|
34872
|
+
}
|
|
34873
|
+
});
|
|
34781
34874
|
var PermissionRequestSchema = external_exports3.object({
|
|
34782
34875
|
method: external_exports3.literal("notifications/claude/channel/permission_request"),
|
|
34783
34876
|
params: external_exports3.object({
|
|
@@ -34921,6 +35014,7 @@ var httpServer = createServer(async (req, res) => {
|
|
|
34921
35014
|
}
|
|
34922
35015
|
body += String(chunk);
|
|
34923
35016
|
}
|
|
35017
|
+
channelLog.log("trigger", `bodyBytes=${body.length}`);
|
|
34924
35018
|
try {
|
|
34925
35019
|
await mcp.notification({
|
|
34926
35020
|
method: "notifications/claude/channel",
|
|
@@ -34932,6 +35026,7 @@ var httpServer = createServer(async (req, res) => {
|
|
|
34932
35026
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
34933
35027
|
res.end(JSON.stringify({ ok: true }));
|
|
34934
35028
|
} catch (err) {
|
|
35029
|
+
channelLog.log("trigger-error", String(err));
|
|
34935
35030
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
34936
35031
|
res.end(JSON.stringify({ error: String(err) }));
|
|
34937
35032
|
}
|
|
@@ -34983,10 +35078,18 @@ httpServer.listen(0, "127.0.0.1", () => {
|
|
|
34983
35078
|
}
|
|
34984
35079
|
process.stderr.write(`${serverName} listening on port ${port}
|
|
34985
35080
|
`);
|
|
35081
|
+
channelLog.log("http-listen", `port=${port}`);
|
|
34986
35082
|
void notifyMainServer();
|
|
34987
35083
|
}
|
|
34988
35084
|
});
|
|
34989
|
-
|
|
35085
|
+
var heartbeatInterval = setInterval(() => {
|
|
35086
|
+
const mem = process.memoryUsage();
|
|
35087
|
+
channelLog.log("heartbeat", `uptime=${process.uptime().toFixed(1)}s rss=${(mem.rss / 1024 / 1024).toFixed(1)}MiB`);
|
|
35088
|
+
}, 6e4);
|
|
35089
|
+
heartbeatInterval.unref();
|
|
35090
|
+
async function cleanup(reason = "unspecified") {
|
|
35091
|
+
channelLog.log("cleanup-start", `reason=${reason}`);
|
|
35092
|
+
clearInterval(heartbeatInterval);
|
|
34990
35093
|
try {
|
|
34991
35094
|
unlinkSync(portFile);
|
|
34992
35095
|
} catch {
|
|
@@ -34997,11 +35100,23 @@ async function cleanup() {
|
|
|
34997
35100
|
await notifyMainServer(controller.signal);
|
|
34998
35101
|
} catch {
|
|
34999
35102
|
}
|
|
35103
|
+
channelLog.log("cleanup-end", `reason=${reason} \u2014 exiting`);
|
|
35000
35104
|
process.exit(0);
|
|
35001
35105
|
}
|
|
35002
|
-
process.on("SIGTERM", () =>
|
|
35003
|
-
|
|
35004
|
-
|
|
35106
|
+
process.on("SIGTERM", () => {
|
|
35107
|
+
channelLog.log("signal", "SIGTERM");
|
|
35108
|
+
void cleanup("SIGTERM");
|
|
35109
|
+
});
|
|
35110
|
+
process.on("SIGINT", () => {
|
|
35111
|
+
channelLog.log("signal", "SIGINT");
|
|
35112
|
+
void cleanup("SIGINT");
|
|
35113
|
+
});
|
|
35114
|
+
process.on("SIGHUP", () => {
|
|
35115
|
+
channelLog.log("signal", "SIGHUP");
|
|
35116
|
+
void cleanup("SIGHUP");
|
|
35117
|
+
});
|
|
35118
|
+
process.on("exit", (code) => {
|
|
35119
|
+
channelLog.log("exit", `code=${code}`);
|
|
35005
35120
|
try {
|
|
35006
35121
|
unlinkSync(portFile);
|
|
35007
35122
|
} catch {
|
package/dist/cli.js
CHANGED
|
@@ -21039,7 +21039,15 @@ var init_global_config = __esm({
|
|
|
21039
21039
|
// HS-8290 — terminal-dashboard settings (formerly stored per-project but
|
|
21040
21040
|
// are inherently cross-project since the dashboard shows tiles for every
|
|
21041
21041
|
// registered project in one view). See docs/39-visibility-groupings.md.
|
|
21042
|
-
dashboard: DashboardConfigSchema2.optional()
|
|
21042
|
+
dashboard: DashboardConfigSchema2.optional(),
|
|
21043
|
+
// HS-8446 — global diagnostics opt-in. When true, the slow-server
|
|
21044
|
+
// banner (HS-8175 / HS-8226) is allowed to surface AND the HS-8054
|
|
21045
|
+
// UI-hang toast fires. Default false so the noisier diagnostic
|
|
21046
|
+
// surfaces stay opt-in across every project on this machine. The
|
|
21047
|
+
// freeze-log entries (`<dataDir>/freeze.log`) and the server-side
|
|
21048
|
+
// event-loop heartbeat continue to fire regardless — the gate only
|
|
21049
|
+
// suppresses the in-window UI surfaces.
|
|
21050
|
+
diagnosticsEnabled: external_exports.boolean().optional()
|
|
21043
21051
|
}).strict();
|
|
21044
21052
|
}
|
|
21045
21053
|
});
|
|
@@ -27276,14 +27284,17 @@ pageRoutes.get("/", (c) => {
|
|
|
27276
27284
|
] })
|
|
27277
27285
|
] }) }),
|
|
27278
27286
|
/* @__PURE__ */ jsx("div", { className: "settings-section", style: "margin-top:16px", children: [
|
|
27279
|
-
/* @__PURE__ */ jsx("div", { className: "settings-section-header", children: /* @__PURE__ */ jsx("h3", { children:
|
|
27287
|
+
/* @__PURE__ */ jsx("div", { className: "settings-section-header", children: /* @__PURE__ */ jsx("h3", { children: [
|
|
27288
|
+
"Diagnostics ",
|
|
27289
|
+
/* @__PURE__ */ jsx("span", { className: "global-setting-badge", title: "This setting applies to every project on this machine.", children: "Global Setting" })
|
|
27290
|
+
] }) }),
|
|
27280
27291
|
/* @__PURE__ */ jsx("div", { className: "settings-field settings-field-checkbox", children: [
|
|
27281
27292
|
/* @__PURE__ */ jsx("label", { children: [
|
|
27282
|
-
/* @__PURE__ */ jsx("input", { type: "checkbox", id: "settings-diagnostics-
|
|
27283
|
-
"
|
|
27293
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", id: "settings-diagnostics-enabled" }),
|
|
27294
|
+
" Enable diagnostic UI surfaces (slow-server banner + UI-hang toast)"
|
|
27284
27295
|
] }),
|
|
27285
27296
|
/* @__PURE__ */ jsx("span", { className: "settings-hint", children: [
|
|
27286
|
-
"When on,
|
|
27297
|
+
"When on, the slow-server banner (HS-8175 / HS-8226) surfaces when an HTTP request stays in flight past 3 s, and the HS-8054 longtask observer emits a small toast for each \u2265 500 ms UI hang (rate-limited to once every 10 s). Off by default \u2014 both surfaces are primarily useful when actively investigating event-loop blocks. Freezes are always logged to ",
|
|
27287
27298
|
/* @__PURE__ */ jsx("code", { children: "<dataDir>/freeze.log" }),
|
|
27288
27299
|
" for diagnostics regardless of this setting."
|
|
27289
27300
|
] })
|