@imweapp/openclaw-imwe 2026.4.12-alpha.1

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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/index.ts +16 -0
  4. package/openclaw.plugin.json +58 -0
  5. package/package.json +73 -0
  6. package/proto/PbBoxPullProto.proto +43 -0
  7. package/proto/PbChatAudioContent.proto +23 -0
  8. package/proto/PbChatDeliverMsg.proto +38 -0
  9. package/proto/PbChatFileMeta.proto +34 -0
  10. package/proto/PbChatMsg.proto +93 -0
  11. package/proto/PbChatRichMediaContent.proto +31 -0
  12. package/proto/PbChatTextContent.proto +38 -0
  13. package/proto/PbMarkdownContent.proto +18 -0
  14. package/proto/PbMsgReadStampContent.proto +11 -0
  15. package/proto/PbPacket.proto +61 -0
  16. package/proto/PbSingleChatMsg.proto +60 -0
  17. package/setup-entry.ts +17 -0
  18. package/src/accounts.ts +109 -0
  19. package/src/api-client.ts +740 -0
  20. package/src/bot-info-cache.ts +49 -0
  21. package/src/channel.runtime.ts +29 -0
  22. package/src/channel.ts +456 -0
  23. package/src/config-schema.ts +26 -0
  24. package/src/e2ee/api.ts +261 -0
  25. package/src/e2ee/canonical.ts +59 -0
  26. package/src/e2ee/errors.ts +103 -0
  27. package/src/e2ee/index.ts +8 -0
  28. package/src/e2ee/proper-lockfile.d.ts +61 -0
  29. package/src/e2ee/service.ts +1273 -0
  30. package/src/e2ee/store.ts +174 -0
  31. package/src/e2ee/types.ts +113 -0
  32. package/src/e2ee/vodozemac.ts +373 -0
  33. package/src/file-transfer/api.ts +364 -0
  34. package/src/file-transfer/concurrency.ts +77 -0
  35. package/src/file-transfer/download.ts +261 -0
  36. package/src/file-transfer/file-crypto.ts +93 -0
  37. package/src/file-transfer/index.ts +18 -0
  38. package/src/file-transfer/scheduler.ts +185 -0
  39. package/src/file-transfer/types.ts +195 -0
  40. package/src/file-transfer/upload.ts +656 -0
  41. package/src/markdown-detect.ts +119 -0
  42. package/src/media-upload.ts +338 -0
  43. package/src/media-utils.ts +110 -0
  44. package/src/monitor.ts +838 -0
  45. package/src/proto/codec.ts +54 -0
  46. package/src/proto/inbound.codec.ts +624 -0
  47. package/src/proto/proto-types.ts +291 -0
  48. package/src/proto/registry.ts +226 -0
  49. package/src/proto/send.codec.ts +535 -0
  50. package/src/recent-message-cache.ts +350 -0
  51. package/src/send.ts +792 -0
  52. package/src/setup-core.ts +62 -0
  53. package/src/types.ts +153 -0
  54. package/src/vodozemackit/index.ts +297 -0
  55. package/src/vodozemackit/pkg/vodozemackit_wasm.d.ts +138 -0
  56. package/src/vodozemackit/pkg/vodozemackit_wasm.js +24 -0
  57. package/src/vodozemackit/pkg/vodozemackit_wasm_bg.js +1172 -0
  58. package/src/vodozemackit/pkg/vodozemackit_wasm_bg.wasm +0 -0
  59. package/src/vodozemackit/pkg/vodozemackit_wasm_bg.wasm.d.ts +109 -0
