diff --git a/apps/cli/README.md b/apps/cli/README.md
new file mode 100644
index 0000000..9ce1112
--- /dev/null
+++ b/apps/cli/README.md
@@ -0,0 +1,59 @@
+# @claudemesh/cli
+
+Client tool for claudemesh — install once per machine, join one or more
+meshes, and your Claude Code sessions can talk to peers on demand.
+
+## Install
+
+```sh
+# From npm (once published)
+npm install -g @claudemesh/cli
+
+# Or from the monorepo during dev
+cd apps/cli && bun link
+```
+
+Then register the MCP server with Claude Code:
+
+```sh
+claudemesh install
+# prints: claude mcp add claudemesh --scope user -- claudemesh mcp
+```
+
+Run the printed command, then restart Claude Code.
+
+## Join a mesh
+
+```sh
+claudemesh join ic://join/BASE64URL...
+```
+
+The invite link is generated by whoever runs the mesh. It bundles the
+mesh id, expiry, signing key, and role. Your CLI verifies it,
+generates a fresh keypair, enrolls you with the broker, and persists
+the result to `~/.claudemesh/config.json`.
+
+## Commands
+
+```sh
+claudemesh install # print MCP registration command
+claudemesh join # join a mesh via invite link
+claudemesh list # show joined meshes + identities
+claudemesh leave # leave a mesh
+claudemesh mcp # start MCP server (stdio — Claude Code only)
+claudemesh --help # show usage
+```
+
+## Env overrides
+
+| Var | Default | Purpose |
+| ----------------------- | ---------------------------- | ------------------------------ |
+| `CLAUDEMESH_BROKER_URL` | `wss://ic.claudemesh.com/ws` | Point at a self-hosted broker |
+| `CLAUDEMESH_CONFIG_DIR` | `~/.claudemesh/` | Override config location |
+| `CLAUDEMESH_DEBUG` | `0` | Verbose logging |
+
+## Status
+
+v0.1.0 scaffold — CLI commands + MCP server shell in place. WS broker
+connection, libsodium crypto, invite-link verification, and auto-install
+of hooks land in subsequent steps.
diff --git a/apps/cli/eslint.config.js b/apps/cli/eslint.config.js
new file mode 100644
index 0000000..005be22
--- /dev/null
+++ b/apps/cli/eslint.config.js
@@ -0,0 +1,3 @@
+import baseConfig from "@turbostarter/eslint-config/base";
+
+export default baseConfig;
diff --git a/apps/cli/package.json b/apps/cli/package.json
new file mode 100644
index 0000000..2e16993
--- /dev/null
+++ b/apps/cli/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "@claudemesh/cli",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "bin": {
+ "claudemesh": "./src/index.ts"
+ },
+ "scripts": {
+ "clean": "git clean -xdf .cache .turbo dist node_modules",
+ "dev": "bun --hot src/index.ts",
+ "start": "bun src/index.ts",
+ "format": "prettier --check . --ignore-path ../../.gitignore",
+ "lint": "eslint",
+ "test": "vitest run",
+ "typecheck": "tsc --noEmit"
+ },
+ "prettier": "@turbostarter/prettier-config",
+ "dependencies": {
+ "@modelcontextprotocol/sdk": "1.27.1",
+ "libsodium-wrappers": "0.7.15",
+ "ws": "8.20.0",
+ "zod": "catalog:"
+ },
+ "devDependencies": {
+ "@turbostarter/eslint-config": "workspace:*",
+ "@turbostarter/prettier-config": "workspace:*",
+ "@turbostarter/tsconfig": "workspace:*",
+ "@turbostarter/vitest-config": "workspace:*",
+ "@types/libsodium-wrappers": "0.7.14",
+ "@types/ws": "8.5.13",
+ "eslint": "catalog:",
+ "prettier": "catalog:",
+ "typescript": "catalog:",
+ "vitest": "catalog:"
+ }
+}
diff --git a/apps/cli/src/commands/install.ts b/apps/cli/src/commands/install.ts
new file mode 100644
index 0000000..83c3ad2
--- /dev/null
+++ b/apps/cli/src/commands/install.ts
@@ -0,0 +1,36 @@
+/**
+ * `claudemesh install` — print Claude Code MCP registration instructions.
+ *
+ * In the v1 flow, users copy-paste a `claude mcp add ...` command.
+ * Later we'll auto-write the MCP entry to ~/.claude.json and hooks
+ * to ~/.claude/settings.json (mirroring claude-intercom's installer).
+ */
+
+import { fileURLToPath } from "node:url";
+import { dirname, resolve } from "node:path";
+
+export function runInstall(): void {
+ // Resolve the path to this package's own index.ts so the generated
+ // command points at the right binary even when installed globally.
+ const here = fileURLToPath(import.meta.url);
+ const entry = resolve(dirname(here), "..", "index.ts");
+
+ console.log("claudemesh — MCP registration");
+ console.log("------------------------------");
+ console.log("");
+ console.log("Register the MCP server with Claude Code:");
+ console.log("");
+ console.log(` claude mcp add claudemesh --scope user -- bun ${entry} mcp`);
+ console.log("");
+ console.log("Or if installed globally:");
+ console.log("");
+ console.log(` claude mcp add claudemesh --scope user -- claudemesh mcp`);
+ console.log("");
+ console.log(
+ "After registering, restart Claude Code. Then join a mesh with:",
+ );
+ console.log("");
+ console.log(" claudemesh join ");
+ console.log("");
+ console.log("(Auto-install of hooks + MCP entry will ship in a later step.)");
+}
diff --git a/apps/cli/src/commands/join.ts b/apps/cli/src/commands/join.ts
new file mode 100644
index 0000000..49132c4
--- /dev/null
+++ b/apps/cli/src/commands/join.ts
@@ -0,0 +1,30 @@
+/**
+ * `claudemesh join ` — parse a mesh invite link and
+ * join the mesh.
+ *
+ * STUB: real invite-link parsing + keypair generation + broker
+ * enrollment lands in Step 17. For now this just validates the link
+ * shape and tells the user what's coming.
+ */
+
+export function runJoin(args: string[]): void {
+ const link = args[0];
+ if (!link) {
+ console.error("Usage: claudemesh join ");
+ console.error("");
+ console.error("Example: claudemesh join ic://join/BASE64URL...");
+ process.exit(1);
+ }
+ if (!link.startsWith("ic://join/")) {
+ console.error(
+ `claudemesh: invalid invite link. Expected ic://join/... got "${link}"`,
+ );
+ process.exit(1);
+ }
+ console.log("claudemesh: join not yet implemented (Step 17).");
+ console.log(` Invite link parsed: ${link.slice(0, 40)}...`);
+ console.log(
+ " Real flow will: verify sig, generate keypair, enroll member, persist to ~/.claudemesh/config.json",
+ );
+ process.exit(0);
+}
diff --git a/apps/cli/src/commands/leave.ts b/apps/cli/src/commands/leave.ts
new file mode 100644
index 0000000..d12e6ea
--- /dev/null
+++ b/apps/cli/src/commands/leave.ts
@@ -0,0 +1,25 @@
+/**
+ * `claudemesh leave ` — remove a mesh from local config.
+ *
+ * Does NOT (yet) notify the broker. In 15b+ this will send a
+ * best-effort revoke request before removing the entry.
+ */
+
+import { loadConfig, saveConfig } from "../state/config";
+
+export function runLeave(args: string[]): void {
+ const slug = args[0];
+ if (!slug) {
+ console.error("Usage: claudemesh leave ");
+ process.exit(1);
+ }
+ const config = loadConfig();
+ const before = config.meshes.length;
+ config.meshes = config.meshes.filter((m) => m.slug !== slug);
+ if (config.meshes.length === before) {
+ console.error(`claudemesh: no joined mesh with slug "${slug}"`);
+ process.exit(1);
+ }
+ saveConfig(config);
+ console.log(`Left mesh "${slug}". Remaining: ${config.meshes.length}`);
+}
diff --git a/apps/cli/src/commands/list.ts b/apps/cli/src/commands/list.ts
new file mode 100644
index 0000000..7d80ab5
--- /dev/null
+++ b/apps/cli/src/commands/list.ts
@@ -0,0 +1,28 @@
+/**
+ * `claudemesh list` — show all joined meshes + their status.
+ */
+
+import { loadConfig, getConfigPath } from "../state/config";
+
+export function runList(): void {
+ const config = loadConfig();
+ if (config.meshes.length === 0) {
+ console.log("No meshes joined yet.");
+ console.log("");
+ console.log("Join one with: claudemesh join ");
+ console.log(`Config file: ${getConfigPath()}`);
+ return;
+ }
+ console.log(`Joined meshes (${config.meshes.length}):`);
+ console.log("");
+ for (const m of config.meshes) {
+ console.log(` ${m.name} (${m.slug})`);
+ console.log(` mesh id: ${m.meshId}`);
+ console.log(` member id: ${m.memberId}`);
+ console.log(` pubkey: ${m.pubkey.slice(0, 16)}…`);
+ console.log(` broker: ${m.brokerUrl}`);
+ console.log(` joined: ${m.joinedAt}`);
+ console.log("");
+ }
+ console.log(`Config: ${getConfigPath()}`);
+}
diff --git a/apps/cli/src/env.ts b/apps/cli/src/env.ts
new file mode 100644
index 0000000..9dddba4
--- /dev/null
+++ b/apps/cli/src/env.ts
@@ -0,0 +1,27 @@
+import { z } from "zod";
+
+/**
+ * CLI environment config.
+ *
+ * Read once at startup. Overridable via env vars so users can point
+ * 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;
+
+export function loadEnv(): CliEnv {
+ const parsed = envSchema.safeParse(process.env);
+ if (!parsed.success) {
+ console.error("[claudemesh] invalid environment:");
+ console.error(z.treeifyError(parsed.error));
+ process.exit(1);
+ }
+ return parsed.data;
+}
+
+export const env = loadEnv();
diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts
new file mode 100644
index 0000000..992ede1
--- /dev/null
+++ b/apps/cli/src/index.ts
@@ -0,0 +1,73 @@
+#!/usr/bin/env bun
+/**
+ * @claudemesh/cli entry point.
+ *
+ * Dispatches between two modes:
+ * - `claudemesh mcp` → MCP server (stdio transport)
+ * - `claudemesh ` → CLI subcommand
+ *
+ * Claude Code invokes the `mcp` mode via stdio. Humans use all others.
+ */
+
+import { startMcpServer } from "./mcp/server";
+import { runInstall } from "./commands/install";
+import { runJoin } from "./commands/join";
+import { runList } from "./commands/list";
+import { runLeave } from "./commands/leave";
+
+const HELP = `claudemesh — peer mesh for Claude Code sessions
+
+Usage:
+ claudemesh [args]
+
+Commands:
+ install Print Claude Code MCP registration instructions
+ join Join a mesh via invite link (ic://join/...)
+ list Show all joined meshes
+ leave Leave a joined mesh
+ mcp Start MCP server (stdio) — invoked by Claude Code
+ --help, -h Show this help
+
+Environment:
+ CLAUDEMESH_BROKER_URL Override broker URL (default: wss://ic.claudemesh.com/ws)
+ CLAUDEMESH_CONFIG_DIR Override config directory (default: ~/.claudemesh/)
+ CLAUDEMESH_DEBUG=1 Verbose logging
+`;
+
+const cmd = process.argv[2];
+const args = process.argv.slice(3);
+
+async function main(): Promise {
+ switch (cmd) {
+ case "mcp":
+ await startMcpServer();
+ return;
+ case "install":
+ runInstall();
+ return;
+ case "join":
+ runJoin(args);
+ return;
+ case "list":
+ runList();
+ return;
+ case "leave":
+ runLeave(args);
+ return;
+ case "--help":
+ case "-h":
+ case "help":
+ case undefined:
+ console.log(HELP);
+ return;
+ default:
+ console.error(`Unknown command: ${cmd}`);
+ console.error("Run `claudemesh --help` for usage.");
+ process.exit(1);
+ }
+}
+
+main().catch((e) => {
+ console.error(`claudemesh: ${e instanceof Error ? e.message : String(e)}`);
+ process.exit(1);
+});
diff --git a/apps/cli/src/mcp/server.ts b/apps/cli/src/mcp/server.ts
new file mode 100644
index 0000000..2359344
--- /dev/null
+++ b/apps/cli/src/mcp/server.ts
@@ -0,0 +1,86 @@
+/**
+ * MCP server (stdio transport) for @claudemesh/cli.
+ *
+ * Invoked by Claude Code as a stdio subprocess. Exposes the 5 tools
+ * in tools.ts. In this 15a scaffold, all tools return a "not
+ * connected" response; 15b will wire them to a live WS broker
+ * connection.
+ */
+
+import { Server } from "@modelcontextprotocol/sdk/server/index.js";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import {
+ ListToolsRequestSchema,
+ CallToolRequestSchema,
+} from "@modelcontextprotocol/sdk/types.js";
+import { TOOLS } from "./tools";
+import { loadConfig } from "../state/config";
+
+const NOT_CONNECTED = {
+ content: [
+ {
+ type: "text" as const,
+ text: "claudemesh: not yet connected to broker. Run `claudemesh join ` to join a mesh, then restart your Claude Code session. (Broker client wiring lands in Step 15b — scaffold only for now.)",
+ },
+ ],
+ isError: true,
+};
+
+const INSTRUCTIONS = `You are connected to a claudemesh — a peer-to-peer network of other Claude Code sessions.
+
+Use these tools to coordinate with peers on demand. Each mesh is a trust boundary; messages are E2E-encrypted and routed through a shared broker.
+
+Available tools:
+- send_message: send a direct or channel message
+- list_peers: see who else is in your meshes and their status
+- check_messages: pull undelivered messages (normally pushed automatically)
+- set_summary: describe what you're working on (visible to peers)
+- set_status: manually override your presence (idle/working/dnd)
+
+When you receive an inbound message (channel notification), respond promptly — like answering a knock on the door. The sender is waiting on you.`;
+
+export async function startMcpServer(): Promise {
+ // Load config so we know which meshes the user has joined.
+ const config = loadConfig();
+
+ const server = new Server(
+ { name: "claudemesh", version: "0.1.0" },
+ {
+ capabilities: { tools: {} },
+ instructions: INSTRUCTIONS,
+ },
+ );
+
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
+ tools: TOOLS,
+ }));
+
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
+ const { name } = req.params;
+ // Stubs: all tools return "not connected" until 15b.
+ if (config.meshes.length === 0) {
+ return {
+ content: [
+ {
+ type: "text" as const,
+ text: `claudemesh: no meshes joined yet. Run \`claudemesh join \` to join one.`,
+ },
+ ],
+ isError: true,
+ };
+ }
+ switch (name) {
+ case "send_message":
+ case "list_peers":
+ case "check_messages":
+ case "set_summary":
+ case "set_status":
+ return NOT_CONNECTED;
+ default:
+ throw new Error(`Unknown tool: ${name}`);
+ }
+ });
+
+ const transport = new StdioServerTransport();
+ await server.connect(transport);
+}
diff --git a/apps/cli/src/mcp/tools.ts b/apps/cli/src/mcp/tools.ts
new file mode 100644
index 0000000..e4a5fa2
--- /dev/null
+++ b/apps/cli/src/mcp/tools.ts
@@ -0,0 +1,81 @@
+/**
+ * MCP tool definitions exposed to Claude Code.
+ *
+ * Mirror the claude-intercom tool surface: send_message, list_peers,
+ * check_messages, set_summary, set_status. Tools return "not
+ * connected" errors until 15b wires the WS client.
+ */
+
+import type { Tool } from "@modelcontextprotocol/sdk/types.js";
+
+export const TOOLS: Tool[] = [
+ {
+ name: "send_message",
+ 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.",
+ inputSchema: {
+ type: "object",
+ properties: {
+ to: {
+ type: "string",
+ description: "Peer name, pubkey, or #channel",
+ },
+ message: { type: "string", description: "Message text" },
+ priority: {
+ type: "string",
+ enum: ["now", "next", "low"],
+ description: "Delivery priority (default: next)",
+ },
+ },
+ required: ["to", "message"],
+ },
+ },
+ {
+ name: "list_peers",
+ description:
+ "List peers across all joined meshes. Shows name, mesh, status (idle/working/dnd), and current summary.",
+ inputSchema: {
+ type: "object",
+ properties: {
+ mesh_slug: {
+ type: "string",
+ description: "Only list peers in this mesh (optional)",
+ },
+ },
+ },
+ },
+ {
+ name: "check_messages",
+ description:
+ "Pull any undelivered messages from the broker. Normally messages arrive via push; use this to drain the queue after being offline.",
+ inputSchema: { type: "object", properties: {} },
+ },
+ {
+ name: "set_summary",
+ description:
+ "Set a 1–2 sentence summary of what you're working on. Visible to other peers.",
+ inputSchema: {
+ type: "object",
+ properties: {
+ summary: { type: "string", description: "1-2 sentence summary" },
+ },
+ required: ["summary"],
+ },
+ },
+ {
+ name: "set_status",
+ description:
+ "Manually override your status. `dnd` blocks everything except `now`-priority messages.",
+ inputSchema: {
+ type: "object",
+ properties: {
+ status: {
+ type: "string",
+ enum: ["idle", "working", "dnd"],
+ description: "Your status",
+ },
+ },
+ required: ["status"],
+ },
+ },
+];
diff --git a/apps/cli/src/mcp/types.ts b/apps/cli/src/mcp/types.ts
new file mode 100644
index 0000000..b4bc92d
--- /dev/null
+++ b/apps/cli/src/mcp/types.ts
@@ -0,0 +1,24 @@
+/**
+ * MCP tool schemas + shared types for the CLI's MCP server.
+ */
+
+export type Priority = "now" | "next" | "low";
+export type PeerStatus = "idle" | "working" | "dnd";
+
+export interface SendMessageArgs {
+ to: string; // peer name, pubkey, or #channel
+ message: string;
+ priority?: Priority;
+}
+
+export interface ListPeersArgs {
+ mesh_slug?: string; // filter to one joined mesh
+}
+
+export interface SetSummaryArgs {
+ summary: string;
+}
+
+export interface SetStatusArgs {
+ status: PeerStatus;
+}
diff --git a/apps/cli/src/state/config.ts b/apps/cli/src/state/config.ts
new file mode 100644
index 0000000..8f34a82
--- /dev/null
+++ b/apps/cli/src/state/config.ts
@@ -0,0 +1,58 @@
+/**
+ * Local persistent config — ~/.claudemesh/config.json
+ *
+ * Stores: joined meshes, per-mesh identity keys (ed25519 keypairs),
+ * last-seen broker URL. Loaded on CLI start, on MCP server start,
+ * and on every join/leave.
+ */
+
+import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
+import { homedir } from "node:os";
+import { join, dirname } from "node:path";
+import { z } from "zod";
+import { env } from "../env";
+
+const joinedMeshSchema = z.object({
+ meshId: z.string(),
+ memberId: z.string(),
+ slug: z.string(),
+ name: z.string(),
+ pubkey: z.string(), // ed25519 hex (32 bytes = 64 chars)
+ secretKey: z.string(), // ed25519 hex (64 bytes = 128 chars)
+ brokerUrl: z.string(),
+ joinedAt: z.string(),
+});
+
+const configSchema = z.object({
+ version: z.literal(1).default(1),
+ meshes: z.array(joinedMeshSchema).default([]),
+});
+
+export type JoinedMesh = z.infer;
+export type Config = z.infer;
+
+const CONFIG_DIR = env.CLAUDEMESH_CONFIG_DIR ?? join(homedir(), ".claudemesh");
+const CONFIG_PATH = join(CONFIG_DIR, "config.json");
+
+export function loadConfig(): Config {
+ if (!existsSync(CONFIG_PATH)) {
+ return configSchema.parse({ version: 1, meshes: [] });
+ }
+ try {
+ const raw = readFileSync(CONFIG_PATH, "utf-8");
+ return configSchema.parse(JSON.parse(raw));
+ } catch (e) {
+ throw new Error(
+ `Failed to load ${CONFIG_PATH}: ${e instanceof Error ? e.message : String(e)}`,
+ );
+ }
+}
+
+export function saveConfig(config: Config): void {
+ mkdirSync(dirname(CONFIG_PATH), { recursive: true });
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
+}
+
+export function getConfigPath(): string {
+ return CONFIG_PATH;
+}
diff --git a/apps/cli/src/ws/client.ts b/apps/cli/src/ws/client.ts
new file mode 100644
index 0000000..b333896
--- /dev/null
+++ b/apps/cli/src/ws/client.ts
@@ -0,0 +1,40 @@
+/**
+ * WS client to the broker (STUB).
+ *
+ * Final implementation in Step 15b — connects to broker, sends hello
+ * (with signed nonce), pumps messages to/from the MCP server, handles
+ * reconnect. For now just a placeholder type surface so the MCP
+ * server can depend on it.
+ */
+
+import type { JoinedMesh } from "../state/config";
+
+export interface BrokerConnection {
+ meshId: string;
+ isConnected(): boolean;
+ sendMessage(args: {
+ targetSpec: string;
+ priority: "now" | "next" | "low";
+ nonce: string;
+ ciphertext: string;
+ }): Promise<{ ok: boolean; messageId?: string; error?: string }>;
+ close(): void;
+}
+
+/**
+ * Stub broker connection. Returns "not implemented" errors on every
+ * call. Real implementation in 15b will connect to env.CLAUDEMESH_BROKER_URL.
+ */
+export function connectBroker(_mesh: JoinedMesh): BrokerConnection {
+ return {
+ meshId: _mesh.meshId,
+ isConnected: () => false,
+ sendMessage: async () => ({
+ ok: false,
+ error: "broker client not implemented (Step 15b)",
+ }),
+ close: () => {
+ /* noop */
+ },
+ };
+}
diff --git a/apps/cli/tsconfig.json b/apps/cli/tsconfig.json
new file mode 100644
index 0000000..379db76
--- /dev/null
+++ b/apps/cli/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "@turbostarter/tsconfig/base.json",
+ "compilerOptions": {
+ "lib": ["es2022"],
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "baseUrl": ".",
+ "paths": {
+ "~/*": ["./src/*"]
+ },
+ "types": ["bun-types"]
+ },
+ "include": ["src/**/*.ts"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8444687..4881c0c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -161,6 +161,52 @@ importers:
specifier: 'catalog:'
version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@24.0.13)(@vitest/ui@4.0.14)(jiti@2.6.1)(jsdom@26.0.0)(lightningcss@1.30.2)(terser@5.43.1)(tsx@4.19.2)(yaml@2.8.0)
+ apps/cli:
+ dependencies:
+ '@modelcontextprotocol/sdk':
+ specifier: 1.27.1
+ version: 1.27.1(zod@4.1.13)
+ libsodium-wrappers:
+ specifier: 0.7.15
+ version: 0.7.15
+ ws:
+ specifier: 8.20.0
+ version: 8.20.0
+ zod:
+ specifier: 'catalog:'
+ version: 4.1.13
+ devDependencies:
+ '@turbostarter/eslint-config':
+ specifier: workspace:*
+ version: link:../../tooling/eslint
+ '@turbostarter/prettier-config':
+ specifier: workspace:*
+ version: link:../../tooling/prettier
+ '@turbostarter/tsconfig':
+ specifier: workspace:*
+ version: link:../../tooling/typescript
+ '@turbostarter/vitest-config':
+ specifier: workspace:*
+ version: link:../../tooling/vitest
+ '@types/libsodium-wrappers':
+ specifier: 0.7.14
+ version: 0.7.14
+ '@types/ws':
+ specifier: 8.5.13
+ version: 8.5.13
+ eslint:
+ specifier: 'catalog:'
+ version: 9.39.0(jiti@2.6.1)
+ prettier:
+ specifier: 'catalog:'
+ version: 3.6.2
+ typescript:
+ specifier: 'catalog:'
+ version: 5.9.3
+ vitest:
+ specifier: 'catalog:'
+ version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@24.0.13)(@vitest/ui@4.0.14)(jiti@2.6.1)(jsdom@26.0.0)(lightningcss@1.30.2)(terser@5.43.1)(tsx@4.19.2)(yaml@2.8.0)
+
apps/web:
dependencies:
'@ai-sdk/react':
@@ -3461,6 +3507,12 @@ packages:
'@hexagon/base64@1.1.28':
resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==}
+ '@hono/node-server@1.19.12':
+ resolution: {integrity: sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==}
+ engines: {node: '>=18.14.1'}
+ peerDependencies:
+ hono: ^4
+
'@hono/zod-validator@0.7.4':
resolution: {integrity: sha512-biKGn3BRJVaftZlIPMyK+HCe/UHAjJ6sH0UyXe3+v0OcgVr9xfImDROTJFLtn9e3XEEAHGZIM9U6evu85abm8Q==}
peerDependencies:
@@ -3835,6 +3887,16 @@ packages:
'@mixpanel/rrweb@2.0.0-alpha.18.2':
resolution: {integrity: sha512-J3dVTEu6Z4p8di7y9KKvUooNuBjX97DdG6XGWoPEPi07A9512h9M8MEtvlY3mK0PGfuC0Mz5Pv/Ws6gjGYfKQg==}
+ '@modelcontextprotocol/sdk@1.27.1':
+ resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@cfworker/json-schema': ^4.1.1
+ zod: ^3.25 || ^4.0
+ peerDependenciesMeta:
+ '@cfworker/json-schema':
+ optional: true
+
'@napi-rs/canvas-android-arm64@0.1.88':
resolution: {integrity: sha512-KEaClPnZuVxJ8smUWjV1wWFkByBO/D+vy4lN+Dm5DFH514oqwukxKGeck9xcKJhaWJGjfruGmYGiwRe//+/zQQ==}
engines: {node: '>= 10'}
@@ -7297,6 +7359,10 @@ packages:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
+ accepts@2.0.0:
+ resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
+ engines: {node: '>= 0.6'}
+
acorn-import-attributes@1.9.5:
resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
peerDependencies:
@@ -7354,6 +7420,14 @@ packages:
ajv:
optional: true
+ ajv-formats@3.0.1:
+ resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
+ peerDependencies:
+ ajv: ^8.0.0
+ peerDependenciesMeta:
+ ajv:
+ optional: true
+
ajv-keywords@5.1.0:
resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==}
peerDependencies:
@@ -7686,6 +7760,10 @@ packages:
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+ body-parser@2.2.2:
+ resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==}
+ engines: {node: '>=18'}
+
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
@@ -7986,6 +8064,14 @@ packages:
constant-case@2.0.0:
resolution: {integrity: sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==}
+ content-disposition@1.0.1:
+ resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==}
+ engines: {node: '>=18'}
+
+ content-type@1.0.5:
+ resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
+ engines: {node: '>= 0.6'}
+
conventional-changelog-angular@7.0.0:
resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==}
engines: {node: '>=16'}
@@ -8005,6 +8091,10 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+ cookie-signature@1.2.2:
+ resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
+ engines: {node: '>=6.6.0'}
+
cookie@0.7.2:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
engines: {node: '>= 0.6'}
@@ -8230,6 +8320,15 @@ packages:
supports-color:
optional: true
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
decamelize@1.2.0:
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
engines: {node: '>=0.10.0'}
@@ -8844,6 +8943,10 @@ packages:
resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
engines: {node: '>=18.0.0'}
+ eventsource@3.0.7:
+ resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==}
+ engines: {node: '>=18.0.0'}
+
exec-async@2.2.0:
resolution: {integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==}
@@ -8988,6 +9091,16 @@ packages:
exponential-backoff@3.1.2:
resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==}
+ express-rate-limit@8.3.2:
+ resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==}
+ engines: {node: '>= 16'}
+ peerDependencies:
+ express: '>= 4.11'
+
+ express@5.2.1:
+ resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
+ engines: {node: '>= 18'}
+
exsolve@1.0.7:
resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
@@ -9089,6 +9202,10 @@ packages:
resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==}
engines: {node: '>= 0.8'}
+ finalhandler@2.1.1:
+ resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
+ engines: {node: '>= 18.0.0'}
+
find-cache-dir@3.3.2:
resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
engines: {node: '>=8'}
@@ -9137,6 +9254,10 @@ packages:
forwarded-parse@2.1.2:
resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
+ forwarded@0.2.0:
+ resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
+ engines: {node: '>= 0.6'}
+
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
@@ -9176,6 +9297,10 @@ packages:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
engines: {node: '>= 0.6'}
+ fresh@2.0.0:
+ resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
+ engines: {node: '>= 0.8'}
+
fromentries@1.3.2:
resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==}
@@ -9442,6 +9567,10 @@ packages:
resolution: {integrity: sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg==}
engines: {node: '>=16.9.0'}
+ hono@4.12.10:
+ resolution: {integrity: sha512-mx/p18PLy5og9ufies2GOSUqep98Td9q4i/EF6X7yJgAiIopxqdfIO3jbqsi3jRgTgw88jMDEzVKi+V2EF+27w==}
+ engines: {node: '>=16.9.0'}
+
hosted-git-info@7.0.2:
resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
engines: {node: ^16.14.0 || >=18.0.0}
@@ -9473,6 +9602,10 @@ packages:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
+ http-errors@2.0.1:
+ resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
+ engines: {node: '>= 0.8'}
+
http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
@@ -9519,6 +9652,10 @@ packages:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
+ iconv-lite@0.7.2:
+ resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
+ engines: {node: '>=0.10.0'}
+
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -9607,10 +9744,18 @@ packages:
invariant@2.2.4:
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
+ ip-address@10.1.0:
+ resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==}
+ engines: {node: '>= 12'}
+
ip-address@9.0.5:
resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
engines: {node: '>= 12'}
+ ipaddr.js@1.9.1:
+ resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
+ engines: {node: '>= 0.10'}
+
is-alphabetical@2.0.1:
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
@@ -9744,6 +9889,9 @@ packages:
is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+ is-promise@4.0.0:
+ resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
+
is-reference@1.2.1:
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
@@ -9932,6 +10080,9 @@ packages:
jose@6.1.0:
resolution: {integrity: sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==}
+ jose@6.2.2:
+ resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==}
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -9987,6 +10138,9 @@ packages:
json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+ json-schema-typed@8.0.2:
+ resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==}
+
json-schema@0.4.0:
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
@@ -10370,6 +10524,10 @@ packages:
mdn-data@2.0.30:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
+ media-typer@1.1.0:
+ resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
+ engines: {node: '>= 0.8'}
+
memoize-one@5.2.1:
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
@@ -10380,6 +10538,10 @@ packages:
resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==}
engines: {node: '>=16.10'}
+ merge-descriptors@2.0.0:
+ resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
+ engines: {node: '>=18'}
+
merge-options@3.0.4:
resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==}
engines: {node: '>=10'}
@@ -10556,6 +10718,10 @@ packages:
resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==}
engines: {node: '>= 0.6'}
+ mime-types@3.0.2:
+ resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
+ engines: {node: '>=18'}
+
mime@1.6.0:
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
engines: {node: '>=4'}
@@ -11089,6 +11255,9 @@ packages:
resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==}
engines: {node: 20 || >=22}
+ path-to-regexp@8.4.2:
+ resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==}
+
path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
@@ -11173,6 +11342,10 @@ packages:
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
engines: {node: '>= 6'}
+ pkce-challenge@5.0.1:
+ resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==}
+ engines: {node: '>=16.20.0'}
+
pkg-dir@4.2.0:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'}
@@ -11446,6 +11619,10 @@ packages:
property-information@7.1.0:
resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
+ proxy-addr@2.0.7:
+ resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
+ engines: {node: '>= 0.10'}
+
proxy-agent@6.5.0:
resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==}
engines: {node: '>= 14'}
@@ -11481,6 +11658,10 @@ packages:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
+ qs@6.15.0:
+ resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==}
+ engines: {node: '>=0.6'}
+
query-string@7.1.3:
resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==}
engines: {node: '>=6'}
@@ -11514,6 +11695,10 @@ packages:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
+ raw-body@3.0.2:
+ resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
+ engines: {node: '>= 0.10'}
+
rc@1.2.8:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
@@ -11962,6 +12147,10 @@ packages:
rou3@0.7.10:
resolution: {integrity: sha512-aoFj6f7MJZ5muJ+Of79nrhs9N3oLGqi2VEMe94Zbkjb6Wupha46EuoYgpWSOZlXww3bbd8ojgXTAA2mzimX5Ww==}
+ router@2.2.0:
+ resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
+ engines: {node: '>= 18'}
+
rrweb-cssom@0.8.0:
resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
@@ -12054,6 +12243,10 @@ packages:
resolution: {integrity: sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==}
engines: {node: '>= 0.8.0'}
+ send@1.2.1:
+ resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
+ engines: {node: '>= 18'}
+
sentence-case@2.1.1:
resolution: {integrity: sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==}
@@ -12068,6 +12261,10 @@ packages:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
engines: {node: '>= 0.8.0'}
+ serve-static@2.2.1:
+ resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
+ engines: {node: '>= 18'}
+
server-only@0.0.1:
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
@@ -12294,6 +12491,10 @@ packages:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
+ statuses@2.0.2:
+ resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
+ engines: {node: '>= 0.8'}
+
std-env@3.10.0:
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
@@ -12745,6 +12946,10 @@ packages:
resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
engines: {node: '>=8'}
+ type-is@2.0.1:
+ resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
+ engines: {node: '>= 0.6'}
+
typed-array-buffer@1.0.3:
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
engines: {node: '>= 0.4'}
@@ -13362,6 +13567,11 @@ packages:
resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==}
engines: {node: '>=18'}
+ zod-to-json-schema@3.25.2:
+ resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==}
+ peerDependencies:
+ zod: ^3.25.28 || ^4
+
zod@3.24.3:
resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==}
@@ -16024,6 +16234,10 @@ snapshots:
'@hexagon/base64@1.1.28': {}
+ '@hono/node-server@1.19.12(hono@4.12.10)':
+ dependencies:
+ hono: 4.12.10
+
'@hono/zod-validator@0.7.4(hono@4.10.4)(zod@4.1.13)':
dependencies:
hono: 4.10.4
@@ -16367,6 +16581,28 @@ snapshots:
base64-arraybuffer: 1.0.2
mitt: 3.0.1
+ '@modelcontextprotocol/sdk@1.27.1(zod@4.1.13)':
+ dependencies:
+ '@hono/node-server': 1.19.12(hono@4.12.10)
+ ajv: 8.17.1
+ ajv-formats: 3.0.1(ajv@8.17.1)
+ content-type: 1.0.5
+ cors: 2.8.5
+ cross-spawn: 7.0.6
+ eventsource: 3.0.7
+ eventsource-parser: 3.0.6
+ express: 5.2.1
+ express-rate-limit: 8.3.2(express@5.2.1)
+ hono: 4.12.10
+ jose: 6.2.2
+ json-schema-typed: 8.0.2
+ pkce-challenge: 5.0.1
+ raw-body: 3.0.2
+ zod: 4.1.13
+ zod-to-json-schema: 3.25.2(zod@4.1.13)
+ transitivePeerDependencies:
+ - supports-color
+
'@napi-rs/canvas-android-arm64@0.1.88':
optional: true
@@ -20696,6 +20932,11 @@ snapshots:
mime-types: 2.1.35
negotiator: 0.6.3
+ accepts@2.0.0:
+ dependencies:
+ mime-types: 3.0.1
+ negotiator: 1.0.0
+
acorn-import-attributes@1.9.5(acorn@8.15.0):
dependencies:
acorn: 8.15.0
@@ -20747,6 +20988,10 @@ snapshots:
optionalDependencies:
ajv: 8.17.1
+ ajv-formats@3.0.1(ajv@8.17.1):
+ optionalDependencies:
+ ajv: 8.17.1
+
ajv-keywords@5.1.0(ajv@8.17.1):
dependencies:
ajv: 8.17.1
@@ -21147,6 +21392,20 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
+ body-parser@2.2.2:
+ dependencies:
+ bytes: 3.1.2
+ content-type: 1.0.5
+ debug: 4.4.3
+ http-errors: 2.0.0
+ iconv-lite: 0.7.2
+ on-finished: 2.4.1
+ qs: 6.15.0
+ raw-body: 3.0.2
+ type-is: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
boolbase@1.0.0: {}
bowser@2.11.0: {}
@@ -21197,8 +21456,7 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
- bytes@3.1.2:
- optional: true
+ bytes@3.1.2: {}
caching-transform@4.0.0:
dependencies:
@@ -21486,6 +21744,10 @@ snapshots:
snake-case: 2.1.0
upper-case: 1.1.3
+ content-disposition@1.0.1: {}
+
+ content-type@1.0.5: {}
+
conventional-changelog-angular@7.0.0:
dependencies:
compare-func: 2.0.0
@@ -21505,6 +21767,8 @@ snapshots:
convert-source-map@2.0.0: {}
+ cookie-signature@1.2.2: {}
+
cookie@0.7.2: {}
core-js-compat@3.44.0:
@@ -21706,6 +21970,10 @@ snapshots:
dependencies:
ms: 2.1.3
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
decamelize@1.2.0: {}
decimal.js-light@2.5.1: {}
@@ -22462,6 +22730,10 @@ snapshots:
eventsource-parser@3.0.6: {}
+ eventsource@3.0.7:
+ dependencies:
+ eventsource-parser: 3.0.6
+
exec-async@2.2.0:
optional: true
@@ -22660,6 +22932,44 @@ snapshots:
exponential-backoff@3.1.2: {}
+ express-rate-limit@8.3.2(express@5.2.1):
+ dependencies:
+ express: 5.2.1
+ ip-address: 10.1.0
+
+ express@5.2.1:
+ dependencies:
+ accepts: 2.0.0
+ body-parser: 2.2.2
+ content-disposition: 1.0.1
+ content-type: 1.0.5
+ cookie: 0.7.2
+ cookie-signature: 1.2.2
+ debug: 4.4.1
+ depd: 2.0.0
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ etag: 1.8.1
+ finalhandler: 2.1.1
+ fresh: 2.0.0
+ http-errors: 2.0.0
+ merge-descriptors: 2.0.0
+ mime-types: 3.0.1
+ on-finished: 2.4.1
+ once: 1.4.0
+ parseurl: 1.3.3
+ proxy-addr: 2.0.7
+ qs: 6.14.0
+ range-parser: 1.2.1
+ router: 2.2.0
+ send: 1.2.1
+ serve-static: 2.2.1
+ statuses: 2.0.1
+ type-is: 2.0.1
+ vary: 1.1.2
+ transitivePeerDependencies:
+ - supports-color
+
exsolve@1.0.7: {}
extend@3.0.2: {}
@@ -22772,6 +23082,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ finalhandler@2.1.1:
+ dependencies:
+ debug: 4.4.1
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ on-finished: 2.4.1
+ parseurl: 1.3.3
+ statuses: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
find-cache-dir@3.3.2:
dependencies:
commondir: 1.0.1
@@ -22830,6 +23151,8 @@ snapshots:
forwarded-parse@2.1.2: {}
+ forwarded@0.2.0: {}
+
fraction.js@4.3.7: {}
framer-motion@12.23.24(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
@@ -22855,6 +23178,8 @@ snapshots:
fresh@0.5.2: {}
+ fresh@2.0.0: {}
+
fromentries@1.3.2: {}
fs-constants@1.0.0:
@@ -23218,6 +23543,8 @@ snapshots:
hono@4.10.4: {}
+ hono@4.12.10: {}
+
hosted-git-info@7.0.2:
dependencies:
lru-cache: 10.4.3
@@ -23260,6 +23587,14 @@ snapshots:
statuses: 2.0.1
toidentifier: 1.0.1
+ http-errors@2.0.1:
+ dependencies:
+ depd: 2.0.0
+ inherits: 2.0.4
+ setprototypeof: 1.2.0
+ statuses: 2.0.2
+ toidentifier: 1.0.1
+
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.4
@@ -23310,6 +23645,10 @@ snapshots:
dependencies:
safer-buffer: 2.1.2
+ iconv-lite@0.7.2:
+ dependencies:
+ safer-buffer: 2.1.2
+
ieee754@1.2.1: {}
ignore@5.3.2: {}
@@ -23415,11 +23754,15 @@ snapshots:
dependencies:
loose-envify: 1.4.0
+ ip-address@10.1.0: {}
+
ip-address@9.0.5:
dependencies:
jsbn: 1.1.0
sprintf-js: 1.1.3
+ ipaddr.js@1.9.1: {}
+
is-alphabetical@2.0.1: {}
is-alphanumerical@2.0.1:
@@ -23533,6 +23876,8 @@ snapshots:
is-potential-custom-element-name@1.0.1: {}
+ is-promise@4.0.0: {}
+
is-reference@1.2.1:
dependencies:
'@types/estree': 1.0.8
@@ -23776,6 +24121,8 @@ snapshots:
jose@6.1.0: {}
+ jose@6.2.2: {}
+
js-tokens@4.0.0: {}
js-tokens@9.0.1: {}
@@ -23838,6 +24185,8 @@ snapshots:
json-schema-traverse@1.0.0: {}
+ json-schema-typed@8.0.2: {}
+
json-schema@0.4.0: {}
json-stable-stringify-without-jsonify@1.0.1: {}
@@ -24281,6 +24630,8 @@ snapshots:
mdn-data@2.0.30: {}
+ media-typer@1.1.0: {}
+
memoize-one@5.2.1: {}
memoize-one@6.0.0:
@@ -24288,6 +24639,8 @@ snapshots:
meow@12.1.1: {}
+ merge-descriptors@2.0.0: {}
+
merge-options@3.0.4:
dependencies:
is-plain-obj: 2.1.0
@@ -24689,6 +25042,10 @@ snapshots:
dependencies:
mime-db: 1.54.0
+ mime-types@3.0.2:
+ dependencies:
+ mime-db: 1.54.0
+
mime@1.6.0: {}
mimic-fn@1.2.0:
@@ -25340,6 +25697,8 @@ snapshots:
lru-cache: 11.1.0
minipass: 7.1.2
+ path-to-regexp@8.4.2: {}
+
path-type@4.0.0: {}
pathe@2.0.3: {}
@@ -25424,6 +25783,8 @@ snapshots:
pirates@4.0.7: {}
+ pkce-challenge@5.0.1: {}
+
pkg-dir@4.2.0:
dependencies:
find-up: 4.1.0
@@ -25626,6 +25987,11 @@ snapshots:
property-information@7.1.0: {}
+ proxy-addr@2.0.7:
+ dependencies:
+ forwarded: 0.2.0
+ ipaddr.js: 1.9.1
+
proxy-agent@6.5.0:
dependencies:
agent-base: 7.1.4
@@ -25666,6 +26032,10 @@ snapshots:
dependencies:
side-channel: 1.1.0
+ qs@6.15.0:
+ dependencies:
+ side-channel: 1.1.0
+
query-string@7.1.3:
dependencies:
decode-uri-component: 0.2.2
@@ -25751,6 +26121,13 @@ snapshots:
range-parser@1.2.1: {}
+ raw-body@3.0.2:
+ dependencies:
+ bytes: 3.1.2
+ http-errors: 2.0.1
+ iconv-lite: 0.7.2
+ unpipe: 1.0.0
+
rc@1.2.8:
dependencies:
deep-extend: 0.6.0
@@ -26556,6 +26933,16 @@ snapshots:
rou3@0.7.10: {}
+ router@2.2.0:
+ dependencies:
+ debug: 4.4.1
+ depd: 2.0.0
+ is-promise: 4.0.0
+ parseurl: 1.3.3
+ path-to-regexp: 8.4.2
+ transitivePeerDependencies:
+ - supports-color
+
rrweb-cssom@0.8.0: {}
rtl-detect@1.1.2:
@@ -26670,6 +27057,22 @@ snapshots:
- supports-color
optional: true
+ send@1.2.1:
+ dependencies:
+ debug: 4.4.3
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ etag: 1.8.1
+ fresh: 2.0.0
+ http-errors: 2.0.1
+ mime-types: 3.0.2
+ ms: 2.1.3
+ on-finished: 2.4.1
+ range-parser: 1.2.1
+ statuses: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
sentence-case@2.1.1:
dependencies:
no-case: 2.3.2
@@ -26690,6 +27093,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ serve-static@2.2.1:
+ dependencies:
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ parseurl: 1.3.3
+ send: 1.2.1
+ transitivePeerDependencies:
+ - supports-color
+
server-only@0.0.1:
optional: true
@@ -27012,6 +27424,8 @@ snapshots:
statuses@2.0.1: {}
+ statuses@2.0.2: {}
+
std-env@3.10.0: {}
stdin-discarder@0.2.2: {}
@@ -27516,6 +27930,12 @@ snapshots:
type-fest@0.8.1: {}
+ type-is@2.0.1:
+ dependencies:
+ content-type: 1.0.5
+ media-typer: 1.1.0
+ mime-types: 3.0.1
+
typed-array-buffer@1.0.3:
dependencies:
call-bound: 1.0.4
@@ -28202,6 +28622,10 @@ snapshots:
yoctocolors@2.1.1: {}
+ zod-to-json-schema@3.25.2(zod@4.1.13):
+ dependencies:
+ zod: 4.1.13
+
zod@3.24.3: {}
zod@3.25.76: {}