Merge pull request #74 from multica-ai/feat/connection-code

feat: connection code flow with cross-platform Chat
This commit is contained in:
Naiyuan Qing 2026-02-03 20:35:13 +08:00 committed by GitHub
commit 91044dea0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 412 additions and 267 deletions

View file

@ -13,13 +13,15 @@
"@hugeicons/core-free-icons": "^3.1.1",
"@hugeicons/react": "^1.1.4",
"@multica/sdk": "workspace:*",
"@multica/store": "workspace:*",
"@multica/ui": "workspace:*",
"qrcode.react": "^4.2.0",
"react": "catalog:",
"react-dom": "catalog:",
"react-router-dom": "^7.13.0",
"socket.io-client": "^4.8.3",
"uuid": "^13.0.0"
"uuid": "^13.0.0",
"zustand": "catalog:"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.18",

View file

@ -1,15 +1,5 @@
import { useNavigate } from 'react-router-dom'
import { Button } from '@multica/ui/components/ui/button'
import { Chat } from '@multica/ui/components/chat'
export default function ChatPage() {
const navigate = useNavigate()
return (
<div className="flex h-screen flex-col items-center justify-center gap-4">
<h1 className="text-2xl font-bold">Chat</h1>
<Button variant="outline" onClick={() => navigate('/')}>
Back to Home
</Button>
</div>
)
return <Chat />
}

View file

