diff --git a/.env.example b/.env.example index 8ff3be7..65f219f 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,7 @@ -# Токен устройства (получить в simple-chat: Настройки → Устройство) +# === Production === PROXY_TOKEN= +SERVER_URL=wss://ai-chat.vigdorov.ru/ws/agent-proxy -# WebSocket URL бэкенда simple-chat -# Локальная разработка: -SERVER_URL=ws://localhost:3000/ws/agent-proxy -# Продакшен: -# SERVER_URL=wss://ai-chat.vigdorov.ru/ws/agent-proxy +# === Dev (используется при npm run start:dev / npm run dev) === +DEV_PROXY_TOKEN= +DEV_SERVER_URL=ws://localhost:3000/ws/agent-proxy diff --git a/package.json b/package.json index 4f55cd0..cdbdf42 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "type": "module", "scripts": { "start": "tsx src/main.ts", - "dev": "tsx watch src/main.ts", + "start:dev": "PROXY_MODE=dev tsx src/main.ts", + "dev": "PROXY_MODE=dev tsx watch src/main.ts", "lint": "eslint src/", "typecheck": "tsc --noEmit" }, diff --git a/src/config.ts b/src/config.ts index 1d36333..55398bc 100644 --- a/src/config.ts +++ b/src/config.ts @@ -6,6 +6,7 @@ import { homedir } from 'os'; export interface ProxyConfig { proxyToken: string; serverUrl: string; + isDev: boolean; } export function loadConfig(): ProxyConfig { @@ -16,12 +17,15 @@ export function loadConfig(): ProxyConfig { } config(); // local .env (won't override existing vars) - const proxyToken = process.env.PROXY_TOKEN; + const isDev = process.env.PROXY_MODE === 'dev'; + const prefix = isDev ? 'DEV_' : ''; + + const proxyToken = process.env[`${prefix}PROXY_TOKEN`]; if (!proxyToken) { - throw new Error('PROXY_TOKEN is required. Set it in .env or ~/.claude-proxy/.env'); + throw new Error(`${prefix}PROXY_TOKEN is required. Set it in .env`); } - const serverUrl = process.env.SERVER_URL || 'wss://ai-chat.vigdorov.ru/ws/agent-proxy'; + const serverUrl = process.env[`${prefix}SERVER_URL`] || 'wss://ai-chat.vigdorov.ru/ws/agent-proxy'; - return { proxyToken, serverUrl }; + return { proxyToken, serverUrl, isDev }; } diff --git a/src/main.ts b/src/main.ts index 4f186a7..007ac36 100644 --- a/src/main.ts +++ b/src/main.ts @@ -23,15 +23,16 @@ import { existsSync, readdirSync, statSync, readFileSync } from 'fs'; class ClaudeCliProxy { private ws: WsClient; private pty: PtyManager; + private config: ReturnType; constructor() { - const config = loadConfig(); + this.config = loadConfig(); this.pty = new PtyManager(); this.ws = new WsClient({ - url: config.serverUrl, - token: config.proxyToken, + url: this.config.serverUrl, + token: this.config.proxyToken, onMessage: (msg) => this.handleMessage(msg), onBinary: (data) => this.handleBinaryFrame(data), onConnected: () => this.onConnected(), @@ -40,7 +41,8 @@ class ClaudeCliProxy { } async start(): Promise { - console.log('[proxy] Starting claude-cli-proxy (xterm mode)...'); + const mode = this.config.isDev ? 'DEV' : 'PROD'; + console.log(`[proxy] Starting claude-cli-proxy (${mode}) → ${this.config.serverUrl}`); this.ws.connect(); @@ -114,15 +116,23 @@ class ClaudeCliProxy { const activePtys = new Set(this.pty.listPtys()); const chatIds = new Set(payload.chats.map((c) => c.id)); - // Only send replay + pty_ready for PTYs that already exist - // Do NOT auto-create PTYs — they should only be created via explicit create_pty for (const chat of payload.chats) { if (activePtys.has(chat.id)) { + // PTY exists — send replay + ready const replay = this.pty.getReplayBuffer(chat.id); if (replay) { this.ws.sendBinary(encodeBinaryFrame(DIR_PTY_OUTPUT, chat.id, replay)); } this.ws.send({ type: 'pty_ready', payload: { chatId: chat.id } }); + } else if (chat.workDir) { + // PTY not running — create it + this.handleCreatePty({ + chatId: chat.id, + dir: chat.workDir, + resumeSessionId: chat.sessionId ?? undefined, + cols: chat.cols ?? 120, + rows: chat.rows ?? 40, + }); } }