@workclaw/openclaw-workclaw 1.0.20 → 1.0.23

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 (54) hide show
  1. package/README.md +0 -1
  2. package/api.ts +3 -0
  3. package/index.ts +325 -0
  4. package/package.json +3 -11
  5. package/setup-entry.ts +13 -0
  6. package/src/accounts.ts +360 -0
  7. package/src/api/accounts-api.ts +156 -0
  8. package/src/api/prompts-api.ts +122 -0
  9. package/src/api/session-api.ts +246 -0
  10. package/src/api/skills-api.ts +74 -0
  11. package/src/api/workspace.ts +45 -0
  12. package/src/channel.ts +226 -0
  13. package/src/config-schema.ts +60 -0
  14. package/src/connection/workclaw-client.ts +618 -0
  15. package/src/gateway/agent-handlers.ts +551 -0
  16. package/src/gateway/config-writer.ts +378 -0
  17. package/src/gateway/cron-tasks-handler.ts +230 -0
  18. package/src/gateway/message-context.ts +645 -0
  19. package/src/gateway/message-dispatcher.ts +688 -0
  20. package/src/gateway/reconnect.ts +260 -0
  21. package/src/gateway/skills-handler.ts +805 -0
  22. package/src/gateway/skills-list-handler.ts +332 -0
  23. package/src/gateway/tools-list-handler.ts +161 -0
  24. package/src/gateway/workclaw-gateway.ts +298 -0
  25. package/src/media/upload.ts +168 -0
  26. package/src/outbound/index.ts +191 -0
  27. package/src/outbound/workclaw-sender.ts +161 -0
  28. package/src/runtime.ts +520 -0
  29. package/src/secret-contract-api.ts +4 -0
  30. package/src/send.ts +1 -0
  31. package/src/setup-api.ts +3 -0
  32. package/src/setup-core.ts +25 -0
  33. package/src/setup-surface.ts +498 -0
  34. package/src/tools/openclaw-workclaw-cron/api/index.ts +326 -0
  35. package/src/tools/openclaw-workclaw-cron/index.ts +39 -0
  36. package/src/tools/openclaw-workclaw-cron/src/add/params.ts +177 -0
  37. package/src/tools/openclaw-workclaw-cron/src/add/sync.ts +188 -0
  38. package/src/tools/openclaw-workclaw-cron/src/disable/params.ts +100 -0
  39. package/src/tools/openclaw-workclaw-cron/src/disable/sync.ts +127 -0
  40. package/src/tools/openclaw-workclaw-cron/src/enable/params.ts +100 -0
  41. package/src/tools/openclaw-workclaw-cron/src/enable/sync.ts +127 -0
  42. package/src/tools/openclaw-workclaw-cron/src/notify/sync.ts +148 -0
  43. package/src/tools/openclaw-workclaw-cron/src/remove/params.ts +109 -0
  44. package/src/tools/openclaw-workclaw-cron/src/remove/sync.ts +127 -0
  45. package/src/tools/openclaw-workclaw-cron/src/update/params.ts +195 -0
  46. package/src/tools/openclaw-workclaw-cron/src/update/sync.ts +161 -0
  47. package/src/tools/openclaw-workclaw-cron/types/index.ts +55 -0
  48. package/src/tools/openclaw-workclaw-cron/utils/index.ts +141 -0
  49. package/src/tools/openclaw-workclaw-system/index.ts +17 -0
  50. package/src/tools/openclaw-workclaw-system/src/get/index.ts +77 -0
  51. package/src/tools/openclaw-workclaw-system/src/token/index.ts +93 -0
  52. package/src/types.ts +50 -0
  53. package/src/utils/content.ts +40 -0
  54. package/tsconfig.json +34 -0
