ddchat 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -1,51 +1,51 @@
1
- # CLAUDE.md
2
-
3
- 本文件为 Claude Code (claude.ai/code) 在本仓库中工作时提供指导。
4
-
5
- ## 项目概述
6
-
7
- DDChat 频道插件 (`@ddchat/openclaw-ddchat`) — OpenClaw AI 智能体平台的 DDChat(内部即时通讯)频道集成插件。通过 WebSocket 或 Webhook 连接,将 DDChat 的私聊和群聊消息桥接到 OpenClaw 智能体。
8
-
9
- ## 技术栈
10
-
11
- - TypeScript,ES 模块 (`"type": "module"`)
12
- - OpenClaw monorepo 的一部分(使用 `workspace:*` 依赖)
13
- - OpenClaw 插件 SDK:`openclaw/plugin-sdk/core`、`openclaw/plugin-sdk/setup`、`openclaw/plugin-sdk/channel-pairing`、`openclaw/plugin-sdk/media-runtime`
14
-
15
- ## 开发与测试
16
-
17
- ```bash
18
- # 启动模拟 DDChat 服务器(WebSocket 端口 :9001,HTTP 界面端口 :9020)
19
- node ddchat/test/server.mjs
20
-
21
- # 将插件安装到 OpenClaw
22
- openclaw channels add --channel ddchat --token "appId:appSecret"
23
- ```
24
-
25
- 无独立的构建或测试脚本 — 构建由 OpenClaw 工作区统一处理。
26
-
27
- ## 架构
28
-
29
- 插件遵循 OpenClaw 的 `ChatChannelPlugin` 模式,包含以下核心模块:
30
-
31
- - **`index.ts`** — 插件入口,导出 extensions 和 setup entry
32
- - **`src/channel.ts`** — 插件定义:能力声明、配置 schema、安装流程、网关、安全策略
33
- - **`src/gateway.ts`** — WebSocket 生命周期:连接、认证(token 作为查询参数)、心跳 ping/pong、指数退避重连(1s→2s→5s→10s→30s)
34
- - **`src/inbound.ts`** — 接收消息(WebSocket 或 webhook),去重,解析媒体/文件,路由到 AI 智能体,处理流式响应
35
- - **`src/outbound.ts`** — 将智能体回复发送回 DDChat;按 4000 字符使用 markdown 感知方式分块;处理媒体附件(base64 或 URL)
36
- - **`src/types.ts`** — `DdchatResolvedAccount` 配置类型、账户解析/合并逻辑、策略类型
37
- - **`src/runtime.ts`** — 全局插件状态:去重存储、WebSocket 发送函数
38
- - **`src/dedupe.ts`** — 基于 TTL 的消息去重(默认 48 小时),带 GC 回收
39
- - **`src/pairing.ts`** — 用户配对审批流程,ID 标准化(去除前缀)
40
- - **`src/session.ts`** — 会话 key 构造,用于私聊/群聊路由
41
-
42
- **数据流:** DDChat → WebSocket/Webhook → inbound.ts(去重、解析、媒体处理)→ OpenClaw 智能体 → outbound.ts(分块、发送)→ DDChat
43
-
44
- ## 关键约定
45
-
46
- - 出站消息包含 `from: "plugin"` 字段
47
- - 标识符:From 使用 `ddchat:${userId}`,To 使用 `user:${userId}` / `group:${groupId}`
48
- - 流式 ID:`ddchat-stream-${messageId}`
49
- - 连接模式:`websocket`(默认)| `webhook`
50
- - 访问策略:私聊使用 `open | pairing | allowlist`(默认:pairing);群聊使用 `open | allowlist | disabled`(默认:allowlist)
51
- - 流式模式:`chunk` | `token`
1
+ # CLAUDE.md
2
+
3
+ 本文件为 Claude Code (claude.ai/code) 在本仓库中工作时提供指导。
4
+
5
+ ## 项目概述
6
+
7
+ DDChat 频道插件 (`@ddchat/openclaw-ddchat`) — OpenClaw AI 智能体平台的 DDChat(内部即时通讯)频道集成插件。通过 WebSocket 或 Webhook 连接,将 DDChat 的私聊和群聊消息桥接到 OpenClaw 智能体。
8
+
9
+ ## 技术栈
10
+
11
+ - TypeScript,ES 模块 (`"type": "module"`)
12
+ - OpenClaw monorepo 的一部分(使用 `workspace:*` 依赖)
13
+ - OpenClaw 插件 SDK:`openclaw/plugin-sdk/core`、`openclaw/plugin-sdk/setup`、`openclaw/plugin-sdk/channel-pairing`、`openclaw/plugin-sdk/media-runtime`
14
+
15
+ ## 开发与测试
16
+
17
+ ```bash
18
+ # 启动模拟 DDChat 服务器(WebSocket 端口 :9001,HTTP 界面端口 :9020)
19
+ node ddchat/test/server.mjs
20
+
21
+ # 将插件安装到 OpenClaw
22
+ openclaw channels add --channel ddchat --token "appId:appSecret"
23
+ ```
24
+
25
+ 无独立的构建或测试脚本 — 构建由 OpenClaw 工作区统一处理。
26
+
27
+ ## 架构
28
+
29
+ 插件遵循 OpenClaw 的 `ChatChannelPlugin` 模式,包含以下核心模块:
30
+
31
+ - **`index.ts`** — 插件入口,导出 extensions 和 setup entry
32
+ - **`src/channel.ts`** — 插件定义:能力声明、配置 schema、安装流程、网关、安全策略
33
+ - **`src/gateway.ts`** — WebSocket 生命周期:连接、认证(token 作为查询参数)、心跳 ping/pong、指数退避重连(1s→2s→5s→10s→30s)
34
+ - **`src/inbound.ts`** — 接收消息(WebSocket 或 webhook),去重,解析媒体/文件,路由到 AI 智能体,处理流式响应
35
+ - **`src/outbound.ts`** — 将智能体回复发送回 DDChat;按 4000 字符使用 markdown 感知方式分块;处理媒体附件(base64 或 URL)
36
+ - **`src/types.ts`** — `DdchatResolvedAccount` 配置类型、账户解析/合并逻辑、策略类型
37
+ - **`src/runtime.ts`** — 全局插件状态:去重存储、WebSocket 发送函数
38
+ - **`src/dedupe.ts`** — 基于 TTL 的消息去重(默认 48 小时),带 GC 回收
39
+ - **`src/pairing.ts`** — 用户配对审批流程,ID 标准化(去除前缀)
40
+ - **`src/session.ts`** — 会话 key 构造,用于私聊/群聊路由
41
+
42
+ **数据流:** DDChat → WebSocket/Webhook → inbound.ts(去重、解析、媒体处理)→ OpenClaw 智能体 → outbound.ts(分块、发送)→ DDChat
43
+
44
+ ## 关键约定
45
+
46
+ - 出站消息包含 `from: "plugin"` 字段
47
+ - 标识符:From 使用 `ddchat:${userId}`,To 使用 `user:${userId}` / `group:${groupId}`
48
+ - 流式 ID:`ddchat-stream-${messageId}`
49
+ - 连接模式:`websocket`(默认)| `webhook`
50
+ - 访问策略:私聊使用 `open | pairing | allowlist`(默认:pairing);群聊使用 `open | allowlist | disabled`(默认:allowlist)
51
+ - 流式模式:`chunk` | `token`
package/OPTIMIZATION.md CHANGED
@@ -1,105 +1,129 @@
1
- # DDChat 插件代码优化建议
2
-
3
- ## 1. 去重 GC 性能问题
4
-
5
- **文件:** `src/dedupe.ts:23-29`
6
-
7
- `gc()` 在每次 `isDuplicate()` 调用时都全量遍历整个 Map。高频消息场景下性能会线性退化。
8
-
9
- **建议:** 改为按频率限流 GC(如每 N 次调用或每 M 秒执行一次),或改用双桶/时间轮策略。
10
-
11
- ---
12
-
13
- ## 2. 全局单例状态不支持多账户并发(功能缺陷)
14
-
15
- **文件:** `src/runtime.ts`
16
-
17
- `wsSend` `wsConnected` 是全局唯一的,但插件支持多账户。如果两个账户同时启动 WebSocket,后启动的会覆盖前一个的 `wsSend`,导致第一个账户的出站消息实际发到第二个连接上。
18
-
19
- **建议:** `wsSend` 改为 `Map<accountId, sendFn>` 结构,出站时按 `accountId` 查找对应的发送函数。
20
-
21
- ---
22
-
23
- ## 3. messageId 使用 Date.now() 存在碰撞风险
24
-
25
- **文件:** `src/outbound.ts:33,55`
26
-
27
- `ddchat-text-${Date.now()}` 在高并发下可能产生重复 ID。
28
-
29
- **建议:** 加入自增计数器或使用 `crypto.randomUUID()`。
30
-
31
- ---
32
-
33
- ## 4. Webhook 模式下的轮询等待(资源浪费)
34
-
35
- **文件:** `src/gateway.ts:29-36`
36
-
37
- Webhook 模式用 `setInterval` 每秒轮询 `abortSignal.aborted`,浪费资源。
38
-
39
- **建议:** 改为直接监听 `abortSignal` 的 `abort` 事件:
40
-
41
- ```ts
42
- await new Promise<void>(resolve => {
43
- ctx.abortSignal.addEventListener("abort", () => resolve(), { once: true });
44
- });
45
- ```
46
-
47
- ---
48
-
49
- ## 5. resolveMediaMaxBytes 重复定义
50
-
51
- **文件:** `src/inbound.ts:68-76` 和 `src/outbound.ts:6-12`
52
-
53
- `inbound.ts` `resolveMediaFetchMaxBytes` 和 `outbound.ts` 的 `resolveMediaMaxBytes` 逻辑完全相同。
54
-
55
- **建议:** 提取到 `types.ts` 或新建一个共享工具函数。
56
-
57
- ---
58
-
59
- ## 6. WebSocket 构造器类型处理冗余
60
-
61
- **文件:** `src/gateway.ts:122-135`
62
-
63
- 通过 `globalThis as unknown` 手动声明 WebSocket 类型,既冗长又不安全。
64
-
65
- **建议:** 使用 TypeScript 内置的 `WebSocket` 类型(TS 4.4+ 全局可用),或在文件顶部用 `/// <reference lib="dom" />` 引入,去掉手动类型断言。
66
-
67
- ---
68
-
69
- ## 7. gateway.ts ctx 类型未复用
70
-
71
- **文件:** `src/gateway.ts:102-117`
72
-
73
- `runWsSession` `ctx` 参数手写了大量类型结构,���这些应该可以从 SDK 中导入。
74
-
75
- **建议:** `openclaw/plugin-sdk/core` 导入网关上下文类型,避免与 SDK 类型不同步。
76
-
77
- ---
78
-
79
- ## 8. 流式响应在 WebSocket 断开时静默丢失
80
-
81
- **文件:** `src/inbound.ts:320-334`
82
-
83
- `emitStream` 中调用 `getDdchatState().wsSend?.(...)` 使用了可选链,连接断开时流式数据无声丢失,客户端无法感知。
84
-
85
- **建议:** 至少记录一条日志,或在 `wsSend` 返回 `false` 时尝试缓冲/通知调用方。
86
-
87
- ---
88
-
89
- ## 9. buildDdchatSessionKey 未被使用
90
-
91
- **文件:** `src/session.ts`
92
-
93
- 整个 `session.ts` 导出了 `buildDdchatSessionKey` 函数,但在项目中没有任何地方引用它(路由使用的是 SDK 的 `resolveAgentRoute`)。
94
-
95
- **建议:** 确认是否为遗留代码,如果不需要则移除。
96
-
97
- ---
98
-
99
- ## 10. Webhook 路径配置未生效
100
-
101
- **文件:** `src/inbound.ts:212` 和 `src/types.ts:108-111`
102
-
103
- `types.ts` 中解析了 `webhookPath` 和 `webhookPort` 配置,但 `registerDdchatWebhook` 硬编码了 `/ddchat/webhook` 路径,并未使用账户配置中的值。
104
-
105
- **建议:** 使用 `account.webhookPath` 注册路由,或移除 `types.ts` 中未使用的配置字段。
1
+ # DDChat 插件优化记录
2
+
3
+ 本文按实施优先级记录当前代码中的优化项。状态说明:`已完成` 表示本轮已落地;`待后续` 表示仍建议单独评估或实施。
4
+
5
+ ## P0:正确性与安全边界
6
+
7
+ ### 1. 多账户 WebSocket 运行态为全局单例(已完成)
8
+
9
+ **文件:** `src/runtime.ts`、`src/gateway.ts`、`src/outbound.ts`、`src/inbound.ts`
10
+
11
+ 当前 `wsSend` 和 `wsConnected` 是全局唯一状态,但插件支持多账户。多个账户同时连接 WebSocket 时,后启动账户会覆盖前一个账户的发送函数,导致出站消息和流式消息可能发到错误连接。
12
+
13
+ **建议:** WebSocket 运行态改为按 `accountId` 存储,例如 `Map<accountId, { connected, send }>`;出站文本、媒体、流式 chunk、入站 reply delivery 都按账户取对应发送函数。停止某个账户时只清理该账户状态。
14
+
15
+ ### 2. WebSocket 入站失败缺少 nack(已完成)
16
+
17
+ **文件:** `src/gateway.ts`
18
+
19
+ WebSocket `message` handler 处理失败时目前只记录日志,不向 DDChat 端返回失败确认。调用方无法区分“仍在处理”“已失败”“消息被丢弃”。
20
+
21
+ **建议:** 在处理异常时,如果连接仍打开,发送 `ack` 且 `ok:false`,包含 `from:"plugin"`、`accountId`、`messageId`(如可解析)和简短错误信息。
22
+
23
+ ### 3. 入站未拒绝禁用账户(已完成)
24
+
25
+ **文件:** `src/inbound.ts`
26
+
27
+ 入站处理解析账户后没有检查 `account.enabled`。禁用账户仍可能通过 webhook 或已有 WebSocket 路径进入后续路由。
28
+
29
+ **建议:** dedupe 前检查 `account.enabled`,禁用账户直接拒绝,避免消息被记录为已处理。
30
+
31
+ ### 4. Webhook 请求体无最大读取限制(已完成)
32
+
33
+ **文件:** `src/inbound.ts`
34
+
35
+ `readRawBodyFromStream` 会将整个请求体读入内存,没有上限。异常大请求可能造成内存压力。
36
+
37
+ **建议:** 增加 webhook body 最大字节数,读取 stream 时累计字节数并在超限时中止处理。
38
+
39
+ ### 5. Base64 媒体解码前未做大小预检(已完成)
40
+
41
+ **文件:** `src/inbound.ts`
42
+
43
+ 入站 base64 媒体会先 `Buffer.from(base64, "base64")` 完整解码,再交给 `saveMediaBuffer` 校验大小。超大 base64 会先消耗内存。
44
+
45
+ **建议:** 解码前根据 base64 长度预估 decoded size,超过 `mediaMaxMb` 时直接拒绝该媒体,保留保存阶段的二次校验。
46
+
47
+ ## P1:可靠性和可观测性
48
+
49
+ ### 6. 去重 GC 每次全量扫描(已完成)
50
+
51
+ **文件:** `src/dedupe.ts`
52
+
53
+ `isDuplicate()` 每次调用都会遍历整个 Map 清理过期项。高频消息场景下会线性退化。
54
+
55
+ **建议:** 按时间间隔或调用次数触发 GC,例如每 60 秒或每 1000 次检查清理一次,同时保持过期 key 被覆盖的现有语义。
56
+
57
+ ### 7. messageId 使用 Date.now() 存在碰撞风险(已完成)
58
+
59
+ **文件:** `src/outbound.ts`、`src/inbound.ts`
60
+
61
+ `ddchat-text-${Date.now()}`、`ddchat-media-${Date.now()}`、`ddchat-out-${Date.now()}` 在同毫秒并发下可能重复。
62
+
63
+ **建议:** 使用 `crypto.randomUUID()` 或时间戳 + 进程内自增计数器生成后缀,保留现有前缀。
64
+
65
+ ### 8. 出站日志过多且可能泄露内容(已完成)
66
+
67
+ **文件:** `src/outbound.ts`
68
+
69
+ 当前出站路径使用 `console.log/error` 打印连接状态、完整 payload、媒体路径和加载细节。完整 payload 可能包含用户消息、媒体 URL、本地路径或大体积字段。
70
+
71
+ **建议:** 移除调试日志,或只保留不含正文/base64/token 的简短错误信息;避免打印完整 payload、text、mediaBase64。
72
+
73
+ ### 9. 流式/非流式发送失败反馈不足(已完成)
74
+
75
+ **文件:** `src/inbound.ts`
76
+
77
+ 流式发送使用可选链,WebSocket 断开时会静默丢失;非流式 reply delivery 计算了 `sent` 但没有使用结果。
78
+
79
+ **建议:** 按账户发送并检查返回值;失败时至少记录简短日志,避免静默丢失。
80
+
81
+ ## P2:配置一致性和维护性
82
+
83
+ ### 10. Webhook 模式使用轮询等待停止(已完成)
84
+
85
+ **文件:** `src/gateway.ts`
86
+
87
+ Webhook 模式通过 `setInterval` 每秒检查 `abortSignal.aborted`。
88
+
89
+ **建议:** 改为监听 `abortSignal` 的 `abort` 事件,避免无意义轮询并提升停止响应速度。
90
+
91
+ ### 11. webhookPath 配置未生效(已完成)
92
+
93
+ **文件:** `src/inbound.ts`、`src/types.ts`
94
+
95
+ `types.ts` 解析了 `webhookPath`,但注册 HTTP 路由时硬编码 `/ddchat/webhook`。用户配置自定义路径不会生效。
96
+
97
+ **建议:** 注册默认路径,并从当前配置收集账户级 `webhookPath` 去重注册;非法路径回退默认路径。若某路径唯一映射到一个账户,可作为该 route 的 fallback account。
98
+
99
+ ### 12. webhookPort 配置未被使用(待后续)
100
+
101
+ **文件:** `src/types.ts`
102
+
103
+ `webhookPort` 被解析但当前插件入口只注册 SDK HTTP route,并未自行监听端口。
104
+
105
+ **建议:** 如果 SDK route 不支持插件自管端口,应移除或标记为暂不支持,避免误导配置使用者。
106
+
107
+ ### 13. mediaMaxMb 解析逻辑重复(已完成)
108
+
109
+ **文件:** `src/inbound.ts`、`src/outbound.ts`
110
+
111
+ 入站和出站分别实现了相同的 `agents.defaults.mediaMaxMb` 解析逻辑。
112
+
113
+ **建议:** 提取共享 helper,例如 `resolveDdchatMediaMaxBytes(cfg)`,统一供入站、出站、大小预检使用。
114
+
115
+ ### 14. gateway 上下文和 WebSocket 类型可进一步收敛(待后续)
116
+
117
+ **文件:** `src/gateway.ts`
118
+
119
+ `runWsSession` 手写了较多 ctx 类型并使用了类型断言。长期看容易和 SDK 类型不同步。
120
+
121
+ **建议:** 若 SDK 暴露对应 gateway context 类型,则复用;否则保留本地最小类型,不为类型美化引入行为风险。
122
+
123
+ ### 15. session.ts 当前未被引用(待后续)
124
+
125
+ **文件:** `src/session.ts`
126
+
127
+ 当前会话路由使用 SDK 的 `resolveAgentRoute`,`buildDdchatSessionKey` 未被项目引用。
128
+
129
+ **建议:** 确认是否属于外部可用 API;如果不是,后续可单独移除,避免误导维护者。
package/README.md CHANGED
@@ -1,14 +1,22 @@
1
- # 本地安装ddchat插件
2
- ```shell
3
- openclaw plugins install "安装包路径"
4
- ```
5
-
6
- # 默认default账号
7
- ```shell
8
- openclaw channels add --channel ddchat --token "appId:appSecret"
9
- ```
10
-
11
- # 多个账号时指定账户名 避免覆盖
12
- ```shell
13
- openclaw channels add --channel ddchat --account xxx --token "appId:appSecret"
14
- ```
1
+ # 本地安装ddchat插件
2
+ ```shell
3
+ openclaw plugins install "安装包路径"
4
+ ```
5
+
6
+ # 仓库管理ddchat插件
7
+ ```shell
8
+ openclaw plugins install ddchat # 安装插件
9
+ openclaw plugins update ddchat # 更新插件
10
+ openclaw plugins uninstall ddchat # 卸载插件
11
+ ```
12
+
13
+
14
+ # 默认default账号
15
+ ```shell
16
+ openclaw channels add --channel ddchat --token "appId:appSecret"
17
+ ```
18
+
19
+ # 多个账号时指定账户名 避免覆盖
20
+ ```shell
21
+ openclaw channels add --channel ddchat --account xxx --token "appId:appSecret"
22
+ ```
package/index.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
2
- import { ddchatPlugin } from "./src/channel.js";
3
- import { registerDdchatWebhook } from "./src/inbound.js";
4
-
5
- export { ddchatPlugin } from "./src/channel.js";
6
-
7
- export default defineChannelPluginEntry({
8
- id: "ddchat",
9
- name: "DDChat",
10
- description: "DDChat channel plugin",
11
- plugin: ddchatPlugin,
12
- registerFull: registerDdchatWebhook,
13
- });
1
+ import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
2
+ import { ddchatPlugin } from "./src/channel.js";
3
+ import { registerDdchatWebhook } from "./src/inbound.js";
4
+
5
+ export { ddchatPlugin } from "./src/channel.js";
6
+
7
+ export default defineChannelPluginEntry({
8
+ id: "ddchat",
9
+ name: "DDChat",
10
+ description: "DDChat channel plugin",
11
+ plugin: ddchatPlugin,
12
+ registerFull: registerDdchatWebhook,
13
+ });
@@ -1,15 +1,15 @@
1
- {
2
- "id": "ddchat",
3
- "kind": "channel",
4
- "channels": ["ddchat"],
5
- "configSchema": {
6
- "type": "object",
7
- "additionalProperties": false,
8
- "properties": {
9
- "channels": {
10
- "type": "object",
11
- "additionalProperties": true
12
- }
13
- }
14
- }
15
- }
1
+ {
2
+ "id": "ddchat",
3
+ "kind": "channel",
4
+ "channels": ["ddchat"],
5
+ "configSchema": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "channels": {
10
+ "type": "object",
11
+ "additionalProperties": true
12
+ }
13
+ }
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,36 +1,36 @@
1
- {
2
- "name": "ddchat",
3
- "version": "0.2.0",
4
- "description": "DDChat channel plugin for OpenClaw",
5
- "type": "module",
6
- "devDependencies": {
7
- "openclaw": "workspace:*"
8
- },
9
- "peerDependencies": {
10
- "openclaw": ">=2026.2.0"
11
- },
12
- "peerDependenciesMeta": {
13
- "openclaw": {
14
- "optional": true
15
- }
16
- },
17
- "openclaw": {
18
- "extensions": [
19
- "./index.ts"
20
- ],
21
- "setupEntry": "./setup-entry.ts",
22
- "channel": {
23
- "id": "ddchat",
24
- "label": "DDChat",
25
- "selectionLabel": "DDChat (IM)",
26
- "detailLabel": "DDChat IM",
27
- "blurb": "DDChat internal IM integration.",
28
- "order": 90
29
- },
30
- "install": {
31
- "npmSpec": "ddchat",
32
- "localPath": "ddchat",
33
- "defaultChoice": "local"
34
- }
35
- }
36
- }
1
+ {
2
+ "name": "ddchat",
3
+ "version": "0.3.0",
4
+ "description": "DDChat channel plugin for OpenClaw",
5
+ "type": "module",
6
+ "devDependencies": {
7
+ "openclaw": "^2026.2.0"
8
+ },
9
+ "peerDependencies": {
10
+ "openclaw": ">=2026.2.0"
11
+ },
12
+ "peerDependenciesMeta": {
13
+ "openclaw": {
14
+ "optional": true
15
+ }
16
+ },
17
+ "openclaw": {
18
+ "extensions": [
19
+ "./index.ts"
20
+ ],
21
+ "setupEntry": "./setup-entry.ts",
22
+ "channel": {
23
+ "id": "ddchat",
24
+ "label": "DDChat",
25
+ "selectionLabel": "DDChat (IM)",
26
+ "detailLabel": "DDChat IM",
27
+ "blurb": "DDChat internal IM integration.",
28
+ "order": 90
29
+ },
30
+ "install": {
31
+ "npmSpec": "ddchat",
32
+ "localPath": "ddchat",
33
+ "defaultChoice": "local"
34
+ }
35
+ }
36
+ }
package/setup-entry.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
2
- import { ddchatPlugin } from "./src/channel.js";
3
-
4
- export default defineSetupPluginEntry(ddchatPlugin);
1
+ import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
2
+ import { ddchatPlugin } from "./src/channel.js";
3
+
4
+ export default defineSetupPluginEntry(ddchatPlugin);