voicecc 1.2.10 → 1.2.12
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.
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { test, describe } from "node:test";
|
|
8
8
|
import { strict as assert } from "node:assert";
|
|
9
9
|
|
|
10
|
+
import type { McpServerStatus } from "@anthropic-ai/claude-agent-sdk";
|
|
10
11
|
import { mapSdkStatusToEntry } from "./mcp-servers.js";
|
|
11
12
|
|
|
12
13
|
// ============================================================================
|
|
@@ -19,7 +20,7 @@ describe("mapSdkStatusToEntry", () => {
|
|
|
19
20
|
name: "claude.ai Notion",
|
|
20
21
|
status: "connected",
|
|
21
22
|
scope: "claudeai",
|
|
22
|
-
config: { url: "https://mcp.notion.com/mcp" },
|
|
23
|
+
config: { url: "https://mcp.notion.com/mcp" } as McpServerStatus["config"],
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
assert.deepEqual(entry, {
|
|
@@ -36,7 +37,7 @@ describe("mapSdkStatusToEntry", () => {
|
|
|
36
37
|
name: "chrome-devtools",
|
|
37
38
|
status: "connected",
|
|
38
39
|
scope: "user",
|
|
39
|
-
config: { command: "npx", args: ["chrome-devtools-mcp@latest"] },
|
|
40
|
+
config: { command: "npx", args: ["chrome-devtools-mcp@latest"] } as McpServerStatus["config"],
|
|
40
41
|
});
|
|
41
42
|
|
|
42
43
|
assert.equal(entry.scope, "user");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "voicecc",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.12",
|
|
4
4
|
"description": "Voice Agent Platform running on Claude Code -- create and deploy conversational voice agents with ElevenLabs STT/TTS and VAD",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"linux"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@anthropic-ai/claude-agent-sdk": "^0.
|
|
39
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.78",
|
|
40
40
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
41
41
|
"@hono/node-server": "^1.19.9",
|
|
42
42
|
"archiver": "^7.0.1",
|
package/server/index.ts
CHANGED
|
@@ -43,6 +43,18 @@ const VOICE_SERVER_DIR = join(import.meta.dirname ?? ".", "..", "voice-server");
|
|
|
43
43
|
/** Reference to the Python voice server child process */
|
|
44
44
|
let pythonProcess: ChildProcess | null = null;
|
|
45
45
|
|
|
46
|
+
/** Maximum number of automatic restart attempts after an unexpected crash */
|
|
47
|
+
const PYTHON_MAX_RESTART_ATTEMPTS = 5;
|
|
48
|
+
|
|
49
|
+
/** Delay before attempting a restart (ms) */
|
|
50
|
+
const PYTHON_RESTART_DELAY_MS = 3_000;
|
|
51
|
+
|
|
52
|
+
/** Number of consecutive restart attempts since last successful start */
|
|
53
|
+
let pythonRestartAttempts = 0;
|
|
54
|
+
|
|
55
|
+
/** Whether the Python server was intentionally stopped (skip auto-restart) */
|
|
56
|
+
let pythonManuallyStopped = false;
|
|
57
|
+
|
|
46
58
|
/**
|
|
47
59
|
* Start the Python voice server as a child process.
|
|
48
60
|
* Waits for the health endpoint to respond before returning.
|
|
@@ -63,6 +75,7 @@ async function startPythonVoiceServer(): Promise<void> {
|
|
|
63
75
|
pythonProcess.on("exit", (code) => {
|
|
64
76
|
console.error(`Python voice server exited with code ${code}`);
|
|
65
77
|
pythonProcess = null;
|
|
78
|
+
schedulePythonRestart();
|
|
66
79
|
});
|
|
67
80
|
|
|
68
81
|
// Wait for health endpoint (up to 15s)
|
|
@@ -72,6 +85,7 @@ async function startPythonVoiceServer(): Promise<void> {
|
|
|
72
85
|
const res = await fetch(`${VOICE_SERVER_API_URL}/health`);
|
|
73
86
|
if (res.ok) {
|
|
74
87
|
console.log("Python voice server is ready");
|
|
88
|
+
pythonRestartAttempts = 0;
|
|
75
89
|
return;
|
|
76
90
|
}
|
|
77
91
|
} catch {
|
|
@@ -83,15 +97,67 @@ async function startPythonVoiceServer(): Promise<void> {
|
|
|
83
97
|
}
|
|
84
98
|
|
|
85
99
|
/**
|
|
86
|
-
* Stop the Python voice server child process.
|
|
100
|
+
* Stop the Python voice server child process. Prevents auto-restart.
|
|
87
101
|
*/
|
|
88
102
|
function stopPythonVoiceServer(): void {
|
|
103
|
+
pythonManuallyStopped = true;
|
|
89
104
|
if (pythonProcess) {
|
|
90
105
|
pythonProcess.kill("SIGTERM");
|
|
91
106
|
pythonProcess = null;
|
|
92
107
|
}
|
|
93
108
|
}
|
|
94
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Schedule an automatic restart of the Python voice server after an unexpected exit.
|
|
112
|
+
* Skips restart if manually stopped or max attempts exceeded.
|
|
113
|
+
*/
|
|
114
|
+
function schedulePythonRestart(): void {
|
|
115
|
+
if (pythonManuallyStopped) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pythonRestartAttempts++;
|
|
120
|
+
|
|
121
|
+
if (pythonRestartAttempts > PYTHON_MAX_RESTART_ATTEMPTS) {
|
|
122
|
+
console.error(`[voice-server] Giving up after ${PYTHON_MAX_RESTART_ATTEMPTS} restart attempts`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
console.log(
|
|
127
|
+
`[voice-server] Restarting in ${PYTHON_RESTART_DELAY_MS / 1000}s ` +
|
|
128
|
+
`(attempt ${pythonRestartAttempts}/${PYTHON_MAX_RESTART_ATTEMPTS})...`
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
setTimeout(async () => {
|
|
132
|
+
if (pythonManuallyStopped || pythonProcess) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
await startPythonVoiceServer();
|
|
138
|
+
pythonRestartAttempts = 0;
|
|
139
|
+
console.log("[voice-server] Restarted successfully");
|
|
140
|
+
|
|
141
|
+
// Re-notify of tunnel URL if tunnel is running
|
|
142
|
+
const currentTunnelUrl = getTunnelUrl();
|
|
143
|
+
if (currentTunnelUrl) {
|
|
144
|
+
try {
|
|
145
|
+
await fetch(`${VOICE_SERVER_API_URL}/config/tunnel-url`, {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers: { "Content-Type": "application/json" },
|
|
148
|
+
body: JSON.stringify({ url: currentTunnelUrl }),
|
|
149
|
+
});
|
|
150
|
+
} catch {
|
|
151
|
+
console.warn("[voice-server] Failed to re-notify tunnel URL after restart");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} catch (err) {
|
|
155
|
+
console.error(`[voice-server] Restart failed: ${err}`);
|
|
156
|
+
schedulePythonRestart();
|
|
157
|
+
}
|
|
158
|
+
}, PYTHON_RESTART_DELAY_MS);
|
|
159
|
+
}
|
|
160
|
+
|
|
95
161
|
// Use VOICECC_DIR env var if set (passed by CLI when dropping root privileges),
|
|
96
162
|
// otherwise fall back to ~/.voicecc.
|
|
97
163
|
const VOICECC_DIR = process.env.VOICECC_DIR ?? join(homedir(), ".voicecc");
|
|
@@ -17,6 +17,7 @@ Responsibilities:
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
import asyncio
|
|
20
|
+
import gc
|
|
20
21
|
import json
|
|
21
22
|
import logging
|
|
22
23
|
import os
|
|
@@ -289,6 +290,9 @@ async def check_single_agent(agent: Agent) -> HeartbeatResult:
|
|
|
289
290
|
pass
|
|
290
291
|
_in_flight_checks.discard(agent.id)
|
|
291
292
|
|
|
293
|
+
# Force garbage collection to reclaim memory from the Claude subprocess
|
|
294
|
+
gc.collect()
|
|
295
|
+
|
|
292
296
|
|
|
293
297
|
async def _run_heartbeat_session(
|
|
294
298
|
agent: Agent, timeout_s: float
|