# Snow CLI Usage Documentation - SSE Service Mode Welcome to Snow CLI! Agentic coding in your terminal. ## Quick Start Want to quickly experience the SSE client? We provide a complete browser test client: **Location**: `source/test/sse-client/index.html` Simply open this file in your browser and connect to the SSE server to start testing. ## What is SSE Service Mode SSE (Server-Sent Events) service mode allows you to run Snow CLI as a backend service, providing AI capabilities to external applications. It is perfect for: - Web application integration - Mobile app backend - Third-party tool integration - Microservice architecture - Custom chat interfaces ## Basic Usage ### Starting SSE Server #### Basic Startup ```bash # Use default port 3000 (foreground) snow --sse # Specify port snow --sse --sse-port 8080 # Specify working directory snow --sse --work-dir /path/to/project # Custom interaction timeout (default 300000ms, i.e., 5 minutes) snow --sse --sse-timeout 600000 # Set to 10 minutes # Combined usage snow --sse --sse-port 8080 --work-dir /path/to/project --sse-timeout 600000 ``` #### Background Daemon Mode If you don't want to occupy the terminal, you can run the server as a background daemon: ```bash # Start background daemon (default port 3000) snow --sse-daemon # Specify different ports (supports multiple instances) snow --sse-daemon --sse-port 3000 snow --sse-daemon --sse-port 8080 snow --sse-daemon --sse-port 9000 # Specify full parameters snow --sse-daemon --sse-port 8080 --work-dir /path/to/project --sse-timeout 600000 # Check all daemon status snow --sse-status # Stop daemon (three methods) snow --sse-stop # Stop default port 3000 daemon snow --sse-stop --sse-port 8080 # Stop by port number snow --sse-stop 12345 # Stop by PID ``` Daemon features: - Supports multiple instances (different ports) - Runs in background, doesn't occupy terminal - Individual log file per port: `~/.snow/sse-logs/port-.log` - Individual PID file per port: `~/.snow/sse-daemons/port-.pid` - Supports stop by port or PID - Status check shows all running daemons #### Enabling YOLO Mode on Startup While the SSE server itself doesn't use the `--yolo` parameter, you can achieve similar functionality through the following methods: **Method 1: Client message with yoloMode** This is the recommended approach, allowing flexible control over whether each request uses YOLO mode: ```javascript // Specify YOLO mode when sending message await fetch('http://localhost:3000/message', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ type: 'chat', content: 'Your question', yoloMode: true, // Enable YOLO mode }), }); ``` **Method 2: Configure auto-approval list** Add commonly used tools to the project's permission configuration file for default auto-approval: ```bash # Edit project permission configuration vi .snow/permissions.json ``` ```json { "alwaysApprovedTools": [ "filesystem-read", "filesystem-edit", "filesystem-create", "codebase-search", "ace-search", "notebook-add" ] } ``` This way, tools in the list will be automatically approved without requiring confirmation each time. **Notes**: - SSE server startup doesn't support `--yolo` parameter - YOLO mode needs to be enabled via client message's `yoloMode` field - Or implement tool auto-approval via `.snow/permissions.json` configuration - Sensitive commands require confirmation even in YOLO mode ### Server Information After startup, the terminal will display a beautiful server status interface: ``` ✓ SSE server started Port: 3000 | Working Directory: /Users/xxx/project | ● Running Available Endpoints: http://localhost:3000/events POST http://localhost:3000/message POST http://localhost:3000/session/create POST http://localhost:3000/session/load GET http://localhost:3000/session/list GET http://localhost:3000/session/rollback-points?sessionId={sessionId} DELETE http://localhost:3000/session/{sessionId} GET http://localhost:3000/health Running Logs: [14:30:45] SSE service started on port 3000 [14:30:50] Created new session: abc-123 Press Ctrl+C to stop server ``` ## API Endpoints ### 1. SSE Event Stream Connection **Endpoint**: `GET /events` Establish SSE connection to receive real-time event stream. #### JavaScript Example ```javascript const eventSource = new EventSource('http://localhost:3000/events'); eventSource.onmessage = event => { const data = JSON.parse(event.data); console.log('Received event:', data); switch (data.type) { case 'connected': console.log( 'Connection successful, connection ID:', data.data.connectionId, ); break; case 'message': if (data.data.streaming) { console.log('AI is responding:', data.data.content); } else if (data.data.role === 'user') { console.log('User message:', data.data.content); } break; case 'tool_confirmation_request': // User confirmation needed for tool execution handleToolConfirmation(data); break; case 'complete': console.log('Conversation complete'); break; } }; ``` ### 2. Send Message **Endpoint**: `POST /message` **Content-Type**: `application/json` #### Send Plain Text Message ```javascript async function sendMessage(content) { const response = await fetch('http://localhost:3000/message', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'chat', content: content, }), }); return await response.json(); } // Usage example await sendMessage('Help me create a React component'); ``` #### Continuous Conversation with Session ```javascript async function continueConversation(content, sessionId) { const response = await fetch('http://localhost:3000/message', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'chat', content: content, sessionId: sessionId, // Use session ID to continue conversation }), }); return await response.json(); } // Session ID will be returned in complete event eventSource.onmessage = event => { const data = JSON.parse(event.data); if (data.type === 'complete') { const sessionId = data.data.sessionId; console.log('Session ID:', sessionId); } }; ``` #### Send Image Message ```javascript async function sendImageMessage(content, imageFile) { // Convert image to Base64 Data URI const reader = new FileReader(); const imageData = await new Promise((resolve, reject) => { reader.onload = e => resolve(e.target.result); reader.onerror = reject; reader.readAsDataURL(imageFile); }); const response = await fetch('http://localhost:3000/message', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'chat', content: content || 'Please analyze this image', images: [ { data: imageData, // Complete data URI, e.g., data:image/png;base64,iVBORw0KG... mimeType: imageFile.type, // e.g., image/png, image/jpeg }, ], }), }); return await response.json(); } // Usage example const fileInput = document.querySelector('input[type="file"]'); fileInput.addEventListener('change', async e => { const file = e.target.files[0]; if (file && file.type.startsWith('image/')) { await sendImageMessage('What is this?', file); } }); ``` #### Abort Running Task ```javascript async function abortTask(sessionId) { const response = await fetch('http://localhost:3000/message', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'abort', sessionId: sessionId, }), }); return await response.json(); } // Listen for abort confirmation eventSource.onmessage = event => { const data = JSON.parse(event.data); if (data.type === 'complete' && data.data.cancelled) { console.log('Task has been aborted by user'); } }; ``` #### Enable YOLO Mode ```javascript async function sendWithYolo(content) { const response = await fetch('http://localhost:3000/message', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'chat', content: content, yoloMode: true, // Auto-approve all non-sensitive tools }), }); return await response.json(); } ``` ### 3. Session Management #### Create New Session **Endpoint**: `POST /session/create` **Content-Type**: `application/json` Create a new conversation session, returns session information and automatically binds to current connection. ```javascript async function createSession() { const response = await fetch('http://localhost:3000/session/create', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ connectionId: 'conn_xxx', // Optional, specify connection ID }), }); const data = await response.json(); console.log('Session ID:', data.session.id); console.log('Created at:', data.session.createdAt); return data.session; } ``` #### Load Existing Session **Endpoint**: `POST /session/load` **Content-Type**: `application/json` Load a saved session to restore conversation context. ```javascript async function loadSession(sessionId) { const response = await fetch('http://localhost:3000/session/load', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ sessionId: sessionId, connectionId: 'conn_xxx', // Optional, specify connection ID }), }); const data = await response.json(); if (data.success) { console.log('Session loaded:', data.session.id); console.log('Message count:', data.session.messages.length); return data.session; } else { console.error('Load failed:', data.error); } } ``` #### Get Session List **Endpoint**: `GET /session/list` **Query Parameters**: - `page`: Page number, starting from 0 (optional, default 0) - `pageSize`: Items per page (optional, default 20, max 200) - `q`: Search keyword (optional, searches message content in sessions) Get all saved sessions with pagination and search support. ```javascript async function listSessions(page = 0, pageSize = 20, searchQuery = '') { const params = new URLSearchParams({ page: page.toString(), pageSize: pageSize.toString(), }); if (searchQuery) { params.append('q', searchQuery); } const response = await fetch(`http://localhost:3000/session/list?${params}`); const data = await response.json(); console.log('Total sessions:', data.total); console.log('Current page:', data.page); console.log('Page size:', data.pageSize); console.log('Session list:', data.sessions); // Session list example // data.sessions = [ // { // id: 'abc-123', // createdAt: '2025-12-30T10:00:00.000Z', // updatedAt: '2025-12-30T10:30:00.000Z', // messageCount: 10, // firstMessage: 'Help me create a function' // }, // ... // ] return data; } ``` #### Get Rollback Points **Endpoint**: `GET /session/rollback-points` **Query Parameters**: - `sessionId`: Session ID (required) Returns a list of user messages in the specified session that can be used as rollback points (demo use). ```javascript async function getRollbackPoints(sessionId) { const params = new URLSearchParams({sessionId}); const response = await fetch( `http://localhost:3000/session/rollback-points?${params.toString()}`, ); const data = await response.json(); // Example (key fields): // { // success: true, // sessionId: 'abc-123', // points: [ // { // messageIndex: 0, // role: 'user', // timestamp: 1730000000000, // summary: '...', // hasSnapshot: true, // snapshot: {timestamp: 1730000000000, fileCount: 12}, // filesToRollbackCount: 5 // } // ] // } return data; } ``` #### Delete Session **Endpoint**: `DELETE /session/{sessionId}` Delete the specified session and all its data. ```javascript async function deleteSession(sessionId) { const response = await fetch(`http://localhost:3000/session/${sessionId}`, { method: 'DELETE', }); const data = await response.json(); if (data.success) { console.log('Session deleted:', data.deleted); } return data; } ``` ### 4. Health Check **Endpoint**: `GET /health` Check server status and current connection count. ```javascript async function checkHealth() { const response = await fetch('http://localhost:3000/health'); const data = await response.json(); console.log('Status:', data.status); console.log('Connections:', data.connections); } ``` ## Event Type Descriptions ### connected Connection successful event. ```javascript { type: 'connected', data: { connectionId: 'conn_1234567890' }, timestamp: '2025-12-30T15:30:00.000Z' } ``` ### message Message event (user or AI). ```javascript // User message { type: 'message', data: { role: 'user', content: 'Help me create a function' } } // AI streaming response { type: 'message', data: { role: 'assistant', content: 'Sure, let me help you...', streaming: true } } // AI final response { type: 'message', data: { role: 'assistant', content: 'Complete response content', streaming: false } } ``` ### tool_call Tool invocation event. ```javascript { type: 'tool_call', data: { name: 'filesystem-create', arguments: { filePath: 'example.js', content: '...' } } } ``` ### tool_confirmation_request Request confirmation for tool execution. ```javascript { type: 'tool_confirmation_request', data: { toolCall: { function: { name: 'terminal-execute', arguments: '{"command":"rm -rf node_modules"}' } }, isSensitive: true, // Whether it's a sensitive command sensitiveInfo: { pattern: 'rm -rf', description: 'Delete files or directories' }, availableOptions: [ {value: 'approve', label: 'Approve once'}, {value: 'approve_always', label: 'Always approve'}, // Only for non-sensitive commands {value: 'reject_with_reply', label: 'Reject with reply'}, {value: 'reject', label: 'Reject and end session'} ] }, requestId: 'req_1234567890' } ``` ### tool_result Tool execution result. ```javascript { type: 'tool_result', data: { content: 'Execution successful', status: 'success' } } ``` ### user_question_request AI asking user a question. ```javascript { type: 'user_question_request', data: { question: 'Please select an option', options: ['Option 1', 'Option 2', 'Option 3'], multiSelect: false }, requestId: 'req_1234567890' } ``` ### usage Token usage information. ```javascript { type: 'usage', data: { input_tokens: 150, output_tokens: 200 } } ``` ### error Error message. ```javascript { type: 'error', data: { message: 'Error description', stack: 'Error stack (optional)' } } ``` ### complete Conversation completed. ```javascript { type: 'complete', data: { usage: { input_tokens: 150, output_tokens: 200 }, tokenCount: 350, sessionId: 'abc-123-def-456', // Session ID cancelled: false // Whether cancelled by user (optional) } } ``` ### abort Task abort request (sent by client). ```javascript // Client sends abort request await fetch('http://localhost:3000/message', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ type: 'abort', sessionId: 'abc-123-def-456' }) }); // Server responds with abort confirmation { type: 'message', data: { role: 'assistant', content: 'Task has been aborted' }, timestamp: '2025-12-30T15:30:00.000Z' } // Followed by complete event { type: 'complete', data: { usage: {input_tokens: 0, output_tokens: 0}, tokenCount: 0, sessionId: 'abc-123-def-456', cancelled: true } } ``` ## Tool Confirmation Flow ### Confirmation Request Response When receiving a `tool_confirmation_request` event, send a confirmation response: ```javascript async function handleToolConfirmation(event) { const toolCall = event.data.toolCall; const options = event.data.availableOptions; // Display tool information to user console.log('Tool:', toolCall.function.name); console.log('Arguments:', toolCall.function.arguments); console.log('Available options:', options); // Send response after user selection const response = await fetch('http://localhost:3000/message', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'tool_confirmation_response', requestId: event.requestId, response: 'approve', // or 'approve_always', 'reject', {type: 'reject_with_reply', reason: '...'} }), }); return await response.json(); } ``` ### Confirmation Options Explained | Option | Value | Description | Applicable Scenario | | ----------------- | -------------------------------------------- | ------------------------------------- | ------------------- | | Approve once | `'approve'` | Approve this execution only | All tools | | Always approve | `'approve_always'` | Approve and add to auto-approval list | Non-sensitive only | | Reject with reply | `{type: 'reject_with_reply', reason: '...'}` | Reject and tell AI the reason | All tools | | Reject and end | `'reject'` | Reject and end session | All tools | ### Sensitive Command Detection The system automatically detects sensitive commands (like `rm -rf`, `sudo`, etc.). Sensitive commands: - Will not show "Always approve" option - Require confirmation even in YOLO mode - Display warning information and matched command pattern For sensitive command configuration, refer to: [Sensitive Commands Configuration](./06.Sensitive%20Commands%20Configuration.md) ## User Question Response When receiving a `user_question_request` event: ```javascript async function handleUserQuestion(event) { const question = event.data.question; const options = event.data.options; const multiSelect = event.data.multiSelect; // Display question and options to user console.log('Question:', question); console.log('Options:', options); // Send response after user selection const response = await fetch('http://localhost:3000/message', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'user_question_response', requestId: event.requestId, response: { selected: multiSelect ? ['Option 1', 'Option 2'] : 'Option 1', customInput: '', // Optional custom input }, }), }); return await response.json(); } ``` ## Permission Configuration ### Auto-Approval List SSE server automatically reads permission configuration file from project root directory: **Location**: `.snow/permissions.json` ```json { "alwaysApprovedTools": [ "filesystem-read", "codebase-search", "filesystem-edit", "notebook-add", "filesystem-create" ] } ``` ### Permission Inheritance Rules 1. **Project-level Configuration**: Server reads `.snow/permissions.json` from working directory on startup 2. **Auto-approval**: Tools in the list are executed automatically without user confirmation 3. **Sensitive Commands Priority**: Sensitive commands require confirmation even if in auto-approval list 4. **Dynamic Updates**: When user selects "Always approve", the tool is automatically added to configuration file ### Configuration Example ```json { "alwaysApprovedTools": [ "filesystem-read", // Read files "filesystem-edit", // Hashline edit "filesystem-create", // Create files "codebase-search", // Code search "ace-search", // Unified ACE code search (semantic_search / find_definition / find_references / file_outline / text_search via action) "notebook-add" // Add note ] } ``` ## YOLO Mode ### Enable YOLO Mode Carry `yoloMode` parameter when sending message: ```javascript const response = await fetch('http://localhost:3000/message', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ type: 'chat', content: 'Your question', yoloMode: true, // Enable YOLO mode }), }); ``` ### YOLO Mode Features - **Auto-approval**: Non-sensitive commands execute automatically - **Sensitive Command Exception**: Sensitive commands still require confirmation - **Fast Response**: Reduce interaction waiting time - **Suitable for Automation**: Script and automation scenarios ### Security Considerations Even with YOLO mode enabled: 1. Sensitive commands still require confirmation 2. Tools not in permission list require first-time confirmation 3. Can abort execution at any time through rejection ## Complete Examples ### JavaScript Client ```javascript class SnowAIClient { constructor(baseUrl = 'http://localhost:3000') { this.baseUrl = baseUrl; this.eventSource = null; this.sessionId = null; } // Connect to SSE server connect() { return new Promise((resolve, reject) => { this.eventSource = new EventSource(`${this.baseUrl}/events`); this.eventSource.onopen = () => { console.log('Connected to Snow AI'); resolve(); }; this.eventSource.onerror = error => { console.error('Connection error:', error); reject(error); }; this.eventSource.onmessage = event => { this.handleEvent(JSON.parse(event.data)); }; }); } // Handle events handleEvent(event) { console.log('[Event]', event.type); switch (event.type) { case 'tool_confirmation_request': this.handleToolConfirmation(event); break; case 'user_question_request': this.handleUserQuestion(event); break; case 'message': if (event.data.streaming) { process.stdout.write(event.data.content); } break; case 'complete': this.sessionId = event.data.sessionId; console.log('\nConversation complete, Session ID:', this.sessionId); break; } } // Handle tool confirmation async handleToolConfirmation(event) { const options = event.data.availableOptions; // Custom confirmation logic can be implemented here // Example: Auto-approve non-sensitive commands const decision = event.data.isSensitive ? 'reject' : 'approve'; await this.sendToolConfirmation(event.requestId, decision); } // Handle user question async handleUserQuestion(event) { // Custom selection logic can be implemented here const selected = event.data.options[0]; await this.sendUserQuestionResponse(event.requestId, { selected: selected, }); } // Send message async sendMessage(content, yoloMode = false) { const payload = { type: 'chat', content: content, }; if (this.sessionId) { payload.sessionId = this.sessionId; } if (yoloMode) { payload.yoloMode = true; } const response = await fetch(`${this.baseUrl}/message`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload), }); return await response.json(); } // Send tool confirmation response async sendToolConfirmation(requestId, decision) { const response = await fetch(`${this.baseUrl}/message`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ type: 'tool_confirmation_response', requestId: requestId, response: decision, }), }); return await response.json(); } // Send user question response async sendUserQuestionResponse(requestId, answer) { const response = await fetch(`${this.baseUrl}/message`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ type: 'user_question_response', requestId: requestId, response: answer, }), }); return await response.json(); } // Disconnect disconnect() { if (this.eventSource) { this.eventSource.close(); this.eventSource = null; } } } // Usage example async function main() { const client = new SnowAIClient(); // Connect await client.connect(); // Send message (enable YOLO mode) await client.sendMessage('Help me create a TypeScript function', true); // Wait for response handling (via event listeners) } main(); ``` ### Python Client ```python import requests import json import sseclient class SnowAIClient: def __init__(self, base_url='http://localhost:3000'): self.base_url = base_url self.session = requests.Session() self.session_id = None def connect(self): """Connect to SSE server""" response = self.session.get( f'{self.base_url}/events', stream=True, headers={'Accept': 'text/event-stream'} ) client = sseclient.SSEClient(response) for event in client.events(): data = json.loads(event.data) self.handle_event(data) def handle_event(self, event): """Handle events""" print(f"[Event] {event['type']}") if event['type'] == 'tool_confirmation_request': self.handle_tool_confirmation(event) elif event['type'] == 'user_question_request': self.handle_user_question(event) elif event['type'] == 'complete': self.session_id = event['data']['sessionId'] print(f"Session ID: {self.session_id}") def handle_tool_confirmation(self, event): """Handle tool confirmation""" # Auto-approve non-sensitive commands decision = 'reject' if event['data']['isSensitive'] else 'approve' self.send_tool_confirmation_response(event['requestId'], decision) def handle_user_question(self, event): """Handle user question""" selected = event['data']['options'][0] self.send_user_question_response(event['requestId'], {'selected': selected}) def send_message(self, content, yolo_mode=False): """Send message""" payload = { 'type': 'chat', 'content': content, } if self.session_id: payload['sessionId'] = self.session_id if yolo_mode: payload['yoloMode'] = True response = self.session.post( f'{self.base_url}/message', json=payload ) return response.json() def send_tool_confirmation_response(self, request_id, decision): """Send tool confirmation response""" response = self.session.post( f'{self.base_url}/message', json={ 'type': 'tool_confirmation_response', 'requestId': request_id, 'response': decision } ) return response.json() def send_user_question_response(self, request_id, answer): """Send user question response""" response = self.session.post( f'{self.base_url}/message', json={ 'type': 'user_question_response', 'requestId': request_id, 'response': answer } ) return response.json() # Usage example if __name__ == '__main__': client = SnowAIClient() # Send message (enable YOLO mode) client.send_message('Help me create a Python function', yolo_mode=True) # Listen for events client.connect() ``` ## Use Cases ### Web Application Integration Integrate Snow AI into your web application to provide intelligent programming assistant functionality: ```javascript // React component example import {useState, useEffect, useRef} from 'react'; function AIAssistantChat() { const [connected, setConnected] = useState(false); const [messages, setMessages] = useState([]); const [sessionId, setSessionId] = useState(null); const eventSourceRef = useRef(null); // Connect to SSE server useEffect(() => { const eventSource = new EventSource('http://localhost:3000/events'); eventSourceRef.current = eventSource; eventSource.onopen = () => { setConnected(true); console.log('Connected to Snow AI'); }; eventSource.onmessage = event => { const data = JSON.parse(event.data); handleSSEEvent(data); }; eventSource.onerror = () => { setConnected(false); console.error('Connection lost'); }; return () => { eventSource.close(); }; }, []); // Handle SSE events const handleSSEEvent = data => { switch (data.type) { case 'message': if (data.data.role === 'assistant') { if (data.data.streaming) { // Stream update last message setMessages(prev => { const newMessages = [...prev]; if ( newMessages.length > 0 && newMessages[newMessages.length - 1].role === 'assistant' ) { newMessages[newMessages.length - 1].content = data.data.content; } else { newMessages.push({ role: 'assistant', content: data.data.content, }); } return newMessages; }); } } break; case 'complete': setSessionId(data.data.sessionId); console.log('Conversation complete'); break; case 'tool_confirmation_request': // Show tool confirmation dialog handleToolConfirmation(data); break; case 'error': console.error('Error:', data.data.message); break; } }; // Send message const sendMessage = async text => { const newMessage = {role: 'user', content: text}; setMessages(prev => [...prev, newMessage]); const payload = { type: 'chat', content: text, yoloMode: true, // Auto-approve safe tools }; if (sessionId) { payload.sessionId = sessionId; } await fetch('http://localhost:3000/message', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload), }); }; // Handle tool confirmation const handleToolConfirmation = async event => { const confirmed = window.confirm( `AI wants to execute tool: ${event.data.toolCall.function.name}\nAllow?`, ); await fetch('http://localhost:3000/message', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ type: 'tool_confirmation_response', requestId: event.requestId, response: confirmed ? 'approve' : 'reject', }), }); }; return (
Status: {connected ? 'Connected' : 'Disconnected'}
{messages.map((msg, i) => (
{msg.role}: {msg.content}
))}
{ if (e.key === 'Enter') { sendMessage(e.target.value); e.target.value = ''; } }} />
); } ``` ### Mobile App Backend Provide AI capabilities for mobile applications: ```javascript // Express middleware app.post('/api/ai/chat', async (req, res) => { const {message, sessionId} = req.body; // Forward to Snow AI const response = await fetch('http://localhost:3000/message', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ type: 'chat', content: message, sessionId: sessionId, yoloMode: true, }), }); res.json(await response.json()); }); ``` ### Microservice Architecture Use as AI microservice: ```yaml # Kubernetes deployment apiVersion: apps/v1 kind: Deployment metadata: name: snow-ai-service spec: replicas: 3 selector: matchLabels: app: snow-ai template: metadata: labels: app: snow-ai spec: containers: - name: snow-ai image: snow-ai:latest command: ['snow', '--sse', '--sse-port', '3000'] ports: - containerPort: 3000 ``` ## Test Client Snow CLI provides a complete HTML test client: **Location**: `sse-test-client.html` ### Features - Real-time SSE event monitoring - Beautiful chat interface - Event log viewer - YOLO mode toggle - Tool confirmation UI (with complete option display) - Session management - Connection status display ### Usage 1. Start SSE server: ```bash snow --sse ``` 2. Open `sse-test-client.html` in browser 3. Click "Connect" button 4. Start chatting and testing ## Best Practices ### 1. Error Handling ```javascript // Comprehensive error handling eventSource.onerror = error => { console.error('SSE connection error:', error); // Auto-reconnect setTimeout(() => { console.log('Attempting to reconnect...'); connect(); }, 5000); }; ``` ### 2. Timeout Handling ```javascript // Set timeout for interaction requests const TIMEOUT = 300000; // 5 minutes (default value, configurable via --sse-timeout parameter) function waitForResponse(requestId) { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Interaction timeout')); }, TIMEOUT); // Listen for response // clearTimeout(timeout) after receiving response }); } ``` ### 3. Session Management ```javascript // Persist Session ID localStorage.setItem('snow-session-id', sessionId); // Restore Session const savedSessionId = localStorage.getItem('snow-session-id'); if (savedSessionId) { await client.sendMessage( 'Continue previous conversation', false, savedSessionId, ); } ``` ### 4. Security Considerations ```javascript // Validate and sanitize user input function sanitizeInput(input) { // Remove dangerous characters return input.replace(/[<>]/g, ''); } // Add authentication in production const response = await fetch('http://localhost:3000/message', { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiToken}`, }, // ... }); ``` ## Limitations and Notes ### Unsupported Features 1. **Interactive UI**: - Cannot use Ink terminal interface - Keyboard shortcuts not supported 2. **Plan Mode**: - Interactive plan approval not supported - All operations execute immediately 3. **Local File Access Restrictions**: - Can only access files under server working directory - Cannot access client-side local files ### Performance Notes 1. **Connection Limit**: - Recommended maximum 100 concurrent connections per server - Consider load balancing 2. **Session Size**: - Long sessions increase memory usage - Regularly clean up old sessions 3. **Network Bandwidth**: - Streaming output continuously occupies connection - Consider message size limits ### Security Notes 1. **Authentication and Authorization**: - Must add authentication in production environment - Implement access control 2. **API Key Protection**: - Don't expose API keys on client side - Use server-side configuration 3. **Command Execution Risks**: - Review all tool invocations - Restrict sensitive operations ## FAQ **Q: What's the difference between SSE server and headless mode?** A: SSE server is a continuously running backend service supporting multiple client connections. Headless mode is single-execution mode that automatically exits after completion. SSE is suitable for web application integration, headless mode for script automation. **Q: How to use different API configurations in SSE mode?** A: SSE server reads configuration files from working directory. Use `--work-dir` parameter to specify different project directories, each with independent configuration. **Q: Can I run multiple SSE servers simultaneously?** A: Yes, but need to use different ports. For example: ```bash snow --sse --sse-port 3000 snow --sse --sse-port 3001 --work-dir /another-project ``` **Q: Do sessions expire?** A: Sessions don't expire and are permanently saved in `~/.snow/sessions/` directory. However, very long sessions increase token consumption. **Q: How to handle tool confirmation timeout?** A: Tool confirmation has a default 5-minute (300000ms) timeout. If timeout occurs, execution is automatically rejected and error returned. Recommend implementing auto-handling or user prompts in client. You can customize the timeout duration via `--sse-timeout` parameter: ```bash # Set to 10 minutes (600000ms) snow --sse --sse-timeout 600000 # Set to 30 seconds (30000ms) snow --sse --sse-timeout 30000 ``` **Q: Does YOLO mode execute all commands?** A: No. Sensitive commands require confirmation even in YOLO mode. YOLO mode only auto-approves safe tools in the permission list. **Q: How to debug SSE connection issues?** A: 1. Check server logs (terminal display) 2. Use browser developer tools to view network requests 3. Use `sse-test-client.html` for testing 4. Check firewall and port usage **Q: Can SSE server run in Docker?** A: Yes. Example Dockerfile: ```dockerfile FROM node:18 RUN npm install -g snow-ai EXPOSE 3000 CMD ["snow", "--sse", "--sse-port", "3000"] ``` ## Configuration File Locations Configuration files used by SSE server: - **API Configuration**: `~/.snow/profiles.json` - **Permission Configuration**: `/.snow/permissions.json` - **Sensitive Commands**: `~/.snow/sensitive-commands.json` - **Session Storage**: `~/.snow/sessions///` For configuration methods, refer to: [First Time Configuration](./02.First%20Time%20Configuration.md) ## Related Features - [Headless Mode](./12.Headless%20Mode.md) - Command line quick conversations - [Sensitive Commands Configuration](./06.Sensitive%20Commands%20Configuration.md) - Configure dangerous commands requiring confirmation - [Async Task Management](./15.Async%20Task%20Management.md) - Background task management - [Startup Parameters Guide](./19.Startup%20Parameters%20Guide.md) - All startup parameters explained