























@@ -29,6 +29,7 @@ const callGatewayFromCliMock = vi.fn();
29293030type Registered = {
3131methods: Map<string, unknown>;
32+methodScopes: Map<string, string | undefined>;
3233tools: unknown[];
3334service?: Parameters<OpenClawPluginApi["registerService"]>[0];
3435};
@@ -108,6 +109,7 @@ function createServiceContext(): Parameters<NonNullable<Registered["service"]>["
108109109110function setup(config: Record<string, unknown>): Registered {
110111const methods = new Map<string, unknown>();
112+const methodScopes = new Map<string, string | undefined>();
111113const tools: unknown[] = [];
112114let service: Registered["service"];
113115const api = createTestPluginApi({
@@ -120,7 +122,10 @@ function setup(config: Record<string, unknown>): Registered {
120122pluginConfig: config,
121123runtime: { tts: { textToSpeechTelephony: vi.fn() } } as unknown as OpenClawPluginApi["runtime"],
122124logger: noopLogger,
123-registerGatewayMethod: (method: string, handler: unknown) => methods.set(method, handler),
125+registerGatewayMethod: (method: string, handler: unknown, opts?: { scope?: string }) => {
126+methods.set(method, handler);
127+methodScopes.set(method, opts?.scope);
128+},
124129registerTool: (tool: unknown) => tools.push(tool),
125130registerCli: () => {},
126131registerService: (registeredService) => {
@@ -129,7 +134,7 @@ function setup(config: Record<string, unknown>): Registered {
129134resolvePath: (p: string) => p,
130135});
131136plugin.register(api);
132-return { methods, tools, service };
137+return { methods, methodScopes, tools, service };
133138}
134139135140function envRef(id: string) {
@@ -363,6 +368,24 @@ describe("voice-call plugin", () => {
363368expect(payload.callId).toBe("call-1");
364369});
365370371+it("registers voice call gateway methods with least-privilege scopes", () => {
372+const { methodScopes } = setup({ provider: "mock" });
373+374+for (const method of [
375+"voicecall.initiate",
376+"voicecall.start",
377+"voicecall.continue",
378+"voicecall.continue.start",
379+"voicecall.speak",
380+"voicecall.dtmf",
381+"voicecall.end",
382+]) {
383+expect(methodScopes.get(method)).toBe("operator.write");
384+}
385+expect(methodScopes.get("voicecall.continue.result")).toBe("operator.read");
386+expect(methodScopes.get("voicecall.status")).toBe("operator.read");
387+});
388+366389it("preserves mode on legacy voicecall.start", async () => {
367390const { methods } = setup({ provider: "mock" });
368391const handler = methods.get("voicecall.start") as
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。