@yach.zhiyinlou.com/yach 2.12.5-beta.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 (174) hide show
  1. package/README.md +203 -0
  2. package/dist/index.js +18 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/src/accounts/index.js +74 -0
  5. package/dist/src/accounts/index.js.map +1 -0
  6. package/dist/src/accounts/types.js +2 -0
  7. package/dist/src/accounts/types.js.map +1 -0
  8. package/dist/src/card/model-fold-links.js +67 -0
  9. package/dist/src/card/model-fold-links.js.map +1 -0
  10. package/dist/src/card/streaming-card.js +95 -0
  11. package/dist/src/card/streaming-card.js.map +1 -0
  12. package/dist/src/channel/dynamic-agent.js +129 -0
  13. package/dist/src/channel/dynamic-agent.js.map +1 -0
  14. package/dist/src/channel/monitor.js +12 -0
  15. package/dist/src/channel/monitor.js.map +1 -0
  16. package/dist/src/channel/plugin.js +238 -0
  17. package/dist/src/channel/plugin.js.map +1 -0
  18. package/dist/src/channel/sdk.js +199 -0
  19. package/dist/src/channel/sdk.js.map +1 -0
  20. package/dist/src/channel/webhook.js +91 -0
  21. package/dist/src/channel/webhook.js.map +1 -0
  22. package/dist/src/commands/index.js +173 -0
  23. package/dist/src/commands/index.js.map +1 -0
  24. package/dist/src/core/aes.js +19 -0
  25. package/dist/src/core/aes.js.map +1 -0
  26. package/dist/src/core/app-token.js +25 -0
  27. package/dist/src/core/app-token.js.map +1 -0
  28. package/dist/src/core/config.js +84 -0
  29. package/dist/src/core/config.js.map +1 -0
  30. package/dist/src/core/device-flow.js +183 -0
  31. package/dist/src/core/device-flow.js.map +1 -0
  32. package/dist/src/core/fetch.js +126 -0
  33. package/dist/src/core/fetch.js.map +1 -0
  34. package/dist/src/core/reporter.js +189 -0
  35. package/dist/src/core/reporter.js.map +1 -0
  36. package/dist/src/core/runtime.js +10 -0
  37. package/dist/src/core/runtime.js.map +1 -0
  38. package/dist/src/core/tool-client.js +156 -0
  39. package/dist/src/core/tool-client.js.map +1 -0
  40. package/dist/src/core/tool-scopes.js +93 -0
  41. package/dist/src/core/tool-scopes.js.map +1 -0
  42. package/dist/src/core/types.js +2 -0
  43. package/dist/src/core/types.js.map +1 -0
  44. package/dist/src/core/user-token-client.js +176 -0
  45. package/dist/src/core/user-token-client.js.map +1 -0
  46. package/dist/src/core/user-token-errors.js +51 -0
  47. package/dist/src/core/user-token-errors.js.map +1 -0
  48. package/dist/src/core/user-token-store.js +229 -0
  49. package/dist/src/core/user-token-store.js.map +1 -0
  50. package/dist/src/core/winston-logger.js +68 -0
  51. package/dist/src/core/winston-logger.js.map +1 -0
  52. package/dist/src/core/yach-client.js +81 -0
  53. package/dist/src/core/yach-client.js.map +1 -0
  54. package/dist/src/core/yach-logger.js +86 -0
  55. package/dist/src/core/yach-logger.js.map +1 -0
  56. package/dist/src/core/yach-ticket.js +31 -0
  57. package/dist/src/core/yach-ticket.js.map +1 -0
  58. package/dist/src/messaging/inbound/abort-detect.js +73 -0
  59. package/dist/src/messaging/inbound/abort-detect.js.map +1 -0
  60. package/dist/src/messaging/inbound/callback-handler.js +163 -0
  61. package/dist/src/messaging/inbound/callback-handler.js.map +1 -0
  62. package/dist/src/messaging/inbound/dedup.js +80 -0
  63. package/dist/src/messaging/inbound/dedup.js.map +1 -0
  64. package/dist/src/messaging/inbound/dispatcher.js +198 -0
  65. package/dist/src/messaging/inbound/dispatcher.js.map +1 -0
  66. package/dist/src/messaging/inbound/handler.js +215 -0
  67. package/dist/src/messaging/inbound/handler.js.map +1 -0
  68. package/dist/src/messaging/inbound/media.js +28 -0
  69. package/dist/src/messaging/inbound/media.js.map +1 -0
  70. package/dist/src/messaging/inbound/parse.js +53 -0
  71. package/dist/src/messaging/inbound/parse.js.map +1 -0
  72. package/dist/src/messaging/inbound/types.js +2 -0
  73. package/dist/src/messaging/inbound/types.js.map +1 -0
  74. package/dist/src/messaging/outbound/chat-queue.js +28 -0
  75. package/dist/src/messaging/outbound/chat-queue.js.map +1 -0
  76. package/dist/src/messaging/outbound/outbound.js +138 -0
  77. package/dist/src/messaging/outbound/outbound.js.map +1 -0
  78. package/dist/src/messaging/outbound/reply-dispatcher.js +218 -0
  79. package/dist/src/messaging/outbound/reply-dispatcher.js.map +1 -0
  80. package/dist/src/oapi/audio.js +26 -0
  81. package/dist/src/oapi/audio.js.map +1 -0
  82. package/dist/src/oapi/calendar.js +69 -0
  83. package/dist/src/oapi/calendar.js.map +1 -0
  84. package/dist/src/oapi/contacts.js +85 -0
  85. package/dist/src/oapi/contacts.js.map +1 -0
  86. package/dist/src/oapi/cos.js +44 -0
  87. package/dist/src/oapi/cos.js.map +1 -0
  88. package/dist/src/oapi/doc.js +784 -0
  89. package/dist/src/oapi/doc.js.map +1 -0
  90. package/dist/src/oapi/errors.js +15 -0
  91. package/dist/src/oapi/errors.js.map +1 -0
  92. package/dist/src/oapi/expression.js +43 -0
  93. package/dist/src/oapi/expression.js.map +1 -0
  94. package/dist/src/oapi/group.js +66 -0
  95. package/dist/src/oapi/group.js.map +1 -0
  96. package/dist/src/oapi/im.js +120 -0
  97. package/dist/src/oapi/im.js.map +1 -0
  98. package/dist/src/oapi/index.js +2 -0
  99. package/dist/src/oapi/index.js.map +1 -0
  100. package/dist/src/oapi/meeting.js +88 -0
  101. package/dist/src/oapi/meeting.js.map +1 -0
  102. package/dist/src/oapi/okr.js +80 -0
  103. package/dist/src/oapi/okr.js.map +1 -0
  104. package/dist/src/oapi/robot.js +36 -0
  105. package/dist/src/oapi/robot.js.map +1 -0
  106. package/dist/src/oapi/stream.js +67 -0
  107. package/dist/src/oapi/stream.js.map +1 -0
  108. package/dist/src/oapi/team.js +40 -0
  109. package/dist/src/oapi/team.js.map +1 -0
  110. package/dist/src/oapi/topic.js +85 -0
  111. package/dist/src/oapi/topic.js.map +1 -0
  112. package/dist/src/oapi/weekly.js +80 -0
  113. package/dist/src/oapi/weekly.js.map +1 -0
  114. package/dist/src/tools/auto-auth.js +344 -0
  115. package/dist/src/tools/auto-auth.js.map +1 -0
  116. package/dist/src/tools/calendar/index.js +295 -0
  117. package/dist/src/tools/calendar/index.js.map +1 -0
  118. package/dist/src/tools/contacts/index.js +97 -0
  119. package/dist/src/tools/contacts/index.js.map +1 -0
  120. package/dist/src/tools/doc/index.js +314 -0
  121. package/dist/src/tools/doc/index.js.map +1 -0
  122. package/dist/src/tools/doc/summarize.js +337 -0
  123. package/dist/src/tools/doc/summarize.js.map +1 -0
  124. package/dist/src/tools/group/index.js +96 -0
  125. package/dist/src/tools/group/index.js.map +1 -0
  126. package/dist/src/tools/helpers.js +10 -0
  127. package/dist/src/tools/helpers.js.map +1 -0
  128. package/dist/src/tools/im/index.js +7 -0
  129. package/dist/src/tools/im/index.js.map +1 -0
  130. package/dist/src/tools/im/messages-read.js +65 -0
  131. package/dist/src/tools/im/messages-read.js.map +1 -0
  132. package/dist/src/tools/im/messages-recall.js +44 -0
  133. package/dist/src/tools/im/messages-recall.js.map +1 -0
  134. package/dist/src/tools/index.js +25 -0
  135. package/dist/src/tools/index.js.map +1 -0
  136. package/dist/src/tools/meeting/index.js +170 -0
  137. package/dist/src/tools/meeting/index.js.map +1 -0
  138. package/dist/src/tools/okr/index.js +86 -0
  139. package/dist/src/tools/okr/index.js.map +1 -0
  140. package/dist/src/tools/robot/groups.js +31 -0
  141. package/dist/src/tools/robot/groups.js.map +1 -0
  142. package/dist/src/tools/robot/index.js +5 -0
  143. package/dist/src/tools/robot/index.js.map +1 -0
  144. package/dist/src/tools/space/space-node.js +454 -0
  145. package/dist/src/tools/space/space-node.js.map +1 -0
  146. package/dist/src/tools/topic/index.js +12 -0
  147. package/dist/src/tools/topic/index.js.map +1 -0
  148. package/dist/src/tools/topic/publish-comment.js +70 -0
  149. package/dist/src/tools/topic/publish-comment.js.map +1 -0
  150. package/dist/src/tools/topic/publish-post.js +95 -0
  151. package/dist/src/tools/topic/publish-post.js.map +1 -0
  152. package/dist/src/tools/weekly/index.js +462 -0
  153. package/dist/src/tools/weekly/index.js.map +1 -0
  154. package/dist/src/tools/weekly/work-summary-task.js +157 -0
  155. package/dist/src/tools/weekly/work-summary-task.js.map +1 -0
  156. package/dist/src/vendor/tal-msg-sdk/index.cjs +1 -0
  157. package/dist/src/vendor/tal-msg-sdk/index.d.cts +1069 -0
  158. package/dist/src/vendor/vendor/tal-msg-sdk/index.cjs +1 -0
  159. package/dist/src/vendor/vendor/tal-msg-sdk/index.d.cts +1069 -0
  160. package/openclaw.plugin.json +41 -0
  161. package/package.json +72 -0
  162. package/skills/yach-calendar/SKILL.md +560 -0
  163. package/skills/yach-contacts/SKILL.md +18 -0
  164. package/skills/yach-doc-read/SKILL.md +79 -0
  165. package/skills/yach-doc-write/SKILL.md +139 -0
  166. package/skills/yach-group/SKILL.md +129 -0
  167. package/skills/yach-im/SKILL.md +81 -0
  168. package/skills/yach-meeting/SKILL.md +147 -0
  169. package/skills/yach-okr/SKILL.md +65 -0
  170. package/skills/yach-robot/SKILL.md +46 -0
  171. package/skills/yach-space/SKILL.md +418 -0
  172. package/skills/yach-team/SKILL.md +52 -0
  173. package/skills/yach-topic/SKILL.md +203 -0
  174. package/skills/yach-weekly/SKILL.md +627 -0
