claude-phone-local 2.1.1 → 2.1.2
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/cli/lib/commands/start.js +23 -3
- package/cli/lib/network.js +27 -0
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ import { loadConfig, configExists, getInstallationType } from '../config.js';
|
|
|
6
6
|
import { checkDocker, writeDockerConfig, startContainers } from '../docker.js';
|
|
7
7
|
import { startServer, isServerRunning } from '../process-manager.js';
|
|
8
8
|
import { isClaudeInstalled } from '../utils.js';
|
|
9
|
-
import { checkClaudeApiServer, waitForVoiceAppReady } from '../network.js';
|
|
9
|
+
import { checkClaudeApiServer, waitForVoiceAppReady, waitForClaudeApiServerReady } from '../network.js';
|
|
10
10
|
import { runPrereqChecks } from '../prereqs.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -91,7 +91,17 @@ async function startApiServer(config) {
|
|
|
91
91
|
spinner.warn('Claude API server already running');
|
|
92
92
|
} else {
|
|
93
93
|
await startServer(config.paths.claudeApiServer, config.server.claudeApiPort);
|
|
94
|
-
|
|
94
|
+
// startServer only confirms the process was spawned, not that it
|
|
95
|
+
// actually bound its port and stayed up (e.g. a stale process already
|
|
96
|
+
// on that port crashes it with EADDRINUSE within milliseconds).
|
|
97
|
+
const readiness = await waitForClaudeApiServerReady(`http://localhost:${config.server.claudeApiPort}`);
|
|
98
|
+
if (readiness.healthy) {
|
|
99
|
+
spinner.succeed(`Claude API server started on port ${config.server.claudeApiPort}`);
|
|
100
|
+
} else {
|
|
101
|
+
spinner.fail(`Claude API server did not become healthy: ${readiness.error || 'timed out'}`);
|
|
102
|
+
console.log(chalk.yellow(`\n Check the log: claude-phone logs api-server\n`));
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
95
105
|
}
|
|
96
106
|
} catch (error) {
|
|
97
107
|
spinner.fail(`Failed to start server: ${error.message}`);
|
|
@@ -332,7 +342,17 @@ async function startBoth(config, isPiMode) {
|
|
|
332
342
|
spinner.warn('Claude API server already running');
|
|
333
343
|
} else {
|
|
334
344
|
await startServer(config.paths.claudeApiServer, config.server.claudeApiPort);
|
|
335
|
-
|
|
345
|
+
// startServer only confirms the process was spawned, not that it
|
|
346
|
+
// actually bound its port and stayed up (e.g. a stale process
|
|
347
|
+
// already on that port crashes it with EADDRINUSE within ms).
|
|
348
|
+
const apiReadiness = await waitForClaudeApiServerReady(`http://localhost:${config.server.claudeApiPort}`);
|
|
349
|
+
if (apiReadiness.healthy) {
|
|
350
|
+
spinner.succeed(`Claude API server started on port ${config.server.claudeApiPort}`);
|
|
351
|
+
} else {
|
|
352
|
+
spinner.fail(`Claude API server did not become healthy: ${apiReadiness.error || 'timed out'}`);
|
|
353
|
+
console.log(chalk.yellow(`\n Check the log: claude-phone logs api-server\n`));
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
336
356
|
}
|
|
337
357
|
} catch (error) {
|
|
338
358
|
spinner.fail(`Failed to start server: ${error.message}`);
|
package/cli/lib/network.js
CHANGED
|
@@ -108,6 +108,33 @@ export async function checkClaudeApiServer(url) {
|
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Poll claude-api-server's /health until it responds, or the timeout
|
|
113
|
+
* elapses. startServer() only confirms the process was spawned (got a PID) -
|
|
114
|
+
* not that it actually bound its port and stayed up. A crash right after
|
|
115
|
+
* spawn (e.g. EADDRINUSE from a stale process already on that port) was
|
|
116
|
+
* previously reported as "✔ Claude API server started" and "✓ All services
|
|
117
|
+
* running!" with no indication anything was wrong.
|
|
118
|
+
* @param {string} url - claude-api-server base URL (e.g. http://localhost:3333)
|
|
119
|
+
* @param {object} [opts]
|
|
120
|
+
* @param {number} [opts.timeoutMs=10000] - Give up after this long
|
|
121
|
+
* @param {number} [opts.intervalMs=500] - Poll interval
|
|
122
|
+
* @returns {Promise<{healthy: boolean, timedOut: boolean, error?: string}>}
|
|
123
|
+
*/
|
|
124
|
+
export async function waitForClaudeApiServerReady(url, { timeoutMs = 10000, intervalMs = 500 } = {}) {
|
|
125
|
+
const deadline = Date.now() + timeoutMs;
|
|
126
|
+
let lastError;
|
|
127
|
+
|
|
128
|
+
while (Date.now() < deadline) {
|
|
129
|
+
const result = await checkClaudeApiServer(url);
|
|
130
|
+
if (result.reachable && result.healthy) return { healthy: true, timedOut: false };
|
|
131
|
+
lastError = result.error;
|
|
132
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return { healthy: false, timedOut: true, error: lastError };
|
|
136
|
+
}
|
|
137
|
+
|
|
111
138
|
/**
|
|
112
139
|
* Poll voice-app's /health until it reports drachtio + FreeSWITCH both
|
|
113
140
|
* connected, or the timeout elapses. Fixes `claude-phone start` reporting
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-phone-local",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Local/offline fork of NetworkChuck's claude-phone: talk to Claude Code over 3CX/SIP with faster-whisper STT + Piper TTS in one Docker container.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|