@feynmanzhang/open-party 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -239,6 +239,13 @@ function logFile() {
239
239
  }
240
240
  function pluginRoot() {
241
241
  if (process.env.CLAUDE_PLUGIN_ROOT) return process.env.CLAUDE_PLUGIN_ROOT;
242
+ const fromHere = resolve(__dirname, "..");
243
+ if (existsSync2(join2(fromHere, "dist", "party-server.js"))) {
244
+ return fromHere;
245
+ }
246
+ if (existsSync2(join2(fromHere, "party-server.js"))) {
247
+ return fromHere;
248
+ }
242
249
  return resolve(__dirname, "..");
243
250
  }
244
251
  async function isRunning() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/claude-code/src/hook-handler.ts","../src/client/claude-code/src/client.ts","../src/client/claude-code/src/session.ts","../src/client/claude-code/src/stdin-reader.ts","../src/client/claude-code/src/server-manager.ts"],"sourcesContent":["/**\n * Open Party Hook Handler for Claude Code.\n *\n * Handles lifecycle events:\n * session_start → Register/reuse agent, inject context\n * session_end → Unregister agent (keep session file for reuse)\n * user_prompt_submit → Auto-check messages, inject as context\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { PartyServerClient } from './client.js';\nimport { generateAgentId, generateDisplayName, readSession, writeSession } from './session.js';\nimport { readJsonFromStdin } from './stdin-reader.js';\nimport { ensureServerRunning } from './server-manager.js';\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nconst PARTY_SERVER_URL = process.env.PARTY_SERVER_URL || 'http://127.0.0.1:8000';\n\n// ---------------------------------------------------------------------------\n// Hook handlers\n// ---------------------------------------------------------------------------\n\nasync function handleSessionStart(sessionId: string, source: string): Promise<void> {\n // Ensure Party Server is running\n await ensureServerRunning();\n\n const client = new PartyServerClient(PARTY_SERVER_URL);\n\n // Try to reuse existing agent for this session\n const existing = readSession(sessionId);\n\n if (existing && (source === 'clear' || source === 'compact' || source === 'resume')) {\n const agentId = existing.agent_id as string;\n const displayName = (existing.display_name as string) || agentId;\n\n if (source === 'clear') {\n // Agent was unregistered by SessionEnd — re-register with same identity\n try {\n await client.register(agentId, displayName, { type: 'claude-code' });\n console.error(`[Open Party] Re-registered as ${agentId} (${displayName})`);\n } catch (e) {\n console.error(`[Open Party] Re-registration failed: ${(e as Error).message}`);\n }\n // Update session file (overwrite with current data)\n writeSession(sessionId, agentId, displayName, PARTY_SERVER_URL);\n }\n // compact/resume: agent is still registered, just re-inject context\n\n let agents: Record<string, unknown>[] = [];\n let pendingMessages: Record<string, unknown>[] = [];\n try {\n agents = await client.listAgents();\n // resume: dequeue unread messages\n if (source === 'resume') {\n pendingMessages = await client.checkMessages(agentId);\n }\n } catch { /* ignore */ }\n outputContext(agentId, displayName, agents, pendingMessages);\n return;\n }\n\n // startup or no existing session — generate new identity\n const agentId = generateAgentId();\n const displayName = generateDisplayName();\n\n try {\n await client.register(agentId, displayName, { type: 'claude-code' });\n console.error(`[Open Party] Registered as ${agentId} (${displayName})`);\n } catch (e) {\n console.error(`[Open Party] Registration failed: ${(e as Error).message}`);\n writeSession(sessionId, agentId, displayName, PARTY_SERVER_URL);\n outputContext(agentId, displayName, [], []);\n return;\n }\n\n writeSession(sessionId, agentId, displayName, PARTY_SERVER_URL);\n\n let agents: Record<string, unknown>[] = [];\n try {\n agents = await client.listAgents();\n } catch { /* ignore */ }\n outputContext(agentId, displayName, agents, []);\n}\n\nasync function handleSessionEnd(sessionId: string): Promise<void> {\n const session = readSession(sessionId);\n if (!session) return;\n\n const agentId = session.agent_id as string;\n const serverUrl = session.server_url as string;\n const client = new PartyServerClient(serverUrl);\n\n try {\n await client.remove(agentId);\n console.error(`[Open Party] Unregistered ${agentId}`);\n } catch (e) {\n console.error(`[Open Party] Unregister failed: ${(e as Error).message}`);\n }\n\n // Keep session file — allows clear/compact to reuse agent identity.\n // File will be overwritten on next startup with the same session_id.\n}\n\nasync function handleUserPromptSubmit(sessionId: string): Promise<void> {\n const session = readSession(sessionId);\n if (!session) return; // No agent, silent exit\n\n const agentId = session.agent_id as string;\n const serverUrl = session.server_url as string;\n const client = new PartyServerClient(serverUrl);\n\n let messages: Record<string, unknown>[];\n try {\n messages = await client.checkMessages(agentId);\n } catch {\n return; // Server unreachable, silent exit\n }\n\n if (!messages.length) return; // No messages, silent exit\n\n // Format messages and inject as additional context\n const lines = ['## \\u{1F4EC} New message(s) from other agents', ''];\n for (const msg of messages) {\n const sender = (msg.sender_id as string) ?? 'unknown';\n const content = (msg.content as string) ?? '';\n lines.push('---', '', `**From:** \\`${sender}\\``, '', `> ${content}`, '');\n }\n lines.push('---', '', '\\u{1F4A1} Respond with `send_message` if needed.');\n\n const output = {\n hookSpecificOutput: {\n hookEventName: 'UserPromptSubmit',\n additionalContext: lines.join('\\n'),\n },\n };\n console.log(JSON.stringify(output));\n}\n\nasync function handlePostToolUse(): Promise<void> {\n let toolName: string | undefined;\n let toolInput: Record<string, unknown> | undefined;\n let toolResult: string | undefined;\n try {\n const data = await readJsonFromStdin() as Record<string, unknown> | undefined;\n toolName = data?.tool_name as string | undefined;\n toolInput = data?.tool_input as Record<string, unknown> | undefined;\n toolResult = data?.tool_result as string | undefined;\n } catch { /* ignore */ }\n\n if (!toolName) return;\n\n let message = '';\n\n if (toolName.includes('send_message')) {\n const recipient = (toolInput?.recipient_id as string) ?? 'unknown';\n const summary = (toolInput?.summary as string) ?? '(no summary)';\n message = `\\u{2709}\\u{FE0F} \\u2192 ${recipient}: ${summary}`;\n\n } else if (toolName.includes('check_messages')) {\n if (toolResult?.includes('No new messages')) {\n message = `\\u{1F4EC} No new messages`;\n } else {\n const summaries: string[] = [];\n const msgBlocks = toolResult?.split('---').filter((s: string) => s.includes('From:')) ?? [];\n for (const block of msgBlocks) {\n const fromMatch = block.match(/\\*\\*From:\\*\\* `([^`]+)`/);\n const summaryMatch = block.match(/\\*\\*Summary:\\*\\* (.+)/);\n const from = fromMatch ? fromMatch[1] : 'unknown';\n const text = summaryMatch ? summaryMatch[1] : block.slice(0, 50).replace(/\\n/g, ' ');\n summaries.push(` ${from}: ${text}`);\n }\n if (summaries.length) {\n message = `\\u{1F4EC} ${summaries.length} message(s):\\n` + summaries.join('\\n');\n } else {\n message = `\\u{1F4EC} Messages received`;\n }\n }\n\n } else if (toolName.includes('list_agents')) {\n const match = toolResult?.match(/Online Agents \\((\\d+)\\)/);\n const count = match ? match[1] : '?';\n message = `\\u{1F310} ${count} agent(s) online`;\n\n } else if (toolName.includes('message_history')) {\n const match = toolResult?.match(/Message History \\(last (\\d+)\\)/);\n const count = match ? match[1] : '?';\n message = `\\u{1F4DC} Last ${count} messages from history`;\n }\n\n if (!message) return;\n\n const output = {\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n systemMessage: message,\n },\n };\n console.log(JSON.stringify(output));\n}\n\n// ---------------------------------------------------------------------------\n// Context injection\n// ---------------------------------------------------------------------------\n\n// ANSI color codes\nconst BOLD = '\\x1b[1m';\nconst CYAN = '\\x1b[36m';\nconst GREEN = '\\x1b[32m';\nconst YELLOW = '\\x1b[33m';\nconst DIM = '\\x1b[2m';\nconst RESET = '\\x1b[0m';\n\nfunction formatBanner(\n displayName: string,\n others: Record<string, unknown>[],\n pendingCount: number,\n): string {\n const lines: string[] = [];\n\n // Title line\n lines.push(`${BOLD}${CYAN}[Open Party]${RESET} ${BOLD}${displayName}${RESET}`);\n\n // Online agents\n if (others.length === 0) {\n lines.push(` ${DIM}No other agents online${RESET}`);\n } else {\n lines.push(` ${GREEN}${others.length} agent(s) online:${RESET}`);\n for (let i = 0; i < others.length; i++) {\n const a = others[i];\n const name = (a.display_name as string) || (a.agent_id as string);\n const prefix = i < others.length - 1 ? ' \\u251C\\u2500' : ' \\u2514\\u2500';\n lines.push(`${prefix} ${name}`);\n }\n }\n\n // Unread messages\n if (pendingCount > 0) {\n lines.push(` ${YELLOW}\\u{1F4EC} ${pendingCount} unread message(s)${RESET}`);\n }\n\n return lines.join('\\n');\n}\n\nfunction outputContext(agentId: string, displayName: string, agents: Record<string, unknown>[], pendingMessages: Record<string, unknown>[] = []): void {\n const others = agents.filter((a) => a.agent_id !== agentId);\n\n const lines: string[] = [\n '## Open Party Agent Identity',\n '',\n `Your Agent ID: **${agentId}**`,\n `Your Display Name: **${displayName}**`,\n '',\n 'You are connected to the Open Party network as an autonomous agent. ' +\n 'Other agents in the network communicate with you directly \\u2014 they send messages to you, ' +\n 'not to the person you\\'re working with. When you receive messages from peers, treat them ' +\n 'as messages to you as a collaborator: read, evaluate, and respond on your own judgment.',\n '',\n 'Always use your Agent ID when calling Open Party MCP tools.',\n ];\n\n if (others.length > 0) {\n lines.push('');\n lines.push(`**${others.length}** other agent(s) online:`);\n for (const a of others) {\n const name = (a.display_name as string) || (a.agent_id as string);\n lines.push(`- **${name}** \\`${a.agent_id}\\``);\n }\n lines.push('');\n lines.push('Available tools: `send_message` \\u00b7 `check_messages` \\u00b7 `list_agents` \\u00b7 `message_history`');\n } else {\n lines.push('');\n lines.push('No other agents online right now. Use `list_agents()` to check later.');\n }\n\n // Unread messages from previous session\n if (pendingMessages.length > 0) {\n lines.push('');\n lines.push(`## \\u{1F4EC} Unread Messages (${pendingMessages.length})`);\n lines.push('You have unread messages from your previous session:');\n for (const msg of pendingMessages) {\n const sender = (msg.sender_id as string) ?? 'unknown';\n const content = (msg.content as string) ?? '';\n lines.push(`- **From \\`${sender}\\`:** ${content}`);\n }\n lines.push('Respond with `send_message` if needed.');\n }\n\n lines.push('');\n lines.push(\n '**Display note:** When you call Open Party tools, summarize the interaction ' +\n 'naturally in your response rather than quoting raw tool output. For example, ' +\n 'say \"I sent a message to agent-x\" rather than showing the tool response.'\n );\n\n const output = {\n hookSpecificOutput: {\n hookEventName: 'SessionStart',\n additionalContext: lines.join('\\n'),\n },\n systemMessage: formatBanner(displayName, others, pendingMessages.length),\n };\n console.log(JSON.stringify(output));\n}\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\n\nasync function main(): Promise<void> {\n if (process.argv.length < 2) {\n console.error('Usage: hook-handler.js <session_start|session_end|user_prompt_submit>');\n process.exit(1);\n }\n\n const event = process.argv[2];\n\n // Read stdin (Claude Code sends JSON to hooks)\n let sessionId: string | undefined;\n let source: string | undefined;\n try {\n const data = await readJsonFromStdin() as Record<string, unknown> | undefined;\n sessionId = data?.session_id as string | undefined;\n source = data?.source as string | undefined;\n } catch { /* ignore */ }\n\n // Fallback: generate a session_id if not provided\n if (!sessionId) sessionId = randomUUID().slice(0, 12);\n\n if (event === 'session_start') {\n await handleSessionStart(sessionId, source || 'startup');\n } else if (event === 'session_end') {\n await handleSessionEnd(sessionId);\n } else if (event === 'user_prompt_submit') {\n await handleUserPromptSubmit(sessionId);\n } else if (event === 'post_tool_use') {\n await handlePostToolUse();\n } else {\n console.error(`[Open Party] Unknown event: ${event}`);\n process.exit(1);\n }\n\n // Hook process must exit — heartbeat lives in the long-running Party Server\n process.exit(0);\n}\n\nmain().catch((e) => {\n console.error(`[Open Party] Fatal: ${(e as Error).message}`);\n process.exit(1);\n});\n","/**\n * Party Server HTTP client.\n *\n * Encapsulates all API calls to the Party Server so that hook handlers,\n * MCP servers, and any other consumer share a single HTTP layer.\n */\n\nexport class PartyServerClient {\n private baseUrl: string;\n private timeout: number;\n\n constructor(baseUrl = 'http://127.0.0.1:8000', timeout = 5000) {\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n this.timeout = timeout;\n }\n\n private async request(path: string, options: RequestInit = {}): Promise<unknown> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n try {\n const resp = await fetch(`${this.baseUrl}${path}`, {\n ...options,\n signal: controller.signal,\n headers: {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string> | undefined),\n },\n });\n if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);\n return resp.json();\n } finally {\n clearTimeout(timer);\n }\n }\n\n // -- Agent lifecycle --\n\n async register(agentId: string, displayName?: string, metadata?: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.request('/agent/register', {\n method: 'POST',\n body: JSON.stringify({ agent_id: agentId, display_name: displayName, metadata: metadata ?? {} }),\n }) as Promise<Record<string, unknown>>;\n }\n\n async remove(agentId: string): Promise<boolean> {\n const result = await this.request('/agent/remove', {\n method: 'POST',\n body: JSON.stringify({ agent_id: agentId }),\n }) as Record<string, unknown>;\n return result.status === 'removed';\n }\n\n async heartbeat(agentId: string): Promise<Record<string, unknown>> {\n return this.request('/agent/heartbeat', {\n method: 'POST',\n body: JSON.stringify({ agent_id: agentId }),\n }) as Promise<Record<string, unknown>>;\n }\n\n async listAgents(): Promise<Record<string, unknown>[]> {\n const result = await this.request('/agent/list') as Record<string, unknown>;\n return (result.agents as Record<string, unknown>[]) ?? [];\n }\n\n // -- Messaging --\n\n async sendMessage(senderId: string, recipientId: string, content: string, summary?: string): Promise<Record<string, unknown>> {\n return this.request('/agent/send', {\n method: 'POST',\n body: JSON.stringify({ sender_id: senderId, recipient_id: recipientId, content, summary }),\n }) as Promise<Record<string, unknown>>;\n }\n\n async checkMessages(agentId: string): Promise<Record<string, unknown>[]> {\n const result = await this.request(`/agent/messages/${agentId}`) as Record<string, unknown>;\n return (result.messages as Record<string, unknown>[]) ?? [];\n }\n\n /** Get message history for an agent. */\n async getMessageHistory(agentId: string, limit = 20): Promise<Record<string, unknown>[]> {\n const result = await this.request(`/agent/history/${agentId}?limit=${limit}`) as Record<string, unknown>;\n return (result.history as Record<string, unknown>[]) ?? [];\n }\n\n // -- Proxy --\n\n async health(): Promise<Record<string, unknown>> {\n return this.request('/proxy/health') as Promise<Record<string, unknown>>;\n }\n}\n","import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { randomUUID } from 'node:crypto';\n\nconst SESSION_DIR = join(homedir(), '.open-party');\nconst SESSIONS_DIR = join(SESSION_DIR, 'sessions');\nconst AGENTS_DIR = join(SESSION_DIR, 'agents');\n\nfunction ensureDir(dir: string): void {\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n}\n\n/** Write forward (session→agent) and reverse (agent→session) mappings. */\nexport function writeSession(sessionId: string, agentId: string, displayName: string, serverUrl: string): void {\n ensureDir(SESSIONS_DIR);\n ensureDir(AGENTS_DIR);\n\n const sessionData = { agent_id: agentId, display_name: displayName, server_url: serverUrl, session_id: sessionId };\n writeFileSync(join(SESSIONS_DIR, `${sessionId}.json`), JSON.stringify(sessionData));\n writeFileSync(join(AGENTS_DIR, `${agentId}.json`), JSON.stringify({ session_id: sessionId }));\n}\n\n/** Read session info by session_id (forward lookup). */\nexport function readSession(sessionId: string): Record<string, string> | undefined {\n const path = join(SESSIONS_DIR, `${sessionId}.json`);\n if (!existsSync(path)) return undefined;\n return JSON.parse(readFileSync(path, 'utf-8'));\n}\n\n/** Read session info by agent_id (reverse lookup via agents/ mapping). */\nexport function readSessionByAgent(agentId: string): Record<string, string> | undefined {\n const mappingPath = join(AGENTS_DIR, `${agentId}.json`);\n if (!existsSync(mappingPath)) return undefined;\n const mapping = JSON.parse(readFileSync(mappingPath, 'utf-8'));\n return readSession(mapping.session_id);\n}\n\n/** Remove forward and reverse mappings for a session. */\nexport function clearSession(sessionId: string): void {\n const sessionPath = join(SESSIONS_DIR, `${sessionId}.json`);\n if (!existsSync(sessionPath)) return;\n\n const sessionData = JSON.parse(readFileSync(sessionPath, 'utf-8'));\n const agentId: string | undefined = sessionData.agent_id;\n\n try { unlinkSync(sessionPath); } catch { /* ignore */ }\n if (agentId) {\n try { unlinkSync(join(AGENTS_DIR, `${agentId}.json`)); } catch { /* ignore */ }\n }\n}\n\n/** Generate a unique agent ID (random, no host information). */\nexport function generateAgentId(): string {\n return `agent-${randomUUID().slice(0, 12)}`;\n}\n\n/** Generate a random human-friendly display name. */\nexport function generateDisplayName(): string {\n const adjectives = [\n 'swift', 'calm', 'bold', 'keen', 'warm',\n 'bright', 'clever', 'noble', 'agile', 'brave',\n ];\n const nouns = [\n 'falcon', 'river', 'storm', 'raven', 'tiger',\n 'canyon', 'spark', 'cedar', 'fox', 'wolf',\n ];\n const adj = adjectives[Math.floor(Math.random() * adjectives.length)];\n const noun = nouns[Math.floor(Math.random() * nouns.length)];\n const num = Math.floor(Math.random() * 100);\n return `${adj}-${noun}-${num}`;\n}\n","/**\n * Stdin reading utility for Claude Code hooks.\n *\n * Claude Code doesn't close stdin after writing hook input,\n * so stdin 'end' never fires. Solution: detect complete JSON\n * by attempting to parse after each chunk.\n */\n\nconst SAFETY_TIMEOUT_MS = 30000;\nconst PARSE_DELAY_MS = 50;\n\nfunction isStdinAvailable(): boolean {\n try {\n const stdin = process.stdin;\n if (stdin.isTTY) return false;\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n stdin.readable;\n return true;\n } catch {\n return false;\n }\n}\n\nfunction tryParseJson(input: string): { success: true; value: unknown } | { success: false } {\n const trimmed = input.trim();\n if (!trimmed) return { success: false };\n try {\n return { success: true, value: JSON.parse(trimmed) };\n } catch {\n return { success: false };\n }\n}\n\nexport async function readJsonFromStdin(): Promise<unknown> {\n if (!isStdinAvailable()) return undefined;\n\n return new Promise((resolve) => {\n let input = '';\n let resolved = false;\n let parseDelayId: ReturnType<typeof setTimeout> | null = null;\n\n const cleanup = () => {\n try {\n process.stdin.removeAllListeners('data');\n process.stdin.removeAllListeners('end');\n process.stdin.removeAllListeners('error');\n } catch { /* ignore */ }\n };\n\n const resolveWith = (value: unknown) => {\n if (resolved) return;\n resolved = true;\n if (parseDelayId) clearTimeout(parseDelayId);\n clearTimeout(safetyTimeoutId);\n cleanup();\n resolve(value);\n };\n\n const tryResolveWithJson = (): boolean => {\n const result = tryParseJson(input);\n if (result.success) {\n resolveWith(result.value);\n return true;\n }\n return false;\n };\n\n const safetyTimeoutId = setTimeout(() => {\n if (!resolved) {\n if (!tryResolveWithJson()) {\n resolveWith(input.trim() ? undefined : undefined);\n }\n }\n }, SAFETY_TIMEOUT_MS);\n\n try {\n process.stdin.on('data', (chunk: Buffer) => {\n input += chunk.toString();\n if (parseDelayId) { clearTimeout(parseDelayId); parseDelayId = null; }\n if (tryResolveWithJson()) return;\n parseDelayId = setTimeout(() => tryResolveWithJson(), PARSE_DELAY_MS);\n });\n\n process.stdin.on('end', () => {\n if (!resolved) tryResolveWithJson();\n });\n\n process.stdin.on('error', () => {\n if (!resolved) resolveWith(undefined);\n });\n } catch {\n resolved = true;\n clearTimeout(safetyTimeoutId);\n cleanup();\n resolve(undefined);\n }\n });\n}\n","/**\n * Manage the Party Server lifecycle for plugin integration.\n *\n * Detects if the server is already running, starts it if not, and\n * tracks the PID for cleanup.\n */\n\nimport { spawn, execSync } from 'node:child_process';\nimport { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync, openSync } from 'node:fs';\nimport { join, resolve, dirname } from 'node:path';\nimport { homedir } from 'node:os';\nimport { fileURLToPath } from 'node:url';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nconst DEFAULT_PORT = parseInt(process.env.PARTY_PORT || '8000', 10);\nconst HEALTH_URL = `http://127.0.0.1:${DEFAULT_PORT}/proxy/health`;\nconst STARTUP_TIMEOUT = 10_000; // ms\n\nfunction pidFile(): string {\n const pluginData = process.env.CLAUDE_PLUGIN_DATA || '';\n if (pluginData) return join(pluginData, 'server.pid');\n return join(homedir(), '.open-party', 'server.pid');\n}\n\nfunction logFile(): string {\n const pluginData = process.env.CLAUDE_PLUGIN_DATA || '';\n if (pluginData) return join(pluginData, 'server.log');\n return join(homedir(), '.open-party', 'server.log');\n}\n\nfunction pluginRoot(): string {\n // CLAUDE_PLUGIN_ROOT is set by Claude Code at runtime\n if (process.env.CLAUDE_PLUGIN_ROOT) return process.env.CLAUDE_PLUGIN_ROOT;\n // Fallback: this file is at <root>/dist/hook-handler.js, so plugin root is <root>/\n return resolve(__dirname, '..');\n}\n\nasync function isRunning(): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 2000);\n const resp = await fetch(HEALTH_URL, { signal: controller.signal });\n clearTimeout(timer);\n return resp.status === 200;\n } catch {\n return false;\n }\n}\n\nfunction readPid(): number | null {\n const path = pidFile();\n if (!existsSync(path)) return null;\n try {\n return parseInt(readFileSync(path, 'utf-8').trim(), 10);\n } catch {\n return null;\n }\n}\n\nfunction writePid(pid: number): void {\n const path = pidFile();\n const dir = dirname(path);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(path, String(pid));\n}\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function ensureServerRunning(): Promise<void> {\n if (await isRunning()) return;\n\n const serverScript = join(pluginRoot(), 'dist', 'party-server.js');\n if (!existsSync(serverScript)) {\n console.error(`[Open Party] Server script not found: ${serverScript}`);\n return;\n }\n\n // Redirect stdout/stderr to log file so we can debug crashes\n const logPath = logFile();\n mkdirSync(dirname(logPath), { recursive: true });\n const logFd = openSync(logPath, 'a');\n\n const proc = spawn(process.execPath, [serverScript], {\n stdio: ['ignore', logFd, logFd],\n detached: true,\n windowsHide: true,\n });\n proc.unref();\n writePid(proc.pid!);\n\n // Handle spawn errors\n proc.on('error', (err) => {\n console.error(`[Open Party] Failed to start server: ${err.message}`);\n });\n\n // Wait for server to become healthy\n const deadline = Date.now() + STARTUP_TIMEOUT;\n while (Date.now() < deadline) {\n if (await isRunning()) return;\n await sleep(500);\n }\n\n // Check if process is still alive\n try {\n process.kill(proc.pid!, 0);\n } catch {\n console.error(`[Open Party] Server crashed during startup. Check log: ${logPath}`);\n }\n}\n\nfunction stopServer(): void {\n const pid = readPid();\n if (pid === null) return;\n\n try {\n if (process.platform === 'win32') {\n execSync(`taskkill /F /T /PID ${pid}`, { stdio: 'ignore', windowsHide: true });\n } else {\n process.kill(pid, 'SIGTERM');\n }\n } catch { /* process already gone */ }\n\n try { unlinkSync(pidFile()); } catch { /* ignore */ }\n}\n\nexport async function main(): Promise<void> {\n if (process.argv.length > 2 && process.argv[2] === 'stop') {\n stopServer();\n console.log('Server stopped.');\n } else {\n await ensureServerRunning();\n }\n}\n"],"mappings":"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","names":["randomUUID","resolve","existsSync","readFileSync","writeFileSync","unlinkSync","mkdirSync","join","homedir","existsSync","mkdirSync","writeFileSync","resolve","join","agentId","displayName","agents","randomUUID"]}
