@yushaw/sanqian-sdk 0.1.4 → 0.1.6

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.
package/dist/index.d.mts CHANGED
@@ -212,6 +212,9 @@ declare class SanqianSDK {
212
212
  private pendingRequests;
213
213
  private heartbeatTimer;
214
214
  private reconnectTimer;
215
+ private heartbeatAckPending;
216
+ private missedHeartbeats;
217
+ private static readonly MAX_MISSED_HEARTBEATS;
215
218
  private eventListeners;
216
219
  constructor(config: SDKConfig);
217
220
  /**
@@ -452,6 +455,11 @@ declare class DiscoveryManager {
452
455
  stopWatching(): void;
453
456
  /**
454
457
  * Check if a process is running by PID
458
+ *
459
+ * Cross-platform implementation:
460
+ * - macOS/Linux: process.kill(pid, 0) works correctly
461
+ * - Windows: process.kill(pid, 0) can give false positives due to PID reuse,
462
+ * so we use tasklist command for reliable checking
455
463
  */
456
464
  private isProcessRunning;
457
465
  /**
package/dist/index.d.ts CHANGED
@@ -212,6 +212,9 @@ declare class SanqianSDK {
212
212
  private pendingRequests;
213
213
  private heartbeatTimer;
214
214
  private reconnectTimer;
215
+ private heartbeatAckPending;
216
+ private missedHeartbeats;
217
+ private static readonly MAX_MISSED_HEARTBEATS;
215
218
  private eventListeners;
216
219
  constructor(config: SDKConfig);
217
220
  /**
@@ -452,6 +455,11 @@ declare class DiscoveryManager {
452
455
  stopWatching(): void;
453
456
  /**
454
457
  * Check if a process is running by PID
458
+ *
459
+ * Cross-platform implementation:
460
+ * - macOS/Linux: process.kill(pid, 0) works correctly
461
+ * - Windows: process.kill(pid, 0) can give false positives due to PID reuse,
462
+ * so we use tasklist command for reliable checking
455
463
  */
456
464
  private isProcessRunning;
457
465
  /**
package/dist/index.js CHANGED
@@ -136,11 +136,28 @@ var DiscoveryManager = class {
136
136
  }
137
137
  /**
138
138
  * Check if a process is running by PID
139
+ *
140
+ * Cross-platform implementation:
141
+ * - macOS/Linux: process.kill(pid, 0) works correctly
142
+ * - Windows: process.kill(pid, 0) can give false positives due to PID reuse,
143
+ * so we use tasklist command for reliable checking
139
144
  */
140
145
  isProcessRunning(pid) {
141
146
  try {
142
- process.kill(pid, 0);
143
- return true;
147
+ if ((0, import_os.platform)() === "win32") {
148
+ try {
149
+ const result = (0, import_child_process.execSync)(`tasklist /FI "PID eq ${pid}" /NH`, {
150
+ encoding: "utf-8",
151
+ stdio: ["pipe", "pipe", "pipe"]
152
+ });
153
+ return !result.includes("No tasks are running");
154
+ } catch {
155
+ return false;
156
+ }
157
+ } else {
158
+ process.kill(pid, 0);
159
+ return true;
160
+ }
144
161
  } catch {
145
162
  return false;
146
163
  }
@@ -263,7 +280,7 @@ var DiscoveryManager = class {
263
280
  };
264
281
 
265
282
  // src/client.ts
266
- var SanqianSDK = class {
283
+ var SanqianSDK = class _SanqianSDK {
267
284
  config;
268
285
  discovery;
269
286
  ws = null;
@@ -281,6 +298,9 @@ var SanqianSDK = class {
281
298
  // Timers
282
299
  heartbeatTimer = null;
283
300
  reconnectTimer = null;
301
+ heartbeatAckPending = false;
302
+ missedHeartbeats = 0;
303
+ static MAX_MISSED_HEARTBEATS = 2;
284
304
  // Event listeners
285
305
  eventListeners = /* @__PURE__ */ new Map();
286
306
  constructor(config) {
@@ -460,6 +480,8 @@ var SanqianSDK = class {
460
480
  this.handleToolCall(message);
461
481
  break;
462
482
  case "heartbeat_ack":
483
+ this.heartbeatAckPending = false;
484
+ this.missedHeartbeats = 0;
463
485
  break;
464
486
  case "chat_stream":
465
487
  this.handleChatStream(message);
@@ -532,9 +554,13 @@ var SanqianSDK = class {
532
554
  console.log(`[SDK] Tool '${toolName}' completed successfully`);
533
555
  await this.sendToolResult(msgId, call_id, true, result);
534
556
  } catch (e) {
535
- const error = e instanceof Error ? e.message : String(e);
536
- console.log(`[SDK] Tool '${toolName}' failed:`, error);
537
- await this.sendToolResult(msgId, call_id, false, void 0, error);
557
+ const errorMessage = e instanceof Error ? e.message : String(e);
558
+ const errorStack = e instanceof Error ? e.stack : void 0;
559
+ console.log(`[SDK] Tool '${toolName}' failed:`, errorMessage);
560
+ if (errorStack) {
561
+ console.log(`[SDK] Tool error stack:`, errorStack);
562
+ }
563
+ await this.sendToolResult(msgId, call_id, false, void 0, errorMessage);
538
564
  }
539
565
  }
540
566
  async sendToolResult(id, callId, success, result, error) {
@@ -600,12 +626,29 @@ var SanqianSDK = class {
600
626
  // ============================================
601
627
  startHeartbeat() {
602
628
  this.stopHeartbeat();
629
+ this.missedHeartbeats = 0;
630
+ this.heartbeatAckPending = false;
603
631
  this.heartbeatTimer = setInterval(() => {
632
+ if (this.heartbeatAckPending) {
633
+ this.missedHeartbeats++;
634
+ console.warn(`[SDK] Missed heartbeat ack (${this.missedHeartbeats}/${_SanqianSDK.MAX_MISSED_HEARTBEATS})`);
635
+ if (this.missedHeartbeats >= _SanqianSDK.MAX_MISSED_HEARTBEATS) {
636
+ console.error("[SDK] Too many missed heartbeat acks, connection may be dead");
637
+ this.ws?.close(4e3, "Heartbeat timeout");
638
+ return;
639
+ }
640
+ }
604
641
  const message = {
605
642
  type: "heartbeat",
606
643
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
607
644
  };
608
- this.send(message);
645
+ this.heartbeatAckPending = true;
646
+ try {
647
+ this.send(message);
648
+ } catch (e) {
649
+ console.error("[SDK] Heartbeat send failed:", e);
650
+ this.ws?.close(4e3, "Heartbeat send failed");
651
+ }
609
652
  }, this.config.heartbeatInterval);
610
653
  }
611
654
  stopHeartbeat() {
@@ -618,13 +661,16 @@ var SanqianSDK = class {
618
661
  // Communication
619
662
  // ============================================
620
663
  send(message) {
621
- if (this.ws && this.ws.readyState === import_ws.default.OPEN) {
622
- const data = JSON.stringify(message);
623
- console.log(`[SDK] WebSocket send:`, data.substring(0, 200));
624
- this.ws.send(data);
625
- } else {
626
- console.error(`[SDK] WebSocket not open, cannot send:`, message);
664
+ if (!this.ws || this.ws.readyState !== import_ws.default.OPEN) {
665
+ const error = new Error(
666
+ `WebSocket not open (state: ${this.ws?.readyState ?? "null"}), cannot send message`
667
+ );
668
+ console.error(`[SDK] ${error.message}:`, message);
669
+ throw error;
627
670
  }
671
+ const data = JSON.stringify(message);
672
+ console.log(`[SDK] WebSocket send:`, data.substring(0, 200));
673
+ this.ws.send(data);
628
674
  }
629
675
  sendAndWait(message, id, timeout) {
630
676
  return new Promise((resolve, reject) => {
@@ -642,7 +688,13 @@ var SanqianSDK = class {
642
688
  reject(error);
643
689
  }
644
690
  });
645
- this.send(message);
691
+ try {
692
+ this.send(message);
693
+ } catch (e) {
694
+ clearTimeout(timer);
695
+ this.pendingRequests.delete(id);
696
+ reject(e);
697
+ }
646
698
  });
647
699
  }
648
700
  // ============================================
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/discovery.ts"],"sourcesContent":["/**\n * Sanqian SDK\n *\n * TypeScript SDK for integrating third-party apps with Sanqian AI Assistant.\n *\n * @example\n * ```typescript\n * import { SanqianSDK } from '@yushaw/sanqian-sdk';\n *\n * const sdk = new SanqianSDK({\n * appName: 'my-app',\n * appVersion: '1.0.0',\n * tools: [\n * {\n * name: 'my_tool',\n * description: 'Does something useful',\n * parameters: { type: 'object', properties: {} },\n * handler: async (args) => {\n * return { success: true };\n * }\n * }\n * ]\n * });\n *\n * await sdk.connect();\n * ```\n */\n\nexport { SanqianSDK, Conversation } from \"./client\";\nexport { DiscoveryManager } from \"./discovery\";\n\n// Types\nexport type {\n SDKConfig,\n ToolDefinition,\n JSONSchema,\n JSONSchemaProperty,\n ConnectionInfo,\n ConnectionState,\n ChatRequest,\n ChatMessage,\n ChatResponse,\n ChatStreamEvent,\n ToolCall,\n SDKEvents,\n SDKEventName,\n // Private Agent API\n AgentConfig,\n AgentInfo,\n AgentUpdateConfig,\n ConversationInfo,\n ConversationMessage,\n ConversationDetail,\n // Remote Tools\n RemoteToolDefinition,\n} from \"./types\";\n","/**\n * Sanqian SDK Client\n *\n * Main class for connecting to Sanqian and registering tools.\n */\n\nimport WebSocket from \"ws\";\nimport { DiscoveryManager } from \"./discovery\";\nimport type {\n SDKConfig,\n ConnectionInfo,\n ConnectionState,\n ToolDefinition,\n RegisterMessage,\n RegisterAckMessage,\n ToolCallMessage,\n ToolResultMessage,\n HeartbeatMessage,\n SDKEvents,\n SDKEventName,\n JSONSchema,\n AgentConfig,\n AgentInfo,\n AgentUpdateConfig,\n ConversationInfo,\n ConversationDetail,\n CreateAgentMessage,\n CreateAgentAckMessage,\n UpdateAgentMessage,\n UpdateAgentAckMessage,\n ListAgentsMessage,\n ListAgentsAckMessage,\n DeleteAgentMessage,\n DeleteAgentAckMessage,\n ListConversationsMessage,\n ListConversationsAckMessage,\n GetConversationMessage,\n GetConversationAckMessage,\n DeleteConversationMessage,\n DeleteConversationAckMessage,\n ChatMessage,\n ChatResponse,\n ChatRequestMessage,\n ChatResponseMessage,\n ChatStreamMessage,\n ChatStreamEvent,\n RemoteToolDefinition,\n} from \"./types\";\n\ntype EventListener<T extends SDKEventName> = SDKEvents[T];\n\nexport class SanqianSDK {\n private config: SDKConfig;\n private discovery: DiscoveryManager;\n private ws: WebSocket | null = null;\n private connectionInfo: ConnectionInfo | null = null;\n\n private state: ConnectionState = {\n connected: false,\n registering: false,\n registered: false,\n reconnectAttempts: 0,\n };\n\n // Tool handlers by name\n private toolHandlers: Map<string, (args: unknown) => Promise<unknown>> =\n new Map();\n\n // Pending request futures\n private pendingRequests: Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n }\n > = new Map();\n\n // Timers\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Event listeners\n private eventListeners: Map<SDKEventName, Set<EventListener<SDKEventName>>> =\n new Map();\n\n constructor(config: SDKConfig) {\n this.config = {\n reconnectInterval: 5000,\n heartbeatInterval: 30000,\n toolExecutionTimeout: 30000,\n ...config,\n };\n this.discovery = new DiscoveryManager();\n\n // Register tool handlers\n for (const tool of config.tools) {\n this.toolHandlers.set(tool.name, tool.handler);\n }\n }\n\n // ============================================\n // Lifecycle\n // ============================================\n\n /**\n * Connect to Sanqian\n *\n * Reads connection info, establishes WebSocket, and registers app.\n * Returns when registration is complete.\n *\n * If autoLaunchSanqian is enabled and Sanqian is not running,\n * SDK will attempt to start it in hidden/tray mode.\n */\n async connect(): Promise<void> {\n // Read connection info\n const info = this.discovery.read();\n\n if (!info) {\n // Sanqian not running\n // Try to auto-launch if enabled\n if (this.config.autoLaunchSanqian) {\n console.log(\"[SDK] Sanqian not running, attempting to launch...\");\n const launched = this.discovery.launchSanqian(this.config.sanqianPath);\n if (launched) {\n console.log(\"[SDK] Sanqian launch initiated, waiting for startup...\");\n } else {\n console.warn(\"[SDK] Failed to launch Sanqian, will wait for manual start\");\n }\n }\n\n // Start watching and wait for connection\n return new Promise((resolve, reject) => {\n console.log(\"[SDK] Waiting for Sanqian...\");\n\n const timeout = setTimeout(() => {\n this.discovery.stopWatching();\n reject(new Error(\"Sanqian connection timeout\"));\n }, 60000); // 60 second timeout\n\n this.discovery.startWatching(async (newInfo) => {\n if (newInfo) {\n clearTimeout(timeout);\n this.discovery.stopWatching();\n try {\n await this.connectWithInfo(newInfo);\n resolve();\n } catch (e) {\n reject(e);\n }\n }\n });\n });\n }\n\n await this.connectWithInfo(info);\n }\n\n /**\n * Connect with known connection info\n */\n private async connectWithInfo(info: ConnectionInfo): Promise<void> {\n this.connectionInfo = info;\n const url = this.discovery.buildWebSocketUrl(info);\n\n return new Promise((resolve, reject) => {\n console.log(`[SDK] Connecting to ${url}`);\n\n this.ws = new WebSocket(url);\n\n const connectTimeout = setTimeout(() => {\n reject(new Error(\"WebSocket connection timeout\"));\n this.ws?.close();\n }, 10000);\n\n this.ws.on(\"open\", async () => {\n clearTimeout(connectTimeout);\n console.log(\"[SDK] WebSocket connected\");\n this.state.connected = true;\n this.state.reconnectAttempts = 0;\n this.emit(\"connected\");\n\n try {\n await this.register();\n resolve();\n } catch (e) {\n reject(e);\n }\n });\n\n this.ws.on(\"close\", (code, reason) => {\n console.log(`[SDK] WebSocket closed: ${code} ${reason.toString()}`);\n this.handleDisconnect(reason.toString());\n });\n\n this.ws.on(\"error\", (error) => {\n console.error(\"[SDK] WebSocket error:\", error);\n this.state.lastError = error;\n this.emit(\"error\", error);\n });\n\n this.ws.on(\"message\", (data) => {\n try {\n const message = JSON.parse(data.toString());\n this.handleMessage(message);\n } catch (e) {\n console.error(\"[SDK] Failed to parse message:\", e);\n }\n });\n });\n }\n\n /**\n * Disconnect from Sanqian\n */\n async disconnect(): Promise<void> {\n this.stopHeartbeat();\n this.stopReconnect();\n this.discovery.stopWatching();\n\n if (this.ws) {\n this.ws.close(1000, \"Client disconnect\");\n this.ws = null;\n }\n\n this.state = {\n connected: false,\n registering: false,\n registered: false,\n reconnectAttempts: 0,\n };\n }\n\n // ============================================\n // Registration\n // ============================================\n\n private async register(): Promise<void> {\n this.state.registering = true;\n\n const msgId = this.generateId();\n\n const message: RegisterMessage = {\n id: msgId,\n type: \"register\",\n app: {\n name: this.config.appName,\n version: this.config.appVersion,\n display_name: this.config.displayName,\n launch_command: this.config.launchCommand,\n },\n tools: this.config.tools.map((t) => ({\n name: `${this.config.appName}:${t.name}`,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n try {\n const response = await this.sendAndWait<RegisterAckMessage>(\n message,\n msgId,\n 10000\n );\n\n if (!response.success) {\n throw new Error(response.error || \"Registration failed\");\n }\n\n this.state.registering = false;\n this.state.registered = true;\n this.startHeartbeat();\n this.emit(\"registered\");\n\n console.log(`[SDK] Registered as '${this.config.appName}'`);\n } catch (e) {\n this.state.registering = false;\n throw e;\n }\n }\n\n // ============================================\n // Message Handling\n // ============================================\n\n private handleMessage(message: { id?: string; type: string; [key: string]: unknown }): void {\n const { id, type } = message;\n\n // Check if this is a response to a pending request\n if (id && this.pendingRequests.has(id)) {\n const pending = this.pendingRequests.get(id)!;\n this.pendingRequests.delete(id);\n pending.resolve(message);\n return;\n }\n\n // Handle server-initiated messages\n switch (type) {\n case \"tool_call\":\n this.handleToolCall(message as unknown as ToolCallMessage);\n break;\n\n case \"heartbeat_ack\":\n // Heartbeat acknowledged, no action needed\n break;\n\n case \"chat_stream\":\n // Handle streaming chat response\n this.handleChatStream(message as unknown as ChatStreamMessage);\n break;\n\n default:\n console.warn(`[SDK] Unknown message type: ${type}`);\n }\n }\n\n private handleChatStream(message: ChatStreamMessage): void {\n const { id, event, content, tool_call, tool_result, conversation_id, title, usage, error } = message;\n\n if (!id) return;\n\n const handler = this.streamHandlers.get(id);\n if (!handler) {\n console.warn(`[SDK] No stream handler for message ${id}`);\n return;\n }\n\n switch (event) {\n case \"text\":\n handler.onEvent({ type: \"text\", content });\n break;\n\n case \"tool_call\":\n handler.onEvent({ type: \"tool_call\", tool_call });\n break;\n\n case \"tool_result\":\n if (tool_result) {\n handler.onEvent({\n type: \"tool_call\",\n tool_call: {\n id: tool_result.call_id,\n type: \"function\",\n function: {\n name: \"tool_result\",\n arguments: JSON.stringify(tool_result),\n },\n },\n });\n }\n break;\n\n case \"done\":\n handler.onDone({\n message: message.message || { role: \"assistant\", content: \"\" },\n conversationId: conversation_id || \"\",\n title,\n usage,\n });\n break;\n\n case \"error\":\n handler.onError(new Error(error || \"Unknown stream error\"));\n break;\n }\n }\n\n private async handleToolCall(message: ToolCallMessage): Promise<void> {\n console.log(`[SDK] handleToolCall received:`, JSON.stringify(message));\n const { id, call_id, name, arguments: args } = message;\n const msgId = id || call_id;\n\n // Extract actual tool name (remove app prefix)\n const toolName = name.includes(\":\") ? name.split(\":\")[1] : name;\n console.log(`[SDK] Looking for handler: '${toolName}', available handlers:`, Array.from(this.toolHandlers.keys()));\n const handler = this.toolHandlers.get(toolName);\n\n // Emit event for debugging/logging\n this.emit(\"tool_call\", { name: toolName, arguments: args });\n\n if (!handler) {\n await this.sendToolResult(msgId, call_id, false, undefined, `Tool '${toolName}' not found`);\n return;\n }\n\n try {\n console.log(`[SDK] Executing tool '${toolName}' with args:`, args);\n // Execute with timeout\n const result = await Promise.race([\n handler(args),\n this.createTimeout(this.config.toolExecutionTimeout!),\n ]);\n\n console.log(`[SDK] Tool '${toolName}' completed successfully`);\n await this.sendToolResult(msgId, call_id, true, result);\n } catch (e) {\n const error = e instanceof Error ? e.message : String(e);\n console.log(`[SDK] Tool '${toolName}' failed:`, error);\n await this.sendToolResult(msgId, call_id, false, undefined, error);\n }\n }\n\n private async sendToolResult(\n id: string,\n callId: string,\n success: boolean,\n result?: unknown,\n error?: string\n ): Promise<void> {\n const message: ToolResultMessage = {\n id,\n type: \"tool_result\",\n call_id: callId,\n success,\n result,\n error,\n };\n\n console.log(`[SDK] Sending tool_result for ${callId}:`, success ? 'success' : `error: ${error}`);\n this.send(message);\n }\n\n // ============================================\n // Connection Management\n // ============================================\n\n private handleDisconnect(reason: string): void {\n this.stopHeartbeat();\n this.state.connected = false;\n this.state.registered = false;\n this.emit(\"disconnected\", reason);\n\n // Reject pending requests\n for (const [, pending] of this.pendingRequests) {\n pending.reject(new Error(\"Disconnected\"));\n }\n this.pendingRequests.clear();\n\n // Schedule reconnect\n this.scheduleReconnect();\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return;\n\n // Exponential backoff: 500ms, 1s, 2s, 4s, max 5s\n // With jitter to avoid thundering herd\n const baseDelay = Math.min(\n 500 * Math.pow(2, this.state.reconnectAttempts),\n 5000 // Cap at 5 seconds\n );\n const jitter = Math.random() * 500; // 0-500ms random jitter\n const delay = baseDelay + jitter;\n\n console.log(`[SDK] Scheduling reconnect in ${Math.round(delay)}ms (attempt ${this.state.reconnectAttempts + 1})`);\n\n this.reconnectTimer = setTimeout(async () => {\n this.reconnectTimer = null;\n this.state.reconnectAttempts++;\n\n // Re-read connection info\n const info = this.discovery.read();\n if (info) {\n try {\n await this.connectWithInfo(info);\n } catch (e) {\n console.error(\"[SDK] Reconnect failed:\", e);\n this.scheduleReconnect();\n }\n } else {\n // Sanqian not running, keep trying\n this.scheduleReconnect();\n }\n }, delay);\n }\n\n private stopReconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n // ============================================\n // Heartbeat\n // ============================================\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n\n this.heartbeatTimer = setInterval(() => {\n const message: HeartbeatMessage = {\n type: \"heartbeat\",\n timestamp: new Date().toISOString(),\n };\n this.send(message);\n }, this.config.heartbeatInterval);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n // ============================================\n // Communication\n // ============================================\n\n private send(message: object): void {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n const data = JSON.stringify(message);\n console.log(`[SDK] WebSocket send:`, data.substring(0, 200));\n this.ws.send(data);\n } else {\n console.error(`[SDK] WebSocket not open, cannot send:`, message);\n }\n }\n\n private sendAndWait<T>(\n message: object,\n id: string,\n timeout: number\n ): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(\"Request timeout\"));\n }, timeout);\n\n this.pendingRequests.set(id, {\n resolve: (value) => {\n clearTimeout(timer);\n resolve(value as T);\n },\n reject: (error) => {\n clearTimeout(timer);\n reject(error);\n },\n });\n\n this.send(message);\n });\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Get current connection state\n */\n getState(): ConnectionState {\n return { ...this.state };\n }\n\n /**\n * Check if connected and registered\n */\n isConnected(): boolean {\n return this.state.connected && this.state.registered;\n }\n\n /**\n * Wait for connection to be established.\n * Used internally by chat() and chatStream() for auto-reconnect.\n * Relies on background scheduleReconnect() to do the actual reconnection.\n */\n private async waitForConnection(timeout: number = 30000): Promise<void> {\n if (this.isConnected()) {\n return;\n }\n\n console.log(\"[SDK] Not connected, waiting for reconnection...\");\n\n // Wait for 'registered' event (emitted when connection + registration completes)\n // IMPORTANT: Register listener BEFORE triggering reconnect to avoid race condition\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n const timer = setTimeout(() => {\n if (!resolved) {\n resolved = true;\n reject(new Error(\"Connection timeout. Please ensure Sanqian is running.\"));\n }\n }, timeout);\n\n this.once(\"registered\", () => {\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n resolve();\n }\n });\n\n // Check again after registering listener (connection might have completed)\n if (this.isConnected()) {\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n resolve();\n }\n return;\n }\n\n // If no reconnect is scheduled, trigger one\n if (!this.reconnectTimer) {\n const info = this.discovery.read();\n if (!info && this.config.autoLaunchSanqian) {\n console.log(\"[SDK] Sanqian not running, attempting to launch...\");\n const launched = this.discovery.launchSanqian(this.config.sanqianPath);\n if (launched) {\n this.scheduleReconnect();\n }\n } else if (!info) {\n // No connection info and can't auto-launch\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n reject(new Error(\"Sanqian is not running. Please start it manually.\"));\n }\n } else {\n // Connection info exists, trigger reconnect\n this.scheduleReconnect();\n }\n }\n });\n }\n\n /**\n * Update tool list dynamically\n */\n async updateTools(tools: ToolDefinition[]): Promise<void> {\n // Update local handlers\n this.toolHandlers.clear();\n for (const tool of tools) {\n this.toolHandlers.set(tool.name, tool.handler);\n }\n\n // Send update to Sanqian\n if (this.isConnected()) {\n const msgId = this.generateId();\n const message = {\n id: msgId,\n type: \"tools_update\",\n tools: tools.map((t) => ({\n name: `${this.config.appName}:${t.name}`,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n await this.sendAndWait(message, msgId, 5000);\n }\n }\n\n // ============================================\n // Private Agent API\n // ============================================\n\n /**\n * Create or update a private agent\n *\n * Private agents are only visible to this SDK app, not in Sanqian UI.\n * If agent with same ID exists, it will be updated.\n *\n * @param config - Agent configuration\n * @returns Full agent info (or agent_id string for backward compatibility)\n */\n async createAgent(config: AgentConfig): Promise<AgentInfo> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: CreateAgentMessage = {\n id: msgId,\n type: \"create_agent\",\n agent: config,\n };\n\n const response = await this.sendAndWait<CreateAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to create agent\");\n }\n\n // Return full agent info if available, fallback to minimal info\n if (response.agent) {\n return response.agent;\n }\n\n // Backward compatibility: construct minimal AgentInfo from agent_id\n return {\n agent_id: response.agent_id!,\n name: config.name,\n description: config.description,\n tools: config.tools || [],\n };\n }\n\n /**\n * List all private agents owned by this app\n */\n async listAgents(): Promise<AgentInfo[]> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: ListAgentsMessage = {\n id: msgId,\n type: \"list_agents\",\n };\n\n const response = await this.sendAndWait<ListAgentsAckMessage>(message, msgId, 10000);\n\n if (response.error) {\n throw new Error(response.error);\n }\n\n return response.agents;\n }\n\n /**\n * Delete a private agent\n */\n async deleteAgent(agentId: string): Promise<void> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: DeleteAgentMessage = {\n id: msgId,\n type: \"delete_agent\",\n agent_id: agentId,\n };\n\n const response = await this.sendAndWait<DeleteAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to delete agent\");\n }\n }\n\n /**\n * Update a private agent\n *\n * Only updates the fields that are provided.\n * @param agentId - Agent ID (short name or full)\n * @param updates - Fields to update\n * @returns Updated agent info\n */\n async updateAgent(\n agentId: string,\n updates: Omit<AgentUpdateConfig, \"agent_id\">\n ): Promise<AgentInfo> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: UpdateAgentMessage = {\n id: msgId,\n type: \"update_agent\",\n agent: {\n agent_id: agentId,\n ...updates,\n },\n };\n\n const response = await this.sendAndWait<UpdateAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success || !response.agent) {\n throw new Error(response.error || \"Failed to update agent\");\n }\n\n return response.agent;\n }\n\n // ============================================\n // Conversation API\n // ============================================\n\n /**\n * List conversations for this app\n */\n async listConversations(options?: {\n agentId?: string;\n limit?: number;\n offset?: number;\n }): Promise<{ conversations: ConversationInfo[]; total: number }> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: ListConversationsMessage = {\n id: msgId,\n type: \"list_conversations\",\n agent_id: options?.agentId,\n limit: options?.limit,\n offset: options?.offset,\n };\n\n const response = await this.sendAndWait<ListConversationsAckMessage>(message, msgId, 10000);\n\n if (response.error) {\n throw new Error(response.error);\n }\n\n return {\n conversations: response.conversations,\n total: response.total,\n };\n }\n\n /**\n * Get conversation details with messages\n */\n async getConversation(\n conversationId: string,\n options?: {\n includeMessages?: boolean;\n messageLimit?: number;\n messageOffset?: number;\n }\n ): Promise<ConversationDetail> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: GetConversationMessage = {\n id: msgId,\n type: \"get_conversation\",\n conversation_id: conversationId,\n include_messages: options?.includeMessages ?? true,\n message_limit: options?.messageLimit,\n message_offset: options?.messageOffset,\n };\n\n const response = await this.sendAndWait<GetConversationAckMessage>(message, msgId, 10000);\n\n if (!response.success || !response.conversation) {\n throw new Error(response.error || \"Failed to get conversation\");\n }\n\n return response.conversation;\n }\n\n /**\n * Delete a conversation\n */\n async deleteConversation(conversationId: string): Promise<void> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: DeleteConversationMessage = {\n id: msgId,\n type: \"delete_conversation\",\n conversation_id: conversationId,\n };\n\n const response = await this.sendAndWait<DeleteConversationAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to delete conversation\");\n }\n }\n\n // ============================================\n // Chat API\n // ============================================\n\n // Pending stream handlers for streaming chat\n private streamHandlers: Map<string, {\n onEvent: (event: ChatStreamEvent) => void;\n onDone: (response: ChatResponse) => void;\n onError: (error: Error) => void;\n }> = new Map();\n\n /**\n * Send a chat message to an agent (non-streaming)\n *\n * Supports two modes:\n * - Stateless (no conversationId): Caller maintains message history\n * - Stateful (with conversationId): Server stores messages in conversations table\n *\n * @param agentId - Agent ID (short name or full, SDK auto-prefixes)\n * @param messages - Messages to send\n * @param options - Optional settings including conversationId and remoteTools\n * @returns Chat response with assistant message and conversation ID\n */\n async chat(\n agentId: string,\n messages: ChatMessage[],\n options?: {\n conversationId?: string;\n remoteTools?: RemoteToolDefinition[];\n }\n ): Promise<ChatResponse> {\n // Auto-reconnect if disconnected\n if (!this.isConnected()) {\n console.log(\"[SDK] Not connected, attempting to reconnect...\");\n await this.waitForConnection();\n }\n\n const msgId = this.generateId();\n const message: ChatRequestMessage = {\n id: msgId,\n type: \"chat\",\n agent_id: agentId,\n messages,\n conversation_id: options?.conversationId,\n stream: false,\n remote_tools: options?.remoteTools?.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n // Longer timeout for chat (agent may take time with complex multi-tool tasks)\n const response = await this.sendAndWait<ChatResponseMessage>(message, msgId, 600000); // 10 minutes\n\n if (!response.success) {\n throw new Error(response.error || \"Chat request failed\");\n }\n\n return {\n message: response.message!,\n conversationId: response.conversation_id!,\n title: response.title,\n usage: response.usage,\n };\n }\n\n /**\n * Send a chat message to an agent with streaming response\n *\n * Supports two modes:\n * - Stateless (no conversationId): Caller maintains message history\n * - Stateful (with conversationId): Server stores messages in conversations table\n *\n * @param agentId - Agent ID (short name or full, SDK auto-prefixes)\n * @param messages - Messages to send\n * @param options - Optional settings including conversationId and remoteTools\n * @returns AsyncIterable of stream events\n */\n async *chatStream(\n agentId: string,\n messages: ChatMessage[],\n options?: {\n conversationId?: string;\n remoteTools?: RemoteToolDefinition[];\n }\n ): AsyncGenerator<ChatStreamEvent> {\n // Auto-reconnect if disconnected\n if (!this.isConnected()) {\n console.log(\"[SDK] Not connected, attempting to reconnect...\");\n await this.waitForConnection();\n }\n\n const msgId = this.generateId();\n const message: ChatRequestMessage = {\n id: msgId,\n type: \"chat\",\n agent_id: agentId,\n messages,\n conversation_id: options?.conversationId,\n stream: true,\n remote_tools: options?.remoteTools?.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n // Create a queue to yield events\n const eventQueue: ChatStreamEvent[] = [];\n let done = false;\n let error: Error | null = null;\n let resolveNext: (() => void) | null = null;\n\n // Register stream handler\n this.streamHandlers.set(msgId, {\n onEvent: (event) => {\n eventQueue.push(event);\n resolveNext?.();\n },\n onDone: (response) => {\n eventQueue.push({\n type: \"done\",\n conversationId: response.conversationId,\n title: response.title,\n });\n done = true;\n resolveNext?.();\n },\n onError: (e) => {\n error = e;\n resolveNext?.();\n },\n });\n\n try {\n // Send request\n this.send(message);\n\n // Yield events as they arrive\n while (!done && !error) {\n if (eventQueue.length > 0) {\n yield eventQueue.shift()!;\n } else {\n // Wait for next event\n await new Promise<void>((resolve) => {\n resolveNext = resolve;\n });\n resolveNext = null;\n }\n }\n\n // Yield remaining events\n while (eventQueue.length > 0) {\n yield eventQueue.shift()!;\n }\n\n if (error) {\n throw error;\n }\n } finally {\n this.streamHandlers.delete(msgId);\n }\n }\n\n /**\n * Start a new conversation with an agent\n *\n * Returns a Conversation object for convenient multi-turn chat.\n *\n * @example\n * ```typescript\n * const conv = sdk.startConversation('assistant')\n * const r1 = await conv.send('Hello')\n * const r2 = await conv.send('Follow up question')\n * console.log(conv.id) // conversation ID\n * ```\n */\n startConversation(agentId: string): Conversation {\n return new Conversation(this, agentId);\n }\n\n // ============================================\n // Events\n // ============================================\n\n /**\n * Add event listener\n */\n on<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(listener as EventListener<SDKEventName>);\n return this;\n }\n\n /**\n * Remove event listener\n */\n off<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n this.eventListeners.get(event)?.delete(listener as EventListener<SDKEventName>);\n return this;\n }\n\n /**\n * Add one-time event listener (auto-removes after first call)\n */\n once<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n const onceWrapper = ((...args: Parameters<SDKEvents[T]>) => {\n this.off(event, onceWrapper as SDKEvents[T]);\n (listener as (...args: unknown[]) => void)(...args);\n }) as SDKEvents[T];\n return this.on(event, onceWrapper);\n }\n\n private emit<T extends SDKEventName>(\n event: T,\n ...args: Parameters<SDKEvents[T]>\n ): void {\n const listeners = this.eventListeners.get(event);\n if (listeners) {\n for (const listener of listeners) {\n try {\n (listener as (...args: unknown[]) => void)(...args);\n } catch (e) {\n console.error(`[SDK] Event listener error for '${event}':`, e);\n }\n }\n }\n }\n\n // ============================================\n // Utilities\n // ============================================\n\n private generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n private createTimeout(ms: number): Promise<never> {\n return new Promise((_, reject) => {\n setTimeout(() => reject(new Error(\"Timeout\")), ms);\n });\n }\n}\n\n/**\n * Conversation helper for multi-turn chat\n *\n * Automatically manages conversationId for stateful conversations.\n */\nexport class Conversation {\n private sdk: SanqianSDK;\n private agentId: string;\n private _conversationId: string | null = null;\n\n constructor(sdk: SanqianSDK, agentId: string, conversationId?: string) {\n this.sdk = sdk;\n this.agentId = agentId;\n this._conversationId = conversationId || null;\n }\n\n /**\n * Get the conversation ID (available after first message)\n */\n get id(): string | null {\n return this._conversationId;\n }\n\n /**\n * Send a message and get a response\n *\n * First call creates a new conversation, subsequent calls continue it.\n */\n async send(content: string, options?: { remoteTools?: RemoteToolDefinition[] }): Promise<ChatResponse> {\n const response = await this.sdk.chat(\n this.agentId,\n [{ role: \"user\", content }],\n {\n conversationId: this._conversationId || undefined,\n remoteTools: options?.remoteTools,\n }\n );\n\n // Store conversation ID for subsequent calls\n if (response.conversationId && !this._conversationId) {\n this._conversationId = response.conversationId;\n }\n\n return response;\n }\n\n /**\n * Send a message with streaming response\n */\n async *sendStream(\n content: string,\n options?: { remoteTools?: RemoteToolDefinition[] }\n ): AsyncGenerator<ChatStreamEvent> {\n const stream = this.sdk.chatStream(\n this.agentId,\n [{ role: \"user\", content }],\n {\n conversationId: this._conversationId || undefined,\n remoteTools: options?.remoteTools,\n }\n );\n\n for await (const event of stream) {\n // Capture conversation ID from done event\n if (event.type === \"done\" && event.conversationId && !this._conversationId) {\n this._conversationId = event.conversationId;\n }\n yield event;\n }\n }\n\n /**\n * Delete this conversation\n */\n async delete(): Promise<void> {\n if (!this._conversationId) {\n throw new Error(\"No conversation to delete\");\n }\n await this.sdk.deleteConversation(this._conversationId);\n this._conversationId = null;\n }\n\n /**\n * Get conversation details including message history\n */\n async getDetails(options?: { messageLimit?: number }): Promise<ConversationDetail> {\n if (!this._conversationId) {\n throw new Error(\"No conversation to get details for\");\n }\n return this.sdk.getConversation(this._conversationId, {\n includeMessages: true,\n messageLimit: options?.messageLimit,\n });\n }\n}\n","/**\n * Service Discovery Module\n *\n * Reads Sanqian's connection info from ~/.sanqian/runtime/connection.json\n * and monitors file changes for reconnection.\n * Also handles auto-launching Sanqian when needed.\n */\n\nimport { existsSync, readFileSync, watch, type FSWatcher } from \"fs\";\nimport { homedir, platform } from \"os\";\nimport { join } from \"path\";\nimport { spawn } from \"child_process\";\nimport type { ConnectionInfo } from \"./types\";\n\nexport class DiscoveryManager {\n private connectionInfo: ConnectionInfo | null = null;\n private watcher: FSWatcher | null = null;\n private onChange: ((info: ConnectionInfo | null) => void) | null = null;\n private pollInterval: ReturnType<typeof setInterval> | null = null;\n\n /**\n * Get the path to connection.json\n */\n getConnectionFilePath(): string {\n return join(homedir(), \".sanqian\", \"runtime\", \"connection.json\");\n }\n\n /**\n * Read and validate connection info\n *\n * Returns null if:\n * - File doesn't exist\n * - File is invalid JSON\n * - Process is not running\n */\n read(): ConnectionInfo | null {\n const filePath = this.getConnectionFilePath();\n\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(filePath, \"utf-8\");\n const info = JSON.parse(content) as ConnectionInfo;\n\n // Validate required fields\n if (!info.port || !info.token || !info.pid) {\n return null;\n }\n\n // Check if Sanqian process is running\n if (!this.isProcessRunning(info.pid)) {\n return null;\n }\n\n this.connectionInfo = info;\n return info;\n } catch {\n return null;\n }\n }\n\n /**\n * Start watching for connection file changes\n */\n startWatching(onChange: (info: ConnectionInfo | null) => void): void {\n this.onChange = onChange;\n\n const dir = join(homedir(), \".sanqian\", \"runtime\");\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n // Directory doesn't exist, poll for it\n this.pollInterval = setInterval(() => {\n if (existsSync(dir)) {\n if (this.pollInterval) {\n clearInterval(this.pollInterval);\n this.pollInterval = null;\n }\n this.setupWatcher(dir);\n }\n }, 2000);\n return;\n }\n\n this.setupWatcher(dir);\n }\n\n private setupWatcher(dir: string): void {\n try {\n this.watcher = watch(dir, (event, filename) => {\n if (filename === \"connection.json\") {\n // Debounce rapid changes\n setTimeout(() => {\n const newInfo = this.read();\n const oldInfoStr = JSON.stringify(this.connectionInfo);\n const newInfoStr = JSON.stringify(newInfo);\n\n // Only trigger if actually changed\n if (oldInfoStr !== newInfoStr) {\n this.connectionInfo = newInfo;\n this.onChange?.(newInfo);\n }\n }, 100);\n }\n });\n } catch (e) {\n console.error(\"Failed to watch connection file:\", e);\n }\n }\n\n /**\n * Stop watching\n */\n stopWatching(): void {\n if (this.pollInterval) {\n clearInterval(this.pollInterval);\n this.pollInterval = null;\n }\n this.watcher?.close();\n this.watcher = null;\n this.onChange = null;\n }\n\n /**\n * Check if a process is running by PID\n */\n private isProcessRunning(pid: number): boolean {\n try {\n // Sending signal 0 doesn't kill the process, just checks if it exists\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get cached connection info (may be stale)\n */\n getCached(): ConnectionInfo | null {\n return this.connectionInfo;\n }\n\n /**\n * Build WebSocket URL from connection info\n */\n buildWebSocketUrl(info: ConnectionInfo): string {\n return `ws://127.0.0.1:${info.port}${info.ws_path}?token=${info.token}`;\n }\n\n /**\n * Build HTTP base URL from connection info\n */\n buildHttpUrl(info: ConnectionInfo): string {\n return `http://127.0.0.1:${info.port}`;\n }\n\n /**\n * Find Sanqian executable path\n * Searches in standard installation locations for each platform\n */\n findSanqianPath(customPath?: string): string | null {\n // If custom path provided, use it\n if (customPath) {\n if (existsSync(customPath)) {\n return customPath;\n }\n console.warn(`[SDK] Custom Sanqian path not found: ${customPath}`);\n return null;\n }\n\n const os = platform();\n const searchPaths: string[] = [];\n\n if (os === \"darwin\") {\n // macOS: Standard app locations + common dev locations\n searchPaths.push(\n // Production: installed app\n \"/Applications/Sanqian.app/Contents/MacOS/Sanqian\",\n join(homedir(), \"Applications/Sanqian.app/Contents/MacOS/Sanqian\"),\n // Development: electron-builder output\n join(homedir(), \"dev/sanqian/dist/mac-arm64/Sanqian.app/Contents/MacOS/Sanqian\"),\n join(homedir(), \"dev/sanqian/dist/mac/Sanqian.app/Contents/MacOS/Sanqian\"),\n );\n } else if (os === \"win32\") {\n // Windows: Standard installation paths + dev locations\n const programFiles = process.env.PROGRAMFILES || \"C:\\\\Program Files\";\n const programFilesX86 =\n process.env[\"PROGRAMFILES(X86)\"] || \"C:\\\\Program Files (x86)\";\n const localAppData =\n process.env.LOCALAPPDATA || join(homedir(), \"AppData\", \"Local\");\n\n searchPaths.push(\n // Production: installed app\n join(programFiles, \"Sanqian\", \"Sanqian.exe\"),\n join(programFilesX86, \"Sanqian\", \"Sanqian.exe\"),\n join(localAppData, \"Programs\", \"Sanqian\", \"Sanqian.exe\"),\n // Development: electron-builder output\n join(homedir(), \"dev\", \"sanqian\", \"dist\", \"win-unpacked\", \"Sanqian.exe\"),\n );\n } else {\n // Linux: Common locations\n searchPaths.push(\n \"/usr/bin/sanqian\",\n \"/usr/local/bin/sanqian\",\n join(homedir(), \".local/bin/sanqian\"),\n \"/opt/Sanqian/sanqian\",\n // Development\n join(homedir(), \"dev/sanqian/dist/linux-unpacked/sanqian\"),\n );\n }\n\n for (const path of searchPaths) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n }\n\n /**\n * Launch Sanqian in hidden/tray mode\n * Returns true if launch was initiated successfully\n */\n launchSanqian(customPath?: string): boolean {\n const sanqianPath = this.findSanqianPath(customPath);\n\n if (!sanqianPath) {\n console.error(\"[SDK] Sanqian executable not found\");\n return false;\n }\n\n console.log(`[SDK] Launching Sanqian from: ${sanqianPath}`);\n\n try {\n const os = platform();\n\n if (os === \"darwin\") {\n // macOS: Use 'open' command with --args for hidden start\n // This properly launches the app bundle\n const appPath = sanqianPath.replace(\n \"/Contents/MacOS/Sanqian\",\n \"\",\n );\n spawn(\"open\", [\"-a\", appPath, \"--args\", \"--hidden\"], {\n detached: true,\n stdio: \"ignore\",\n }).unref();\n } else {\n // Windows/Linux: Direct execution with --hidden flag\n spawn(sanqianPath, [\"--hidden\"], {\n detached: true,\n stdio: \"ignore\",\n // On Windows, hide the console window\n ...(os === \"win32\" && {\n windowsHide: true,\n shell: false,\n }),\n }).unref();\n }\n\n return true;\n } catch (e) {\n console.error(\"[SDK] Failed to launch Sanqian:\", e);\n return false;\n }\n }\n\n /**\n * Check if Sanqian is running\n */\n isSanqianRunning(): boolean {\n return this.read() !== null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACMA,gBAAsB;;;ACEtB,gBAAgE;AAChE,gBAAkC;AAClC,kBAAqB;AACrB,2BAAsB;AAGf,IAAM,mBAAN,MAAuB;AAAA,EACpB,iBAAwC;AAAA,EACxC,UAA4B;AAAA,EAC5B,WAA2D;AAAA,EAC3D,eAAsD;AAAA;AAAA;AAAA;AAAA,EAK9D,wBAAgC;AAC9B,eAAO,sBAAK,mBAAQ,GAAG,YAAY,WAAW,iBAAiB;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,OAA8B;AAC5B,UAAM,WAAW,KAAK,sBAAsB;AAE5C,QAAI,KAAC,sBAAW,QAAQ,GAAG;AACzB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,cAAU,wBAAa,UAAU,OAAO;AAC9C,YAAM,OAAO,KAAK,MAAM,OAAO;AAG/B,UAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,SAAS,CAAC,KAAK,KAAK;AAC1C,eAAO;AAAA,MACT;AAGA,UAAI,CAAC,KAAK,iBAAiB,KAAK,GAAG,GAAG;AACpC,eAAO;AAAA,MACT;AAEA,WAAK,iBAAiB;AACtB,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,UAAuD;AACnE,SAAK,WAAW;AAEhB,UAAM,UAAM,sBAAK,mBAAQ,GAAG,YAAY,SAAS;AAGjD,QAAI,KAAC,sBAAW,GAAG,GAAG;AAEpB,WAAK,eAAe,YAAY,MAAM;AACpC,gBAAI,sBAAW,GAAG,GAAG;AACnB,cAAI,KAAK,cAAc;AACrB,0BAAc,KAAK,YAAY;AAC/B,iBAAK,eAAe;AAAA,UACtB;AACA,eAAK,aAAa,GAAG;AAAA,QACvB;AAAA,MACF,GAAG,GAAI;AACP;AAAA,IACF;AAEA,SAAK,aAAa,GAAG;AAAA,EACvB;AAAA,EAEQ,aAAa,KAAmB;AACtC,QAAI;AACF,WAAK,cAAU,iBAAM,KAAK,CAAC,OAAO,aAAa;AAC7C,YAAI,aAAa,mBAAmB;AAElC,qBAAW,MAAM;AACf,kBAAM,UAAU,KAAK,KAAK;AAC1B,kBAAM,aAAa,KAAK,UAAU,KAAK,cAAc;AACrD,kBAAM,aAAa,KAAK,UAAU,OAAO;AAGzC,gBAAI,eAAe,YAAY;AAC7B,mBAAK,iBAAiB;AACtB,mBAAK,WAAW,OAAO;AAAA,YACzB;AAAA,UACF,GAAG,GAAG;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH,SAAS,GAAG;AACV,cAAQ,MAAM,oCAAoC,CAAC;AAAA,IACrD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAqB;AACnB,QAAI,KAAK,cAAc;AACrB,oBAAc,KAAK,YAAY;AAC/B,WAAK,eAAe;AAAA,IACtB;AACA,SAAK,SAAS,MAAM;AACpB,SAAK,UAAU;AACf,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,KAAsB;AAC7C,QAAI;AAEF,cAAQ,KAAK,KAAK,CAAC;AACnB,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,YAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAkB,MAA8B;AAC9C,WAAO,kBAAkB,KAAK,IAAI,GAAG,KAAK,OAAO,UAAU,KAAK,KAAK;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,MAA8B;AACzC,WAAO,oBAAoB,KAAK,IAAI;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgB,YAAoC;AAElD,QAAI,YAAY;AACd,cAAI,sBAAW,UAAU,GAAG;AAC1B,eAAO;AAAA,MACT;AACA,cAAQ,KAAK,wCAAwC,UAAU,EAAE;AACjE,aAAO;AAAA,IACT;AAEA,UAAM,SAAK,oBAAS;AACpB,UAAM,cAAwB,CAAC;AAE/B,QAAI,OAAO,UAAU;AAEnB,kBAAY;AAAA;AAAA,QAEV;AAAA,YACA,sBAAK,mBAAQ,GAAG,iDAAiD;AAAA;AAAA,YAEjE,sBAAK,mBAAQ,GAAG,+DAA+D;AAAA,YAC/E,sBAAK,mBAAQ,GAAG,yDAAyD;AAAA,MAC3E;AAAA,IACF,WAAW,OAAO,SAAS;AAEzB,YAAM,eAAe,QAAQ,IAAI,gBAAgB;AACjD,YAAM,kBACJ,QAAQ,IAAI,mBAAmB,KAAK;AACtC,YAAM,eACJ,QAAQ,IAAI,oBAAgB,sBAAK,mBAAQ,GAAG,WAAW,OAAO;AAEhE,kBAAY;AAAA;AAAA,YAEV,kBAAK,cAAc,WAAW,aAAa;AAAA,YAC3C,kBAAK,iBAAiB,WAAW,aAAa;AAAA,YAC9C,kBAAK,cAAc,YAAY,WAAW,aAAa;AAAA;AAAA,YAEvD,sBAAK,mBAAQ,GAAG,OAAO,WAAW,QAAQ,gBAAgB,aAAa;AAAA,MACzE;AAAA,IACF,OAAO;AAEL,kBAAY;AAAA,QACV;AAAA,QACA;AAAA,YACA,sBAAK,mBAAQ,GAAG,oBAAoB;AAAA,QACpC;AAAA;AAAA,YAEA,sBAAK,mBAAQ,GAAG,yCAAyC;AAAA,MAC3D;AAAA,IACF;AAEA,eAAW,QAAQ,aAAa;AAC9B,cAAI,sBAAW,IAAI,GAAG;AACpB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAc,YAA8B;AAC1C,UAAM,cAAc,KAAK,gBAAgB,UAAU;AAEnD,QAAI,CAAC,aAAa;AAChB,cAAQ,MAAM,oCAAoC;AAClD,aAAO;AAAA,IACT;AAEA,YAAQ,IAAI,iCAAiC,WAAW,EAAE;AAE1D,QAAI;AACF,YAAM,SAAK,oBAAS;AAEpB,UAAI,OAAO,UAAU;AAGnB,cAAM,UAAU,YAAY;AAAA,UAC1B;AAAA,UACA;AAAA,QACF;AACA,wCAAM,QAAQ,CAAC,MAAM,SAAS,UAAU,UAAU,GAAG;AAAA,UACnD,UAAU;AAAA,UACV,OAAO;AAAA,QACT,CAAC,EAAE,MAAM;AAAA,MACX,OAAO;AAEL,wCAAM,aAAa,CAAC,UAAU,GAAG;AAAA,UAC/B,UAAU;AAAA,UACV,OAAO;AAAA;AAAA,UAEP,GAAI,OAAO,WAAW;AAAA,YACpB,aAAa;AAAA,YACb,OAAO;AAAA,UACT;AAAA,QACF,CAAC,EAAE,MAAM;AAAA,MACX;AAEA,aAAO;AAAA,IACT,SAAS,GAAG;AACV,cAAQ,MAAM,mCAAmC,CAAC;AAClD,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,mBAA4B;AAC1B,WAAO,KAAK,KAAK,MAAM;AAAA,EACzB;AACF;;;ADlOO,IAAM,aAAN,MAAiB;AAAA,EACd;AAAA,EACA;AAAA,EACA,KAAuB;AAAA,EACvB,iBAAwC;AAAA,EAExC,QAAyB;AAAA,IAC/B,WAAW;AAAA,IACX,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,mBAAmB;AAAA,EACrB;AAAA;AAAA,EAGQ,eACN,oBAAI,IAAI;AAAA;AAAA,EAGF,kBAMJ,oBAAI,IAAI;AAAA;AAAA,EAGJ,iBAAwD;AAAA,EACxD,iBAAuD;AAAA;AAAA,EAGvD,iBACN,oBAAI,IAAI;AAAA,EAEV,YAAY,QAAmB;AAC7B,SAAK,SAAS;AAAA,MACZ,mBAAmB;AAAA,MACnB,mBAAmB;AAAA,MACnB,sBAAsB;AAAA,MACtB,GAAG;AAAA,IACL;AACA,SAAK,YAAY,IAAI,iBAAiB;AAGtC,eAAW,QAAQ,OAAO,OAAO;AAC/B,WAAK,aAAa,IAAI,KAAK,MAAM,KAAK,OAAO;AAAA,IAC/C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,UAAyB;AAE7B,UAAM,OAAO,KAAK,UAAU,KAAK;AAEjC,QAAI,CAAC,MAAM;AAGT,UAAI,KAAK,OAAO,mBAAmB;AACjC,gBAAQ,IAAI,oDAAoD;AAChE,cAAM,WAAW,KAAK,UAAU,cAAc,KAAK,OAAO,WAAW;AACrE,YAAI,UAAU;AACZ,kBAAQ,IAAI,wDAAwD;AAAA,QACtE,OAAO;AACL,kBAAQ,KAAK,4DAA4D;AAAA,QAC3E;AAAA,MACF;AAGA,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAQ,IAAI,8BAA8B;AAE1C,cAAM,UAAU,WAAW,MAAM;AAC/B,eAAK,UAAU,aAAa;AAC5B,iBAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,QAChD,GAAG,GAAK;AAER,aAAK,UAAU,cAAc,OAAO,YAAY;AAC9C,cAAI,SAAS;AACX,yBAAa,OAAO;AACpB,iBAAK,UAAU,aAAa;AAC5B,gBAAI;AACF,oBAAM,KAAK,gBAAgB,OAAO;AAClC,sBAAQ;AAAA,YACV,SAAS,GAAG;AACV,qBAAO,CAAC;AAAA,YACV;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,UAAM,KAAK,gBAAgB,IAAI;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBAAgB,MAAqC;AACjE,SAAK,iBAAiB;AACtB,UAAM,MAAM,KAAK,UAAU,kBAAkB,IAAI;AAEjD,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAQ,IAAI,uBAAuB,GAAG,EAAE;AAExC,WAAK,KAAK,IAAI,UAAAA,QAAU,GAAG;AAE3B,YAAM,iBAAiB,WAAW,MAAM;AACtC,eAAO,IAAI,MAAM,8BAA8B,CAAC;AAChD,aAAK,IAAI,MAAM;AAAA,MACjB,GAAG,GAAK;AAER,WAAK,GAAG,GAAG,QAAQ,YAAY;AAC7B,qBAAa,cAAc;AAC3B,gBAAQ,IAAI,2BAA2B;AACvC,aAAK,MAAM,YAAY;AACvB,aAAK,MAAM,oBAAoB;AAC/B,aAAK,KAAK,WAAW;AAErB,YAAI;AACF,gBAAM,KAAK,SAAS;AACpB,kBAAQ;AAAA,QACV,SAAS,GAAG;AACV,iBAAO,CAAC;AAAA,QACV;AAAA,MACF,CAAC;AAED,WAAK,GAAG,GAAG,SAAS,CAAC,MAAM,WAAW;AACpC,gBAAQ,IAAI,2BAA2B,IAAI,IAAI,OAAO,SAAS,CAAC,EAAE;AAClE,aAAK,iBAAiB,OAAO,SAAS,CAAC;AAAA,MACzC,CAAC;AAED,WAAK,GAAG,GAAG,SAAS,CAAC,UAAU;AAC7B,gBAAQ,MAAM,0BAA0B,KAAK;AAC7C,aAAK,MAAM,YAAY;AACvB,aAAK,KAAK,SAAS,KAAK;AAAA,MAC1B,CAAC;AAED,WAAK,GAAG,GAAG,WAAW,CAAC,SAAS;AAC9B,YAAI;AACF,gBAAM,UAAU,KAAK,MAAM,KAAK,SAAS,CAAC;AAC1C,eAAK,cAAc,OAAO;AAAA,QAC5B,SAAS,GAAG;AACV,kBAAQ,MAAM,kCAAkC,CAAC;AAAA,QACnD;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAA4B;AAChC,SAAK,cAAc;AACnB,SAAK,cAAc;AACnB,SAAK,UAAU,aAAa;AAE5B,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM,KAAM,mBAAmB;AACvC,WAAK,KAAK;AAAA,IACZ;AAEA,SAAK,QAAQ;AAAA,MACX,WAAW;AAAA,MACX,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,mBAAmB;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,WAA0B;AACtC,SAAK,MAAM,cAAc;AAEzB,UAAM,QAAQ,KAAK,WAAW;AAE9B,UAAM,UAA2B;AAAA,MAC/B,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,KAAK;AAAA,QACH,MAAM,KAAK,OAAO;AAAA,QAClB,SAAS,KAAK,OAAO;AAAA,QACrB,cAAc,KAAK,OAAO;AAAA,QAC1B,gBAAgB,KAAK,OAAO;AAAA,MAC9B;AAAA,MACA,OAAO,KAAK,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,QACnC,MAAM,GAAG,KAAK,OAAO,OAAO,IAAI,EAAE,IAAI;AAAA,QACtC,aAAa,EAAE;AAAA,QACf,YAAY,EAAE;AAAA,MAChB,EAAE;AAAA,IACJ;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,KAAK;AAAA,QAC1B;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,UAAI,CAAC,SAAS,SAAS;AACrB,cAAM,IAAI,MAAM,SAAS,SAAS,qBAAqB;AAAA,MACzD;AAEA,WAAK,MAAM,cAAc;AACzB,WAAK,MAAM,aAAa;AACxB,WAAK,eAAe;AACpB,WAAK,KAAK,YAAY;AAEtB,cAAQ,IAAI,wBAAwB,KAAK,OAAO,OAAO,GAAG;AAAA,IAC5D,SAAS,GAAG;AACV,WAAK,MAAM,cAAc;AACzB,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAc,SAAsE;AAC1F,UAAM,EAAE,IAAI,KAAK,IAAI;AAGrB,QAAI,MAAM,KAAK,gBAAgB,IAAI,EAAE,GAAG;AACtC,YAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,WAAK,gBAAgB,OAAO,EAAE;AAC9B,cAAQ,QAAQ,OAAO;AACvB;AAAA,IACF;AAGA,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,aAAK,eAAe,OAAqC;AACzD;AAAA,MAEF,KAAK;AAEH;AAAA,MAEF,KAAK;AAEH,aAAK,iBAAiB,OAAuC;AAC7D;AAAA,MAEF;AACE,gBAAQ,KAAK,+BAA+B,IAAI,EAAE;AAAA,IACtD;AAAA,EACF;AAAA,EAEQ,iBAAiB,SAAkC;AACzD,UAAM,EAAE,IAAI,OAAO,SAAS,WAAW,aAAa,iBAAiB,OAAO,OAAO,MAAM,IAAI;AAE7F,QAAI,CAAC,GAAI;AAET,UAAM,UAAU,KAAK,eAAe,IAAI,EAAE;AAC1C,QAAI,CAAC,SAAS;AACZ,cAAQ,KAAK,uCAAuC,EAAE,EAAE;AACxD;AAAA,IACF;AAEA,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,gBAAQ,QAAQ,EAAE,MAAM,QAAQ,QAAQ,CAAC;AACzC;AAAA,MAEF,KAAK;AACH,gBAAQ,QAAQ,EAAE,MAAM,aAAa,UAAU,CAAC;AAChD;AAAA,MAEF,KAAK;AACH,YAAI,aAAa;AACf,kBAAQ,QAAQ;AAAA,YACd,MAAM;AAAA,YACN,WAAW;AAAA,cACT,IAAI,YAAY;AAAA,cAChB,MAAM;AAAA,cACN,UAAU;AAAA,gBACR,MAAM;AAAA,gBACN,WAAW,KAAK,UAAU,WAAW;AAAA,cACvC;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AACA;AAAA,MAEF,KAAK;AACH,gBAAQ,OAAO;AAAA,UACb,SAAS,QAAQ,WAAW,EAAE,MAAM,aAAa,SAAS,GAAG;AAAA,UAC7D,gBAAgB,mBAAmB;AAAA,UACnC;AAAA,UACA;AAAA,QACF,CAAC;AACD;AAAA,MAEF,KAAK;AACH,gBAAQ,QAAQ,IAAI,MAAM,SAAS,sBAAsB,CAAC;AAC1D;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,SAAyC;AACpE,YAAQ,IAAI,kCAAkC,KAAK,UAAU,OAAO,CAAC;AACrE,UAAM,EAAE,IAAI,SAAS,MAAM,WAAW,KAAK,IAAI;AAC/C,UAAM,QAAQ,MAAM;AAGpB,UAAM,WAAW,KAAK,SAAS,GAAG,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC,IAAI;AAC3D,YAAQ,IAAI,+BAA+B,QAAQ,0BAA0B,MAAM,KAAK,KAAK,aAAa,KAAK,CAAC,CAAC;AACjH,UAAM,UAAU,KAAK,aAAa,IAAI,QAAQ;AAG9C,SAAK,KAAK,aAAa,EAAE,MAAM,UAAU,WAAW,KAAK,CAAC;AAE1D,QAAI,CAAC,SAAS;AACZ,YAAM,KAAK,eAAe,OAAO,SAAS,OAAO,QAAW,SAAS,QAAQ,aAAa;AAC1F;AAAA,IACF;AAEA,QAAI;AACF,cAAQ,IAAI,yBAAyB,QAAQ,gBAAgB,IAAI;AAEjE,YAAM,SAAS,MAAM,QAAQ,KAAK;AAAA,QAChC,QAAQ,IAAI;AAAA,QACZ,KAAK,cAAc,KAAK,OAAO,oBAAqB;AAAA,MACtD,CAAC;AAED,cAAQ,IAAI,eAAe,QAAQ,0BAA0B;AAC7D,YAAM,KAAK,eAAe,OAAO,SAAS,MAAM,MAAM;AAAA,IACxD,SAAS,GAAG;AACV,YAAM,QAAQ,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACvD,cAAQ,IAAI,eAAe,QAAQ,aAAa,KAAK;AACrD,YAAM,KAAK,eAAe,OAAO,SAAS,OAAO,QAAW,KAAK;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,MAAc,eACZ,IACA,QACA,SACA,QACA,OACe;AACf,UAAM,UAA6B;AAAA,MACjC;AAAA,MACA,MAAM;AAAA,MACN,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,YAAQ,IAAI,iCAAiC,MAAM,KAAK,UAAU,YAAY,UAAU,KAAK,EAAE;AAC/F,SAAK,KAAK,OAAO;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAMQ,iBAAiB,QAAsB;AAC7C,SAAK,cAAc;AACnB,SAAK,MAAM,YAAY;AACvB,SAAK,MAAM,aAAa;AACxB,SAAK,KAAK,gBAAgB,MAAM;AAGhC,eAAW,CAAC,EAAE,OAAO,KAAK,KAAK,iBAAiB;AAC9C,cAAQ,OAAO,IAAI,MAAM,cAAc,CAAC;AAAA,IAC1C;AACA,SAAK,gBAAgB,MAAM;AAG3B,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEQ,oBAA0B;AAChC,QAAI,KAAK,eAAgB;AAIzB,UAAM,YAAY,KAAK;AAAA,MACrB,MAAM,KAAK,IAAI,GAAG,KAAK,MAAM,iBAAiB;AAAA,MAC9C;AAAA;AAAA,IACF;AACA,UAAM,SAAS,KAAK,OAAO,IAAI;AAC/B,UAAM,QAAQ,YAAY;AAE1B,YAAQ,IAAI,iCAAiC,KAAK,MAAM,KAAK,CAAC,eAAe,KAAK,MAAM,oBAAoB,CAAC,GAAG;AAEhH,SAAK,iBAAiB,WAAW,YAAY;AAC3C,WAAK,iBAAiB;AACtB,WAAK,MAAM;AAGX,YAAM,OAAO,KAAK,UAAU,KAAK;AACjC,UAAI,MAAM;AACR,YAAI;AACF,gBAAM,KAAK,gBAAgB,IAAI;AAAA,QACjC,SAAS,GAAG;AACV,kBAAQ,MAAM,2BAA2B,CAAC;AAC1C,eAAK,kBAAkB;AAAA,QACzB;AAAA,MACF,OAAO;AAEL,aAAK,kBAAkB;AAAA,MACzB;AAAA,IACF,GAAG,KAAK;AAAA,EACV;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,gBAAgB;AACvB,mBAAa,KAAK,cAAc;AAChC,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,iBAAuB;AAC7B,SAAK,cAAc;AAEnB,SAAK,iBAAiB,YAAY,MAAM;AACtC,YAAM,UAA4B;AAAA,QAChC,MAAM;AAAA,QACN,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC;AACA,WAAK,KAAK,OAAO;AAAA,IACnB,GAAG,KAAK,OAAO,iBAAiB;AAAA,EAClC;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,gBAAgB;AACvB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,KAAK,SAAuB;AAClC,QAAI,KAAK,MAAM,KAAK,GAAG,eAAe,UAAAA,QAAU,MAAM;AACpD,YAAM,OAAO,KAAK,UAAU,OAAO;AACnC,cAAQ,IAAI,yBAAyB,KAAK,UAAU,GAAG,GAAG,CAAC;AAC3D,WAAK,GAAG,KAAK,IAAI;AAAA,IACnB,OAAO;AACL,cAAQ,MAAM,0CAA0C,OAAO;AAAA,IACjE;AAAA,EACF;AAAA,EAEQ,YACN,SACA,IACA,SACY;AACZ,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,gBAAgB,OAAO,EAAE;AAC9B,eAAO,IAAI,MAAM,iBAAiB,CAAC;AAAA,MACrC,GAAG,OAAO;AAEV,WAAK,gBAAgB,IAAI,IAAI;AAAA,QAC3B,SAAS,CAAC,UAAU;AAClB,uBAAa,KAAK;AAClB,kBAAQ,KAAU;AAAA,QACpB;AAAA,QACA,QAAQ,CAAC,UAAU;AACjB,uBAAa,KAAK;AAClB,iBAAO,KAAK;AAAA,QACd;AAAA,MACF,CAAC;AAED,WAAK,KAAK,OAAO;AAAA,IACnB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,WAA4B;AAC1B,WAAO,EAAE,GAAG,KAAK,MAAM;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAuB;AACrB,WAAO,KAAK,MAAM,aAAa,KAAK,MAAM;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,kBAAkB,UAAkB,KAAsB;AACtE,QAAI,KAAK,YAAY,GAAG;AACtB;AAAA,IACF;AAEA,YAAQ,IAAI,kDAAkD;AAI9D,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,WAAW;AAEf,YAAM,QAAQ,WAAW,MAAM;AAC7B,YAAI,CAAC,UAAU;AACb,qBAAW;AACX,iBAAO,IAAI,MAAM,uDAAuD,CAAC;AAAA,QAC3E;AAAA,MACF,GAAG,OAAO;AAEV,WAAK,KAAK,cAAc,MAAM;AAC5B,YAAI,CAAC,UAAU;AACb,qBAAW;AACX,uBAAa,KAAK;AAClB,kBAAQ;AAAA,QACV;AAAA,MACF,CAAC;AAGD,UAAI,KAAK,YAAY,GAAG;AACtB,YAAI,CAAC,UAAU;AACb,qBAAW;AACX,uBAAa,KAAK;AAClB,kBAAQ;AAAA,QACV;AACA;AAAA,MACF;AAGA,UAAI,CAAC,KAAK,gBAAgB;AACxB,cAAM,OAAO,KAAK,UAAU,KAAK;AACjC,YAAI,CAAC,QAAQ,KAAK,OAAO,mBAAmB;AAC1C,kBAAQ,IAAI,oDAAoD;AAChE,gBAAM,WAAW,KAAK,UAAU,cAAc,KAAK,OAAO,WAAW;AACrE,cAAI,UAAU;AACZ,iBAAK,kBAAkB;AAAA,UACzB;AAAA,QACF,WAAW,CAAC,MAAM;AAEhB,cAAI,CAAC,UAAU;AACb,uBAAW;AACX,yBAAa,KAAK;AAClB,mBAAO,IAAI,MAAM,mDAAmD,CAAC;AAAA,UACvE;AAAA,QACF,OAAO;AAEL,eAAK,kBAAkB;AAAA,QACzB;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,OAAwC;AAExD,SAAK,aAAa,MAAM;AACxB,eAAW,QAAQ,OAAO;AACxB,WAAK,aAAa,IAAI,KAAK,MAAM,KAAK,OAAO;AAAA,IAC/C;AAGA,QAAI,KAAK,YAAY,GAAG;AACtB,YAAM,QAAQ,KAAK,WAAW;AAC9B,YAAM,UAAU;AAAA,QACd,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,UACvB,MAAM,GAAG,KAAK,OAAO,OAAO,IAAI,EAAE,IAAI;AAAA,UACtC,aAAa,EAAE;AAAA,UACf,YAAY,EAAE;AAAA,QAChB,EAAE;AAAA,MACJ;AAEA,YAAM,KAAK,YAAY,SAAS,OAAO,GAAI;AAAA,IAC7C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,YAAY,QAAyC;AACzD,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,UAA8B;AAAA,MAClC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAEA,UAAM,WAAW,MAAM,KAAK,YAAmC,SAAS,OAAO,GAAK;AAEpF,QAAI,CAAC,SAAS,SAAS;AACrB,YAAM,IAAI,MAAM,SAAS,SAAS,wBAAwB;AAAA,IAC5D;AAGA,QAAI,SAAS,OAAO;AAClB,aAAO,SAAS;AAAA,IAClB;AAGA,WAAO;AAAA,MACL,UAAU,SAAS;AAAA,MACnB,MAAM,OAAO;AAAA,MACb,aAAa,OAAO;AAAA,MACpB,OAAO,OAAO,SAAS,CAAC;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAmC;AACvC,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,UAA6B;AAAA,MACjC,IAAI;AAAA,MACJ,MAAM;AAAA,IACR;AAEA,UAAM,WAAW,MAAM,KAAK,YAAkC,SAAS,OAAO,GAAK;AAEnF,QAAI,SAAS,OAAO;AAClB,YAAM,IAAI,MAAM,SAAS,KAAK;AAAA,IAChC;AAEA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,SAAgC;AAChD,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,UAA8B;AAAA,MAClC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAEA,UAAM,WAAW,MAAM,KAAK,YAAmC,SAAS,OAAO,GAAK;AAEpF,QAAI,CAAC,SAAS,SAAS;AACrB,YAAM,IAAI,MAAM,SAAS,SAAS,wBAAwB;AAAA,IAC5D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,YACJ,SACA,SACoB;AACpB,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,UAA8B;AAAA,MAClC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,QACL,UAAU;AAAA,QACV,GAAG;AAAA,MACL;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,KAAK,YAAmC,SAAS,OAAO,GAAK;AAEpF,QAAI,CAAC,SAAS,WAAW,CAAC,SAAS,OAAO;AACxC,YAAM,IAAI,MAAM,SAAS,SAAS,wBAAwB;AAAA,IAC5D;AAEA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,kBAAkB,SAI0C;AAChE,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,UAAoC;AAAA,MACxC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,UAAU,SAAS;AAAA,MACnB,OAAO,SAAS;AAAA,MAChB,QAAQ,SAAS;AAAA,IACnB;AAEA,UAAM,WAAW,MAAM,KAAK,YAAyC,SAAS,OAAO,GAAK;AAE1F,QAAI,SAAS,OAAO;AAClB,YAAM,IAAI,MAAM,SAAS,KAAK;AAAA,IAChC;AAEA,WAAO;AAAA,MACL,eAAe,SAAS;AAAA,MACxB,OAAO,SAAS;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,gBACA,SAK6B;AAC7B,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,UAAkC;AAAA,MACtC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,iBAAiB;AAAA,MACjB,kBAAkB,SAAS,mBAAmB;AAAA,MAC9C,eAAe,SAAS;AAAA,MACxB,gBAAgB,SAAS;AAAA,IAC3B;AAEA,UAAM,WAAW,MAAM,KAAK,YAAuC,SAAS,OAAO,GAAK;AAExF,QAAI,CAAC,SAAS,WAAW,CAAC,SAAS,cAAc;AAC/C,YAAM,IAAI,MAAM,SAAS,SAAS,4BAA4B;AAAA,IAChE;AAEA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBAAmB,gBAAuC;AAC9D,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,UAAqC;AAAA,MACzC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,iBAAiB;AAAA,IACnB;AAEA,UAAM,WAAW,MAAM,KAAK,YAA0C,SAAS,OAAO,GAAK;AAE3F,QAAI,CAAC,SAAS,SAAS;AACrB,YAAM,IAAI,MAAM,SAAS,SAAS,+BAA+B;AAAA,IACnE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAIH,oBAAI,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcb,MAAM,KACJ,SACA,UACA,SAIuB;AAEvB,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,cAAQ,IAAI,iDAAiD;AAC7D,YAAM,KAAK,kBAAkB;AAAA,IAC/B;AAEA,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,UAA8B;AAAA,MAClC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS;AAAA,MAC1B,QAAQ;AAAA,MACR,cAAc,SAAS,aAAa,IAAI,CAAC,OAAO;AAAA,QAC9C,MAAM,EAAE;AAAA,QACR,aAAa,EAAE;AAAA,QACf,YAAY,EAAE;AAAA,MAChB,EAAE;AAAA,IACJ;AAGA,UAAM,WAAW,MAAM,KAAK,YAAiC,SAAS,OAAO,GAAM;AAEnF,QAAI,CAAC,SAAS,SAAS;AACrB,YAAM,IAAI,MAAM,SAAS,SAAS,qBAAqB;AAAA,IACzD;AAEA,WAAO;AAAA,MACL,SAAS,SAAS;AAAA,MAClB,gBAAgB,SAAS;AAAA,MACzB,OAAO,SAAS;AAAA,MAChB,OAAO,SAAS;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,OAAO,WACL,SACA,UACA,SAIiC;AAEjC,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,cAAQ,IAAI,iDAAiD;AAC7D,YAAM,KAAK,kBAAkB;AAAA,IAC/B;AAEA,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,UAA8B;AAAA,MAClC,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,MACV;AAAA,MACA,iBAAiB,SAAS;AAAA,MAC1B,QAAQ;AAAA,MACR,cAAc,SAAS,aAAa,IAAI,CAAC,OAAO;AAAA,QAC9C,MAAM,EAAE;AAAA,QACR,aAAa,EAAE;AAAA,QACf,YAAY,EAAE;AAAA,MAChB,EAAE;AAAA,IACJ;AAGA,UAAM,aAAgC,CAAC;AACvC,QAAI,OAAO;AACX,QAAI,QAAsB;AAC1B,QAAI,cAAmC;AAGvC,SAAK,eAAe,IAAI,OAAO;AAAA,MAC7B,SAAS,CAAC,UAAU;AAClB,mBAAW,KAAK,KAAK;AACrB,sBAAc;AAAA,MAChB;AAAA,MACA,QAAQ,CAAC,aAAa;AACpB,mBAAW,KAAK;AAAA,UACd,MAAM;AAAA,UACN,gBAAgB,SAAS;AAAA,UACzB,OAAO,SAAS;AAAA,QAClB,CAAC;AACD,eAAO;AACP,sBAAc;AAAA,MAChB;AAAA,MACA,SAAS,CAAC,MAAM;AACd,gBAAQ;AACR,sBAAc;AAAA,MAChB;AAAA,IACF,CAAC;AAED,QAAI;AAEF,WAAK,KAAK,OAAO;AAGjB,aAAO,CAAC,QAAQ,CAAC,OAAO;AACtB,YAAI,WAAW,SAAS,GAAG;AACzB,gBAAM,WAAW,MAAM;AAAA,QACzB,OAAO;AAEL,gBAAM,IAAI,QAAc,CAAC,YAAY;AACnC,0BAAc;AAAA,UAChB,CAAC;AACD,wBAAc;AAAA,QAChB;AAAA,MACF;AAGA,aAAO,WAAW,SAAS,GAAG;AAC5B,cAAM,WAAW,MAAM;AAAA,MACzB;AAEA,UAAI,OAAO;AACT,cAAM;AAAA,MACR;AAAA,IACF,UAAE;AACA,WAAK,eAAe,OAAO,KAAK;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,kBAAkB,SAA+B;AAC/C,WAAO,IAAI,aAAa,MAAM,OAAO;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,GAA2B,OAAU,UAA8B;AACjE,QAAI,CAAC,KAAK,eAAe,IAAI,KAAK,GAAG;AACnC,WAAK,eAAe,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IAC1C;AACA,SAAK,eAAe,IAAI,KAAK,EAAG,IAAI,QAAuC;AAC3E,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,IAA4B,OAAU,UAA8B;AAClE,SAAK,eAAe,IAAI,KAAK,GAAG,OAAO,QAAuC;AAC9E,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,KAA6B,OAAU,UAA8B;AACnE,UAAM,eAAe,IAAI,SAAmC;AAC1D,WAAK,IAAI,OAAO,WAA2B;AAC3C,MAAC,SAA0C,GAAG,IAAI;AAAA,IACpD;AACA,WAAO,KAAK,GAAG,OAAO,WAAW;AAAA,EACnC;AAAA,EAEQ,KACN,UACG,MACG;AACN,UAAM,YAAY,KAAK,eAAe,IAAI,KAAK;AAC/C,QAAI,WAAW;AACb,iBAAW,YAAY,WAAW;AAChC,YAAI;AACF,UAAC,SAA0C,GAAG,IAAI;AAAA,QACpD,SAAS,GAAG;AACV,kBAAQ,MAAM,mCAAmC,KAAK,MAAM,CAAC;AAAA,QAC/D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,aAAqB;AAC3B,WAAO,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EACpE;AAAA,EAEQ,cAAc,IAA4B;AAChD,WAAO,IAAI,QAAQ,CAAC,GAAG,WAAW;AAChC,iBAAW,MAAM,OAAO,IAAI,MAAM,SAAS,CAAC,GAAG,EAAE;AAAA,IACnD,CAAC;AAAA,EACH;AACF;AAOO,IAAM,eAAN,MAAmB;AAAA,EAChB;AAAA,EACA;AAAA,EACA,kBAAiC;AAAA,EAEzC,YAAY,KAAiB,SAAiB,gBAAyB;AACrE,SAAK,MAAM;AACX,SAAK,UAAU;AACf,SAAK,kBAAkB,kBAAkB;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,KAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAK,SAAiB,SAA2E;AACrG,UAAM,WAAW,MAAM,KAAK,IAAI;AAAA,MAC9B,KAAK;AAAA,MACL,CAAC,EAAE,MAAM,QAAQ,QAAQ,CAAC;AAAA,MAC1B;AAAA,QACE,gBAAgB,KAAK,mBAAmB;AAAA,QACxC,aAAa,SAAS;AAAA,MACxB;AAAA,IACF;AAGA,QAAI,SAAS,kBAAkB,CAAC,KAAK,iBAAiB;AACpD,WAAK,kBAAkB,SAAS;AAAA,IAClC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,WACL,SACA,SACiC;AACjC,UAAM,SAAS,KAAK,IAAI;AAAA,MACtB,KAAK;AAAA,MACL,CAAC,EAAE,MAAM,QAAQ,QAAQ,CAAC;AAAA,MAC1B;AAAA,QACE,gBAAgB,KAAK,mBAAmB;AAAA,QACxC,aAAa,SAAS;AAAA,MACxB;AAAA,IACF;AAEA,qBAAiB,SAAS,QAAQ;AAEhC,UAAI,MAAM,SAAS,UAAU,MAAM,kBAAkB,CAAC,KAAK,iBAAiB;AAC1E,aAAK,kBAAkB,MAAM;AAAA,MAC/B;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAwB;AAC5B,QAAI,CAAC,KAAK,iBAAiB;AACzB,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AACA,UAAM,KAAK,IAAI,mBAAmB,KAAK,eAAe;AACtD,SAAK,kBAAkB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,SAAkE;AACjF,QAAI,CAAC,KAAK,iBAAiB;AACzB,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AACA,WAAO,KAAK,IAAI,gBAAgB,KAAK,iBAAiB;AAAA,MACpD,iBAAiB;AAAA,MACjB,cAAc,SAAS;AAAA,IACzB,CAAC;AAAA,EACH;AACF;","names":["WebSocket"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/discovery.ts"],"sourcesContent":["/**\n * Sanqian SDK\n *\n * TypeScript SDK for integrating third-party apps with Sanqian AI Assistant.\n *\n * @example\n * ```typescript\n * import { SanqianSDK } from '@yushaw/sanqian-sdk';\n *\n * const sdk = new SanqianSDK({\n * appName: 'my-app',\n * appVersion: '1.0.0',\n * tools: [\n * {\n * name: 'my_tool',\n * description: 'Does something useful',\n * parameters: { type: 'object', properties: {} },\n * handler: async (args) => {\n * return { success: true };\n * }\n * }\n * ]\n * });\n *\n * await sdk.connect();\n * ```\n */\n\nexport { SanqianSDK, Conversation } from \"./client\";\nexport { DiscoveryManager } from \"./discovery\";\n\n// Types\nexport type {\n SDKConfig,\n ToolDefinition,\n JSONSchema,\n JSONSchemaProperty,\n ConnectionInfo,\n ConnectionState,\n ChatRequest,\n ChatMessage,\n ChatResponse,\n ChatStreamEvent,\n ToolCall,\n SDKEvents,\n SDKEventName,\n // Private Agent API\n AgentConfig,\n AgentInfo,\n AgentUpdateConfig,\n ConversationInfo,\n ConversationMessage,\n ConversationDetail,\n // Remote Tools\n RemoteToolDefinition,\n} from \"./types\";\n","/**\n * Sanqian SDK Client\n *\n * Main class for connecting to Sanqian and registering tools.\n */\n\nimport WebSocket from \"ws\";\nimport { DiscoveryManager } from \"./discovery\";\nimport type {\n SDKConfig,\n ConnectionInfo,\n ConnectionState,\n ToolDefinition,\n RegisterMessage,\n RegisterAckMessage,\n ToolCallMessage,\n ToolResultMessage,\n HeartbeatMessage,\n SDKEvents,\n SDKEventName,\n JSONSchema,\n AgentConfig,\n AgentInfo,\n AgentUpdateConfig,\n ConversationInfo,\n ConversationDetail,\n CreateAgentMessage,\n CreateAgentAckMessage,\n UpdateAgentMessage,\n UpdateAgentAckMessage,\n ListAgentsMessage,\n ListAgentsAckMessage,\n DeleteAgentMessage,\n DeleteAgentAckMessage,\n ListConversationsMessage,\n ListConversationsAckMessage,\n GetConversationMessage,\n GetConversationAckMessage,\n DeleteConversationMessage,\n DeleteConversationAckMessage,\n ChatMessage,\n ChatResponse,\n ChatRequestMessage,\n ChatResponseMessage,\n ChatStreamMessage,\n ChatStreamEvent,\n RemoteToolDefinition,\n} from \"./types\";\n\ntype EventListener<T extends SDKEventName> = SDKEvents[T];\n\nexport class SanqianSDK {\n private config: SDKConfig;\n private discovery: DiscoveryManager;\n private ws: WebSocket | null = null;\n private connectionInfo: ConnectionInfo | null = null;\n\n private state: ConnectionState = {\n connected: false,\n registering: false,\n registered: false,\n reconnectAttempts: 0,\n };\n\n // Tool handlers by name\n private toolHandlers: Map<string, (args: unknown) => Promise<unknown>> =\n new Map();\n\n // Pending request futures\n private pendingRequests: Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n }\n > = new Map();\n\n // Timers\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private heartbeatAckPending: boolean = false;\n private missedHeartbeats: number = 0;\n private static readonly MAX_MISSED_HEARTBEATS = 2;\n\n // Event listeners\n private eventListeners: Map<SDKEventName, Set<EventListener<SDKEventName>>> =\n new Map();\n\n constructor(config: SDKConfig) {\n this.config = {\n reconnectInterval: 5000,\n heartbeatInterval: 30000,\n toolExecutionTimeout: 30000,\n ...config,\n };\n this.discovery = new DiscoveryManager();\n\n // Register tool handlers\n for (const tool of config.tools) {\n this.toolHandlers.set(tool.name, tool.handler);\n }\n }\n\n // ============================================\n // Lifecycle\n // ============================================\n\n /**\n * Connect to Sanqian\n *\n * Reads connection info, establishes WebSocket, and registers app.\n * Returns when registration is complete.\n *\n * If autoLaunchSanqian is enabled and Sanqian is not running,\n * SDK will attempt to start it in hidden/tray mode.\n */\n async connect(): Promise<void> {\n // Read connection info\n const info = this.discovery.read();\n\n if (!info) {\n // Sanqian not running\n // Try to auto-launch if enabled\n if (this.config.autoLaunchSanqian) {\n console.log(\"[SDK] Sanqian not running, attempting to launch...\");\n const launched = this.discovery.launchSanqian(this.config.sanqianPath);\n if (launched) {\n console.log(\"[SDK] Sanqian launch initiated, waiting for startup...\");\n } else {\n console.warn(\"[SDK] Failed to launch Sanqian, will wait for manual start\");\n }\n }\n\n // Start watching and wait for connection\n return new Promise((resolve, reject) => {\n console.log(\"[SDK] Waiting for Sanqian...\");\n\n const timeout = setTimeout(() => {\n this.discovery.stopWatching();\n reject(new Error(\"Sanqian connection timeout\"));\n }, 60000); // 60 second timeout\n\n this.discovery.startWatching(async (newInfo) => {\n if (newInfo) {\n clearTimeout(timeout);\n this.discovery.stopWatching();\n try {\n await this.connectWithInfo(newInfo);\n resolve();\n } catch (e) {\n reject(e);\n }\n }\n });\n });\n }\n\n await this.connectWithInfo(info);\n }\n\n /**\n * Connect with known connection info\n */\n private async connectWithInfo(info: ConnectionInfo): Promise<void> {\n this.connectionInfo = info;\n const url = this.discovery.buildWebSocketUrl(info);\n\n return new Promise((resolve, reject) => {\n console.log(`[SDK] Connecting to ${url}`);\n\n this.ws = new WebSocket(url);\n\n const connectTimeout = setTimeout(() => {\n reject(new Error(\"WebSocket connection timeout\"));\n this.ws?.close();\n }, 10000);\n\n this.ws.on(\"open\", async () => {\n clearTimeout(connectTimeout);\n console.log(\"[SDK] WebSocket connected\");\n this.state.connected = true;\n this.state.reconnectAttempts = 0;\n this.emit(\"connected\");\n\n try {\n await this.register();\n resolve();\n } catch (e) {\n reject(e);\n }\n });\n\n this.ws.on(\"close\", (code, reason) => {\n console.log(`[SDK] WebSocket closed: ${code} ${reason.toString()}`);\n this.handleDisconnect(reason.toString());\n });\n\n this.ws.on(\"error\", (error) => {\n console.error(\"[SDK] WebSocket error:\", error);\n this.state.lastError = error;\n this.emit(\"error\", error);\n });\n\n this.ws.on(\"message\", (data) => {\n try {\n const message = JSON.parse(data.toString());\n this.handleMessage(message);\n } catch (e) {\n console.error(\"[SDK] Failed to parse message:\", e);\n }\n });\n });\n }\n\n /**\n * Disconnect from Sanqian\n */\n async disconnect(): Promise<void> {\n this.stopHeartbeat();\n this.stopReconnect();\n this.discovery.stopWatching();\n\n if (this.ws) {\n this.ws.close(1000, \"Client disconnect\");\n this.ws = null;\n }\n\n this.state = {\n connected: false,\n registering: false,\n registered: false,\n reconnectAttempts: 0,\n };\n }\n\n // ============================================\n // Registration\n // ============================================\n\n private async register(): Promise<void> {\n this.state.registering = true;\n\n const msgId = this.generateId();\n\n const message: RegisterMessage = {\n id: msgId,\n type: \"register\",\n app: {\n name: this.config.appName,\n version: this.config.appVersion,\n display_name: this.config.displayName,\n launch_command: this.config.launchCommand,\n },\n tools: this.config.tools.map((t) => ({\n name: `${this.config.appName}:${t.name}`,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n try {\n const response = await this.sendAndWait<RegisterAckMessage>(\n message,\n msgId,\n 10000\n );\n\n if (!response.success) {\n throw new Error(response.error || \"Registration failed\");\n }\n\n this.state.registering = false;\n this.state.registered = true;\n this.startHeartbeat();\n this.emit(\"registered\");\n\n console.log(`[SDK] Registered as '${this.config.appName}'`);\n } catch (e) {\n this.state.registering = false;\n throw e;\n }\n }\n\n // ============================================\n // Message Handling\n // ============================================\n\n private handleMessage(message: { id?: string; type: string; [key: string]: unknown }): void {\n const { id, type } = message;\n\n // Check if this is a response to a pending request\n if (id && this.pendingRequests.has(id)) {\n const pending = this.pendingRequests.get(id)!;\n this.pendingRequests.delete(id);\n pending.resolve(message);\n return;\n }\n\n // Handle server-initiated messages\n switch (type) {\n case \"tool_call\":\n this.handleToolCall(message as unknown as ToolCallMessage);\n break;\n\n case \"heartbeat_ack\":\n // Heartbeat acknowledged - reset missed counter\n this.heartbeatAckPending = false;\n this.missedHeartbeats = 0;\n break;\n\n case \"chat_stream\":\n // Handle streaming chat response\n this.handleChatStream(message as unknown as ChatStreamMessage);\n break;\n\n default:\n console.warn(`[SDK] Unknown message type: ${type}`);\n }\n }\n\n private handleChatStream(message: ChatStreamMessage): void {\n const { id, event, content, tool_call, tool_result, conversation_id, title, usage, error } = message;\n\n if (!id) return;\n\n const handler = this.streamHandlers.get(id);\n if (!handler) {\n console.warn(`[SDK] No stream handler for message ${id}`);\n return;\n }\n\n switch (event) {\n case \"text\":\n handler.onEvent({ type: \"text\", content });\n break;\n\n case \"tool_call\":\n handler.onEvent({ type: \"tool_call\", tool_call });\n break;\n\n case \"tool_result\":\n if (tool_result) {\n handler.onEvent({\n type: \"tool_call\",\n tool_call: {\n id: tool_result.call_id,\n type: \"function\",\n function: {\n name: \"tool_result\",\n arguments: JSON.stringify(tool_result),\n },\n },\n });\n }\n break;\n\n case \"done\":\n handler.onDone({\n message: message.message || { role: \"assistant\", content: \"\" },\n conversationId: conversation_id || \"\",\n title,\n usage,\n });\n break;\n\n case \"error\":\n handler.onError(new Error(error || \"Unknown stream error\"));\n break;\n }\n }\n\n private async handleToolCall(message: ToolCallMessage): Promise<void> {\n console.log(`[SDK] handleToolCall received:`, JSON.stringify(message));\n const { id, call_id, name, arguments: args } = message;\n const msgId = id || call_id;\n\n // Extract actual tool name (remove app prefix)\n const toolName = name.includes(\":\") ? name.split(\":\")[1] : name;\n console.log(`[SDK] Looking for handler: '${toolName}', available handlers:`, Array.from(this.toolHandlers.keys()));\n const handler = this.toolHandlers.get(toolName);\n\n // Emit event for debugging/logging\n this.emit(\"tool_call\", { name: toolName, arguments: args });\n\n if (!handler) {\n await this.sendToolResult(msgId, call_id, false, undefined, `Tool '${toolName}' not found`);\n return;\n }\n\n try {\n console.log(`[SDK] Executing tool '${toolName}' with args:`, args);\n // Execute with timeout\n const result = await Promise.race([\n handler(args),\n this.createTimeout(this.config.toolExecutionTimeout!),\n ]);\n\n console.log(`[SDK] Tool '${toolName}' completed successfully`);\n await this.sendToolResult(msgId, call_id, true, result);\n } catch (e) {\n // Preserve full error info for debugging\n const errorMessage = e instanceof Error ? e.message : String(e);\n const errorStack = e instanceof Error ? e.stack : undefined;\n console.log(`[SDK] Tool '${toolName}' failed:`, errorMessage);\n if (errorStack) {\n console.log(`[SDK] Tool error stack:`, errorStack);\n }\n await this.sendToolResult(msgId, call_id, false, undefined, errorMessage);\n }\n }\n\n private async sendToolResult(\n id: string,\n callId: string,\n success: boolean,\n result?: unknown,\n error?: string\n ): Promise<void> {\n const message: ToolResultMessage = {\n id,\n type: \"tool_result\",\n call_id: callId,\n success,\n result,\n error,\n };\n\n console.log(`[SDK] Sending tool_result for ${callId}:`, success ? 'success' : `error: ${error}`);\n this.send(message);\n }\n\n // ============================================\n // Connection Management\n // ============================================\n\n private handleDisconnect(reason: string): void {\n this.stopHeartbeat();\n this.state.connected = false;\n this.state.registered = false;\n this.emit(\"disconnected\", reason);\n\n // Reject pending requests\n for (const [, pending] of this.pendingRequests) {\n pending.reject(new Error(\"Disconnected\"));\n }\n this.pendingRequests.clear();\n\n // Schedule reconnect\n this.scheduleReconnect();\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return;\n\n // Exponential backoff: 500ms, 1s, 2s, 4s, max 5s\n // With jitter to avoid thundering herd\n const baseDelay = Math.min(\n 500 * Math.pow(2, this.state.reconnectAttempts),\n 5000 // Cap at 5 seconds\n );\n const jitter = Math.random() * 500; // 0-500ms random jitter\n const delay = baseDelay + jitter;\n\n console.log(`[SDK] Scheduling reconnect in ${Math.round(delay)}ms (attempt ${this.state.reconnectAttempts + 1})`);\n\n this.reconnectTimer = setTimeout(async () => {\n this.reconnectTimer = null;\n this.state.reconnectAttempts++;\n\n // Re-read connection info\n const info = this.discovery.read();\n if (info) {\n try {\n await this.connectWithInfo(info);\n } catch (e) {\n console.error(\"[SDK] Reconnect failed:\", e);\n this.scheduleReconnect();\n }\n } else {\n // Sanqian not running, keep trying\n this.scheduleReconnect();\n }\n }, delay);\n }\n\n private stopReconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n // ============================================\n // Heartbeat\n // ============================================\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.missedHeartbeats = 0;\n this.heartbeatAckPending = false;\n\n this.heartbeatTimer = setInterval(() => {\n // Check if previous heartbeat was acknowledged\n if (this.heartbeatAckPending) {\n this.missedHeartbeats++;\n console.warn(`[SDK] Missed heartbeat ack (${this.missedHeartbeats}/${SanqianSDK.MAX_MISSED_HEARTBEATS})`);\n\n if (this.missedHeartbeats >= SanqianSDK.MAX_MISSED_HEARTBEATS) {\n console.error(\"[SDK] Too many missed heartbeat acks, connection may be dead\");\n // Force disconnect and reconnect\n this.ws?.close(4000, \"Heartbeat timeout\");\n return;\n }\n }\n\n const message: HeartbeatMessage = {\n type: \"heartbeat\",\n timestamp: new Date().toISOString(),\n };\n this.heartbeatAckPending = true;\n try {\n this.send(message);\n } catch (e) {\n // send() failure in heartbeat should trigger reconnect\n console.error(\"[SDK] Heartbeat send failed:\", e);\n this.ws?.close(4000, \"Heartbeat send failed\");\n }\n }, this.config.heartbeatInterval);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n // ============================================\n // Communication\n // ============================================\n\n private send(message: object): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n const error = new Error(\n `WebSocket not open (state: ${this.ws?.readyState ?? \"null\"}), cannot send message`\n );\n console.error(`[SDK] ${error.message}:`, message);\n throw error;\n }\n\n const data = JSON.stringify(message);\n console.log(`[SDK] WebSocket send:`, data.substring(0, 200));\n this.ws.send(data);\n }\n\n private sendAndWait<T>(\n message: object,\n id: string,\n timeout: number\n ): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(\"Request timeout\"));\n }, timeout);\n\n this.pendingRequests.set(id, {\n resolve: (value) => {\n clearTimeout(timer);\n resolve(value as T);\n },\n reject: (error) => {\n clearTimeout(timer);\n reject(error);\n },\n });\n\n try {\n this.send(message);\n } catch (e) {\n // Clean up on send failure to prevent memory leak\n clearTimeout(timer);\n this.pendingRequests.delete(id);\n reject(e);\n }\n });\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Get current connection state\n */\n getState(): ConnectionState {\n return { ...this.state };\n }\n\n /**\n * Check if connected and registered\n */\n isConnected(): boolean {\n return this.state.connected && this.state.registered;\n }\n\n /**\n * Wait for connection to be established.\n * Used internally by chat() and chatStream() for auto-reconnect.\n * Relies on background scheduleReconnect() to do the actual reconnection.\n */\n private async waitForConnection(timeout: number = 30000): Promise<void> {\n if (this.isConnected()) {\n return;\n }\n\n console.log(\"[SDK] Not connected, waiting for reconnection...\");\n\n // Wait for 'registered' event (emitted when connection + registration completes)\n // IMPORTANT: Register listener BEFORE triggering reconnect to avoid race condition\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n const timer = setTimeout(() => {\n if (!resolved) {\n resolved = true;\n reject(new Error(\"Connection timeout. Please ensure Sanqian is running.\"));\n }\n }, timeout);\n\n this.once(\"registered\", () => {\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n resolve();\n }\n });\n\n // Check again after registering listener (connection might have completed)\n if (this.isConnected()) {\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n resolve();\n }\n return;\n }\n\n // If no reconnect is scheduled, trigger one\n if (!this.reconnectTimer) {\n const info = this.discovery.read();\n if (!info && this.config.autoLaunchSanqian) {\n console.log(\"[SDK] Sanqian not running, attempting to launch...\");\n const launched = this.discovery.launchSanqian(this.config.sanqianPath);\n if (launched) {\n this.scheduleReconnect();\n }\n } else if (!info) {\n // No connection info and can't auto-launch\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n reject(new Error(\"Sanqian is not running. Please start it manually.\"));\n }\n } else {\n // Connection info exists, trigger reconnect\n this.scheduleReconnect();\n }\n }\n });\n }\n\n /**\n * Update tool list dynamically\n */\n async updateTools(tools: ToolDefinition[]): Promise<void> {\n // Update local handlers\n this.toolHandlers.clear();\n for (const tool of tools) {\n this.toolHandlers.set(tool.name, tool.handler);\n }\n\n // Send update to Sanqian\n if (this.isConnected()) {\n const msgId = this.generateId();\n const message = {\n id: msgId,\n type: \"tools_update\",\n tools: tools.map((t) => ({\n name: `${this.config.appName}:${t.name}`,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n await this.sendAndWait(message, msgId, 5000);\n }\n }\n\n // ============================================\n // Private Agent API\n // ============================================\n\n /**\n * Create or update a private agent\n *\n * Private agents are only visible to this SDK app, not in Sanqian UI.\n * If agent with same ID exists, it will be updated.\n *\n * @param config - Agent configuration\n * @returns Full agent info (or agent_id string for backward compatibility)\n */\n async createAgent(config: AgentConfig): Promise<AgentInfo> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: CreateAgentMessage = {\n id: msgId,\n type: \"create_agent\",\n agent: config,\n };\n\n const response = await this.sendAndWait<CreateAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to create agent\");\n }\n\n // Return full agent info if available, fallback to minimal info\n if (response.agent) {\n return response.agent;\n }\n\n // Backward compatibility: construct minimal AgentInfo from agent_id\n return {\n agent_id: response.agent_id!,\n name: config.name,\n description: config.description,\n tools: config.tools || [],\n };\n }\n\n /**\n * List all private agents owned by this app\n */\n async listAgents(): Promise<AgentInfo[]> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: ListAgentsMessage = {\n id: msgId,\n type: \"list_agents\",\n };\n\n const response = await this.sendAndWait<ListAgentsAckMessage>(message, msgId, 10000);\n\n if (response.error) {\n throw new Error(response.error);\n }\n\n return response.agents;\n }\n\n /**\n * Delete a private agent\n */\n async deleteAgent(agentId: string): Promise<void> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: DeleteAgentMessage = {\n id: msgId,\n type: \"delete_agent\",\n agent_id: agentId,\n };\n\n const response = await this.sendAndWait<DeleteAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to delete agent\");\n }\n }\n\n /**\n * Update a private agent\n *\n * Only updates the fields that are provided.\n * @param agentId - Agent ID (short name or full)\n * @param updates - Fields to update\n * @returns Updated agent info\n */\n async updateAgent(\n agentId: string,\n updates: Omit<AgentUpdateConfig, \"agent_id\">\n ): Promise<AgentInfo> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: UpdateAgentMessage = {\n id: msgId,\n type: \"update_agent\",\n agent: {\n agent_id: agentId,\n ...updates,\n },\n };\n\n const response = await this.sendAndWait<UpdateAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success || !response.agent) {\n throw new Error(response.error || \"Failed to update agent\");\n }\n\n return response.agent;\n }\n\n // ============================================\n // Conversation API\n // ============================================\n\n /**\n * List conversations for this app\n */\n async listConversations(options?: {\n agentId?: string;\n limit?: number;\n offset?: number;\n }): Promise<{ conversations: ConversationInfo[]; total: number }> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: ListConversationsMessage = {\n id: msgId,\n type: \"list_conversations\",\n agent_id: options?.agentId,\n limit: options?.limit,\n offset: options?.offset,\n };\n\n const response = await this.sendAndWait<ListConversationsAckMessage>(message, msgId, 10000);\n\n if (response.error) {\n throw new Error(response.error);\n }\n\n return {\n conversations: response.conversations,\n total: response.total,\n };\n }\n\n /**\n * Get conversation details with messages\n */\n async getConversation(\n conversationId: string,\n options?: {\n includeMessages?: boolean;\n messageLimit?: number;\n messageOffset?: number;\n }\n ): Promise<ConversationDetail> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: GetConversationMessage = {\n id: msgId,\n type: \"get_conversation\",\n conversation_id: conversationId,\n include_messages: options?.includeMessages ?? true,\n message_limit: options?.messageLimit,\n message_offset: options?.messageOffset,\n };\n\n const response = await this.sendAndWait<GetConversationAckMessage>(message, msgId, 10000);\n\n if (!response.success || !response.conversation) {\n throw new Error(response.error || \"Failed to get conversation\");\n }\n\n return response.conversation;\n }\n\n /**\n * Delete a conversation\n */\n async deleteConversation(conversationId: string): Promise<void> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: DeleteConversationMessage = {\n id: msgId,\n type: \"delete_conversation\",\n conversation_id: conversationId,\n };\n\n const response = await this.sendAndWait<DeleteConversationAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to delete conversation\");\n }\n }\n\n // ============================================\n // Chat API\n // ============================================\n\n // Pending stream handlers for streaming chat\n private streamHandlers: Map<string, {\n onEvent: (event: ChatStreamEvent) => void;\n onDone: (response: ChatResponse) => void;\n onError: (error: Error) => void;\n }> = new Map();\n\n /**\n * Send a chat message to an agent (non-streaming)\n *\n * Supports two modes:\n * - Stateless (no conversationId): Caller maintains message history\n * - Stateful (with conversationId): Server stores messages in conversations table\n *\n * @param agentId - Agent ID (short name or full, SDK auto-prefixes)\n * @param messages - Messages to send\n * @param options - Optional settings including conversationId and remoteTools\n * @returns Chat response with assistant message and conversation ID\n */\n async chat(\n agentId: string,\n messages: ChatMessage[],\n options?: {\n conversationId?: string;\n remoteTools?: RemoteToolDefinition[];\n }\n ): Promise<ChatResponse> {\n // Auto-reconnect if disconnected\n if (!this.isConnected()) {\n console.log(\"[SDK] Not connected, attempting to reconnect...\");\n await this.waitForConnection();\n }\n\n const msgId = this.generateId();\n const message: ChatRequestMessage = {\n id: msgId,\n type: \"chat\",\n agent_id: agentId,\n messages,\n conversation_id: options?.conversationId,\n stream: false,\n remote_tools: options?.remoteTools?.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n // Longer timeout for chat (agent may take time with complex multi-tool tasks)\n const response = await this.sendAndWait<ChatResponseMessage>(message, msgId, 600000); // 10 minutes\n\n if (!response.success) {\n throw new Error(response.error || \"Chat request failed\");\n }\n\n return {\n message: response.message!,\n conversationId: response.conversation_id!,\n title: response.title,\n usage: response.usage,\n };\n }\n\n /**\n * Send a chat message to an agent with streaming response\n *\n * Supports two modes:\n * - Stateless (no conversationId): Caller maintains message history\n * - Stateful (with conversationId): Server stores messages in conversations table\n *\n * @param agentId - Agent ID (short name or full, SDK auto-prefixes)\n * @param messages - Messages to send\n * @param options - Optional settings including conversationId and remoteTools\n * @returns AsyncIterable of stream events\n */\n async *chatStream(\n agentId: string,\n messages: ChatMessage[],\n options?: {\n conversationId?: string;\n remoteTools?: RemoteToolDefinition[];\n }\n ): AsyncGenerator<ChatStreamEvent> {\n // Auto-reconnect if disconnected\n if (!this.isConnected()) {\n console.log(\"[SDK] Not connected, attempting to reconnect...\");\n await this.waitForConnection();\n }\n\n const msgId = this.generateId();\n const message: ChatRequestMessage = {\n id: msgId,\n type: \"chat\",\n agent_id: agentId,\n messages,\n conversation_id: options?.conversationId,\n stream: true,\n remote_tools: options?.remoteTools?.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n // Create a queue to yield events\n const eventQueue: ChatStreamEvent[] = [];\n let done = false;\n let error: Error | null = null;\n let resolveNext: (() => void) | null = null;\n\n // Register stream handler\n this.streamHandlers.set(msgId, {\n onEvent: (event) => {\n eventQueue.push(event);\n resolveNext?.();\n },\n onDone: (response) => {\n eventQueue.push({\n type: \"done\",\n conversationId: response.conversationId,\n title: response.title,\n });\n done = true;\n resolveNext?.();\n },\n onError: (e) => {\n error = e;\n resolveNext?.();\n },\n });\n\n try {\n // Send request\n this.send(message);\n\n // Yield events as they arrive\n while (!done && !error) {\n if (eventQueue.length > 0) {\n yield eventQueue.shift()!;\n } else {\n // Wait for next event\n await new Promise<void>((resolve) => {\n resolveNext = resolve;\n });\n resolveNext = null;\n }\n }\n\n // Yield remaining events\n while (eventQueue.length > 0) {\n yield eventQueue.shift()!;\n }\n\n if (error) {\n throw error;\n }\n } finally {\n this.streamHandlers.delete(msgId);\n }\n }\n\n /**\n * Start a new conversation with an agent\n *\n * Returns a Conversation object for convenient multi-turn chat.\n *\n * @example\n * ```typescript\n * const conv = sdk.startConversation('assistant')\n * const r1 = await conv.send('Hello')\n * const r2 = await conv.send('Follow up question')\n * console.log(conv.id) // conversation ID\n * ```\n */\n startConversation(agentId: string): Conversation {\n return new Conversation(this, agentId);\n }\n\n // ============================================\n // Events\n // ============================================\n\n /**\n * Add event listener\n */\n on<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(listener as EventListener<SDKEventName>);\n return this;\n }\n\n /**\n * Remove event listener\n */\n off<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n this.eventListeners.get(event)?.delete(listener as EventListener<SDKEventName>);\n return this;\n }\n\n /**\n * Add one-time event listener (auto-removes after first call)\n */\n once<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n const onceWrapper = ((...args: Parameters<SDKEvents[T]>) => {\n this.off(event, onceWrapper as SDKEvents[T]);\n (listener as (...args: unknown[]) => void)(...args);\n }) as SDKEvents[T];\n return this.on(event, onceWrapper);\n }\n\n private emit<T extends SDKEventName>(\n event: T,\n ...args: Parameters<SDKEvents[T]>\n ): void {\n const listeners = this.eventListeners.get(event);\n if (listeners) {\n for (const listener of listeners) {\n try {\n (listener as (...args: unknown[]) => void)(...args);\n } catch (e) {\n console.error(`[SDK] Event listener error for '${event}':`, e);\n }\n }\n }\n }\n\n // ============================================\n // Utilities\n // ============================================\n\n private generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n private createTimeout(ms: number): Promise<never> {\n return new Promise((_, reject) => {\n setTimeout(() => reject(new Error(\"Timeout\")), ms);\n });\n }\n}\n\n/**\n * Conversation helper for multi-turn chat\n *\n * Automatically manages conversationId for stateful conversations.\n */\nexport class Conversation {\n private sdk: SanqianSDK;\n private agentId: string;\n private _conversationId: string | null = null;\n\n constructor(sdk: SanqianSDK, agentId: string, conversationId?: string) {\n this.sdk = sdk;\n this.agentId = agentId;\n this._conversationId = conversationId || null;\n }\n\n /**\n * Get the conversation ID (available after first message)\n */\n get id(): string | null {\n return this._conversationId;\n }\n\n /**\n * Send a message and get a response\n *\n * First call creates a new conversation, subsequent calls continue it.\n */\n async send(content: string, options?: { remoteTools?: RemoteToolDefinition[] }): Promise<ChatResponse> {\n const response = await this.sdk.chat(\n this.agentId,\n [{ role: \"user\", content }],\n {\n conversationId: this._conversationId || undefined,\n remoteTools: options?.remoteTools,\n }\n );\n\n // Store conversation ID for subsequent calls\n if (response.conversationId && !this._conversationId) {\n this._conversationId = response.conversationId;\n }\n\n return response;\n }\n\n /**\n * Send a message with streaming response\n */\n async *sendStream(\n content: string,\n options?: { remoteTools?: RemoteToolDefinition[] }\n ): AsyncGenerator<ChatStreamEvent> {\n const stream = this.sdk.chatStream(\n this.agentId,\n [{ role: \"user\", content }],\n {\n conversationId: this._conversationId || undefined,\n remoteTools: options?.remoteTools,\n }\n );\n\n for await (const event of stream) {\n // Capture conversation ID from done event\n if (event.type === \"done\" && event.conversationId && !this._conversationId) {\n this._conversationId = event.conversationId;\n }\n yield event;\n }\n }\n\n /**\n * Delete this conversation\n */\n async delete(): Promise<void> {\n if (!this._conversationId) {\n throw new Error(\"No conversation to delete\");\n }\n await this.sdk.deleteConversation(this._conversationId);\n this._conversationId = null;\n }\n\n /**\n * Get conversation details including message history\n */\n async getDetails(options?: { messageLimit?: number }): Promise<ConversationDetail> {\n if (!this._conversationId) {\n throw new Error(\"No conversation to get details for\");\n }\n return this.sdk.getConversation(this._conversationId, {\n includeMessages: true,\n messageLimit: options?.messageLimit,\n });\n }\n}\n","/**\n * Service Discovery Module\n *\n * Reads Sanqian's connection info from ~/.sanqian/runtime/connection.json\n * and monitors file changes for reconnection.\n * Also handles auto-launching Sanqian when needed.\n */\n\nimport { existsSync, readFileSync, watch, type FSWatcher } from \"fs\";\nimport { homedir, platform } from \"os\";\nimport { join } from \"path\";\nimport { spawn, execSync } from \"child_process\";\nimport type { ConnectionInfo } from \"./types\";\n\nexport class DiscoveryManager {\n private connectionInfo: ConnectionInfo | null = null;\n private watcher: FSWatcher | null = null;\n private onChange: ((info: ConnectionInfo | null) => void) | null = null;\n private pollInterval: ReturnType<typeof setInterval> | null = null;\n\n /**\n * Get the path to connection.json\n */\n getConnectionFilePath(): string {\n return join(homedir(), \".sanqian\", \"runtime\", \"connection.json\");\n }\n\n /**\n * Read and validate connection info\n *\n * Returns null if:\n * - File doesn't exist\n * - File is invalid JSON\n * - Process is not running\n */\n read(): ConnectionInfo | null {\n const filePath = this.getConnectionFilePath();\n\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(filePath, \"utf-8\");\n const info = JSON.parse(content) as ConnectionInfo;\n\n // Validate required fields\n if (!info.port || !info.token || !info.pid) {\n return null;\n }\n\n // Check if Sanqian process is running\n if (!this.isProcessRunning(info.pid)) {\n return null;\n }\n\n this.connectionInfo = info;\n return info;\n } catch {\n return null;\n }\n }\n\n /**\n * Start watching for connection file changes\n */\n startWatching(onChange: (info: ConnectionInfo | null) => void): void {\n this.onChange = onChange;\n\n const dir = join(homedir(), \".sanqian\", \"runtime\");\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n // Directory doesn't exist, poll for it\n this.pollInterval = setInterval(() => {\n if (existsSync(dir)) {\n if (this.pollInterval) {\n clearInterval(this.pollInterval);\n this.pollInterval = null;\n }\n this.setupWatcher(dir);\n }\n }, 2000);\n return;\n }\n\n this.setupWatcher(dir);\n }\n\n private setupWatcher(dir: string): void {\n try {\n this.watcher = watch(dir, (event, filename) => {\n if (filename === \"connection.json\") {\n // Debounce rapid changes\n setTimeout(() => {\n const newInfo = this.read();\n const oldInfoStr = JSON.stringify(this.connectionInfo);\n const newInfoStr = JSON.stringify(newInfo);\n\n // Only trigger if actually changed\n if (oldInfoStr !== newInfoStr) {\n this.connectionInfo = newInfo;\n this.onChange?.(newInfo);\n }\n }, 100);\n }\n });\n } catch (e) {\n console.error(\"Failed to watch connection file:\", e);\n }\n }\n\n /**\n * Stop watching\n */\n stopWatching(): void {\n if (this.pollInterval) {\n clearInterval(this.pollInterval);\n this.pollInterval = null;\n }\n this.watcher?.close();\n this.watcher = null;\n this.onChange = null;\n }\n\n /**\n * Check if a process is running by PID\n *\n * Cross-platform implementation:\n * - macOS/Linux: process.kill(pid, 0) works correctly\n * - Windows: process.kill(pid, 0) can give false positives due to PID reuse,\n * so we use tasklist command for reliable checking\n */\n private isProcessRunning(pid: number): boolean {\n try {\n if (platform() === \"win32\") {\n // Windows: Use tasklist to check if PID exists\n // This is more reliable than process.kill on Windows\n try {\n const result = execSync(`tasklist /FI \"PID eq ${pid}\" /NH`, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n // tasklist returns the process name if found, or \"INFO: No tasks are running...\"\n return !result.includes(\"No tasks are running\");\n } catch {\n return false;\n }\n } else {\n // macOS/Linux: Sending signal 0 doesn't kill the process, just checks if it exists\n process.kill(pid, 0);\n return true;\n }\n } catch {\n return false;\n }\n }\n\n /**\n * Get cached connection info (may be stale)\n */\n getCached(): ConnectionInfo | null {\n return this.connectionInfo;\n }\n\n /**\n * Build WebSocket URL from connection info\n */\n buildWebSocketUrl(info: ConnectionInfo): string {\n return `ws://127.0.0.1:${info.port}${info.ws_path}?token=${info.token}`;\n }\n\n /**\n * Build HTTP base URL from connection info\n */\n buildHttpUrl(info: ConnectionInfo): string {\n return `http://127.0.0.1:${info.port}`;\n }\n\n /**\n * Find Sanqian executable path\n * Searches in standard installation locations for each platform\n */\n findSanqianPath(customPath?: string): string | null {\n // If custom path provided, use it\n if (customPath) {\n if (existsSync(customPath)) {\n return customPath;\n }\n console.warn(`[SDK] Custom Sanqian path not found: ${customPath}`);\n return null;\n }\n\n const os = platform();\n const searchPaths: string[] = [];\n\n if (os === \"darwin\") {\n // macOS: Standard app locations + common dev locations\n searchPaths.push(\n // Production: installed app\n \"/Applications/Sanqian.app/Contents/MacOS/Sanqian\",\n join(homedir(), \"Applications/Sanqian.app/Contents/MacOS/Sanqian\"),\n // Development: electron-builder output\n join(homedir(), \"dev/sanqian/dist/mac-arm64/Sanqian.app/Contents/MacOS/Sanqian\"),\n join(homedir(), \"dev/sanqian/dist/mac/Sanqian.app/Contents/MacOS/Sanqian\"),\n );\n } else if (os === \"win32\") {\n // Windows: Standard installation paths + dev locations\n const programFiles = process.env.PROGRAMFILES || \"C:\\\\Program Files\";\n const programFilesX86 =\n process.env[\"PROGRAMFILES(X86)\"] || \"C:\\\\Program Files (x86)\";\n const localAppData =\n process.env.LOCALAPPDATA || join(homedir(), \"AppData\", \"Local\");\n\n searchPaths.push(\n // Production: installed app\n join(programFiles, \"Sanqian\", \"Sanqian.exe\"),\n join(programFilesX86, \"Sanqian\", \"Sanqian.exe\"),\n join(localAppData, \"Programs\", \"Sanqian\", \"Sanqian.exe\"),\n // Development: electron-builder output\n join(homedir(), \"dev\", \"sanqian\", \"dist\", \"win-unpacked\", \"Sanqian.exe\"),\n );\n } else {\n // Linux: Common locations\n searchPaths.push(\n \"/usr/bin/sanqian\",\n \"/usr/local/bin/sanqian\",\n join(homedir(), \".local/bin/sanqian\"),\n \"/opt/Sanqian/sanqian\",\n // Development\n join(homedir(), \"dev/sanqian/dist/linux-unpacked/sanqian\"),\n );\n }\n\n for (const path of searchPaths) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n }\n\n /**\n * Launch Sanqian in hidden/tray mode\n * Returns true if launch was initiated successfully\n */\n launchSanqian(customPath?: string): boolean {\n const sanqianPath = this.findSanqianPath(customPath);\n\n if (!sanqianPath) {\n console.error(\"[SDK] Sanqian executable not found\");\n return false;\n }\n\n console.log(`[SDK] Launching Sanqian from: ${sanqianPath}`);\n\n try {\n const os = platform();\n\n if (os === \"darwin\") {\n // macOS: Use 'open' command with --args for hidden start\n // This properly launches the app bundle\n const appPath = sanqianPath.replace(\n \"/Contents/MacOS/Sanqian\",\n \"\",\n );\n spawn(\"open\", [\"-a\", appPath, \"--args\", \"--hidden\"], {\n detached: true,\n stdio: \"ignore\",\n }).unref();\n } else {\n // Windows/Linux: Direct execution with --hidden flag\n spawn(sanqianPath, [\"--hidden\"], {\n detached: true,\n stdio: \"ignore\",\n // On Windows, hide the console window\n ...(os === \"win32\" && {\n windowsHide: true,\n shell: false,\n }),\n }).unref();\n }\n\n return true;\n } catch (e) {\n console.error(\"[SDK] Failed to launch Sanqian:\", e);\n return false;\n }\n }\n\n /**\n * Check if Sanqian is running\n */\n isSanqianRunning(): boolean {\n return this.read() !== null;\n }\n}\n"],"mappings":"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","names":["WebSocket"]}
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import WebSocket from "ws";
5
5
  import { existsSync, readFileSync, watch } from "fs";
6
6
  import { homedir, platform } from "os";
7
7
  import { join } from "path";
8
- import { spawn } from "child_process";
8
+ import { spawn, execSync } from "child_process";
9
9
  var DiscoveryManager = class {
10
10
  connectionInfo = null;
11
11
  watcher = null;
@@ -98,11 +98,28 @@ var DiscoveryManager = class {
98
98
  }
99
99
  /**
100
100
  * Check if a process is running by PID
101
+ *
102
+ * Cross-platform implementation:
103
+ * - macOS/Linux: process.kill(pid, 0) works correctly
104
+ * - Windows: process.kill(pid, 0) can give false positives due to PID reuse,
105
+ * so we use tasklist command for reliable checking
101
106
  */
102
107
  isProcessRunning(pid) {
103
108
  try {
104
- process.kill(pid, 0);
105
- return true;
109
+ if (platform() === "win32") {
110
+ try {
111
+ const result = execSync(`tasklist /FI "PID eq ${pid}" /NH`, {
112
+ encoding: "utf-8",
113
+ stdio: ["pipe", "pipe", "pipe"]
114
+ });
115
+ return !result.includes("No tasks are running");
116
+ } catch {
117
+ return false;
118
+ }
119
+ } else {
120
+ process.kill(pid, 0);
121
+ return true;
122
+ }
106
123
  } catch {
107
124
  return false;
108
125
  }
@@ -225,7 +242,7 @@ var DiscoveryManager = class {
225
242
  };
226
243
 
227
244
  // src/client.ts
228
- var SanqianSDK = class {
245
+ var SanqianSDK = class _SanqianSDK {
229
246
  config;
230
247
  discovery;
231
248
  ws = null;
@@ -243,6 +260,9 @@ var SanqianSDK = class {
243
260
  // Timers
244
261
  heartbeatTimer = null;
245
262
  reconnectTimer = null;
263
+ heartbeatAckPending = false;
264
+ missedHeartbeats = 0;
265
+ static MAX_MISSED_HEARTBEATS = 2;
246
266
  // Event listeners
247
267
  eventListeners = /* @__PURE__ */ new Map();
248
268
  constructor(config) {
@@ -422,6 +442,8 @@ var SanqianSDK = class {
422
442
  this.handleToolCall(message);
423
443
  break;
424
444
  case "heartbeat_ack":
445
+ this.heartbeatAckPending = false;
446
+ this.missedHeartbeats = 0;
425
447
  break;
426
448
  case "chat_stream":
427
449
  this.handleChatStream(message);
@@ -494,9 +516,13 @@ var SanqianSDK = class {
494
516
  console.log(`[SDK] Tool '${toolName}' completed successfully`);
495
517
  await this.sendToolResult(msgId, call_id, true, result);
496
518
  } catch (e) {
497
- const error = e instanceof Error ? e.message : String(e);
498
- console.log(`[SDK] Tool '${toolName}' failed:`, error);
499
- await this.sendToolResult(msgId, call_id, false, void 0, error);
519
+ const errorMessage = e instanceof Error ? e.message : String(e);
520
+ const errorStack = e instanceof Error ? e.stack : void 0;
521
+ console.log(`[SDK] Tool '${toolName}' failed:`, errorMessage);
522
+ if (errorStack) {
523
+ console.log(`[SDK] Tool error stack:`, errorStack);
524
+ }
525
+ await this.sendToolResult(msgId, call_id, false, void 0, errorMessage);
500
526
  }
501
527
  }
502
528
  async sendToolResult(id, callId, success, result, error) {
@@ -562,12 +588,29 @@ var SanqianSDK = class {
562
588
  // ============================================
563
589
  startHeartbeat() {
564
590
  this.stopHeartbeat();
591
+ this.missedHeartbeats = 0;
592
+ this.heartbeatAckPending = false;
565
593
  this.heartbeatTimer = setInterval(() => {
594
+ if (this.heartbeatAckPending) {
595
+ this.missedHeartbeats++;
596
+ console.warn(`[SDK] Missed heartbeat ack (${this.missedHeartbeats}/${_SanqianSDK.MAX_MISSED_HEARTBEATS})`);
597
+ if (this.missedHeartbeats >= _SanqianSDK.MAX_MISSED_HEARTBEATS) {
598
+ console.error("[SDK] Too many missed heartbeat acks, connection may be dead");
599
+ this.ws?.close(4e3, "Heartbeat timeout");
600
+ return;
601
+ }
602
+ }
566
603
  const message = {
567
604
  type: "heartbeat",
568
605
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
569
606
  };
570
- this.send(message);
607
+ this.heartbeatAckPending = true;
608
+ try {
609
+ this.send(message);
610
+ } catch (e) {
611
+ console.error("[SDK] Heartbeat send failed:", e);
612
+ this.ws?.close(4e3, "Heartbeat send failed");
613
+ }
571
614
  }, this.config.heartbeatInterval);
572
615
  }
573
616
  stopHeartbeat() {
@@ -580,13 +623,16 @@ var SanqianSDK = class {
580
623
  // Communication
581
624
  // ============================================
582
625
  send(message) {
583
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
584
- const data = JSON.stringify(message);
585
- console.log(`[SDK] WebSocket send:`, data.substring(0, 200));
586
- this.ws.send(data);
587
- } else {
588
- console.error(`[SDK] WebSocket not open, cannot send:`, message);
626
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
627
+ const error = new Error(
628
+ `WebSocket not open (state: ${this.ws?.readyState ?? "null"}), cannot send message`
629
+ );
630
+ console.error(`[SDK] ${error.message}:`, message);
631
+ throw error;
589
632
  }
633
+ const data = JSON.stringify(message);
634
+ console.log(`[SDK] WebSocket send:`, data.substring(0, 200));
635
+ this.ws.send(data);
590
636
  }
591
637
  sendAndWait(message, id, timeout) {
592
638
  return new Promise((resolve, reject) => {
@@ -604,7 +650,13 @@ var SanqianSDK = class {
604
650
  reject(error);
605
651
  }
606
652
  });
607
- this.send(message);
653
+ try {
654
+ this.send(message);
655
+ } catch (e) {
656
+ clearTimeout(timer);
657
+ this.pendingRequests.delete(id);
658
+ reject(e);
659
+ }
608
660
  });
609
661
  }
610
662
  // ============================================
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/discovery.ts"],"sourcesContent":["/**\n * Sanqian SDK Client\n *\n * Main class for connecting to Sanqian and registering tools.\n */\n\nimport WebSocket from \"ws\";\nimport { DiscoveryManager } from \"./discovery\";\nimport type {\n SDKConfig,\n ConnectionInfo,\n ConnectionState,\n ToolDefinition,\n RegisterMessage,\n RegisterAckMessage,\n ToolCallMessage,\n ToolResultMessage,\n HeartbeatMessage,\n SDKEvents,\n SDKEventName,\n JSONSchema,\n AgentConfig,\n AgentInfo,\n AgentUpdateConfig,\n ConversationInfo,\n ConversationDetail,\n CreateAgentMessage,\n CreateAgentAckMessage,\n UpdateAgentMessage,\n UpdateAgentAckMessage,\n ListAgentsMessage,\n ListAgentsAckMessage,\n DeleteAgentMessage,\n DeleteAgentAckMessage,\n ListConversationsMessage,\n ListConversationsAckMessage,\n GetConversationMessage,\n GetConversationAckMessage,\n DeleteConversationMessage,\n DeleteConversationAckMessage,\n ChatMessage,\n ChatResponse,\n ChatRequestMessage,\n ChatResponseMessage,\n ChatStreamMessage,\n ChatStreamEvent,\n RemoteToolDefinition,\n} from \"./types\";\n\ntype EventListener<T extends SDKEventName> = SDKEvents[T];\n\nexport class SanqianSDK {\n private config: SDKConfig;\n private discovery: DiscoveryManager;\n private ws: WebSocket | null = null;\n private connectionInfo: ConnectionInfo | null = null;\n\n private state: ConnectionState = {\n connected: false,\n registering: false,\n registered: false,\n reconnectAttempts: 0,\n };\n\n // Tool handlers by name\n private toolHandlers: Map<string, (args: unknown) => Promise<unknown>> =\n new Map();\n\n // Pending request futures\n private pendingRequests: Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n }\n > = new Map();\n\n // Timers\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Event listeners\n private eventListeners: Map<SDKEventName, Set<EventListener<SDKEventName>>> =\n new Map();\n\n constructor(config: SDKConfig) {\n this.config = {\n reconnectInterval: 5000,\n heartbeatInterval: 30000,\n toolExecutionTimeout: 30000,\n ...config,\n };\n this.discovery = new DiscoveryManager();\n\n // Register tool handlers\n for (const tool of config.tools) {\n this.toolHandlers.set(tool.name, tool.handler);\n }\n }\n\n // ============================================\n // Lifecycle\n // ============================================\n\n /**\n * Connect to Sanqian\n *\n * Reads connection info, establishes WebSocket, and registers app.\n * Returns when registration is complete.\n *\n * If autoLaunchSanqian is enabled and Sanqian is not running,\n * SDK will attempt to start it in hidden/tray mode.\n */\n async connect(): Promise<void> {\n // Read connection info\n const info = this.discovery.read();\n\n if (!info) {\n // Sanqian not running\n // Try to auto-launch if enabled\n if (this.config.autoLaunchSanqian) {\n console.log(\"[SDK] Sanqian not running, attempting to launch...\");\n const launched = this.discovery.launchSanqian(this.config.sanqianPath);\n if (launched) {\n console.log(\"[SDK] Sanqian launch initiated, waiting for startup...\");\n } else {\n console.warn(\"[SDK] Failed to launch Sanqian, will wait for manual start\");\n }\n }\n\n // Start watching and wait for connection\n return new Promise((resolve, reject) => {\n console.log(\"[SDK] Waiting for Sanqian...\");\n\n const timeout = setTimeout(() => {\n this.discovery.stopWatching();\n reject(new Error(\"Sanqian connection timeout\"));\n }, 60000); // 60 second timeout\n\n this.discovery.startWatching(async (newInfo) => {\n if (newInfo) {\n clearTimeout(timeout);\n this.discovery.stopWatching();\n try {\n await this.connectWithInfo(newInfo);\n resolve();\n } catch (e) {\n reject(e);\n }\n }\n });\n });\n }\n\n await this.connectWithInfo(info);\n }\n\n /**\n * Connect with known connection info\n */\n private async connectWithInfo(info: ConnectionInfo): Promise<void> {\n this.connectionInfo = info;\n const url = this.discovery.buildWebSocketUrl(info);\n\n return new Promise((resolve, reject) => {\n console.log(`[SDK] Connecting to ${url}`);\n\n this.ws = new WebSocket(url);\n\n const connectTimeout = setTimeout(() => {\n reject(new Error(\"WebSocket connection timeout\"));\n this.ws?.close();\n }, 10000);\n\n this.ws.on(\"open\", async () => {\n clearTimeout(connectTimeout);\n console.log(\"[SDK] WebSocket connected\");\n this.state.connected = true;\n this.state.reconnectAttempts = 0;\n this.emit(\"connected\");\n\n try {\n await this.register();\n resolve();\n } catch (e) {\n reject(e);\n }\n });\n\n this.ws.on(\"close\", (code, reason) => {\n console.log(`[SDK] WebSocket closed: ${code} ${reason.toString()}`);\n this.handleDisconnect(reason.toString());\n });\n\n this.ws.on(\"error\", (error) => {\n console.error(\"[SDK] WebSocket error:\", error);\n this.state.lastError = error;\n this.emit(\"error\", error);\n });\n\n this.ws.on(\"message\", (data) => {\n try {\n const message = JSON.parse(data.toString());\n this.handleMessage(message);\n } catch (e) {\n console.error(\"[SDK] Failed to parse message:\", e);\n }\n });\n });\n }\n\n /**\n * Disconnect from Sanqian\n */\n async disconnect(): Promise<void> {\n this.stopHeartbeat();\n this.stopReconnect();\n this.discovery.stopWatching();\n\n if (this.ws) {\n this.ws.close(1000, \"Client disconnect\");\n this.ws = null;\n }\n\n this.state = {\n connected: false,\n registering: false,\n registered: false,\n reconnectAttempts: 0,\n };\n }\n\n // ============================================\n // Registration\n // ============================================\n\n private async register(): Promise<void> {\n this.state.registering = true;\n\n const msgId = this.generateId();\n\n const message: RegisterMessage = {\n id: msgId,\n type: \"register\",\n app: {\n name: this.config.appName,\n version: this.config.appVersion,\n display_name: this.config.displayName,\n launch_command: this.config.launchCommand,\n },\n tools: this.config.tools.map((t) => ({\n name: `${this.config.appName}:${t.name}`,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n try {\n const response = await this.sendAndWait<RegisterAckMessage>(\n message,\n msgId,\n 10000\n );\n\n if (!response.success) {\n throw new Error(response.error || \"Registration failed\");\n }\n\n this.state.registering = false;\n this.state.registered = true;\n this.startHeartbeat();\n this.emit(\"registered\");\n\n console.log(`[SDK] Registered as '${this.config.appName}'`);\n } catch (e) {\n this.state.registering = false;\n throw e;\n }\n }\n\n // ============================================\n // Message Handling\n // ============================================\n\n private handleMessage(message: { id?: string; type: string; [key: string]: unknown }): void {\n const { id, type } = message;\n\n // Check if this is a response to a pending request\n if (id && this.pendingRequests.has(id)) {\n const pending = this.pendingRequests.get(id)!;\n this.pendingRequests.delete(id);\n pending.resolve(message);\n return;\n }\n\n // Handle server-initiated messages\n switch (type) {\n case \"tool_call\":\n this.handleToolCall(message as unknown as ToolCallMessage);\n break;\n\n case \"heartbeat_ack\":\n // Heartbeat acknowledged, no action needed\n break;\n\n case \"chat_stream\":\n // Handle streaming chat response\n this.handleChatStream(message as unknown as ChatStreamMessage);\n break;\n\n default:\n console.warn(`[SDK] Unknown message type: ${type}`);\n }\n }\n\n private handleChatStream(message: ChatStreamMessage): void {\n const { id, event, content, tool_call, tool_result, conversation_id, title, usage, error } = message;\n\n if (!id) return;\n\n const handler = this.streamHandlers.get(id);\n if (!handler) {\n console.warn(`[SDK] No stream handler for message ${id}`);\n return;\n }\n\n switch (event) {\n case \"text\":\n handler.onEvent({ type: \"text\", content });\n break;\n\n case \"tool_call\":\n handler.onEvent({ type: \"tool_call\", tool_call });\n break;\n\n case \"tool_result\":\n if (tool_result) {\n handler.onEvent({\n type: \"tool_call\",\n tool_call: {\n id: tool_result.call_id,\n type: \"function\",\n function: {\n name: \"tool_result\",\n arguments: JSON.stringify(tool_result),\n },\n },\n });\n }\n break;\n\n case \"done\":\n handler.onDone({\n message: message.message || { role: \"assistant\", content: \"\" },\n conversationId: conversation_id || \"\",\n title,\n usage,\n });\n break;\n\n case \"error\":\n handler.onError(new Error(error || \"Unknown stream error\"));\n break;\n }\n }\n\n private async handleToolCall(message: ToolCallMessage): Promise<void> {\n console.log(`[SDK] handleToolCall received:`, JSON.stringify(message));\n const { id, call_id, name, arguments: args } = message;\n const msgId = id || call_id;\n\n // Extract actual tool name (remove app prefix)\n const toolName = name.includes(\":\") ? name.split(\":\")[1] : name;\n console.log(`[SDK] Looking for handler: '${toolName}', available handlers:`, Array.from(this.toolHandlers.keys()));\n const handler = this.toolHandlers.get(toolName);\n\n // Emit event for debugging/logging\n this.emit(\"tool_call\", { name: toolName, arguments: args });\n\n if (!handler) {\n await this.sendToolResult(msgId, call_id, false, undefined, `Tool '${toolName}' not found`);\n return;\n }\n\n try {\n console.log(`[SDK] Executing tool '${toolName}' with args:`, args);\n // Execute with timeout\n const result = await Promise.race([\n handler(args),\n this.createTimeout(this.config.toolExecutionTimeout!),\n ]);\n\n console.log(`[SDK] Tool '${toolName}' completed successfully`);\n await this.sendToolResult(msgId, call_id, true, result);\n } catch (e) {\n const error = e instanceof Error ? e.message : String(e);\n console.log(`[SDK] Tool '${toolName}' failed:`, error);\n await this.sendToolResult(msgId, call_id, false, undefined, error);\n }\n }\n\n private async sendToolResult(\n id: string,\n callId: string,\n success: boolean,\n result?: unknown,\n error?: string\n ): Promise<void> {\n const message: ToolResultMessage = {\n id,\n type: \"tool_result\",\n call_id: callId,\n success,\n result,\n error,\n };\n\n console.log(`[SDK] Sending tool_result for ${callId}:`, success ? 'success' : `error: ${error}`);\n this.send(message);\n }\n\n // ============================================\n // Connection Management\n // ============================================\n\n private handleDisconnect(reason: string): void {\n this.stopHeartbeat();\n this.state.connected = false;\n this.state.registered = false;\n this.emit(\"disconnected\", reason);\n\n // Reject pending requests\n for (const [, pending] of this.pendingRequests) {\n pending.reject(new Error(\"Disconnected\"));\n }\n this.pendingRequests.clear();\n\n // Schedule reconnect\n this.scheduleReconnect();\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return;\n\n // Exponential backoff: 500ms, 1s, 2s, 4s, max 5s\n // With jitter to avoid thundering herd\n const baseDelay = Math.min(\n 500 * Math.pow(2, this.state.reconnectAttempts),\n 5000 // Cap at 5 seconds\n );\n const jitter = Math.random() * 500; // 0-500ms random jitter\n const delay = baseDelay + jitter;\n\n console.log(`[SDK] Scheduling reconnect in ${Math.round(delay)}ms (attempt ${this.state.reconnectAttempts + 1})`);\n\n this.reconnectTimer = setTimeout(async () => {\n this.reconnectTimer = null;\n this.state.reconnectAttempts++;\n\n // Re-read connection info\n const info = this.discovery.read();\n if (info) {\n try {\n await this.connectWithInfo(info);\n } catch (e) {\n console.error(\"[SDK] Reconnect failed:\", e);\n this.scheduleReconnect();\n }\n } else {\n // Sanqian not running, keep trying\n this.scheduleReconnect();\n }\n }, delay);\n }\n\n private stopReconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n // ============================================\n // Heartbeat\n // ============================================\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n\n this.heartbeatTimer = setInterval(() => {\n const message: HeartbeatMessage = {\n type: \"heartbeat\",\n timestamp: new Date().toISOString(),\n };\n this.send(message);\n }, this.config.heartbeatInterval);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n // ============================================\n // Communication\n // ============================================\n\n private send(message: object): void {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n const data = JSON.stringify(message);\n console.log(`[SDK] WebSocket send:`, data.substring(0, 200));\n this.ws.send(data);\n } else {\n console.error(`[SDK] WebSocket not open, cannot send:`, message);\n }\n }\n\n private sendAndWait<T>(\n message: object,\n id: string,\n timeout: number\n ): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(\"Request timeout\"));\n }, timeout);\n\n this.pendingRequests.set(id, {\n resolve: (value) => {\n clearTimeout(timer);\n resolve(value as T);\n },\n reject: (error) => {\n clearTimeout(timer);\n reject(error);\n },\n });\n\n this.send(message);\n });\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Get current connection state\n */\n getState(): ConnectionState {\n return { ...this.state };\n }\n\n /**\n * Check if connected and registered\n */\n isConnected(): boolean {\n return this.state.connected && this.state.registered;\n }\n\n /**\n * Wait for connection to be established.\n * Used internally by chat() and chatStream() for auto-reconnect.\n * Relies on background scheduleReconnect() to do the actual reconnection.\n */\n private async waitForConnection(timeout: number = 30000): Promise<void> {\n if (this.isConnected()) {\n return;\n }\n\n console.log(\"[SDK] Not connected, waiting for reconnection...\");\n\n // Wait for 'registered' event (emitted when connection + registration completes)\n // IMPORTANT: Register listener BEFORE triggering reconnect to avoid race condition\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n const timer = setTimeout(() => {\n if (!resolved) {\n resolved = true;\n reject(new Error(\"Connection timeout. Please ensure Sanqian is running.\"));\n }\n }, timeout);\n\n this.once(\"registered\", () => {\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n resolve();\n }\n });\n\n // Check again after registering listener (connection might have completed)\n if (this.isConnected()) {\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n resolve();\n }\n return;\n }\n\n // If no reconnect is scheduled, trigger one\n if (!this.reconnectTimer) {\n const info = this.discovery.read();\n if (!info && this.config.autoLaunchSanqian) {\n console.log(\"[SDK] Sanqian not running, attempting to launch...\");\n const launched = this.discovery.launchSanqian(this.config.sanqianPath);\n if (launched) {\n this.scheduleReconnect();\n }\n } else if (!info) {\n // No connection info and can't auto-launch\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n reject(new Error(\"Sanqian is not running. Please start it manually.\"));\n }\n } else {\n // Connection info exists, trigger reconnect\n this.scheduleReconnect();\n }\n }\n });\n }\n\n /**\n * Update tool list dynamically\n */\n async updateTools(tools: ToolDefinition[]): Promise<void> {\n // Update local handlers\n this.toolHandlers.clear();\n for (const tool of tools) {\n this.toolHandlers.set(tool.name, tool.handler);\n }\n\n // Send update to Sanqian\n if (this.isConnected()) {\n const msgId = this.generateId();\n const message = {\n id: msgId,\n type: \"tools_update\",\n tools: tools.map((t) => ({\n name: `${this.config.appName}:${t.name}`,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n await this.sendAndWait(message, msgId, 5000);\n }\n }\n\n // ============================================\n // Private Agent API\n // ============================================\n\n /**\n * Create or update a private agent\n *\n * Private agents are only visible to this SDK app, not in Sanqian UI.\n * If agent with same ID exists, it will be updated.\n *\n * @param config - Agent configuration\n * @returns Full agent info (or agent_id string for backward compatibility)\n */\n async createAgent(config: AgentConfig): Promise<AgentInfo> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: CreateAgentMessage = {\n id: msgId,\n type: \"create_agent\",\n agent: config,\n };\n\n const response = await this.sendAndWait<CreateAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to create agent\");\n }\n\n // Return full agent info if available, fallback to minimal info\n if (response.agent) {\n return response.agent;\n }\n\n // Backward compatibility: construct minimal AgentInfo from agent_id\n return {\n agent_id: response.agent_id!,\n name: config.name,\n description: config.description,\n tools: config.tools || [],\n };\n }\n\n /**\n * List all private agents owned by this app\n */\n async listAgents(): Promise<AgentInfo[]> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: ListAgentsMessage = {\n id: msgId,\n type: \"list_agents\",\n };\n\n const response = await this.sendAndWait<ListAgentsAckMessage>(message, msgId, 10000);\n\n if (response.error) {\n throw new Error(response.error);\n }\n\n return response.agents;\n }\n\n /**\n * Delete a private agent\n */\n async deleteAgent(agentId: string): Promise<void> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: DeleteAgentMessage = {\n id: msgId,\n type: \"delete_agent\",\n agent_id: agentId,\n };\n\n const response = await this.sendAndWait<DeleteAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to delete agent\");\n }\n }\n\n /**\n * Update a private agent\n *\n * Only updates the fields that are provided.\n * @param agentId - Agent ID (short name or full)\n * @param updates - Fields to update\n * @returns Updated agent info\n */\n async updateAgent(\n agentId: string,\n updates: Omit<AgentUpdateConfig, \"agent_id\">\n ): Promise<AgentInfo> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: UpdateAgentMessage = {\n id: msgId,\n type: \"update_agent\",\n agent: {\n agent_id: agentId,\n ...updates,\n },\n };\n\n const response = await this.sendAndWait<UpdateAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success || !response.agent) {\n throw new Error(response.error || \"Failed to update agent\");\n }\n\n return response.agent;\n }\n\n // ============================================\n // Conversation API\n // ============================================\n\n /**\n * List conversations for this app\n */\n async listConversations(options?: {\n agentId?: string;\n limit?: number;\n offset?: number;\n }): Promise<{ conversations: ConversationInfo[]; total: number }> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: ListConversationsMessage = {\n id: msgId,\n type: \"list_conversations\",\n agent_id: options?.agentId,\n limit: options?.limit,\n offset: options?.offset,\n };\n\n const response = await this.sendAndWait<ListConversationsAckMessage>(message, msgId, 10000);\n\n if (response.error) {\n throw new Error(response.error);\n }\n\n return {\n conversations: response.conversations,\n total: response.total,\n };\n }\n\n /**\n * Get conversation details with messages\n */\n async getConversation(\n conversationId: string,\n options?: {\n includeMessages?: boolean;\n messageLimit?: number;\n messageOffset?: number;\n }\n ): Promise<ConversationDetail> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: GetConversationMessage = {\n id: msgId,\n type: \"get_conversation\",\n conversation_id: conversationId,\n include_messages: options?.includeMessages ?? true,\n message_limit: options?.messageLimit,\n message_offset: options?.messageOffset,\n };\n\n const response = await this.sendAndWait<GetConversationAckMessage>(message, msgId, 10000);\n\n if (!response.success || !response.conversation) {\n throw new Error(response.error || \"Failed to get conversation\");\n }\n\n return response.conversation;\n }\n\n /**\n * Delete a conversation\n */\n async deleteConversation(conversationId: string): Promise<void> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: DeleteConversationMessage = {\n id: msgId,\n type: \"delete_conversation\",\n conversation_id: conversationId,\n };\n\n const response = await this.sendAndWait<DeleteConversationAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to delete conversation\");\n }\n }\n\n // ============================================\n // Chat API\n // ============================================\n\n // Pending stream handlers for streaming chat\n private streamHandlers: Map<string, {\n onEvent: (event: ChatStreamEvent) => void;\n onDone: (response: ChatResponse) => void;\n onError: (error: Error) => void;\n }> = new Map();\n\n /**\n * Send a chat message to an agent (non-streaming)\n *\n * Supports two modes:\n * - Stateless (no conversationId): Caller maintains message history\n * - Stateful (with conversationId): Server stores messages in conversations table\n *\n * @param agentId - Agent ID (short name or full, SDK auto-prefixes)\n * @param messages - Messages to send\n * @param options - Optional settings including conversationId and remoteTools\n * @returns Chat response with assistant message and conversation ID\n */\n async chat(\n agentId: string,\n messages: ChatMessage[],\n options?: {\n conversationId?: string;\n remoteTools?: RemoteToolDefinition[];\n }\n ): Promise<ChatResponse> {\n // Auto-reconnect if disconnected\n if (!this.isConnected()) {\n console.log(\"[SDK] Not connected, attempting to reconnect...\");\n await this.waitForConnection();\n }\n\n const msgId = this.generateId();\n const message: ChatRequestMessage = {\n id: msgId,\n type: \"chat\",\n agent_id: agentId,\n messages,\n conversation_id: options?.conversationId,\n stream: false,\n remote_tools: options?.remoteTools?.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n // Longer timeout for chat (agent may take time with complex multi-tool tasks)\n const response = await this.sendAndWait<ChatResponseMessage>(message, msgId, 600000); // 10 minutes\n\n if (!response.success) {\n throw new Error(response.error || \"Chat request failed\");\n }\n\n return {\n message: response.message!,\n conversationId: response.conversation_id!,\n title: response.title,\n usage: response.usage,\n };\n }\n\n /**\n * Send a chat message to an agent with streaming response\n *\n * Supports two modes:\n * - Stateless (no conversationId): Caller maintains message history\n * - Stateful (with conversationId): Server stores messages in conversations table\n *\n * @param agentId - Agent ID (short name or full, SDK auto-prefixes)\n * @param messages - Messages to send\n * @param options - Optional settings including conversationId and remoteTools\n * @returns AsyncIterable of stream events\n */\n async *chatStream(\n agentId: string,\n messages: ChatMessage[],\n options?: {\n conversationId?: string;\n remoteTools?: RemoteToolDefinition[];\n }\n ): AsyncGenerator<ChatStreamEvent> {\n // Auto-reconnect if disconnected\n if (!this.isConnected()) {\n console.log(\"[SDK] Not connected, attempting to reconnect...\");\n await this.waitForConnection();\n }\n\n const msgId = this.generateId();\n const message: ChatRequestMessage = {\n id: msgId,\n type: \"chat\",\n agent_id: agentId,\n messages,\n conversation_id: options?.conversationId,\n stream: true,\n remote_tools: options?.remoteTools?.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n // Create a queue to yield events\n const eventQueue: ChatStreamEvent[] = [];\n let done = false;\n let error: Error | null = null;\n let resolveNext: (() => void) | null = null;\n\n // Register stream handler\n this.streamHandlers.set(msgId, {\n onEvent: (event) => {\n eventQueue.push(event);\n resolveNext?.();\n },\n onDone: (response) => {\n eventQueue.push({\n type: \"done\",\n conversationId: response.conversationId,\n title: response.title,\n });\n done = true;\n resolveNext?.();\n },\n onError: (e) => {\n error = e;\n resolveNext?.();\n },\n });\n\n try {\n // Send request\n this.send(message);\n\n // Yield events as they arrive\n while (!done && !error) {\n if (eventQueue.length > 0) {\n yield eventQueue.shift()!;\n } else {\n // Wait for next event\n await new Promise<void>((resolve) => {\n resolveNext = resolve;\n });\n resolveNext = null;\n }\n }\n\n // Yield remaining events\n while (eventQueue.length > 0) {\n yield eventQueue.shift()!;\n }\n\n if (error) {\n throw error;\n }\n } finally {\n this.streamHandlers.delete(msgId);\n }\n }\n\n /**\n * Start a new conversation with an agent\n *\n * Returns a Conversation object for convenient multi-turn chat.\n *\n * @example\n * ```typescript\n * const conv = sdk.startConversation('assistant')\n * const r1 = await conv.send('Hello')\n * const r2 = await conv.send('Follow up question')\n * console.log(conv.id) // conversation ID\n * ```\n */\n startConversation(agentId: string): Conversation {\n return new Conversation(this, agentId);\n }\n\n // ============================================\n // Events\n // ============================================\n\n /**\n * Add event listener\n */\n on<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(listener as EventListener<SDKEventName>);\n return this;\n }\n\n /**\n * Remove event listener\n */\n off<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n this.eventListeners.get(event)?.delete(listener as EventListener<SDKEventName>);\n return this;\n }\n\n /**\n * Add one-time event listener (auto-removes after first call)\n */\n once<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n const onceWrapper = ((...args: Parameters<SDKEvents[T]>) => {\n this.off(event, onceWrapper as SDKEvents[T]);\n (listener as (...args: unknown[]) => void)(...args);\n }) as SDKEvents[T];\n return this.on(event, onceWrapper);\n }\n\n private emit<T extends SDKEventName>(\n event: T,\n ...args: Parameters<SDKEvents[T]>\n ): void {\n const listeners = this.eventListeners.get(event);\n if (listeners) {\n for (const listener of listeners) {\n try {\n (listener as (...args: unknown[]) => void)(...args);\n } catch (e) {\n console.error(`[SDK] Event listener error for '${event}':`, e);\n }\n }\n }\n }\n\n // ============================================\n // Utilities\n // ============================================\n\n private generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n private createTimeout(ms: number): Promise<never> {\n return new Promise((_, reject) => {\n setTimeout(() => reject(new Error(\"Timeout\")), ms);\n });\n }\n}\n\n/**\n * Conversation helper for multi-turn chat\n *\n * Automatically manages conversationId for stateful conversations.\n */\nexport class Conversation {\n private sdk: SanqianSDK;\n private agentId: string;\n private _conversationId: string | null = null;\n\n constructor(sdk: SanqianSDK, agentId: string, conversationId?: string) {\n this.sdk = sdk;\n this.agentId = agentId;\n this._conversationId = conversationId || null;\n }\n\n /**\n * Get the conversation ID (available after first message)\n */\n get id(): string | null {\n return this._conversationId;\n }\n\n /**\n * Send a message and get a response\n *\n * First call creates a new conversation, subsequent calls continue it.\n */\n async send(content: string, options?: { remoteTools?: RemoteToolDefinition[] }): Promise<ChatResponse> {\n const response = await this.sdk.chat(\n this.agentId,\n [{ role: \"user\", content }],\n {\n conversationId: this._conversationId || undefined,\n remoteTools: options?.remoteTools,\n }\n );\n\n // Store conversation ID for subsequent calls\n if (response.conversationId && !this._conversationId) {\n this._conversationId = response.conversationId;\n }\n\n return response;\n }\n\n /**\n * Send a message with streaming response\n */\n async *sendStream(\n content: string,\n options?: { remoteTools?: RemoteToolDefinition[] }\n ): AsyncGenerator<ChatStreamEvent> {\n const stream = this.sdk.chatStream(\n this.agentId,\n [{ role: \"user\", content }],\n {\n conversationId: this._conversationId || undefined,\n remoteTools: options?.remoteTools,\n }\n );\n\n for await (const event of stream) {\n // Capture conversation ID from done event\n if (event.type === \"done\" && event.conversationId && !this._conversationId) {\n this._conversationId = event.conversationId;\n }\n yield event;\n }\n }\n\n /**\n * Delete this conversation\n */\n async delete(): Promise<void> {\n if (!this._conversationId) {\n throw new Error(\"No conversation to delete\");\n }\n await this.sdk.deleteConversation(this._conversationId);\n this._conversationId = null;\n }\n\n /**\n * Get conversation details including message history\n */\n async getDetails(options?: { messageLimit?: number }): Promise<ConversationDetail> {\n if (!this._conversationId) {\n throw new Error(\"No conversation to get details for\");\n }\n return this.sdk.getConversation(this._conversationId, {\n includeMessages: true,\n messageLimit: options?.messageLimit,\n });\n }\n}\n","/**\n * Service Discovery Module\n *\n * Reads Sanqian's connection info from ~/.sanqian/runtime/connection.json\n * and monitors file changes for reconnection.\n * Also handles auto-launching Sanqian when needed.\n */\n\nimport { existsSync, readFileSync, watch, type FSWatcher } from \"fs\";\nimport { homedir, platform } from \"os\";\nimport { join } from \"path\";\nimport { spawn } from \"child_process\";\nimport type { ConnectionInfo } from \"./types\";\n\nexport class DiscoveryManager {\n private connectionInfo: ConnectionInfo | null = null;\n private watcher: FSWatcher | null = null;\n private onChange: ((info: ConnectionInfo | null) => void) | null = null;\n private pollInterval: ReturnType<typeof setInterval> | null = null;\n\n /**\n * Get the path to connection.json\n */\n getConnectionFilePath(): string {\n return join(homedir(), \".sanqian\", \"runtime\", \"connection.json\");\n }\n\n /**\n * Read and validate connection info\n *\n * Returns null if:\n * - File doesn't exist\n * - File is invalid JSON\n * - Process is not running\n */\n read(): ConnectionInfo | null {\n const filePath = this.getConnectionFilePath();\n\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(filePath, \"utf-8\");\n const info = JSON.parse(content) as ConnectionInfo;\n\n // Validate required fields\n if (!info.port || !info.token || !info.pid) {\n return null;\n }\n\n // Check if Sanqian process is running\n if (!this.isProcessRunning(info.pid)) {\n return null;\n }\n\n this.connectionInfo = info;\n return info;\n } catch {\n return null;\n }\n }\n\n /**\n * Start watching for connection file changes\n */\n startWatching(onChange: (info: ConnectionInfo | null) => void): void {\n this.onChange = onChange;\n\n const dir = join(homedir(), \".sanqian\", \"runtime\");\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n // Directory doesn't exist, poll for it\n this.pollInterval = setInterval(() => {\n if (existsSync(dir)) {\n if (this.pollInterval) {\n clearInterval(this.pollInterval);\n this.pollInterval = null;\n }\n this.setupWatcher(dir);\n }\n }, 2000);\n return;\n }\n\n this.setupWatcher(dir);\n }\n\n private setupWatcher(dir: string): void {\n try {\n this.watcher = watch(dir, (event, filename) => {\n if (filename === \"connection.json\") {\n // Debounce rapid changes\n setTimeout(() => {\n const newInfo = this.read();\n const oldInfoStr = JSON.stringify(this.connectionInfo);\n const newInfoStr = JSON.stringify(newInfo);\n\n // Only trigger if actually changed\n if (oldInfoStr !== newInfoStr) {\n this.connectionInfo = newInfo;\n this.onChange?.(newInfo);\n }\n }, 100);\n }\n });\n } catch (e) {\n console.error(\"Failed to watch connection file:\", e);\n }\n }\n\n /**\n * Stop watching\n */\n stopWatching(): void {\n if (this.pollInterval) {\n clearInterval(this.pollInterval);\n this.pollInterval = null;\n }\n this.watcher?.close();\n this.watcher = null;\n this.onChange = null;\n }\n\n /**\n * Check if a process is running by PID\n */\n private isProcessRunning(pid: number): boolean {\n try {\n // Sending signal 0 doesn't kill the process, just checks if it exists\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get cached connection info (may be stale)\n */\n getCached(): ConnectionInfo | null {\n return this.connectionInfo;\n }\n\n /**\n * Build WebSocket URL from connection info\n */\n buildWebSocketUrl(info: ConnectionInfo): string {\n return `ws://127.0.0.1:${info.port}${info.ws_path}?token=${info.token}`;\n }\n\n /**\n * Build HTTP base URL from connection info\n */\n buildHttpUrl(info: ConnectionInfo): string {\n return `http://127.0.0.1:${info.port}`;\n }\n\n /**\n * Find Sanqian executable path\n * Searches in standard installation locations for each platform\n */\n findSanqianPath(customPath?: string): string | null {\n // If custom path provided, use it\n if (customPath) {\n if (existsSync(customPath)) {\n return customPath;\n }\n console.warn(`[SDK] Custom Sanqian path not found: ${customPath}`);\n return null;\n }\n\n const os = platform();\n const searchPaths: string[] = [];\n\n if (os === \"darwin\") {\n // macOS: Standard app locations + common dev locations\n searchPaths.push(\n // Production: installed app\n \"/Applications/Sanqian.app/Contents/MacOS/Sanqian\",\n join(homedir(), \"Applications/Sanqian.app/Contents/MacOS/Sanqian\"),\n // Development: electron-builder output\n join(homedir(), \"dev/sanqian/dist/mac-arm64/Sanqian.app/Contents/MacOS/Sanqian\"),\n join(homedir(), \"dev/sanqian/dist/mac/Sanqian.app/Contents/MacOS/Sanqian\"),\n );\n } else if (os === \"win32\") {\n // Windows: Standard installation paths + dev locations\n const programFiles = process.env.PROGRAMFILES || \"C:\\\\Program Files\";\n const programFilesX86 =\n process.env[\"PROGRAMFILES(X86)\"] || \"C:\\\\Program Files (x86)\";\n const localAppData =\n process.env.LOCALAPPDATA || join(homedir(), \"AppData\", \"Local\");\n\n searchPaths.push(\n // Production: installed app\n join(programFiles, \"Sanqian\", \"Sanqian.exe\"),\n join(programFilesX86, \"Sanqian\", \"Sanqian.exe\"),\n join(localAppData, \"Programs\", \"Sanqian\", \"Sanqian.exe\"),\n // Development: electron-builder output\n join(homedir(), \"dev\", \"sanqian\", \"dist\", \"win-unpacked\", \"Sanqian.exe\"),\n );\n } else {\n // Linux: Common locations\n searchPaths.push(\n \"/usr/bin/sanqian\",\n \"/usr/local/bin/sanqian\",\n join(homedir(), \".local/bin/sanqian\"),\n \"/opt/Sanqian/sanqian\",\n // Development\n join(homedir(), \"dev/sanqian/dist/linux-unpacked/sanqian\"),\n );\n }\n\n for (const path of searchPaths) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n }\n\n /**\n * Launch Sanqian in hidden/tray mode\n * Returns true if launch was initiated successfully\n */\n launchSanqian(customPath?: string): boolean {\n const sanqianPath = this.findSanqianPath(customPath);\n\n if (!sanqianPath) {\n console.error(\"[SDK] Sanqian executable not found\");\n return false;\n }\n\n console.log(`[SDK] Launching Sanqian from: ${sanqianPath}`);\n\n try {\n const os = platform();\n\n if (os === \"darwin\") {\n // macOS: Use 'open' command with --args for hidden start\n // This properly launches the app bundle\n const appPath = sanqianPath.replace(\n \"/Contents/MacOS/Sanqian\",\n \"\",\n );\n spawn(\"open\", [\"-a\", appPath, \"--args\", \"--hidden\"], {\n detached: true,\n stdio: \"ignore\",\n }).unref();\n } else {\n // Windows/Linux: Direct execution with --hidden flag\n spawn(sanqianPath, [\"--hidden\"], {\n detached: true,\n stdio: \"ignore\",\n // On Windows, hide the console window\n ...(os === \"win32\" && {\n windowsHide: true,\n shell: false,\n }),\n }).unref();\n }\n\n return true;\n } catch (e) {\n console.error(\"[SDK] Failed to launch Sanqian:\", e);\n return false;\n }\n }\n\n /**\n * Check if Sanqian is running\n */\n isSanqianRunning(): boolean {\n return this.read() !== null;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/discovery.ts"],"sourcesContent":["/**\n * Sanqian SDK Client\n *\n * Main class for connecting to Sanqian and registering tools.\n */\n\nimport WebSocket from \"ws\";\nimport { DiscoveryManager } from \"./discovery\";\nimport type {\n SDKConfig,\n ConnectionInfo,\n ConnectionState,\n ToolDefinition,\n RegisterMessage,\n RegisterAckMessage,\n ToolCallMessage,\n ToolResultMessage,\n HeartbeatMessage,\n SDKEvents,\n SDKEventName,\n JSONSchema,\n AgentConfig,\n AgentInfo,\n AgentUpdateConfig,\n ConversationInfo,\n ConversationDetail,\n CreateAgentMessage,\n CreateAgentAckMessage,\n UpdateAgentMessage,\n UpdateAgentAckMessage,\n ListAgentsMessage,\n ListAgentsAckMessage,\n DeleteAgentMessage,\n DeleteAgentAckMessage,\n ListConversationsMessage,\n ListConversationsAckMessage,\n GetConversationMessage,\n GetConversationAckMessage,\n DeleteConversationMessage,\n DeleteConversationAckMessage,\n ChatMessage,\n ChatResponse,\n ChatRequestMessage,\n ChatResponseMessage,\n ChatStreamMessage,\n ChatStreamEvent,\n RemoteToolDefinition,\n} from \"./types\";\n\ntype EventListener<T extends SDKEventName> = SDKEvents[T];\n\nexport class SanqianSDK {\n private config: SDKConfig;\n private discovery: DiscoveryManager;\n private ws: WebSocket | null = null;\n private connectionInfo: ConnectionInfo | null = null;\n\n private state: ConnectionState = {\n connected: false,\n registering: false,\n registered: false,\n reconnectAttempts: 0,\n };\n\n // Tool handlers by name\n private toolHandlers: Map<string, (args: unknown) => Promise<unknown>> =\n new Map();\n\n // Pending request futures\n private pendingRequests: Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n }\n > = new Map();\n\n // Timers\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private heartbeatAckPending: boolean = false;\n private missedHeartbeats: number = 0;\n private static readonly MAX_MISSED_HEARTBEATS = 2;\n\n // Event listeners\n private eventListeners: Map<SDKEventName, Set<EventListener<SDKEventName>>> =\n new Map();\n\n constructor(config: SDKConfig) {\n this.config = {\n reconnectInterval: 5000,\n heartbeatInterval: 30000,\n toolExecutionTimeout: 30000,\n ...config,\n };\n this.discovery = new DiscoveryManager();\n\n // Register tool handlers\n for (const tool of config.tools) {\n this.toolHandlers.set(tool.name, tool.handler);\n }\n }\n\n // ============================================\n // Lifecycle\n // ============================================\n\n /**\n * Connect to Sanqian\n *\n * Reads connection info, establishes WebSocket, and registers app.\n * Returns when registration is complete.\n *\n * If autoLaunchSanqian is enabled and Sanqian is not running,\n * SDK will attempt to start it in hidden/tray mode.\n */\n async connect(): Promise<void> {\n // Read connection info\n const info = this.discovery.read();\n\n if (!info) {\n // Sanqian not running\n // Try to auto-launch if enabled\n if (this.config.autoLaunchSanqian) {\n console.log(\"[SDK] Sanqian not running, attempting to launch...\");\n const launched = this.discovery.launchSanqian(this.config.sanqianPath);\n if (launched) {\n console.log(\"[SDK] Sanqian launch initiated, waiting for startup...\");\n } else {\n console.warn(\"[SDK] Failed to launch Sanqian, will wait for manual start\");\n }\n }\n\n // Start watching and wait for connection\n return new Promise((resolve, reject) => {\n console.log(\"[SDK] Waiting for Sanqian...\");\n\n const timeout = setTimeout(() => {\n this.discovery.stopWatching();\n reject(new Error(\"Sanqian connection timeout\"));\n }, 60000); // 60 second timeout\n\n this.discovery.startWatching(async (newInfo) => {\n if (newInfo) {\n clearTimeout(timeout);\n this.discovery.stopWatching();\n try {\n await this.connectWithInfo(newInfo);\n resolve();\n } catch (e) {\n reject(e);\n }\n }\n });\n });\n }\n\n await this.connectWithInfo(info);\n }\n\n /**\n * Connect with known connection info\n */\n private async connectWithInfo(info: ConnectionInfo): Promise<void> {\n this.connectionInfo = info;\n const url = this.discovery.buildWebSocketUrl(info);\n\n return new Promise((resolve, reject) => {\n console.log(`[SDK] Connecting to ${url}`);\n\n this.ws = new WebSocket(url);\n\n const connectTimeout = setTimeout(() => {\n reject(new Error(\"WebSocket connection timeout\"));\n this.ws?.close();\n }, 10000);\n\n this.ws.on(\"open\", async () => {\n clearTimeout(connectTimeout);\n console.log(\"[SDK] WebSocket connected\");\n this.state.connected = true;\n this.state.reconnectAttempts = 0;\n this.emit(\"connected\");\n\n try {\n await this.register();\n resolve();\n } catch (e) {\n reject(e);\n }\n });\n\n this.ws.on(\"close\", (code, reason) => {\n console.log(`[SDK] WebSocket closed: ${code} ${reason.toString()}`);\n this.handleDisconnect(reason.toString());\n });\n\n this.ws.on(\"error\", (error) => {\n console.error(\"[SDK] WebSocket error:\", error);\n this.state.lastError = error;\n this.emit(\"error\", error);\n });\n\n this.ws.on(\"message\", (data) => {\n try {\n const message = JSON.parse(data.toString());\n this.handleMessage(message);\n } catch (e) {\n console.error(\"[SDK] Failed to parse message:\", e);\n }\n });\n });\n }\n\n /**\n * Disconnect from Sanqian\n */\n async disconnect(): Promise<void> {\n this.stopHeartbeat();\n this.stopReconnect();\n this.discovery.stopWatching();\n\n if (this.ws) {\n this.ws.close(1000, \"Client disconnect\");\n this.ws = null;\n }\n\n this.state = {\n connected: false,\n registering: false,\n registered: false,\n reconnectAttempts: 0,\n };\n }\n\n // ============================================\n // Registration\n // ============================================\n\n private async register(): Promise<void> {\n this.state.registering = true;\n\n const msgId = this.generateId();\n\n const message: RegisterMessage = {\n id: msgId,\n type: \"register\",\n app: {\n name: this.config.appName,\n version: this.config.appVersion,\n display_name: this.config.displayName,\n launch_command: this.config.launchCommand,\n },\n tools: this.config.tools.map((t) => ({\n name: `${this.config.appName}:${t.name}`,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n try {\n const response = await this.sendAndWait<RegisterAckMessage>(\n message,\n msgId,\n 10000\n );\n\n if (!response.success) {\n throw new Error(response.error || \"Registration failed\");\n }\n\n this.state.registering = false;\n this.state.registered = true;\n this.startHeartbeat();\n this.emit(\"registered\");\n\n console.log(`[SDK] Registered as '${this.config.appName}'`);\n } catch (e) {\n this.state.registering = false;\n throw e;\n }\n }\n\n // ============================================\n // Message Handling\n // ============================================\n\n private handleMessage(message: { id?: string; type: string; [key: string]: unknown }): void {\n const { id, type } = message;\n\n // Check if this is a response to a pending request\n if (id && this.pendingRequests.has(id)) {\n const pending = this.pendingRequests.get(id)!;\n this.pendingRequests.delete(id);\n pending.resolve(message);\n return;\n }\n\n // Handle server-initiated messages\n switch (type) {\n case \"tool_call\":\n this.handleToolCall(message as unknown as ToolCallMessage);\n break;\n\n case \"heartbeat_ack\":\n // Heartbeat acknowledged - reset missed counter\n this.heartbeatAckPending = false;\n this.missedHeartbeats = 0;\n break;\n\n case \"chat_stream\":\n // Handle streaming chat response\n this.handleChatStream(message as unknown as ChatStreamMessage);\n break;\n\n default:\n console.warn(`[SDK] Unknown message type: ${type}`);\n }\n }\n\n private handleChatStream(message: ChatStreamMessage): void {\n const { id, event, content, tool_call, tool_result, conversation_id, title, usage, error } = message;\n\n if (!id) return;\n\n const handler = this.streamHandlers.get(id);\n if (!handler) {\n console.warn(`[SDK] No stream handler for message ${id}`);\n return;\n }\n\n switch (event) {\n case \"text\":\n handler.onEvent({ type: \"text\", content });\n break;\n\n case \"tool_call\":\n handler.onEvent({ type: \"tool_call\", tool_call });\n break;\n\n case \"tool_result\":\n if (tool_result) {\n handler.onEvent({\n type: \"tool_call\",\n tool_call: {\n id: tool_result.call_id,\n type: \"function\",\n function: {\n name: \"tool_result\",\n arguments: JSON.stringify(tool_result),\n },\n },\n });\n }\n break;\n\n case \"done\":\n handler.onDone({\n message: message.message || { role: \"assistant\", content: \"\" },\n conversationId: conversation_id || \"\",\n title,\n usage,\n });\n break;\n\n case \"error\":\n handler.onError(new Error(error || \"Unknown stream error\"));\n break;\n }\n }\n\n private async handleToolCall(message: ToolCallMessage): Promise<void> {\n console.log(`[SDK] handleToolCall received:`, JSON.stringify(message));\n const { id, call_id, name, arguments: args } = message;\n const msgId = id || call_id;\n\n // Extract actual tool name (remove app prefix)\n const toolName = name.includes(\":\") ? name.split(\":\")[1] : name;\n console.log(`[SDK] Looking for handler: '${toolName}', available handlers:`, Array.from(this.toolHandlers.keys()));\n const handler = this.toolHandlers.get(toolName);\n\n // Emit event for debugging/logging\n this.emit(\"tool_call\", { name: toolName, arguments: args });\n\n if (!handler) {\n await this.sendToolResult(msgId, call_id, false, undefined, `Tool '${toolName}' not found`);\n return;\n }\n\n try {\n console.log(`[SDK] Executing tool '${toolName}' with args:`, args);\n // Execute with timeout\n const result = await Promise.race([\n handler(args),\n this.createTimeout(this.config.toolExecutionTimeout!),\n ]);\n\n console.log(`[SDK] Tool '${toolName}' completed successfully`);\n await this.sendToolResult(msgId, call_id, true, result);\n } catch (e) {\n // Preserve full error info for debugging\n const errorMessage = e instanceof Error ? e.message : String(e);\n const errorStack = e instanceof Error ? e.stack : undefined;\n console.log(`[SDK] Tool '${toolName}' failed:`, errorMessage);\n if (errorStack) {\n console.log(`[SDK] Tool error stack:`, errorStack);\n }\n await this.sendToolResult(msgId, call_id, false, undefined, errorMessage);\n }\n }\n\n private async sendToolResult(\n id: string,\n callId: string,\n success: boolean,\n result?: unknown,\n error?: string\n ): Promise<void> {\n const message: ToolResultMessage = {\n id,\n type: \"tool_result\",\n call_id: callId,\n success,\n result,\n error,\n };\n\n console.log(`[SDK] Sending tool_result for ${callId}:`, success ? 'success' : `error: ${error}`);\n this.send(message);\n }\n\n // ============================================\n // Connection Management\n // ============================================\n\n private handleDisconnect(reason: string): void {\n this.stopHeartbeat();\n this.state.connected = false;\n this.state.registered = false;\n this.emit(\"disconnected\", reason);\n\n // Reject pending requests\n for (const [, pending] of this.pendingRequests) {\n pending.reject(new Error(\"Disconnected\"));\n }\n this.pendingRequests.clear();\n\n // Schedule reconnect\n this.scheduleReconnect();\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return;\n\n // Exponential backoff: 500ms, 1s, 2s, 4s, max 5s\n // With jitter to avoid thundering herd\n const baseDelay = Math.min(\n 500 * Math.pow(2, this.state.reconnectAttempts),\n 5000 // Cap at 5 seconds\n );\n const jitter = Math.random() * 500; // 0-500ms random jitter\n const delay = baseDelay + jitter;\n\n console.log(`[SDK] Scheduling reconnect in ${Math.round(delay)}ms (attempt ${this.state.reconnectAttempts + 1})`);\n\n this.reconnectTimer = setTimeout(async () => {\n this.reconnectTimer = null;\n this.state.reconnectAttempts++;\n\n // Re-read connection info\n const info = this.discovery.read();\n if (info) {\n try {\n await this.connectWithInfo(info);\n } catch (e) {\n console.error(\"[SDK] Reconnect failed:\", e);\n this.scheduleReconnect();\n }\n } else {\n // Sanqian not running, keep trying\n this.scheduleReconnect();\n }\n }, delay);\n }\n\n private stopReconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n // ============================================\n // Heartbeat\n // ============================================\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.missedHeartbeats = 0;\n this.heartbeatAckPending = false;\n\n this.heartbeatTimer = setInterval(() => {\n // Check if previous heartbeat was acknowledged\n if (this.heartbeatAckPending) {\n this.missedHeartbeats++;\n console.warn(`[SDK] Missed heartbeat ack (${this.missedHeartbeats}/${SanqianSDK.MAX_MISSED_HEARTBEATS})`);\n\n if (this.missedHeartbeats >= SanqianSDK.MAX_MISSED_HEARTBEATS) {\n console.error(\"[SDK] Too many missed heartbeat acks, connection may be dead\");\n // Force disconnect and reconnect\n this.ws?.close(4000, \"Heartbeat timeout\");\n return;\n }\n }\n\n const message: HeartbeatMessage = {\n type: \"heartbeat\",\n timestamp: new Date().toISOString(),\n };\n this.heartbeatAckPending = true;\n try {\n this.send(message);\n } catch (e) {\n // send() failure in heartbeat should trigger reconnect\n console.error(\"[SDK] Heartbeat send failed:\", e);\n this.ws?.close(4000, \"Heartbeat send failed\");\n }\n }, this.config.heartbeatInterval);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n // ============================================\n // Communication\n // ============================================\n\n private send(message: object): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n const error = new Error(\n `WebSocket not open (state: ${this.ws?.readyState ?? \"null\"}), cannot send message`\n );\n console.error(`[SDK] ${error.message}:`, message);\n throw error;\n }\n\n const data = JSON.stringify(message);\n console.log(`[SDK] WebSocket send:`, data.substring(0, 200));\n this.ws.send(data);\n }\n\n private sendAndWait<T>(\n message: object,\n id: string,\n timeout: number\n ): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(id);\n reject(new Error(\"Request timeout\"));\n }, timeout);\n\n this.pendingRequests.set(id, {\n resolve: (value) => {\n clearTimeout(timer);\n resolve(value as T);\n },\n reject: (error) => {\n clearTimeout(timer);\n reject(error);\n },\n });\n\n try {\n this.send(message);\n } catch (e) {\n // Clean up on send failure to prevent memory leak\n clearTimeout(timer);\n this.pendingRequests.delete(id);\n reject(e);\n }\n });\n }\n\n // ============================================\n // Public API\n // ============================================\n\n /**\n * Get current connection state\n */\n getState(): ConnectionState {\n return { ...this.state };\n }\n\n /**\n * Check if connected and registered\n */\n isConnected(): boolean {\n return this.state.connected && this.state.registered;\n }\n\n /**\n * Wait for connection to be established.\n * Used internally by chat() and chatStream() for auto-reconnect.\n * Relies on background scheduleReconnect() to do the actual reconnection.\n */\n private async waitForConnection(timeout: number = 30000): Promise<void> {\n if (this.isConnected()) {\n return;\n }\n\n console.log(\"[SDK] Not connected, waiting for reconnection...\");\n\n // Wait for 'registered' event (emitted when connection + registration completes)\n // IMPORTANT: Register listener BEFORE triggering reconnect to avoid race condition\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n const timer = setTimeout(() => {\n if (!resolved) {\n resolved = true;\n reject(new Error(\"Connection timeout. Please ensure Sanqian is running.\"));\n }\n }, timeout);\n\n this.once(\"registered\", () => {\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n resolve();\n }\n });\n\n // Check again after registering listener (connection might have completed)\n if (this.isConnected()) {\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n resolve();\n }\n return;\n }\n\n // If no reconnect is scheduled, trigger one\n if (!this.reconnectTimer) {\n const info = this.discovery.read();\n if (!info && this.config.autoLaunchSanqian) {\n console.log(\"[SDK] Sanqian not running, attempting to launch...\");\n const launched = this.discovery.launchSanqian(this.config.sanqianPath);\n if (launched) {\n this.scheduleReconnect();\n }\n } else if (!info) {\n // No connection info and can't auto-launch\n if (!resolved) {\n resolved = true;\n clearTimeout(timer);\n reject(new Error(\"Sanqian is not running. Please start it manually.\"));\n }\n } else {\n // Connection info exists, trigger reconnect\n this.scheduleReconnect();\n }\n }\n });\n }\n\n /**\n * Update tool list dynamically\n */\n async updateTools(tools: ToolDefinition[]): Promise<void> {\n // Update local handlers\n this.toolHandlers.clear();\n for (const tool of tools) {\n this.toolHandlers.set(tool.name, tool.handler);\n }\n\n // Send update to Sanqian\n if (this.isConnected()) {\n const msgId = this.generateId();\n const message = {\n id: msgId,\n type: \"tools_update\",\n tools: tools.map((t) => ({\n name: `${this.config.appName}:${t.name}`,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n await this.sendAndWait(message, msgId, 5000);\n }\n }\n\n // ============================================\n // Private Agent API\n // ============================================\n\n /**\n * Create or update a private agent\n *\n * Private agents are only visible to this SDK app, not in Sanqian UI.\n * If agent with same ID exists, it will be updated.\n *\n * @param config - Agent configuration\n * @returns Full agent info (or agent_id string for backward compatibility)\n */\n async createAgent(config: AgentConfig): Promise<AgentInfo> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: CreateAgentMessage = {\n id: msgId,\n type: \"create_agent\",\n agent: config,\n };\n\n const response = await this.sendAndWait<CreateAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to create agent\");\n }\n\n // Return full agent info if available, fallback to minimal info\n if (response.agent) {\n return response.agent;\n }\n\n // Backward compatibility: construct minimal AgentInfo from agent_id\n return {\n agent_id: response.agent_id!,\n name: config.name,\n description: config.description,\n tools: config.tools || [],\n };\n }\n\n /**\n * List all private agents owned by this app\n */\n async listAgents(): Promise<AgentInfo[]> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: ListAgentsMessage = {\n id: msgId,\n type: \"list_agents\",\n };\n\n const response = await this.sendAndWait<ListAgentsAckMessage>(message, msgId, 10000);\n\n if (response.error) {\n throw new Error(response.error);\n }\n\n return response.agents;\n }\n\n /**\n * Delete a private agent\n */\n async deleteAgent(agentId: string): Promise<void> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: DeleteAgentMessage = {\n id: msgId,\n type: \"delete_agent\",\n agent_id: agentId,\n };\n\n const response = await this.sendAndWait<DeleteAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to delete agent\");\n }\n }\n\n /**\n * Update a private agent\n *\n * Only updates the fields that are provided.\n * @param agentId - Agent ID (short name or full)\n * @param updates - Fields to update\n * @returns Updated agent info\n */\n async updateAgent(\n agentId: string,\n updates: Omit<AgentUpdateConfig, \"agent_id\">\n ): Promise<AgentInfo> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: UpdateAgentMessage = {\n id: msgId,\n type: \"update_agent\",\n agent: {\n agent_id: agentId,\n ...updates,\n },\n };\n\n const response = await this.sendAndWait<UpdateAgentAckMessage>(message, msgId, 10000);\n\n if (!response.success || !response.agent) {\n throw new Error(response.error || \"Failed to update agent\");\n }\n\n return response.agent;\n }\n\n // ============================================\n // Conversation API\n // ============================================\n\n /**\n * List conversations for this app\n */\n async listConversations(options?: {\n agentId?: string;\n limit?: number;\n offset?: number;\n }): Promise<{ conversations: ConversationInfo[]; total: number }> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: ListConversationsMessage = {\n id: msgId,\n type: \"list_conversations\",\n agent_id: options?.agentId,\n limit: options?.limit,\n offset: options?.offset,\n };\n\n const response = await this.sendAndWait<ListConversationsAckMessage>(message, msgId, 10000);\n\n if (response.error) {\n throw new Error(response.error);\n }\n\n return {\n conversations: response.conversations,\n total: response.total,\n };\n }\n\n /**\n * Get conversation details with messages\n */\n async getConversation(\n conversationId: string,\n options?: {\n includeMessages?: boolean;\n messageLimit?: number;\n messageOffset?: number;\n }\n ): Promise<ConversationDetail> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: GetConversationMessage = {\n id: msgId,\n type: \"get_conversation\",\n conversation_id: conversationId,\n include_messages: options?.includeMessages ?? true,\n message_limit: options?.messageLimit,\n message_offset: options?.messageOffset,\n };\n\n const response = await this.sendAndWait<GetConversationAckMessage>(message, msgId, 10000);\n\n if (!response.success || !response.conversation) {\n throw new Error(response.error || \"Failed to get conversation\");\n }\n\n return response.conversation;\n }\n\n /**\n * Delete a conversation\n */\n async deleteConversation(conversationId: string): Promise<void> {\n if (!this.isConnected()) {\n throw new Error(\"Not connected to Sanqian\");\n }\n\n const msgId = this.generateId();\n const message: DeleteConversationMessage = {\n id: msgId,\n type: \"delete_conversation\",\n conversation_id: conversationId,\n };\n\n const response = await this.sendAndWait<DeleteConversationAckMessage>(message, msgId, 10000);\n\n if (!response.success) {\n throw new Error(response.error || \"Failed to delete conversation\");\n }\n }\n\n // ============================================\n // Chat API\n // ============================================\n\n // Pending stream handlers for streaming chat\n private streamHandlers: Map<string, {\n onEvent: (event: ChatStreamEvent) => void;\n onDone: (response: ChatResponse) => void;\n onError: (error: Error) => void;\n }> = new Map();\n\n /**\n * Send a chat message to an agent (non-streaming)\n *\n * Supports two modes:\n * - Stateless (no conversationId): Caller maintains message history\n * - Stateful (with conversationId): Server stores messages in conversations table\n *\n * @param agentId - Agent ID (short name or full, SDK auto-prefixes)\n * @param messages - Messages to send\n * @param options - Optional settings including conversationId and remoteTools\n * @returns Chat response with assistant message and conversation ID\n */\n async chat(\n agentId: string,\n messages: ChatMessage[],\n options?: {\n conversationId?: string;\n remoteTools?: RemoteToolDefinition[];\n }\n ): Promise<ChatResponse> {\n // Auto-reconnect if disconnected\n if (!this.isConnected()) {\n console.log(\"[SDK] Not connected, attempting to reconnect...\");\n await this.waitForConnection();\n }\n\n const msgId = this.generateId();\n const message: ChatRequestMessage = {\n id: msgId,\n type: \"chat\",\n agent_id: agentId,\n messages,\n conversation_id: options?.conversationId,\n stream: false,\n remote_tools: options?.remoteTools?.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n // Longer timeout for chat (agent may take time with complex multi-tool tasks)\n const response = await this.sendAndWait<ChatResponseMessage>(message, msgId, 600000); // 10 minutes\n\n if (!response.success) {\n throw new Error(response.error || \"Chat request failed\");\n }\n\n return {\n message: response.message!,\n conversationId: response.conversation_id!,\n title: response.title,\n usage: response.usage,\n };\n }\n\n /**\n * Send a chat message to an agent with streaming response\n *\n * Supports two modes:\n * - Stateless (no conversationId): Caller maintains message history\n * - Stateful (with conversationId): Server stores messages in conversations table\n *\n * @param agentId - Agent ID (short name or full, SDK auto-prefixes)\n * @param messages - Messages to send\n * @param options - Optional settings including conversationId and remoteTools\n * @returns AsyncIterable of stream events\n */\n async *chatStream(\n agentId: string,\n messages: ChatMessage[],\n options?: {\n conversationId?: string;\n remoteTools?: RemoteToolDefinition[];\n }\n ): AsyncGenerator<ChatStreamEvent> {\n // Auto-reconnect if disconnected\n if (!this.isConnected()) {\n console.log(\"[SDK] Not connected, attempting to reconnect...\");\n await this.waitForConnection();\n }\n\n const msgId = this.generateId();\n const message: ChatRequestMessage = {\n id: msgId,\n type: \"chat\",\n agent_id: agentId,\n messages,\n conversation_id: options?.conversationId,\n stream: true,\n remote_tools: options?.remoteTools?.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n })),\n };\n\n // Create a queue to yield events\n const eventQueue: ChatStreamEvent[] = [];\n let done = false;\n let error: Error | null = null;\n let resolveNext: (() => void) | null = null;\n\n // Register stream handler\n this.streamHandlers.set(msgId, {\n onEvent: (event) => {\n eventQueue.push(event);\n resolveNext?.();\n },\n onDone: (response) => {\n eventQueue.push({\n type: \"done\",\n conversationId: response.conversationId,\n title: response.title,\n });\n done = true;\n resolveNext?.();\n },\n onError: (e) => {\n error = e;\n resolveNext?.();\n },\n });\n\n try {\n // Send request\n this.send(message);\n\n // Yield events as they arrive\n while (!done && !error) {\n if (eventQueue.length > 0) {\n yield eventQueue.shift()!;\n } else {\n // Wait for next event\n await new Promise<void>((resolve) => {\n resolveNext = resolve;\n });\n resolveNext = null;\n }\n }\n\n // Yield remaining events\n while (eventQueue.length > 0) {\n yield eventQueue.shift()!;\n }\n\n if (error) {\n throw error;\n }\n } finally {\n this.streamHandlers.delete(msgId);\n }\n }\n\n /**\n * Start a new conversation with an agent\n *\n * Returns a Conversation object for convenient multi-turn chat.\n *\n * @example\n * ```typescript\n * const conv = sdk.startConversation('assistant')\n * const r1 = await conv.send('Hello')\n * const r2 = await conv.send('Follow up question')\n * console.log(conv.id) // conversation ID\n * ```\n */\n startConversation(agentId: string): Conversation {\n return new Conversation(this, agentId);\n }\n\n // ============================================\n // Events\n // ============================================\n\n /**\n * Add event listener\n */\n on<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners.get(event)!.add(listener as EventListener<SDKEventName>);\n return this;\n }\n\n /**\n * Remove event listener\n */\n off<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n this.eventListeners.get(event)?.delete(listener as EventListener<SDKEventName>);\n return this;\n }\n\n /**\n * Add one-time event listener (auto-removes after first call)\n */\n once<T extends SDKEventName>(event: T, listener: SDKEvents[T]): this {\n const onceWrapper = ((...args: Parameters<SDKEvents[T]>) => {\n this.off(event, onceWrapper as SDKEvents[T]);\n (listener as (...args: unknown[]) => void)(...args);\n }) as SDKEvents[T];\n return this.on(event, onceWrapper);\n }\n\n private emit<T extends SDKEventName>(\n event: T,\n ...args: Parameters<SDKEvents[T]>\n ): void {\n const listeners = this.eventListeners.get(event);\n if (listeners) {\n for (const listener of listeners) {\n try {\n (listener as (...args: unknown[]) => void)(...args);\n } catch (e) {\n console.error(`[SDK] Event listener error for '${event}':`, e);\n }\n }\n }\n }\n\n // ============================================\n // Utilities\n // ============================================\n\n private generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n private createTimeout(ms: number): Promise<never> {\n return new Promise((_, reject) => {\n setTimeout(() => reject(new Error(\"Timeout\")), ms);\n });\n }\n}\n\n/**\n * Conversation helper for multi-turn chat\n *\n * Automatically manages conversationId for stateful conversations.\n */\nexport class Conversation {\n private sdk: SanqianSDK;\n private agentId: string;\n private _conversationId: string | null = null;\n\n constructor(sdk: SanqianSDK, agentId: string, conversationId?: string) {\n this.sdk = sdk;\n this.agentId = agentId;\n this._conversationId = conversationId || null;\n }\n\n /**\n * Get the conversation ID (available after first message)\n */\n get id(): string | null {\n return this._conversationId;\n }\n\n /**\n * Send a message and get a response\n *\n * First call creates a new conversation, subsequent calls continue it.\n */\n async send(content: string, options?: { remoteTools?: RemoteToolDefinition[] }): Promise<ChatResponse> {\n const response = await this.sdk.chat(\n this.agentId,\n [{ role: \"user\", content }],\n {\n conversationId: this._conversationId || undefined,\n remoteTools: options?.remoteTools,\n }\n );\n\n // Store conversation ID for subsequent calls\n if (response.conversationId && !this._conversationId) {\n this._conversationId = response.conversationId;\n }\n\n return response;\n }\n\n /**\n * Send a message with streaming response\n */\n async *sendStream(\n content: string,\n options?: { remoteTools?: RemoteToolDefinition[] }\n ): AsyncGenerator<ChatStreamEvent> {\n const stream = this.sdk.chatStream(\n this.agentId,\n [{ role: \"user\", content }],\n {\n conversationId: this._conversationId || undefined,\n remoteTools: options?.remoteTools,\n }\n );\n\n for await (const event of stream) {\n // Capture conversation ID from done event\n if (event.type === \"done\" && event.conversationId && !this._conversationId) {\n this._conversationId = event.conversationId;\n }\n yield event;\n }\n }\n\n /**\n * Delete this conversation\n */\n async delete(): Promise<void> {\n if (!this._conversationId) {\n throw new Error(\"No conversation to delete\");\n }\n await this.sdk.deleteConversation(this._conversationId);\n this._conversationId = null;\n }\n\n /**\n * Get conversation details including message history\n */\n async getDetails(options?: { messageLimit?: number }): Promise<ConversationDetail> {\n if (!this._conversationId) {\n throw new Error(\"No conversation to get details for\");\n }\n return this.sdk.getConversation(this._conversationId, {\n includeMessages: true,\n messageLimit: options?.messageLimit,\n });\n }\n}\n","/**\n * Service Discovery Module\n *\n * Reads Sanqian's connection info from ~/.sanqian/runtime/connection.json\n * and monitors file changes for reconnection.\n * Also handles auto-launching Sanqian when needed.\n */\n\nimport { existsSync, readFileSync, watch, type FSWatcher } from \"fs\";\nimport { homedir, platform } from \"os\";\nimport { join } from \"path\";\nimport { spawn, execSync } from \"child_process\";\nimport type { ConnectionInfo } from \"./types\";\n\nexport class DiscoveryManager {\n private connectionInfo: ConnectionInfo | null = null;\n private watcher: FSWatcher | null = null;\n private onChange: ((info: ConnectionInfo | null) => void) | null = null;\n private pollInterval: ReturnType<typeof setInterval> | null = null;\n\n /**\n * Get the path to connection.json\n */\n getConnectionFilePath(): string {\n return join(homedir(), \".sanqian\", \"runtime\", \"connection.json\");\n }\n\n /**\n * Read and validate connection info\n *\n * Returns null if:\n * - File doesn't exist\n * - File is invalid JSON\n * - Process is not running\n */\n read(): ConnectionInfo | null {\n const filePath = this.getConnectionFilePath();\n\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(filePath, \"utf-8\");\n const info = JSON.parse(content) as ConnectionInfo;\n\n // Validate required fields\n if (!info.port || !info.token || !info.pid) {\n return null;\n }\n\n // Check if Sanqian process is running\n if (!this.isProcessRunning(info.pid)) {\n return null;\n }\n\n this.connectionInfo = info;\n return info;\n } catch {\n return null;\n }\n }\n\n /**\n * Start watching for connection file changes\n */\n startWatching(onChange: (info: ConnectionInfo | null) => void): void {\n this.onChange = onChange;\n\n const dir = join(homedir(), \".sanqian\", \"runtime\");\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n // Directory doesn't exist, poll for it\n this.pollInterval = setInterval(() => {\n if (existsSync(dir)) {\n if (this.pollInterval) {\n clearInterval(this.pollInterval);\n this.pollInterval = null;\n }\n this.setupWatcher(dir);\n }\n }, 2000);\n return;\n }\n\n this.setupWatcher(dir);\n }\n\n private setupWatcher(dir: string): void {\n try {\n this.watcher = watch(dir, (event, filename) => {\n if (filename === \"connection.json\") {\n // Debounce rapid changes\n setTimeout(() => {\n const newInfo = this.read();\n const oldInfoStr = JSON.stringify(this.connectionInfo);\n const newInfoStr = JSON.stringify(newInfo);\n\n // Only trigger if actually changed\n if (oldInfoStr !== newInfoStr) {\n this.connectionInfo = newInfo;\n this.onChange?.(newInfo);\n }\n }, 100);\n }\n });\n } catch (e) {\n console.error(\"Failed to watch connection file:\", e);\n }\n }\n\n /**\n * Stop watching\n */\n stopWatching(): void {\n if (this.pollInterval) {\n clearInterval(this.pollInterval);\n this.pollInterval = null;\n }\n this.watcher?.close();\n this.watcher = null;\n this.onChange = null;\n }\n\n /**\n * Check if a process is running by PID\n *\n * Cross-platform implementation:\n * - macOS/Linux: process.kill(pid, 0) works correctly\n * - Windows: process.kill(pid, 0) can give false positives due to PID reuse,\n * so we use tasklist command for reliable checking\n */\n private isProcessRunning(pid: number): boolean {\n try {\n if (platform() === \"win32\") {\n // Windows: Use tasklist to check if PID exists\n // This is more reliable than process.kill on Windows\n try {\n const result = execSync(`tasklist /FI \"PID eq ${pid}\" /NH`, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n // tasklist returns the process name if found, or \"INFO: No tasks are running...\"\n return !result.includes(\"No tasks are running\");\n } catch {\n return false;\n }\n } else {\n // macOS/Linux: Sending signal 0 doesn't kill the process, just checks if it exists\n process.kill(pid, 0);\n return true;\n }\n } catch {\n return false;\n }\n }\n\n /**\n * Get cached connection info (may be stale)\n */\n getCached(): ConnectionInfo | null {\n return this.connectionInfo;\n }\n\n /**\n * Build WebSocket URL from connection info\n */\n buildWebSocketUrl(info: ConnectionInfo): string {\n return `ws://127.0.0.1:${info.port}${info.ws_path}?token=${info.token}`;\n }\n\n /**\n * Build HTTP base URL from connection info\n */\n buildHttpUrl(info: ConnectionInfo): string {\n return `http://127.0.0.1:${info.port}`;\n }\n\n /**\n * Find Sanqian executable path\n * Searches in standard installation locations for each platform\n */\n findSanqianPath(customPath?: string): string | null {\n // If custom path provided, use it\n if (customPath) {\n if (existsSync(customPath)) {\n return customPath;\n }\n console.warn(`[SDK] Custom Sanqian path not found: ${customPath}`);\n return null;\n }\n\n const os = platform();\n const searchPaths: string[] = [];\n\n if (os === \"darwin\") {\n // macOS: Standard app locations + common dev locations\n searchPaths.push(\n // Production: installed app\n \"/Applications/Sanqian.app/Contents/MacOS/Sanqian\",\n join(homedir(), \"Applications/Sanqian.app/Contents/MacOS/Sanqian\"),\n // Development: electron-builder output\n join(homedir(), \"dev/sanqian/dist/mac-arm64/Sanqian.app/Contents/MacOS/Sanqian\"),\n join(homedir(), \"dev/sanqian/dist/mac/Sanqian.app/Contents/MacOS/Sanqian\"),\n );\n } else if (os === \"win32\") {\n // Windows: Standard installation paths + dev locations\n const programFiles = process.env.PROGRAMFILES || \"C:\\\\Program Files\";\n const programFilesX86 =\n process.env[\"PROGRAMFILES(X86)\"] || \"C:\\\\Program Files (x86)\";\n const localAppData =\n process.env.LOCALAPPDATA || join(homedir(), \"AppData\", \"Local\");\n\n searchPaths.push(\n // Production: installed app\n join(programFiles, \"Sanqian\", \"Sanqian.exe\"),\n join(programFilesX86, \"Sanqian\", \"Sanqian.exe\"),\n join(localAppData, \"Programs\", \"Sanqian\", \"Sanqian.exe\"),\n // Development: electron-builder output\n join(homedir(), \"dev\", \"sanqian\", \"dist\", \"win-unpacked\", \"Sanqian.exe\"),\n );\n } else {\n // Linux: Common locations\n searchPaths.push(\n \"/usr/bin/sanqian\",\n \"/usr/local/bin/sanqian\",\n join(homedir(), \".local/bin/sanqian\"),\n \"/opt/Sanqian/sanqian\",\n // Development\n join(homedir(), \"dev/sanqian/dist/linux-unpacked/sanqian\"),\n );\n }\n\n for (const path of searchPaths) {\n if (existsSync(path)) {\n return path;\n }\n }\n\n return null;\n }\n\n /**\n * Launch Sanqian in hidden/tray mode\n * Returns true if launch was initiated successfully\n */\n launchSanqian(customPath?: string): boolean {\n const sanqianPath = this.findSanqianPath(customPath);\n\n if (!sanqianPath) {\n console.error(\"[SDK] Sanqian executable not found\");\n return false;\n }\n\n console.log(`[SDK] Launching Sanqian from: ${sanqianPath}`);\n\n try {\n const os = platform();\n\n if (os === \"darwin\") {\n // macOS: Use 'open' command with --args for hidden start\n // This properly launches the app bundle\n const appPath = sanqianPath.replace(\n \"/Contents/MacOS/Sanqian\",\n \"\",\n );\n spawn(\"open\", [\"-a\", appPath, \"--args\", \"--hidden\"], {\n detached: true,\n stdio: \"ignore\",\n }).unref();\n } else {\n // Windows/Linux: Direct execution with --hidden flag\n spawn(sanqianPath, [\"--hidden\"], {\n detached: true,\n stdio: \"ignore\",\n // On Windows, hide the console window\n ...(os === \"win32\" && {\n windowsHide: true,\n shell: false,\n }),\n }).unref();\n }\n\n return true;\n } catch (e) {\n console.error(\"[SDK] Failed to launch Sanqian:\", e);\n return false;\n }\n }\n\n /**\n * Check if Sanqian is running\n */\n isSanqianRunning(): boolean {\n return this.read() !== null;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yushaw/sanqian-sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Sanqian SDK for third-party app integration",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",