@ -1,4 +1,6 @@
import { Outlet, NavLink, useLocation } from 'react-router-dom'
import { useHubInit, useGatewayStore, useHubStore, clearConnection } from '@multica/store'
import { Toaster } from '@multica/ui/components/ui/sonner'
import { Button } from '@multica/ui/components/ui/button'
import { HugeiconsIcon } from '@hugeicons/react'
import {
@ -18,8 +20,20 @@ const tabs = [
]
export default function Layout() {
useHubInit()
const location = useLocation()
const gwState = useGatewayStore((s) => s.connectionState)
const hubId = useGatewayStore((s) => s.hubId)
const activeAgentId = useHubStore((s) => s.activeAgentId)
const isConnected = gwState === 'registered' && !!hubId && !!activeAgentId
const handleDisconnect = () => {
useGatewayStore.getState().disconnect()
useHubStore.getState().reset()
clearConnection()
}
return (
<div className="h-dvh flex flex-col bg-background">
{/* Header */}
@ -27,9 +41,21 @@ export default function Layout() {
<div className="flex items-center gap-2">
<span className="text-lg font-semibold">Multica</span>
</div>
<Button variant="ghost" size="icon">
<HugeiconsIcon icon={Settings02Icon} className="size-5" />
</Button>
<div className="flex items-center gap-1">
{isConnected && (
<Button
variant="ghost"
size="sm"
onClick={handleDisconnect}
className="text-xs text-muted-foreground"
>
Disconnect
</Button>
)}
<Button variant="ghost" size="icon">
<HugeiconsIcon icon={Settings02Icon} className="size-5" />
</Button>
</div>
</header>
{/* Tabs */}
@ -55,9 +81,10 @@ export default function Layout() {
</nav>
{/* Content */}
<main className="flex-1 overflow-auto p-4">
<main className={cn('flex-1 overflow-auto', location.pathname === '/chat' ? '' : 'p-4')}>
<Outlet />
</main>
<Toaster />
</div>
)
}

View file

@ -0,0 +1,54 @@
"use client";
import {
useHubInit,
useGatewayStore,
useHubStore,
clearConnection,
} from "@multica/store";
import { Button } from "@multica/ui/components/ui/button";
import { ThemeToggle } from "./theme-toggle";
export function AppHeader({ children }: { children: React.ReactNode }) {
useHubInit();
const gwState = useGatewayStore((s) => s.connectionState);
const hubId = useGatewayStore((s) => s.hubId);
const activeAgentId = useHubStore((s) => s.activeAgentId);
const isConnected = gwState === "registered" && !!hubId && !!activeAgentId;
const handleDisconnect = () => {
useGatewayStore.getState().disconnect();
useHubStore.getState().reset();
clearConnection();
};
return (
<>
<header>
<div className="flex items-center justify-between px-4 py-2 max-w-4xl mx-auto">
<div className="flex items-center gap-2.5">
<img src="/icon.png" alt="Multica" className="size-6 rounded-md" />
<span className="text-sm tracking-wide font-[family-name:var(--font-brand)]">
Multica
</span>
</div>
<div className="flex items-center gap-1">
<ThemeToggle />
{isConnected && (
<Button
variant="ghost"
size="sm"
onClick={handleDisconnect}
className="text-xs text-muted-foreground"
>
Disconnect
</Button>
)}
</div>
</div>
</header>
{children}
</>
);
}

View file

@ -1,22 +1,11 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono, Inter, Playfair_Display } from "next/font/google";
import { useGatewayStore } from "@multica/store";
import "@multica/ui/globals.css";
import {
SidebarProvider,
SidebarInset,
} from "@multica/ui/components/ui/sidebar";
import { AppSidebar } from "@multica/ui/components/app-sidebar";
import { ThemeProvider } from "@multica/ui/components/theme-provider";
import { AppHeader } from "./app-header";
import { Toaster } from "@multica/ui/components/ui/sonner";
import { HubSidebar } from "@multica/ui/components/hub-sidebar";
import { ServiceWorkerRegister } from "./sw-register";
const gatewayUrl = process.env.NEXT_PUBLIC_GATEWAY_URL;
if (gatewayUrl) {
useGatewayStore.getState().setGatewayUrl(gatewayUrl);
}
const inter = Inter({ subsets: ["latin"], variable: "--font-sans" });
const geistSans = Geist({
@ -56,7 +45,7 @@ export default function RootLayout({
return (
<html lang="en" className={inter.variable} suppressHydrationWarning>
<body
className={`${geistSans.variable} ${geistMono.variable} ${playfair.variable} antialiased`}
className={`${geistSans.variable} ${geistMono.variable} ${playfair.variable} antialiased h-dvh flex flex-col`}
>
<ThemeProvider
attribute="class"
@ -64,14 +53,9 @@ export default function RootLayout({
enableSystem
disableTransitionOnChange
>
<SidebarProvider>
<AppSidebar>
<HubSidebar />
</AppSidebar>
<SidebarInset>
<div className="flex h-dvh overflow-hidden">{children}</div>
</SidebarInset>
</SidebarProvider>
<AppHeader>
<div className="flex-1 overflow-hidden">{children}</div>
</AppHeader>
</ThemeProvider>
<Toaster />
<ServiceWorkerRegister />

View file

@ -0,0 +1,22 @@
"use client";
import { useTheme } from "next-themes";
import { Button } from "@multica/ui/components/ui/button";
import { HugeiconsIcon } from "@hugeicons/react";
import { Sun01Icon, Moon01Icon } from "@hugeicons/core-free-icons";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
return (
<Button
variant="ghost"
size="icon"
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="size-8 text-muted-foreground"
>
<HugeiconsIcon icon={Sun01Icon} strokeWidth={1.5} className="size-4 dark:hidden" />
<HugeiconsIcon icon={Moon01Icon} strokeWidth={1.5} className="size-4 hidden dark:block" />
</Button>
);
}

View file

@ -17,6 +17,7 @@
"@hugeicons/core-free-icons": "^3.1.1",
"@hugeicons/react": "^1.1.4",
"next": "16.1.6",
"next-themes": "^0.4.6",
"react": "catalog:",
"react-dom": "catalog:"
},

View file

@ -0,0 +1,116 @@
const STORAGE_KEY = "multica-connection"
export interface ConnectionInfo {
type: "multica-connect"
gateway: string
hubId: string
agentId: string
token: string
expires: number
}
function isConnectionInfo(obj: unknown): obj is ConnectionInfo {
if (typeof obj !== "object" || obj === null) return false
const o = obj as Record<string, unknown>
return (
o.type === "multica-connect" &&
typeof o.gateway === "string" &&
typeof o.hubId === "string" &&
typeof o.agentId === "string" &&
typeof o.token === "string" &&
typeof o.expires === "number"
)
}
// Parse multica://connect?gateway=...&hub=...&agent=...&token=...&exp=... URL format
// Uses string prefix + URLSearchParams to avoid cross-engine URL hostname differences
function parseConnectionUrl(input: string): ConnectionInfo | null {
const prefix = "multica://connect?"
if (!input.startsWith(prefix)) return null
try {
const params = new URLSearchParams(input.slice(prefix.length))
const gateway = params.get("gateway")
const hubId = params.get("hub")
const agentId = params.get("agent")
const token = params.get("token")
const exp = params.get("exp")
if (!gateway || !hubId || !agentId || !token || !exp) return null
return {
type: "multica-connect",
gateway,
hubId,
agentId,
token,
expires: Number(exp),
}
} catch {
return null
}
}
function isExpired(expires: number): boolean {
// Desktop generates expires as millisecond timestamp (Date.now() + seconds * 1000)
return Date.now() > expires
}
export function parseConnectionCode(input: string): ConnectionInfo {
const trimmed = input.trim()
// Try multica:// URL format first (desktop "Copy Link" output)
const fromUrl = parseConnectionUrl(trimmed)
if (fromUrl) {
if (isExpired(fromUrl.expires)) {
throw new Error("Connection code has expired")
}
return fromUrl
}
// Try JSON (QR code scan output)
let parsed: unknown
try {
parsed = JSON.parse(trimmed)
} catch {
// Try base64 decode then JSON
try {
parsed = JSON.parse(atob(trimmed))
} catch {
throw new Error("Invalid connection code")
}
}
if (!isConnectionInfo(parsed)) {
throw new Error("Invalid connection code format")
}
if (isExpired(parsed.expires)) {
throw new Error("Connection code has expired")
}
return parsed
}
export function saveConnection(info: ConnectionInfo): void {
localStorage.setItem(STORAGE_KEY, JSON.stringify(info))
}
export function loadConnection(): ConnectionInfo | null {
const raw = localStorage.getItem(STORAGE_KEY)
if (!raw) return null
try {
const info = JSON.parse(raw)
if (!isConnectionInfo(info)) return null
if (isExpired(info.expires)) {
localStorage.removeItem(STORAGE_KEY)
return null
}
return info
} catch {
localStorage.removeItem(STORAGE_KEY)
return null
}
}
export function clearConnection(): void {
localStorage.removeItem(STORAGE_KEY)
}

View file

@ -1,6 +1,7 @@
import { create } from "zustand"
import { GatewayClient, StreamAction, extractTextFromEvent, type ConnectionState, type DeviceInfo, type SendErrorResponse, type StreamPayload, type StreamMessageEvent } from "@multica/sdk"
import { useMessagesStore } from "./messages"
import type { ConnectionInfo } from "./connection"
const DEFAULT_GATEWAY_URL = "http://localhost:3000"
@ -8,6 +9,7 @@ interface GatewayState {
gatewayUrl: string
connectionState: ConnectionState
hubId: string | null
agentId: string | null
hubs: DeviceInfo[]
lastError: SendErrorResponse | null
}
@ -15,6 +17,7 @@ interface GatewayState {
interface GatewayActions {
setGatewayUrl: (url: string) => void
connect: (deviceId: string) => void
connectWithCode: (code: ConnectionInfo, deviceId: string) => void
disconnect: () => void
setHubId: (hubId: string) => void
listDevices: () => Promise<DeviceInfo[]>
@ -26,10 +29,56 @@ export type GatewayStore = GatewayState & GatewayActions
let client: GatewayClient | null = null
function createClient(url: string, deviceId: string, set: (s: Partial<GatewayState>) => void): GatewayClient {
return new GatewayClient({
url,
deviceId,
deviceType: "client",
})
.onStateChange((connectionState) => set({ connectionState }))
.onMessage((msg) => {
if (msg.action === StreamAction) {
const payload = msg.payload as StreamPayload
const store = useMessagesStore.getState()
const { event } = payload
switch (event.type) {
case "message_start": {
store.startStream(payload.streamId, payload.agentId)
const text = extractTextFromEvent(event as StreamMessageEvent)
if (text) store.appendStream(payload.streamId, text)
break
}
case "message_update": {
const text = extractTextFromEvent(event as StreamMessageEvent)
store.appendStream(payload.streamId, text)
break
}
case "message_end": {
const text = extractTextFromEvent(event as StreamMessageEvent)
store.endStream(payload.streamId, text)
break
}
case "tool_execution_start":
case "tool_execution_end":
break
}
return
}
const payload = msg.payload as { agentId?: string; content?: string }
if (payload?.agentId && payload?.content) {
useMessagesStore.getState().addAssistantMessage(payload.content, payload.agentId)
}
})
.onSendError((error) => set({ lastError: error }))
}
export const useGatewayStore = create<GatewayStore>()((set, get) => ({
gatewayUrl: DEFAULT_GATEWAY_URL,
connectionState: "disconnected",
hubId: null,
agentId: null,
hubs: [],
lastError: null,
@ -37,53 +86,24 @@ export const useGatewayStore = create<GatewayStore>()((set, get) => ({
connect: (deviceId) => {
if (client) return
client = createClient(get().gatewayUrl, deviceId, set)
client.connect()
},
client = new GatewayClient({
url: get().gatewayUrl,
deviceId,
deviceType: "client",
connectWithCode: (code, deviceId) => {
// Disconnect existing connection if any
if (client) {
client.disconnect()
client = null
}
set({
gatewayUrl: code.gateway,
hubId: code.hubId,
agentId: code.agentId,
})
.onStateChange((connectionState) => set({ connectionState }))
.onMessage((msg) => {
// Handle streaming messages (new protocol: payload.event is a raw AgentEvent)
if (msg.action === StreamAction) {
const payload = msg.payload as StreamPayload
const store = useMessagesStore.getState()
const { event } = payload
switch (event.type) {
case "message_start": {
store.startStream(payload.streamId, payload.agentId)
const text = extractTextFromEvent(event as StreamMessageEvent)
if (text) store.appendStream(payload.streamId, text)
break
}
case "message_update": {
const text = extractTextFromEvent(event as StreamMessageEvent)
store.appendStream(payload.streamId, text)
break
}
case "message_end": {
const text = extractTextFromEvent(event as StreamMessageEvent)
store.endStream(payload.streamId, text)
break
}
case "tool_execution_start":
case "tool_execution_end":
// TODO: surface tool execution status in UI
break
}
return
}
// Fallback: complete message handling
const payload = msg.payload as { agentId?: string; content?: string }
if (payload?.agentId && payload?.content) {
useMessagesStore.getState().addAssistantMessage(payload.content, payload.agentId)
}
})
.onSendError((error) => set({ lastError: error }))
client = createClient(code.gateway, deviceId, set)
client.connect()
},
@ -92,7 +112,7 @@ export const useGatewayStore = create<GatewayStore>()((set, get) => ({
client.disconnect()
client = null
}
set({ connectionState: "disconnected", hubId: null, hubs: [] })
set({ connectionState: "disconnected", hubId: null, agentId: null, hubs: [] })
},
setHubId: (hubId) => set({ hubId }),

View file

@ -4,37 +4,39 @@ import { useEffect } from "react"
import { useHubStore } from "./hub"
import { useDeviceId } from "./device-id"
import { useGatewayStore } from "./gateway"
import { loadConnection } from "./connection"
export function useHubInit() {
const deviceId = useDeviceId()
const gwState = useGatewayStore((s) => s.connectionState)
const hubId = useGatewayStore((s) => s.hubId)
const agentId = useGatewayStore((s) => s.agentId)
const reset = useHubStore((s) => s.reset)
const fetchHub = useHubStore((s) => s.fetchHub)
const fetchAgents = useHubStore((s) => s.fetchAgents)
const setActiveAgentId = useHubStore((s) => s.setActiveAgentId)
// Auto-connect WS when deviceId is available
// Auto-connect from saved connection code
useEffect(() => {
if (deviceId) {
useGatewayStore.getState().connect(deviceId)
return () => { useGatewayStore.getState().disconnect() }
if (!deviceId) return
const saved = loadConnection()
if (saved) {
useGatewayStore.getState().connectWithCode(saved, deviceId)
}
return () => { useGatewayStore.getState().disconnect() }
}, [deviceId])
// Once WS is registered, discover available hubs
useEffect(() => {
if (gwState === "registered") {
useGatewayStore.getState().listDevices()
}
}, [gwState])
// Once hubId is set and WS is registered, fetch hub info and agents via RPC
// Once registered with a hub, fetch hub info and agents, set active agent
useEffect(() => {
if (gwState === "registered" && hubId) {
fetchHub()
fetchAgents()
if (agentId) {
setActiveAgentId(agentId)
}
}
if (gwState === "disconnected") {
useHubStore.setState({ status: "idle", hub: null, agents: [], activeAgentId: null })
reset()
}
}, [gwState, hubId, fetchHub, fetchAgents])
}, [gwState, hubId, agentId, reset, fetchHub, fetchAgents, setActiveAgentId])
}

View file

@ -38,6 +38,7 @@ interface HubState {
}
interface HubActions {
reset: () => void
setActiveAgentId: (id: string | null) => void
fetchHub: () => Promise<void>
fetchAgents: () => Promise<void>
@ -54,6 +55,8 @@ export const useHubStore = create<HubStore>()((set, get) => ({
agents: [],
activeAgentId: null,
reset: () => set({ status: "idle", hub: null, agents: [], activeAgentId: null }),
setActiveAgentId: (id) => {
set({ activeAgentId: id })
if (id) {

View file

@ -6,3 +6,5 @@ export { useMessagesStore } from "./messages"
export type { Message, MessagesStore } from "./messages"
export { useGatewayStore } from "./gateway"
export type { GatewayStore } from "./gateway"
export { parseConnectionCode, saveConnection, loadConnection, clearConnection } from "./connection"
export type { ConnectionInfo } from "./connection"

View file

@ -1,36 +1,50 @@
"use client";
import { useRef, useState, useCallback, useMemo } from "react";
import { SidebarTrigger } from "@multica/ui/components/ui/sidebar";
import { Badge } from "@multica/ui/components/ui/badge";
import { Button } from "@multica/ui/components/ui/button";
import { Textarea } from "@multica/ui/components/ui/textarea";
import { ChatInput } from "@multica/ui/components/chat-input";
import { MemoizedMarkdown } from "@multica/ui/components/markdown";
import { StreamingMarkdown } from "@multica/ui/components/markdown/StreamingMarkdown";
import { HugeiconsIcon } from "@hugeicons/react";
import { UserIcon, Copy01Icon, CheckmarkCircle02Icon } from "@hugeicons/core-free-icons";
import { toast } from "@multica/ui/components/ui/sonner";
import { useHubStore, useDeviceId, useMessagesStore, useGatewayStore } from "@multica/store";
import {
useHubStore,
useMessagesStore,
useGatewayStore,
useDeviceId,
parseConnectionCode,
saveConnection,
} from "@multica/store";
import { useScrollFade } from "@multica/ui/hooks/use-scroll-fade";
import { useAutoScroll } from "@multica/ui/hooks/use-auto-scroll";
import { Skeleton } from "@multica/ui/components/ui/skeleton";
import { cn } from "@multica/ui/lib/utils";
const STATE_VARIANT: Record<string, "default" | "secondary" | "destructive" | "outline"> = {
registered: "default",
connected: "secondary",
connecting: "secondary",
disconnected: "destructive",
}
export function Chat() {
const deviceId = useDeviceId()
const activeAgentId = useHubStore((s) => s.activeAgentId)
const gwState = useGatewayStore((s) => s.connectionState)
const hubId = useGatewayStore((s) => s.hubId)
const messages = useMessagesStore((s) => s.messages)
const streamingIds = useMessagesStore((s) => s.streamingIds)
const filtered = useMemo(() => messages.filter(m => m.agentId === activeAgentId), [messages, activeAgentId])
const isConnected = gwState === "registered" && !!hubId && !!activeAgentId
const [codeInput, setCodeInput] = useState("")
const handleConnect = useCallback(() => {
const trimmed = codeInput.trim()
if (!trimmed || !deviceId) return
try {
const info = parseConnectionCode(trimmed)
saveConnection(info)
useGatewayStore.getState().connectWithCode(info, deviceId)
setCodeInput("")
} catch (e) {
toast.error((e as Error).message)
}
}, [codeInput, deviceId])
const handleSend = useCallback((text: string) => {
const { hubId } = useGatewayStore.getState()
const agentId = useHubStore.getState().activeAgentId
@ -39,61 +53,43 @@ export function Chat() {
useGatewayStore.getState().send(hubId, "message", { agentId, content: text })
}, [])
const canSend = gwState === "registered" && !!activeAgentId
const deviceId = useDeviceId()
const [deviceCopied, setDeviceCopied] = useState(false)
const handleCopyDevice = useCallback(async () => {
if (!deviceId) return
try {
await navigator.clipboard.writeText(deviceId)
setDeviceCopied(true)
toast.success("Device ID copied")
setTimeout(() => setDeviceCopied(false), 2000)
} catch {
toast.error("Failed to copy")
}
}, [deviceId])
const mainRef = useRef<HTMLElement>(null)
const fadeStyle = useScrollFade(mainRef)
useAutoScroll(mainRef)
return (
<div className="h-dvh flex flex-col overflow-hidden w-full">
<header className="flex items-center gap-2 p-2">
<SidebarTrigger />
{deviceId ? (
<>
<span className="text-xs text-muted-foreground font-mono">
{deviceId}
</span>
<Button
variant="ghost"
size="icon-xs"
onClick={handleCopyDevice}
aria-label="Copy device ID"
>
<HugeiconsIcon
icon={deviceCopied ? CheckmarkCircle02Icon : Copy01Icon}
strokeWidth={2}
className={cn("size-3", deviceCopied && "text-green-500")}
/>
</Button>
</>
) : (
<Skeleton className="h-4 w-56" />
)}
<Badge variant={STATE_VARIANT[gwState] ?? "outline"} className="text-xs">
{gwState}
</Badge>
</header>
<div className="h-full flex flex-col overflow-hidden w-full">
<main ref={mainRef} className="flex-1 overflow-y-auto min-h-0" style={fadeStyle}>
{!activeAgentId ? (
<div className="flex flex-col items-center justify-center h-full gap-3 text-muted-foreground">
<HugeiconsIcon icon={UserIcon} strokeWidth={1.5} className="size-10 opacity-30" />
<span className="text-sm">Select an agent to start chatting</span>
{!isConnected ? (
<div className="flex flex-col items-center justify-center h-full gap-4 px-4">
<div className="text-center space-y-1">
<p className="text-sm text-muted-foreground">Paste a connection code to start</p>
{(gwState === "connecting" || gwState === "connected") && (
<p className="text-xs text-muted-foreground/60 animate-pulse">Connecting...</p>
)}
</div>
<div className="w-full max-w-sm space-y-3">
<Textarea
value={codeInput}
onChange={(e) => setCodeInput(e.target.value)}
placeholder="Paste connection code here..."
className="text-xs font-mono min-h-[100px] resize-none"
onKeyDown={(e) => {
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
e.preventDefault()
handleConnect()
}
}}
/>
<Button
size="sm"
onClick={handleConnect}
disabled={!codeInput.trim() || gwState === "connecting"}
className="w-full text-xs"
>
Connect
</Button>
</div>
</div>
) : filtered.length === 0 ? (
<div className="flex items-center justify-center h-full text-muted-foreground text-sm">
@ -131,11 +127,12 @@ export function Chat() {
)}
</main>
{/* Footer */}
<footer className="w-full p-2 pt-1 max-w-4xl mx-auto">
<ChatInput
onSubmit={handleSend}
disabled={!canSend}
placeholder={!activeAgentId ? "Select an agent first..." : "Type a message..."}
disabled={!isConnected}
placeholder={!isConnected ? "Connect first..." : "Type a message..."}
/>
</footer>
</div>

137
pnpm-lock.yaml generated
View file

@ -34,13 +34,13 @@ importers:
dependencies:
'@mariozechner/pi-agent-core':
specifier: ^0.50.3
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
'@mariozechner/pi-ai':
specifier: ^0.50.3
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
'@mariozechner/pi-coding-agent':
specifier: ^0.50.3
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
'@mozilla/readability':
specifier: ^0.6.0
version: 0.6.0
@ -156,6 +156,9 @@ importers:
'@multica/sdk':
specifier: workspace:*
version: link:../../packages/sdk
'@multica/store':
specifier: workspace:*
version: link:../../packages/store
'@multica/ui':
specifier: workspace:*
version: link:../../packages/ui
@ -177,6 +180,9 @@ importers:
uuid:
specifier: ^13.0.0
version: 13.0.0
zustand:
specifier: 'catalog:'
version: 5.0.10(@types/react@19.1.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))
devDependencies:
'@tailwindcss/vite':
specifier: ^4.1.18
@ -279,7 +285,7 @@ importers:
version: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
expo-router:
specifier: ~6.0.23
version: 6.0.23(52130e04a12e6efd1d6e48b3f2ad01c3)
version: 6.0.23(w7u2e3gmpia3npio76ytuzityu)
expo-splash-screen:
specifier: ~31.0.13
version: 31.0.13(expo@54.0.33)
@ -377,6 +383,9 @@ importers:
next:
specifier: 16.1.6
version: 16.1.6(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react:
specifier: 'catalog:'
version: 19.2.3
@ -1862,105 +1871,89 @@ packages:
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
@ -2125,28 +2118,24 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@mariozechner/clipboard-linux-riscv64-gnu@0.3.0':
resolution: {integrity: sha512-4BC08CIaOXSSAGRZLEjqJmQfioED8ohAzwt0k2amZPEbH96YKoBNorq5EdwPf5VT+odS0DeyCwhwtxokRLZIvQ==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@mariozechner/clipboard-linux-x64-gnu@0.3.0':
resolution: {integrity: sha512-GpNY5Y9nOzr0Vt0Qi5U88qwe6piiIHk44kSMexl8ns90LluN5UTNYmyfi7Xq3/lmPZCpnB2xvBTYbsXCxnopIA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@mariozechner/clipboard-linux-x64-musl@0.3.0':
resolution: {integrity: sha512-+PnR48/x9GMY5Kh8BLjzHMx6trOegMtxAuqTM9X/bhV3QuW6sLLd7nojDHSGj/ZueK6i0tcQxvOrgNLozVtNDA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@mariozechner/clipboard-win32-arm64-msvc@0.3.0':
resolution: {integrity: sha512-+dy2vZ1Ph4EYj0cotB+bVUVk/uKl2bh9LOp/zlnFqoCCYDN6sm+L0VyIOPPo3hjoEVdGpHe1MUxp3qG/OLwXgg==}
@ -2308,28 +2297,24 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@next/swc-linux-arm64-musl@16.1.6':
resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@next/swc-linux-x64-gnu@16.1.6':
resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@next/swc-linux-x64-musl@16.1.6':
resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@next/swc-win32-arm64-msvc@16.1.6':
resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==}
@ -2790,79 +2775,66 @@ packages:
resolution: {integrity: sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.57.0':
resolution: {integrity: sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.57.0':
resolution: {integrity: sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.57.0':
resolution: {integrity: sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.57.0':
resolution: {integrity: sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.57.0':
resolution: {integrity: sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.57.0':
resolution: {integrity: sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.57.0':
resolution: {integrity: sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.57.0':
resolution: {integrity: sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.57.0':
resolution: {integrity: sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.57.0':
resolution: {integrity: sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.57.0':
resolution: {integrity: sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.57.0':
resolution: {integrity: sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.57.0':
resolution: {integrity: sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==}
@ -3187,28 +3159,24 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
@ -3557,49 +3525,41 @@ packages:
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1':
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1':
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1':
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1':
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@ -6285,56 +6245,48 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-gnu@1.30.2:
resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.27.0:
resolution: {integrity: sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.27.0:
resolution: {integrity: sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.27.0:
resolution: {integrity: sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.27.0:
resolution: {integrity: sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==}
@ -9128,11 +9080,11 @@ snapshots:
package-manager-detector: 1.6.0
tinyexec: 1.0.2
'@anthropic-ai/sdk@0.71.2(zod@4.3.6)':
'@anthropic-ai/sdk@0.71.2(zod@3.25.76)':
dependencies:
json-schema-to-ts: 3.1.1
optionalDependencies:
zod: 4.3.6
zod: 3.25.76
'@aws-crypto/crc32@5.2.0':
dependencies:
@ -10656,7 +10608,7 @@ snapshots:
wrap-ansi: 7.0.0
ws: 8.18.3
optionalDependencies:
expo-router: 6.0.23(52130e04a12e6efd1d6e48b3f2ad01c3)
expo-router: 6.0.23(w7u2e3gmpia3npio76ytuzityu)
react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)
transitivePeerDependencies:
- bufferutil
@ -10905,12 +10857,12 @@ snapshots:
'@floating-ui/utils@0.2.10': {}
'@google/genai@1.34.0(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))':
'@google/genai@1.34.0(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))':
dependencies:
google-auth-library: 10.5.0
ws: 8.18.3
optionalDependencies:
'@modelcontextprotocol/sdk': 1.25.3(hono@4.11.7)(zod@4.3.6)
'@modelcontextprotocol/sdk': 1.25.3(hono@4.11.7)(zod@3.25.76)
transitivePeerDependencies:
- bufferutil
- supports-color
@ -11245,9 +11197,9 @@ snapshots:
std-env: 3.10.0
yoctocolors: 2.1.2
'@mariozechner/pi-agent-core@0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)':
'@mariozechner/pi-agent-core@0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)':
dependencies:
'@mariozechner/pi-ai': 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
'@mariozechner/pi-ai': 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
'@mariozechner/pi-tui': 0.50.3
transitivePeerDependencies:
- '@modelcontextprotocol/sdk'
@ -11258,21 +11210,21 @@ snapshots:
- ws
- zod
'@mariozechner/pi-ai@0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)':
'@mariozechner/pi-ai@0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)':
dependencies:
'@anthropic-ai/sdk': 0.71.2(zod@4.3.6)
'@anthropic-ai/sdk': 0.71.2(zod@3.25.76)
'@aws-sdk/client-bedrock-runtime': 3.978.0
'@google/genai': 1.34.0(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))
'@google/genai': 1.34.0(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))
'@mistralai/mistralai': 1.10.0
'@sinclair/typebox': 0.34.48
ajv: 8.17.1
ajv-formats: 3.0.1(ajv@8.17.1)
chalk: 5.6.2
openai: 6.10.0(ws@8.18.3)(zod@4.3.6)
openai: 6.10.0(ws@8.18.3)(zod@3.25.76)
partial-json: 0.1.7
proxy-agent: 6.5.0
undici: 7.19.2
zod-to-json-schema: 3.25.1(zod@4.3.6)
zod-to-json-schema: 3.25.1(zod@3.25.76)
transitivePeerDependencies:
- '@modelcontextprotocol/sdk'
- aws-crt
@ -11282,12 +11234,12 @@ snapshots:
- ws
- zod
'@mariozechner/pi-coding-agent@0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)':
'@mariozechner/pi-coding-agent@0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)':
dependencies:
'@mariozechner/clipboard': 0.3.0
'@mariozechner/jiti': 2.6.5
'@mariozechner/pi-agent-core': 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
'@mariozechner/pi-ai': 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
'@mariozechner/pi-agent-core': 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
'@mariozechner/pi-ai': 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
'@mariozechner/pi-tui': 0.50.3
'@silvia-odwyer/photon-node': 0.3.4
chalk: 5.6.2
@ -11345,29 +11297,6 @@ snapshots:
- hono
- supports-color
'@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6)':
dependencies:
'@hono/node-server': 1.19.9(hono@4.11.7)
ajv: 8.17.1
ajv-formats: 3.0.1(ajv@8.17.1)
content-type: 1.0.5
cors: 2.8.6
cross-spawn: 7.0.6
eventsource: 3.0.7
eventsource-parser: 3.0.6
express: 5.2.1
express-rate-limit: 7.5.1(express@5.2.1)
jose: 6.1.3
json-schema-typed: 8.0.2
pkce-challenge: 5.0.1
raw-body: 3.0.2
zod: 4.3.6
zod-to-json-schema: 3.25.1(zod@4.3.6)
transitivePeerDependencies:
- hono
- supports-color
optional: true
'@mozilla/readability@0.6.0': {}
'@mswjs/interceptors@0.40.0':
@ -14793,7 +14722,7 @@ snapshots:
react: 19.1.0
react-native: 0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0)
expo-router@6.0.23(52130e04a12e6efd1d6e48b3f2ad01c3):
expo-router@6.0.23(w7u2e3gmpia3npio76ytuzityu):
dependencies:
'@expo/metro-runtime': 6.1.2(expo@54.0.33)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.6)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
'@expo/schema-utils': 0.1.8
@ -17180,10 +17109,10 @@ snapshots:
is-docker: 2.2.1
is-wsl: 2.2.0
openai@6.10.0(ws@8.18.3)(zod@4.3.6):
openai@6.10.0(ws@8.18.3)(zod@3.25.76):
optionalDependencies:
ws: 8.18.3
zod: 4.3.6
zod: 3.25.76
optionator@0.9.4:
dependencies:
@ -19471,10 +19400,6 @@ snapshots:
dependencies:
zod: 3.25.76
zod-to-json-schema@3.25.1(zod@4.3.6):
dependencies:
zod: 4.3.6
zod-validation-error@4.0.2(zod@4.3.6):
dependencies:
zod: 4.3.6