openclaw-channel-xiaozhu 3.0.1 → 3.1.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.
- package/dist/channel.d.ts +4 -1
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js +218 -61
- package/dist/channel.js.map +1 -1
- package/dist/cli/init-config.d.ts +28 -0
- package/dist/cli/init-config.d.ts.map +1 -0
- package/dist/cli/init-config.js +116 -0
- package/dist/cli/init-config.js.map +1 -0
- package/dist/cli/init.d.ts +36 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +519 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/relay/client.d.ts +1 -1
- package/dist/relay/client.d.ts.map +1 -1
- package/dist/relay/client.js +2 -2
- package/dist/relay/client.js.map +1 -1
- package/dist/server.d.ts +2 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +196 -14
- package/dist/server.js.map +1 -1
- package/dist/setup.d.ts +7 -4
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +90 -17
- package/dist/setup.js.map +1 -1
- package/dist/types.d.ts +12 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/webhook-sender.d.ts +1 -1
- package/dist/utils/webhook-sender.d.ts.map +1 -1
- package/dist/utils/webhook-sender.js +4 -2
- package/dist/utils/webhook-sender.js.map +1 -1
- package/openclaw.plugin.json +30 -3
- package/package.json +25 -2
package/dist/channel.d.ts
CHANGED
package/dist/channel.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"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"}
|
package/dist/channel.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* auth / elevated / commands / streaming / threading / agentPrompt /
|
|
5
5
|
* directory / resolver / actions / heartbeat)
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { startHttpServer, getRuntimeWebhookUrl, getRuntimeWebhookSecret, getAccountState } from "./server.js";
|
|
8
9
|
import { startRelayClient } from "./relay/client.js";
|
|
9
10
|
import { sendWithRetry } from "./utils/webhook-sender.js";
|
|
10
11
|
import { chunkText } from "./utils/text-chunker.js";
|
|
@@ -12,6 +13,7 @@ import { applySetupAccountConfig, applySetupAccountName, listAccountIds as listC
|
|
|
12
13
|
const CHANNEL_ID = "openclaw-channel-xiaozhu";
|
|
13
14
|
const DEFAULT_PORT = 18800;
|
|
14
15
|
const DEFAULT_RELAY_URL = "wss://relay.xiaozhu.ai/ws/bot";
|
|
16
|
+
const DEFAULT_PLATFORM_BASE_URL = "https://xiaozhu.wxwyzn.com";
|
|
15
17
|
const TARGET_ID_PATTERN = /^[\w.@:+\-]{1,128}$/;
|
|
16
18
|
const ACCOUNT_ID_PATTERN = /^[a-zA-Z0-9._-]{1,64}$/;
|
|
17
19
|
const AUTH_PROBE_TIMEOUT_MS = 3500;
|
|
@@ -24,6 +26,30 @@ export function setRuntime(rt, logger) {
|
|
|
24
26
|
if (logger)
|
|
25
27
|
pluginLogger = logger;
|
|
26
28
|
}
|
|
29
|
+
function buildPlatformRelayUrl(account) {
|
|
30
|
+
const base = (account.platformBaseUrl || "").trim() || DEFAULT_PLATFORM_BASE_URL;
|
|
31
|
+
if (!account.clientId || !account.clientSecret)
|
|
32
|
+
return null;
|
|
33
|
+
try {
|
|
34
|
+
const parsed = new URL(base);
|
|
35
|
+
if (parsed.protocol === "http:")
|
|
36
|
+
parsed.protocol = "ws:";
|
|
37
|
+
if (parsed.protocol === "https:")
|
|
38
|
+
parsed.protocol = "wss:";
|
|
39
|
+
if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:")
|
|
40
|
+
return null;
|
|
41
|
+
let wsPath = (account.platformWsPath || "/ws/plugin").trim() || "/ws/plugin";
|
|
42
|
+
if (!wsPath.startsWith("/"))
|
|
43
|
+
wsPath = `/${wsPath}`;
|
|
44
|
+
parsed.pathname = wsPath;
|
|
45
|
+
parsed.search = "";
|
|
46
|
+
parsed.searchParams.set("clientId", account.clientId);
|
|
47
|
+
return parsed.toString();
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
27
53
|
function getChannelConfig(cfg) {
|
|
28
54
|
return cfg?.channels?.[CHANNEL_ID] || {};
|
|
29
55
|
}
|
|
@@ -34,35 +60,62 @@ function simpleHash(s) {
|
|
|
34
60
|
h = ((h << 5) - h + s.charCodeAt(i)) | 0;
|
|
35
61
|
return Math.abs(h);
|
|
36
62
|
}
|
|
63
|
+
function readSecretFromFile(path) {
|
|
64
|
+
if (!path)
|
|
65
|
+
return undefined;
|
|
66
|
+
try {
|
|
67
|
+
const content = readFileSync(path, "utf8").trim();
|
|
68
|
+
return content || undefined;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
37
74
|
/** 从原始配置对象解析 HttpApiAccount */
|
|
38
75
|
function parseAccountFromRaw(raw, accountId) {
|
|
39
76
|
const rawGroupToolPolicy = raw.groupToolPolicy;
|
|
40
77
|
const groupToolPolicy = rawGroupToolPolicy && typeof rawGroupToolPolicy === "object" && !Array.isArray(rawGroupToolPolicy)
|
|
41
78
|
? rawGroupToolPolicy
|
|
42
79
|
: undefined;
|
|
80
|
+
const envSuffix = accountId.replace(/[^a-zA-Z0-9]/g, "_").toUpperCase();
|
|
81
|
+
const envClientId = process.env[`XIAOZHU_CLIENT_ID_${envSuffix}`]
|
|
82
|
+
|| process.env.XIAOZHU_CLIENT_ID
|
|
83
|
+
|| "";
|
|
84
|
+
const envClientSecret = process.env[`XIAOZHU_CLIENT_SECRET_${envSuffix}`]
|
|
85
|
+
|| process.env.XIAOZHU_CLIENT_SECRET
|
|
86
|
+
|| "";
|
|
87
|
+
const fileSecret = readSecretFromFile(raw.clientSecretFile);
|
|
88
|
+
const resolvedClientId = (raw.clientId || envClientId || "").trim();
|
|
89
|
+
const resolvedClientSecret = (raw.clientSecret || fileSecret || envClientSecret || "").trim();
|
|
43
90
|
return {
|
|
44
91
|
accountId,
|
|
45
92
|
name: raw.name || undefined,
|
|
46
93
|
enabled: raw.enabled !== false,
|
|
47
94
|
port: Number(raw.port) || DEFAULT_PORT + (accountId === "default" ? 0 : simpleHash(accountId) % 100),
|
|
48
|
-
|
|
95
|
+
clientId: resolvedClientId,
|
|
96
|
+
clientSecret: resolvedClientSecret,
|
|
97
|
+
clientSecretFile: raw.clientSecretFile || undefined,
|
|
49
98
|
rateLimitWindowMs: raw.rateLimitWindowMs,
|
|
50
99
|
rateLimitMax: raw.rateLimitMax,
|
|
51
100
|
rateLimitBurst: raw.rateLimitBurst,
|
|
52
101
|
dedupTtlMs: raw.dedupTtlMs,
|
|
53
102
|
allowFrom: raw.allowFrom,
|
|
54
103
|
webhookUrl: raw.webhookUrl,
|
|
104
|
+
webhookAuthToken: raw.webhookAuthToken,
|
|
55
105
|
allowOrigins: raw.allowOrigins,
|
|
56
106
|
coalesce: raw.coalesce,
|
|
57
107
|
perIpRateLimit: raw.perIpRateLimit,
|
|
58
108
|
webhookSecret: raw.webhookSecret,
|
|
59
109
|
webhookRetry: raw.webhookRetry,
|
|
110
|
+
accessTokenTtlSec: raw.accessTokenTtlSec,
|
|
60
111
|
heartbeatIntervalMs: raw.heartbeatIntervalMs,
|
|
61
112
|
textChunkLimit: raw.textChunkLimit,
|
|
62
113
|
commandsEnabled: raw.commandsEnabled,
|
|
63
114
|
networkMode: raw.networkMode,
|
|
64
115
|
relayUrl: raw.relayUrl,
|
|
65
116
|
relayToken: raw.relayToken,
|
|
117
|
+
platformBaseUrl: raw.platformBaseUrl,
|
|
118
|
+
platformWsPath: raw.platformWsPath,
|
|
66
119
|
requireMention: raw.requireMention,
|
|
67
120
|
groupIntroHint: raw.groupIntroHint,
|
|
68
121
|
replyToMode: raw.replyToMode,
|
|
@@ -309,10 +362,13 @@ function parseAuthChannelInput(channelInput) {
|
|
|
309
362
|
if (text.startsWith("{") && text.endsWith("}")) {
|
|
310
363
|
try {
|
|
311
364
|
const parsed = JSON.parse(text);
|
|
312
|
-
const
|
|
313
|
-
parsed.
|
|
314
|
-
parsed.
|
|
315
|
-
|
|
365
|
+
const clientId = [
|
|
366
|
+
parsed.clientId,
|
|
367
|
+
parsed.client_id,
|
|
368
|
+
].find((value) => typeof value === "string" && value.trim().length > 0);
|
|
369
|
+
const clientSecret = [
|
|
370
|
+
parsed.clientSecret,
|
|
371
|
+
parsed.client_secret,
|
|
316
372
|
].find((value) => typeof value === "string" && value.trim().length > 0);
|
|
317
373
|
const accountId = [
|
|
318
374
|
parsed.accountId,
|
|
@@ -320,12 +376,13 @@ function parseAuthChannelInput(channelInput) {
|
|
|
320
376
|
parsed.id,
|
|
321
377
|
].find((value) => typeof value === "string" && value.trim().length > 0);
|
|
322
378
|
return {
|
|
323
|
-
|
|
379
|
+
clientId: clientId?.trim(),
|
|
380
|
+
clientSecret: clientSecret?.trim(),
|
|
324
381
|
accountId: accountId?.trim(),
|
|
325
382
|
};
|
|
326
383
|
}
|
|
327
384
|
catch {
|
|
328
|
-
// fallthrough to key-value
|
|
385
|
+
// fallthrough to key-value mode
|
|
329
386
|
}
|
|
330
387
|
}
|
|
331
388
|
const result = {};
|
|
@@ -335,17 +392,28 @@ function parseAuthChannelInput(channelInput) {
|
|
|
335
392
|
const value = match[2]?.trim();
|
|
336
393
|
if (!value)
|
|
337
394
|
continue;
|
|
338
|
-
if (key === "
|
|
339
|
-
result.
|
|
395
|
+
if (key === "clientid" || key === "client_id") {
|
|
396
|
+
result.clientId = value;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (key === "clientsecret" || key === "client_secret") {
|
|
400
|
+
result.clientSecret = value;
|
|
340
401
|
continue;
|
|
341
402
|
}
|
|
342
403
|
if (key === "account" || key === "accountid" || key === "id") {
|
|
343
404
|
result.accountId = value;
|
|
344
405
|
}
|
|
345
406
|
}
|
|
346
|
-
if (result.
|
|
407
|
+
if (result.clientId || result.clientSecret || result.accountId)
|
|
347
408
|
return result;
|
|
348
|
-
|
|
409
|
+
const colonIndex = text.indexOf(":");
|
|
410
|
+
if (colonIndex > 0) {
|
|
411
|
+
return {
|
|
412
|
+
clientId: text.slice(0, colonIndex).trim() || undefined,
|
|
413
|
+
clientSecret: text.slice(colonIndex + 1).trim() || undefined,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
return {};
|
|
349
417
|
}
|
|
350
418
|
async function fetchWithTimeout(url, init, timeoutMs) {
|
|
351
419
|
const controller = new AbortController();
|
|
@@ -434,12 +502,15 @@ async function performAuthLogin(params) {
|
|
|
434
502
|
throw new Error(`[${CHANNEL_ID}] invalid accountId '${resolvedAccountId}' in auth.login`);
|
|
435
503
|
}
|
|
436
504
|
const account = resolveAccount(params.cfg, resolvedAccountId);
|
|
437
|
-
const
|
|
438
|
-
const
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
505
|
+
const configuredClientId = account.clientId?.trim();
|
|
506
|
+
const configuredClientSecret = account.clientSecret?.trim();
|
|
507
|
+
const inputClientId = parsedInput.clientId?.trim();
|
|
508
|
+
const inputClientSecret = parsedInput.clientSecret?.trim();
|
|
509
|
+
const effectiveClientId = inputClientId || configuredClientId;
|
|
510
|
+
const effectiveClientSecret = inputClientSecret || configuredClientSecret;
|
|
511
|
+
if (!effectiveClientId || !effectiveClientSecret) {
|
|
512
|
+
throw new Error(`[${CHANNEL_ID}] account '${resolvedAccountId}' missing client credentials. ` +
|
|
513
|
+
`Set channels.${CHANNEL_ID}.accounts.${resolvedAccountId}.clientId/clientSecret or pass channelInput credentials.`);
|
|
443
514
|
}
|
|
444
515
|
if (!Number.isInteger(account.port) || account.port < 1 || account.port > 65535) {
|
|
445
516
|
throw new Error(`[${CHANNEL_ID}] account '${resolvedAccountId}' has invalid port '${String(account.port)}'`);
|
|
@@ -468,20 +539,65 @@ async function performAuthLogin(params) {
|
|
|
468
539
|
}
|
|
469
540
|
// ignore non-JSON responses
|
|
470
541
|
}
|
|
542
|
+
const tokenEndpoint = `http://127.0.0.1:${account.port}/api/oauth2/token`;
|
|
543
|
+
let tokenRes;
|
|
544
|
+
try {
|
|
545
|
+
tokenRes = await fetchWithTimeout(tokenEndpoint, {
|
|
546
|
+
method: "POST",
|
|
547
|
+
headers: { "Content-Type": "application/json" },
|
|
548
|
+
body: JSON.stringify({
|
|
549
|
+
grant_type: "client_credentials",
|
|
550
|
+
client_id: effectiveClientId,
|
|
551
|
+
client_secret: effectiveClientSecret,
|
|
552
|
+
scope: "chat",
|
|
553
|
+
}),
|
|
554
|
+
}, AUTH_PROBE_TIMEOUT_MS);
|
|
555
|
+
}
|
|
556
|
+
catch (err) {
|
|
557
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
558
|
+
throw new Error(`[${CHANNEL_ID}] token exchange failed for account '${resolvedAccountId}': ${msg}`);
|
|
559
|
+
}
|
|
560
|
+
if (tokenRes.status === 401) {
|
|
561
|
+
throw new Error(`[${CHANNEL_ID}] client credentials rejected for account '${resolvedAccountId}' (HTTP 401)`);
|
|
562
|
+
}
|
|
563
|
+
if (tokenRes.status === 503) {
|
|
564
|
+
throw new Error(`[${CHANNEL_ID}] server reports missing configured client credentials for account '${resolvedAccountId}' (HTTP 503)`);
|
|
565
|
+
}
|
|
566
|
+
if (!tokenRes.ok) {
|
|
567
|
+
let detail = "";
|
|
568
|
+
try {
|
|
569
|
+
const tokenError = await tokenRes.json();
|
|
570
|
+
if (typeof tokenError.error === "string" && tokenError.error.trim()) {
|
|
571
|
+
detail = `: ${tokenError.error.trim()}`;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
catch {
|
|
575
|
+
// ignore parse errors
|
|
576
|
+
}
|
|
577
|
+
throw new Error(`[${CHANNEL_ID}] token exchange failed for account '${resolvedAccountId}' with HTTP ${tokenRes.status}${detail}`);
|
|
578
|
+
}
|
|
579
|
+
let accessToken = "";
|
|
580
|
+
try {
|
|
581
|
+
const tokenPayload = await tokenRes.json();
|
|
582
|
+
accessToken = typeof tokenPayload.access_token === "string" ? tokenPayload.access_token : "";
|
|
583
|
+
}
|
|
584
|
+
catch {
|
|
585
|
+
accessToken = "";
|
|
586
|
+
}
|
|
587
|
+
if (!accessToken) {
|
|
588
|
+
throw new Error(`[${CHANNEL_ID}] token exchange returned empty access_token for account '${resolvedAccountId}'`);
|
|
589
|
+
}
|
|
471
590
|
const authProbeUrl = `http://127.0.0.1:${account.port}/api/sessions?limit=1`;
|
|
472
591
|
let authRes;
|
|
473
592
|
try {
|
|
474
|
-
authRes = await fetchWithTimeout(authProbeUrl, { headers: { Authorization: `Bearer ${
|
|
593
|
+
authRes = await fetchWithTimeout(authProbeUrl, { headers: { Authorization: `Bearer ${accessToken}` } }, AUTH_PROBE_TIMEOUT_MS);
|
|
475
594
|
}
|
|
476
595
|
catch (err) {
|
|
477
596
|
const msg = err instanceof Error ? err.message : String(err);
|
|
478
597
|
throw new Error(`[${CHANNEL_ID}] auth probe request failed for account '${resolvedAccountId}': ${msg}`);
|
|
479
598
|
}
|
|
480
599
|
if (authRes.status === 401) {
|
|
481
|
-
throw new Error(`[${CHANNEL_ID}]
|
|
482
|
-
}
|
|
483
|
-
if (authRes.status === 503) {
|
|
484
|
-
throw new Error(`[${CHANNEL_ID}] remote server reports token is not configured for account '${resolvedAccountId}' (HTTP 503)`);
|
|
600
|
+
throw new Error(`[${CHANNEL_ID}] issued access_token rejected for account '${resolvedAccountId}' (HTTP 401)`);
|
|
485
601
|
}
|
|
486
602
|
if (!authRes.ok) {
|
|
487
603
|
let detail = "";
|
|
@@ -498,21 +614,24 @@ async function performAuthLogin(params) {
|
|
|
498
614
|
}
|
|
499
615
|
const log = resolveRuntimeLog(params.runtime);
|
|
500
616
|
if (params.verbose) {
|
|
501
|
-
if (
|
|
502
|
-
log?.(`[${CHANNEL_ID}] using channelInput
|
|
617
|
+
if ((inputClientId || inputClientSecret) && (!configuredClientId || !configuredClientSecret)) {
|
|
618
|
+
log?.(`[${CHANNEL_ID}] using channelInput client credentials for account '${resolvedAccountId}' (not persisted).`);
|
|
503
619
|
}
|
|
504
|
-
else if (
|
|
505
|
-
|
|
620
|
+
else if ((inputClientId && configuredClientId && inputClientId !== configuredClientId)
|
|
621
|
+
|| (inputClientSecret && configuredClientSecret && inputClientSecret !== configuredClientSecret)) {
|
|
622
|
+
log?.(`[${CHANNEL_ID}] channelInput credentials override configured credentials for this auth probe only.`);
|
|
506
623
|
}
|
|
507
624
|
log?.(`[${CHANNEL_ID}] auth/login probe passed for account '${resolvedAccountId}'`);
|
|
508
625
|
}
|
|
509
626
|
return {
|
|
510
627
|
accountId: resolvedAccountId,
|
|
511
628
|
port: account.port,
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
629
|
+
credentialSource: (inputClientId || inputClientSecret) ? "channelInput" : "config",
|
|
630
|
+
usedInputCredential: Boolean(inputClientId || inputClientSecret),
|
|
631
|
+
overrideConfiguredCredential: Boolean((inputClientId && configuredClientId && inputClientId !== configuredClientId)
|
|
632
|
+
|| (inputClientSecret && configuredClientSecret && inputClientSecret !== configuredClientSecret)),
|
|
515
633
|
healthUrl,
|
|
634
|
+
tokenEndpoint,
|
|
516
635
|
authProbeUrl,
|
|
517
636
|
};
|
|
518
637
|
}
|
|
@@ -611,13 +730,13 @@ export const httpChannelPlugin = {
|
|
|
611
730
|
getStatus: async ({ cfg }) => {
|
|
612
731
|
const accountIds = listConfiguredAccountIds(cfg, CHANNEL_ID);
|
|
613
732
|
const statuses = accountIds.map((accountId) => resolveAccount(cfg, accountId));
|
|
614
|
-
const configured = statuses.some((account) => Boolean(account.
|
|
733
|
+
const configured = statuses.some((account) => Boolean(account.clientId && account.clientSecret));
|
|
615
734
|
const active = statuses.filter((account) => account.enabled !== false).length;
|
|
616
735
|
return {
|
|
617
736
|
channel: CHANNEL_ID,
|
|
618
737
|
configured,
|
|
619
738
|
statusLines: [
|
|
620
|
-
configured ? "小助:已配置" : "小助:未配置
|
|
739
|
+
configured ? "小助:已配置" : "小助:未配置 clientId/clientSecret",
|
|
621
740
|
`账号数:${accountIds.length || 1}`,
|
|
622
741
|
`启用账号:${active || (configured ? 1 : 0)}`,
|
|
623
742
|
],
|
|
@@ -637,10 +756,20 @@ export const httpChannelPlugin = {
|
|
|
637
756
|
});
|
|
638
757
|
const current = resolveAccount(ctx.cfg, accountId);
|
|
639
758
|
const setupInput = { ...(ctx.input || {}) };
|
|
640
|
-
if (!setupInput.
|
|
641
|
-
const
|
|
642
|
-
if (
|
|
643
|
-
setupInput.
|
|
759
|
+
if (!setupInput.clientId && !current.clientId) {
|
|
760
|
+
const promptedClientId = await promptTextInput(ctx.prompter, "请输入小助渠道 clientId");
|
|
761
|
+
if (promptedClientId)
|
|
762
|
+
setupInput.clientId = promptedClientId;
|
|
763
|
+
}
|
|
764
|
+
if (!setupInput.clientSecret && !current.clientSecret) {
|
|
765
|
+
const promptedClientSecret = await promptTextInput(ctx.prompter, "请输入小助渠道 clientSecret", { secret: true });
|
|
766
|
+
if (promptedClientSecret)
|
|
767
|
+
setupInput.clientSecret = promptedClientSecret;
|
|
768
|
+
}
|
|
769
|
+
const hasClientId = Boolean((setupInput.clientId || current.clientId || "").trim());
|
|
770
|
+
const hasClientSecret = Boolean((setupInput.clientSecret || setupInput.clientSecretFile || current.clientSecret || "").toString().trim());
|
|
771
|
+
if (!hasClientId || !hasClientSecret) {
|
|
772
|
+
pluginLogger?.warn(`[${CHANNEL_ID}] account '${accountId}' is saved without client credentials; configure clientId/clientSecret before serving traffic.`);
|
|
644
773
|
}
|
|
645
774
|
if (!setupInput.name) {
|
|
646
775
|
const promptedName = await promptTextInput(ctx.prompter, "账号显示名称(可选)");
|
|
@@ -799,7 +928,7 @@ export const httpChannelPlugin = {
|
|
|
799
928
|
return Object.keys(ch.accounts);
|
|
800
929
|
}
|
|
801
930
|
// 单账号模式(向后兼容)
|
|
802
|
-
if (ch.
|
|
931
|
+
if (ch.clientId || ch.clientSecret || ch.clientSecretFile)
|
|
803
932
|
return ["default"];
|
|
804
933
|
return [];
|
|
805
934
|
},
|
|
@@ -817,13 +946,13 @@ export const httpChannelPlugin = {
|
|
|
817
946
|
},
|
|
818
947
|
defaultAccountId: (_cfg) => "default",
|
|
819
948
|
isConfigured: (account) => {
|
|
820
|
-
return !!account.
|
|
949
|
+
return !!(account.clientId && account.clientSecret);
|
|
821
950
|
},
|
|
822
951
|
describeAccount: (account) => ({
|
|
823
952
|
accountId: account.accountId,
|
|
824
953
|
name: account.name || `小助 :${account.port}`,
|
|
825
954
|
enabled: account.enabled,
|
|
826
|
-
configured: !!account.
|
|
955
|
+
configured: !!(account.clientId && account.clientSecret),
|
|
827
956
|
}),
|
|
828
957
|
setAccountEnabled: ({ cfg, accountId, enabled }) => {
|
|
829
958
|
const ch = getChannelConfig(cfg) || {};
|
|
@@ -878,7 +1007,6 @@ export const httpChannelPlugin = {
|
|
|
878
1007
|
try {
|
|
879
1008
|
startedServer = await startHttpServer({
|
|
880
1009
|
port: account.port,
|
|
881
|
-
token: account.token,
|
|
882
1010
|
accountId: account.accountId,
|
|
883
1011
|
account,
|
|
884
1012
|
runtime: rt,
|
|
@@ -905,15 +1033,33 @@ export const httpChannelPlugin = {
|
|
|
905
1033
|
}
|
|
906
1034
|
return;
|
|
907
1035
|
}
|
|
908
|
-
|
|
909
|
-
if (
|
|
1036
|
+
const platformRelayUrl = buildPlatformRelayUrl(account);
|
|
1037
|
+
if (platformRelayUrl) {
|
|
1038
|
+
log.info(`[${CHANNEL_ID}] platform relay enabled → ${platformRelayUrl}`);
|
|
1039
|
+
ctx.setStatus({ accountId: account.accountId, connected: true, networkMode: "platform", relayUrl: platformRelayUrl });
|
|
1040
|
+
startRelayClient({
|
|
1041
|
+
relayUrl: platformRelayUrl,
|
|
1042
|
+
relayToken: account.clientSecret,
|
|
1043
|
+
localClientSecret: account.clientSecret,
|
|
1044
|
+
accountId: account.accountId,
|
|
1045
|
+
runtime: rt,
|
|
1046
|
+
config: cfg,
|
|
1047
|
+
log,
|
|
1048
|
+
abortSignal: internalStopController.signal,
|
|
1049
|
+
onStatusChange: (connected) => {
|
|
1050
|
+
ctx.setStatus({ accountId: account.accountId, connected, networkMode: "platform", relayUrl: platformRelayUrl });
|
|
1051
|
+
},
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
else if (account.networkMode === "cloud") {
|
|
1055
|
+
// relay 模式:无公网 IP,通过中继服务器转发
|
|
910
1056
|
const relayUrl = account.relayUrl || DEFAULT_RELAY_URL;
|
|
911
1057
|
log.info(`[${CHANNEL_ID}] networkMode=cloud → ${relayUrl}`);
|
|
912
1058
|
ctx.setStatus({ accountId: account.accountId, connected: true, networkMode: "cloud", relayUrl });
|
|
913
1059
|
startRelayClient({
|
|
914
1060
|
relayUrl,
|
|
915
|
-
relayToken: account.relayToken || account.
|
|
916
|
-
|
|
1061
|
+
relayToken: account.relayToken || account.clientSecret,
|
|
1062
|
+
localClientSecret: account.clientSecret,
|
|
917
1063
|
accountId: account.accountId,
|
|
918
1064
|
runtime: rt,
|
|
919
1065
|
config: cfg,
|
|
@@ -977,7 +1123,7 @@ export const httpChannelPlugin = {
|
|
|
977
1123
|
cleared: true,
|
|
978
1124
|
loggedOut: false,
|
|
979
1125
|
stopped,
|
|
980
|
-
message: `[${CHANNEL_ID}] HTTP API channel uses
|
|
1126
|
+
message: `[${CHANNEL_ID}] HTTP API channel uses OAuth2 client credentials; logout only clears runtime gateway state.`,
|
|
981
1127
|
};
|
|
982
1128
|
},
|
|
983
1129
|
},
|
|
@@ -1025,6 +1171,7 @@ export const httpChannelPlugin = {
|
|
|
1025
1171
|
const text = opts?.text || "";
|
|
1026
1172
|
const session = opts?.session || opts?.to || "";
|
|
1027
1173
|
const webhookUrl = getRuntimeWebhookUrl(account?.accountId) || account?.webhookUrl;
|
|
1174
|
+
const webhookSecret = getRuntimeWebhookSecret(account?.accountId) || account?.webhookSecret;
|
|
1028
1175
|
if (!webhookUrl) {
|
|
1029
1176
|
return { channel: CHANNEL_ID, ok: false, error: new Error("HTTP API channel is passive; configure webhookUrl or register via /api/webhook/register") };
|
|
1030
1177
|
}
|
|
@@ -1036,8 +1183,8 @@ export const httpChannelPlugin = {
|
|
|
1036
1183
|
const result = await sendWithRetry({
|
|
1037
1184
|
url: webhookUrl,
|
|
1038
1185
|
payload: { type: "text", text: chunk, session, from: "openclaw", channel: CHANNEL_ID, timestamp: Date.now() },
|
|
1039
|
-
|
|
1040
|
-
secret:
|
|
1186
|
+
authorizationBearer: account?.webhookAuthToken,
|
|
1187
|
+
secret: webhookSecret || undefined,
|
|
1041
1188
|
maxAttempts: account?.webhookRetry?.maxAttempts ?? 3,
|
|
1042
1189
|
baseDelayMs: account?.webhookRetry?.baseDelayMs ?? 1000,
|
|
1043
1190
|
});
|
|
@@ -1052,6 +1199,7 @@ export const httpChannelPlugin = {
|
|
|
1052
1199
|
const mediaUrl = opts?.mediaUrl || "";
|
|
1053
1200
|
const session = opts?.session || opts?.to || "";
|
|
1054
1201
|
const webhookUrl = getRuntimeWebhookUrl(account?.accountId) || account?.webhookUrl;
|
|
1202
|
+
const webhookSecret = getRuntimeWebhookSecret(account?.accountId) || account?.webhookSecret;
|
|
1055
1203
|
if (!webhookUrl) {
|
|
1056
1204
|
return { channel: CHANNEL_ID, ok: false, error: new Error("No webhookUrl configured for media delivery") };
|
|
1057
1205
|
}
|
|
@@ -1061,8 +1209,8 @@ export const httpChannelPlugin = {
|
|
|
1061
1209
|
const result = await sendWithRetry({
|
|
1062
1210
|
url: webhookUrl,
|
|
1063
1211
|
payload: { type: "media", mediaUrl, session, from: "openclaw", channel: CHANNEL_ID, timestamp: Date.now() },
|
|
1064
|
-
|
|
1065
|
-
secret:
|
|
1212
|
+
authorizationBearer: account?.webhookAuthToken,
|
|
1213
|
+
secret: webhookSecret || undefined,
|
|
1066
1214
|
maxAttempts: account?.webhookRetry?.maxAttempts ?? 3,
|
|
1067
1215
|
baseDelayMs: account?.webhookRetry?.baseDelayMs ?? 1000,
|
|
1068
1216
|
});
|
|
@@ -1078,6 +1226,7 @@ export const httpChannelPlugin = {
|
|
|
1078
1226
|
const payload = ctx.payload;
|
|
1079
1227
|
const account = cfg ? resolveAccount(cfg, accountId) : undefined;
|
|
1080
1228
|
const webhookUrl = getRuntimeWebhookUrl(accountId) || account?.webhookUrl;
|
|
1229
|
+
const webhookSecret = getRuntimeWebhookSecret(accountId) || account?.webhookSecret;
|
|
1081
1230
|
if (!webhookUrl) {
|
|
1082
1231
|
return { channel: CHANNEL_ID, ok: false, error: new Error("No webhookUrl configured for payload delivery") };
|
|
1083
1232
|
}
|
|
@@ -1095,8 +1244,8 @@ export const httpChannelPlugin = {
|
|
|
1095
1244
|
const result = await sendWithRetry({
|
|
1096
1245
|
url: webhookUrl,
|
|
1097
1246
|
payload: webhookPayload,
|
|
1098
|
-
|
|
1099
|
-
secret:
|
|
1247
|
+
authorizationBearer: account?.webhookAuthToken,
|
|
1248
|
+
secret: webhookSecret || undefined,
|
|
1100
1249
|
maxAttempts: account?.webhookRetry?.maxAttempts ?? 3,
|
|
1101
1250
|
baseDelayMs: account?.webhookRetry?.baseDelayMs ?? 1000,
|
|
1102
1251
|
});
|
|
@@ -1175,7 +1324,7 @@ export const httpChannelPlugin = {
|
|
|
1175
1324
|
accountId: account.accountId,
|
|
1176
1325
|
name: `小助 :${account.port}`,
|
|
1177
1326
|
enabled: account.enabled,
|
|
1178
|
-
configured: !!account.
|
|
1327
|
+
configured: !!(account.clientId && account.clientSecret),
|
|
1179
1328
|
running: runtime?.running ?? false,
|
|
1180
1329
|
connected: runtime?.connected ?? false,
|
|
1181
1330
|
lastStartAt: runtime?.lastStartAt ?? null,
|
|
@@ -1217,8 +1366,8 @@ export const httpChannelPlugin = {
|
|
|
1217
1366
|
channel: CHANNEL_ID,
|
|
1218
1367
|
accountId,
|
|
1219
1368
|
kind: "config",
|
|
1220
|
-
message: "未配置
|
|
1221
|
-
fix: `设置 channels.${CHANNEL_ID}.accounts.${accountId}.
|
|
1369
|
+
message: "未配置 clientId/clientSecret,账号不可用",
|
|
1370
|
+
fix: `设置 channels.${CHANNEL_ID}.accounts.${accountId}.clientId/clientSecret`,
|
|
1222
1371
|
});
|
|
1223
1372
|
}
|
|
1224
1373
|
if (!enabled) {
|
|
@@ -1236,7 +1385,7 @@ export const httpChannelPlugin = {
|
|
|
1236
1385
|
accountId,
|
|
1237
1386
|
kind: "runtime",
|
|
1238
1387
|
message: "网关运行中但未连接",
|
|
1239
|
-
fix: "检查端口占用、网络连通性和
|
|
1388
|
+
fix: "检查端口占用、网络连通性和 client credentials 配置",
|
|
1240
1389
|
});
|
|
1241
1390
|
}
|
|
1242
1391
|
if (lastError) {
|
|
@@ -1423,8 +1572,8 @@ export const httpChannelPlugin = {
|
|
|
1423
1572
|
},
|
|
1424
1573
|
collectWarnings: ({ account }) => {
|
|
1425
1574
|
const warnings = [];
|
|
1426
|
-
if (!account.
|
|
1427
|
-
warnings.push("未配置
|
|
1575
|
+
if (!account.clientId || !account.clientSecret)
|
|
1576
|
+
warnings.push("未配置 clientId/clientSecret,服务不可用");
|
|
1428
1577
|
if (account.allowFrom?.includes("*"))
|
|
1429
1578
|
warnings.push("allowFrom 设为 *,任何人可发送消息");
|
|
1430
1579
|
if (!account.webhookUrl && account.networkMode !== "cloud") {
|
|
@@ -1533,7 +1682,8 @@ export const httpChannelPlugin = {
|
|
|
1533
1682
|
type: "array",
|
|
1534
1683
|
items: { type: "string" },
|
|
1535
1684
|
},
|
|
1536
|
-
|
|
1685
|
+
clientId: { type: "string" },
|
|
1686
|
+
clientSecret: { type: "string" },
|
|
1537
1687
|
verbose: { type: "boolean" },
|
|
1538
1688
|
limit: { type: "number", minimum: 1, maximum: 50 },
|
|
1539
1689
|
},
|
|
@@ -1566,7 +1716,7 @@ export const httpChannelPlugin = {
|
|
|
1566
1716
|
account: {
|
|
1567
1717
|
name: account.name || null,
|
|
1568
1718
|
enabled: account.enabled,
|
|
1569
|
-
configured: Boolean(account.
|
|
1719
|
+
configured: Boolean(account.clientId && account.clientSecret),
|
|
1570
1720
|
port: account.port,
|
|
1571
1721
|
mode: account.networkMode || "webhook",
|
|
1572
1722
|
dmPolicy: account.dmPolicy || null,
|
|
@@ -1584,14 +1734,21 @@ export const httpChannelPlugin = {
|
|
|
1584
1734
|
});
|
|
1585
1735
|
}
|
|
1586
1736
|
if (action === "auth.probe") {
|
|
1587
|
-
const
|
|
1737
|
+
const probeClientId = readToolString(args, "clientId");
|
|
1738
|
+
const probeClientSecret = readToolString(args, "clientSecret");
|
|
1588
1739
|
const verbose = args.verbose === true;
|
|
1740
|
+
const channelInput = (probeClientId || probeClientSecret)
|
|
1741
|
+
? JSON.stringify({
|
|
1742
|
+
...(probeClientId ? { clientId: probeClientId } : {}),
|
|
1743
|
+
...(probeClientSecret ? { clientSecret: probeClientSecret } : {}),
|
|
1744
|
+
})
|
|
1745
|
+
: undefined;
|
|
1589
1746
|
const result = await performAuthLogin({
|
|
1590
1747
|
cfg: toolCfg,
|
|
1591
1748
|
accountId,
|
|
1592
1749
|
runtime: pluginRuntime || {},
|
|
1593
1750
|
verbose,
|
|
1594
|
-
channelInput
|
|
1751
|
+
channelInput,
|
|
1595
1752
|
});
|
|
1596
1753
|
return jsonToolResult({
|
|
1597
1754
|
channel: CHANNEL_ID,
|