feat: add plugin architecture
This commit is contained in:
64
extensions/voice-call/README.md
Normal file
64
extensions/voice-call/README.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Voice Call Plugin (Placeholder)
|
||||
|
||||
This is a **stub** plugin used to validate the Clawdbot plugin API.
|
||||
It does not place real calls yet.
|
||||
|
||||
## Install (local dev)
|
||||
|
||||
Option 1: copy into your global extensions folder:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.clawdbot/extensions
|
||||
cp -R extensions/voice-call ~/.clawdbot/extensions/voice-call
|
||||
cd ~/.clawdbot/extensions/voice-call && pnpm install
|
||||
```
|
||||
|
||||
Option 2: add via config:
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
load: { paths: ["/absolute/path/to/extensions/voice-call"] },
|
||||
entries: {
|
||||
"voice-call": { enabled: true, config: { provider: "twilio" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Restart the Gateway after changes.
|
||||
|
||||
## CLI
|
||||
|
||||
```bash
|
||||
clawdbot voicecall status
|
||||
clawdbot voicecall start --to "+15555550123" --message "Hello"
|
||||
```
|
||||
|
||||
## Tool
|
||||
|
||||
Tool name: `voice_call`
|
||||
|
||||
Parameters:
|
||||
- `mode`: `"call" | "status"`
|
||||
- `to`: target string
|
||||
- `message`: optional intro text
|
||||
|
||||
## Gateway RPC
|
||||
|
||||
- `voicecall.status`
|
||||
|
||||
## Skill
|
||||
|
||||
The repo includes `skills/voice-call/SKILL.md` for agent guidance. Enable it by
|
||||
setting:
|
||||
|
||||
```json5
|
||||
{ plugins: { entries: { "voice-call": { enabled: true } } } }
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- This plugin is a placeholder. Implement your real call flow in the tool and
|
||||
RPC handlers.
|
||||
- Use `voicecall.*` for RPC names and `voice_call` for tool naming consistency.
|
||||
122
extensions/voice-call/index.ts
Normal file
122
extensions/voice-call/index.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
|
||||
const voiceCallConfigSchema = {
|
||||
parse(value) {
|
||||
if (value === undefined) return {};
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new Error("voice-call config must be an object");
|
||||
}
|
||||
return value;
|
||||
},
|
||||
};
|
||||
|
||||
const voiceCallPlugin = {
|
||||
id: "voice-call",
|
||||
name: "Voice Call",
|
||||
description: "Voice-call plugin stub (placeholder)",
|
||||
configSchema: voiceCallConfigSchema,
|
||||
register(api) {
|
||||
api.registerGatewayMethod("voicecall.status", ({ respond }) => {
|
||||
respond(true, {
|
||||
status: "idle",
|
||||
provider: api.pluginConfig?.provider ?? "unset",
|
||||
});
|
||||
});
|
||||
|
||||
api.registerTool(
|
||||
{
|
||||
name: "voice_call",
|
||||
label: "Voice Call",
|
||||
description: "Start or inspect a voice call via the voice-call plugin",
|
||||
parameters: Type.Object({
|
||||
mode: Type.Optional(
|
||||
Type.Union([Type.Literal("call"), Type.Literal("status")]),
|
||||
),
|
||||
to: Type.Optional(Type.String({ description: "Call target" })),
|
||||
message: Type.Optional(
|
||||
Type.String({ description: "Optional intro message" }),
|
||||
),
|
||||
}),
|
||||
async execute(_toolCallId, params) {
|
||||
if (params.mode === "status") {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({ status: "idle" }, null, 2),
|
||||
},
|
||||
],
|
||||
details: { status: "idle" },
|
||||
};
|
||||
}
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
status: "not_implemented",
|
||||
to: params.to ?? null,
|
||||
message: params.message ?? null,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
details: {
|
||||
status: "not_implemented",
|
||||
to: params.to ?? null,
|
||||
message: params.message ?? null,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{ name: "voice_call" },
|
||||
);
|
||||
|
||||
api.registerCli(({ program }) => {
|
||||
const voicecall = program
|
||||
.command("voicecall")
|
||||
.description("Voice call plugin commands");
|
||||
|
||||
voicecall
|
||||
.command("status")
|
||||
.description("Show voice-call status")
|
||||
.action(() => {
|
||||
console.log(JSON.stringify({ status: "idle" }, null, 2));
|
||||
});
|
||||
|
||||
voicecall
|
||||
.command("start")
|
||||
.description("Start a voice call (placeholder)")
|
||||
.option("--to <target>", "Target to call")
|
||||
.option("--message <text>", "Optional intro message")
|
||||
.action((opts) => {
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
status: "not_implemented",
|
||||
to: opts.to ?? null,
|
||||
message: opts.message ?? null,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
});
|
||||
}, { commands: ["voicecall"] });
|
||||
|
||||
api.registerService({
|
||||
id: "voicecall",
|
||||
start: () => {
|
||||
api.logger.info("voice-call service ready (placeholder)");
|
||||
},
|
||||
stop: () => {
|
||||
api.logger.info("voice-call service stopped (placeholder)");
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default voiceCallPlugin;
|
||||
13
extensions/voice-call/package.json
Normal file
13
extensions/voice-call/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "voice-call",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Clawdbot voice-call plugin (example)",
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "0.34.47"
|
||||
},
|
||||
"clawdbot": {
|
||||
"extensions": ["./index.ts"]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user