* chore(release): v0.19.8 * docs(changelog): sync for v0.19.8 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|---|---|---|
| .. | ||
| src | ||
| package.json | ||
| qwen-extension.json | ||
| README.md | ||
| tsconfig.json | ||
@qwen-code/channel-plugin-example
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:
- Try out the channel plugin system — install it as an extension and run it with the built-in mock server
- Use it as a starting point — fork the source to build your own channel adapter (see the Channel Plugin Developer Guide)
Quick start
1. Install the package
npm install @qwen-code/channel-plugin-example
2. Link it as a Qwen Code extension
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
3. Configure the channel
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"
}
}
}
4. Start the mock server
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
5. Start the channel
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.
6. Send a message
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.
How it works
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
Building your own channel
See src/MockPluginChannel.ts for a working example. The key points:
- Extend
ChannelBaseand implementconnect(),sendMessage(),disconnect() - Build an
Envelopefrom incoming platform messages and callthis.handleInbound(envelope) - Type the adapter constructor bridge parameter as
ChannelAgentBridge - Export a
pluginobject conforming toChannelPlugin - Add a
qwen-extension.jsonmanifest
AcpBridge 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.
Features you get for free
- Block streaming — enable
blockStreaming: "on"in config and the agent's response is automatically split into multiple messages at paragraph boundaries - Attachments — populate
envelope.attachmentswith images/files andhandleInbound()routes them to the agent (images as vision input, files as paths in the prompt) - Streaming hooks — override
onResponseChunk()for progressive display (e.g., editing a message in-place) - Access control (allowlist, pairing, open), session routing, slash commands, crash recovery
Lifecycle status
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