fix(voice-call): scope call control gateway methods

This commit is contained in:
scoootscooob
2026-05-03 21:01:38 -07:00
committed by Peter Steinberger
parent 309ff6bada
commit 0c1df35315
2 changed files with 37 additions and 2 deletions

View File

@@ -29,6 +29,7 @@ const callGatewayFromCliMock = vi.fn();
type Registered = {
methods: Map<string, unknown>;
methodScopes: Map<string, string | undefined>;
tools: unknown[];
service?: Parameters<OpenClawPluginApi["registerService"]>[0];
};
@@ -108,6 +109,7 @@ function createServiceContext(): Parameters<NonNullable<Registered["service"]>["
function setup(config: Record<string, unknown>): Registered {
const methods = new Map<string, unknown>();
const methodScopes = new Map<string, string | undefined>();
const tools: unknown[] = [];
let service: Registered["service"];
const api = createTestPluginApi({
@@ -120,7 +122,10 @@ function setup(config: Record<string, unknown>): Registered {
pluginConfig: config,
runtime: { tts: { textToSpeechTelephony: vi.fn() } } as unknown as OpenClawPluginApi["runtime"],
logger: noopLogger,
registerGatewayMethod: (method: string, handler: unknown) => methods.set(method, handler),
registerGatewayMethod: (method: string, handler: unknown, opts?: { scope?: string }) => {
methods.set(method, handler);
methodScopes.set(method, opts?.scope);
},
registerTool: (tool: unknown) => tools.push(tool),
registerCli: () => {},
registerService: (registeredService) => {
@@ -129,7 +134,7 @@ function setup(config: Record<string, unknown>): Registered {
resolvePath: (p: string) => p,
});
plugin.register(api);
return { methods, tools, service };
return { methods, methodScopes, tools, service };
}
function envRef(id: string) {
@@ -363,6 +368,24 @@ describe("voice-call plugin", () => {
expect(payload.callId).toBe("call-1");
});
it("registers voice call gateway methods with least-privilege scopes", () => {
const { methodScopes } = setup({ provider: "mock" });
for (const method of [
"voicecall.initiate",
"voicecall.start",
"voicecall.continue",
"voicecall.continue.start",
"voicecall.speak",
"voicecall.dtmf",
"voicecall.end",
]) {
expect(methodScopes.get(method)).toBe("operator.write");
}
expect(methodScopes.get("voicecall.continue.result")).toBe("operator.read");
expect(methodScopes.get("voicecall.status")).toBe("operator.read");
});
it("preserves mode on legacy voicecall.start", async () => {
const { methods } = setup({ provider: "mock" });
const handler = methods.get("voicecall.start") as

View File

@@ -22,6 +22,9 @@ import {
import type { CoreConfig } from "./src/core-bridge.js";
import { createVoiceCallContinueOperationStore } from "./src/gateway-continue-operation.js";
const VOICE_CALL_WRITE_METHOD_SCOPE = { scope: "operator.write" as const };
const VOICE_CALL_READ_METHOD_SCOPE = { scope: "operator.read" as const };
const voiceCallConfigSchema = {
parse(value: unknown): VoiceCallConfig {
const normalized = normalizeVoiceCallLegacyConfigInput(value);
@@ -415,6 +418,7 @@ export default definePluginEntry({
sendError(respond, err);
}
},
VOICE_CALL_WRITE_METHOD_SCOPE,
);
api.registerGatewayMethod(
@@ -432,6 +436,7 @@ export default definePluginEntry({
sendError(respond, err);
}
},
VOICE_CALL_WRITE_METHOD_SCOPE,
);
api.registerGatewayMethod(
@@ -452,6 +457,7 @@ export default definePluginEntry({
sendError(respond, err);
}
},
VOICE_CALL_WRITE_METHOD_SCOPE,
);
api.registerGatewayMethod(
@@ -473,6 +479,7 @@ export default definePluginEntry({
sendError(respond, err);
}
},
VOICE_CALL_READ_METHOD_SCOPE,
);
api.registerGatewayMethod(
@@ -508,6 +515,7 @@ export default definePluginEntry({
sendError(respond, err);
}
},
VOICE_CALL_WRITE_METHOD_SCOPE,
);
api.registerGatewayMethod(
@@ -531,6 +539,7 @@ export default definePluginEntry({
sendError(respond, err);
}
},
VOICE_CALL_WRITE_METHOD_SCOPE,
);
api.registerGatewayMethod(
@@ -553,6 +562,7 @@ export default definePluginEntry({
sendError(respond, err);
}
},
VOICE_CALL_WRITE_METHOD_SCOPE,
);
api.registerGatewayMethod(
@@ -576,6 +586,7 @@ export default definePluginEntry({
sendError(respond, err);
}
},
VOICE_CALL_READ_METHOD_SCOPE,
);
api.registerGatewayMethod(
@@ -604,6 +615,7 @@ export default definePluginEntry({
sendError(respond, err);
}
},
VOICE_CALL_WRITE_METHOD_SCOPE,
);
api.registerTool({