@@ -0,0 +1,618 @@
1
+ import { Agent, fetch as undiciFetch } from 'undici'
2
+ import { getWorkclawLogger } from '../runtime.js'
3
+
4
+ interface WorkClawTokenCache {
5
+ token: string
6
+ expiresAt: number
7
+ }
8
+
9
+ const tokenCache = new Map<string, WorkClawTokenCache>()
10
+
11
+ export function clearWorkclawTokenCache(cacheKey: string): void {
12
+ tokenCache.delete(cacheKey)
13
+ }
14
+
15
+ export function normalizeBaseUrl(value: string | undefined): string {
16
+ const raw = (value || 'https://workbrain.cn/backend-api').trim()
17
+ return raw.endsWith('/') ? raw.slice(0, -1) : raw
18
+ }
19
+
20
+ function createWsEndpoint(baseUrl: string): string {
21
+ const wsBase = baseUrl.replace(/^https?:\/\//i, match =>
22
+ match.toLowerCase() === 'https://' ? 'wss://' : 'ws://')
23
+ return `${wsBase}/v1/ws/connect`
24
+ }
25
+
26
+ export async function doFetchJson(
27
+ url: string,
28
+ init: RequestInit,
29
+ allowInsecureTls?: boolean,
30
+ requestTimeout?: number,
31
+ ): Promise<any> {
32
+ const dispatcher = allowInsecureTls
33
+ ? new Agent({ connect: { rejectUnauthorized: false } })
34
+ : undefined
35
+ const fetcher = allowInsecureTls ? undiciFetch : globalThis.fetch
36
+ const controller = new AbortController()
37
+ const timeoutMs = typeof requestTimeout === 'number' && requestTimeout > 0 ? requestTimeout : 30000
38
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs)
39
+
40
+ let response: any
41
+ let text = ''
42
+ try {
43
+ response = await fetcher(url, {
44
+ ...init,
45
+ signal: controller.signal,
46
+ ...(dispatcher ? { dispatcher } : {}),
47
+ } as any)
48
+ text = await response.text().catch(() => '')
49
+ }
50
+ finally {
51
+ clearTimeout(timeoutId)
52
+ }
53
+
54
+ let data: any = {}
55
+ if (text) {
56
+ try {
57
+ data = JSON.parse(text)
58
+ }
59
+ catch {
60
+ data = {}
61
+ }
62
+ }
63
+
64
+ if (!response.ok) {
65
+ const message = data?.message || text || `HTTP ${response.status}`
66
+ throw new Error(message)
67
+ }
68
+ return data
69
+ }
70
+
71
+ export interface WorkclawConnectionConfig {
72
+ baseUrl?: string
73
+ websocketUrl?: string
74
+ appKey?: string
75
+ appSecret?: string
76
+ localIp?: string
77
+ allowInsecureTls?: boolean
78
+ requestTimeout?: number
79
+ }
80
+
81
+ async function fetchAccessTokenFromServer(
82
+ requestUrl: string,
83
+ config: WorkclawConnectionConfig,
84
+ ): Promise<{ token: string, expiresAt: number }> {
85
+ getWorkclawLogger().info(
86
+ `Requesting access token url=${requestUrl} appKeyPrefix=${String(config.appKey)}... appSecretLen=${String(config.appSecret)}`,
87
+ )
88
+
89
+ const data = await doFetchJson(
90
+ requestUrl,
91
+ {
92
+ method: 'POST',
93
+ headers: { 'Content-Type': 'application/json' },
94
+ body: JSON.stringify({
95
+ app_key: config.appKey,
96
+ app_secret: config.appSecret,
97
+ }),
98
+ },
99
+ config.allowInsecureTls,
100
+ config.requestTimeout,
101
+ )
102
+
103
+ getWorkclawLogger().info(
104
+ `Access token response code=${String(data?.code ?? '')} errCode=${String(data?.errCode ?? '')} errMsg=${String(data?.errMsg ?? data?.message ?? '')}`,
105
+ )
106
+
107
+ if (data?.code !== 200 || !data?.data?.accessToken) {
108
+ throw new Error(
109
+ String(data?.message || data?.errMsg || 'Failed to acquire access token'),
110
+ )
111
+ }
112
+
113
+ const token = String(data.data.accessToken)
114
+ const expiresIn = Number(data.data.expiresIn || 0)
115
+ const expiresAt = Date.now() + (expiresIn > 0 ? expiresIn * 1000 : 0)
116
+
117
+ getWorkclawLogger().info(
118
+ `Access token acquired tokenLen=${token.length} expiresInSec=${expiresIn} expiresAt=${new Date(expiresAt).toISOString()}`,
119
+ )
120
+
121
+ return { token, expiresAt }
122
+ }
123
+
124
+ export async function getWorkclawAccessToken(
125
+ cacheKey: string,
126
+ config: WorkclawConnectionConfig,
127
+ ): Promise<string> {
128
+ const startedAt = Date.now()
129
+ const baseUrl = normalizeBaseUrl(config.baseUrl)
130
+ const requestUrl = `${baseUrl}/authen/v1/access_token/internal`
131
+ const timeoutMs
132
+ = typeof config.requestTimeout === 'number' && config.requestTimeout > 0
133
+ ? config.requestTimeout
134
+ : 30000
135
+
136
+ try {
137
+ const cached = tokenCache.get(cacheKey)
138
+
139
+ if (cached && cached.token) {
140
+ const now = Date.now()
141
+ const refreshAt = cached.expiresAt - 5 * 60 * 1000
142
+
143
+ if (now < refreshAt) {
144
+ return cached.token
145
+ }
146
+
147
+ getWorkclawLogger().info(`[TOKEN] cache refresh needed cacheKey=${cacheKey}`)
148
+ }
149
+
150
+ if (!config.appKey || !config.appSecret) {
151
+ getWorkclawLogger().error(`[TOKEN] missing appKey/appSecret`)
152
+ throw new Error('Missing appKey/appSecret')
153
+ }
154
+
155
+ const result = await fetchAccessTokenFromServer(requestUrl, config)
156
+ tokenCache.set(cacheKey, { token: result.token, expiresAt: result.expiresAt })
157
+ getWorkclawLogger().info(`[TOKEN] refreshed cacheKey=${cacheKey}`)
158
+ return result.token
159
+ }
160
+ catch (error) {
161
+ getWorkclawLogger().error(`[TOKEN] failed cacheKey=${cacheKey} error=${String(error)}`)
162
+ throw error
163
+ }
164
+ }
165
+
166
+ export async function openWorkclawConnection(
167
+ cacheKey: string,
168
+ config: WorkclawConnectionConfig,
169
+ ): Promise<{ endpoint: string, ticket: string }> {
170
+ const startedAt = Date.now()
171
+ const baseUrl = normalizeBaseUrl(config.baseUrl)
172
+ const requestUrl = `${baseUrl}/open-apis/v1/connections/open`
173
+ const timeoutMs
174
+ = typeof config.requestTimeout === 'number' && config.requestTimeout > 0
175
+ ? config.requestTimeout
176
+ : 30000
177
+
178
+ const appKeyRaw = String(config.appKey ?? '')
179
+ const appSecretRaw = String(config.appSecret ?? '')
180
+
181
+ getWorkclawLogger().info(
182
+ `openWorkclawConnection start cacheKey=${cacheKey} baseUrl=${baseUrl} requestUrl=${requestUrl} allowInsecureTls=${Boolean(config.allowInsecureTls)} timeoutMs=${timeoutMs} localIp=${String(config.localIp ?? '')} websocketUrl=${String(config.websocketUrl ?? '')} appKeyPrefix=${appKeyRaw ? `${appKeyRaw.slice(0, 8)}...` : 'missing'} appSecretLen=${appSecretRaw.length}`,
183
+ )
184
+
185
+ const tokenStartedAt = Date.now()
186
+ const token = await getWorkclawAccessToken(cacheKey, config)
187
+ getWorkclawLogger().info(
188
+ `openWorkclawConnection got access token cacheKey=${cacheKey} tokenLen=${token.length} elapsedMs=${Date.now() - tokenStartedAt}`,
189
+ )
190
+
191
+ const payload = {
192
+ appKey: config.appKey,
193
+ appSecret: config.appSecret,
194
+ subscriptions: [
195
+ { type: 'CALLBACK', topic: 'AGENT_MESSAGE' },
196
+ { type: 'EVENT', topic: '*' },
197
+ { type: 'SYSTEM', topic: '*' },
198
+ ],
199
+ ...(config.localIp ? { localIp: config.localIp } : {}),
200
+ }
201
+
202
+ getWorkclawLogger().info(
203
+ `Opening connection url=${requestUrl} cacheKey=${cacheKey} subscriptions=${payload.subscriptions.length}`,
204
+ )
205
+
206
+ const data = await doFetchJson(
207
+ requestUrl,
208
+ {
209
+ method: 'POST',
210
+ headers: {
211
+ 'Content-Type': 'application/json',
212
+ 'Authorization': `Bearer ${token}`,
213
+ },
214
+ body: JSON.stringify(payload),
215
+ },
216
+ config.allowInsecureTls,
217
+ config.requestTimeout,
218
+ )
219
+
220
+ getWorkclawLogger().info(
221
+ `Open connection response code=${String(data?.code ?? '')} errCode=${String(data?.errCode ?? '')} errMsg=${String(data?.errMsg ?? data?.message ?? '')} hasTicket=${Boolean(data?.data?.ticket)} hasEndpoint=${Boolean(data?.data?.endpoint)}`,
222
+ )
223
+
224
+ if (data?.code !== 200 || !data?.data?.ticket) {
225
+ throw new Error(String(data?.message || data?.errMsg || 'Failed to open connection'))
226
+ }
227
+
228
+ const ticket = String(data.data.ticket)
229
+
230
+ let endpointSource: 'config' | 'api' | 'derived' = 'derived'
231
+
232
+ // 优先使用配置中的 websocketUrl,其次使用 API 返回的 endpoint,最后使用默认构建的 endpoint
233
+ const endpoint = config.websocketUrl
234
+ ? ((endpointSource = 'config'), String(config.websocketUrl))
235
+ : data?.data?.endpoint
236
+ ? ((endpointSource = 'api'), String(data.data.endpoint))
237
+ : ((endpointSource = 'derived'), createWsEndpoint(baseUrl))
238
+
239
+ getWorkclawLogger().info(
240
+ `openWorkclawConnection success cacheKey=${cacheKey} endpoint=${endpoint} endpointSource=${endpointSource} ticketLen=${ticket.length} elapsedMs=${Date.now() - startedAt}`,
241
+ )
242
+
243
+ return { endpoint, ticket }
244
+ }
245
+
246
+ export interface AgentInstance {
247
+ id: string
248
+ createTime: string
249
+ updateTime: string
250
+ futureId: string
251
+ phone: string
252
+ nickName: string
253
+ imageId: string
254
+ level: number
255
+ identity: string
256
+ type: string
257
+ name: string
258
+ gender: string
259
+ age: number | null
260
+ tcId: string | null
261
+ city: string | null
262
+ loginType: string | null
263
+ status: string
264
+ publicScope: string
265
+ ip: string | null
266
+ phoneName: string | null
267
+ tip: string | null
268
+ email: string | null
269
+ featureId: string | null
270
+ job: string | null
271
+ newUser: boolean
272
+ imageKnBase: string | null
273
+ recommend: string | null
274
+ comment: string | null
275
+ treeKnbase: string | null
276
+ preferredLanguage: string
277
+ timezone: string
278
+ introduction: string
279
+ backgroundId: string
280
+ voiceId: string
281
+ characterSettings: string | null
282
+ personFeatures: string | null
283
+ learningFeatures: string | null
284
+ workFeatures: string | null
285
+ socializeFeatures: string | null
286
+ useKnowledge: boolean
287
+ roomOnlyKnowledge: boolean
288
+ roomWebSearch: boolean
289
+ abilityId: string | null
290
+ baiduCensoringStrategy: string | null
291
+ knowledgeCheckStrategy: string | null
292
+ interestTags: string | null
293
+ temporaryId: string | null
294
+ treeBatchNo: string | null
295
+ allowJoinSpace: boolean
296
+ modelId: string
297
+ orgId: string | null
298
+ qrCodeUrl: string | null
299
+ locationPoint: {
300
+ x: number
301
+ y: number
302
+ } | null
303
+ hot: string
304
+ score: number
305
+ professionalUser: string | null
306
+ autoCreateFace: boolean
307
+ liveId: string | null
308
+ liveStartTime: string | null
309
+ isIpAgent: number
310
+ isAdopted: number
311
+ }
312
+
313
+ /**
314
+ * 获取智能体列表
315
+ * POST /open-apis/instance/list
316
+ */
317
+ export async function getAgentInstances(
318
+ cacheKey: string,
319
+ config: WorkclawConnectionConfig,
320
+ ): Promise<AgentInstance[]> {
321
+ const baseUrl = normalizeBaseUrl(config.baseUrl)
322
+ const token = await getWorkclawAccessToken(cacheKey, config)
323
+
324
+ const payload = {
325
+ appKey: config.appKey,
326
+ }
327
+
328
+ getWorkclawLogger().info(`Getting agent instances from ${baseUrl}/open-apis/instance/list`)
329
+ getWorkclawLogger().info(`Payload:`, JSON.stringify(payload, null, 2))
330
+
331
+ const data = await doFetchJson(
332
+ `${baseUrl}/open-apis/instance/list`,
333
+ {
334
+ method: 'POST',
335
+ headers: {
336
+ 'Content-Type': 'application/json',
337
+ 'Authorization': `Bearer ${token}`,
338
+ },
339
+ body: JSON.stringify(payload),
340
+ },
341
+ config.allowInsecureTls,
342
+ config.requestTimeout,
343
+ )
344
+ getWorkclawLogger().info(`Agent instances response:`, JSON.stringify(data, null, 2))
345
+
346
+ if (data?.code !== 200 || !Array.isArray(data?.data)) {
347
+ throw new Error(data?.message || 'Failed to get agent instances')
348
+ }
349
+
350
+ return data.data as AgentInstance[]
351
+ }
352
+
353
+ export interface WorkClawMessageParams {
354
+ cacheKey: string
355
+ config: WorkclawConnectionConfig
356
+ agentId: string | number
357
+ receiveId: string | number
358
+ msgType: string
359
+ content: string
360
+ openConversationId?: string
361
+ replayMsgId?: string
362
+ endpoint?: string
363
+ last?: boolean
364
+ source?: 'deliver' | 'sendText' | 'after_tool_call'
365
+ }
366
+
367
+ /**
368
+ * 发送主动消息(非回复)
369
+ * POST /im/v1/messages
370
+ */
371
+ export async function sendWorkclawMessage(params: WorkClawMessageParams): Promise<string> {
372
+ const baseUrl = normalizeBaseUrl(params.config.baseUrl)
373
+ const token = await getWorkclawAccessToken(params.cacheKey, params.config)
374
+
375
+ // 主动消息参数
376
+ const payload: any = {
377
+ agentId: params.agentId,
378
+ receiveId: params.receiveId,
379
+ msgType: params.msgType,
380
+ content: params.content,
381
+ }
382
+
383
+ // 添加 last 字段(仅当为 true 时)
384
+ if (params.last === true) {
385
+ payload.is_last = true
386
+ }
387
+
388
+ // openConversationId 非必填,API 字段名为 conversationId
389
+ if (params.openConversationId) {
390
+ payload.conversationId = params.openConversationId
391
+ }
392
+
393
+ const endpoint = params.endpoint || '/open-apis/im/v1/messages'
394
+ getWorkclawLogger().info(`Sending proactive message to ${baseUrl}${endpoint}`)
395
+ getWorkclawLogger().info(`Payload:`, JSON.stringify(payload, null, 2))
396
+
397
+ const data = await doFetchJson(
398
+ `${baseUrl}${endpoint}`,
399
+ {
400
+ method: 'POST',
401
+ headers: {
402
+ 'Content-Type': 'application/json',
403
+ 'Authorization': `Bearer ${token}`,
404
+ },
405
+ body: JSON.stringify(payload),
406
+ },
407
+ params.config.allowInsecureTls,
408
+ params.config.requestTimeout,
409
+ )
410
+ getWorkclawLogger().info(`Send message response:`, JSON.stringify(data, null, 2))
411
+
412
+ if (data?.code !== 200 || !data?.data?.msgId) {
413
+ throw new Error(data?.message || 'Failed to send message')
414
+ }
415
+ return String(data.data.msgId)
416
+ }
417
+
418
+ /**
419
+ * 发送回复消息
420
+ * POST /im/v1/messages/{messageId}/reply
421
+ */
422
+ export async function sendWorkclawReplyMessage(params: WorkClawMessageParams & { messageId: string }): Promise<string> {
423
+ const baseUrl = normalizeBaseUrl(params.config.baseUrl)
424
+ const token = await getWorkclawAccessToken(params.cacheKey, params.config)
425
+
426
+ // 回复消息参数
427
+ const payload: any = {
428
+ agentId: params.agentId,
429
+ receiveId: params.receiveId,
430
+ msgType: params.msgType,
431
+ content: params.content,
432
+ }
433
+
434
+ // 添加 last 字段(仅当为 true 时)
435
+ if (params.last === true) {
436
+ payload.is_last = true
437
+ }
438
+
439
+ if (params.openConversationId) {
440
+ payload.conversationId = params.openConversationId
441
+ }
442
+
443
+ const endpoint = params.endpoint || `/open-apis/im/v1/messages/${params.messageId}/reply`
444
+ getWorkclawLogger().info(`[SEND] source=${params.source || '?'} last=${params.last} conv=${params.openConversationId || '(none)'} msgId=${params.messageId}`)
445
+
446
+ const data = await doFetchJson(
447
+ `${baseUrl}${endpoint}`,
448
+ {
449
+ method: 'POST',
450
+ headers: {
451
+ 'Content-Type': 'application/json',
452
+ 'Authorization': `Bearer ${token}`,
453
+ },
454
+ body: JSON.stringify(payload),
455
+ },
456
+ params.config.allowInsecureTls,
457
+ params.config.requestTimeout,
458
+ )
459
+ getWorkclawLogger().info(`[SEND] done msgId=${data?.data?.msgId} code=${data?.code}`)
460
+
461
+ if (data?.code !== 200 || !data?.data?.msgId) {
462
+ throw new Error(data?.message || 'Failed to send reply')
463
+ }
464
+ return String(data.data.msgId)
465
+ }
466
+
467
+ export function resolveWorkclawMessage(
468
+ text: string,
469
+ mediaUrl?: string,
470
+ ): { msgType: string, content: string } {
471
+ if (!mediaUrl || text.trim()) {
472
+ return { msgType: 'text', content: text }
473
+ }
474
+ const url = mediaUrl.trim()
475
+ const lower = url.toLowerCase()
476
+ const isAudio
477
+ = lower.endsWith('.mp3')
478
+ || lower.endsWith('.wav')
479
+ || lower.endsWith('.aac')
480
+ || lower.endsWith('.m4a')
481
+ || lower.endsWith('.ogg')
482
+ const payload = JSON.stringify({ url })
483
+ return { msgType: isAudio ? 'audio' : 'image', content: payload }
484
+ }
485
+
486
+ export interface CronJobPayload {
487
+ messageId: number | string
488
+ planId?: number | string
489
+ clawJobId: string
490
+ name: string
491
+ kind: string // 'at' | 'every' | 'cron'
492
+ expr: string
493
+ message?: string
494
+ }
495
+
496
+ /**
497
+ * 同步 openclaw 创建的定时任务至后端
498
+ * POST /cron/job/add
499
+ */
500
+ export async function syncWorkclawCronJobToBackend(
501
+ cacheKey: string,
502
+ config: WorkclawConnectionConfig,
503
+ payload: CronJobPayload,
504
+ ): Promise<any> {
505
+ const baseUrl = normalizeBaseUrl(config.baseUrl)
506
+ const token = await getWorkclawAccessToken(cacheKey, config)
507
+
508
+ // 尝试添加 /open-apis 前缀,以匹配其他 API 的模式 (如 getAgentInstances)
509
+ const requestUrl = `${baseUrl}/open-apis/cron/job/add`
510
+ getWorkclawLogger().info(`Syncing cron job to ${requestUrl}`)
511
+ getWorkclawLogger().info(`Payload:`, JSON.stringify(payload, null, 2))
512
+
513
+ const data = await doFetchJson(
514
+ requestUrl,
515
+ {
516
+ method: 'POST',
517
+ headers: {
518
+ 'Content-Type': 'application/json',
519
+ 'Authorization': `Bearer ${token}`,
520
+ },
521
+ body: JSON.stringify(payload),
522
+ },
523
+ config.allowInsecureTls,
524
+ config.requestTimeout,
525
+ )
526
+ getWorkclawLogger().info(`Sync cron job response:`, JSON.stringify(data, null, 2))
527
+
528
+ if (data?.code !== 200 && data?.code !== 0) {
529
+ throw new Error(data?.message || data?.msg || 'Failed to sync cron job')
530
+ }
531
+ return data
532
+ }
533
+
534
+ /**
535
+ * 获取AppKey所对应的模型配置
536
+ * POST /open-apis/instance/apiKey/{appKey}
537
+ */
538
+ export async function getWorkclawModelConfigByAppKey(
539
+ cacheKey: string,
540
+ config: WorkclawConnectionConfig,
541
+ ): Promise<{
542
+ baseUrl: string
543
+ apiKey: string
544
+ }> {
545
+ const baseUrl = normalizeBaseUrl(config.baseUrl)
546
+ const token = await getWorkclawAccessToken(cacheKey, config)
547
+ const appKey = config.appKey
548
+
549
+ if (!appKey) {
550
+ throw new Error('Missing appKey for getModelKeyByAppKey')
551
+ }
552
+
553
+ const requestUrl = `${baseUrl}/open-apis/instance/apiKey/${appKey}`
554
+ getWorkclawLogger().info(`Getting model key from ${requestUrl}`)
555
+
556
+ const data = await doFetchJson(
557
+ requestUrl,
558
+ {
559
+ method: 'GET',
560
+ headers: {
561
+ 'Content-Type': 'x-www-form-urlencoded',
562
+ 'Authorization': `Bearer ${token}`,
563
+ },
564
+ },
565
+ config.allowInsecureTls,
566
+ config.requestTimeout,
567
+ )
568
+
569
+ getWorkclawLogger().info(`Model key response:`, JSON.stringify(data, null, 2))
570
+
571
+ if (data?.code !== 200 || !data?.data) {
572
+ throw new Error(data?.message || data?.msg || 'Failed to get model key')
573
+ }
574
+
575
+ return data.data
576
+ }
577
+
578
+ export interface CronJobMessagePayload {
579
+ clawJobId: string
580
+ message: string
581
+ }
582
+
583
+ /**
584
+ * 此时定时任务触发消息至后端
585
+ * POST /open-apis/cron/job/message
586
+ */
587
+ export async function sendWorkclawCronJobMessageToBackend(
588
+ cacheKey: string,
589
+ config: WorkclawConnectionConfig,
590
+ payload: CronJobMessagePayload,
591
+ ): Promise<any> {
592
+ const baseUrl = normalizeBaseUrl(config.baseUrl)
593
+ const token = await getWorkclawAccessToken(cacheKey, config)
594
+
595
+ const requestUrl = `${baseUrl}/open-apis/cron/job/message`
596
+ getWorkclawLogger().info(`Sending cron job message to ${requestUrl}`)
597
+ getWorkclawLogger().info(`Payload:`, JSON.stringify(payload, null, 2))
598
+
599
+ const data = await doFetchJson(
600
+ requestUrl,
601
+ {
602
+ method: 'POST',
603
+ headers: {
604
+ 'Content-Type': 'application/json',
605
+ 'Authorization': `Bearer ${token}`,
606
+ },
607
+ body: JSON.stringify(payload),
608
+ },
609
+ config.allowInsecureTls,
610
+ config.requestTimeout,
611
+ )
612
+ getWorkclawLogger().info(`Send cron job message response:`, JSON.stringify(data, null, 2))
613
+
614
+ if (data?.code !== 200 && data?.code !== 0) {
615
+ throw new Error(data?.message || data?.msg || 'Failed to send cron job message')
616
+ }
617
+ return data
618
+ }