packages/channels/plugin-example/README.md
A reference channel plugin for Qwen Code. It connects to a WebSocket server and routes messages through the full channel pipeline (access control, session routing, agent bridge).
Use this package to:
npm install @qwen-code/channel-plugin-example
The package ships a qwen-extension.json manifest, so it works as an extension out of the box:
qwen extensions link ./node_modules/@qwen-code/channel-plugin-example
Add a channel entry to ~/.qwen/settings.json:
{
"channels": {
"my-plugin-test": {
"type": "plugin-example",
"serverWsUrl": "ws://localhost:9201",
"senderPolicy": "open",
"sessionScope": "user",
"cwd": "/path/to/your/project"
}
}
}
npx qwen-channel-plugin-example-server
The server prints the HTTP and WebSocket URLs. You can customize ports with environment variables:
HTTP_PORT=8080 WS_PORT=8081 npx qwen-channel-plugin-example-server
In a separate terminal:
qwen channel start my-plugin-test
Or run the same adapter under the experimental daemon-managed channel worker:
cd /path/to/your/project
qwen serve --channel my-plugin-test
qwen serve --channel requires the channel's configured cwd to resolve to the daemon workspace.
curl -sX POST http://localhost:9200/message \
-H 'Content-Type: application/json' \
-d '{"senderId":"user1","senderName":"Tester","text":"What is 2+2?"}'
You should get a JSON response with the agent's reply.
Mock Server (HTTP + WS)
↕ WebSocket
MockPluginChannel (this package)
→ Envelope → ChannelBase.handleInbound()
→ SenderGate → SessionRouter → ChannelAgentBridge.prompt()
→ qwen-code agent → model API
← response
← sendMessage() → WebSocket → Mock Server
← HTTP response
See src/MockPluginChannel.ts for a working example. The key points:
ChannelBase and implement connect(), sendMessage(), disconnect()Envelope from incoming platform messages and call this.handleInbound(envelope)ChannelAgentBridgeplugin object conforming to ChannelPluginqwen-extension.json manifestAcpBridge is still the current standalone qwen channel start implementation. Plugin adapters should depend on the ChannelAgentBridge abstraction provided by @qwen-code/channel-base.
Existing TypeScript plugins that explicitly type the adapter constructor or factory bridge parameter as AcpBridge should change that annotation to ChannelAgentBridge. JavaScript plugins are unaffected at runtime.
qwen serve --channel <name> hosts the same plugin through a daemon-managed worker backed by DaemonChannelBridge. The worker is owned by qwen serve; stop the daemon to stop serve-managed channels.
blockStreaming: "on" in config and the agent's response is automatically split into multiple messages at paragraph boundariesenvelope.attachments with images/files and handleInbound() routes them to the agent (images as vision input, files as paths in the prompt)onResponseChunk() for progressive display (e.g., editing a message in-place)The mock plugin protocol exposes streamed chunks and final outbound messages only. It does not model typing indicators, reactions, card updates, or any other status surface, so prompt and task lifecycle status are intentionally no-op for this example channel.
Full guide: Channel Plugin Developer Guide