mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-18 21:40:53 +00:00
* Plugins: add inbound claim and Telegram interaction seams * Plugins: add Discord interaction surface * Chore: fix formatting after plugin rebase * fix(hooks): preserve observers after inbound claim * test(hooks): cover claimed inbound observer delivery * fix(plugins): harden typing lease refreshes * fix(discord): pass real auth to plugin interactions * fix(plugins): remove raw session binding runtime exposure * fix(plugins): tighten interactive callback handling * Plugins: gate conversation binding with approvals * Plugins: migrate legacy plugin binding records * Plugins/phone-control: update test command context * Plugins: migrate legacy binding ids * Plugins: migrate legacy codex session bindings * Discord: fix plugin interaction handling * Discord: support direct plugin conversation binds * Plugins: preserve Discord command bind targets * Tests: fix plugin binding and interactive fallout * Discord: stabilize directory lookup tests * Discord: route bound DMs to plugins * Discord: restore plugin bindings after restart * Telegram: persist detached plugin bindings * Plugins: limit binding APIs to Telegram and Discord * Plugins: harden bound conversation routing * Plugins: fix extension target imports * Plugins: fix Telegram runtime extension imports * Plugins: format rebased binding handlers * Discord: bind group DM interactions by channel --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
104 lines
3.1 KiB
TypeScript
104 lines
3.1 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import type {
|
|
OpenClawPluginApi,
|
|
OpenClawPluginCommandDefinition,
|
|
PluginCommandContext,
|
|
} from "openclaw/plugin-sdk/phone-control";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { createTestPluginApi } from "../test-utils/plugin-api.js";
|
|
import registerPhoneControl from "./index.js";
|
|
|
|
function createApi(params: {
|
|
stateDir: string;
|
|
getConfig: () => Record<string, unknown>;
|
|
writeConfig: (next: Record<string, unknown>) => Promise<void>;
|
|
registerCommand: (command: OpenClawPluginCommandDefinition) => void;
|
|
}): OpenClawPluginApi {
|
|
return createTestPluginApi({
|
|
id: "phone-control",
|
|
name: "phone-control",
|
|
source: "test",
|
|
config: {},
|
|
pluginConfig: {},
|
|
runtime: {
|
|
state: {
|
|
resolveStateDir: () => params.stateDir,
|
|
},
|
|
config: {
|
|
loadConfig: () => params.getConfig(),
|
|
writeConfigFile: (next: Record<string, unknown>) => params.writeConfig(next),
|
|
},
|
|
} as OpenClawPluginApi["runtime"],
|
|
registerCommand: params.registerCommand,
|
|
}) as OpenClawPluginApi;
|
|
}
|
|
|
|
function createCommandContext(args: string): PluginCommandContext {
|
|
return {
|
|
channel: "test",
|
|
isAuthorizedSender: true,
|
|
commandBody: `/phone ${args}`,
|
|
args,
|
|
config: {},
|
|
requestConversationBinding: async () => ({
|
|
status: "error",
|
|
message: "unsupported",
|
|
}),
|
|
detachConversationBinding: async () => ({ removed: false }),
|
|
getCurrentConversationBinding: async () => null,
|
|
};
|
|
}
|
|
|
|
describe("phone-control plugin", () => {
|
|
it("arms sms.send as part of the writes group", async () => {
|
|
const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-phone-control-test-"));
|
|
try {
|
|
let config: Record<string, unknown> = {
|
|
gateway: {
|
|
nodes: {
|
|
allowCommands: [],
|
|
denyCommands: ["calendar.add", "contacts.add", "reminders.add", "sms.send"],
|
|
},
|
|
},
|
|
};
|
|
const writeConfigFile = vi.fn(async (next: Record<string, unknown>) => {
|
|
config = next;
|
|
});
|
|
|
|
let command: OpenClawPluginCommandDefinition | undefined;
|
|
registerPhoneControl(
|
|
createApi({
|
|
stateDir,
|
|
getConfig: () => config,
|
|
writeConfig: writeConfigFile,
|
|
registerCommand: (nextCommand) => {
|
|
command = nextCommand;
|
|
},
|
|
}),
|
|
);
|
|
|
|
expect(command?.name).toBe("phone");
|
|
|
|
const res = await command?.handler(createCommandContext("arm writes 30s"));
|
|
const text = String(res?.text ?? "");
|
|
const nodes = (
|
|
config.gateway as { nodes?: { allowCommands?: string[]; denyCommands?: string[] } }
|
|
).nodes;
|
|
|
|
expect(writeConfigFile).toHaveBeenCalledTimes(1);
|
|
expect(nodes?.allowCommands).toEqual([
|
|
"calendar.add",
|
|
"contacts.add",
|
|
"reminders.add",
|
|
"sms.send",
|
|
]);
|
|
expect(nodes?.denyCommands).toEqual([]);
|
|
expect(text).toContain("sms.send");
|
|
} finally {
|
|
await fs.rm(stateDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|