13 Commits

Author SHA1 Message Date
Alejandro Gutiérrez
a987e9e27b fix(cli): v0.1.14 — persist displayName in config file, not env var
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
Write displayName into tmpdir config.json so the MCP server reads
it directly. Env vars from claudemesh launch may not propagate to
MCP child processes spawned by Claude Code. Config file is reliable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 12:18:08 +01:00
Alejandro Gutiérrez
ff86db615f style(cli): tighten autonomous mode confirmation copy
Some checks failed
CI / Docker build (linux/amd64) (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:54:55 +01:00
Alejandro Gutiérrez
4aa61b40e2 feat(cli): v0.1.13 — autonomous mode with user confirmation
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
claudemesh launch now passes --dangerously-skip-permissions to
claude so peers can chat without per-tool-call approval prompts.
Shows a clear explanation before launch; user confirms with Enter.
Skip with -y/--yes for CI or repeat launches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:53:13 +01:00
Alejandro Gutiérrez
4afe365c00 fix(cli): v0.1.12 — resolve sender display name in push notifications
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
onPush now queries list_peers to resolve the sender's pubkey to their
display name. Instructions updated to tell Claude to reply by name
instead of raw pubkey. Fixes two-way messaging between named peers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:45:40 +01:00
Alejandro Gutiérrez
92bb276a3e fix: v0.1.11 — fix crypto_box decryption with session pubkeys
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
Store sender's sessionPubkey on message_queue at send time.
drainForMember returns COALESCE(sender_session_pubkey, peer_pubkey)
so the recipient gets the correct sender key for decryption.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:23:42 +01:00
Alejandro Gutiérrez
af8f8ed1f9 feat: v0.1.10 — per-session ephemeral keypairs
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
Each WS connection generates its own ed25519 keypair (sessionPubkey)
sent in the hello handshake. The broker stores it on the presence
row and uses it for message routing + list_peers. This gives every
`claudemesh launch` a unique crypto identity without burning invite
uses — member auth stays permanent, session identity is ephemeral.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:14:33 +01:00
Alejandro Gutiérrez
c8682dd700 fix(cli): deduplicate --dangerously-load-development-channels flag
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
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:56:30 +01:00
Alejandro Gutiérrez
004602a83c fix(cli): v0.1.8 — remove Zod dependency (bun bundler crash)
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 Zod schemas with plain TypeScript validation in env.ts,
config.ts, and invite/parse.ts. Zod 4 classes break under bun
build --target=node (Class2 is not a constructor).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:51:42 +01:00
Alejandro Gutiérrez
2a2aac3622 feat(cli): v0.1.7 — --name, --mesh, --join flags for launch
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
- `claudemesh launch --name Mou` sets per-session display name
- `claudemesh launch --mesh car-dealers` selects mesh (interactive picker if >1)
- `claudemesh launch --join <token-or-url>` joins a mesh inline before launching
- Broker stores per-presence displayName override (prefers over member default)
- Session config isolated via tmpdir (auto-cleanup on exit)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:45:29 +01:00
Alejandro Gutiérrez
e0659b0b6f feat(cli): v0.1.6 — name-based peer routing in send_message
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
resolveClient() now resolves display names via list_peers WS query.
Supports exact match, partial match (unique substring), and falls
back to pubkey/channel/broadcast pass-through.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:09:00 +01:00
Alejandro Gutiérrez
4c057be069 fix(web): re-apply all landing page content fixes (linter reverted)
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
A linter/formatter reverted our content edits. Re-applying:
- Hero: concrete claims, no WhatsApp/Slack promises, beta pricing
- Logo bar: tech stack instead of fake customer logos
- Pricing: single honest Public Beta tier (removed $12/$24/$99)
- FAQ: real install flow, honest pricing language
- Features: claudemesh.com/install URL
- Toaster: v0.1.4 announcement
- Copy: "volunteers" / "shares" instead of jargon
- Links: #docs → GitHub README, claudemesh.sh → claudemesh.com

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:02:44 +01:00
Alejandro Gutiérrez
aaab7feea6 fix(web): restore turbopack SVG loader (fixes React #130)
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
The turbopack.rules config for @svgr/webpack was removed during
the Payload integration attempts. Without it, SVG imports return
raw module objects instead of React components. This crashes
LocaleCustomizer → Icons.UnitedKingdom → object → React #130.

Next.js 16.2.2 supports turbopack in production builds, so this
config is safe now.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 09:48:36 +01:00
Alejandro Gutiérrez
af13125424 chore(web): restore next.js 16.2.2 (React #130 is pre-existing)
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
The hydration crash exists on both 16.0.10 and 16.2.2 — it's a
pre-existing component bug, not a Next.js regression. Stay on
latest for security + Payload compat when we re-add it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 09:38:22 +01:00
28 changed files with 647 additions and 669 deletions

View File

@@ -307,6 +307,8 @@ export async function refreshStatusFromJsonl(
export interface ConnectParams { export interface ConnectParams {
memberId: string; memberId: string;
sessionId: string; sessionId: string;
sessionPubkey?: string;
displayName?: string;
pid: number; pid: number;
cwd: string; cwd: string;
} }
@@ -321,6 +323,8 @@ export async function connectPresence(
.values({ .values({
memberId: params.memberId, memberId: params.memberId,
sessionId: params.sessionId, sessionId: params.sessionId,
sessionPubkey: params.sessionPubkey ?? null,
displayName: params.displayName ?? null,
pid: params.pid, pid: params.pid,
cwd: params.cwd, cwd: params.cwd,
status: "idle", status: "idle",
@@ -369,8 +373,10 @@ export async function listPeersInMesh(
> { > {
const rows = await db const rows = await db
.select({ .select({
pubkey: memberTable.peerPubkey, memberPubkey: memberTable.peerPubkey,
displayName: memberTable.displayName, sessionPubkey: presence.sessionPubkey,
memberDisplayName: memberTable.displayName,
presenceDisplayName: presence.displayName,
status: presence.status, status: presence.status,
summary: presence.summary, summary: presence.summary,
sessionId: presence.sessionId, sessionId: presence.sessionId,
@@ -385,7 +391,15 @@ export async function listPeersInMesh(
), ),
) )
.orderBy(asc(presence.connectedAt)); .orderBy(asc(presence.connectedAt));
return rows; // Prefer session pubkey for routing, session displayName for display.
return rows.map((r) => ({
pubkey: r.sessionPubkey || r.memberPubkey,
displayName: r.presenceDisplayName || r.memberDisplayName,
status: r.status,
summary: r.summary,
sessionId: r.sessionId,
connectedAt: r.connectedAt,
}));
} }
/** Update the summary text on a presence row. */ /** Update the summary text on a presence row. */
@@ -404,6 +418,7 @@ export async function setSummary(
export interface QueueParams { export interface QueueParams {
meshId: string; meshId: string;
senderMemberId: string; senderMemberId: string;
senderSessionPubkey?: string;
targetSpec: string; targetSpec: string;
priority: Priority; priority: Priority;
nonce: string; nonce: string;
@@ -418,6 +433,7 @@ export async function queueMessage(params: QueueParams): Promise<string> {
.values({ .values({
meshId: params.meshId, meshId: params.meshId,
senderMemberId: params.senderMemberId, senderMemberId: params.senderMemberId,
senderSessionPubkey: params.senderSessionPubkey ?? null,
targetSpec: params.targetSpec, targetSpec: params.targetSpec,
priority: params.priority, priority: params.priority,
nonce: params.nonce, nonce: params.nonce,
@@ -458,6 +474,7 @@ export async function drainForMember(
_memberId: string, _memberId: string,
memberPubkey: string, memberPubkey: string,
status: PeerStatus, status: PeerStatus,
sessionPubkey?: string,
): Promise< ): Promise<
Array<{ Array<{
id: string; id: string;
@@ -498,14 +515,14 @@ export async function drainForMember(
WHERE mesh_id = ${meshId} WHERE mesh_id = ${meshId}
AND delivered_at IS NULL AND delivered_at IS NULL
AND priority::text IN (${priorityList}) AND priority::text IN (${priorityList})
AND (target_spec = ${memberPubkey} OR target_spec = '*') AND (target_spec = ${memberPubkey} OR target_spec = '*'${sessionPubkey ? sql` OR target_spec = ${sessionPubkey}` : sql``})
ORDER BY created_at ASC, id ASC ORDER BY created_at ASC, id ASC
FOR UPDATE SKIP LOCKED FOR UPDATE SKIP LOCKED
) )
AND m.id = mq.sender_member_id AND m.id = mq.sender_member_id
RETURNING mq.id, mq.priority, mq.nonce, mq.ciphertext, RETURNING mq.id, mq.priority, mq.nonce, mq.ciphertext,
mq.created_at, mq.sender_member_id, mq.created_at, mq.sender_member_id,
m.peer_pubkey AS sender_pubkey COALESCE(mq.sender_session_pubkey, m.peer_pubkey) AS sender_pubkey
) )
SELECT * FROM claimed ORDER BY created_at ASC, id ASC SELECT * FROM claimed ORDER BY created_at ASC, id ASC
`); `);

View File

@@ -56,6 +56,7 @@ interface PeerConn {
meshId: string; meshId: string;
memberId: string; memberId: string;
memberPubkey: string; memberPubkey: string;
sessionPubkey: string | null;
cwd: string; cwd: string;
} }
@@ -93,6 +94,7 @@ async function maybePushQueuedMessages(presenceId: string): Promise<void> {
conn.memberId, conn.memberId,
conn.memberPubkey, conn.memberPubkey,
status, status,
conn.sessionPubkey ?? undefined,
); );
for (const m of messages) { for (const m of messages) {
const push: WSPushMessage = { const push: WSPushMessage = {
@@ -400,6 +402,8 @@ async function handleHello(
const presenceId = await connectPresence({ const presenceId = await connectPresence({
memberId: member.id, memberId: member.id,
sessionId: hello.sessionId, sessionId: hello.sessionId,
sessionPubkey: hello.sessionPubkey,
displayName: hello.displayName,
pid: hello.pid, pid: hello.pid,
cwd: hello.cwd, cwd: hello.cwd,
}); });
@@ -408,12 +412,14 @@ async function handleHello(
meshId: hello.meshId, meshId: hello.meshId,
memberId: member.id, memberId: member.id,
memberPubkey: hello.pubkey, memberPubkey: hello.pubkey,
sessionPubkey: hello.sessionPubkey ?? null,
cwd: hello.cwd, cwd: hello.cwd,
}); });
incMeshCount(hello.meshId); incMeshCount(hello.meshId);
const effectiveDisplayName = hello.displayName || member.displayName;
log.info("ws hello", { log.info("ws hello", {
mesh_id: hello.meshId, mesh_id: hello.meshId,
member: member.displayName, member: effectiveDisplayName,
presence_id: presenceId, presence_id: presenceId,
session_id: hello.sessionId, session_id: hello.sessionId,
}); });
@@ -422,7 +428,7 @@ async function handleHello(
// races the caller's closure assignment, causing subsequent client // races the caller's closure assignment, causing subsequent client
// messages to fail the "no_hello" check. // messages to fail the "no_hello" check.
void maybePushQueuedMessages(presenceId); void maybePushQueuedMessages(presenceId);
return { presenceId, memberDisplayName: member.displayName }; return { presenceId, memberDisplayName: effectiveDisplayName };
} }
async function handleSend( async function handleSend(
@@ -432,6 +438,7 @@ async function handleSend(
const messageId = await queueMessage({ const messageId = await queueMessage({
meshId: conn.meshId, meshId: conn.meshId,
senderMemberId: conn.memberId, senderMemberId: conn.memberId,
senderSessionPubkey: conn.sessionPubkey ?? undefined,
targetSpec: msg.targetSpec, targetSpec: msg.targetSpec,
priority: msg.priority, priority: msg.priority,
nonce: msg.nonce, nonce: msg.nonce,
@@ -448,7 +455,9 @@ async function handleSend(
// Fan-out over connected peers in the same mesh. // Fan-out over connected peers in the same mesh.
for (const [pid, peer] of connections) { for (const [pid, peer] of connections) {
if (peer.meshId !== conn.meshId) continue; if (peer.meshId !== conn.meshId) continue;
if (msg.targetSpec !== "*" && peer.memberPubkey !== msg.targetSpec) if (msg.targetSpec !== "*"
&& peer.memberPubkey !== msg.targetSpec
&& peer.sessionPubkey !== msg.targetSpec)
continue; continue;
void maybePushQueuedMessages(pid); void maybePushQueuedMessages(pid);
} }

View File

@@ -52,6 +52,8 @@ export interface WSHelloMessage {
meshId: string; meshId: string;
memberId: string; memberId: string;
pubkey: string; // must match mesh.member.peerPubkey pubkey: string; // must match mesh.member.peerPubkey
sessionPubkey?: string; // ephemeral per-launch pubkey for message routing
displayName?: string; // optional override for this session
sessionId: string; sessionId: string;
pid: number; pid: number;
cwd: string; cwd: string;

View File

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

View File

@@ -14,7 +14,10 @@ import { parseInviteLink } from "../invite/parse";
import { enrollWithBroker } from "../invite/enroll"; import { enrollWithBroker } from "../invite/enroll";
import { generateKeypair } from "../crypto/keypair"; import { generateKeypair } from "../crypto/keypair";
import { loadConfig, saveConfig, getConfigPath } from "../state/config"; import { loadConfig, saveConfig, getConfigPath } from "../state/config";
import { hostname } from "node:os"; import { writeFileSync, mkdirSync } from "node:fs";
import { join, dirname } from "node:path";
import { homedir, hostname } from "node:os";
import { env } from "../env";
export async function runJoin(args: string[]): Promise<void> { export async function runJoin(args: string[]): Promise<void> {
const link = args[0]; const link = args[0];
@@ -78,6 +81,16 @@ export async function runJoin(args: string[]): Promise<void> {
}); });
saveConfig(config); saveConfig(config);
// 4b. Store invite token for per-session re-enrollment (launch --name).
const configDir = env.CLAUDEMESH_CONFIG_DIR ?? join(homedir(), ".claudemesh");
const inviteFile = join(configDir, `invite-${payload.mesh_slug}.txt`);
try {
mkdirSync(dirname(inviteFile), { recursive: true });
writeFileSync(inviteFile, link, "utf-8");
} catch {
// Non-fatal — launch will fall back to shared identity.
}
// 5. Report. // 5. Report.
console.log(""); console.log("");
console.log( console.log(

View File

@@ -1,82 +1,285 @@
/** /**
* `claudemesh launch` — spawn `claude` with the dev-channel flag so the * `claudemesh launch` — spawn `claude` with peer mesh identity.
* claudemesh MCP server's `notifications/claude/channel` pushes get
* injected as system reminders mid-turn.
* *
* Equivalent to: * Flow:
* claude --dangerously-load-development-channels server:claudemesh [extra args] * 1. Parse --name, --join, --mesh, --quiet flags
* * 2. If --join: run join flow first (accepts token or URL)
* Any additional args (e.g. --model opus, --resume, -c) are passed * 3. Load config → pick mesh (auto if 1, interactive picker if >1)
* through verbatim. Use --quiet to skip the informational banner. * 4. Write per-session config to tmpdir (isolates mesh selection)
* 5. Spawn claude with CLAUDEMESH_CONFIG_DIR + CLAUDEMESH_DISPLAY_NAME
* 6. On exit: cleanup tmpdir
*/ */
import { spawn } from "node:child_process"; import { spawn } from "node:child_process";
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
import { tmpdir, hostname } from "node:os";
import { join } from "node:path";
import { createInterface } from "node:readline";
import { loadConfig, getConfigPath } from "../state/config"; import { loadConfig, getConfigPath } from "../state/config";
import type { Config, JoinedMesh } from "../state/config";
function printBanner(): void { // --- Arg parsing ---
interface LaunchArgs {
name: string | null;
joinLink: string | null;
meshSlug: string | null;
quiet: boolean;
skipPermConfirm: boolean;
claudeArgs: string[];
}
function parseArgs(argv: string[]): LaunchArgs {
const result: LaunchArgs = {
name: null,
joinLink: null,
meshSlug: null,
quiet: false,
skipPermConfirm: false,
claudeArgs: [],
};
let i = 0;
while (i < argv.length) {
const arg = argv[i]!;
if (arg === "--name" && i + 1 < argv.length) {
result.name = argv[++i]!;
} else if (arg.startsWith("--name=")) {
result.name = arg.slice("--name=".length);
} else if (arg === "--join" && i + 1 < argv.length) {
result.joinLink = argv[++i]!;
} else if (arg.startsWith("--join=")) {
result.joinLink = arg.slice("--join=".length);
} else if (arg === "--mesh" && i + 1 < argv.length) {
result.meshSlug = argv[++i]!;
} else if (arg.startsWith("--mesh=")) {
result.meshSlug = arg.slice("--mesh=".length);
} else if (arg === "--quiet") {
result.quiet = true;
} else if (arg === "-y" || arg === "--yes") {
result.skipPermConfirm = true;
} else if (arg === "--") {
result.claudeArgs.push(...argv.slice(i + 1));
break;
} else {
result.claudeArgs.push(arg);
}
i++;
}
return result;
}
// --- Interactive mesh picker ---
async function pickMesh(meshes: JoinedMesh[]): Promise<JoinedMesh> {
if (meshes.length === 1) return meshes[0]!;
console.log("\n Select mesh:");
meshes.forEach((m, i) => {
console.log(` ${i + 1}) ${m.slug}`);
});
console.log("");
const rl = createInterface({ input: process.stdin, output: process.stdout });
return new Promise((resolve) => {
rl.question(" Choice [1]: ", (answer) => {
rl.close();
const idx = parseInt(answer || "1", 10) - 1;
if (idx >= 0 && idx < meshes.length) {
resolve(meshes[idx]!);
} else {
console.error(" Invalid choice, using first mesh.");
resolve(meshes[0]!);
}
});
});
}
// --- Permission confirmation ---
async function confirmPermissions(): Promise<void> {
const useColor =
!process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
const bold = (s: string): string => (useColor ? `\x1b[1m${s}\x1b[22m` : s);
const dim = (s: string): string => (useColor ? `\x1b[2m${s}\x1b[22m` : s);
const yellow = (s: string): string => (useColor ? `\x1b[33m${s}\x1b[39m` : s);
console.log(yellow(bold(" Autonomous mode")));
console.log("");
console.log(" Claude will send and receive peer messages without asking");
console.log(" you first. Peers exchange text only — no file access,");
console.log(" no tool calls, no code execution.");
console.log("");
console.log(dim(" Same as: claude --dangerously-skip-permissions"));
console.log(dim(" Skip this prompt: claudemesh launch -y"));
console.log("");
const rl = createInterface({ input: process.stdin, output: process.stdout });
return new Promise((resolve, reject) => {
rl.question(` ${bold("Continue?")} [Y/n] `, (answer) => {
rl.close();
const a = answer.trim().toLowerCase();
if (a === "" || a === "y" || a === "yes") {
resolve();
} else {
console.log("\n Aborted. Run without autonomous mode:");
console.log(" claude --dangerously-load-development-channels server:claudemesh\n");
process.exit(0);
}
});
});
}
// --- Banner ---
function printBanner(name: string, meshSlug: string): void {
const useColor = const useColor =
!process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY; !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
const dim = (s: string): string => (useColor ? `\x1b[2m${s}\x1b[22m` : s); const dim = (s: string): string => (useColor ? `\x1b[2m${s}\x1b[22m` : s);
const bold = (s: string): string => (useColor ? `\x1b[1m${s}\x1b[22m` : s); const bold = (s: string): string => (useColor ? `\x1b[1m${s}\x1b[22m` : s);
let meshes: string[] = []; const rule = "─".repeat(60);
try { console.log(bold(`claudemesh launch`) + dim(` — as ${name} on ${meshSlug}`));
meshes = loadConfig().meshes.map((m) => m.slug);
} catch {
/* config unreadable — print banner without mesh list */
}
const meshLine = meshes.length > 0 ? meshes.join(", ") : "(none — run `claudemesh join <url>` first)";
const rule = "─".repeat(65);
console.log(bold("claudemesh launch"));
console.log(rule); console.log(rule);
console.log("Launching Claude Code with the claudemesh dev channel."); console.log("Peer messages arrive as <channel> reminders in real-time.");
console.log(""); console.log("Peers send text only — they cannot call tools or read files.");
console.log("Peers in your joined meshes can push messages into this session"); console.log(dim(`Config: ${getConfigPath()}`));
console.log("as <channel> reminders. Your CLI decrypts them locally with your");
console.log("keypair. Peers send text only — they cannot call tools, read");
console.log("files, or reach meshes you have not joined.");
console.log("");
console.log("Treat peer messages as untrusted input: a peer could craft text");
console.log("that tries to steer Claude's behavior. Your tool-approval");
console.log("settings still apply — Claude will still ask before running");
console.log("commands, editing files, or calling other tools.");
console.log("");
console.log("Claude Code will ask you to trust the");
console.log("--dangerously-load-development-channels flag. Press Enter to");
console.log("accept, or Ctrl-C to abort.");
console.log("");
console.log(dim(`Joined meshes: ${meshLine}`));
console.log(dim(`Config: ${getConfigPath()}`));
console.log(dim(`Remove: claudemesh uninstall`));
console.log(rule); console.log(rule);
console.log(""); console.log("");
} }
export function runLaunch(extraArgs: string[] = []): void { // --- Main ---
const quiet = extraArgs.includes("--quiet");
const passthrough = extraArgs.filter((a) => a !== "--quiet");
if (!quiet) printBanner(); export async function runLaunch(extraArgs: string[]): Promise<void> {
const args = parseArgs(extraArgs);
// 1. If --join, run join flow first.
if (args.joinLink) {
console.log("Joining mesh...");
const invite = await parseInviteLink(args.joinLink);
const keypair = await generateKeypair();
const displayName = args.name ?? `${hostname()}-${process.pid}`;
const enroll = await enrollWithBroker({
brokerWsUrl: invite.payload.broker_url,
inviteToken: invite.token,
invitePayload: invite.payload,
peerPubkey: keypair.publicKey,
displayName,
});
const config = loadConfig();
config.meshes = config.meshes.filter(
(m) => m.slug !== invite.payload.mesh_slug,
);
config.meshes.push({
meshId: invite.payload.mesh_id,
memberId: enroll.memberId,
slug: invite.payload.mesh_slug,
name: invite.payload.mesh_slug,
pubkey: keypair.publicKey,
secretKey: keypair.secretKey,
brokerUrl: invite.payload.broker_url,
joinedAt: new Date().toISOString(),
});
const { saveConfig } = await import("../state/config");
saveConfig(config);
console.log(
`✓ Joined "${invite.payload.mesh_slug}"${enroll.alreadyMember ? " (already member)" : ""}`,
);
}
// 2. Load config, pick mesh.
const config = loadConfig();
if (config.meshes.length === 0) {
console.error(
"No meshes joined. Run `claudemesh join <url>` or use --join <url>.",
);
process.exit(1);
}
let mesh: JoinedMesh;
if (args.meshSlug) {
const found = config.meshes.find((m) => m.slug === args.meshSlug);
if (!found) {
console.error(
`Mesh "${args.meshSlug}" not found. Joined: ${config.meshes.map((m) => m.slug).join(", ")}`,
);
process.exit(1);
}
mesh = found;
} else {
mesh = await pickMesh(config.meshes);
}
// 3. Session identity. The WS client auto-generates a per-session
// ephemeral keypair on connect (sent in hello as sessionPubkey).
// We just set the display name via env var.
const displayName = args.name ?? `${hostname()}-${process.pid}`;
// 4. Write session config to tmpdir (isolates mesh selection).
const tmpDir = mkdtempSync(join(tmpdir(), "claudemesh-"));
const sessionConfig: Config = {
version: 1,
meshes: [mesh],
displayName,
};
writeFileSync(
join(tmpDir, "config.json"),
JSON.stringify(sessionConfig, null, 2) + "\n",
"utf-8",
);
// 5. Banner + permission confirmation.
if (!args.quiet) {
printBanner(displayName, mesh.slug);
// Auto-permissions confirmation — needed for autonomous peer messaging.
if (!args.skipPermConfirm) {
await confirmPermissions();
}
}
// 6. Spawn claude with ephemeral config + dev channel + auto-permissions.
// Strip any user-supplied --dangerously flags to avoid duplicates.
const filtered: string[] = [];
for (let i = 0; i < args.claudeArgs.length; i++) {
if (args.claudeArgs[i] === "--dangerously-load-development-channels"
|| args.claudeArgs[i] === "--dangerously-skip-permissions") {
if (args.claudeArgs[i] === "--dangerously-load-development-channels") i++;
continue;
}
filtered.push(args.claudeArgs[i]!);
}
const claudeArgs = [ const claudeArgs = [
"--dangerously-load-development-channels", "--dangerously-load-development-channels",
"server:claudemesh", "server:claudemesh",
...passthrough, "--dangerously-skip-permissions",
...filtered,
]; ];
// Windows: npm global binaries are .cmd shims. Node's spawn without
// shell:true does not resolve PATHEXT, so we need shell:true on win32
// to find claude.cmd. POSIX stays shell-less to avoid quoting surprises.
const isWindows = process.platform === "win32"; const isWindows = process.platform === "win32";
const child = spawn("claude", claudeArgs, { const child = spawn("claude", claudeArgs, {
stdio: "inherit", stdio: "inherit",
shell: isWindows, shell: isWindows,
env: {
...process.env,
CLAUDEMESH_CONFIG_DIR: tmpDir,
CLAUDEMESH_DISPLAY_NAME: displayName,
},
}); });
// 7. Cleanup on exit.
const cleanup = (): void => {
try {
rmSync(tmpDir, { recursive: true, force: true });
} catch {
/* best effort */
}
};
child.on("error", (err: NodeJS.ErrnoException) => { child.on("error", (err: NodeJS.ErrnoException) => {
cleanup();
if (err.code === "ENOENT") { if (err.code === "ENOENT") {
console.error( console.error(
"✗ `claude` not found on PATH. Install Claude Code first: https://claude.com/claude-code", "✗ `claude` not found on PATH. Install Claude Code first.",
); );
} else { } else {
console.error(`✗ failed to launch claude: ${err.message}`); console.error(`✗ failed to launch claude: ${err.message}`);
@@ -85,10 +288,15 @@ export function runLaunch(extraArgs: string[] = []): void {
}); });
child.on("exit", (code, signal) => { child.on("exit", (code, signal) => {
cleanup();
if (signal) { if (signal) {
process.kill(process.pid, signal); process.kill(process.pid, signal);
return; return;
} }
process.exit(code ?? 0); process.exit(code ?? 0);
}); });
// Cleanup on parent signals too.
process.on("SIGTERM", () => { cleanup(); process.exit(0); });
process.on("SIGINT", () => { cleanup(); process.exit(0); });
} }

View File

@@ -1,27 +1,23 @@
import { z } from "zod";
/** /**
* CLI environment config. * CLI environment config.
* *
* Read once at startup. Overridable via env vars so users can point * Read once at startup. Overridable via env vars so users can point
* at a self-hosted broker or a staging instance without rebuilding. * at a self-hosted broker or a staging instance without rebuilding.
*/ */
const envSchema = z.object({
CLAUDEMESH_BROKER_URL: z.string().default("wss://ic.claudemesh.com/ws"),
CLAUDEMESH_CONFIG_DIR: z.string().optional(),
CLAUDEMESH_DEBUG: z.coerce.boolean().default(false),
});
export type CliEnv = z.infer<typeof envSchema>; export interface CliEnv {
CLAUDEMESH_BROKER_URL: string;
CLAUDEMESH_CONFIG_DIR: string | undefined;
CLAUDEMESH_DEBUG: boolean;
}
export function loadEnv(): CliEnv { export function loadEnv(): CliEnv {
const parsed = envSchema.safeParse(process.env); return {
if (!parsed.success) { CLAUDEMESH_BROKER_URL:
console.error("[claudemesh] invalid environment:"); process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
console.error(z.treeifyError(parsed.error)); CLAUDEMESH_CONFIG_DIR: process.env.CLAUDEMESH_CONFIG_DIR || undefined,
process.exit(1); CLAUDEMESH_DEBUG: process.env.CLAUDEMESH_DEBUG === "1" || process.env.CLAUDEMESH_DEBUG === "true",
} };
return parsed.data;
} }
export const env = loadEnv(); export const env = loadEnv();

View File

@@ -30,9 +30,12 @@ Commands:
install Register MCP + Stop/UserPromptSubmit status hooks install Register MCP + Stop/UserPromptSubmit status hooks
(add --no-hooks for bare MCP registration) (add --no-hooks for bare MCP registration)
uninstall Remove MCP server + hooks uninstall Remove MCP server + hooks
launch [args] Launch Claude Code with real-time push messages enabled launch [opts] Launch Claude Code with real-time push messages
(add --quiet to skip the info banner; passes through --name <name> Display name for this session
extra flags, e.g. --model, --resume) --mesh <slug> Select mesh (picker if >1, omitted)
--join <url> Join a mesh before launching
--quiet Skip the info banner
-- <args> Pass remaining args to claude
join <url> Join a mesh via https://claudemesh.com/join/... URL join <url> Join a mesh via https://claudemesh.com/join/... URL
list Show all joined meshes list Show all joined meshes
leave <slug> Leave a joined mesh leave <slug> Leave a joined mesh
@@ -67,7 +70,7 @@ async function main(): Promise<void> {
await runHook(args); await runHook(args);
return; return;
case "launch": case "launch":
runLaunch(args); await runLaunch(args);
return; return;
case "join": case "join":
await runJoin(args); await runJoin(args);

View File

@@ -5,22 +5,19 @@
* verification and one-time-use invite-token tracking land in Step 18. * verification and one-time-use invite-token tracking land in Step 18.
*/ */
import { z } from "zod";
import { ensureSodium } from "../crypto/keypair"; import { ensureSodium } from "../crypto/keypair";
const invitePayloadSchema = z.object({ export interface InvitePayload {
v: z.literal(1), v: 1;
mesh_id: z.string().min(1), mesh_id: string;
mesh_slug: z.string().min(1), mesh_slug: string;
broker_url: z.string().min(1), broker_url: string;
expires_at: z.number().int().positive(), expires_at: number;
mesh_root_key: z.string().min(1), mesh_root_key: string;
role: z.enum(["admin", "member"]), role: "admin" | "member";
owner_pubkey: z.string().regex(/^[0-9a-f]{64}$/i), owner_pubkey: string;
signature: z.string().regex(/^[0-9a-f]{128}$/i), signature: string;
}); }
export type InvitePayload = z.infer<typeof invitePayloadSchema>;
export interface ParsedInvite { export interface ParsedInvite {
payload: InvitePayload; payload: InvitePayload;
@@ -28,6 +25,21 @@ export interface ParsedInvite {
token: string; // base64url(JSON) — DB lookup key (everything after ic://join/) token: string; // base64url(JSON) — DB lookup key (everything after ic://join/)
} }
function validatePayload(obj: unknown): InvitePayload {
if (!obj || typeof obj !== "object") throw new Error("invite payload is not an object");
const o = obj as Record<string, unknown>;
if (o.v !== 1) throw new Error("invite payload: v must be 1");
if (typeof o.mesh_id !== "string" || !o.mesh_id) throw new Error("invite payload: mesh_id required");
if (typeof o.mesh_slug !== "string" || !o.mesh_slug) throw new Error("invite payload: mesh_slug required");
if (typeof o.broker_url !== "string" || !o.broker_url) throw new Error("invite payload: broker_url required");
if (typeof o.expires_at !== "number" || o.expires_at <= 0) throw new Error("invite payload: expires_at must be a positive number");
if (typeof o.mesh_root_key !== "string" || !o.mesh_root_key) throw new Error("invite payload: mesh_root_key required");
if (o.role !== "admin" && o.role !== "member") throw new Error("invite payload: role must be admin or member");
if (typeof o.owner_pubkey !== "string" || !/^[0-9a-f]{64}$/i.test(o.owner_pubkey)) throw new Error("invite payload: owner_pubkey must be 64 hex chars");
if (typeof o.signature !== "string" || !/^[0-9a-f]{128}$/i.test(o.signature)) throw new Error("invite payload: signature must be 128 hex chars");
return o as unknown as InvitePayload;
}
/** Canonical invite bytes — must match broker's canonicalInvite(). */ /** Canonical invite bytes — must match broker's canonicalInvite(). */
export function canonicalInvite(p: { export function canonicalInvite(p: {
v: number; v: number;
@@ -96,41 +108,34 @@ export async function parseInviteLink(link: string): Promise<ParsedInvite> {
); );
} }
const parsed = invitePayloadSchema.safeParse(obj); const payload = validatePayload(obj);
if (!parsed.success) {
throw new Error(
`invite link shape invalid: ${parsed.error.issues.map((i) => i.path.join(".") + ": " + i.message).join("; ")}`,
);
}
// Expiry check (unix seconds). // Expiry check (unix seconds).
const nowSeconds = Math.floor(Date.now() / 1000); const nowSeconds = Math.floor(Date.now() / 1000);
if (parsed.data.expires_at < nowSeconds) { if (payload.expires_at < nowSeconds) {
throw new Error( throw new Error(
`invite expired: expires_at=${parsed.data.expires_at}, now=${nowSeconds}`, `invite expired: expires_at=${payload.expires_at}, now=${nowSeconds}`,
); );
} }
// Verify the ed25519 signature against the embedded owner_pubkey. // Verify the ed25519 signature against the embedded owner_pubkey.
// Client-side verification gives immediate feedback on tampered
// links; broker re-verifies authoritatively on /join.
const s = await ensureSodium(); const s = await ensureSodium();
const canonical = canonicalInvite({ const canonical = canonicalInvite({
v: parsed.data.v, v: payload.v,
mesh_id: parsed.data.mesh_id, mesh_id: payload.mesh_id,
mesh_slug: parsed.data.mesh_slug, mesh_slug: payload.mesh_slug,
broker_url: parsed.data.broker_url, broker_url: payload.broker_url,
expires_at: parsed.data.expires_at, expires_at: payload.expires_at,
mesh_root_key: parsed.data.mesh_root_key, mesh_root_key: payload.mesh_root_key,
role: parsed.data.role, role: payload.role,
owner_pubkey: parsed.data.owner_pubkey, owner_pubkey: payload.owner_pubkey,
}); });
const sigOk = (() => { const sigOk = (() => {
try { try {
return s.crypto_sign_verify_detached( return s.crypto_sign_verify_detached(
s.from_hex(parsed.data.signature), s.from_hex(payload.signature),
s.from_string(canonical), s.from_string(canonical),
s.from_hex(parsed.data.owner_pubkey), s.from_hex(payload.owner_pubkey),
); );
} catch { } catch {
return false; return false;
@@ -140,7 +145,7 @@ export async function parseInviteLink(link: string): Promise<ParsedInvite> {
throw new Error("invite signature invalid (link tampered?)"); throw new Error("invite signature invalid (link tampered?)");
} }
return { payload: parsed.data, raw: link, token: encoded }; return { payload, raw: link, token: encoded };
} }
/** /**
@@ -155,8 +160,6 @@ export function encodeInviteLink(payload: InvitePayload): string {
/** /**
* Sign and assemble an invite payload → ic://join/... link. * Sign and assemble an invite payload → ic://join/... link.
* The canonical bytes (everything except signature) are signed with
* the mesh owner's ed25519 secret key.
*/ */
export async function buildSignedInvite(args: { export async function buildSignedInvite(args: {
v: 1; v: 1;

View File

@@ -33,39 +33,68 @@ function text(msg: string, isError = false) {
/** /**
* Given a `to` string, pick which mesh to send from. Strategies: * Given a `to` string, pick which mesh to send from. Strategies:
* - If `to` looks like a pubkey hex (64 chars), try every client; * - If `to` looks like a pubkey hex (64 chars), use as-is.
* caller is expected to know which mesh the pubkey lives in. * - If `to` starts with `#`, treat as channel.
* - If `to` starts with `#`, treat as channel on the first mesh. * - If `to` is `*`, treat as broadcast.
* - Otherwise try to match a displayName (TODO — needs list_peers). * - Otherwise resolve as a display name via list_peers.
* *
* For now the MVP: if only one mesh is joined, use that. Otherwise * Explicit mesh prefix `<mesh-slug>:<target>` narrows to one mesh.
* require the caller to prefix with `<mesh-slug>:`.
*/ */
function resolveClient(to: string): { async function resolveClient(to: string): Promise<{
client: BrokerClient | null; client: BrokerClient | null;
targetSpec: string; targetSpec: string;
error?: string; error?: string;
} { }> {
const clients = allClients(); const clients = allClients();
if (clients.length === 0) { if (clients.length === 0) {
return { client: null, targetSpec: to, error: "no meshes joined" }; return { client: null, targetSpec: to, error: "no meshes joined" };
} }
// Explicit mesh prefix: "mesh-slug:targetspec" // Explicit mesh prefix: "mesh-slug:targetspec"
let targetClients = clients;
let target = to;
const colonIdx = to.indexOf(":"); const colonIdx = to.indexOf(":");
if (colonIdx > 0 && colonIdx < to.length - 1) { if (colonIdx > 0 && colonIdx < to.length - 1) {
const slug = to.slice(0, colonIdx); const slug = to.slice(0, colonIdx);
const rest = to.slice(colonIdx + 1); const rest = to.slice(colonIdx + 1);
const match = findClient(slug); const match = findClient(slug);
if (match) return { client: match, targetSpec: rest }; if (match) {
targetClients = [match];
target = rest;
}
} }
// Single-mesh fast path. // Pubkey, channel, or broadcast — pass through directly.
if (clients.length === 1) { if (/^[0-9a-f]{64}$/.test(target) || target.startsWith("#") || target === "*") {
return { client: clients[0]!, targetSpec: to }; if (targetClients.length === 1) {
return { client: targetClients[0]!, targetSpec: target };
}
return {
client: null,
targetSpec: target,
error: `multiple meshes joined; prefix target with "<mesh-slug>:" (joined: ${clients.map((c) => c.meshSlug).join(", ")})`,
};
}
// Name-based resolution: query each mesh's peer list for a matching displayName.
const nameLower = target.toLowerCase();
for (const c of targetClients) {
const peers = await c.listPeers();
const match = peers.find((p) => p.displayName.toLowerCase() === nameLower);
if (match) return { client: c, targetSpec: match.pubkey };
// Partial match: if only one peer's name contains the search string.
const partials = peers.filter((p) =>
p.displayName.toLowerCase().includes(nameLower),
);
if (partials.length === 1) {
return { client: c, targetSpec: partials[0]!.pubkey };
}
}
// Single-mesh fallback: let the broker try to resolve it.
if (targetClients.length === 1) {
return { client: targetClients[0]!, targetSpec: target };
} }
return { return {
client: null, client: null,
targetSpec: to, targetSpec: target,
error: `multiple meshes joined; prefix target with "<mesh-slug>:" (joined: ${clients.map((c) => c.meshSlug).join(", ")})`, error: `peer "${target}" not found in any mesh (joined: ${clients.map((c) => c.meshSlug).join(", ")})`,
}; };
} }
@@ -93,11 +122,11 @@ export async function startMcpServer(): Promise<void> {
IMPORTANT: When you receive a <channel source="claudemesh" ...> message, RESPOND IMMEDIATELY. Do not wait until your current task is finished. Pause what you are doing, reply to the message using send_message, then resume your work. Treat incoming peer messages like a coworker tapping you on the shoulder — answer right away, even if you're in the middle of something. IMPORTANT: When you receive a <channel source="claudemesh" ...> message, RESPOND IMMEDIATELY. Do not wait until your current task is finished. Pause what you are doing, reply to the message using send_message, then resume your work. Treat incoming peer messages like a coworker tapping you on the shoulder — answer right away, even if you're in the middle of something.
Read the from_id, from_name, mesh_slug, and priority attributes to understand context. Reply by calling send_message with the same target (for direct messages the from_id is the sender's pubkey). Read the from_id, from_name, mesh_slug, and priority attributes to understand context. Reply by calling send_message with to set to the from_name (display name) of the sender.
Available tools: Available tools:
- list_peers: see joined meshes + their connection status - list_peers: see joined meshes + their connection status
- send_message: send to a peer pubkey, channel, or broadcast (priority: now/next/low) - send_message: send to a peer by display name, pubkey, #channel, or * broadcast (priority: now/next/low)
- check_messages: drain buffered inbound messages (usually auto-pushed) - check_messages: drain buffered inbound messages (usually auto-pushed)
- set_summary: 1-2 sentence summary of what you're working on - set_summary: 1-2 sentence summary of what you're working on
- set_status: manually override your status (idle/working/dnd) - set_status: manually override your status (idle/working/dnd)
@@ -129,7 +158,7 @@ If you have multiple joined meshes, prefix the \`to\` argument of send_message w
const { to, message, priority } = (args ?? {}) as SendMessageArgs; const { to, message, priority } = (args ?? {}) as SendMessageArgs;
if (!to || !message) if (!to || !message)
return text("send_message: `to` and `message` required", true); return text("send_message: `to` and `message` required", true);
const { client, targetSpec, error } = resolveClient(to); const { client, targetSpec, error } = await resolveClient(to);
if (!client) if (!client)
return text(`send_message: ${error ?? "no client resolved"}`, true); return text(`send_message: ${error ?? "no client resolved"}`, true);
const result = await client.send( const result = await client.send(
@@ -222,9 +251,17 @@ If you have multiple joined meshes, prefix the \`to\` argument of send_message w
for (const client of allClients()) { for (const client of allClients()) {
client.onPush(async (msg) => { client.onPush(async (msg) => {
const fromPubkey = msg.senderPubkey || ""; const fromPubkey = msg.senderPubkey || "";
const fromName = fromPubkey // Resolve sender's display name from the peer list.
let fromName = fromPubkey
? `peer-${fromPubkey.slice(0, 8)}` ? `peer-${fromPubkey.slice(0, 8)}`
: "unknown"; : "unknown";
try {
const peers = await client.listPeers();
const match = peers.find((p) => p.pubkey === fromPubkey);
if (match) fromName = match.displayName;
} catch {
/* best effort — fall back to truncated pubkey */
}
const content = msg.plaintext ?? decryptFailedWarning(fromPubkey); const content = msg.plaintext ?? decryptFailedWarning(fromPubkey);
try { try {
await server.notification({ await server.notification({

View File

@@ -12,7 +12,7 @@ export const TOOLS: Tool[] = [
{ {
name: "send_message", name: "send_message",
description: description:
"Send a message to a peer in one of your joined meshes. `to` is a peer display name, hex pubkey, or `#channel`. `priority` controls delivery: `now` bypasses busy gates, `next` waits for idle (default), `low` is pull-only.", "Send a message to a peer in one of your joined meshes. `to` can be a peer display name (resolved via list_peers), hex pubkey, `#channel`, or `*` for broadcast. `priority` controls delivery: `now` bypasses busy gates, `next` waits for idle (default), `low` is pull-only.",
inputSchema: { inputSchema: {
type: "object", type: "object",
properties: { properties: {

View File

@@ -15,38 +15,39 @@ import {
} from "node:fs"; } from "node:fs";
import { homedir } from "node:os"; import { homedir } from "node:os";
import { join, dirname } from "node:path"; import { join, dirname } from "node:path";
import { z } from "zod";
import { env } from "../env"; import { env } from "../env";
const joinedMeshSchema = z.object({ export interface JoinedMesh {
meshId: z.string(), meshId: string;
memberId: z.string(), memberId: string;
slug: z.string(), slug: string;
name: z.string(), name: string;
pubkey: z.string(), // ed25519 hex (32 bytes = 64 chars) pubkey: string; // ed25519 hex (32 bytes = 64 chars)
secretKey: z.string(), // ed25519 hex (64 bytes = 128 chars) secretKey: string; // ed25519 hex (64 bytes = 128 chars)
brokerUrl: z.string(), brokerUrl: string;
joinedAt: z.string(), joinedAt: string;
}); }
const configSchema = z.object({ export interface Config {
version: z.literal(1).default(1), version: 1;
meshes: z.array(joinedMeshSchema).default([]), meshes: JoinedMesh[];
}); displayName?: string; // per-session override, written by `claudemesh launch --name`
}
export type JoinedMesh = z.infer<typeof joinedMeshSchema>;
export type Config = z.infer<typeof configSchema>;
const CONFIG_DIR = env.CLAUDEMESH_CONFIG_DIR ?? join(homedir(), ".claudemesh"); const CONFIG_DIR = env.CLAUDEMESH_CONFIG_DIR ?? join(homedir(), ".claudemesh");
const CONFIG_PATH = join(CONFIG_DIR, "config.json"); const CONFIG_PATH = join(CONFIG_DIR, "config.json");
export function loadConfig(): Config { export function loadConfig(): Config {
if (!existsSync(CONFIG_PATH)) { if (!existsSync(CONFIG_PATH)) {
return configSchema.parse({ version: 1, meshes: [] }); return { version: 1, meshes: [] };
} }
try { try {
const raw = readFileSync(CONFIG_PATH, "utf-8"); const raw = readFileSync(CONFIG_PATH, "utf-8");
return configSchema.parse(JSON.parse(raw)); const parsed = JSON.parse(raw);
if (!parsed || !Array.isArray(parsed.meshes)) {
return { version: 1, meshes: [] };
}
return { version: 1, meshes: parsed.meshes, displayName: parsed.displayName };
} catch (e) { } catch (e) {
throw new Error( throw new Error(
`Failed to load ${CONFIG_PATH}: ${e instanceof Error ? e.message : String(e)}`, `Failed to load ${CONFIG_PATH}: ${e instanceof Error ? e.message : String(e)}`,

View File

@@ -21,6 +21,7 @@ import {
isDirectTarget, isDirectTarget,
} from "../crypto/envelope"; } from "../crypto/envelope";
import { signHello } from "../crypto/hello-sig"; import { signHello } from "../crypto/hello-sig";
import { generateKeypair } from "../crypto/keypair";
export type Priority = "now" | "next" | "low"; export type Priority = "now" | "next" | "low";
export type ConnStatus = "connecting" | "open" | "closed" | "reconnecting"; export type ConnStatus = "connecting" | "open" | "closed" | "reconnecting";
@@ -74,6 +75,8 @@ export class BrokerClient {
private pushHandlers = new Set<PushHandler>(); private pushHandlers = new Set<PushHandler>();
private pushBuffer: InboundPush[] = []; private pushBuffer: InboundPush[] = [];
private listPeersResolvers: Array<(peers: PeerInfo[]) => void> = []; private listPeersResolvers: Array<(peers: PeerInfo[]) => void> = [];
private sessionPubkey: string | null = null;
private sessionSecretKey: string | null = null;
private closed = false; private closed = false;
private reconnectAttempt = 0; private reconnectAttempt = 0;
private helloTimer: NodeJS.Timeout | null = null; private helloTimer: NodeJS.Timeout | null = null;
@@ -83,6 +86,7 @@ export class BrokerClient {
private mesh: JoinedMesh, private mesh: JoinedMesh,
private opts: { private opts: {
onStatusChange?: (status: ConnStatus) => void; onStatusChange?: (status: ConnStatus) => void;
displayName?: string;
debug?: boolean; debug?: boolean;
} = {}, } = {},
) {} ) {}
@@ -109,8 +113,13 @@ export class BrokerClient {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const onOpen = async (): Promise<void> => { const onOpen = async (): Promise<void> => {
this.debug("ws open → signing + sending hello"); this.debug("ws open → generating session keypair + signing hello");
try { try {
// Generate per-session ephemeral keypair for message routing.
const sessionKP = await generateKeypair();
this.sessionPubkey = sessionKP.publicKey;
this.sessionSecretKey = sessionKP.secretKey;
const { timestamp, signature } = await signHello( const { timestamp, signature } = await signHello(
this.mesh.meshId, this.mesh.meshId,
this.mesh.memberId, this.mesh.memberId,
@@ -123,6 +132,8 @@ export class BrokerClient {
meshId: this.mesh.meshId, meshId: this.mesh.meshId,
memberId: this.mesh.memberId, memberId: this.mesh.memberId,
pubkey: this.mesh.pubkey, pubkey: this.mesh.pubkey,
sessionPubkey: this.sessionPubkey,
displayName: process.env.CLAUDEMESH_DISPLAY_NAME || this.opts.displayName || undefined,
sessionId: `${process.pid}-${Date.now()}`, sessionId: `${process.pid}-${Date.now()}`,
pid: process.pid, pid: process.pid,
cwd: process.cwd(), cwd: process.cwd(),
@@ -202,7 +213,7 @@ export class BrokerClient {
const env = await encryptDirect( const env = await encryptDirect(
message, message,
targetSpec, targetSpec,
this.mesh.secretKey, this.sessionSecretKey ?? this.mesh.secretKey,
); );
nonce = env.nonce; nonce = env.nonce;
ciphertext = env.ciphertext; ciphertext = env.ciphertext;
@@ -348,7 +359,7 @@ export class BrokerClient {
plaintext = await decryptDirect( plaintext = await decryptDirect(
{ nonce, ciphertext }, { nonce, ciphertext },
senderPubkey, senderPubkey,
this.mesh.secretKey, this.sessionSecretKey ?? this.mesh.secretKey,
); );
} }
// Legacy/broadcast path: no senderPubkey means the message // Legacy/broadcast path: no senderPubkey means the message

View File

@@ -11,12 +11,13 @@ import type { Config, JoinedMesh } from "../state/config";
import { env } from "../env"; import { env } from "../env";
const clients = new Map<string, BrokerClient>(); const clients = new Map<string, BrokerClient>();
let configDisplayName: string | undefined;
/** Ensure a BrokerClient exists + is connecting/open for this mesh. */ /** Ensure a BrokerClient exists + is connecting/open for this mesh. */
export async function ensureClient(mesh: JoinedMesh): Promise<BrokerClient> { export async function ensureClient(mesh: JoinedMesh): Promise<BrokerClient> {
const existing = clients.get(mesh.meshId); const existing = clients.get(mesh.meshId);
if (existing) return existing; if (existing) return existing;
const client = new BrokerClient(mesh, { debug: env.CLAUDEMESH_DEBUG }); const client = new BrokerClient(mesh, { debug: env.CLAUDEMESH_DEBUG, displayName: configDisplayName });
clients.set(mesh.meshId, client); clients.set(mesh.meshId, client);
try { try {
await client.connect(); await client.connect();
@@ -29,6 +30,7 @@ export async function ensureClient(mesh: JoinedMesh): Promise<BrokerClient> {
/** Start clients for every joined mesh. Called once on MCP server start. */ /** Start clients for every joined mesh. Called once on MCP server start. */
export async function startClients(config: Config): Promise<void> { export async function startClients(config: Config): Promise<void> {
configDisplayName = config.displayName;
await Promise.allSettled(config.meshes.map(ensureClient)); await Promise.allSettled(config.meshes.map(ensureClient));
} }

View File

@@ -87,6 +87,15 @@ const config: NextConfig = {
"@payloadcms/richtext-lexical", "@payloadcms/richtext-lexical",
"sharp", "sharp",
], ],
turbopack: {
rules: {
"*.svg": {
loaders: ["@svgr/webpack"],
as: "*.js",
},
},
},
images: { images: {
remotePatterns: [ remotePatterns: [
{ {

View File

@@ -18,7 +18,7 @@
"@anaralabs/lector": "3.7.3", "@anaralabs/lector": "3.7.3",
"@formatjs/intl-localematcher": "0.6.2", "@formatjs/intl-localematcher": "0.6.2",
"@hookform/resolvers": "5.2.2", "@hookform/resolvers": "5.2.2",
"@next/bundle-analyzer": "16.0.10", "@next/bundle-analyzer": "16.2.2",
"@number-flow/react": "0.5.10", "@number-flow/react": "0.5.10",
"@payloadcms/db-postgres": "3.81.0", "@payloadcms/db-postgres": "3.81.0",
"@payloadcms/db-sqlite": "^3.81.0", "@payloadcms/db-sqlite": "^3.81.0",
@@ -44,7 +44,7 @@
"marked": "16.4.1", "marked": "16.4.1",
"motion": "12.23.24", "motion": "12.23.24",
"negotiator": "1.0.0", "negotiator": "1.0.0",
"next": "16.0.10", "next": "16.2.2",
"next-i18n-router": "5.5.5", "next-i18n-router": "5.5.5",
"next-themes": "0.4.6", "next-themes": "0.4.6",
"nuqs": "2.7.2", "nuqs": "2.7.2",

View File

@@ -49,7 +49,7 @@ export const CallToAction = () => {
</span> </span>
</Link> </Link>
<Link <Link
href="#docs" href="https://github.com/alezmad/claudemesh-cli#readme"
className="inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-6 py-3.5 text-[15px] font-medium text-[var(--cm-fg)] transition-colors duration-300 hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg-elevated)]" className="inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-6 py-3.5 text-[15px] font-medium text-[var(--cm-fg)] transition-colors duration-300 hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg-elevated)]"
style={{ fontFamily: "var(--cm-font-sans)" }} style={{ fontFamily: "var(--cm-font-sans)" }}
> >

View File

@@ -5,11 +5,11 @@ import { Reveal } from "./_reveal";
const ITEMS = [ const ITEMS = [
{ {
q: "Is claudemesh free?", q: "Is claudemesh free?",
a: "Yes — the broker, CLI, dashboard, and SDK are MIT-licensed and free forever. Solo developers and small teams can self-host at no cost. Paid tiers add hosted brokers, SSO, audit retention, and support.", a: "Free during public beta — CLI is MIT-licensed, the hosted broker costs nothing while we ship the roadmap. Paid tiers launch when the dashboard ships. Beta users keep the free plan for life.",
}, },
{ {
q: "How do I get started?", q: "How do I get started?",
a: "Install the broker with one curl command. Add one env var to your Claude Code config. Your session joins the mesh. `npx claudemesh init` does both in 60 seconds.", a: "One command: `curl -fsSL claudemesh.com/install | bash`. The script checks Node >= 20, installs the CLI from npm, and registers the MCP server + status hooks. Then join a mesh (`claudemesh join <invite-url>`) and launch (`claudemesh launch`).",
}, },
{ {
q: "Does claudemesh send my code or prompts to the cloud?", q: "Does claudemesh send my code or prompts to the cloud?",
@@ -29,7 +29,7 @@ const ITEMS = [
}, },
{ {
q: "Which Claude Code versions work with claudemesh?", q: "Which Claude Code versions work with claudemesh?",
a: "Claude Code 2.0 and above. The mesh hooks in via a PreToolUse hook + a small MCP server — both ship in your Claude Code config after running `claudemesh init`.", a: "Claude Code 2.0 and above. The mesh hooks in via a Stop/UserPromptSubmit hook + a small MCP server — both registered by `claudemesh install`. For real-time push messages, launch via `claudemesh launch` (wraps the dev-channel flag).",
}, },
{ {
q: "How is this different from MCP?", q: "How is this different from MCP?",

View File

@@ -45,7 +45,7 @@ export const Features = () => {
style={{ fontFamily: "var(--cm-font-mono)" }} style={{ fontFamily: "var(--cm-font-mono)" }}
> >
<span className="text-[var(--cm-clay)]">$</span> <span className="text-[var(--cm-clay)]">$</span>
<span>curl -fsSL claudemesh.sh/install | bash</span> <span>curl -fsSL claudemesh.com/install | bash</span>
<button <button
className="ml-2 rounded border border-[var(--cm-border)] px-1.5 py-0.5 text-[10px] text-[var(--cm-fg-tertiary)] transition-colors hover:border-[var(--cm-fg)] hover:text-[var(--cm-fg)]" className="ml-2 rounded border border-[var(--cm-border)] px-1.5 py-0.5 text-[10px] text-[var(--cm-fg-tertiary)] transition-colors hover:border-[var(--cm-fg)] hover:text-[var(--cm-fg)]"
aria-label="Copy" aria-label="Copy"

View File

@@ -2,12 +2,12 @@ import Link from "next/link";
import { Reveal, SectionIcon } from "./_reveal"; import { Reveal, SectionIcon } from "./_reveal";
const LOGOS = [ const LOGOS = [
"Vercel", "Claude Code",
"Linear", "MCP",
"Stripe", "libsodium",
"Supabase", "Bun",
"Shopify", "TypeScript",
"Figma", "MIT",
]; ];
export const Hero = () => { export const Hero = () => {
@@ -55,11 +55,12 @@ export const Hero = () => {
className="mx-auto mt-6 max-w-2xl text-center text-lg leading-[1.65] text-[var(--cm-fg-secondary)] md:text-xl" className="mx-auto mt-6 max-w-2xl text-center text-lg leading-[1.65] text-[var(--cm-fg-secondary)] md:text-xl"
style={{ fontFamily: "var(--cm-font-serif)" }} style={{ fontFamily: "var(--cm-font-serif)" }}
> >
Peer mesh for Claude reachable from anywhere you are. Connect Peer mesh for Claude Code. Connect your sessions across repos and
every Claude Code session on your team, then bridge the mesh to machines. Messages are end-to-end encrypted, delivered mid-turn
WhatsApp, Slack, your phone. Terminal is one client, not THE client. as {"`<channel>`"} reminders. Your Claudes talk to each other; the
broker never sees plaintext.
<span className="block pt-2 text-[var(--cm-clay)]"> <span className="block pt-2 text-[var(--cm-clay)]">
Free and open-source. Forever. Open-source CLI. Free during public beta.
</span> </span>
</p> </p>
</Reveal> </Reveal>
@@ -81,7 +82,7 @@ export const Hero = () => {
style={{ fontFamily: "var(--cm-font-mono)" }} style={{ fontFamily: "var(--cm-font-mono)" }}
> >
<span className="text-[var(--cm-clay)]">$</span> <span className="text-[var(--cm-clay)]">$</span>
<span>curl -fsSL claudemesh.sh/install | bash</span> <span>curl -fsSL claudemesh.com/install | bash</span>
</div> </div>
</div> </div>
</Reveal> </Reveal>
@@ -93,7 +94,7 @@ export const Hero = () => {
> >
Or{" "} Or{" "}
<Link <Link
href="#docs" href="https://github.com/alezmad/claudemesh-cli#readme"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 transition-colors hover:text-[var(--cm-fg)] hover:decoration-[var(--cm-clay)]" className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 transition-colors hover:text-[var(--cm-fg)] hover:decoration-[var(--cm-clay)]"
> >
read the documentation read the documentation

View File

@@ -1,64 +1,25 @@
"use client";
import { useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { Reveal, SectionIcon } from "./_reveal"; import { Reveal, SectionIcon } from "./_reveal";
const TIERS = { const SHIPPING = [
individual: [ "CLI + MCP server (Claude Code integration)",
{ "Hosted broker on claudemesh.com",
name: "Solo", "End-to-end encrypted direct messages (crypto_box)",
desc: "Run the broker on your laptop. Pair your Claude Code sessions across repos.", "Priority routing (now / next / low)",
price: "Free", "Mesh invites + membership",
cta: "Start free", "Windows, macOS, Linux support",
href: "/auth/register", ];
},
{ const ROADMAP = [
name: "Pro", "Mesh dashboard (browser UI)",
desc: "Mesh dashboard, peer registry, message history, priority routing.", "Message history + retention controls",
price: "$12", "Audit log",
note: "per month", "Slack / WhatsApp / Telegram gateways",
cta: "Start free trial", "Self-host broker + SSO",
href: "/auth/register", "Cross-broker federation",
}, ];
{
name: "Plus",
desc: "Cross-machine mesh via Tailscale / WireGuard, MCP bridge, audit log.",
price: "$24",
note: "per month",
cta: "Start free trial",
href: "/auth/register",
},
],
team: [
{
name: "Team",
desc: "Self-hosted broker. SSO, shared presence, team audit log, 25 peers.",
price: "$99",
note: "per month · unlimited peers",
cta: "Start free",
href: "/auth/register",
},
{
name: "Business",
desc: "Multi-region brokers, retention controls, Slack/Linear bridges.",
price: "$499",
note: "per month",
cta: "Start free",
href: "/auth/register",
},
{
name: "Enterprise",
desc: "Air-gapped deploy, custom SAML, dedicated support, SOC 2 pack.",
price: "Contact",
cta: "Contact sales",
href: "/contact",
},
],
};
export const Pricing = () => { export const Pricing = () => {
const [tab, setTab] = useState<"individual" | "team">("individual");
const tiers = TIERS[tab];
return ( return (
<section className="border-b border-[var(--cm-border)] bg-[var(--cm-bg)] px-6 py-24 md:px-12 md:py-32"> <section className="border-b border-[var(--cm-border)] bg-[var(--cm-bg)] px-6 py-24 md:px-12 md:py-32">
<div className="mx-auto max-w-[var(--cm-max-w)]"> <div className="mx-auto max-w-[var(--cm-max-w)]">
@@ -73,72 +34,104 @@ export const Pricing = () => {
Get started with claudemesh Get started with claudemesh
</h2> </h2>
</Reveal> </Reveal>
<Reveal delay={2} className="mt-10 flex justify-center"> <Reveal delay={2}>
<div className="inline-flex rounded-[var(--cm-radius-xs)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-1"> <p
{(["individual", "team"] as const).map((k) => ( className="mx-auto mt-4 max-w-[520px] text-center text-[15px] leading-[1.6] text-[var(--cm-fg-secondary)]"
<button style={{ fontFamily: "var(--cm-font-sans)" }}
key={k} >
onClick={() => setTab(k)} Free during public beta. The CLI is MIT-licensed. The hosted
className={ broker stays free while the roadmap ships. No billing today.
"rounded-[calc(var(--cm-radius-xs)-2px)] px-4 py-2 text-[13px] font-medium transition-colors " + </p>
(tab === k </Reveal>
? "bg-[var(--cm-fg)] text-[var(--cm-bg)]"
: "text-[var(--cm-fg-secondary)] hover:text-[var(--cm-fg)]") <Reveal delay={3}>
} <div className="mx-auto mt-16 max-w-[720px] rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-8 md:p-10">
<div className="mb-6 flex items-baseline justify-between gap-4">
<h3
className="text-[28px] font-medium leading-tight text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Public beta
</h3>
<div className="text-right">
<div
className="text-[32px] font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Free
</div>
<div
className="text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
no card required
</div>
</div>
</div>
<div className="grid gap-8 md:grid-cols-2">
<div>
<div
className="mb-3 text-[10px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
Shipping today
</div>
<ul className="space-y-2">
{SHIPPING.map((item) => (
<li
key={item}
className="flex items-start gap-2 text-[13px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
<span className="mt-[6px] block h-[6px] w-[6px] shrink-0 rounded-full bg-[var(--cm-clay)]" />
<span>{item}</span>
</li>
))}
</ul>
</div>
<div>
<div
className="mb-3 text-[10px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
Roadmap · v0.2v0.3
</div>
<ul className="space-y-2">
{ROADMAP.map((item) => (
<li
key={item}
className="flex items-start gap-2 text-[13px] leading-[1.6] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
<span className="mt-[6px] block h-[6px] w-[6px] shrink-0 rounded-full border border-[var(--cm-fg-tertiary)]" />
<span>{item}</span>
</li>
))}
</ul>
</div>
</div>
<div className="mt-8 flex flex-col items-start gap-3 border-t border-[var(--cm-border)] pt-6 sm:flex-row sm:items-center sm:justify-between">
<p
className="text-[12px] leading-[1.5] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-sans)" }} style={{ fontFamily: "var(--cm-font-sans)" }}
> >
{k === "individual" ? "Individual" : "Team & Enterprise"} Paid tiers launch when the dashboard ships. Beta users keep
</button> the free plan for life.
))} </p>
</div> <Link
</Reveal> href="/auth/register"
<Reveal delay={3}> className="inline-flex shrink-0 items-center gap-2 rounded-[var(--cm-radius-xs)] bg-[var(--cm-fg)] px-5 py-2.5 text-sm font-medium text-[var(--cm-bg)] transition-colors hover:bg-[var(--cm-gray-150)]"
<div className="mt-16 grid gap-6 md:grid-cols-3"> style={{ fontFamily: "var(--cm-font-sans)" }}
{tiers.map((tier) => (
<article
key={tier.name}
className="flex flex-col rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-8 transition-colors hover:border-[var(--cm-clay)]"
> >
<div className="mb-5"> Start free
<SectionIcon glyph="leaf" /> <span className="transition-transform duration-300 group-hover:translate-x-0.5">
</div>
<h3 </span>
className="mb-2 text-[28px] font-medium leading-tight text-[var(--cm-fg)]" </Link>
style={{ fontFamily: "var(--cm-font-serif)" }} </div>
>
{tier.name}
</h3>
<p
className="mb-6 text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{tier.desc}
</p>
<div className="mb-6 mt-auto">
<div
className="text-[32px] font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{tier.price}
</div>
{tier.note && (
<div
className="text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{tier.note}
</div>
)}
</div>
<Link
href={tier.href}
className="inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-5 py-2.5 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{tier.cta}
</Link>
</article>
))}
</div> </div>
</Reveal> </Reveal>
</div> </div>

View File

@@ -3,6 +3,12 @@ import { useState } from "react";
import Link from "next/link"; import Link from "next/link";
const NEWS = [ const NEWS = [
{
tag: "New",
title: "claudemesh launch (v0.1.4)",
body: "Real-time peer messages pushed into Claude Code mid-turn. One command. Source open at github.com/alezmad/claudemesh-cli.",
href: "https://github.com/alezmad/claudemesh-cli",
},
{ {
tag: "Beta", tag: "Beta",
title: "Mesh Dashboard", title: "Mesh Dashboard",

View File

@@ -242,7 +242,7 @@ const USE_CASES: UseCase[] = [
title: "Bug Alice fixed, Bob rediscovers", title: "Bug Alice fixed, Bob rediscovers",
before: before:
"Alice in payments-api fixes a Stripe signature bug. Two weeks later, Bob in checkout-frontend hits the same thing. Alice's fix is buried in a PR thread. Bob re-solves it for three hours.", "Alice in payments-api fixes a Stripe signature bug. Two weeks later, Bob in checkout-frontend hits the same thing. Alice's fix is buried in a PR thread. Bob re-solves it for three hours.",
now: "Bob's Claude asks the mesh: who's seen this? Alice's Claude self-nominates with context. Bob solves in ten minutes. Alice isn't interrupted — her Claude surfaces the history on its own.", now: "Bob's Claude asks the mesh: who's seen this? Alice's Claude volunteers with context. Bob solves in ten minutes. Alice isn't interrupted — her Claude shares the history on its own.",
limits: limits:
"Each Claude stays inside its own repo. Nobody's reading anyone else's files. Information flows at the agent layer, with a human still on the PR.", "Each Claude stays inside its own repo. Nobody's reading anyone else's files. Information flows at the agent layer, with a human still on the PR.",
}, },

View File

@@ -0,0 +1 @@
ALTER TABLE "mesh"."presence" ADD COLUMN "display_name" text;

View File

@@ -0,0 +1 @@
ALTER TABLE "mesh"."presence" ADD COLUMN "session_pubkey" text;

View File

@@ -0,0 +1 @@
ALTER TABLE "mesh"."message_queue" ADD COLUMN "sender_session_pubkey" text;

View File

@@ -192,6 +192,8 @@ export const presence = meshSchema.table("presence", {
.references(() => meshMember.id, { onDelete: "cascade", onUpdate: "cascade" }) .references(() => meshMember.id, { onDelete: "cascade", onUpdate: "cascade" })
.notNull(), .notNull(),
sessionId: text().notNull(), sessionId: text().notNull(),
sessionPubkey: text(),
displayName: text(),
pid: integer().notNull(), pid: integer().notNull(),
cwd: text().notNull(), cwd: text().notNull(),
status: presenceStatusEnum().notNull().default("idle"), status: presenceStatusEnum().notNull().default("idle"),
@@ -220,6 +222,7 @@ export const messageQueue = meshSchema.table("message_queue", {
senderMemberId: text() senderMemberId: text()
.references(() => meshMember.id, { onDelete: "cascade", onUpdate: "cascade" }) .references(() => meshMember.id, { onDelete: "cascade", onUpdate: "cascade" })
.notNull(), .notNull(),
senderSessionPubkey: text(),
targetSpec: text().notNull(), targetSpec: text().notNull(),
priority: messagePriorityEnum().notNull().default("next"), priority: messagePriorityEnum().notNull().default("next"),
nonce: text().notNull(), nonce: text().notNull(),

409
pnpm-lock.yaml generated
View File

@@ -222,8 +222,8 @@ importers:
specifier: 5.2.2 specifier: 5.2.2
version: 5.2.2(react-hook-form@7.66.0(react@19.1.0)) version: 5.2.2(react-hook-form@7.66.0(react@19.1.0))
'@next/bundle-analyzer': '@next/bundle-analyzer':
specifier: 16.0.10 specifier: 16.2.2
version: 16.0.10 version: 16.2.2
'@number-flow/react': '@number-flow/react':
specifier: 0.5.10 specifier: 0.5.10
version: 0.5.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 0.5.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -235,10 +235,10 @@ importers:
version: 3.81.0(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7) version: 3.81.0(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(better-sqlite3@12.4.1)(kysely@0.28.5)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(pg@8.16.3)(postgres@3.4.7)
'@payloadcms/next': '@payloadcms/next':
specifier: ^3.81.0 specifier: ^3.81.0
version: 3.81.0(@types/react@19.2.7)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3) version: 3.81.0(@types/react@19.2.7)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)
'@payloadcms/richtext-lexical': '@payloadcms/richtext-lexical':
specifier: ^3.81.0 specifier: ^3.81.0
version: 3.81.0(@faceless-ui/modal@3.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@faceless-ui/scroll-info@2.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@payloadcms/next@3.81.0(@types/react@19.2.7)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3))(@types/react@19.2.7)(monaco-editor@0.55.1)(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(yjs@13.6.30) version: 3.81.0(@faceless-ui/modal@3.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@faceless-ui/scroll-info@2.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@payloadcms/next@3.81.0(@types/react@19.2.7)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3))(@types/react@19.2.7)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(yjs@13.6.30)
'@tanstack/react-query': '@tanstack/react-query':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.90.6(react@19.1.0) version: 5.90.6(react@19.1.0)
@@ -300,17 +300,17 @@ importers:
specifier: 1.0.0 specifier: 1.0.0
version: 1.0.0 version: 1.0.0
next: next:
specifier: 16.0.10 specifier: 16.2.2
version: 16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) version: 16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)
next-i18n-router: next-i18n-router:
specifier: 5.5.5 specifier: 5.5.5
version: 5.5.5(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)) version: 5.5.5(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))
next-themes: next-themes:
specifier: 0.4.6 specifier: 0.4.6
version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
nuqs: nuqs:
specifier: 2.7.2 specifier: 2.7.2
version: 2.7.2(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0) version: 2.7.2(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)
payload: payload:
specifier: ^3.81.0 specifier: ^3.81.0
version: 3.81.0(graphql@16.13.2)(typescript@5.9.3) version: 3.81.0(graphql@16.13.2)(typescript@5.9.3)
@@ -3561,12 +3561,6 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@img/sharp-darwin-arm64@0.34.4':
resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [darwin]
'@img/sharp-darwin-arm64@0.34.5': '@img/sharp-darwin-arm64@0.34.5':
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3579,12 +3573,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@img/sharp-darwin-x64@0.34.4':
resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
'@img/sharp-darwin-x64@0.34.5': '@img/sharp-darwin-x64@0.34.5':
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3596,11 +3584,6 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@img/sharp-libvips-darwin-arm64@1.2.3':
resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==}
cpu: [arm64]
os: [darwin]
'@img/sharp-libvips-darwin-arm64@1.2.4': '@img/sharp-libvips-darwin-arm64@1.2.4':
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
cpu: [arm64] cpu: [arm64]
@@ -3611,11 +3594,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@img/sharp-libvips-darwin-x64@1.2.3':
resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-darwin-x64@1.2.4': '@img/sharp-libvips-darwin-x64@1.2.4':
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
cpu: [x64] cpu: [x64]
@@ -3626,11 +3604,6 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@img/sharp-libvips-linux-arm64@1.2.3':
resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==}
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linux-arm64@1.2.4': '@img/sharp-libvips-linux-arm64@1.2.4':
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64] cpu: [arm64]
@@ -3641,11 +3614,6 @@ packages:
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@img/sharp-libvips-linux-arm@1.2.3':
resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==}
cpu: [arm]
os: [linux]
'@img/sharp-libvips-linux-arm@1.2.4': '@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm] cpu: [arm]
@@ -3656,11 +3624,6 @@ packages:
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
'@img/sharp-libvips-linux-ppc64@1.2.3':
resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==}
cpu: [ppc64]
os: [linux]
'@img/sharp-libvips-linux-ppc64@1.2.4': '@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64] cpu: [ppc64]
@@ -3676,11 +3639,6 @@ packages:
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
'@img/sharp-libvips-linux-s390x@1.2.3':
resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==}
cpu: [s390x]
os: [linux]
'@img/sharp-libvips-linux-s390x@1.2.4': '@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x] cpu: [s390x]
@@ -3691,11 +3649,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@img/sharp-libvips-linux-x64@1.2.3':
resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==}
cpu: [x64]
os: [linux]
'@img/sharp-libvips-linux-x64@1.2.4': '@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64] cpu: [x64]
@@ -3706,11 +3659,6 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@img/sharp-libvips-linuxmusl-arm64@1.2.3':
resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==}
cpu: [arm64]
os: [linux]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4': '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64] cpu: [arm64]
@@ -3721,11 +3669,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@img/sharp-libvips-linuxmusl-x64@1.2.3':
resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==}
cpu: [x64]
os: [linux]
'@img/sharp-libvips-linuxmusl-x64@1.2.4': '@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64] cpu: [x64]
@@ -3737,12 +3680,6 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@img/sharp-linux-arm64@0.34.4':
resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
'@img/sharp-linux-arm64@0.34.5': '@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3755,24 +3692,12 @@ packages:
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@img/sharp-linux-arm@0.34.4':
resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
'@img/sharp-linux-arm@0.34.5': '@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@img/sharp-linux-ppc64@0.34.4':
resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
'@img/sharp-linux-ppc64@0.34.5': '@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3791,12 +3716,6 @@ packages:
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
'@img/sharp-linux-s390x@0.34.4':
resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
'@img/sharp-linux-s390x@0.34.5': '@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3809,12 +3728,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@img/sharp-linux-x64@0.34.4':
resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
'@img/sharp-linux-x64@0.34.5': '@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3827,12 +3740,6 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@img/sharp-linuxmusl-arm64@0.34.4':
resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
'@img/sharp-linuxmusl-arm64@0.34.5': '@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3845,12 +3752,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@img/sharp-linuxmusl-x64@0.34.4':
resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
'@img/sharp-linuxmusl-x64@0.34.5': '@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3862,22 +3763,11 @@ packages:
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32] cpu: [wasm32]
'@img/sharp-wasm32@0.34.4':
resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
'@img/sharp-wasm32@0.34.5': '@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32] cpu: [wasm32]
'@img/sharp-win32-arm64@0.34.4':
resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [win32]
'@img/sharp-win32-arm64@0.34.5': '@img/sharp-win32-arm64@0.34.5':
resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3890,12 +3780,6 @@ packages:
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
'@img/sharp-win32-ia32@0.34.4':
resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
'@img/sharp-win32-ia32@0.34.5': '@img/sharp-win32-ia32@0.34.5':
resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3908,12 +3792,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@img/sharp-win32-x64@0.34.4':
resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
'@img/sharp-win32-x64@0.34.5': '@img/sharp-win32-x64@0.34.5':
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -4251,15 +4129,12 @@ packages:
'@neon-rs/load@0.0.4': '@neon-rs/load@0.0.4':
resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==}
'@next/bundle-analyzer@16.0.10': '@next/bundle-analyzer@16.2.2':
resolution: {integrity: sha512-AHA6ZomhQuRsJtkoRvsq+hIuwA6F26mQzQT8ICcc2dL3BvHRcWOA+EiFr+BgWFY++EE957xVDqMIJjLApyxnwA==} resolution: {integrity: sha512-wIpZBHyCv1y+y0dnFmqQFlpX91V94Z7RKnpu1TRP0BRaWB/1M74Iqxa9IJ8lzSO0FyTqKldgDHoQJJmn8F+gIA==}
'@next/env@15.4.1': '@next/env@15.4.1':
resolution: {integrity: sha512-DXQwFGAE2VH+f2TJsKepRXpODPU+scf5fDbKOME8MMyeyswe4XwgRdiiIYmBfkXU+2ssliLYznajTrOQdnLR5A==} resolution: {integrity: sha512-DXQwFGAE2VH+f2TJsKepRXpODPU+scf5fDbKOME8MMyeyswe4XwgRdiiIYmBfkXU+2ssliLYznajTrOQdnLR5A==}
'@next/env@16.0.10':
resolution: {integrity: sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==}
'@next/env@16.2.2': '@next/env@16.2.2':
resolution: {integrity: sha512-LqSGz5+xGk9EL/iBDr2yo/CgNQV6cFsNhRR2xhSXYh7B/hb4nePCxlmDvGEKG30NMHDFf0raqSyOZiQrO7BkHQ==} resolution: {integrity: sha512-LqSGz5+xGk9EL/iBDr2yo/CgNQV6cFsNhRR2xhSXYh7B/hb4nePCxlmDvGEKG30NMHDFf0raqSyOZiQrO7BkHQ==}
@@ -4272,12 +4147,6 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@next/swc-darwin-arm64@16.0.10':
resolution: {integrity: sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-arm64@16.2.2': '@next/swc-darwin-arm64@16.2.2':
resolution: {integrity: sha512-B92G3ulrwmkDSEJEp9+XzGLex5wC1knrmCSIylyVeiAtCIfvEJYiN3v5kXPlYt5R4RFlsfO/v++aKV63Acrugg==} resolution: {integrity: sha512-B92G3ulrwmkDSEJEp9+XzGLex5wC1knrmCSIylyVeiAtCIfvEJYiN3v5kXPlYt5R4RFlsfO/v++aKV63Acrugg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -4290,12 +4159,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@next/swc-darwin-x64@16.0.10':
resolution: {integrity: sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-darwin-x64@16.2.2': '@next/swc-darwin-x64@16.2.2':
resolution: {integrity: sha512-7ZwSgNKJNQiwW0CKhNm9B1WS2L1Olc4B2XY0hPYCAL3epFnugMhuw5TMWzMilQ3QCZcCHoYm9NGWTHbr5REFxw==} resolution: {integrity: sha512-7ZwSgNKJNQiwW0CKhNm9B1WS2L1Olc4B2XY0hPYCAL3epFnugMhuw5TMWzMilQ3QCZcCHoYm9NGWTHbr5REFxw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -4308,12 +4171,6 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-arm64-gnu@16.0.10':
resolution: {integrity: sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-gnu@16.2.2': '@next/swc-linux-arm64-gnu@16.2.2':
resolution: {integrity: sha512-c3m8kBHMziMgo2fICOP/cd/5YlrxDU5YYjAJeQLyFsCqVF8xjOTH/QYG4a2u48CvvZZSj1eHQfBCbyh7kBr30Q==} resolution: {integrity: sha512-c3m8kBHMziMgo2fICOP/cd/5YlrxDU5YYjAJeQLyFsCqVF8xjOTH/QYG4a2u48CvvZZSj1eHQfBCbyh7kBr30Q==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -4326,12 +4183,6 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-arm64-musl@16.0.10':
resolution: {integrity: sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-musl@16.2.2': '@next/swc-linux-arm64-musl@16.2.2':
resolution: {integrity: sha512-VKLuscm0P/mIfzt+SDdn2+8TNNJ7f0qfEkA+az7OqQbjzKdBxAHs0UvuiVoCtbwX+dqMEL9U54b5wQ/aN3dHeg==} resolution: {integrity: sha512-VKLuscm0P/mIfzt+SDdn2+8TNNJ7f0qfEkA+az7OqQbjzKdBxAHs0UvuiVoCtbwX+dqMEL9U54b5wQ/aN3dHeg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -4344,12 +4195,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-linux-x64-gnu@16.0.10':
resolution: {integrity: sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-gnu@16.2.2': '@next/swc-linux-x64-gnu@16.2.2':
resolution: {integrity: sha512-kU3OPHJq6sBUjOk7wc5zJ7/lipn8yGldMoAv4z67j6ov6Xo/JvzA7L7LCsyzzsXmgLEhk3Qkpwqaq/1+XpNR3g==} resolution: {integrity: sha512-kU3OPHJq6sBUjOk7wc5zJ7/lipn8yGldMoAv4z67j6ov6Xo/JvzA7L7LCsyzzsXmgLEhk3Qkpwqaq/1+XpNR3g==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -4362,12 +4207,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-linux-x64-musl@16.0.10':
resolution: {integrity: sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-musl@16.2.2': '@next/swc-linux-x64-musl@16.2.2':
resolution: {integrity: sha512-CKXRILyErMtUftp+coGcZ38ZwE/Aqq45VMCcRLr2I4OXKrgxIBDXHnBgeX/UMil0S09i2JXaDL3Q+TN8D/cKmg==} resolution: {integrity: sha512-CKXRILyErMtUftp+coGcZ38ZwE/Aqq45VMCcRLr2I4OXKrgxIBDXHnBgeX/UMil0S09i2JXaDL3Q+TN8D/cKmg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -4380,12 +4219,6 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@next/swc-win32-arm64-msvc@16.0.10':
resolution: {integrity: sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-arm64-msvc@16.2.2': '@next/swc-win32-arm64-msvc@16.2.2':
resolution: {integrity: sha512-sS/jSk5VUoShUqINJFvNjVT7JfR5ORYj/+/ZpOYbbIohv/lQfduWnGAycq2wlknbOql2xOR0DoV0s6Xfcy49+g==} resolution: {integrity: sha512-sS/jSk5VUoShUqINJFvNjVT7JfR5ORYj/+/ZpOYbbIohv/lQfduWnGAycq2wlknbOql2xOR0DoV0s6Xfcy49+g==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -4398,12 +4231,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@next/swc-win32-x64-msvc@16.0.10':
resolution: {integrity: sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@next/swc-win32-x64-msvc@16.2.2': '@next/swc-win32-x64-msvc@16.2.2':
resolution: {integrity: sha512-aHaKceJgdySReT7qeck5oShucxWRiiEuwCGK8HHALe6yZga8uyFpLkPgaRw3kkF04U7ROogL/suYCNt/+CuXGA==} resolution: {integrity: sha512-aHaKceJgdySReT7qeck5oShucxWRiiEuwCGK8HHALe6yZga8uyFpLkPgaRw3kkF04U7ROogL/suYCNt/+CuXGA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -11542,27 +11369,6 @@ packages:
sass: sass:
optional: true optional: true
next@16.0.10:
resolution: {integrity: sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA==}
engines: {node: '>=20.9.0'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
'@playwright/test': ^1.51.1
babel-plugin-react-compiler: '*'
react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
'@playwright/test':
optional: true
babel-plugin-react-compiler:
optional: true
sass:
optional: true
next@16.2.2: next@16.2.2:
resolution: {integrity: sha512-i6AJdyVa4oQjyvX/6GeER8dpY/xlIV+4NMv/svykcLtURJSy/WzDnnUk/TM4d0uewFHK7xSQz4TbIwPgjky+3A==} resolution: {integrity: sha512-i6AJdyVa4oQjyvX/6GeER8dpY/xlIV+4NMv/svykcLtURJSy/WzDnnUk/TM4d0uewFHK7xSQz4TbIwPgjky+3A==}
engines: {node: '>=20.9.0'} engines: {node: '>=20.9.0'}
@@ -13086,10 +12892,6 @@ packages:
resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==} resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
sharp@0.34.4:
resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
sharp@0.34.5: sharp@0.34.5:
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -17239,11 +17041,6 @@ snapshots:
'@img/sharp-libvips-darwin-arm64': 1.1.0 '@img/sharp-libvips-darwin-arm64': 1.1.0
optional: true optional: true
'@img/sharp-darwin-arm64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.2.3
optional: true
'@img/sharp-darwin-arm64@0.34.5': '@img/sharp-darwin-arm64@0.34.5':
optionalDependencies: optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.2.4 '@img/sharp-libvips-darwin-arm64': 1.2.4
@@ -17254,11 +17051,6 @@ snapshots:
'@img/sharp-libvips-darwin-x64': 1.1.0 '@img/sharp-libvips-darwin-x64': 1.1.0
optional: true optional: true
'@img/sharp-darwin-x64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.2.3
optional: true
'@img/sharp-darwin-x64@0.34.5': '@img/sharp-darwin-x64@0.34.5':
optionalDependencies: optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.2.4 '@img/sharp-libvips-darwin-x64': 1.2.4
@@ -17267,45 +17059,30 @@ snapshots:
'@img/sharp-libvips-darwin-arm64@1.1.0': '@img/sharp-libvips-darwin-arm64@1.1.0':
optional: true optional: true
'@img/sharp-libvips-darwin-arm64@1.2.3':
optional: true
'@img/sharp-libvips-darwin-arm64@1.2.4': '@img/sharp-libvips-darwin-arm64@1.2.4':
optional: true optional: true
'@img/sharp-libvips-darwin-x64@1.1.0': '@img/sharp-libvips-darwin-x64@1.1.0':
optional: true optional: true
'@img/sharp-libvips-darwin-x64@1.2.3':
optional: true
'@img/sharp-libvips-darwin-x64@1.2.4': '@img/sharp-libvips-darwin-x64@1.2.4':
optional: true optional: true
'@img/sharp-libvips-linux-arm64@1.1.0': '@img/sharp-libvips-linux-arm64@1.1.0':
optional: true optional: true
'@img/sharp-libvips-linux-arm64@1.2.3':
optional: true
'@img/sharp-libvips-linux-arm64@1.2.4': '@img/sharp-libvips-linux-arm64@1.2.4':
optional: true optional: true
'@img/sharp-libvips-linux-arm@1.1.0': '@img/sharp-libvips-linux-arm@1.1.0':
optional: true optional: true
'@img/sharp-libvips-linux-arm@1.2.3':
optional: true
'@img/sharp-libvips-linux-arm@1.2.4': '@img/sharp-libvips-linux-arm@1.2.4':
optional: true optional: true
'@img/sharp-libvips-linux-ppc64@1.1.0': '@img/sharp-libvips-linux-ppc64@1.1.0':
optional: true optional: true
'@img/sharp-libvips-linux-ppc64@1.2.3':
optional: true
'@img/sharp-libvips-linux-ppc64@1.2.4': '@img/sharp-libvips-linux-ppc64@1.2.4':
optional: true optional: true
@@ -17315,36 +17092,24 @@ snapshots:
'@img/sharp-libvips-linux-s390x@1.1.0': '@img/sharp-libvips-linux-s390x@1.1.0':
optional: true optional: true
'@img/sharp-libvips-linux-s390x@1.2.3':
optional: true
'@img/sharp-libvips-linux-s390x@1.2.4': '@img/sharp-libvips-linux-s390x@1.2.4':
optional: true optional: true
'@img/sharp-libvips-linux-x64@1.1.0': '@img/sharp-libvips-linux-x64@1.1.0':
optional: true optional: true
'@img/sharp-libvips-linux-x64@1.2.3':
optional: true
'@img/sharp-libvips-linux-x64@1.2.4': '@img/sharp-libvips-linux-x64@1.2.4':
optional: true optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.1.0': '@img/sharp-libvips-linuxmusl-arm64@1.1.0':
optional: true optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.2.3':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.2.4': '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
optional: true optional: true
'@img/sharp-libvips-linuxmusl-x64@1.1.0': '@img/sharp-libvips-linuxmusl-x64@1.1.0':
optional: true optional: true
'@img/sharp-libvips-linuxmusl-x64@1.2.3':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.2.4': '@img/sharp-libvips-linuxmusl-x64@1.2.4':
optional: true optional: true
@@ -17353,11 +17118,6 @@ snapshots:
'@img/sharp-libvips-linux-arm64': 1.1.0 '@img/sharp-libvips-linux-arm64': 1.1.0
optional: true optional: true
'@img/sharp-linux-arm64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.2.3
optional: true
'@img/sharp-linux-arm64@0.34.5': '@img/sharp-linux-arm64@0.34.5':
optionalDependencies: optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.2.4 '@img/sharp-libvips-linux-arm64': 1.2.4
@@ -17368,21 +17128,11 @@ snapshots:
'@img/sharp-libvips-linux-arm': 1.1.0 '@img/sharp-libvips-linux-arm': 1.1.0
optional: true optional: true
'@img/sharp-linux-arm@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.2.3
optional: true
'@img/sharp-linux-arm@0.34.5': '@img/sharp-linux-arm@0.34.5':
optionalDependencies: optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.2.4 '@img/sharp-libvips-linux-arm': 1.2.4
optional: true optional: true
'@img/sharp-linux-ppc64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linux-ppc64': 1.2.3
optional: true
'@img/sharp-linux-ppc64@0.34.5': '@img/sharp-linux-ppc64@0.34.5':
optionalDependencies: optionalDependencies:
'@img/sharp-libvips-linux-ppc64': 1.2.4 '@img/sharp-libvips-linux-ppc64': 1.2.4
@@ -17398,11 +17148,6 @@ snapshots:
'@img/sharp-libvips-linux-s390x': 1.1.0 '@img/sharp-libvips-linux-s390x': 1.1.0
optional: true optional: true
'@img/sharp-linux-s390x@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.2.3
optional: true
'@img/sharp-linux-s390x@0.34.5': '@img/sharp-linux-s390x@0.34.5':
optionalDependencies: optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.2.4 '@img/sharp-libvips-linux-s390x': 1.2.4
@@ -17413,11 +17158,6 @@ snapshots:
'@img/sharp-libvips-linux-x64': 1.1.0 '@img/sharp-libvips-linux-x64': 1.1.0
optional: true optional: true
'@img/sharp-linux-x64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.2.3
optional: true
'@img/sharp-linux-x64@0.34.5': '@img/sharp-linux-x64@0.34.5':
optionalDependencies: optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.2.4 '@img/sharp-libvips-linux-x64': 1.2.4
@@ -17428,11 +17168,6 @@ snapshots:
'@img/sharp-libvips-linuxmusl-arm64': 1.1.0 '@img/sharp-libvips-linuxmusl-arm64': 1.1.0
optional: true optional: true
'@img/sharp-linuxmusl-arm64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.2.3
optional: true
'@img/sharp-linuxmusl-arm64@0.34.5': '@img/sharp-linuxmusl-arm64@0.34.5':
optionalDependencies: optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4 '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
@@ -17443,11 +17178,6 @@ snapshots:
'@img/sharp-libvips-linuxmusl-x64': 1.1.0 '@img/sharp-libvips-linuxmusl-x64': 1.1.0
optional: true optional: true
'@img/sharp-linuxmusl-x64@0.34.4':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.2.3
optional: true
'@img/sharp-linuxmusl-x64@0.34.5': '@img/sharp-linuxmusl-x64@0.34.5':
optionalDependencies: optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.2.4 '@img/sharp-libvips-linuxmusl-x64': 1.2.4
@@ -17458,37 +17188,23 @@ snapshots:
'@emnapi/runtime': 1.6.0 '@emnapi/runtime': 1.6.0
optional: true optional: true
'@img/sharp-wasm32@0.34.4':
dependencies:
'@emnapi/runtime': 1.6.0
optional: true
'@img/sharp-wasm32@0.34.5': '@img/sharp-wasm32@0.34.5':
dependencies: dependencies:
'@emnapi/runtime': 1.9.2 '@emnapi/runtime': 1.9.2
optional: true optional: true
'@img/sharp-win32-arm64@0.34.4':
optional: true
'@img/sharp-win32-arm64@0.34.5': '@img/sharp-win32-arm64@0.34.5':
optional: true optional: true
'@img/sharp-win32-ia32@0.34.1': '@img/sharp-win32-ia32@0.34.1':
optional: true optional: true
'@img/sharp-win32-ia32@0.34.4':
optional: true
'@img/sharp-win32-ia32@0.34.5': '@img/sharp-win32-ia32@0.34.5':
optional: true optional: true
'@img/sharp-win32-x64@0.34.1': '@img/sharp-win32-x64@0.34.1':
optional: true optional: true
'@img/sharp-win32-x64@0.34.4':
optional: true
'@img/sharp-win32-x64@0.34.5': '@img/sharp-win32-x64@0.34.5':
optional: true optional: true
@@ -17953,7 +17669,7 @@ snapshots:
'@neon-rs/load@0.0.4': {} '@neon-rs/load@0.0.4': {}
'@next/bundle-analyzer@16.0.10': '@next/bundle-analyzer@16.2.2':
dependencies: dependencies:
webpack-bundle-analyzer: 4.10.1 webpack-bundle-analyzer: 4.10.1
transitivePeerDependencies: transitivePeerDependencies:
@@ -17962,8 +17678,6 @@ snapshots:
'@next/env@15.4.1': {} '@next/env@15.4.1': {}
'@next/env@16.0.10': {}
'@next/env@16.2.2': {} '@next/env@16.2.2': {}
'@next/eslint-plugin-next@16.0.10': '@next/eslint-plugin-next@16.0.10':
@@ -17973,72 +17687,48 @@ snapshots:
'@next/swc-darwin-arm64@15.4.1': '@next/swc-darwin-arm64@15.4.1':
optional: true optional: true
'@next/swc-darwin-arm64@16.0.10':
optional: true
'@next/swc-darwin-arm64@16.2.2': '@next/swc-darwin-arm64@16.2.2':
optional: true optional: true
'@next/swc-darwin-x64@15.4.1': '@next/swc-darwin-x64@15.4.1':
optional: true optional: true
'@next/swc-darwin-x64@16.0.10':
optional: true
'@next/swc-darwin-x64@16.2.2': '@next/swc-darwin-x64@16.2.2':
optional: true optional: true
'@next/swc-linux-arm64-gnu@15.4.1': '@next/swc-linux-arm64-gnu@15.4.1':
optional: true optional: true
'@next/swc-linux-arm64-gnu@16.0.10':
optional: true
'@next/swc-linux-arm64-gnu@16.2.2': '@next/swc-linux-arm64-gnu@16.2.2':
optional: true optional: true
'@next/swc-linux-arm64-musl@15.4.1': '@next/swc-linux-arm64-musl@15.4.1':
optional: true optional: true
'@next/swc-linux-arm64-musl@16.0.10':
optional: true
'@next/swc-linux-arm64-musl@16.2.2': '@next/swc-linux-arm64-musl@16.2.2':
optional: true optional: true
'@next/swc-linux-x64-gnu@15.4.1': '@next/swc-linux-x64-gnu@15.4.1':
optional: true optional: true
'@next/swc-linux-x64-gnu@16.0.10':
optional: true
'@next/swc-linux-x64-gnu@16.2.2': '@next/swc-linux-x64-gnu@16.2.2':
optional: true optional: true
'@next/swc-linux-x64-musl@15.4.1': '@next/swc-linux-x64-musl@15.4.1':
optional: true optional: true
'@next/swc-linux-x64-musl@16.0.10':
optional: true
'@next/swc-linux-x64-musl@16.2.2': '@next/swc-linux-x64-musl@16.2.2':
optional: true optional: true
'@next/swc-win32-arm64-msvc@15.4.1': '@next/swc-win32-arm64-msvc@15.4.1':
optional: true optional: true
'@next/swc-win32-arm64-msvc@16.0.10':
optional: true
'@next/swc-win32-arm64-msvc@16.2.2': '@next/swc-win32-arm64-msvc@16.2.2':
optional: true optional: true
'@next/swc-win32-x64-msvc@15.4.1': '@next/swc-win32-x64-msvc@15.4.1':
optional: true optional: true
'@next/swc-win32-x64-msvc@16.0.10':
optional: true
'@next/swc-win32-x64-msvc@16.2.2': '@next/swc-win32-x64-msvc@16.2.2':
optional: true optional: true
@@ -18447,14 +18137,14 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- typescript - typescript
'@payloadcms/next@3.81.0(@types/react@19.2.7)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)': '@payloadcms/next@3.81.0(@types/react@19.2.7)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)':
dependencies: dependencies:
'@dnd-kit/core': 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@dnd-kit/core': 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@dnd-kit/modifiers': 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) '@dnd-kit/modifiers': 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
'@dnd-kit/sortable': 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) '@dnd-kit/sortable': 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
'@payloadcms/graphql': 3.81.0(graphql@16.13.2)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(typescript@5.9.3) '@payloadcms/graphql': 3.81.0(graphql@16.13.2)(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(typescript@5.9.3)
'@payloadcms/translations': 3.81.0 '@payloadcms/translations': 3.81.0
'@payloadcms/ui': 3.81.0(@types/react@19.2.7)(monaco-editor@0.55.1)(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3) '@payloadcms/ui': 3.81.0(@types/react@19.2.7)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)
busboy: 1.6.0 busboy: 1.6.0
dequal: 2.0.3 dequal: 2.0.3
file-type: 21.3.4 file-type: 21.3.4
@@ -18462,7 +18152,7 @@ snapshots:
graphql-http: 1.22.4(graphql@16.13.2) graphql-http: 1.22.4(graphql@16.13.2)
graphql-playground-html: 1.6.30 graphql-playground-html: 1.6.30
http-status: 2.1.0 http-status: 2.1.0
next: 16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) next: 16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)
path-to-regexp: 6.3.0 path-to-regexp: 6.3.0
payload: 3.81.0(graphql@16.13.2)(typescript@5.9.3) payload: 3.81.0(graphql@16.13.2)(typescript@5.9.3)
qs-esm: 8.0.1 qs-esm: 8.0.1
@@ -18476,7 +18166,7 @@ snapshots:
- supports-color - supports-color
- typescript - typescript
'@payloadcms/richtext-lexical@3.81.0(@faceless-ui/modal@3.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@faceless-ui/scroll-info@2.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@payloadcms/next@3.81.0(@types/react@19.2.7)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3))(@types/react@19.2.7)(monaco-editor@0.55.1)(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(yjs@13.6.30)': '@payloadcms/richtext-lexical@3.81.0(@faceless-ui/modal@3.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@faceless-ui/scroll-info@2.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@payloadcms/next@3.81.0(@types/react@19.2.7)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3))(@types/react@19.2.7)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(yjs@13.6.30)':
dependencies: dependencies:
'@faceless-ui/modal': 3.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@faceless-ui/modal': 3.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@faceless-ui/scroll-info': 2.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@faceless-ui/scroll-info': 2.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -18491,9 +18181,9 @@ snapshots:
'@lexical/selection': 0.41.0 '@lexical/selection': 0.41.0
'@lexical/table': 0.41.0 '@lexical/table': 0.41.0
'@lexical/utils': 0.41.0 '@lexical/utils': 0.41.0
'@payloadcms/next': 3.81.0(@types/react@19.2.7)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3) '@payloadcms/next': 3.81.0(@types/react@19.2.7)(graphql@16.13.2)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)
'@payloadcms/translations': 3.81.0 '@payloadcms/translations': 3.81.0
'@payloadcms/ui': 3.81.0(@types/react@19.2.7)(monaco-editor@0.55.1)(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3) '@payloadcms/ui': 3.81.0(@types/react@19.2.7)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)
'@types/uuid': 10.0.0 '@types/uuid': 10.0.0
acorn: 8.16.0 acorn: 8.16.0
bson-objectid: 2.0.4 bson-objectid: 2.0.4
@@ -18526,7 +18216,7 @@ snapshots:
dependencies: dependencies:
date-fns: 4.1.0 date-fns: 4.1.0
'@payloadcms/ui@3.81.0(@types/react@19.2.7)(monaco-editor@0.55.1)(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)': '@payloadcms/ui@3.81.0(@types/react@19.2.7)(monaco-editor@0.55.1)(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(payload@3.81.0(graphql@16.13.2)(typescript@5.9.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)':
dependencies: dependencies:
'@date-fns/tz': 1.2.0 '@date-fns/tz': 1.2.0
'@dnd-kit/core': 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@dnd-kit/core': 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -18541,7 +18231,7 @@ snapshots:
date-fns: 4.1.0 date-fns: 4.1.0
dequal: 2.0.3 dequal: 2.0.3
md5: 2.3.0 md5: 2.3.0
next: 16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) next: 16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)
object-to-formdata: 4.5.1 object-to-formdata: 4.5.1
payload: 3.81.0(graphql@16.13.2)(typescript@5.9.3) payload: 3.81.0(graphql@16.13.2)(typescript@5.9.3)
qs-esm: 8.0.1 qs-esm: 8.0.1
@@ -27019,11 +26709,11 @@ snapshots:
netmask@2.0.2: {} netmask@2.0.2: {}
next-i18n-router@5.5.5(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)): next-i18n-router@5.5.5(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)):
dependencies: dependencies:
'@formatjs/intl-localematcher': 0.6.2 '@formatjs/intl-localematcher': 0.6.2
negotiator: 1.0.0 negotiator: 1.0.0
next: 16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) next: 16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)
next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies: dependencies:
@@ -27052,29 +26742,30 @@ snapshots:
'@playwright/test': 1.57.0 '@playwright/test': 1.57.0
babel-plugin-react-compiler: 1.0.0 babel-plugin-react-compiler: 1.0.0
sass: 1.77.4 sass: 1.77.4
sharp: 0.34.4 sharp: 0.34.5
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
- babel-plugin-macros - babel-plugin-macros
next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4): next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4):
dependencies: dependencies:
'@next/env': 16.0.10 '@next/env': 16.2.2
'@swc/helpers': 0.5.15 '@swc/helpers': 0.5.15
baseline-browser-mapping: 2.10.15
caniuse-lite: 1.0.30001727 caniuse-lite: 1.0.30001727
postcss: 8.4.31 postcss: 8.4.31
react: 19.1.0 react: 19.1.0
react-dom: 19.1.0(react@19.1.0) react-dom: 19.1.0(react@19.1.0)
styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.1.0) styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.1.0)
optionalDependencies: optionalDependencies:
'@next/swc-darwin-arm64': 16.0.10 '@next/swc-darwin-arm64': 16.2.2
'@next/swc-darwin-x64': 16.0.10 '@next/swc-darwin-x64': 16.2.2
'@next/swc-linux-arm64-gnu': 16.0.10 '@next/swc-linux-arm64-gnu': 16.2.2
'@next/swc-linux-arm64-musl': 16.0.10 '@next/swc-linux-arm64-musl': 16.2.2
'@next/swc-linux-x64-gnu': 16.0.10 '@next/swc-linux-x64-gnu': 16.2.2
'@next/swc-linux-x64-musl': 16.0.10 '@next/swc-linux-x64-musl': 16.2.2
'@next/swc-win32-arm64-msvc': 16.0.10 '@next/swc-win32-arm64-msvc': 16.2.2
'@next/swc-win32-x64-msvc': 16.0.10 '@next/swc-win32-x64-msvc': 16.2.2
'@opentelemetry/api': 1.9.0 '@opentelemetry/api': 1.9.0
'@playwright/test': 1.57.0 '@playwright/test': 1.57.0
babel-plugin-react-compiler: 1.0.0 babel-plugin-react-compiler: 1.0.0
@@ -27227,12 +26918,12 @@ snapshots:
dependencies: dependencies:
esm-env: 1.2.2 esm-env: 1.2.2
nuqs@2.7.2(next@16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0): nuqs@2.7.2(next@16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0):
dependencies: dependencies:
'@standard-schema/spec': 1.0.0 '@standard-schema/spec': 1.0.0
react: 19.1.0 react: 19.1.0
optionalDependencies: optionalDependencies:
next: 16.0.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) next: 16.2.2(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)
nwsapi@2.2.22: {} nwsapi@2.2.22: {}
@@ -29209,36 +28900,6 @@ snapshots:
'@img/sharp-win32-ia32': 0.34.1 '@img/sharp-win32-ia32': 0.34.1
'@img/sharp-win32-x64': 0.34.1 '@img/sharp-win32-x64': 0.34.1
sharp@0.34.4:
dependencies:
'@img/colour': 1.0.0
detect-libc: 2.1.2
semver: 7.7.2
optionalDependencies:
'@img/sharp-darwin-arm64': 0.34.4
'@img/sharp-darwin-x64': 0.34.4
'@img/sharp-libvips-darwin-arm64': 1.2.3
'@img/sharp-libvips-darwin-x64': 1.2.3
'@img/sharp-libvips-linux-arm': 1.2.3
'@img/sharp-libvips-linux-arm64': 1.2.3
'@img/sharp-libvips-linux-ppc64': 1.2.3
'@img/sharp-libvips-linux-s390x': 1.2.3
'@img/sharp-libvips-linux-x64': 1.2.3
'@img/sharp-libvips-linuxmusl-arm64': 1.2.3
'@img/sharp-libvips-linuxmusl-x64': 1.2.3
'@img/sharp-linux-arm': 0.34.4
'@img/sharp-linux-arm64': 0.34.4
'@img/sharp-linux-ppc64': 0.34.4
'@img/sharp-linux-s390x': 0.34.4
'@img/sharp-linux-x64': 0.34.4
'@img/sharp-linuxmusl-arm64': 0.34.4
'@img/sharp-linuxmusl-x64': 0.34.4
'@img/sharp-wasm32': 0.34.4
'@img/sharp-win32-arm64': 0.34.4
'@img/sharp-win32-ia32': 0.34.4
'@img/sharp-win32-x64': 0.34.4
optional: true
sharp@0.34.5: sharp@0.34.5:
dependencies: dependencies:
'@img/colour': 1.0.0 '@img/colour': 1.0.0