@workclaw/openclaw-workclaw 1.0.18 → 1.0.20

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 (145) hide show
  1. package/README.md +30 -11
  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 +264 -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 +30 -0
  20. package/dist/src/channel.d.ts +11 -0
  21. package/{src/channel.ts → dist/src/channel.js} +199 -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} +372 -462
  28. package/dist/src/gateway/config-writer.d.ts +71 -0
  29. package/dist/src/gateway/config-writer.js +302 -0
  30. package/dist/src/gateway/cron-tasks-handler.d.ts +19 -0
  31. package/dist/src/gateway/cron-tasks-handler.js +188 -0
  32. package/dist/src/gateway/message-context.d.ts +80 -0
  33. package/{src/gateway/message-context.ts → dist/src/gateway/message-context.js} +509 -594
  34. package/dist/src/gateway/message-dispatcher.d.ts +18 -0
  35. package/dist/src/gateway/message-dispatcher.js +572 -0
  36. package/dist/src/gateway/reconnect.d.ts +27 -0
  37. package/{src/gateway/reconnect.ts → dist/src/gateway/reconnect.js} +210 -253
  38. package/dist/src/gateway/skills-handler.d.ts +29 -0
  39. package/dist/src/gateway/skills-handler.js +615 -0
  40. package/dist/src/gateway/skills-list-handler.d.ts +27 -0
  41. package/dist/src/gateway/skills-list-handler.js +233 -0
  42. package/dist/src/gateway/tools-list-handler.d.ts +30 -0
  43. package/dist/src/gateway/tools-list-handler.js +101 -0
  44. package/dist/src/gateway/workclaw-gateway.d.ts +14 -0
  45. package/dist/src/gateway/workclaw-gateway.js +237 -0
  46. package/dist/src/media/upload.d.ts +13 -0
  47. package/dist/src/media/upload.js +125 -0
  48. package/dist/src/outbound/index.d.ts +36 -0
  49. package/dist/src/outbound/index.js +123 -0
  50. package/dist/src/outbound/workclaw-sender.d.ts +36 -0
  51. package/{src/outbound/workclaw-sender.ts → dist/src/outbound/workclaw-sender.js} +95 -158
  52. package/dist/src/runtime.d.ts +116 -0
  53. package/dist/src/runtime.js +374 -0
  54. package/dist/src/secret-contract-api.d.ts +4 -0
  55. package/dist/src/secret-contract-api.js +4 -0
  56. package/{src/send.ts → dist/src/send.d.ts} +1 -1
  57. package/dist/src/send.js +1 -0
  58. package/dist/src/setup-api.d.ts +3 -0
  59. package/dist/src/setup-api.js +3 -0
  60. package/dist/src/setup-core.d.ts +3 -0
  61. package/dist/src/setup-core.js +13 -0
  62. package/dist/src/setup-surface.d.ts +7 -0
  63. package/dist/src/setup-surface.js +363 -0
  64. package/dist/src/tools/openclaw-workclaw-cron/api/index.d.ts +93 -0
  65. package/dist/src/tools/openclaw-workclaw-cron/api/index.js +209 -0
  66. package/dist/src/tools/openclaw-workclaw-cron/index.d.ts +10 -0
  67. package/{src/tools/openclaw-workclaw-cron/index.ts → dist/src/tools/openclaw-workclaw-cron/index.js} +33 -39
  68. package/dist/src/tools/openclaw-workclaw-cron/src/add/params.d.ts +16 -0
  69. package/dist/src/tools/openclaw-workclaw-cron/src/add/params.js +152 -0
  70. package/dist/src/tools/openclaw-workclaw-cron/src/add/sync.d.ts +18 -0
  71. package/dist/src/tools/openclaw-workclaw-cron/src/add/sync.js +162 -0
  72. package/dist/src/tools/openclaw-workclaw-cron/src/disable/params.d.ts +14 -0
  73. package/dist/src/tools/openclaw-workclaw-cron/src/disable/params.js +80 -0
  74. package/dist/src/tools/openclaw-workclaw-cron/src/disable/sync.d.ts +14 -0
  75. package/dist/src/tools/openclaw-workclaw-cron/src/disable/sync.js +107 -0
  76. package/dist/src/tools/openclaw-workclaw-cron/src/enable/params.d.ts +14 -0
  77. package/dist/src/tools/openclaw-workclaw-cron/src/enable/params.js +80 -0
  78. package/dist/src/tools/openclaw-workclaw-cron/src/enable/sync.d.ts +14 -0
  79. package/dist/src/tools/openclaw-workclaw-cron/src/enable/sync.js +107 -0
  80. package/dist/src/tools/openclaw-workclaw-cron/src/notify/sync.d.ts +17 -0
  81. package/dist/src/tools/openclaw-workclaw-cron/src/notify/sync.js +127 -0
  82. package/dist/src/tools/openclaw-workclaw-cron/src/remove/params.d.ts +14 -0
  83. package/dist/src/tools/openclaw-workclaw-cron/src/remove/params.js +87 -0
  84. package/dist/src/tools/openclaw-workclaw-cron/src/remove/sync.d.ts +14 -0
  85. package/dist/src/tools/openclaw-workclaw-cron/src/remove/sync.js +107 -0
  86. package/dist/src/tools/openclaw-workclaw-cron/src/update/params.d.ts +17 -0
  87. package/dist/src/tools/openclaw-workclaw-cron/src/update/params.js +164 -0
  88. package/dist/src/tools/openclaw-workclaw-cron/src/update/sync.d.ts +18 -0
  89. package/dist/src/tools/openclaw-workclaw-cron/src/update/sync.js +135 -0
  90. package/dist/src/tools/openclaw-workclaw-cron/types/index.d.ts +52 -0
  91. package/dist/src/tools/openclaw-workclaw-cron/types/index.js +4 -0
  92. package/dist/src/tools/openclaw-workclaw-cron/utils/index.d.ts +40 -0
  93. package/dist/src/tools/openclaw-workclaw-cron/utils/index.js +122 -0
  94. package/dist/src/tools/openclaw-workclaw-system/index.d.ts +10 -0
  95. package/{src/tools/openclaw-workclaw-system/index.ts → dist/src/tools/openclaw-workclaw-system/index.js} +15 -17
  96. package/dist/src/tools/openclaw-workclaw-system/src/get/index.d.ts +8 -0
  97. package/dist/src/tools/openclaw-workclaw-system/src/get/index.js +64 -0
  98. package/dist/src/tools/openclaw-workclaw-system/src/token/index.d.ts +8 -0
  99. package/dist/src/tools/openclaw-workclaw-system/src/token/index.js +78 -0
  100. package/{src/types.ts → dist/src/types.d.ts} +39 -58
  101. package/dist/src/types.js +1 -0
  102. package/dist/src/utils/content.d.ts +15 -0
  103. package/{src/utils/content.ts → dist/src/utils/content.js} +38 -35
  104. package/package.json +13 -9
  105. package/index.ts +0 -302
  106. package/setup-entry.ts +0 -6
  107. package/src/accounts.ts +0 -312
  108. package/src/api/accounts-api.ts +0 -156
  109. package/src/api/prompts-api.ts +0 -116
  110. package/src/api/session-api.ts +0 -238
  111. package/src/api/skills-api.ts +0 -72
  112. package/src/api/workspace.ts +0 -41
  113. package/src/config-schema.ts +0 -110
  114. package/src/connection/workclaw-client.ts +0 -643
  115. package/src/gateway/config-writer.ts +0 -296
  116. package/src/gateway/message-dispatcher.ts +0 -641
  117. package/src/gateway/skills-handler.ts +0 -741
  118. package/src/gateway/skills-list-handler.ts +0 -332
  119. package/src/gateway/tools-list-handler.ts +0 -160
  120. package/src/gateway/workclaw-gateway.ts +0 -367
  121. package/src/media/upload.ts +0 -157
  122. package/src/outbound/index.ts +0 -185
  123. package/src/runtime.ts +0 -497
  124. package/src/tools/openclaw-workclaw-cron/api/index.ts +0 -326
  125. package/src/tools/openclaw-workclaw-cron/src/add/params.ts +0 -177
  126. package/src/tools/openclaw-workclaw-cron/src/add/sync.ts +0 -188
  127. package/src/tools/openclaw-workclaw-cron/src/disable/params.ts +0 -100
  128. package/src/tools/openclaw-workclaw-cron/src/disable/sync.ts +0 -127
  129. package/src/tools/openclaw-workclaw-cron/src/enable/params.ts +0 -100
  130. package/src/tools/openclaw-workclaw-cron/src/enable/sync.ts +0 -127
  131. package/src/tools/openclaw-workclaw-cron/src/notify/sync.ts +0 -148
  132. package/src/tools/openclaw-workclaw-cron/src/remove/params.ts +0 -109
  133. package/src/tools/openclaw-workclaw-cron/src/remove/sync.ts +0 -127
  134. package/src/tools/openclaw-workclaw-cron/src/update/params.ts +0 -195
  135. package/src/tools/openclaw-workclaw-cron/src/update/sync.ts +0 -161
  136. package/src/tools/openclaw-workclaw-cron/types/index.ts +0 -55
  137. package/src/tools/openclaw-workclaw-cron/utils/index.ts +0 -141
  138. package/src/tools/openclaw-workclaw-system/src/get/index.ts +0 -77
  139. package/src/tools/openclaw-workclaw-system/src/token/index.ts +0 -93
  140. package/tests/accounts.test.ts +0 -285
  141. package/tests/message-context.test.ts +0 -313
  142. package/tests/reconnect.test.ts +0 -257
  143. package/tests/workclaw-client.test.ts +0 -112
  144. package/tsconfig.json +0 -14
  145. package/vitest.config.ts +0 -8
