2 Commits

Author SHA1 Message Date
Alejandro Gutiérrez
8c6b0c0e07 fix(cli): v0.5.2 — poll-based push delivery (1s interval)
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
Release / Publish multi-arch images (push) Has been cancelled
Replace WS onPush→notification with timer-based buffer drain.
The old claude-intercom used 1s polling and worked reliably.
WS async callbacks may not flush stdio properly for MCP
notifications. Polling on a timer ensures consistent delivery.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:33:26 +01:00
Alejandro Gutiérrez
ec9626503c fix(web): force-dynamic on payload admin page (build CSS error)
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:16:21 +01:00
3 changed files with 51 additions and 47 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "claudemesh-cli",
"version": "0.5.1",
"version": "0.5.2",
"description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
"keywords": [
"claude-code",

View File

@@ -722,54 +722,56 @@ Your message mode is "${messageMode}".
// any mesh's broker connection becomes a <channel source="claudemesh">
// system reminder injected into Claude Code's context.
for (const client of allClients()) {
client.onPush(async (msg) => {
// In "off" mode, silently skip notification — messages are still
// buffered in pushBuffer and accessible via check_messages.
if (messageMode === "off") return;
// Poll-based push: drain pushBuffer every 1s and emit channel notifications.
// This is the proven approach from claude-intercom. The WS onPush handler
// fires instantly but server.notification() may not flush stdio reliably
// from an async WS callback. Polling on a timer ensures consistent delivery.
if (messageMode !== "off") {
const pushPollTimer = setInterval(async () => {
const buffered = client.drainPushBuffer();
for (const msg of buffered) {
const fromPubkey = msg.senderPubkey || "";
const fromName = fromPubkey
? await resolvePeerName(client, fromPubkey)
: "unknown";
const fromPubkey = msg.senderPubkey || "";
// Resolve sender's display name from the cached peer list.
const fromName = fromPubkey
? await resolvePeerName(client, fromPubkey)
: "unknown";
if (messageMode === "inbox") {
try {
await server.notification({
method: "notifications/claude/channel",
params: {
content: `[inbox] New message from ${fromName}. Use check_messages to read.`,
meta: { kind: "inbox_notification", from_name: fromName },
},
});
} catch { /* best effort */ }
continue;
}
if (messageMode === "inbox") {
// Count-only notification, no content
try {
await server.notification({
method: "notifications/claude/channel",
params: {
content: `[inbox] New message from ${fromName}. Use check_messages to read.`,
meta: { kind: "inbox_notification", from_name: fromName },
},
});
} catch { /* best effort */ }
return;
}
// push mode — full content notification
const content = msg.plaintext ?? decryptFailedWarning(fromPubkey);
try {
await server.notification({
method: "notifications/claude/channel",
params: {
content,
meta: {
from_id: fromPubkey,
from_name: fromName,
mesh_slug: client.meshSlug,
mesh_id: client.meshId,
priority: msg.priority,
sent_at: msg.createdAt,
delivered_at: msg.receivedAt,
kind: msg.kind,
},
},
});
} catch {
/* channel push is best-effort; check_messages is the fallback */
}
});
// push mode — full content
const content = msg.plaintext ?? decryptFailedWarning(fromPubkey);
try {
await server.notification({
method: "notifications/claude/channel",
params: {
content,
meta: {
from_id: fromPubkey,
from_name: fromName,
mesh_slug: client.meshSlug,
mesh_id: client.meshId,
priority: msg.priority,
sent_at: msg.createdAt,
delivered_at: msg.receivedAt,
kind: msg.kind,
},
},
});
} catch { /* best effort */ }
}
}, 1_000);
pushPollTimer.unref();
}
client.onStreamData(async (evt) => {
try {

View File

@@ -4,6 +4,8 @@ import { RootPage, generatePageMetadata } from "@payloadcms/next/views";
import { importMap } from "../importMap";
import config from "@payload-config";
export const dynamic = "force-dynamic";
type Args = { params: Promise<{ segments: string[] }> };
export const generateMetadata = ({ params }: Args) =>