@workclaw/openclaw-workclaw 1.0.18 → 1.0.19

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 (142) hide show
  1. package/README.md +364 -345
  2. package/dist/api.d.ts +3 -0
  3. package/dist/api.js +3 -0
  4. package/dist/index.d.ts +11 -0
  5. package/dist/index.js +263 -0
  6. package/dist/setup-entry.d.ts +2 -0
  7. package/dist/setup-entry.js +12 -0
  8. package/dist/src/accounts.d.ts +66 -0
  9. package/dist/src/accounts.js +279 -0
  10. package/dist/src/api/accounts-api.d.ts +2 -0
  11. package/dist/src/api/accounts-api.js +130 -0
  12. package/dist/src/api/prompts-api.d.ts +2 -0
  13. package/dist/src/api/prompts-api.js +103 -0
  14. package/dist/src/api/session-api.d.ts +2 -0
  15. package/dist/src/api/session-api.js +207 -0
  16. package/dist/src/api/skills-api.d.ts +2 -0
  17. package/dist/src/api/skills-api.js +64 -0
  18. package/dist/src/api/workspace.d.ts +3 -0
  19. package/dist/src/api/workspace.js +28 -0
  20. package/dist/src/channel.d.ts +11 -0
  21. package/{src/channel.ts → dist/src/channel.js} +198 -225
  22. package/dist/src/config-schema.d.ts +93 -0
  23. package/dist/src/config-schema.js +56 -0
  24. package/dist/src/connection/workclaw-client.d.ts +147 -0
  25. package/dist/src/connection/workclaw-client.js +351 -0
  26. package/dist/src/gateway/agent-handlers.d.ts +12 -0
  27. package/{src/gateway/agent-handlers.ts → dist/src/gateway/agent-handlers.js} +433 -523
  28. package/dist/src/gateway/config-writer.d.ts +71 -0
  29. package/dist/src/gateway/config-writer.js +312 -0
  30. package/dist/src/gateway/message-context.d.ts +76 -0
  31. package/{src/gateway/message-context.ts → dist/src/gateway/message-context.js} +499 -594
  32. package/dist/src/gateway/message-dispatcher.d.ts +19 -0
  33. package/{src/gateway/message-dispatcher.ts → dist/src/gateway/message-dispatcher.js} +512 -641
  34. package/dist/src/gateway/reconnect.d.ts +27 -0
  35. package/{src/gateway/reconnect.ts → dist/src/gateway/reconnect.js} +206 -253
  36. package/dist/src/gateway/skills-handler.d.ts +29 -0
  37. package/dist/src/gateway/skills-handler.js +576 -0
  38. package/dist/src/gateway/skills-list-handler.d.ts +27 -0
  39. package/dist/src/gateway/skills-list-handler.js +233 -0
  40. package/dist/src/gateway/tools-list-handler.d.ts +30 -0
  41. package/dist/src/gateway/tools-list-handler.js +101 -0
  42. package/dist/src/gateway/workclaw-gateway.d.ts +14 -0
  43. package/dist/src/gateway/workclaw-gateway.js +223 -0
  44. package/dist/src/media/upload.d.ts +13 -0
  45. package/dist/src/media/upload.js +125 -0
  46. package/dist/src/outbound/index.d.ts +36 -0
  47. package/dist/src/outbound/index.js +123 -0
  48. package/dist/src/outbound/workclaw-sender.d.ts +36 -0
  49. package/{src/outbound/workclaw-sender.ts → dist/src/outbound/workclaw-sender.js} +95 -158
  50. package/dist/src/runtime.d.ts +116 -0
  51. package/dist/src/runtime.js +374 -0
  52. package/dist/src/secret-contract-api.d.ts +4 -0
  53. package/dist/src/secret-contract-api.js +4 -0
  54. package/{src/send.ts → dist/src/send.d.ts} +1 -1
  55. package/dist/src/send.js +1 -0
  56. package/dist/src/setup-api.d.ts +3 -0
  57. package/dist/src/setup-api.js +3 -0
  58. package/dist/src/setup-core.d.ts +3 -0
  59. package/dist/src/setup-core.js +13 -0
  60. package/dist/src/setup-surface.d.ts +7 -0
  61. package/dist/src/setup-surface.js +363 -0
  62. package/dist/src/tools/openclaw-workclaw-cron/api/index.d.ts +93 -0
  63. package/dist/src/tools/openclaw-workclaw-cron/api/index.js +209 -0
  64. package/dist/src/tools/openclaw-workclaw-cron/index.d.ts +10 -0
  65. package/{src/tools/openclaw-workclaw-cron/index.ts → dist/src/tools/openclaw-workclaw-cron/index.js} +33 -39
  66. package/dist/src/tools/openclaw-workclaw-cron/src/add/params.d.ts +16 -0
  67. package/dist/src/tools/openclaw-workclaw-cron/src/add/params.js +152 -0
  68. package/dist/src/tools/openclaw-workclaw-cron/src/add/sync.d.ts +18 -0
  69. package/dist/src/tools/openclaw-workclaw-cron/src/add/sync.js +162 -0
  70. package/dist/src/tools/openclaw-workclaw-cron/src/disable/params.d.ts +14 -0
  71. package/dist/src/tools/openclaw-workclaw-cron/src/disable/params.js +80 -0
  72. package/dist/src/tools/openclaw-workclaw-cron/src/disable/sync.d.ts +14 -0
  73. package/dist/src/tools/openclaw-workclaw-cron/src/disable/sync.js +107 -0
  74. package/dist/src/tools/openclaw-workclaw-cron/src/enable/params.d.ts +14 -0
  75. package/dist/src/tools/openclaw-workclaw-cron/src/enable/params.js +80 -0
  76. package/dist/src/tools/openclaw-workclaw-cron/src/enable/sync.d.ts +14 -0
  77. package/dist/src/tools/openclaw-workclaw-cron/src/enable/sync.js +107 -0
  78. package/dist/src/tools/openclaw-workclaw-cron/src/notify/sync.d.ts +17 -0
  79. package/dist/src/tools/openclaw-workclaw-cron/src/notify/sync.js +127 -0
  80. package/dist/src/tools/openclaw-workclaw-cron/src/remove/params.d.ts +14 -0
  81. package/dist/src/tools/openclaw-workclaw-cron/src/remove/params.js +87 -0
  82. package/dist/src/tools/openclaw-workclaw-cron/src/remove/sync.d.ts +14 -0
  83. package/dist/src/tools/openclaw-workclaw-cron/src/remove/sync.js +107 -0
  84. package/dist/src/tools/openclaw-workclaw-cron/src/update/params.d.ts +17 -0
  85. package/dist/src/tools/openclaw-workclaw-cron/src/update/params.js +164 -0
  86. package/dist/src/tools/openclaw-workclaw-cron/src/update/sync.d.ts +18 -0
  87. package/dist/src/tools/openclaw-workclaw-cron/src/update/sync.js +135 -0
  88. package/dist/src/tools/openclaw-workclaw-cron/types/index.d.ts +52 -0
  89. package/dist/src/tools/openclaw-workclaw-cron/types/index.js +4 -0
  90. package/dist/src/tools/openclaw-workclaw-cron/utils/index.d.ts +40 -0
  91. package/dist/src/tools/openclaw-workclaw-cron/utils/index.js +122 -0
  92. package/dist/src/tools/openclaw-workclaw-system/index.d.ts +10 -0
  93. package/{src/tools/openclaw-workclaw-system/index.ts → dist/src/tools/openclaw-workclaw-system/index.js} +15 -17
  94. package/dist/src/tools/openclaw-workclaw-system/src/get/index.d.ts +8 -0
  95. package/dist/src/tools/openclaw-workclaw-system/src/get/index.js +64 -0
  96. package/dist/src/tools/openclaw-workclaw-system/src/token/index.d.ts +8 -0
  97. package/dist/src/tools/openclaw-workclaw-system/src/token/index.js +78 -0
  98. package/{src/types.ts → dist/src/types.d.ts} +39 -58
  99. package/dist/src/types.js +1 -0
  100. package/dist/src/utils/content.d.ts +15 -0
  101. package/{src/utils/content.ts → dist/src/utils/content.js} +38 -35
  102. package/package.json +62 -49
  103. package/index.ts +0 -302
  104. package/setup-entry.ts +0 -6
  105. package/src/accounts.ts +0 -312
  106. package/src/api/accounts-api.ts +0 -156
  107. package/src/api/prompts-api.ts +0 -116
  108. package/src/api/session-api.ts +0 -238
  109. package/src/api/skills-api.ts +0 -72
  110. package/src/api/workspace.ts +0 -41
  111. package/src/config-schema.ts +0 -110
  112. package/src/connection/workclaw-client.ts +0 -643
  113. package/src/gateway/config-writer.ts +0 -296
  114. package/src/gateway/skills-handler.ts +0 -741
  115. package/src/gateway/skills-list-handler.ts +0 -332
  116. package/src/gateway/tools-list-handler.ts +0 -160
  117. package/src/gateway/workclaw-gateway.ts +0 -367
  118. package/src/media/upload.ts +0 -157
  119. package/src/outbound/index.ts +0 -185
  120. package/src/runtime.ts +0 -497
  121. package/src/tools/openclaw-workclaw-cron/api/index.ts +0 -326
  122. package/src/tools/openclaw-workclaw-cron/src/add/params.ts +0 -177
  123. package/src/tools/openclaw-workclaw-cron/src/add/sync.ts +0 -188
  124. package/src/tools/openclaw-workclaw-cron/src/disable/params.ts +0 -100
  125. package/src/tools/openclaw-workclaw-cron/src/disable/sync.ts +0 -127
  126. package/src/tools/openclaw-workclaw-cron/src/enable/params.ts +0 -100
  127. package/src/tools/openclaw-workclaw-cron/src/enable/sync.ts +0 -127
  128. package/src/tools/openclaw-workclaw-cron/src/notify/sync.ts +0 -148
  129. package/src/tools/openclaw-workclaw-cron/src/remove/params.ts +0 -109
  130. package/src/tools/openclaw-workclaw-cron/src/remove/sync.ts +0 -127
  131. package/src/tools/openclaw-workclaw-cron/src/update/params.ts +0 -195
  132. package/src/tools/openclaw-workclaw-cron/src/update/sync.ts +0 -161
  133. package/src/tools/openclaw-workclaw-cron/types/index.ts +0 -55
  134. package/src/tools/openclaw-workclaw-cron/utils/index.ts +0 -141
  135. package/src/tools/openclaw-workclaw-system/src/get/index.ts +0 -77
  136. package/src/tools/openclaw-workclaw-system/src/token/index.ts +0 -93
  137. package/tests/accounts.test.ts +0 -285
  138. package/tests/message-context.test.ts +0 -313
  139. package/tests/reconnect.test.ts +0 -257
  140. package/tests/workclaw-client.test.ts +0 -112
  141. package/tsconfig.json +0 -14
  142. package/vitest.config.ts +0 -8