@@ -1,225 +1,199 @@
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
+ ...(replyToId ? { replyToMessageId: replyToId } : {}),
83
+ });
84
+ return {
85
+ channel: "openclaw-workclaw",
86
+ ok: true,
87
+ messageId: result.messageId,
88
+ };
89
+ },
90
+ sendMedia: async ({ to, text, mediaUrl, accountId, cfg }) => {
91
+ if (!mediaUrl && !text.trim()) {
92
+ throw new Error("mediaUrl is required for outbound media");
93
+ }
94
+ const log = getWorkclawRuntime().log ?? console.log;
95
+ if (typeof log === "function") {
96
+ log(`outbound sendMedia called for ${to} with text ${text} and mediaUrl ${mediaUrl}`);
97
+ }
98
+ else if (log?.info) {
99
+ log.info(`outbound sendMedia called for ${to} with text ${text} and mediaUrl ${mediaUrl}`);
100
+ }
101
+ const messageText = text.trim() ? text : mediaUrl ?? "";
102
+ const result = await sendMessageWorkclaw({
103
+ accountId: accountId ?? undefined,
104
+ cfg: cfg,
105
+ to,
106
+ text: messageText,
107
+ mediaUrl: mediaUrl ?? undefined,
108
+ });
109
+ return {
110
+ channel: "openclaw-workclaw",
111
+ ok: true,
112
+ messageId: result.messageId,
113
+ };
114
+ },
115
+ },
116
+ security: {
117
+ resolveDmPolicy: ({ cfg, accountId, account }) => {
118
+ const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
119
+ const channelConfig = cfg.channels?.["openclaw-workclaw"];
120
+ const useAccountPath = Boolean(channelConfig?.accounts?.[resolvedAccountId]);
121
+ const basePath = useAccountPath
122
+ ? `channels.openclaw-workclaw.accounts.${resolvedAccountId}.`
123
+ : "channels.openclaw-workclaw.";
124
+ return {
125
+ policy: account.config.dmPolicy ?? "open",
126
+ allowFrom: account.config.allowFrom ?? ["*"],
127
+ policyPath: `${basePath}dmPolicy`,
128
+ allowFromPath: `${basePath}allowFrom`,
129
+ approveHint: formatPairingApproveHint("openclaw-workclaw"),
130
+ normalizeEntry: (raw) => raw.replace(/^openclaw-workclaw:/i, ""),
131
+ };
132
+ },
133
+ },
134
+ status: {
135
+ buildAccountSnapshot: async ({ account, cfg }) => {
136
+ // WebSocket 用 appKey 存储
137
+ const wsKey = account.config.appKey;
138
+ const ws = wsKey ? getWorkclawWsConnection(wsKey) : undefined;
139
+ const isConnected = ws && ws.readyState === 1; // WebSocket.OPEN = 1
140
+ const lastInboundAt = account.accountId ? getLastInboundAt(account.accountId) : undefined;
141
+ const lastOutboundAt = account.accountId ? getLastOutboundAt(account.accountId) : undefined;
142
+ return {
143
+ accountId: account.accountId,
144
+ name: account.name,
145
+ enabled: account.enabled,
146
+ configured: account.configured,
147
+ connected: isConnected,
148
+ running: account.enabled && account.configured,
149
+ lastInboundAt: lastInboundAt ?? null,
150
+ lastOutboundAt: lastOutboundAt ?? null,
151
+ };
152
+ },
153
+ },
154
+ gateway: {
155
+ startAccount: async (ctx) => {
156
+ const { setStatus, getStatus, account, accountId, cfg, log } = ctx;
157
+ log?.info?.(`startAccount called for ${accountId}`);
158
+ await startWorkclawGateway({
159
+ accountId,
160
+ account,
161
+ cfg,
162
+ log,
163
+ });
164
+ setStatus({
165
+ ...getStatus(),
166
+ running: true,
167
+ lastStartAt: Date.now(),
168
+ lastError: null,
169
+ });
170
+ log?.info?.(`startAccount completed for ${accountId}, keeping pending until abort`);
171
+ // 保持 pending 状态直到 abortSignal 触发(防止 OpenClaw 3.8 认为启动立即退出)
172
+ await new Promise((resolve) => {
173
+ if (ctx.abortSignal.aborted) {
174
+ resolve();
175
+ return;
176
+ }
177
+ ctx.abortSignal.addEventListener("abort", () => resolve(), { once: true });
178
+ });
179
+ // abort 触发后,清理资源
180
+ log?.info?.(`startAccount abort signal received for ${accountId}`);
181
+ stopWorkclawGateway(accountId);
182
+ setStatus({
183
+ ...getStatus(),
184
+ running: false,
185
+ lastStopAt: Date.now(),
186
+ });
187
+ },
188
+ stopAccount: async (ctx) => {
189
+ const { setStatus, getStatus, accountId, account } = ctx;
190
+ // Stop WorkClaw gateway
191
+ stopWorkclawGateway(accountId);
192
+ setStatus({
193
+ ...getStatus(),
194
+ running: false,
195
+ lastStopAt: Date.now(),
196
+ });
197
+ },
198
+ },
199
+ };
@@ -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;