refactor(voice-call): remove dead internal exports (#107130)

This commit is contained in:
Peter Steinberger
2026-07-13 21:42:11 -07:00
committed by GitHub
parent 6b0a835116
commit 1dc9d47130
24 changed files with 162 additions and 145 deletions
+13 -3
View File
@@ -2,10 +2,8 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
VoiceCallConfigSchema,
resolveVoiceCallAgentSessionKey,
resolveTwilioAuthToken,
resolveVoiceCallEffectiveConfig,
resolveVoiceCallNumberRouteKey,
resolveVoiceCallNumberRouteKeyForCall,
resolveVoiceCallSessionKey,
validateProviderConfig,
@@ -19,6 +17,19 @@ function createBaseConfig(provider: "telnyx" | "twilio" | "plivo" | "mock"): Voi
return createVoiceCallBaseConfig({ provider });
}
function resolveVoiceCallAgentSessionKey(params: {
config: VoiceCallConfig;
sessionKey: string;
coreSession?: Parameters<typeof resolveVoiceCallSessionKey>[0]["coreSession"];
}): string {
return resolveVoiceCallSessionKey({
config: params.config,
callId: "test-call",
explicitSessionKey: params.sessionKey,
coreSession: params.coreSession,
});
}
function envRef(id: string) {
return { source: "env" as const, provider: "default", id };
}
@@ -570,7 +581,6 @@ describe("resolveVoiceCallConfig session routing", () => {
},
});
expect(resolveVoiceCallNumberRouteKey(config, "+1 (555) 000-1111")).toBe("+15550001111");
const effective = resolveVoiceCallEffectiveConfig(config, "+1 (555) 000-1111");
expect(effective.numberRouteKey).toBe("+15550001111");
+3 -7
View File
@@ -264,10 +264,6 @@ const VoiceCallRealtimeFastContextConfigSchema = z
sources: ["memory", "sessions"],
fallbackToConsult: false,
});
export type VoiceCallRealtimeFastContextConfig = z.infer<
typeof VoiceCallRealtimeFastContextConfigSchema
>;
const VoiceCallRealtimeAgentContextConfigSchema = z
.object({
/** Inject a compact agent persona/context capsule into realtime voice instructions. */
@@ -518,7 +514,7 @@ type DeepPartial<T> = T extends SecretInput
: T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T;
export type VoiceCallConfigInput = DeepPartial<VoiceCallConfig>;
type VoiceCallConfigInput = DeepPartial<VoiceCallConfig>;
const TWILIO_AUTH_TOKEN_PATH = "plugins.entries.voice-call.config.twilio.authToken";
// -----------------------------------------------------------------------------
@@ -557,7 +553,7 @@ function normalizePhoneRouteKey(phone: string | undefined): string {
return phone?.replace(/\D/g, "") ?? "";
}
export function resolveVoiceCallNumberRouteKey(
function resolveVoiceCallNumberRouteKey(
config: Pick<VoiceCallConfig, "numbers">,
phone: string | undefined,
): string | undefined {
@@ -750,7 +746,7 @@ export function resolveVoiceCallSessionKey(params: {
}
/** Resolve persisted or integration-provided keys into the configured agent namespace. */
export function resolveVoiceCallAgentSessionKey(params: {
function resolveVoiceCallAgentSessionKey(params: {
config: Pick<VoiceCallConfig, "agentId">;
sessionKey: string;
coreSession?: VoiceCallCoreSessionConfig;
@@ -13,7 +13,7 @@ import {
makePersistedCall,
writeCallsToStore,
} from "./manager.test-harness.js";
import { flushPendingCallRecordWritesForTest, loadActiveCallsFromStore } from "./manager/store.js";
import { loadActiveCallsFromStore } from "./manager/store.js";
import { clearVoiceCallStateRuntime, setVoiceCallStateRuntime } from "./runtime-state.js";
function installStateRuntime(): void {
@@ -182,7 +182,6 @@ describe("CallManager verification on restore", () => {
const hangupCall = requireSingleHangupCall(provider);
expect(hangupCall.reason).toBe("timeout");
await flushPendingCallRecordWritesForTest();
expect(loadActiveCallsFromStore(storePath).activeCalls.size).toBe(0);
});
@@ -15,7 +15,6 @@ import type { AnswerCallInput, HangupCallInput, NormalizedEvent } from "../types
import type { CallManagerContext } from "./context.js";
import { processEvent } from "./events.js";
import { speakInitialMessage } from "./outbound.js";
import { flushPendingCallRecordWritesForTest } from "./store.js";
const logSpy = vi.hoisted(() => {
const logEntries: string[] = [];
@@ -78,7 +77,6 @@ afterEach(async () => {
clearTimeout(waiter.timeout);
}
ctx.transcriptWaiters.clear();
await flushPendingCallRecordWritesForTest();
fs.rmSync(ctx.storePath, { recursive: true, force: true });
}
clearVoiceCallStateRuntime();
@@ -16,7 +16,6 @@ import { clearVoiceCallStateRuntime, setVoiceCallStateRuntime } from "../runtime
import { CallRecordSchema } from "../types.js";
import {
findCallMatchesInStore,
flushPendingCallRecordWritesForTest,
getCallHistoryFromStore,
loadActiveCallsFromStore,
persistCallRecord,
@@ -73,7 +72,6 @@ describe("voice-call call record store", () => {
);
persistCallRecord(storePath, call);
await flushPendingCallRecordWritesForTest();
expect(fs.existsSync(path.join(storePath, "calls.jsonl"))).toBe(false);
const restored = loadActiveCallsFromStore(storePath);
@@ -122,7 +120,6 @@ describe("voice-call call record store", () => {
);
persistCallRecord(storePath, call);
await flushPendingCallRecordWritesForTest();
const restored = loadActiveCallsFromStore(storePath);
const restoredCall = restored.activeCalls.get("call-large");
@@ -182,8 +179,6 @@ describe("voice-call call record store", () => {
),
);
}
await flushPendingCallRecordWritesForTest();
expect(await getCallHistoryFromStore(storePath, 100)).toHaveLength(100);
const internalMatches = await findCallMatchesInStore(storePath, "call-target");
expect(internalMatches.byCallId).toMatchObject({
@@ -324,11 +324,6 @@ export function persistCallRecord(storePath: string, call: CallRecord): void {
}
}
/** Test hook for older async persistence call sites. */
export async function flushPendingCallRecordWritesForTest(): Promise<void> {
await Promise.resolve();
}
/** Restore nonterminal active calls and provider/event indexes from persisted records. */
export function loadActiveCallsFromStore(storePath: string): {
activeCalls: Map<CallId, CallRecord>;
+84 -23
View File
@@ -10,7 +10,7 @@ import type {
import { createTalkSessionController, type TalkEvent } from "openclaw/plugin-sdk/realtime-voice";
import { describe, expect, it, vi } from "vitest";
import { WebSocket } from "ws";
import { MediaStreamHandler, parseTwilioMediaMessage, sanitizeLogText } from "./media-stream.js";
import { MediaStreamHandler } from "./media-stream.js";
import {
connectWs,
startUpgradeWsServer,
@@ -185,18 +185,39 @@ describe("MediaStreamHandler TTS queue", () => {
});
describe("MediaStreamHandler security hardening", () => {
it("wraps malformed Twilio media stream JSON with an owned parser error", () => {
let error: unknown;
try {
parseTwilioMediaMessage(Buffer.from("{not json"));
} catch (caught) {
error = caught;
}
it("wraps malformed Twilio media stream JSON with an owned parser error", async () => {
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const handler = new MediaStreamHandler({
transcriptionProvider: createStubSttProvider(),
providerConfig: {},
});
const server = await startWsServer(handler);
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toBe("Twilio media stream message was malformed JSON");
expect(error).not.toBeInstanceOf(SyntaxError);
expect((error as Error).cause).toBeInstanceOf(SyntaxError);
try {
const ws = await connectWs(server.url);
ws.send("{not json");
await vi.waitFor(() => {
expect(errorSpy).toHaveBeenCalledWith(
"[MediaStream] Error processing message:",
expect.objectContaining({
message: "Twilio media stream message was malformed JSON",
}),
);
});
const error = errorSpy.mock.calls.find(
([message]) => message === "[MediaStream] Error processing message:",
)?.[1];
expect(error).toBeInstanceOf(Error);
expect(error).not.toBeInstanceOf(SyntaxError);
expect((error as Error).cause).toBeInstanceOf(SyntaxError);
ws.close();
await waitForClose(ws);
} finally {
errorSpy.mockRestore();
await server.close();
}
});
it("rejects start frames when no stream acceptance validator is configured", async () => {
@@ -436,19 +457,59 @@ describe("MediaStreamHandler security hardening", () => {
expect(ws["close"]).toHaveBeenCalledWith(1013, "Backpressure: send buffer exceeded");
});
it("sanitizes websocket close reason before logging", () => {
const reason = sanitizeLogText("forged\nline\r\tentry", 120);
expect(reason).not.toContain("\n");
expect(reason).not.toContain("\r");
expect(reason).not.toContain("\t");
expect(reason).toContain("forged line entry");
it("sanitizes websocket close reason before logging", async () => {
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
const handler = new MediaStreamHandler({
transcriptionProvider: createStubSttProvider(),
providerConfig: {},
});
const server = await startWsServer(handler);
try {
const ws = await connectWs(server.url);
ws.close(1000, "forged\nline\r\tentry");
await waitForClose(ws);
await vi.waitFor(() => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("reason: forged line entry"));
});
const line = logSpy.mock.calls
.map(([message]) => String(message))
.find((message) => message.includes("WebSocket closed"));
expect(line).not.toContain("\n");
expect(line).not.toContain("\r");
expect(line).not.toContain("\t");
} finally {
logSpy.mockRestore();
await server.close();
}
});
it("truncates websocket close reason without splitting UTF-16 surrogate pairs", () => {
const reason = sanitizeLogText(`abc\uD83D\uDE80tail`, 4);
expect(reason).toBe("abc...");
expect(reason).not.toContain("\uD83D");
expect(reason).not.toContain("\uDE80");
it("truncates websocket close reason without splitting UTF-16 surrogate pairs", async () => {
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
const handler = new MediaStreamHandler({
transcriptionProvider: createStubSttProvider(),
providerConfig: {},
});
const server = await startWsServer(handler);
try {
const ws = await connectWs(server.url);
ws.close(1000, `${"a".repeat(119)}\uD83D\uDE80`);
await waitForClose(ws);
await vi.waitFor(() => {
expect(logSpy).toHaveBeenCalledWith(
expect.stringContaining(`reason: ${"a".repeat(119)}...`),
);
});
const line = logSpy.mock.calls
.map(([message]) => String(message))
.find((message) => message.includes("WebSocket closed"));
expect(line).not.toContain("\uD83D");
expect(line).not.toContain("\uDE80");
} finally {
logSpy.mockRestore();
await server.close();
}
});
it("closes idle pre-start connections after timeout", async () => {
+2 -2
View File
@@ -102,7 +102,7 @@ const MAX_INBOUND_MESSAGE_BYTES = 64 * 1024;
const MAX_WS_BUFFERED_BYTES = 1024 * 1024;
const CLOSE_REASON_LOG_MAX_CHARS = 120;
export function sanitizeLogText(value: string, maxChars: number): string {
function sanitizeLogText(value: string, maxChars: number): string {
const sanitized = value
.replace(/\p{Cc}/gu, " ")
.replace(/\s+/g, " ")
@@ -123,7 +123,7 @@ function normalizeWsMessageData(data: RawData): Buffer {
return Buffer.from(data);
}
export function parseTwilioMediaMessage(data: RawData): TwilioMediaMessage {
function parseTwilioMediaMessage(data: RawData): TwilioMediaMessage {
const raw = normalizeWsMessageData(data);
try {
return JSON.parse(raw.toString("utf8")) as TwilioMediaMessage;
@@ -1,7 +1,6 @@
// Voice Call tests cover realtime fast context plugin behavior.
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { VoiceCallRealtimeFastContextConfig } from "./config.js";
const mocks = vi.hoisted(() => ({
resolveRealtimeVoiceFastContextConsult: vi.fn(),
@@ -14,6 +13,9 @@ vi.mock("openclaw/plugin-sdk/realtime-voice", () => ({
import { resolveRealtimeFastContextConsult } from "./realtime-fast-context.js";
const cfg = {} as OpenClawConfig;
type VoiceCallRealtimeFastContextConfig = Parameters<
typeof resolveRealtimeFastContextConsult
>[0]["config"];
function createFastContextConfig(
overrides: Partial<VoiceCallRealtimeFastContextConfig> = {},
@@ -1,6 +1,6 @@
// Voice Call tests cover telephony audio plugin behavior.
import { describe, expect, it } from "vitest";
import { convertPcmToMulaw8k, resamplePcmTo8k } from "./telephony-audio.js";
import { convertPcmToMulaw8k } from "./telephony-audio.js";
function makeSinePcm(
sampleRate: number,
@@ -17,54 +17,12 @@ function makeSinePcm(
return output;
}
function rmsPcm(buffer: Buffer): number {
const samples = Math.floor(buffer.length / 2);
if (samples === 0) {
return 0;
}
let sum = 0;
for (let i = 0; i < samples; i++) {
const sample = buffer.readInt16LE(i * 2);
sum += sample * sample;
}
return Math.sqrt(sum / samples);
}
function unalignedCopy(buffer: Buffer): Buffer {
const padded = Buffer.alloc(buffer.length + 1);
buffer.copy(padded, 1);
return padded.subarray(1);
}
describe("telephony-audio resamplePcmTo8k", () => {
it("returns identical buffer for 8k input", () => {
const pcm8k = makeSinePcm(8_000, 1_000, 0.2);
const resampled = resamplePcmTo8k(pcm8k, 8_000);
expect(resampled).toBe(pcm8k);
});
it("preserves low-frequency speech-band energy when downsampling", () => {
const input = makeSinePcm(48_000, 1_000, 0.6);
const output = resamplePcmTo8k(input, 48_000);
expect(output.length).toBe(9_600);
expect(rmsPcm(output)).toBeGreaterThan(7_500);
});
it("attenuates out-of-band high frequencies before 8k telephony conversion", () => {
const lowTone = resamplePcmTo8k(makeSinePcm(48_000, 1_000, 0.6), 48_000);
const highTone = resamplePcmTo8k(makeSinePcm(48_000, 6_000, 0.6), 48_000);
const ratio = rmsPcm(highTone) / rmsPcm(lowTone);
expect(ratio).toBeLessThan(0.1);
});
it("matches the typed-array path for unaligned input buffers", () => {
const input = makeSinePcm(48_000, 1_000, 0.2);
const output = resamplePcmTo8k(input, 48_000);
const unalignedOutput = resamplePcmTo8k(unalignedCopy(input), 48_000);
expect(unalignedOutput.equals(output)).toBe(true);
});
});
describe("telephony-audio convertPcmToMulaw8k", () => {
it("converts to 8k mu-law frame length", () => {
const input = makeSinePcm(24_000, 1_000, 0.5);
+1 -1
View File
@@ -1,5 +1,5 @@
// Voice Call plugin module implements telephony audio behavior.
export { convertPcmToMulaw8k, resamplePcmTo8k } from "openclaw/plugin-sdk/realtime-voice";
export { convertPcmToMulaw8k } from "openclaw/plugin-sdk/realtime-voice";
/**
* Chunk audio buffer into 20ms frames for streaming (8kHz mono mu-law).
+36 -1
View File
@@ -40,7 +40,42 @@ vi.mock("openclaw/plugin-sdk/process-runtime", () => ({
runCommandWithTimeout: mocks.runCommand,
}));
import { startNgrokTunnel, startTailscaleTunnel, startTunnel } from "./tunnel.js";
import { startTunnel } from "./tunnel.js";
async function requireTunnel(result: ReturnType<typeof startTunnel>) {
const tunnel = await result;
if (!tunnel) {
throw new Error("Expected tunnel to start");
}
return tunnel;
}
function startNgrokTunnel(config: {
port: number;
path: string;
authToken?: string;
domain?: string;
}) {
return requireTunnel(
startTunnel({
provider: "ngrok",
port: config.port,
path: config.path,
ngrokAuthToken: config.authToken,
ngrokDomain: config.domain,
}),
);
}
function startTailscaleTunnel(config: { mode: "serve" | "funnel"; port: number; path: string }) {
return requireTunnel(
startTunnel({
provider: config.mode === "serve" ? "tailscale-serve" : "tailscale-funnel",
port: config.port,
path: config.path,
}),
);
}
function nextProcess(): FakeChildProcess {
const proc = new FakeChildProcess();
+2 -2
View File
@@ -60,7 +60,7 @@ export interface TunnelResult {
* console.log('Public URL:', tunnel.publicUrl);
* // Later: await tunnel.stop();
*/
export async function startNgrokTunnel(config: {
async function startNgrokTunnel(config: {
port: number;
path: string;
authToken?: string;
@@ -240,7 +240,7 @@ async function runNgrokCommand(args: string[]): Promise<string> {
/**
* Start a Tailscale serve/funnel tunnel.
*/
export async function startTailscaleTunnel(config: {
async function startTailscaleTunnel(config: {
mode: "serve" | "funnel";
port: number;
path: string;
@@ -1,6 +1,6 @@
// Voice Call tests cover voice mapping plugin behavior.
import { describe, expect, it } from "vitest";
import { DEFAULT_POLLY_VOICE, escapeXml, mapVoiceToPolly } from "./voice-mapping.js";
import { escapeXml, mapVoiceToPolly } from "./voice-mapping.js";
describe("voice mapping", () => {
it("escapes xml-special characters", () => {
@@ -14,7 +14,7 @@ describe("voice mapping", () => {
expect(mapVoiceToPolly("ECHO")).toBe("Polly.Matthew");
expect(mapVoiceToPolly("Polly.Brian")).toBe("Polly.Brian");
expect(mapVoiceToPolly("Google.en-US-Standard-C")).toBe("Google.en-US-Standard-C");
expect(mapVoiceToPolly("unknown")).toBe(DEFAULT_POLLY_VOICE);
expect(mapVoiceToPolly(undefined)).toBe(DEFAULT_POLLY_VOICE);
expect(mapVoiceToPolly("unknown")).toBe("Polly.Joanna");
expect(mapVoiceToPolly(undefined)).toBe("Polly.Joanna");
});
});
+1 -1
View File
@@ -28,7 +28,7 @@ const OPENAI_TO_POLLY_MAP: Record<string, string> = {
/**
* Default Polly voice when no mapping is found.
*/
export const DEFAULT_POLLY_VOICE = "Polly.Joanna";
const DEFAULT_POLLY_VOICE = "Polly.Joanna";
/**
* Map OpenAI voice names to Twilio Polly equivalents.
@@ -1,6 +1,6 @@
// Voice Call tests cover webhook exposure plugin behavior.
import { describe, expect, it } from "vitest";
import { isLocalOnlyWebhookHost, isProviderUnreachableWebhookUrl } from "./webhook-exposure.js";
import { isProviderUnreachableWebhookUrl } from "./webhook-exposure.js";
describe("webhook exposure host classification", () => {
it.each([
@@ -24,11 +24,4 @@ describe("webhook exposure host classification", () => {
])("does not reject public webhook URL %s", (url) => {
expect(isProviderUnreachableWebhookUrl(url)).toBe(false);
});
it.each(["[::1]", "[fc00::1]", "[fd00::1]", "::ffff:7f00:1", "::ffff:a00:1", "[fe80::1]"])(
"normalizes local/private URL hostnames like %s",
(host) => {
expect(isLocalOnlyWebhookHost(host)).toBe(true);
},
);
});
@@ -28,7 +28,7 @@ export function providerRequiresPublicWebhook(providerName: string | undefined):
}
/** Return true for localhost, private, or otherwise provider-unreachable hosts. */
export function isLocalOnlyWebhookHost(hostname: string): boolean {
function isLocalOnlyWebhookHost(hostname: string): boolean {
return isBlockedHostnameOrIp(hostname);
}
@@ -8,7 +8,6 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { VoiceCallConfigSchema, type VoiceCallConfig } from "./config.js";
import { CallManager } from "./manager.js";
import { createTestStorePath, FakeProvider } from "./manager.test-harness.js";
import { flushPendingCallRecordWritesForTest } from "./manager/store.js";
import { clearVoiceCallStateRuntime, setVoiceCallStateRuntime } from "./runtime-state.js";
import type { WebhookContext, WebhookParseOptions } from "./types.js";
import { VoiceCallWebhookServer } from "./webhook.js";
@@ -184,7 +183,6 @@ describe("Voice-call webhook hangup-once lifecycle", () => {
} finally {
await firstServer.stop();
}
await flushPendingCallRecordWritesForTest();
expect(firstProvider.hangupCalls).toHaveLength(1);
const secondProvider = new RejectInboundReplayProvider("plivo");
+3 -5
View File
@@ -2,11 +2,7 @@
import { request, type IncomingMessage } from "node:http";
import type { RealtimeTranscriptionProviderPlugin } from "openclaw/plugin-sdk/realtime-transcription";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
VoiceCallConfigSchema,
type VoiceCallConfig,
type VoiceCallConfigInput,
} from "./config.js";
import { VoiceCallConfigSchema, resolveVoiceCallConfig, type VoiceCallConfig } from "./config.js";
import type { CallManager } from "./manager.js";
import type { VoiceCallProvider } from "./providers/base.js";
import { PlivoProvider } from "./providers/plivo.js";
@@ -77,6 +73,8 @@ type TwilioProviderTestDouble = VoiceCallProvider &
| "clearTtsQueue"
>;
type VoiceCallConfigInput = Parameters<typeof resolveVoiceCallConfig>[0];
const createConfig = (overrides: VoiceCallConfigInput = {}): VoiceCallConfig => {
const base = VoiceCallConfigSchema.parse({});
base.serve.port = 0;
@@ -1,11 +1,8 @@
// Voice Call tests cover realtime audio pacer plugin behavior.
import { afterEach, describe, expect, it, vi } from "vitest";
import {
RealtimeAudioPacer,
RealtimeMulawSpeechStartDetector,
calculateMulawRms,
type RealtimeAudioSerializer,
} from "./realtime-audio-pacer.js";
import { RealtimeAudioPacer, RealtimeMulawSpeechStartDetector } from "./realtime-audio-pacer.js";
type RealtimeAudioSerializer = ConstructorParameters<typeof RealtimeAudioPacer>[0]["serializer"];
function createTwilioSerializer(streamSid: string): RealtimeAudioSerializer {
return {
@@ -134,8 +131,6 @@ describe("RealtimeMulawSpeechStartDetector", () => {
const silence = Buffer.alloc(160, 0xff);
const speech = Buffer.alloc(160, 0x00);
expect(calculateMulawRms(silence)).toBeLessThan(0.02);
expect(calculateMulawRms(speech)).toBeGreaterThan(0.02);
expect(detector.accept(speech)).toBe(false);
expect(detector.accept(speech)).toBe(true);
expect(detector.accept(speech)).toBe(false);
@@ -30,7 +30,7 @@ type RealtimeAudioQueueItem =
type RealtimeAudioSend = (message: string) => boolean;
/** Provider-specific serializer for media, clear, and mark frames. */
export interface RealtimeAudioSerializer {
interface RealtimeAudioSerializer {
media(payloadBase64: string): string;
clear(): string;
mark(name: string): string;
@@ -164,7 +164,7 @@ export class RealtimeAudioPacer {
}
/** Calculate normalized RMS from mulaw bytes. */
export function calculateMulawRms(muLaw: Buffer): number {
function calculateMulawRms(muLaw: Buffer): number {
if (muLaw.length === 0) {
return 0;
}
@@ -14,7 +14,6 @@ import {
getTailscaleSelfInfo,
setupTailscaleExposure,
setupTailscaleExposureRoute,
TAILSCALE_COMMAND_STDOUT_MAX_BYTES,
} from "./tailscale.js";
function commandResult(overrides: Record<string, unknown> = {}) {
@@ -50,7 +49,7 @@ describe("voice-call tailscale helpers", () => {
["tailscale", "status", "--json", "--peers=false"],
expect.objectContaining({
killProcessTree: true,
maxOutputBytes: { stdout: TAILSCALE_COMMAND_STDOUT_MAX_BYTES, stderr: 1 },
maxOutputBytes: { stdout: 4 * 1024 * 1024, stderr: 1 },
terminateOnOutputLimit: { stdout: true },
timeoutMs: 2500,
}),
@@ -7,7 +7,7 @@ type TailscaleSelfInfo = {
nodeId: string | null;
};
export const TAILSCALE_COMMAND_STDOUT_MAX_BYTES = 4 * 1024 * 1024;
const TAILSCALE_COMMAND_STDOUT_MAX_BYTES = 4 * 1024 * 1024;
async function runTailscaleCommand(
args: string[],
-15
View File
@@ -391,22 +391,7 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [
"extensions/twitch/src/token.ts: TwitchTokenSource",
"extensions/vault/src/cli.ts: testing",
"extensions/voice-call/src/cli.ts: testing",
"extensions/voice-call/src/config.ts: resolveVoiceCallAgentSessionKey",
"extensions/voice-call/src/config.ts: resolveVoiceCallNumberRouteKey",
"extensions/voice-call/src/config.ts: VoiceCallConfigInput",
"extensions/voice-call/src/config.ts: VoiceCallRealtimeFastContextConfig",
"extensions/voice-call/src/manager/store.ts: flushPendingCallRecordWritesForTest",
"extensions/voice-call/src/media-stream.ts: parseTwilioMediaMessage",
"extensions/voice-call/src/media-stream.ts: sanitizeLogText",
"extensions/voice-call/src/runtime-state.ts: clearVoiceCallStateRuntime",
"extensions/voice-call/src/telephony-audio.ts: resamplePcmTo8k",
"extensions/voice-call/src/tunnel.ts: startNgrokTunnel",
"extensions/voice-call/src/tunnel.ts: startTailscaleTunnel",
"extensions/voice-call/src/voice-mapping.ts: DEFAULT_POLLY_VOICE",
"extensions/voice-call/src/webhook-exposure.ts: isLocalOnlyWebhookHost",
"extensions/voice-call/src/webhook/realtime-audio-pacer.ts: calculateMulawRms",
"extensions/voice-call/src/webhook/realtime-audio-pacer.ts: RealtimeAudioSerializer",
"extensions/voice-call/src/webhook/tailscale.ts: TAILSCALE_COMMAND_STDOUT_MAX_BYTES",
"extensions/whatsapp/src/agent-tools-call.ts: createWhatsAppCallTool",
"extensions/whatsapp/src/agent-tools-call.ts: testing",
"extensions/whatsapp/src/auto-reply/mentions.ts: isBotMentionedFromTargets",