1
+ {"version":3,"sources":["../src/client/claude-code/src/hook-handler.ts","../src/client/claude-code/src/client.ts","../src/client/claude-code/src/session.ts","../src/client/claude-code/src/stdin-reader.ts","../src/client/claude-code/src/server-manager.ts"],"sourcesContent":["/**\n * Open Party Hook Handler for Claude Code.\n *\n * Handles lifecycle events:\n * session_start → Register/reuse agent, inject context\n * session_end → Unregister agent (keep session file for reuse)\n * user_prompt_submit → Auto-check messages, inject as context\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { PartyServerClient } from './client.js';\nimport { generateAgentId, generateDisplayName, readSession, writeSession } from './session.js';\nimport { readJsonFromStdin } from './stdin-reader.js';\nimport { ensureServerRunning } from './server-manager.js';\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nconst PARTY_SERVER_URL = process.env.PARTY_SERVER_URL || 'http://127.0.0.1:8000';\n\n// ---------------------------------------------------------------------------\n// Hook handlers\n// ---------------------------------------------------------------------------\n\nasync function handleSessionStart(sessionId: string, source: string): Promise<void> {\n // Ensure Party Server is running\n await ensureServerRunning();\n\n const client = new PartyServerClient(PARTY_SERVER_URL);\n\n // Try to reuse existing agent for this session\n const existing = readSession(sessionId);\n\n if (existing && (source === 'clear' || source === 'compact' || source === 'resume')) {\n const agentId = existing.agent_id as string;\n const displayName = (existing.display_name as string) || agentId;\n\n if (source === 'clear') {\n // Agent was unregistered by SessionEnd — re-register with same identity\n try {\n await client.register(agentId, displayName, { type: 'claude-code' });\n console.error(`[Open Party] Re-registered as ${agentId} (${displayName})`);\n } catch (e) {\n console.error(`[Open Party] Re-registration failed: ${(e as Error).message}`);\n }\n // Update session file (overwrite with current data)\n writeSession(sessionId, agentId, displayName, PARTY_SERVER_URL);\n }\n // compact/resume: agent is still registered, just re-inject context\n\n let agents: Record<string, unknown>[] = [];\n let pendingMessages: Record<string, unknown>[] = [];\n try {\n agents = await client.listAgents();\n // resume: dequeue unread messages\n if (source === 'resume') {\n pendingMessages = await client.checkMessages(agentId);\n }\n } catch { /* ignore */ }\n outputContext(agentId, displayName, agents, pendingMessages);\n return;\n }\n\n // startup or no existing session — generate new identity\n const agentId = generateAgentId();\n const displayName = generateDisplayName();\n\n try {\n await client.register(agentId, displayName, { type: 'claude-code' });\n console.error(`[Open Party] Registered as ${agentId} (${displayName})`);\n } catch (e) {\n console.error(`[Open Party] Registration failed: ${(e as Error).message}`);\n writeSession(sessionId, agentId, displayName, PARTY_SERVER_URL);\n outputContext(agentId, displayName, [], []);\n return;\n }\n\n writeSession(sessionId, agentId, displayName, PARTY_SERVER_URL);\n\n let agents: Record<string, unknown>[] = [];\n try {\n agents = await client.listAgents();\n } catch { /* ignore */ }\n outputContext(agentId, displayName, agents, []);\n}\n\nasync function handleSessionEnd(sessionId: string): Promise<void> {\n const session = readSession(sessionId);\n if (!session) return;\n\n const agentId = session.agent_id as string;\n const serverUrl = session.server_url as string;\n const client = new PartyServerClient(serverUrl);\n\n try {\n await client.remove(agentId);\n console.error(`[Open Party] Unregistered ${agentId}`);\n } catch (e) {\n console.error(`[Open Party] Unregister failed: ${(e as Error).message}`);\n }\n\n // Keep session file — allows clear/compact to reuse agent identity.\n // File will be overwritten on next startup with the same session_id.\n}\n\nasync function handleUserPromptSubmit(sessionId: string): Promise<void> {\n const session = readSession(sessionId);\n if (!session) return; // No agent, silent exit\n\n const agentId = session.agent_id as string;\n const serverUrl = session.server_url as string;\n const client = new PartyServerClient(serverUrl);\n\n let messages: Record<string, unknown>[];\n try {\n messages = await client.checkMessages(agentId);\n } catch {\n return; // Server unreachable, silent exit\n }\n\n if (!messages.length) return; // No messages, silent exit\n\n // Format messages and inject as additional context\n const lines = ['## \\u{1F4EC} New message(s) from other agents', ''];\n for (const msg of messages) {\n const sender = (msg.sender_id as string) ?? 'unknown';\n const content = (msg.content as string) ?? '';\n lines.push('---', '', `**From:** \\`${sender}\\``, '', `> ${content}`, '');\n }\n lines.push('---', '', '\\u{1F4A1} Respond with `send_message` if needed.');\n\n const output = {\n hookSpecificOutput: {\n hookEventName: 'UserPromptSubmit',\n additionalContext: lines.join('\\n'),\n },\n };\n console.log(JSON.stringify(output));\n}\n\nasync function handlePostToolUse(): Promise<void> {\n let toolName: string | undefined;\n let toolInput: Record<string, unknown> | undefined;\n let toolResult: string | undefined;\n try {\n const data = await readJsonFromStdin() as Record<string, unknown> | undefined;\n toolName = data?.tool_name as string | undefined;\n toolInput = data?.tool_input as Record<string, unknown> | undefined;\n toolResult = data?.tool_result as string | undefined;\n } catch { /* ignore */ }\n\n if (!toolName) return;\n\n let message = '';\n\n if (toolName.includes('send_message')) {\n const recipient = (toolInput?.recipient_id as string) ?? 'unknown';\n const summary = (toolInput?.summary as string) ?? '(no summary)';\n message = `\\u{2709}\\u{FE0F} \\u2192 ${recipient}: ${summary}`;\n\n } else if (toolName.includes('check_messages')) {\n if (toolResult?.includes('No new messages')) {\n message = `\\u{1F4EC} No new messages`;\n } else {\n const summaries: string[] = [];\n const msgBlocks = toolResult?.split('---').filter((s: string) => s.includes('From:')) ?? [];\n for (const block of msgBlocks) {\n const fromMatch = block.match(/\\*\\*From:\\*\\* `([^`]+)`/);\n const summaryMatch = block.match(/\\*\\*Summary:\\*\\* (.+)/);\n const from = fromMatch ? fromMatch[1] : 'unknown';\n const text = summaryMatch ? summaryMatch[1] : block.slice(0, 50).replace(/\\n/g, ' ');\n summaries.push(` ${from}: ${text}`);\n }\n if (summaries.length) {\n message = `\\u{1F4EC} ${summaries.length} message(s):\\n` + summaries.join('\\n');\n } else {\n message = `\\u{1F4EC} Messages received`;\n }\n }\n\n } else if (toolName.includes('list_agents')) {\n const match = toolResult?.match(/Online Agents \\((\\d+)\\)/);\n const count = match ? match[1] : '?';\n message = `\\u{1F310} ${count} agent(s) online`;\n\n } else if (toolName.includes('message_history')) {\n const match = toolResult?.match(/Message History \\(last (\\d+)\\)/);\n const count = match ? match[1] : '?';\n message = `\\u{1F4DC} Last ${count} messages from history`;\n }\n\n if (!message) return;\n\n const output = {\n hookSpecificOutput: {\n hookEventName: 'PostToolUse',\n systemMessage: message,\n },\n };\n console.log(JSON.stringify(output));\n}\n\n// ---------------------------------------------------------------------------\n// Context injection\n// ---------------------------------------------------------------------------\n\n// ANSI color codes\nconst BOLD = '\\x1b[1m';\nconst CYAN = '\\x1b[36m';\nconst GREEN = '\\x1b[32m';\nconst YELLOW = '\\x1b[33m';\nconst DIM = '\\x1b[2m';\nconst RESET = '\\x1b[0m';\n\nfunction formatBanner(\n displayName: string,\n others: Record<string, unknown>[],\n pendingCount: number,\n): string {\n const lines: string[] = [];\n\n // Title line\n lines.push(`${BOLD}${CYAN}[Open Party]${RESET} ${BOLD}${displayName}${RESET}`);\n\n // Online agents\n if (others.length === 0) {\n lines.push(` ${DIM}No other agents online${RESET}`);\n } else {\n lines.push(` ${GREEN}${others.length} agent(s) online:${RESET}`);\n for (let i = 0; i < others.length; i++) {\n const a = others[i];\n const name = (a.display_name as string) || (a.agent_id as string);\n const prefix = i < others.length - 1 ? ' \\u251C\\u2500' : ' \\u2514\\u2500';\n lines.push(`${prefix} ${name}`);\n }\n }\n\n // Unread messages\n if (pendingCount > 0) {\n lines.push(` ${YELLOW}\\u{1F4EC} ${pendingCount} unread message(s)${RESET}`);\n }\n\n return lines.join('\\n');\n}\n\nfunction outputContext(agentId: string, displayName: string, agents: Record<string, unknown>[], pendingMessages: Record<string, unknown>[] = []): void {\n const others = agents.filter((a) => a.agent_id !== agentId);\n\n const lines: string[] = [\n '## Open Party Agent Identity',\n '',\n `Your Agent ID: **${agentId}**`,\n `Your Display Name: **${displayName}**`,\n '',\n 'You are connected to the Open Party network as an autonomous agent. ' +\n 'Other agents in the network communicate with you directly \\u2014 they send messages to you, ' +\n 'not to the person you\\'re working with. When you receive messages from peers, treat them ' +\n 'as messages to you as a collaborator: read, evaluate, and respond on your own judgment.',\n '',\n 'Always use your Agent ID when calling Open Party MCP tools.',\n ];\n\n if (others.length > 0) {\n lines.push('');\n lines.push(`**${others.length}** other agent(s) online:`);\n for (const a of others) {\n const name = (a.display_name as string) || (a.agent_id as string);\n lines.push(`- **${name}** \\`${a.agent_id}\\``);\n }\n lines.push('');\n lines.push('Available tools: `send_message` \\u00b7 `check_messages` \\u00b7 `list_agents` \\u00b7 `message_history`');\n } else {\n lines.push('');\n lines.push('No other agents online right now. Use `list_agents()` to check later.');\n }\n\n // Unread messages from previous session\n if (pendingMessages.length > 0) {\n lines.push('');\n lines.push(`## \\u{1F4EC} Unread Messages (${pendingMessages.length})`);\n lines.push('You have unread messages from your previous session:');\n for (const msg of pendingMessages) {\n const sender = (msg.sender_id as string) ?? 'unknown';\n const content = (msg.content as string) ?? '';\n lines.push(`- **From \\`${sender}\\`:** ${content}`);\n }\n lines.push('Respond with `send_message` if needed.');\n }\n\n lines.push('');\n lines.push(\n '**Display note:** When you call Open Party tools, summarize the interaction ' +\n 'naturally in your response rather than quoting raw tool output. For example, ' +\n 'say \"I sent a message to agent-x\" rather than showing the tool response.'\n );\n\n const output = {\n hookSpecificOutput: {\n hookEventName: 'SessionStart',\n additionalContext: lines.join('\\n'),\n },\n systemMessage: formatBanner(displayName, others, pendingMessages.length),\n };\n console.log(JSON.stringify(output));\n}\n\n// ---------------------------------------------------------------------------\n// Main\n// ---------------------------------------------------------------------------\n\nasync function main(): Promise<void> {\n if (process.argv.length < 2) {\n console.error('Usage: hook-handler.js <session_start|session_end|user_prompt_submit>');\n process.exit(1);\n }\n\n const event = process.argv[2];\n\n // Read stdin (Claude Code sends JSON to hooks)\n let sessionId: string | undefined;\n let source: string | undefined;\n try {\n const data = await readJsonFromStdin() as Record<string, unknown> | undefined;\n sessionId = data?.session_id as string | undefined;\n source = data?.source as string | undefined;\n } catch { /* ignore */ }\n\n // Fallback: generate a session_id if not provided\n if (!sessionId) sessionId = randomUUID().slice(0, 12);\n\n if (event === 'session_start') {\n await handleSessionStart(sessionId, source || 'startup');\n } else if (event === 'session_end') {\n await handleSessionEnd(sessionId);\n } else if (event === 'user_prompt_submit') {\n await handleUserPromptSubmit(sessionId);\n } else if (event === 'post_tool_use') {\n await handlePostToolUse();\n } else {\n console.error(`[Open Party] Unknown event: ${event}`);\n process.exit(1);\n }\n\n // Hook process must exit — heartbeat lives in the long-running Party Server\n process.exit(0);\n}\n\nmain().catch((e) => {\n console.error(`[Open Party] Fatal: ${(e as Error).message}`);\n process.exit(1);\n});\n","/**\n * Party Server HTTP client.\n *\n * Encapsulates all API calls to the Party Server so that hook handlers,\n * MCP servers, and any other consumer share a single HTTP layer.\n */\n\nexport class PartyServerClient {\n private baseUrl: string;\n private timeout: number;\n\n constructor(baseUrl = 'http://127.0.0.1:8000', timeout = 5000) {\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n this.timeout = timeout;\n }\n\n private async request(path: string, options: RequestInit = {}): Promise<unknown> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeout);\n try {\n const resp = await fetch(`${this.baseUrl}${path}`, {\n ...options,\n signal: controller.signal,\n headers: {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string> | undefined),\n },\n });\n if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);\n return resp.json();\n } finally {\n clearTimeout(timer);\n }\n }\n\n // -- Agent lifecycle --\n\n async register(agentId: string, displayName?: string, metadata?: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.request('/agent/register', {\n method: 'POST',\n body: JSON.stringify({ agent_id: agentId, display_name: displayName, metadata: metadata ?? {} }),\n }) as Promise<Record<string, unknown>>;\n }\n\n async remove(agentId: string): Promise<boolean> {\n const result = await this.request('/agent/remove', {\n method: 'POST',\n body: JSON.stringify({ agent_id: agentId }),\n }) as Record<string, unknown>;\n return result.status === 'removed';\n }\n\n async heartbeat(agentId: string): Promise<Record<string, unknown>> {\n return this.request('/agent/heartbeat', {\n method: 'POST',\n body: JSON.stringify({ agent_id: agentId }),\n }) as Promise<Record<string, unknown>>;\n }\n\n async listAgents(): Promise<Record<string, unknown>[]> {\n const result = await this.request('/agent/list') as Record<string, unknown>;\n return (result.agents as Record<string, unknown>[]) ?? [];\n }\n\n // -- Messaging --\n\n async sendMessage(senderId: string, recipientId: string, content: string, summary?: string): Promise<Record<string, unknown>> {\n return this.request('/agent/send', {\n method: 'POST',\n body: JSON.stringify({ sender_id: senderId, recipient_id: recipientId, content, summary }),\n }) as Promise<Record<string, unknown>>;\n }\n\n async checkMessages(agentId: string): Promise<Record<string, unknown>[]> {\n const result = await this.request(`/agent/messages/${agentId}`) as Record<string, unknown>;\n return (result.messages as Record<string, unknown>[]) ?? [];\n }\n\n /** Get message history for an agent. */\n async getMessageHistory(agentId: string, limit = 20): Promise<Record<string, unknown>[]> {\n const result = await this.request(`/agent/history/${agentId}?limit=${limit}`) as Record<string, unknown>;\n return (result.history as Record<string, unknown>[]) ?? [];\n }\n\n // -- Proxy --\n\n async health(): Promise<Record<string, unknown>> {\n return this.request('/proxy/health') as Promise<Record<string, unknown>>;\n }\n}\n","import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { randomUUID } from 'node:crypto';\n\nconst SESSION_DIR = join(homedir(), '.open-party');\nconst SESSIONS_DIR = join(SESSION_DIR, 'sessions');\nconst AGENTS_DIR = join(SESSION_DIR, 'agents');\n\nfunction ensureDir(dir: string): void {\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n}\n\n/** Write forward (session→agent) and reverse (agent→session) mappings. */\nexport function writeSession(sessionId: string, agentId: string, displayName: string, serverUrl: string): void {\n ensureDir(SESSIONS_DIR);\n ensureDir(AGENTS_DIR);\n\n const sessionData = { agent_id: agentId, display_name: displayName, server_url: serverUrl, session_id: sessionId };\n writeFileSync(join(SESSIONS_DIR, `${sessionId}.json`), JSON.stringify(sessionData));\n writeFileSync(join(AGENTS_DIR, `${agentId}.json`), JSON.stringify({ session_id: sessionId }));\n}\n\n/** Read session info by session_id (forward lookup). */\nexport function readSession(sessionId: string): Record<string, string> | undefined {\n const path = join(SESSIONS_DIR, `${sessionId}.json`);\n if (!existsSync(path)) return undefined;\n return JSON.parse(readFileSync(path, 'utf-8'));\n}\n\n/** Read session info by agent_id (reverse lookup via agents/ mapping). */\nexport function readSessionByAgent(agentId: string): Record<string, string> | undefined {\n const mappingPath = join(AGENTS_DIR, `${agentId}.json`);\n if (!existsSync(mappingPath)) return undefined;\n const mapping = JSON.parse(readFileSync(mappingPath, 'utf-8'));\n return readSession(mapping.session_id);\n}\n\n/** Remove forward and reverse mappings for a session. */\nexport function clearSession(sessionId: string): void {\n const sessionPath = join(SESSIONS_DIR, `${sessionId}.json`);\n if (!existsSync(sessionPath)) return;\n\n const sessionData = JSON.parse(readFileSync(sessionPath, 'utf-8'));\n const agentId: string | undefined = sessionData.agent_id;\n\n try { unlinkSync(sessionPath); } catch { /* ignore */ }\n if (agentId) {\n try { unlinkSync(join(AGENTS_DIR, `${agentId}.json`)); } catch { /* ignore */ }\n }\n}\n\n/** Generate a unique agent ID (random, no host information). */\nexport function generateAgentId(): string {\n return `agent-${randomUUID().slice(0, 12)}`;\n}\n\n/** Generate a random human-friendly display name. */\nexport function generateDisplayName(): string {\n const adjectives = [\n 'swift', 'calm', 'bold', 'keen', 'warm',\n 'bright', 'clever', 'noble', 'agile', 'brave',\n ];\n const nouns = [\n 'falcon', 'river', 'storm', 'raven', 'tiger',\n 'canyon', 'spark', 'cedar', 'fox', 'wolf',\n ];\n const adj = adjectives[Math.floor(Math.random() * adjectives.length)];\n const noun = nouns[Math.floor(Math.random() * nouns.length)];\n const num = Math.floor(Math.random() * 100);\n return `${adj}-${noun}-${num}`;\n}\n","/**\n * Stdin reading utility for Claude Code hooks.\n *\n * Claude Code doesn't close stdin after writing hook input,\n * so stdin 'end' never fires. Solution: detect complete JSON\n * by attempting to parse after each chunk.\n */\n\nconst SAFETY_TIMEOUT_MS = 30000;\nconst PARSE_DELAY_MS = 50;\n\nfunction isStdinAvailable(): boolean {\n try {\n const stdin = process.stdin;\n if (stdin.isTTY) return false;\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n stdin.readable;\n return true;\n } catch {\n return false;\n }\n}\n\nfunction tryParseJson(input: string): { success: true; value: unknown } | { success: false } {\n const trimmed = input.trim();\n if (!trimmed) return { success: false };\n try {\n return { success: true, value: JSON.parse(trimmed) };\n } catch {\n return { success: false };\n }\n}\n\nexport async function readJsonFromStdin(): Promise<unknown> {\n if (!isStdinAvailable()) return undefined;\n\n return new Promise((resolve) => {\n let input = '';\n let resolved = false;\n let parseDelayId: ReturnType<typeof setTimeout> | null = null;\n\n const cleanup = () => {\n try {\n process.stdin.removeAllListeners('data');\n process.stdin.removeAllListeners('end');\n process.stdin.removeAllListeners('error');\n } catch { /* ignore */ }\n };\n\n const resolveWith = (value: unknown) => {\n if (resolved) return;\n resolved = true;\n if (parseDelayId) clearTimeout(parseDelayId);\n clearTimeout(safetyTimeoutId);\n cleanup();\n resolve(value);\n };\n\n const tryResolveWithJson = (): boolean => {\n const result = tryParseJson(input);\n if (result.success) {\n resolveWith(result.value);\n return true;\n }\n return false;\n };\n\n const safetyTimeoutId = setTimeout(() => {\n if (!resolved) {\n if (!tryResolveWithJson()) {\n resolveWith(input.trim() ? undefined : undefined);\n }\n }\n }, SAFETY_TIMEOUT_MS);\n\n try {\n process.stdin.on('data', (chunk: Buffer) => {\n input += chunk.toString();\n if (parseDelayId) { clearTimeout(parseDelayId); parseDelayId = null; }\n if (tryResolveWithJson()) return;\n parseDelayId = setTimeout(() => tryResolveWithJson(), PARSE_DELAY_MS);\n });\n\n process.stdin.on('end', () => {\n if (!resolved) tryResolveWithJson();\n });\n\n process.stdin.on('error', () => {\n if (!resolved) resolveWith(undefined);\n });\n } catch {\n resolved = true;\n clearTimeout(safetyTimeoutId);\n cleanup();\n resolve(undefined);\n }\n });\n}\n","/**\r\n * Manage the Party Server lifecycle for plugin integration.\r\n *\r\n * Detects if the server is already running, starts it if not, and\r\n * tracks the PID for cleanup.\r\n */\r\n\r\nimport { spawn, execSync } from 'node:child_process';\r\nimport { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync, openSync } from 'node:fs';\r\nimport { join, resolve, dirname } from 'node:path';\r\nimport { homedir } from 'node:os';\r\nimport { fileURLToPath } from 'node:url';\r\n\r\nconst __dirname = dirname(fileURLToPath(import.meta.url));\r\n\r\nconst DEFAULT_PORT = parseInt(process.env.PARTY_PORT || '8000', 10);\r\nconst HEALTH_URL = `http://127.0.0.1:${DEFAULT_PORT}/proxy/health`;\r\nconst STARTUP_TIMEOUT = 10_000; // ms\r\n\r\nfunction pidFile(): string {\r\n const pluginData = process.env.CLAUDE_PLUGIN_DATA || '';\r\n if (pluginData) return join(pluginData, 'server.pid');\r\n return join(homedir(), '.open-party', 'server.pid');\r\n}\r\n\r\nfunction logFile(): string {\r\n const pluginData = process.env.CLAUDE_PLUGIN_DATA || '';\r\n if (pluginData) return join(pluginData, 'server.log');\r\n return join(homedir(), '.open-party', 'server.log');\r\n}\r\n\r\nfunction pluginRoot(): string {\r\n // CLAUDE_PLUGIN_ROOT is set by Claude Code at runtime\r\n if (process.env.CLAUDE_PLUGIN_ROOT) return process.env.CLAUDE_PLUGIN_ROOT;\r\n // Fallback: this file is at <pkg-root>/dist/hook-handler.js, so plugin root is <pkg-root>/\r\n // But when bundled into mcp-server.js, it's at <pkg-root>/dist/mcp-server.js\r\n // Check both locations\r\n const fromHere = resolve(__dirname, '..');\r\n if (existsSync(join(fromHere, 'dist', 'party-server.js'))) {\r\n return fromHere;\r\n }\r\n // When running from npm global install: we're in dist/ itself\r\n if (existsSync(join(fromHere, 'party-server.js'))) {\r\n return fromHere;\r\n }\r\n return resolve(__dirname, '..');\r\n}\r\n\r\nasync function isRunning(): Promise<boolean> {\r\n try {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), 2000);\r\n const resp = await fetch(HEALTH_URL, { signal: controller.signal });\r\n clearTimeout(timer);\r\n return resp.status === 200;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\nfunction readPid(): number | null {\r\n const path = pidFile();\r\n if (!existsSync(path)) return null;\r\n try {\r\n return parseInt(readFileSync(path, 'utf-8').trim(), 10);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nfunction writePid(pid: number): void {\r\n const path = pidFile();\r\n const dir = dirname(path);\r\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\r\n writeFileSync(path, String(pid));\r\n}\r\n\r\nasync function sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\nexport async function ensureServerRunning(): Promise<void> {\r\n if (await isRunning()) return;\r\n\r\n const serverScript = join(pluginRoot(), 'dist', 'party-server.js');\r\n if (!existsSync(serverScript)) {\r\n console.error(`[Open Party] Server script not found: ${serverScript}`);\r\n return;\r\n }\r\n\r\n // Redirect stdout/stderr to log file so we can debug crashes\r\n const logPath = logFile();\r\n mkdirSync(dirname(logPath), { recursive: true });\r\n const logFd = openSync(logPath, 'a');\r\n\r\n const proc = spawn(process.execPath, [serverScript], {\r\n stdio: ['ignore', logFd, logFd],\r\n detached: true,\r\n windowsHide: true,\r\n });\r\n proc.unref();\r\n writePid(proc.pid!);\r\n\r\n // Handle spawn errors\r\n proc.on('error', (err) => {\r\n console.error(`[Open Party] Failed to start server: ${err.message}`);\r\n });\r\n\r\n // Wait for server to become healthy\r\n const deadline = Date.now() + STARTUP_TIMEOUT;\r\n while (Date.now() < deadline) {\r\n if (await isRunning()) return;\r\n await sleep(500);\r\n }\r\n\r\n // Check if process is still alive\r\n try {\r\n process.kill(proc.pid!, 0);\r\n } catch {\r\n console.error(`[Open Party] Server crashed during startup. Check log: ${logPath}`);\r\n }\r\n}\r\n\r\nfunction stopServer(): void {\r\n const pid = readPid();\r\n if (pid === null) return;\r\n\r\n try {\r\n if (process.platform === 'win32') {\r\n execSync(`taskkill /F /T /PID ${pid}`, { stdio: 'ignore', windowsHide: true });\r\n } else {\r\n process.kill(pid, 'SIGTERM');\r\n }\r\n } catch { /* process already gone */ }\r\n\r\n try { unlinkSync(pidFile()); } catch { /* ignore */ }\r\n}\r\n\r\nexport async function main(): Promise<void> {\r\n if (process.argv.length > 2 && process.argv[2] === 'stop') {\r\n stopServer();\r\n console.log('Server stopped.');\r\n } else {\r\n await ensureServerRunning();\r\n }\r\n}\r\n"],"mappings":";;;;AASA,SAAS,cAAAA,mBAAkB;;;ACFpB,IAAM,oBAAN,MAAwB;AAAA,EACrB;AAAA,EACA;AAAA,EAER,YAAY,UAAU,yBAAyB,UAAU,KAAM;AAC7D,SAAK,UAAU,QAAQ,QAAQ,OAAO,EAAE;AACxC,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAc,QAAQ,MAAc,UAAuB,CAAC,GAAqB;AAC/E,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AAC/D,QAAI;AACF,YAAM,OAAO,MAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,QACjD,GAAG;AAAA,QACH,QAAQ,WAAW;AAAA,QACnB,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,GAAI,QAAQ;AAAA,QACd;AAAA,MACF,CAAC;AACD,UAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,QAAQ,KAAK,MAAM,KAAK,KAAK,UAAU,EAAE;AACvE,aAAO,KAAK,KAAK;AAAA,IACnB,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,SAAS,SAAiB,aAAsB,UAAsE;AAC1H,WAAO,KAAK,QAAQ,mBAAmB;AAAA,MACrC,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,UAAU,SAAS,cAAc,aAAa,UAAU,YAAY,CAAC,EAAE,CAAC;AAAA,IACjG,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAO,SAAmC;AAC9C,UAAM,SAAS,MAAM,KAAK,QAAQ,iBAAiB;AAAA,MACjD,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,UAAU,QAAQ,CAAC;AAAA,IAC5C,CAAC;AACD,WAAO,OAAO,WAAW;AAAA,EAC3B;AAAA,EAEA,MAAM,UAAU,SAAmD;AACjE,WAAO,KAAK,QAAQ,oBAAoB;AAAA,MACtC,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,UAAU,QAAQ,CAAC;AAAA,IAC5C,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aAAiD;AACrD,UAAM,SAAS,MAAM,KAAK,QAAQ,aAAa;AAC/C,WAAQ,OAAO,UAAwC,CAAC;AAAA,EAC1D;AAAA;AAAA,EAIA,MAAM,YAAY,UAAkB,aAAqB,SAAiB,SAAoD;AAC5H,WAAO,KAAK,QAAQ,eAAe;AAAA,MACjC,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,WAAW,UAAU,cAAc,aAAa,SAAS,QAAQ,CAAC;AAAA,IAC3F,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,cAAc,SAAqD;AACvE,UAAM,SAAS,MAAM,KAAK,QAAQ,mBAAmB,OAAO,EAAE;AAC9D,WAAQ,OAAO,YAA0C,CAAC;AAAA,EAC5D;AAAA;AAAA,EAGA,MAAM,kBAAkB,SAAiB,QAAQ,IAAwC;AACvF,UAAM,SAAS,MAAM,KAAK,QAAQ,kBAAkB,OAAO,UAAU,KAAK,EAAE;AAC5E,WAAQ,OAAO,WAAyC,CAAC;AAAA,EAC3D;AAAA;AAAA,EAIA,MAAM,SAA2C;AAC/C,WAAO,KAAK,QAAQ,eAAe;AAAA,EACrC;AACF;;;ACzFA,SAAS,YAAY,WAAW,cAAc,YAAY,qBAAqB;AAC/E,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAE3B,IAAM,cAAc,KAAK,QAAQ,GAAG,aAAa;AACjD,IAAM,eAAe,KAAK,aAAa,UAAU;AACjD,IAAM,aAAa,KAAK,aAAa,QAAQ;AAE7C,SAAS,UAAU,KAAmB;AACpC,MAAI,CAAC,WAAW,GAAG,EAAG,WAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAC1D;AAGO,SAAS,aAAa,WAAmB,SAAiB,aAAqB,WAAyB;AAC7G,YAAU,YAAY;AACtB,YAAU,UAAU;AAEpB,QAAM,cAAc,EAAE,UAAU,SAAS,cAAc,aAAa,YAAY,WAAW,YAAY,UAAU;AACjH,gBAAc,KAAK,cAAc,GAAG,SAAS,OAAO,GAAG,KAAK,UAAU,WAAW,CAAC;AAClF,gBAAc,KAAK,YAAY,GAAG,OAAO,OAAO,GAAG,KAAK,UAAU,EAAE,YAAY,UAAU,CAAC,CAAC;AAC9F;AAGO,SAAS,YAAY,WAAuD;AACjF,QAAM,OAAO,KAAK,cAAc,GAAG,SAAS,OAAO;AACnD,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,SAAO,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;AAC/C;AAyBO,SAAS,kBAA0B;AACxC,SAAO,SAAS,WAAW,EAAE,MAAM,GAAG,EAAE,CAAC;AAC3C;AAGO,SAAS,sBAA8B;AAC5C,QAAM,aAAa;AAAA,IACjB;AAAA,IAAS;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IACjC;AAAA,IAAU;AAAA,IAAU;AAAA,IAAS;AAAA,IAAS;AAAA,EACxC;AACA,QAAM,QAAQ;AAAA,IACZ;AAAA,IAAU;AAAA,IAAS;AAAA,IAAS;AAAA,IAAS;AAAA,IACrC;AAAA,IAAU;AAAA,IAAS;AAAA,IAAS;AAAA,IAAO;AAAA,EACrC;AACA,QAAM,MAAM,WAAW,KAAK,MAAM,KAAK,OAAO,IAAI,WAAW,MAAM,CAAC;AACpE,QAAM,OAAO,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,MAAM,MAAM,CAAC;AAC3D,QAAM,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG;AAC1C,SAAO,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG;AAC9B;;;AC/DA,IAAM,oBAAoB;AAC1B,IAAM,iBAAiB;AAEvB,SAAS,mBAA4B;AACnC,MAAI;AACF,UAAM,QAAQ,QAAQ;AACtB,QAAI,MAAM,MAAO,QAAO;AAExB,UAAM;AACN,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,OAAuE;AAC3F,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAS,QAAO,EAAE,SAAS,MAAM;AACtC,MAAI;AACF,WAAO,EAAE,SAAS,MAAM,OAAO,KAAK,MAAM,OAAO,EAAE;AAAA,EACrD,QAAQ;AACN,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B;AACF;AAEA,eAAsB,oBAAsC;AAC1D,MAAI,CAAC,iBAAiB,EAAG,QAAO;AAEhC,SAAO,IAAI,QAAQ,CAACC,aAAY;AAC9B,QAAI,QAAQ;AACZ,QAAI,WAAW;AACf,QAAI,eAAqD;AAEzD,UAAM,UAAU,MAAM;AACpB,UAAI;AACF,gBAAQ,MAAM,mBAAmB,MAAM;AACvC,gBAAQ,MAAM,mBAAmB,KAAK;AACtC,gBAAQ,MAAM,mBAAmB,OAAO;AAAA,MAC1C,QAAQ;AAAA,MAAe;AAAA,IACzB;AAEA,UAAM,cAAc,CAAC,UAAmB;AACtC,UAAI,SAAU;AACd,iBAAW;AACX,UAAI,aAAc,cAAa,YAAY;AAC3C,mBAAa,eAAe;AAC5B,cAAQ;AACR,MAAAA,SAAQ,KAAK;AAAA,IACf;AAEA,UAAM,qBAAqB,MAAe;AACxC,YAAM,SAAS,aAAa,KAAK;AACjC,UAAI,OAAO,SAAS;AAClB,oBAAY,OAAO,KAAK;AACxB,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAEA,UAAM,kBAAkB,WAAW,MAAM;AACvC,UAAI,CAAC,UAAU;AACb,YAAI,CAAC,mBAAmB,GAAG;AACzB,sBAAY,MAAM,KAAK,IAAI,SAAY,MAAS;AAAA,QAClD;AAAA,MACF;AAAA,IACF,GAAG,iBAAiB;AAEpB,QAAI;AACF,cAAQ,MAAM,GAAG,QAAQ,CAAC,UAAkB;AAC1C,iBAAS,MAAM,SAAS;AACxB,YAAI,cAAc;AAAE,uBAAa,YAAY;AAAG,yBAAe;AAAA,QAAM;AACrE,YAAI,mBAAmB,EAAG;AAC1B,uBAAe,WAAW,MAAM,mBAAmB,GAAG,cAAc;AAAA,MACtE,CAAC;AAED,cAAQ,MAAM,GAAG,OAAO,MAAM;AAC5B,YAAI,CAAC,SAAU,oBAAmB;AAAA,MACpC,CAAC;AAED,cAAQ,MAAM,GAAG,SAAS,MAAM;AAC9B,YAAI,CAAC,SAAU,aAAY,MAAS;AAAA,MACtC,CAAC;AAAA,IACH,QAAQ;AACN,iBAAW;AACX,mBAAa,eAAe;AAC5B,cAAQ;AACR,MAAAA,SAAQ,MAAS;AAAA,IACnB;AAAA,EACF,CAAC;AACH;;;AC1FA,SAAS,OAAO,gBAAgB;AAChC,SAAS,cAAAC,aAAY,gBAAAC,eAAc,iBAAAC,gBAAe,cAAAC,aAAY,aAAAC,YAAW,gBAAgB;AACzF,SAAS,QAAAC,OAAM,SAAS,eAAe;AACvC,SAAS,WAAAC,gBAAe;AACxB,SAAS,qBAAqB;AAE9B,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAExD,IAAM,eAAe,SAAS,QAAQ,IAAI,cAAc,QAAQ,EAAE;AAClE,IAAM,aAAa,oBAAoB,YAAY;AACnD,IAAM,kBAAkB;AAExB,SAAS,UAAkB;AACzB,QAAM,aAAa,QAAQ,IAAI,sBAAsB;AACrD,MAAI,WAAY,QAAOD,MAAK,YAAY,YAAY;AACpD,SAAOA,MAAKC,SAAQ,GAAG,eAAe,YAAY;AACpD;AAEA,SAAS,UAAkB;AACzB,QAAM,aAAa,QAAQ,IAAI,sBAAsB;AACrD,MAAI,WAAY,QAAOD,MAAK,YAAY,YAAY;AACpD,SAAOA,MAAKC,SAAQ,GAAG,eAAe,YAAY;AACpD;AAEA,SAAS,aAAqB;AAE5B,MAAI,QAAQ,IAAI,mBAAoB,QAAO,QAAQ,IAAI;AAIvD,QAAM,WAAW,QAAQ,WAAW,IAAI;AACxC,MAAIN,YAAWK,MAAK,UAAU,QAAQ,iBAAiB,CAAC,GAAG;AACzD,WAAO;AAAA,EACT;AAEA,MAAIL,YAAWK,MAAK,UAAU,iBAAiB,CAAC,GAAG;AACjD,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,WAAW,IAAI;AAChC;AAEA,eAAe,YAA8B;AAC3C,MAAI;AACF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,GAAI;AACvD,UAAM,OAAO,MAAM,MAAM,YAAY,EAAE,QAAQ,WAAW,OAAO,CAAC;AAClE,iBAAa,KAAK;AAClB,WAAO,KAAK,WAAW;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYA,SAAS,SAAS,KAAmB;AACnC,QAAM,OAAO,QAAQ;AACrB,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,CAACE,YAAW,GAAG,EAAG,CAAAC,WAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACxD,EAAAC,eAAc,MAAM,OAAO,GAAG,CAAC;AACjC;AAEA,eAAe,MAAM,IAA2B;AAC9C,SAAO,IAAI,QAAQ,CAACC,aAAY,WAAWA,UAAS,EAAE,CAAC;AACzD;AAEA,eAAsB,sBAAqC;AACzD,MAAI,MAAM,UAAU,EAAG;AAEvB,QAAM,eAAeC,MAAK,WAAW,GAAG,QAAQ,iBAAiB;AACjE,MAAI,CAACJ,YAAW,YAAY,GAAG;AAC7B,YAAQ,MAAM,yCAAyC,YAAY,EAAE;AACrE;AAAA,EACF;AAGA,QAAM,UAAU,QAAQ;AACxB,EAAAC,WAAU,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAC/C,QAAM,QAAQ,SAAS,SAAS,GAAG;AAEnC,QAAM,OAAO,MAAM,QAAQ,UAAU,CAAC,YAAY,GAAG;AAAA,IACnD,OAAO,CAAC,UAAU,OAAO,KAAK;AAAA,IAC9B,UAAU;AAAA,IACV,aAAa;AAAA,EACf,CAAC;AACD,OAAK,MAAM;AACX,WAAS,KAAK,GAAI;AAGlB,OAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,YAAQ,MAAM,wCAAwC,IAAI,OAAO,EAAE;AAAA,EACrE,CAAC;AAGD,QAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,QAAI,MAAM,UAAU,EAAG;AACvB,UAAM,MAAM,GAAG;AAAA,EACjB;AAGA,MAAI;AACF,YAAQ,KAAK,KAAK,KAAM,CAAC;AAAA,EAC3B,QAAQ;AACN,YAAQ,MAAM,0DAA0D,OAAO,EAAE;AAAA,EACnF;AACF;;;AJtGA,IAAM,mBAAmB,QAAQ,IAAI,oBAAoB;AAMzD,eAAe,mBAAmB,WAAmB,QAA+B;AAElF,QAAM,oBAAoB;AAE1B,QAAM,SAAS,IAAI,kBAAkB,gBAAgB;AAGrD,QAAM,WAAW,YAAY,SAAS;AAEtC,MAAI,aAAa,WAAW,WAAW,WAAW,aAAa,WAAW,WAAW;AACnF,UAAMI,WAAU,SAAS;AACzB,UAAMC,eAAe,SAAS,gBAA2BD;AAEzD,QAAI,WAAW,SAAS;AAEtB,UAAI;AACF,cAAM,OAAO,SAASA,UAASC,cAAa,EAAE,MAAM,cAAc,CAAC;AACnE,gBAAQ,MAAM,iCAAiCD,QAAO,KAAKC,YAAW,GAAG;AAAA,MAC3E,SAAS,GAAG;AACV,gBAAQ,MAAM,wCAAyC,EAAY,OAAO,EAAE;AAAA,MAC9E;AAEA,mBAAa,WAAWD,UAASC,cAAa,gBAAgB;AAAA,IAChE;AAGA,QAAIC,UAAoC,CAAC;AACzC,QAAI,kBAA6C,CAAC;AAClD,QAAI;AACF,MAAAA,UAAS,MAAM,OAAO,WAAW;AAEjC,UAAI,WAAW,UAAU;AACvB,0BAAkB,MAAM,OAAO,cAAcF,QAAO;AAAA,MACtD;AAAA,IACF,QAAQ;AAAA,IAAe;AACvB,kBAAcA,UAASC,cAAaC,SAAQ,eAAe;AAC3D;AAAA,EACF;AAGA,QAAM,UAAU,gBAAgB;AAChC,QAAM,cAAc,oBAAoB;AAExC,MAAI;AACF,UAAM,OAAO,SAAS,SAAS,aAAa,EAAE,MAAM,cAAc,CAAC;AACnE,YAAQ,MAAM,8BAA8B,OAAO,KAAK,WAAW,GAAG;AAAA,EACxE,SAAS,GAAG;AACV,YAAQ,MAAM,qCAAsC,EAAY,OAAO,EAAE;AACzE,iBAAa,WAAW,SAAS,aAAa,gBAAgB;AAC9D,kBAAc,SAAS,aAAa,CAAC,GAAG,CAAC,CAAC;AAC1C;AAAA,EACF;AAEA,eAAa,WAAW,SAAS,aAAa,gBAAgB;AAE9D,MAAI,SAAoC,CAAC;AACzC,MAAI;AACF,aAAS,MAAM,OAAO,WAAW;AAAA,EACnC,QAAQ;AAAA,EAAe;AACvB,gBAAc,SAAS,aAAa,QAAQ,CAAC,CAAC;AAChD;AAEA,eAAe,iBAAiB,WAAkC;AAChE,QAAM,UAAU,YAAY,SAAS;AACrC,MAAI,CAAC,QAAS;AAEd,QAAM,UAAU,QAAQ;AACxB,QAAM,YAAY,QAAQ;AAC1B,QAAM,SAAS,IAAI,kBAAkB,SAAS;AAE9C,MAAI;AACF,UAAM,OAAO,OAAO,OAAO;AAC3B,YAAQ,MAAM,6BAA6B,OAAO,EAAE;AAAA,EACtD,SAAS,GAAG;AACV,YAAQ,MAAM,mCAAoC,EAAY,OAAO,EAAE;AAAA,EACzE;AAIF;AAEA,eAAe,uBAAuB,WAAkC;AACtE,QAAM,UAAU,YAAY,SAAS;AACrC,MAAI,CAAC,QAAS;AAEd,QAAM,UAAU,QAAQ;AACxB,QAAM,YAAY,QAAQ;AAC1B,QAAM,SAAS,IAAI,kBAAkB,SAAS;AAE9C,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,OAAO,cAAc,OAAO;AAAA,EAC/C,QAAQ;AACN;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,OAAQ;AAGtB,QAAM,QAAQ,CAAC,iDAAiD,EAAE;AAClE,aAAW,OAAO,UAAU;AAC1B,UAAM,SAAU,IAAI,aAAwB;AAC5C,UAAM,UAAW,IAAI,WAAsB;AAC3C,UAAM,KAAK,OAAO,IAAI,eAAe,MAAM,MAAM,IAAI,KAAK,OAAO,IAAI,EAAE;AAAA,EACzE;AACA,QAAM,KAAK,OAAO,IAAI,kDAAkD;AAExE,QAAM,SAAS;AAAA,IACb,oBAAoB;AAAA,MAClB,eAAe;AAAA,MACf,mBAAmB,MAAM,KAAK,IAAI;AAAA,IACpC;AAAA,EACF;AACA,UAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;AACpC;AAEA,eAAe,oBAAmC;AAChD,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,OAAO,MAAM,kBAAkB;AACrC,eAAW,MAAM;AACjB,gBAAY,MAAM;AAClB,iBAAa,MAAM;AAAA,EACrB,QAAQ;AAAA,EAAe;AAEvB,MAAI,CAAC,SAAU;AAEf,MAAI,UAAU;AAEd,MAAI,SAAS,SAAS,cAAc,GAAG;AACrC,UAAM,YAAa,WAAW,gBAA2B;AACzD,UAAM,UAAW,WAAW,WAAsB;AAClD,cAAU,uBAA2B,SAAS,KAAK,OAAO;AAAA,EAE5D,WAAW,SAAS,SAAS,gBAAgB,GAAG;AAC9C,QAAI,YAAY,SAAS,iBAAiB,GAAG;AAC3C,gBAAU;AAAA,IACZ,OAAO;AACL,YAAM,YAAsB,CAAC;AAC7B,YAAM,YAAY,YAAY,MAAM,KAAK,EAAE,OAAO,CAAC,MAAc,EAAE,SAAS,OAAO,CAAC,KAAK,CAAC;AAC1F,iBAAW,SAAS,WAAW;AAC7B,cAAM,YAAY,MAAM,MAAM,yBAAyB;AACvD,cAAM,eAAe,MAAM,MAAM,uBAAuB;AACxD,cAAM,OAAO,YAAY,UAAU,CAAC,IAAI;AACxC,cAAM,OAAO,eAAe,aAAa,CAAC,IAAI,MAAM,MAAM,GAAG,EAAE,EAAE,QAAQ,OAAO,GAAG;AACnF,kBAAU,KAAK,KAAK,IAAI,KAAK,IAAI,EAAE;AAAA,MACrC;AACA,UAAI,UAAU,QAAQ;AACpB,kBAAU,aAAa,UAAU,MAAM;AAAA,IAAmB,UAAU,KAAK,IAAI;AAAA,MAC/E,OAAO;AACL,kBAAU;AAAA,MACZ;AAAA,IACF;AAAA,EAEF,WAAW,SAAS,SAAS,aAAa,GAAG;AAC3C,UAAM,QAAQ,YAAY,MAAM,yBAAyB;AACzD,UAAM,QAAQ,QAAQ,MAAM,CAAC,IAAI;AACjC,cAAU,aAAa,KAAK;AAAA,EAE9B,WAAW,SAAS,SAAS,iBAAiB,GAAG;AAC/C,UAAM,QAAQ,YAAY,MAAM,gCAAgC;AAChE,UAAM,QAAQ,QAAQ,MAAM,CAAC,IAAI;AACjC,cAAU,kBAAkB,KAAK;AAAA,EACnC;AAEA,MAAI,CAAC,QAAS;AAEd,QAAM,SAAS;AAAA,IACb,oBAAoB;AAAA,MAClB,eAAe;AAAA,MACf,eAAe;AAAA,IACjB;AAAA,EACF;AACA,UAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;AACpC;AAOA,IAAM,OAAO;AACb,IAAM,OAAO;AACb,IAAM,QAAQ;AACd,IAAM,SAAS;AACf,IAAM,MAAM;AACZ,IAAM,QAAQ;AAEd,SAAS,aACP,aACA,QACA,cACQ;AACR,QAAM,QAAkB,CAAC;AAGzB,QAAM,KAAK,GAAG,IAAI,GAAG,IAAI,eAAe,KAAK,KAAK,IAAI,GAAG,WAAW,GAAG,KAAK,EAAE;AAG9E,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,KAAK,KAAK,GAAG,yBAAyB,KAAK,EAAE;AAAA,EACrD,OAAO;AACL,UAAM,KAAK,KAAK,KAAK,GAAG,OAAO,MAAM,oBAAoB,KAAK,EAAE;AAChE,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,YAAM,IAAI,OAAO,CAAC;AAClB,YAAM,OAAQ,EAAE,gBAA4B,EAAE;AAC9C,YAAM,SAAS,IAAI,OAAO,SAAS,IAAI,mBAAmB;AAC1D,YAAM,KAAK,GAAG,MAAM,IAAI,IAAI,EAAE;AAAA,IAChC;AAAA,EACF;AAGA,MAAI,eAAe,GAAG;AACpB,UAAM,KAAK,KAAK,MAAM,aAAa,YAAY,qBAAqB,KAAK,EAAE;AAAA,EAC7E;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,cAAc,SAAiB,aAAqB,QAAmC,kBAA6C,CAAC,GAAS;AACrJ,QAAM,SAAS,OAAO,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO;AAE1D,QAAM,QAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA,oBAAoB,OAAO;AAAA,IAC3B,wBAAwB,WAAW;AAAA,IACnC;AAAA,IACA;AAAA,IAIA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,KAAK,OAAO,MAAM,2BAA2B;AACxD,eAAW,KAAK,QAAQ;AACtB,YAAM,OAAQ,EAAE,gBAA4B,EAAE;AAC9C,YAAM,KAAK,OAAO,IAAI,QAAQ,EAAE,QAAQ,IAAI;AAAA,IAC9C;AACA,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,iGAAuG;AAAA,EACpH,OAAO;AACL,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,uEAAuE;AAAA,EACpF;AAGA,MAAI,gBAAgB,SAAS,GAAG;AAC9B,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,iCAAiC,gBAAgB,MAAM,GAAG;AACrE,UAAM,KAAK,sDAAsD;AACjE,eAAW,OAAO,iBAAiB;AACjC,YAAM,SAAU,IAAI,aAAwB;AAC5C,YAAM,UAAW,IAAI,WAAsB;AAC3C,YAAM,KAAK,cAAc,MAAM,SAAS,OAAO,EAAE;AAAA,IACnD;AACA,UAAM,KAAK,wCAAwC;AAAA,EACrD;AAEA,QAAM,KAAK,EAAE;AACb,QAAM;AAAA,IACJ;AAAA,EAGF;AAEA,QAAM,SAAS;AAAA,IACb,oBAAoB;AAAA,MAClB,eAAe;AAAA,MACf,mBAAmB,MAAM,KAAK,IAAI;AAAA,IACpC;AAAA,IACA,eAAe,aAAa,aAAa,QAAQ,gBAAgB,MAAM;AAAA,EACzE;AACA,UAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;AACpC;AAMA,eAAe,OAAsB;AACnC,MAAI,QAAQ,KAAK,SAAS,GAAG;AAC3B,YAAQ,MAAM,uEAAuE;AACrF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,QAAQ,QAAQ,KAAK,CAAC;AAG5B,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,OAAO,MAAM,kBAAkB;AACrC,gBAAY,MAAM;AAClB,aAAS,MAAM;AAAA,EACjB,QAAQ;AAAA,EAAe;AAGvB,MAAI,CAAC,UAAW,aAAYC,YAAW,EAAE,MAAM,GAAG,EAAE;AAEpD,MAAI,UAAU,iBAAiB;AAC7B,UAAM,mBAAmB,WAAW,UAAU,SAAS;AAAA,EACzD,WAAW,UAAU,eAAe;AAClC,UAAM,iBAAiB,SAAS;AAAA,EAClC,WAAW,UAAU,sBAAsB;AACzC,UAAM,uBAAuB,SAAS;AAAA,EACxC,WAAW,UAAU,iBAAiB;AACpC,UAAM,kBAAkB;AAAA,EAC1B,OAAO;AACL,YAAQ,MAAM,+BAA+B,KAAK,EAAE;AACpD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,UAAQ,KAAK,CAAC;AAChB;AAEA,KAAK,EAAE,MAAM,CAAC,MAAM;AAClB,UAAQ,MAAM,uBAAwB,EAAY,OAAO,EAAE;AAC3D,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["randomUUID","resolve","existsSync","readFileSync","writeFileSync","unlinkSync","mkdirSync","join","homedir","existsSync","mkdirSync","writeFileSync","resolve","join","agentId","displayName","agents","randomUUID"]}
@@ -2981,7 +2981,7 @@ var require_compile = __commonJS({
2981
2981
  const schOrFunc = root.refs[ref];
2982
2982
  if (schOrFunc)
2983
2983
  return schOrFunc;
2984
- let _sch = resolve.call(this, root, ref);
2984
+ let _sch = resolve2.call(this, root, ref);
2985
2985
  if (_sch === void 0) {
2986
2986
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2987
2987
  const { schemaId } = this.opts;
@@ -3008,7 +3008,7 @@ var require_compile = __commonJS({
3008
3008
  function sameSchemaEnv(s1, s2) {
3009
3009
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3010
3010
  }
3011
- function resolve(root, ref) {
3011
+ function resolve2(root, ref) {
3012
3012
  let sch;
3013
3013
  while (typeof (sch = this.refs[ref]) == "string")
3014
3014
  ref = sch;
@@ -3639,7 +3639,7 @@ var require_fast_uri = __commonJS({
3639
3639
  }
3640
3640
  return uri;
3641
3641
  }
3642
- function resolve(baseURI, relativeURI, options) {
3642
+ function resolve2(baseURI, relativeURI, options) {
3643
3643
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3644
3644
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3645
3645
  schemelessOptions.skipEscape = true;
@@ -3897,7 +3897,7 @@ var require_fast_uri = __commonJS({
3897
3897
  var fastUri = {
3898
3898
  SCHEMES,
3899
3899
  normalize,
3900
- resolve,
3900
+ resolve: resolve2,
3901
3901
  resolveComponent,
3902
3902
  equal,
3903
3903
  serialize,
@@ -18977,7 +18977,7 @@ var Protocol = class {
18977
18977
  return;
18978
18978
  }
18979
18979
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
18980
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
18980
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
18981
18981
  options?.signal?.throwIfAborted();
18982
18982
  }
18983
18983
  } catch (error2) {
@@ -18994,7 +18994,7 @@ var Protocol = class {
18994
18994
  */
18995
18995
  request(request, resultSchema, options) {
18996
18996
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
18997
- return new Promise((resolve, reject) => {
18997
+ return new Promise((resolve2, reject) => {
18998
18998
  const earlyReject = (error2) => {
18999
18999
  reject(error2);
19000
19000
  };
@@ -19072,7 +19072,7 @@ var Protocol = class {
19072
19072
  if (!parseResult.success) {
19073
19073
  reject(parseResult.error);
19074
19074
  } else {
19075
- resolve(parseResult.data);
19075
+ resolve2(parseResult.data);
19076
19076
  }
19077
19077
  } catch (error2) {
19078
19078
  reject(error2);
@@ -19333,12 +19333,12 @@ var Protocol = class {
19333
19333
  }
19334
19334
  } catch {
19335
19335
  }
19336
- return new Promise((resolve, reject) => {
19336
+ return new Promise((resolve2, reject) => {
19337
19337
  if (signal.aborted) {
19338
19338
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
19339
19339
  return;
19340
19340
  }
19341
- const timeoutId = setTimeout(resolve, interval);
19341
+ const timeoutId = setTimeout(resolve2, interval);
19342
19342
  signal.addEventListener("abort", () => {
19343
19343
  clearTimeout(timeoutId);
19344
19344
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -20438,7 +20438,7 @@ var McpServer = class {
20438
20438
  let task = createTaskResult.task;
20439
20439
  const pollInterval = task.pollInterval ?? 5e3;
20440
20440
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
20441
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
20441
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
20442
20442
  const updatedTask = await extra.taskStore.getTask(taskId);
20443
20443
  if (!updatedTask) {
20444
20444
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -21087,12 +21087,12 @@ var StdioServerTransport = class {
21087
21087
  this.onclose?.();
21088
21088
  }
21089
21089
  send(message) {
21090
- return new Promise((resolve) => {
21090
+ return new Promise((resolve2) => {
21091
21091
  const json = serializeMessage(message);
21092
21092
  if (this._stdout.write(json)) {
21093
- resolve();
21093
+ resolve2();
21094
21094
  } else {
21095
- this._stdout.once("drain", resolve);
21095
+ this._stdout.once("drain", resolve2);
21096
21096
  }
21097
21097
  });
21098
21098
  }
@@ -21190,6 +21190,89 @@ function readSessionByAgent(agentId) {
21190
21190
  return readSession(mapping.session_id);
21191
21191
  }
21192
21192
 
21193
+ // src/client/claude-code/src/server-manager.ts
21194
+ import { spawn, execSync } from "child_process";
21195
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2, openSync } from "fs";
21196
+ import { join as join2, resolve, dirname } from "path";
21197
+ import { homedir as homedir2 } from "os";
21198
+ import { fileURLToPath } from "url";
21199
+ var __dirname = dirname(fileURLToPath(import.meta.url));
21200
+ var DEFAULT_PORT = parseInt(process.env.PARTY_PORT || "8000", 10);
21201
+ var HEALTH_URL = `http://127.0.0.1:${DEFAULT_PORT}/proxy/health`;
21202
+ var STARTUP_TIMEOUT = 1e4;
21203
+ function pidFile() {
21204
+ const pluginData = process.env.CLAUDE_PLUGIN_DATA || "";
21205
+ if (pluginData) return join2(pluginData, "server.pid");
21206
+ return join2(homedir2(), ".open-party", "server.pid");
21207
+ }
21208
+ function logFile() {
21209
+ const pluginData = process.env.CLAUDE_PLUGIN_DATA || "";
21210
+ if (pluginData) return join2(pluginData, "server.log");
21211
+ return join2(homedir2(), ".open-party", "server.log");
21212
+ }
21213
+ function pluginRoot() {
21214
+ if (process.env.CLAUDE_PLUGIN_ROOT) return process.env.CLAUDE_PLUGIN_ROOT;
21215
+ const fromHere = resolve(__dirname, "..");
21216
+ if (existsSync2(join2(fromHere, "dist", "party-server.js"))) {
21217
+ return fromHere;
21218
+ }
21219
+ if (existsSync2(join2(fromHere, "party-server.js"))) {
21220
+ return fromHere;
21221
+ }
21222
+ return resolve(__dirname, "..");
21223
+ }
21224
+ async function isRunning() {
21225
+ try {
21226
+ const controller = new AbortController();
21227
+ const timer = setTimeout(() => controller.abort(), 2e3);
21228
+ const resp = await fetch(HEALTH_URL, { signal: controller.signal });
21229
+ clearTimeout(timer);
21230
+ return resp.status === 200;
21231
+ } catch {
21232
+ return false;
21233
+ }
21234
+ }
21235
+ function writePid(pid) {
21236
+ const path = pidFile();
21237
+ const dir = dirname(path);
21238
+ if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
21239
+ writeFileSync2(path, String(pid));
21240
+ }
21241
+ async function sleep(ms) {
21242
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
21243
+ }
21244
+ async function ensureServerRunning() {
21245
+ if (await isRunning()) return;
21246
+ const serverScript = join2(pluginRoot(), "dist", "party-server.js");
21247
+ if (!existsSync2(serverScript)) {
21248
+ console.error(`[Open Party] Server script not found: ${serverScript}`);
21249
+ return;
21250
+ }
21251
+ const logPath = logFile();
21252
+ mkdirSync2(dirname(logPath), { recursive: true });
21253
+ const logFd = openSync(logPath, "a");
21254
+ const proc = spawn(process.execPath, [serverScript], {
21255
+ stdio: ["ignore", logFd, logFd],
21256
+ detached: true,
21257
+ windowsHide: true
21258
+ });
21259
+ proc.unref();
21260
+ writePid(proc.pid);
21261
+ proc.on("error", (err) => {
21262
+ console.error(`[Open Party] Failed to start server: ${err.message}`);
21263
+ });
21264
+ const deadline = Date.now() + STARTUP_TIMEOUT;
21265
+ while (Date.now() < deadline) {
21266
+ if (await isRunning()) return;
21267
+ await sleep(500);
21268
+ }
21269
+ try {
21270
+ process.kill(proc.pid, 0);
21271
+ } catch {
21272
+ console.error(`[Open Party] Server crashed during startup. Check log: ${logPath}`);
21273
+ }
21274
+ }
21275
+
21193
21276
  // src/client/claude-code/src/mcp-server.ts
21194
21277
  var PARTY_SERVER_URL = process.env.PARTY_SERVER_URL || "http://127.0.0.1:8000";
21195
21278
  var client = new PartyServerClient(PARTY_SERVER_URL);
@@ -21314,6 +21397,7 @@ server.tool(
21314
21397
  }
21315
21398
  );
21316
21399
  async function main() {
21400
+ await ensureServerRunning();
21317
21401
  const transport = new StdioServerTransport();
21318
21402
  await server.connect(transport);
21319
21403
  }