package/README.md ADDED
@@ -0,0 +1,203 @@
1
+ # yach(知音楼)OpenClaw 渠道插件
2
+
3
+ 将好未来内部 IM 工具**知音楼**接入 OpenClaw,让员工在熟悉的聊天界面直接与 AI 助手对话。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ openclaw plugins install openclaw-plugin-yach.zip
9
+ ```
10
+
11
+ ## 配置
12
+
13
+ 在 `~/.openclaw/openclaw.json` 中添加渠道配置:
14
+
15
+ ```json
16
+ {
17
+ "channels": {
18
+ "yach": {
19
+ "enabled": true,
20
+ "accounts": {
21
+ "my-bot": {
22
+ "name": "AI 助手",
23
+ "appKey": "your-app-key",
24
+ "appSecret": "your-app-secret",
25
+ "baseUrl": "https://yach-oapi.zhiyinlou.com"
26
+ }
27
+ },
28
+ "replyMode": "stream",
29
+ "dmPolicy": "open",
30
+ "groupPolicy": "disabled"
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ ## 知音楼平台配置
37
+
38
+ 在知音楼开放平台,将机器人的**回调地址**设置为:
39
+
40
+ ```
41
+ http://<openclaw-host>:18789/yach/<accountId>/messages
42
+ ```
43
+
44
+ 其中 `<accountId>` 是 `channels.yach.accounts` 中的账号 key(如上例中的 `my-bot`)。
45
+
46
+ 也可以通过 `webhookPath` 自定义路径:
47
+
48
+ ```json
49
+ {
50
+ "channels": {
51
+ "yach": {
52
+ "accounts": {
53
+ "my-bot": {
54
+ "webhookPath": "/my-bot/webhook"
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ## 消息接收模式(connectionMode)
63
+
64
+ 支持两种消息接收传输方式:
65
+
66
+ | 值 | 效果 |
67
+ |----|------|
68
+ | `webhook`(默认)| HTTP POST 回调,需公网可达 |
69
+ | `channel` | Channel SDK 长连接,适合无公网 IP 的内网环境 |
70
+
71
+ ```json
72
+ {
73
+ "channels": {
74
+ "yach": {
75
+ "accounts": {
76
+ "my-bot": {
77
+ "connectionMode": "channel"
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ 两种模式下回复均走 OAPI,仅消息接收方式不同。
86
+
87
+ ## Agent 路由
88
+
89
+ 在顶层 `bindings` 中配置消息路由规则:
90
+
91
+ ```json
92
+ {
93
+ "bindings": [
94
+ {
95
+ "agentId": "main",
96
+ "match": {
97
+ "channel": "yach"
98
+ }
99
+ }
100
+ ]
101
+ }
102
+ ```
103
+
104
+ 也支持按账号或按用户精细路由:
105
+
106
+ ```json
107
+ {
108
+ "bindings": [
109
+ {
110
+ "agentId": "vip-agent",
111
+ "match": {
112
+ "channel": "yach",
113
+ "accountId": "my-bot",
114
+ "peer": { "kind": "direct", "id": "some-user-id" }
115
+ }
116
+ }
117
+ ]
118
+ }
119
+ ```
120
+
121
+ ## 动态 Agent(多用户隔离)
122
+
123
+ 开启后,私聊用户在首次发消息时自动创建独立的 Agent 实例,群聊每个群也会获得独立 Agent,各拥有隔离的 workspace 和对话历史,无需手动配置 `bindings`。
124
+
125
+ ```json
126
+ {
127
+ "channels": {
128
+ "yach": {
129
+ "dynamicAgentCreation": {
130
+ "enabled": true,
131
+ "maxAgents": 1000
132
+ }
133
+ }
134
+ }
135
+ }
136
+ ```
137
+
138
+ Agent ID 自动生成规则:
139
+ - 私聊:`yach-{accountId}-{senderId}`
140
+ - 群聊:`yach-{accountId}-group-{groupId}`
141
+
142
+ 生成的 Agent 自动写入配置文件的 `agents.list` 和 `bindings`。
143
+
144
+ ## 访问控制
145
+
146
+ ### 私聊(dmPolicy)
147
+
148
+ | 值 | 效果 |
149
+ |----|------|
150
+ | `open`(默认)| 所有人均可使用 |
151
+ | `allowlist` | 仅 `allowFrom` 白名单中的用户 |
152
+ | `pairing` | 未授权用户收到配对码,管理员通过 `openclaw pairing list yach` 审批 |
153
+
154
+ ### 群聊(groupPolicy)
155
+
156
+ | 值 | 效果 |
157
+ |----|------|
158
+ | `open` | 响应所有群(知音楼只回调 @ 机器人的消息) |
159
+ | `allowlist` | 仅 `groupAllowFrom` 白名单中的群 |
160
+ | `disabled`(默认)| 禁用所有群消息 |
161
+
162
+ ## 回复模式(replyMode)
163
+
164
+ | 值 | 效果 |
165
+ |----|------|
166
+ | `stream`(默认)| 流式消息卡片,AI 生成内容实时逐字推送到卡片(create → push → close) |
167
+ | `direct` | AI 生成完成后一次性发送 markdown 消息,无超时限制 |
168
+
169
+ 流式模式特性:
170
+ - **懒启动**:首次收到 AI 内容时才创建卡片
171
+ - **智能限流**:最多 10 次/秒推送,超频内容自动合并
172
+ - **自动轮换**:卡片有效期 10 分钟,9 分钟时自动切换新卡片
173
+ - **显式关闭**:AI 输出完成后关闭卡片,客户端不再显示等待状态
174
+
175
+ ## 支持的消息类型
176
+
177
+ | msgtype | 处理方式 |
178
+ |---------|---------|
179
+ | `text` | 直接转发给 AI |
180
+ | `audio` | 提取语音识别文本(`audio_text`) |
181
+ | `image` | 提取 OCR 识别文本(`image_text`)或下载图片 |
182
+ | `file` / `video` | 下载附件供 AI 处理 |
183
+ | `reply` / `fold` / `link` / `merge_forward` | 提取 content 字段 |
184
+ | `start_new_session` | 触发 `/new` 重置会话 |
185
+ | 其他 | 静默忽略,返回 `{ msgtype: "empty" }` |
186
+
187
+ ## 发布
188
+
189
+ 1. `openclaw.install.npmSpec` 的值为 `name@version`(与 `package.json` 顶层 `name`、`version` 保持一致)。
190
+ 2. CDN 脚本安装构建:
191
+ ```bash
192
+ make publish # 发布当前版本到 CDN
193
+ make publish-latest # 发布并设为 latest,刷新 CDN
194
+ ```
195
+ 3. npm 包构建发布:
196
+ ```bash
197
+ npm run npm:publish:beta # 发布到 npm,tag 为 beta
198
+ npm run npm:publish # 发布到 npm,tag 为 latest
199
+ ```
200
+
201
+ ## 更多
202
+
203
+ - 架构说明:[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
2
+ import { yachPlugin } from "./src/channel/plugin.js";
3
+ import { setYachRuntime } from "./src/core/runtime.js";
4
+ import { registerYachTools } from "./src/tools/index.js";
5
+ import { registerYachCommands } from "./src/commands/index.js";
6
+ export default {
7
+ id: "yach",
8
+ name: "Yach",
9
+ description: "Yach channel plugin",
10
+ configSchema: emptyPluginConfigSchema(),
11
+ register(api) {
12
+ setYachRuntime(api.runtime);
13
+ api.registerChannel({ plugin: yachPlugin });
14
+ registerYachTools(api);
15
+ registerYachCommands(api);
16
+ },
17
+ };
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,eAAe;IACb,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,qBAAqB;IAClC,YAAY,EAAE,uBAAuB,EAAE;IACvC,QAAQ,CAAC,GAAsB;QAC7B,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5B,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QAC5C,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACvB,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;CACF,CAAC"}
@@ -0,0 +1,74 @@
1
+ import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
2
+ function getYachPluginCfg(cfg) {
3
+ return cfg?.["channels"]?.["yach"] ?? {};
4
+ }
5
+ function listConfiguredAccountIds(cfg) {
6
+ const accounts = getYachPluginCfg(cfg).accounts;
7
+ if (!accounts || typeof accounts !== "object")
8
+ return [];
9
+ return Object.keys(accounts).filter(Boolean);
10
+ }
11
+ export function listYachAccountIds(cfg) {
12
+ const ids = listConfiguredAccountIds(cfg);
13
+ if (ids.length === 0)
14
+ return [DEFAULT_ACCOUNT_ID];
15
+ return [...ids].sort((a, b) => a.localeCompare(b));
16
+ }
17
+ function mergeYachAccountConfig(cfg, accountId) {
18
+ const pluginCfg = getYachPluginCfg(cfg);
19
+ const { accounts: _ignored, ...base } = pluginCfg;
20
+ const accountOverrides = pluginCfg.accounts?.[accountId] ?? {};
21
+ return { ...base, ...accountOverrides };
22
+ }
23
+ export function resolveYachAccount(params) {
24
+ const accountId = normalizeAccountId(params.accountId);
25
+ const pluginCfg = getYachPluginCfg(params.cfg);
26
+ const baseEnabled = pluginCfg.enabled !== false;
27
+ const merged = mergeYachAccountConfig(params.cfg, accountId);
28
+ const accountEnabled = merged.enabled !== false;
29
+ const appKey = merged.appKey?.trim();
30
+ const appSecret = merged.appSecret?.trim();
31
+ const configured = Boolean(appKey && appSecret);
32
+ return {
33
+ accountId,
34
+ name: merged.name,
35
+ enabled: baseEnabled && accountEnabled,
36
+ configured,
37
+ appKey,
38
+ appSecret,
39
+ baseUrl: merged.baseUrl ?? "https://yach-oapi.zhiyinlou.com",
40
+ config: merged,
41
+ };
42
+ }
43
+ export function resolveYachAccountByBotId(params) {
44
+ const { cfg, botId } = params;
45
+ const pluginCfg = getYachPluginCfg(cfg);
46
+ const accounts = pluginCfg.accounts;
47
+ if (accounts) {
48
+ const matchedKey = Object.entries(accounts).find(([, v]) => v?.appKey === botId)?.[0];
49
+ const keyById = normalizeAccountId(botId);
50
+ const resolvedKey = matchedKey ?? (accounts[keyById] ? keyById : undefined);
51
+ if (resolvedKey) {
52
+ return resolveYachAccount({ cfg, accountId: resolvedKey });
53
+ }
54
+ }
55
+ return resolveYachAccount({ cfg, accountId: DEFAULT_ACCOUNT_ID });
56
+ }
57
+ export function resolveDefaultYachAccountId(cfg) {
58
+ const ids = listYachAccountIds(cfg);
59
+ if (ids.includes(DEFAULT_ACCOUNT_ID))
60
+ return DEFAULT_ACCOUNT_ID;
61
+ return ids[0] ?? DEFAULT_ACCOUNT_ID;
62
+ }
63
+ export function getAccountBotCfg(account) {
64
+ if (!account.appKey || !account.appSecret) {
65
+ throw new Error(`[yach] account ${account.accountId} missing appKey/appSecret`);
66
+ }
67
+ return { appKey: account.appKey, appSecret: account.appSecret };
68
+ }
69
+ export function listEnabledYachAccounts(cfg) {
70
+ return listYachAccountIds(cfg)
71
+ .map((accountId) => resolveYachAccount({ cfg, accountId }))
72
+ .filter((a) => a.enabled && a.configured);
73
+ }
74
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/accounts/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/accounts/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * 知音楼 fold link 工具。
3
+ *
4
+ * 知音楼支持一种特殊 markdown 链接:点击后自动发送一条 fold 类型消息,
5
+ * 内容为 reply 参数的 URL decode 值,可用于实现类似 Telegram inline button 的交互。
6
+ *
7
+ * 格式:[显示文本](yach://yach.zhiyinlou.com/session/robot?type=fold&extra=%7B%7D&reply=<encoded>)
8
+ */
9
+ const FOLD_BASE = "yach://yach.zhiyinlou.com/session/robot?type=fold&extra=%7B%7D&reply=";
10
+ export function buildFoldUrl(cmd) {
11
+ return `${FOLD_BASE}${encodeURIComponent(cmd)}`;
12
+ }
13
+ function foldLink(text, cmd) {
14
+ return `[${text}](${buildFoldUrl(cmd)})`;
15
+ }
16
+ /**
17
+ * 将 /models 命令返回的纯文本转换为可点击的 fold link markdown。
18
+ */
19
+ export function transformModelTextToFoldLinks(text) {
20
+ const lines = text.split("\n");
21
+ const firstLine = lines[0]?.trim() ?? "";
22
+ if (firstLine === "Providers:") {
23
+ const out = lines.map((line) => {
24
+ const m = line.match(/^(- )(\S+) \((\d+)\)$/);
25
+ if (m) {
26
+ const provider = m[2];
27
+ return `${m[1]}${foldLink(`${provider} (${m[3]})`, `/models ${provider}`)}`;
28
+ }
29
+ return line;
30
+ });
31
+ return out.join("\n");
32
+ }
33
+ if (firstLine.startsWith("Models (")) {
34
+ const out = lines.map((line) => {
35
+ const modelM = line.match(/^(- )((\S+)\/(\S+))$/);
36
+ if (modelM) {
37
+ const ref = modelM[2];
38
+ return `${modelM[1]}${foldLink(ref, `/model ${ref}`)}`;
39
+ }
40
+ const moreM = line.match(/^(More: )(\/models .+)$/);
41
+ if (moreM)
42
+ return foldLink(line, moreM[2]);
43
+ const allM = line.match(/^(All: )(\/models .+)$/);
44
+ if (allM)
45
+ return foldLink(line, allM[2]);
46
+ return line;
47
+ });
48
+ return out.join("\n");
49
+ }
50
+ return null;
51
+ }
52
+ /**
53
+ * 从 fold 消息的 content(yach:// URL)中提取实际命令文本。
54
+ */
55
+ export function decodeFoldContent(content) {
56
+ const m = content.match(/[?&]reply=(.*)$/);
57
+ if (m?.[1] !== undefined) {
58
+ try {
59
+ return decodeURIComponent(m[1]);
60
+ }
61
+ catch {
62
+ return m[1];
63
+ }
64
+ }
65
+ return content;
66
+ }
67
+ //# sourceMappingURL=model-fold-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-fold-links.js","sourceRoot":"","sources":["../../../src/card/model-fold-links.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,SAAS,GAAG,uEAAuE,CAAC;AAE1F,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAG,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,GAAW;IACzC,OAAO,IAAI,IAAI,KAAK,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAAC,IAAY;IACxD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAEzC,IAAI,SAAS,KAAK,YAAY,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;YAC9C,IAAI,CAAC,EAAE,CAAC;gBACN,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;gBACvB,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,WAAW,QAAQ,EAAE,CAAC,EAAE,CAAC;YAC9E,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,IAAI,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;YAClD,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;gBACvB,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,EAAE,CAAC,EAAE,CAAC;YACzD,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;YACpD,IAAI,KAAK;gBAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;YAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;YAClD,IAAI,IAAI;gBAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;YAC1C,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC3C,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,95 @@
1
+ /** 卡片轮换阈值:9 分钟(留 1 分钟安全余量) */
2
+ const CARD_ROTATE_MS = 9 * 60 * 1000;
3
+ export class YachStreamingCard {
4
+ client;
5
+ msgId = null;
6
+ queue = Promise.resolve();
7
+ closed = false;
8
+ pendingContent = "";
9
+ flushTimer = null;
10
+ throttleMs = 100; // 限流:最多 10 次/秒
11
+ toId = null;
12
+ sessionType = null;
13
+ replyToMessageId;
14
+ createdAt = 0;
15
+ constructor(client) {
16
+ this.client = client;
17
+ }
18
+ async start(toId, sessionType, replyToMessageId) {
19
+ if (this.msgId)
20
+ return;
21
+ this.toId = toId;
22
+ this.sessionType = sessionType;
23
+ this.replyToMessageId = replyToMessageId;
24
+ try {
25
+ this.msgId = await this.client.stream.createCard(toId, sessionType, replyToMessageId);
26
+ this.createdAt = Date.now();
27
+ }
28
+ catch (err) {
29
+ // start 失败不向上抛,card 保持未激活状态,后续 push/close 是 no-op
30
+ }
31
+ }
32
+ push(content) {
33
+ if (!this.msgId || this.closed || !content)
34
+ return;
35
+ this.pendingContent += content;
36
+ if (!this.flushTimer) {
37
+ this.flushTimer = setTimeout(() => {
38
+ this.flushTimer = null;
39
+ if (!this.pendingContent || this.closed)
40
+ return;
41
+ const toSend = this.pendingContent;
42
+ this.pendingContent = "";
43
+ this.queue = this.queue.then(() => this.doPush(toSend));
44
+ }, this.throttleMs);
45
+ }
46
+ }
47
+ async close(finalContent) {
48
+ if (!this.msgId || this.closed)
49
+ return;
50
+ this.closed = true;
51
+ if (this.flushTimer !== null) {
52
+ clearTimeout(this.flushTimer);
53
+ this.flushTimer = null;
54
+ }
55
+ await this.queue;
56
+ const remaining = this.pendingContent + (finalContent ?? "");
57
+ this.pendingContent = "";
58
+ if (remaining)
59
+ await this.doPush(remaining);
60
+ await this.client.stream.close(this.msgId).catch(() => { });
61
+ }
62
+ isActive() {
63
+ return this.msgId !== null && !this.closed;
64
+ }
65
+ getCardMsgId() {
66
+ return this.msgId;
67
+ }
68
+ async rotate() {
69
+ if (!this.toId || !this.sessionType)
70
+ return;
71
+ try {
72
+ if (this.msgId)
73
+ await this.client.stream.close(this.msgId).catch(() => { });
74
+ this.msgId = await this.client.stream.createCard(this.toId, this.sessionType, this.replyToMessageId);
75
+ this.createdAt = Date.now();
76
+ }
77
+ catch {
78
+ // rotate 失败继续用旧卡片推送
79
+ }
80
+ }
81
+ async doPush(content) {
82
+ if (!this.msgId || !content)
83
+ return;
84
+ if (this.createdAt && Date.now() - this.createdAt > CARD_ROTATE_MS) {
85
+ await this.rotate();
86
+ }
87
+ try {
88
+ await this.client.stream.push(this.msgId, content);
89
+ }
90
+ catch {
91
+ // push 失败静默,不中断队列
92
+ }
93
+ }
94
+ }
95
+ //# sourceMappingURL=streaming-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming-card.js","sourceRoot":"","sources":["../../../src/card/streaming-card.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { getYachRuntime, getYachConfig } from "../core/runtime.js";
5
+ import { reportEvent } from "../core/reporter.js";
6
+ /** Promise-chain mutex:保证 maybeCreateDynamicAgent 串行执行 */
7
+ let configMutex = Promise.resolve();
8
+ function withConfigLock(fn) {
9
+ let release;
10
+ const next = new Promise((resolve) => {
11
+ release = resolve;
12
+ });
13
+ const result = configMutex.then(fn);
14
+ configMutex = result.then(() => release(), () => release());
15
+ return result;
16
+ }
17
+ /**
18
+ * 检查是否需要为私聊用户或群聊创建动态 Agent,并在需要时创建。
19
+ * 私聊每位用户一个 Agent,群聊每个群一个 Agent,各有独立 workspace。
20
+ *
21
+ * 使用互斥锁串行化,防止并发读-写导致配置数据丢失。
22
+ */
23
+ export function maybeCreateDynamicAgent(params) {
24
+ return withConfigLock(() => maybeCreateDynamicAgentUnsafe(params));
25
+ }
26
+ async function maybeCreateDynamicAgentUnsafe(params) {
27
+ const { peerId, peerKind, dynamicCfg, accountId, log } = params;
28
+ const runtime = getYachRuntime();
29
+ // 在锁内重新读取最新配置,避免使用调用方传入的可能已过期的快照
30
+ const cfg = getYachConfig();
31
+ // 检查该用户/群是否已有绑定
32
+ const existingBindings = cfg.bindings ?? [];
33
+ const hasBinding = existingBindings.some((b) => b.match?.channel === "yach" &&
34
+ b.match?.peer?.kind === peerKind &&
35
+ b.match?.peer?.id === peerId &&
36
+ b.match?.accountId === accountId);
37
+ if (hasBinding) {
38
+ return { created: false, updatedCfg: cfg };
39
+ }
40
+ // 检查 maxAgents 上限
41
+ if (dynamicCfg.maxAgents !== undefined) {
42
+ const yachAgentCount = (cfg.agents?.list ?? []).filter((a) => a.id.startsWith("yach-")).length;
43
+ if (yachAgentCount >= dynamicCfg.maxAgents) {
44
+ log(`yach: maxAgents limit (${dynamicCfg.maxAgents}) reached, not creating agent for ${peerKind}:${peerId}`);
45
+ reportEvent("channel.dynamic-agent", "maxAgents limit reached", { accountId, peerId, peerKind, maxAgents: String(dynamicCfg.maxAgents) });
46
+ return { created: false, updatedCfg: cfg };
47
+ }
48
+ }
49
+ const agentId = peerKind === "group"
50
+ ? `yach-${accountId}-group-${peerId}`
51
+ : `yach-${accountId}-${peerId}`;
52
+ // Agent 已存在但缺少绑定 —— 仅补充绑定
53
+ const existingAgent = (cfg.agents?.list ?? []).find((a) => a.id === agentId);
54
+ if (existingAgent) {
55
+ log(`yach: agent "${agentId}" exists, adding missing binding for ${peerKind}:${peerId}`);
56
+ const updatedCfg = {
57
+ ...cfg,
58
+ bindings: [
59
+ ...existingBindings,
60
+ {
61
+ agentId,
62
+ match: {
63
+ accountId: accountId,
64
+ channel: "yach",
65
+ peer: { kind: peerKind, id: peerId },
66
+ },
67
+ },
68
+ ],
69
+ };
70
+ await runtime.config.writeConfigFile(updatedCfg);
71
+ reportEvent("channel.dynamic-agent", "binding added to existing agent", { accountId, peerId, peerKind, agentId });
72
+ return { created: true, updatedCfg, agentId };
73
+ }
74
+ // 解析路径模板
75
+ const workspaceTemplate = dynamicCfg.workspaceTemplate ?? "~/.openclaw/workspace-{agentId}";
76
+ const agentDirTemplate = dynamicCfg.agentDirTemplate ?? "~/.openclaw/agents/{agentId}/agent";
77
+ const workspace = resolveUserPath(workspaceTemplate.replace("{userId}", peerId).replace("{agentId}", agentId));
78
+ const agentDir = resolveUserPath(agentDirTemplate.replace("{userId}", peerId).replace("{agentId}", agentId));
79
+ log(`yach: creating dynamic agent "${agentId}" for ${peerKind}:${peerId}`);
80
+ log(` workspace: ${workspace}`);
81
+ log(` agentDir: ${agentDir}`);
82
+ // 创建目录
83
+ await fs.promises.mkdir(workspace, { recursive: true });
84
+ await fs.promises.mkdir(agentDir, { recursive: true });
85
+ // 写入更新后的配置
86
+ const updatedCfg = {
87
+ ...cfg,
88
+ agents: {
89
+ ...cfg.agents,
90
+ defaults: {
91
+ ...cfg.agents?.defaults,
92
+ // heartbeat: {
93
+ // ...cfg.agents?.defaults?.heartbeat,
94
+ // ackMaxChars: Math.floor(Math.random() * 2) + 29,
95
+ // }
96
+ },
97
+ list: [...(cfg.agents?.list ?? []), {
98
+ id: agentId,
99
+ workspace,
100
+ agentDir,
101
+ // heartbeat:{}
102
+ }],
103
+ },
104
+ bindings: [
105
+ ...existingBindings,
106
+ {
107
+ agentId,
108
+ match: {
109
+ accountId: accountId,
110
+ channel: "yach",
111
+ peer: { kind: peerKind, id: peerId },
112
+ },
113
+ },
114
+ ],
115
+ };
116
+ await runtime.config.writeConfigFile(updatedCfg);
117
+ reportEvent("channel.dynamic-agent", "agent created", { accountId, peerId, peerKind, agentId });
118
+ return { created: true, updatedCfg, agentId };
119
+ }
120
+ /**
121
+ * 将以 ~ 开头的路径展开为用户主目录。
122
+ */
123
+ function resolveUserPath(p) {
124
+ if (p.startsWith("~/")) {
125
+ return path.join(os.homedir(), p.slice(2));
126
+ }
127
+ return p;
128
+ }
129
+ //# sourceMappingURL=dynamic-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-agent.js","sourceRoot":"","sources":["../../../src/channel/dynamic-agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { monitorWebhook } from "./webhook.js";
2
+ import { monitorChannel } from "./sdk.js";
3
+ export function monitorSingleAccount(options) {
4
+ const { account, logger } = options;
5
+ const connectionMode = account.config.connectionMode ?? "channel";
6
+ logger.info("[yach] account " + account.accountId + " starting in " + connectionMode + " mode");
7
+ if (connectionMode === "channel") {
8
+ return monitorChannel(options);
9
+ }
10
+ return monitorWebhook(options);
11
+ }
12
+ //# sourceMappingURL=monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor.js","sourceRoot":"","sources":["../../../src/channel/monitor.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAW1C,MAAM,UAAU,oBAAoB,CAAC,OAA8B;IACjE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACpC,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,IAAI,SAAS,CAAC;IAElE,MAAM,CAAC,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,SAAS,GAAG,eAAe,GAAG,cAAc,GAAG,OAAO,CAAC,CAAC;IAEhG,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC"}