@@ -0,0 +1,11 @@
1
+ // PbMsgReadStampContent.proto — 消息已读发送内容
2
+
3
+ syntax = "proto3";
4
+
5
+ package proto;
6
+
7
+ // 消息已读 - 发送内容
8
+ message PbMsgReadStampSendContent {
9
+ repeated string clientMsgIds = 1;
10
+ int64 readStamp = 2;
11
+ }
@@ -0,0 +1,61 @@
1
+ // PbPacket.proto — 通用长连接通道协议(业务无关)
2
+ //
3
+ // 来源:im_proto/MsgCore/PbPacket.proto(原样拷贝,仅保留插件所需部分)
4
+ // 插件只处理 SERVER_REQ(type=2)方向的入站消息,
5
+ // 以及 CLIENT_REQ(type=0)方向的出站发送请求。
6
+ //
7
+ // 设计原则:
8
+ // 1. envelope 只承载路由 + 元信息,不携带任何业务字段
9
+ // 2. body 是不透明字节流(业务自己 protobuf 序列化),通道层不解析
10
+ // 3. 业务字段通过 headers 携带,命名约定 x-<module>-<key>,例如:
11
+ // x-im-acct-id : IM 账号 ID(仅 IM 业务使用,分身场景传递)
12
+ // x-im-box-id : IM 事件箱 ID(仅 IM 业务使用)
13
+ // x-im-box-seq : IM 事件箱序列号(仅 IM 业务使用,用于同步)
14
+ // 4. 业务模块各自负责 body 的编解码,通道层不依赖任何业务 .proto
15
+
16
+ syntax = "proto3";
17
+
18
+ package proto;
19
+
20
+ // golang的编译选项
21
+ option go_package = "./;proto";
22
+ // java编译选项
23
+ option java_outer_classname = "PbPacketProto";
24
+ option java_package = "com.imwe.im.proto";
25
+
26
+ enum PbType {
27
+ CLIENT_REQ = 0; // client 请求
28
+ SERVER_RSP = 1; // server 响应
29
+ SERVER_REQ = 2; // server 主动推送(用于服务端投递事件)
30
+ CLIENT_RSP = 3; // client 响应(暂未使用)
31
+ }
32
+
33
+ message PbPacket {
34
+ PbType type = 1; // 数据包类型
35
+ optional PbRequest request = 2; // 请求体
36
+ optional PbResponse response = 3; // 响应体
37
+ }
38
+
39
+ message PbRequest {
40
+ string path = 1; // 请求路径,按业务命名空间约定(/im/... · /wallet/... · /sys/...)
41
+ string reqId = 2; // 请求 ID
42
+ int64 reqStamp = 3; // 请求发起时间
43
+ optional bytes body = 4; // 业务 protobuf 二进制,按 path 由业务自行解码
44
+ map<string, string> headers = 5; // 业务字段 / 元信息(参见文件头注释)
45
+ }
46
+
47
+ message PbResponse {
48
+ string orgPath = 1; // 原始请求路径
49
+ string orgReqId = 2; // 原始请求 ID
50
+ int64 orgReqStamp = 3; // 原始请求发起时间
51
+ int64 rspStamp = 4; // 响应时间
52
+ int64 statusCode = 5; // 状态码(HTTP 语义)
53
+ optional bytes body = 6; // 业务 protobuf 二进制,按 orgPath 由业务自行解码
54
+ map<string, string> headers = 7; // 响应元信息
55
+ }
56
+
57
+ // Pull 消息列表
58
+ message PbPacketList {
59
+ string lastUpdateTime = 1; // 更新的游标
60
+ repeated PbRequest list = 2; // 消息列表
61
+ }
@@ -0,0 +1,60 @@
1
+ // PbSingleChatMsg.proto — 单聊消息请求体
2
+ //
3
+ // 来源:im_proto/Core/PbSingleChatMsg.proto(精简拷贝,仅保留插件所需消息)
4
+ // 插件发送单聊文字消息时,将 PbSingleChatMsgReqBody 编码后放入
5
+ // PbRequest.body,再包装进 PbPacket(CLIENT_REQ) 发送给平台。
6
+
7
+ syntax = "proto3";
8
+
9
+ package proto;
10
+
11
+ option java_outer_classname = "SingleChatProto";
12
+ option java_package = "com.imwe.im.proto";
13
+
14
+ // 单聊消息请求(客户端发送消息时使用)
15
+ message PbSingleChatMsgReqBody {
16
+ string fromId = 1; // 发送方Id(由平台从AppKey解析,可传空)
17
+ string toId = 2; // 接收方Id
18
+ optional string winId = 3; // 密聊窗口id
19
+ string clientMsgId = 4; // 客户端消息id(用于去重和响应匹配)
20
+ int64 clientStamp = 5; // 客户端消息时间戳
21
+ int64 envelopeType = 6; // 信封类型:1-聊天加密类型信封,2-操作内容信封,3-支付内容信封
22
+ optional string fromE2eeId = 7; // 发送方端对端设备id
23
+ bool e2eeFlag = 8; // 消息端对端加密标识(插件使用false,不加密)
24
+ bytes envelope = 9; // 信封内容(e2eeFlag=false时为PbChatMsgEnvelope的bytes)
25
+
26
+ optional int64 msgAutoDeleteInterval = 10; // 自动删除时间配置
27
+ optional int64 msgTimingDeleteStamp = 11; // 定时删除时间
28
+ optional bool offlinePush = 12; // 离线推送开关,仅为true时开启离线通知
29
+
30
+ optional int64 envelopeSubType = 13; // 信封子类型
31
+ optional bool needOfflinePassThrough = 14; // 离线推送消息是否需要透传
32
+ optional bool boldNotify = 15; // 提醒加粗
33
+
34
+ optional int32 convType = 16; // 会话类型,单聊/闪聊等
35
+ }
36
+
37
+ // ── E2EE 单聊加密装箱结构(e2eeFlag=true 时使用)──
38
+ // 出站:对端每台设备的密文装进 PbSingleChatDeviceMsg,
39
+ // 同一联系人的多台设备聚合到 PbSingleChatMsgRecipient,
40
+ // 再多联系人聚合到 PbSingleChatMsgRecipients,序列化为
41
+ // PbSingleChatMsgReqBody.envelope 的 bytes。
42
+
43
+ // 单设备密文条目
44
+ message PbSingleChatDeviceMsg {
45
+ string toE2eeId = 1; // 接收方设备 e2eeId(= 对端 Account.curve25519Key())
46
+ string e2eeSid = 2; // 发送本条消息时使用的 Session.sessionId()
47
+ bytes envelope = 3; // vodozemac EncryptedMessage.body(OlmMessage JSON bytes)
48
+ optional bool boldNotify = 4; // 提醒加粗(透传 PbSingleChatMsgReqBody.boldNotify 语义)
49
+ }
50
+
51
+ // 同一对端 imAcctId 的多设备分发
52
+ message PbSingleChatMsgRecipient {
53
+ string acctId = 1; // 对端 imAcctId
54
+ repeated PbSingleChatDeviceMsg msgs = 2; // 该联系人下每台活跃设备一份密文
55
+ }
56
+
57
+ // 最外层:所有联系人的设备分发集合
58
+ message PbSingleChatMsgRecipients {
59
+ repeated PbSingleChatMsgRecipient recipients = 1;
60
+ }
package/setup-entry.ts ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * setup-entry.ts — 轻量入口(setup-only 模式)
3
+ *
4
+ * 当渠道未配置或被禁用时,core 加载这个文件而不是 index.ts。
5
+ * defineSetupPluginEntry 只暴露 ChannelPlugin 对象(用于 setup wizard),
6
+ * 不注入 runtime,不注册 CLI,不加载任何运行时代码。
7
+ *
8
+ * 这样做的好处:
9
+ * - 节省内存:不加载 monitor.ts、api-client.ts 等重量级模块
10
+ * - 加快启动:setup 流程不需要建立网络连接
11
+ * - 安全隔离:未配置的渠道不会尝试连接平台
12
+ */
13
+
14
+ import { defineSetupPluginEntry } from 'openclaw/plugin-sdk/channel-core';
15
+ import { imwePlugin } from './src/channel.js';
16
+
17
+ export default defineSetupPluginEntry(imwePlugin);
@@ -0,0 +1,109 @@
1
+ /**
2
+ * accounts.ts — 账号解析逻辑
3
+ *
4
+ * 职责:
5
+ * - 从 OpenClawConfig 中读取 imwe 渠道配置
6
+ * - 合并顶层默认值与账号级配置(多账号继承)
7
+ * - 解析 appKey / appSecret(优先级:账号级 config > 顶层 config > 环境变量)
8
+ * - 导出 listImweAccountIds / resolveImweAccount 供 ChannelConfigAdapter 使用
9
+ */
10
+
11
+ import {
12
+ createAccountListHelpers,
13
+ resolveMergedAccountConfig,
14
+ } from 'openclaw/plugin-sdk/account-helpers';
15
+ import { normalizeAccountId } from 'openclaw/plugin-sdk/account-id';
16
+ import type { OpenClawConfig } from 'openclaw/plugin-sdk/config-runtime';
17
+ import type { ImweAccountConfig, ImweConfig, ResolvedImweAccount } from './types.js';
18
+
19
+ // ─────────────────────────────────────────────────────────────────────────────
20
+ // 账号 id 列表工具
21
+ // ─────────────────────────────────────────────────────────────────────────────
22
+
23
+ const { listAccountIds: listImweAccountIds, resolveDefaultAccountId: resolveDefaultImweAccountId } =
24
+ createAccountListHelpers('imwe');
25
+
26
+ export { listImweAccountIds, resolveDefaultImweAccountId };
27
+
28
+ // ─────────────────────────────────────────────────────────────────────────────
29
+ // 配置合并
30
+ // ─────────────────────────────────────────────────────────────────────────────
31
+
32
+ function mergeImweAccountConfig(cfg: OpenClawConfig, accountId: string): ImweAccountConfig {
33
+ return resolveMergedAccountConfig<ImweAccountConfig>({
34
+ channelConfig: cfg.channels?.imwe as ImweAccountConfig | undefined,
35
+ accounts: (cfg.channels?.imwe as ImweConfig | undefined)?.accounts as
36
+ | Record<string, Partial<ImweAccountConfig>>
37
+ | undefined,
38
+ accountId,
39
+ omitKeys: ['defaultAccount'],
40
+ });
41
+ }
42
+
43
+ // ─────────────────────────────────────────────────────────────────────────────
44
+ // 凭证解析:优先级 账号级 config > 顶层 config > 环境变量
45
+ // ─────────────────────────────────────────────────────────────────────────────
46
+
47
+ function resolveImweCredentials(
48
+ merged: ImweAccountConfig,
49
+ accountId: string,
50
+ ): { appKey: string; appSecret: string; source: ResolvedImweAccount['credentialSource'] } {
51
+ // 1. config 里的 appKey + appSecret
52
+ if (merged.appKey?.trim() && merged.appSecret?.trim()) {
53
+ return {
54
+ appKey: merged.appKey.trim(),
55
+ appSecret: merged.appSecret.trim(),
56
+ source: 'config',
57
+ };
58
+ }
59
+
60
+ // 2. 环境变量(仅 default 账号,避免多账号混用同一组 env)
61
+ if (normalizeAccountId(accountId) === 'default') {
62
+ const envKey = process.env.IMWE_APP_KEY?.trim();
63
+ const envSecret = process.env.IMWE_APP_SECRET?.trim();
64
+ if (envKey && envSecret) {
65
+ return { appKey: envKey, appSecret: envSecret, source: 'env' };
66
+ }
67
+ }
68
+
69
+ return { appKey: '', appSecret: '', source: 'none' };
70
+ }
71
+
72
+ // ─────────────────────────────────────────────────────────────────────────────
73
+ // 主解析函数
74
+ // ─────────────────────────────────────────────────────────────────────────────
75
+
76
+ export function resolveImweAccount(params: {
77
+ cfg: OpenClawConfig;
78
+ accountId?: string | null;
79
+ }): ResolvedImweAccount {
80
+ const accountId =
81
+ normalizeAccountId(params.accountId) ??
82
+ normalizeAccountId((params.cfg.channels?.imwe as ImweConfig | undefined)?.defaultAccount) ??
83
+ 'default';
84
+
85
+ const baseEnabled = (params.cfg.channels?.imwe as ImweConfig | undefined)?.enabled !== false;
86
+ const merged = mergeImweAccountConfig(params.cfg, accountId);
87
+ const accountEnabled = merged.enabled !== false;
88
+
89
+ const { appKey, appSecret, source } = resolveImweCredentials(merged, accountId);
90
+
91
+ const apiBaseUrl = merged.apiBaseUrl?.trim() || process.env.IMWE_API_BASE_URL?.trim() || '';
92
+
93
+ return {
94
+ accountId,
95
+ name: merged.name,
96
+ enabled: baseEnabled && accountEnabled,
97
+ apiBaseUrl,
98
+ appKey,
99
+ appSecret,
100
+ credentialSource: source,
101
+ config: merged,
102
+ };
103
+ }
104
+
105
+ export function listEnabledImweAccounts(cfg: OpenClawConfig): ResolvedImweAccount[] {
106
+ return listImweAccountIds(cfg)
107
+ .map((accountId) => resolveImweAccount({ cfg, accountId }))
108
+ .filter((account) => account.enabled);
109
+ }