@@ -1,225 +1,198 @@
1
- import type {
2
- ChannelGatewayContext,
3
- ChannelPlugin,
4
- OpenClawConfig,
5
- } from "openclaw/plugin-sdk";
6
- import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
7
- import { formatPairingApproveHint } from "openclaw/plugin-sdk/core";
8
- import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
9
- import type { ResolvedWorkclawAccount, WorkclawConfig } from "./types.js";
10
- import {
11
- isWorkclawAccountConfigured,
12
- listWorkclawAccountIds,
13
- resolveWorkclawAccount,
14
- } from "./accounts.js";
15
- import { WorkclawConfigSchema } from "./config-schema.js";
16
- import { sendMessageWorkclaw } from "./outbound/index.js";
17
- import { setWorkclawRuntime, getWorkclawRuntime, getWorkclawWsConnection, getLastInboundAt, getLastOutboundAt } from "./runtime.js";
18
- import { startWorkclawGateway, stopWorkclawGateway } from "./gateway/workclaw-gateway.js";
19
- import { appendFileSync } from "fs";
20
- import { join } from "path";
21
- import { homedir } from "os";
22
-
23
- const meta = {
24
- id: "openclaw-workclaw",
25
- label: "智小途",
26
- selectionLabel: "智小途",
27
- docsPath: "/channels/openclaw-workclaw",
28
- docsLabel: "智小途",
29
- blurb: "智小途 OpenClaw通道",
30
- aliases: [],
31
- order: 90,
32
- };
33
-
34
- /**
35
- * 写入文件日志到磁盘
36
- * @param message 日志消息
37
- */
38
- function writeFileLog(message: string): void {
39
- const timestamp = new Date().toISOString();
40
- const logLine = `[${timestamp}] ${message}\n`;
41
- const logDir = join(homedir(), ".openclaw", "logs");
42
- const logFile = join(logDir, "sendtext-outbound.log");
43
-
44
- try {
45
- appendFileSync(logFile, logLine);
46
- } catch (err) {
47
- console.error(`[writeFileLog] Failed to write log: ${err}`);
48
- }
49
- }
50
-
51
- export const workclawDock = {
52
- id: "openclaw-workclaw",
53
- capabilities: {
54
- chatTypes: ["direct", "group"],
55
- media: true,
56
- blockStreaming: true,
57
- },
58
- };
59
-
60
- export const workclawPlugin: ChannelPlugin<ResolvedWorkclawAccount> = {
61
- id: "openclaw-workclaw",
62
- meta,
63
- capabilities: {
64
- chatTypes: ["direct", "group"],
65
- media: true,
66
- reactions: false,
67
- threads: false,
68
- polls: false,
69
- nativeCommands: false,
70
- blockStreaming: true,
71
- },
72
- reload: { configPrefixes: ["channels.openclaw-workclaw"] },
73
- configSchema: buildChannelConfigSchema(WorkclawConfigSchema),
74
- config: {
75
- listAccountIds: (cfg) => listWorkclawAccountIds(cfg as OpenClawConfig),
76
- resolveAccount: (cfg, accountId) =>
77
- resolveWorkclawAccount({ cfg: cfg as OpenClawConfig, accountId }),
78
- isConfigured: (account) => isWorkclawAccountConfigured(account.config),
79
- describeAccount: (account) => ({
80
- accountId: account.accountId,
81
- name: account.name,
82
- enabled: account.enabled,
83
- configured: isWorkclawAccountConfigured(account.config),
84
- }),
85
- },
86
- outbound: {
87
- deliveryMode: "direct",
88
- chunker: (text: string, _limit: number) => [text],
89
- textChunkLimit: 4096,
90
- sendText: async ({ to, text, accountId, cfg, replyToId }) => {
91
- writeFileLog(`outbound sendText called: replyToId=${replyToId}, accountId=${accountId}, to=${to}, text=${text.substring(0, 100)}`);
92
- const result = await sendMessageWorkclaw({
93
- accountId: accountId ?? undefined,
94
- cfg: cfg as OpenClawConfig,
95
- to,
96
- text,
97
- });
98
- return {
99
- channel: "openclaw-workclaw",
100
- ok: true,
101
- messageId: result.messageId,
102
- };
103
- },
104
- sendMedia: async ({ to, text, mediaUrl, accountId, cfg }) => {
105
- if (!mediaUrl && !text.trim()) {
106
- throw new Error("mediaUrl is required for outbound media");
107
- }
108
- const log = (getWorkclawRuntime() as any).log ?? console.log;
109
- if (typeof log === "function") {
110
- log(`outbound sendMedia called for ${to} with text ${text} and mediaUrl ${mediaUrl}`);
111
- }
112
- else if (log?.info) {
113
- log.info(`outbound sendMedia called for ${to} with text ${text} and mediaUrl ${mediaUrl}`);
114
- }
115
- const messageText = text.trim() ? text : mediaUrl ?? "";
116
- const result = await sendMessageWorkclaw({
117
- accountId: accountId ?? undefined,
118
- cfg: cfg as OpenClawConfig,
119
- to,
120
- text: messageText,
121
- mediaUrl: mediaUrl ?? undefined,
122
- });
123
- return {
124
- channel: "openclaw-workclaw",
125
- ok: true,
126
- messageId: result.messageId,
127
- };
128
- },
129
- },
130
- security: {
131
- resolveDmPolicy: ({ cfg, accountId, account }) => {
132
- const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
133
- const channelConfig = cfg.channels?.["openclaw-workclaw"] as WorkclawConfig | undefined;
134
- const useAccountPath = Boolean(channelConfig?.accounts?.[resolvedAccountId]);
135
- const basePath = useAccountPath
136
- ? `channels.openclaw-workclaw.accounts.${resolvedAccountId}.`
137
- : "channels.openclaw-workclaw.";
138
- return {
139
- policy: account.config.dmPolicy ?? "pairing",
140
- allowFrom: account.config.allowFrom ?? [],
141
- policyPath: `${basePath}dmPolicy`,
142
- allowFromPath: `${basePath}allowFrom`,
143
- approveHint: formatPairingApproveHint("openclaw-workclaw"),
144
- normalizeEntry: (raw) => raw.replace(/^openclaw-workclaw:/i, ""),
145
- };
146
- },
147
- },
148
- status: {
149
- buildAccountSnapshot: async ({ account, cfg }) => {
150
- // per-appKey 模式下 WebSocket 用 appKey 存储,per-account 用 accountId
151
- const wsKey = account.config.wsConnectionStrategy === "per-appKey"
152
- ? account.config.appKey
153
- : account.accountId;
154
- const ws = wsKey ? getWorkclawWsConnection(wsKey) : undefined;
155
- const isConnected = ws && ws.readyState === 1; // WebSocket.OPEN = 1
156
- const lastInboundAt = account.accountId ? getLastInboundAt(account.accountId) : undefined;
157
- const lastOutboundAt = account.accountId ? getLastOutboundAt(account.accountId) : undefined;
158
-
159
- return {
160
- accountId: account.accountId,
161
- name: account.name,
162
- enabled: account.enabled,
163
- configured: account.configured,
164
- connected: isConnected,
165
- running: account.enabled && account.configured,
166
- mode: account.config.wsConnectionStrategy || "per-account",
167
- lastInboundAt: lastInboundAt ?? null,
168
- lastOutboundAt: lastOutboundAt ?? null,
169
- };
170
- },
171
- },
172
- gateway: {
173
- startAccount: async (ctx: ChannelGatewayContext<ResolvedWorkclawAccount>) => {
174
- const { setStatus, getStatus, account, accountId, cfg, log } = ctx;
175
- log?.info?.(`startAccount called for ${accountId}`);
176
-
177
- await startWorkclawGateway({
178
- accountId,
179
- account,
180
- cfg,
181
- log,
182
- });
183
-
184
- setStatus({
185
- ...getStatus(),
186
- running: true,
187
- lastStartAt: Date.now(),
188
- lastError: null,
189
- });
190
- log?.info?.(`startAccount completed for ${accountId}, keeping pending until abort`);
191
-
192
- // 保持 pending 状态直到 abortSignal 触发(防止 OpenClaw 3.8 认为启动立即退出)
193
- await new Promise<void>((resolve) => {
194
- if (ctx.abortSignal.aborted) {
195
- resolve();
196
- return;
197
- }
198
- ctx.abortSignal.addEventListener("abort", () => resolve(), { once: true });
199
- });
200
-
201
- // abort 触发后,清理资源
202
- log?.info?.(`startAccount abort signal received for ${accountId}`);
203
- const wsStrategy = (account.config as any)?.wsConnectionStrategy || "per-account";
204
- stopWorkclawGateway(accountId, wsStrategy);
205
- setStatus({
206
- ...getStatus(),
207
- running: false,
208
- lastStopAt: Date.now(),
209
- });
210
- },
211
- stopAccount: async (ctx: ChannelGatewayContext<ResolvedWorkclawAccount>) => {
212
- const { setStatus, getStatus, accountId, account } = ctx;
213
-
214
- // Stop WorkClaw gateway
215
- const wsStrategy = (account.config as any)?.wsConnectionStrategy || "per-account";
216
- stopWorkclawGateway(accountId, wsStrategy);
217
-
218
- setStatus({
219
- ...getStatus(),
220
- running: false,
221
- lastStopAt: Date.now(),
222
- });
223
- },
224
- },
225
- };
1
+ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
2
+ import { formatPairingApproveHint } from "openclaw/plugin-sdk/core";
3
+ import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
4
+ import { isWorkclawAccountConfigured, listWorkclawAccountIds, resolveWorkclawAccount, } from "./accounts.js";
5
+ import { WorkclawConfigSchema } from "./config-schema.js";
6
+ import { sendMessageWorkclaw } from "./outbound/index.js";
7
+ import { getWorkclawRuntime, getWorkclawWsConnection, getLastInboundAt, getLastOutboundAt } from "./runtime.js";
8
+ import { startWorkclawGateway, stopWorkclawGateway } from "./gateway/workclaw-gateway.js";
9
+ import { appendFileSync } from "fs";
10
+ import { join } from "path";
11
+ import { homedir } from "os";
12
+ const meta = {
13
+ id: "openclaw-workclaw",
14
+ label: "智小途",
15
+ selectionLabel: "智小途",
16
+ docsPath: "/channels/openclaw-workclaw",
17
+ docsLabel: "智小途",
18
+ blurb: "智小途 OpenClaw通道",
19
+ aliases: [],
20
+ order: 90,
21
+ };
22
+ /**
23
+ * 写入文件日志到磁盘
24
+ * @param message 日志消息
25
+ */
26
+ function writeFileLog(message) {
27
+ const timestamp = new Date().toISOString();
28
+ const logLine = `[${timestamp}] ${message}\n`;
29
+ const logDir = join(homedir(), ".openclaw", "logs");
30
+ const logFile = join(logDir, "sendtext-outbound.log");
31
+ try {
32
+ appendFileSync(logFile, logLine);
33
+ }
34
+ catch (err) {
35
+ console.error(`[writeFileLog] Failed to write log: ${err}`);
36
+ }
37
+ }
38
+ export const workclawDock = {
39
+ id: "openclaw-workclaw",
40
+ capabilities: {
41
+ chatTypes: ["direct", "group"],
42
+ media: true,
43
+ blockStreaming: true,
44
+ },
45
+ };
46
+ export const workclawPlugin = {
47
+ id: "openclaw-workclaw",
48
+ meta,
49
+ capabilities: {
50
+ chatTypes: ["direct", "group"],
51
+ media: true,
52
+ reactions: false,
53
+ threads: false,
54
+ polls: false,
55
+ nativeCommands: false,
56
+ blockStreaming: true,
57
+ },
58
+ reload: { configPrefixes: ["channels.openclaw-workclaw"] },
59
+ configSchema: buildChannelConfigSchema(WorkclawConfigSchema),
60
+ config: {
61
+ listAccountIds: (cfg) => listWorkclawAccountIds(cfg),
62
+ resolveAccount: (cfg, accountId) => resolveWorkclawAccount({ cfg: cfg, accountId }),
63
+ isConfigured: (account) => isWorkclawAccountConfigured(account.config),
64
+ describeAccount: (account) => ({
65
+ accountId: account.accountId,
66
+ name: account.name,
67
+ enabled: account.enabled,
68
+ configured: isWorkclawAccountConfigured(account.config),
69
+ }),
70
+ },
71
+ outbound: {
72
+ deliveryMode: "direct",
73
+ chunker: (text, _limit) => [text],
74
+ textChunkLimit: 4096,
75
+ sendText: async ({ to, text, accountId, cfg, replyToId }) => {
76
+ writeFileLog(`outbound sendText called: replyToId=${replyToId}, accountId=${accountId}, to=${to}, text=${text.substring(0, 100)}`);
77
+ const result = await sendMessageWorkclaw({
78
+ accountId: accountId ?? undefined,
79
+ cfg: cfg,
80
+ to,
81
+ text,
82
+ });
83
+ return {
84
+ channel: "openclaw-workclaw",
85
+ ok: true,
86
+ messageId: result.messageId,
87
+ };
88
+ },
89
+ sendMedia: async ({ to, text, mediaUrl, accountId, cfg }) => {
90
+ if (!mediaUrl && !text.trim()) {
91
+ throw new Error("mediaUrl is required for outbound media");
92
+ }
93
+ const log = getWorkclawRuntime().log ?? console.log;
94
+ if (typeof log === "function") {
95
+ log(`outbound sendMedia called for ${to} with text ${text} and mediaUrl ${mediaUrl}`);
96
+ }
97
+ else if (log?.info) {
98
+ log.info(`outbound sendMedia called for ${to} with text ${text} and mediaUrl ${mediaUrl}`);
99
+ }
100
+ const messageText = text.trim() ? text : mediaUrl ?? "";
101
+ const result = await sendMessageWorkclaw({
102
+ accountId: accountId ?? undefined,
103
+ cfg: cfg,
104
+ to,
105
+ text: messageText,
106
+ mediaUrl: mediaUrl ?? undefined,
107
+ });
108
+ return {
109
+ channel: "openclaw-workclaw",
110
+ ok: true,
111
+ messageId: result.messageId,
112
+ };
113
+ },
114
+ },
115
+ security: {
116
+ resolveDmPolicy: ({ cfg, accountId, account }) => {
117
+ const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
118
+ const channelConfig = cfg.channels?.["openclaw-workclaw"];
119
+ const useAccountPath = Boolean(channelConfig?.accounts?.[resolvedAccountId]);
120
+ const basePath = useAccountPath
121
+ ? `channels.openclaw-workclaw.accounts.${resolvedAccountId}.`
122
+ : "channels.openclaw-workclaw.";
123
+ return {
124
+ policy: account.config.dmPolicy ?? "open",
125
+ allowFrom: account.config.allowFrom ?? ["*"],
126
+ policyPath: `${basePath}dmPolicy`,
127
+ allowFromPath: `${basePath}allowFrom`,
128
+ approveHint: formatPairingApproveHint("openclaw-workclaw"),
129
+ normalizeEntry: (raw) => raw.replace(/^openclaw-workclaw:/i, ""),
130
+ };
131
+ },
132
+ },
133
+ status: {
134
+ buildAccountSnapshot: async ({ account, cfg }) => {
135
+ // WebSocket appKey 存储
136
+ const wsKey = account.config.appKey;
137
+ const ws = wsKey ? getWorkclawWsConnection(wsKey) : undefined;
138
+ const isConnected = ws && ws.readyState === 1; // WebSocket.OPEN = 1
139
+ const lastInboundAt = account.accountId ? getLastInboundAt(account.accountId) : undefined;
140
+ const lastOutboundAt = account.accountId ? getLastOutboundAt(account.accountId) : undefined;
141
+ return {
142
+ accountId: account.accountId,
143
+ name: account.name,
144
+ enabled: account.enabled,
145
+ configured: account.configured,
146
+ connected: isConnected,
147
+ running: account.enabled && account.configured,
148
+ lastInboundAt: lastInboundAt ?? null,
149
+ lastOutboundAt: lastOutboundAt ?? null,
150
+ };
151
+ },
152
+ },
153
+ gateway: {
154
+ startAccount: async (ctx) => {
155
+ const { setStatus, getStatus, account, accountId, cfg, log } = ctx;
156
+ log?.info?.(`startAccount called for ${accountId}`);
157
+ await startWorkclawGateway({
158
+ accountId,
159
+ account,
160
+ cfg,
161
+ log,
162
+ });
163
+ setStatus({
164
+ ...getStatus(),
165
+ running: true,
166
+ lastStartAt: Date.now(),
167
+ lastError: null,
168
+ });
169
+ log?.info?.(`startAccount completed for ${accountId}, keeping pending until abort`);
170
+ // 保持 pending 状态直到 abortSignal 触发(防止 OpenClaw 3.8 认为启动立即退出)
171
+ await new Promise((resolve) => {
172
+ if (ctx.abortSignal.aborted) {
173
+ resolve();
174
+ return;
175
+ }
176
+ ctx.abortSignal.addEventListener("abort", () => resolve(), { once: true });
177
+ });
178
+ // abort 触发后,清理资源
179
+ log?.info?.(`startAccount abort signal received for ${accountId}`);
180
+ stopWorkclawGateway(accountId);
181
+ setStatus({
182
+ ...getStatus(),
183
+ running: false,
184
+ lastStopAt: Date.now(),
185
+ });
186
+ },
187
+ stopAccount: async (ctx) => {
188
+ const { setStatus, getStatus, accountId, account } = ctx;
189
+ // Stop WorkClaw gateway
190
+ stopWorkclawGateway(accountId);
191
+ setStatus({
192
+ ...getStatus(),
193
+ running: false,
194
+ lastStopAt: Date.now(),
195
+ });
196
+ },
197
+ },
198
+ };
@@ -0,0 +1,93 @@
1
+ import { z } from 'zod';
2
+ export { z };
3
+ export declare const workclawAccountConfigSchema: z.ZodObject<{
4
+ enabled: z.ZodOptional<z.ZodBoolean>;
5
+ name: z.ZodOptional<z.ZodString>;
6
+ agentId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
7
+ userId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
8
+ openConversationId: z.ZodOptional<z.ZodString>;
9
+ dmPolicy: z.ZodOptional<z.ZodEnum<{
10
+ open: "open";
11
+ }>>;
12
+ allowFrom: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
13
+ mediaMaxMb: z.ZodOptional<z.ZodNumber>;
14
+ allowInsecureTls: z.ZodOptional<z.ZodBoolean>;
15
+ }, z.core.$strict>;
16
+ export declare const WorkclawConfigSchema: z.ZodObject<{
17
+ enabled: z.ZodOptional<z.ZodBoolean>;
18
+ connectionMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
19
+ websocket: "websocket";
20
+ }>>>;
21
+ baseUrl: z.ZodOptional<z.ZodString>;
22
+ websocketUrl: z.ZodOptional<z.ZodString>;
23
+ appKey: z.ZodOptional<z.ZodString>;
24
+ appSecret: z.ZodOptional<z.ZodString>;
25
+ agentId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
26
+ localIp: z.ZodOptional<z.ZodString>;
27
+ userId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
28
+ requestTimeout: z.ZodOptional<z.ZodNumber>;
29
+ allowInsecureTls: z.ZodOptional<z.ZodBoolean>;
30
+ allowRawJsonPayload: z.ZodOptional<z.ZodBoolean>;
31
+ uploadUrl: z.ZodOptional<z.ZodString>;
32
+ uploadFieldName: z.ZodOptional<z.ZodString>;
33
+ uploadHeaders: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
34
+ uploadFormFields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
35
+ uploadResponseUrlPath: z.ZodOptional<z.ZodString>;
36
+ dmPolicy: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
37
+ open: "open";
38
+ }>>>;
39
+ allowFrom: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
40
+ mediaMaxMb: z.ZodOptional<z.ZodNumber>;
41
+ accounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodObject<{
42
+ enabled: z.ZodOptional<z.ZodBoolean>;
43
+ name: z.ZodOptional<z.ZodString>;
44
+ agentId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
45
+ userId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
46
+ openConversationId: z.ZodOptional<z.ZodString>;
47
+ dmPolicy: z.ZodOptional<z.ZodEnum<{
48
+ open: "open";
49
+ }>>;
50
+ allowFrom: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
51
+ mediaMaxMb: z.ZodOptional<z.ZodNumber>;
52
+ allowInsecureTls: z.ZodOptional<z.ZodBoolean>;
53
+ }, z.core.$strict>>>>;
54
+ }, z.core.$strict>;
55
+ export declare const workclawConfigSchema: z.ZodObject<{
56
+ enabled: z.ZodOptional<z.ZodBoolean>;
57
+ connectionMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
58
+ websocket: "websocket";
59
+ }>>>;
60
+ baseUrl: z.ZodOptional<z.ZodString>;
61
+ websocketUrl: z.ZodOptional<z.ZodString>;
62
+ appKey: z.ZodOptional<z.ZodString>;
63
+ appSecret: z.ZodOptional<z.ZodString>;
64
+ agentId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
65
+ localIp: z.ZodOptional<z.ZodString>;
66
+ userId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
67
+ requestTimeout: z.ZodOptional<z.ZodNumber>;
68
+ allowInsecureTls: z.ZodOptional<z.ZodBoolean>;
69
+ allowRawJsonPayload: z.ZodOptional<z.ZodBoolean>;
70
+ uploadUrl: z.ZodOptional<z.ZodString>;
71
+ uploadFieldName: z.ZodOptional<z.ZodString>;
72
+ uploadHeaders: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
73
+ uploadFormFields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
74
+ uploadResponseUrlPath: z.ZodOptional<z.ZodString>;
75
+ dmPolicy: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
76
+ open: "open";
77
+ }>>>;
78
+ allowFrom: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
79
+ mediaMaxMb: z.ZodOptional<z.ZodNumber>;
80
+ accounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodObject<{
81
+ enabled: z.ZodOptional<z.ZodBoolean>;
82
+ name: z.ZodOptional<z.ZodString>;
83
+ agentId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
84
+ userId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
85
+ openConversationId: z.ZodOptional<z.ZodString>;
86
+ dmPolicy: z.ZodOptional<z.ZodEnum<{
87
+ open: "open";
88
+ }>>;
89
+ allowFrom: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
90
+ mediaMaxMb: z.ZodOptional<z.ZodNumber>;
91
+ allowInsecureTls: z.ZodOptional<z.ZodBoolean>;
92
+ }, z.core.$strict>>>>;
93
+ }, z.core.$strict>;
@@ -0,0 +1,56 @@
1
+ import { z } from 'zod';
2
+ export { z };
3
+ const DmPolicySchema = z.enum(["open"]);
4
+ const WorkclawConnectionModeSchema = z.enum(["websocket"]);
5
+ export const workclawAccountConfigSchema = z
6
+ .object({
7
+ enabled: z.boolean().optional(),
8
+ name: z.string().optional(),
9
+ agentId: z.union([z.string(), z.number()]).optional(),
10
+ userId: z.union([z.string(), z.number()]).optional(),
11
+ openConversationId: z.string().optional(),
12
+ dmPolicy: DmPolicySchema.optional(),
13
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
14
+ mediaMaxMb: z.number().positive().optional(),
15
+ allowInsecureTls: z.boolean().optional(),
16
+ })
17
+ .strict();
18
+ export const WorkclawConfigSchema = z
19
+ .object({
20
+ enabled: z.boolean().optional(),
21
+ connectionMode: WorkclawConnectionModeSchema.optional().default("websocket"),
22
+ baseUrl: z.string().url().optional(),
23
+ websocketUrl: z.string().url().optional(),
24
+ appKey: z.string().optional(),
25
+ appSecret: z.string().optional(),
26
+ agentId: z.union([z.string(), z.number()]).optional(),
27
+ localIp: z.string().optional(),
28
+ userId: z.union([z.string(), z.number()]).optional(),
29
+ requestTimeout: z.number().int().positive().optional(),
30
+ allowInsecureTls: z.boolean().optional(),
31
+ allowRawJsonPayload: z.boolean().optional(),
32
+ uploadUrl: z.string().url().optional(),
33
+ uploadFieldName: z.string().optional(),
34
+ uploadHeaders: z.record(z.string(), z.string()).optional(),
35
+ uploadFormFields: z
36
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
37
+ .optional(),
38
+ uploadResponseUrlPath: z.string().optional(),
39
+ dmPolicy: DmPolicySchema.optional().default('open'),
40
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
41
+ mediaMaxMb: z.number().positive().optional(),
42
+ accounts: z.record(z.string(), workclawAccountConfigSchema.optional()).optional(),
43
+ })
44
+ .strict()
45
+ .superRefine((value, ctx) => {
46
+ const allowFrom = value.allowFrom ?? [];
47
+ const hasWildcard = allowFrom.some(entry => String(entry).trim() === '*');
48
+ if (!hasWildcard) {
49
+ ctx.addIssue({
50
+ code: z.ZodIssueCode.custom,
51
+ path: ['allowFrom'],
52
+ message: 'channels.openclaw-workclaw.allowFrom must include "*"',
53
+ });
54
+ }
55
+ });
56
+ export const workclawConfigSchema = WorkclawConfigSchema;