fix: Opus review pass — harden before building

- Widen voice ID validation to 20-64 alphanumeric (future-proof)
- Remove hardcoded default voiceId (SJ personal clone)
- Require voiceId in isConfigured + synthesize guard with clear error
- Add model header comment explaining Fish Audio's non-standard API
- Truncate error bodies to 500 chars to prevent log pollution
- Update tests and README to match
This commit is contained in:
Clawdbot
2026-03-29 18:17:06 +11:00
parent 4842dc64a5
commit ed505dcce1
4 changed files with 43 additions and 23 deletions

View File

@@ -3,14 +3,15 @@ import { isValidFishAudioVoiceId } from "./speech-provider.js";
describe("fish-audio speech provider", () => {
describe("isValidFishAudioVoiceId", () => {
it("accepts valid Fish Audio ref IDs (24-40 char hex)", () => {
it("accepts valid Fish Audio ref IDs (20-64 alphanumeric chars)", () => {
const valid = [
"8a2d42279389471993460b85340235c5", // 32 char - standard
"0dad9e24630447cf97803f4beee10481", // 32 char
"5796fe24630447cf97803f4beee10481", // 32 char
"d8b0991f96b44e489422ca2ddf0bd31d", // 32 char - author id
"aabbccddee112233445566778899", // 28 char
"aabbccddee11223344556677", // 24 char (minimum)
"8a2d42279389471993460b85340235c5", // 32 char hex - standard
"0dad9e24630447cf97803f4beee10481", // 32 char hex
"d8b0991f96b44e489422ca2ddf0bd31d", // 32 char hex - author id
"aabbccddee112233445566778899aabb", // 32 char hex
"abcdefABCDEF12345678901234567890", // mixed case alphanumeric
"a1b2c3d4e5f6g7h8i9j0", // 20 char (minimum)
"a".repeat(64), // 64 char (maximum)
];
for (const v of valid) {
expect(isValidFishAudioVoiceId(v), `expected valid: ${v}`).toBe(true);
@@ -20,14 +21,14 @@ describe("fish-audio speech provider", () => {
it("rejects invalid voice IDs", () => {
const invalid = [
"", // empty
"abc123", // too short
"12345678901234567890123", // 23 chars - below minimum
"a".repeat(41), // too long
"abc123", // too short (6)
"1234567890123456789", // 19 chars - below minimum
"a".repeat(65), // too long (65)
"8a2d4227-9389-4719-9346-0b85340235c5", // UUID with dashes
"../../../etc/passwd", // path traversal
"voice?param=value", // query string
"pMsXgVXv3BLzUgSXRplE", // ElevenLabs-style (mixed case, 20 chars)
"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", // non-hex chars
"hello world 1234567890", // spaces
"abcdef!@#$%^&*()12345678", // special chars
];
for (const v of invalid) {
expect(isValidFishAudioVoiceId(v), `expected invalid: ${v}`).toBe(