openclaw-channel-xiaozhu 3.1.0 → 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 +211 -57
- package/dist/channel.js.map +1 -1
- package/dist/cli/init-config.d.ts +10 -3
- package/dist/cli/init-config.d.ts.map +1 -1
- package/dist/cli/init-config.js +37 -11
- package/dist/cli/init-config.js.map +1 -1
- package/dist/cli/init.d.ts +8 -2
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +96 -22
- package/dist/cli/init.js.map +1 -1
- 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 +0 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +159 -10
- 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 +1 -1
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,6 +4,7 @@
|
|
|
4
4
|
* auth / elevated / commands / streaming / threading / agentPrompt /
|
|
5
5
|
* directory / resolver / actions / heartbeat)
|
|
6
6
|
*/
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
7
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";
|
|
@@ -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
|
},
|
|
@@ -1037,7 +1183,7 @@ export const httpChannelPlugin = {
|
|
|
1037
1183
|
const result = await sendWithRetry({
|
|
1038
1184
|
url: webhookUrl,
|
|
1039
1185
|
payload: { type: "text", text: chunk, session, from: "openclaw", channel: CHANNEL_ID, timestamp: Date.now() },
|
|
1040
|
-
|
|
1186
|
+
authorizationBearer: account?.webhookAuthToken,
|
|
1041
1187
|
secret: webhookSecret || undefined,
|
|
1042
1188
|
maxAttempts: account?.webhookRetry?.maxAttempts ?? 3,
|
|
1043
1189
|
baseDelayMs: account?.webhookRetry?.baseDelayMs ?? 1000,
|
|
@@ -1063,7 +1209,7 @@ export const httpChannelPlugin = {
|
|
|
1063
1209
|
const result = await sendWithRetry({
|
|
1064
1210
|
url: webhookUrl,
|
|
1065
1211
|
payload: { type: "media", mediaUrl, session, from: "openclaw", channel: CHANNEL_ID, timestamp: Date.now() },
|
|
1066
|
-
|
|
1212
|
+
authorizationBearer: account?.webhookAuthToken,
|
|
1067
1213
|
secret: webhookSecret || undefined,
|
|
1068
1214
|
maxAttempts: account?.webhookRetry?.maxAttempts ?? 3,
|
|
1069
1215
|
baseDelayMs: account?.webhookRetry?.baseDelayMs ?? 1000,
|
|
@@ -1098,7 +1244,7 @@ export const httpChannelPlugin = {
|
|
|
1098
1244
|
const result = await sendWithRetry({
|
|
1099
1245
|
url: webhookUrl,
|
|
1100
1246
|
payload: webhookPayload,
|
|
1101
|
-
|
|
1247
|
+
authorizationBearer: account?.webhookAuthToken,
|
|
1102
1248
|
secret: webhookSecret || undefined,
|
|
1103
1249
|
maxAttempts: account?.webhookRetry?.maxAttempts ?? 3,
|
|
1104
1250
|
baseDelayMs: account?.webhookRetry?.baseDelayMs ?? 1000,
|
|
@@ -1178,7 +1324,7 @@ export const httpChannelPlugin = {
|
|
|
1178
1324
|
accountId: account.accountId,
|
|
1179
1325
|
name: `小助 :${account.port}`,
|
|
1180
1326
|
enabled: account.enabled,
|
|
1181
|
-
configured: !!account.
|
|
1327
|
+
configured: !!(account.clientId && account.clientSecret),
|
|
1182
1328
|
running: runtime?.running ?? false,
|
|
1183
1329
|
connected: runtime?.connected ?? false,
|
|
1184
1330
|
lastStartAt: runtime?.lastStartAt ?? null,
|
|
@@ -1220,8 +1366,8 @@ export const httpChannelPlugin = {
|
|
|
1220
1366
|
channel: CHANNEL_ID,
|
|
1221
1367
|
accountId,
|
|
1222
1368
|
kind: "config",
|
|
1223
|
-
message: "未配置
|
|
1224
|
-
fix: `设置 channels.${CHANNEL_ID}.accounts.${accountId}.
|
|
1369
|
+
message: "未配置 clientId/clientSecret,账号不可用",
|
|
1370
|
+
fix: `设置 channels.${CHANNEL_ID}.accounts.${accountId}.clientId/clientSecret`,
|
|
1225
1371
|
});
|
|
1226
1372
|
}
|
|
1227
1373
|
if (!enabled) {
|
|
@@ -1239,7 +1385,7 @@ export const httpChannelPlugin = {
|
|
|
1239
1385
|
accountId,
|
|
1240
1386
|
kind: "runtime",
|
|
1241
1387
|
message: "网关运行中但未连接",
|
|
1242
|
-
fix: "检查端口占用、网络连通性和
|
|
1388
|
+
fix: "检查端口占用、网络连通性和 client credentials 配置",
|
|
1243
1389
|
});
|
|
1244
1390
|
}
|
|
1245
1391
|
if (lastError) {
|
|
@@ -1426,8 +1572,8 @@ export const httpChannelPlugin = {
|
|
|
1426
1572
|
},
|
|
1427
1573
|
collectWarnings: ({ account }) => {
|
|
1428
1574
|
const warnings = [];
|
|
1429
|
-
if (!account.
|
|
1430
|
-
warnings.push("未配置
|
|
1575
|
+
if (!account.clientId || !account.clientSecret)
|
|
1576
|
+
warnings.push("未配置 clientId/clientSecret,服务不可用");
|
|
1431
1577
|
if (account.allowFrom?.includes("*"))
|
|
1432
1578
|
warnings.push("allowFrom 设为 *,任何人可发送消息");
|
|
1433
1579
|
if (!account.webhookUrl && account.networkMode !== "cloud") {
|
|
@@ -1536,7 +1682,8 @@ export const httpChannelPlugin = {
|
|
|
1536
1682
|
type: "array",
|
|
1537
1683
|
items: { type: "string" },
|
|
1538
1684
|
},
|
|
1539
|
-
|
|
1685
|
+
clientId: { type: "string" },
|
|
1686
|
+
clientSecret: { type: "string" },
|
|
1540
1687
|
verbose: { type: "boolean" },
|
|
1541
1688
|
limit: { type: "number", minimum: 1, maximum: 50 },
|
|
1542
1689
|
},
|
|
@@ -1569,7 +1716,7 @@ export const httpChannelPlugin = {
|
|
|
1569
1716
|
account: {
|
|
1570
1717
|
name: account.name || null,
|
|
1571
1718
|
enabled: account.enabled,
|
|
1572
|
-
configured: Boolean(account.
|
|
1719
|
+
configured: Boolean(account.clientId && account.clientSecret),
|
|
1573
1720
|
port: account.port,
|
|
1574
1721
|
mode: account.networkMode || "webhook",
|
|
1575
1722
|
dmPolicy: account.dmPolicy || null,
|
|
@@ -1587,14 +1734,21 @@ export const httpChannelPlugin = {
|
|
|
1587
1734
|
});
|
|
1588
1735
|
}
|
|
1589
1736
|
if (action === "auth.probe") {
|
|
1590
|
-
const
|
|
1737
|
+
const probeClientId = readToolString(args, "clientId");
|
|
1738
|
+
const probeClientSecret = readToolString(args, "clientSecret");
|
|
1591
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;
|
|
1592
1746
|
const result = await performAuthLogin({
|
|
1593
1747
|
cfg: toolCfg,
|
|
1594
1748
|
accountId,
|
|
1595
1749
|
runtime: pluginRuntime || {},
|
|
1596
1750
|
verbose,
|
|
1597
|
-
channelInput
|
|
1751
|
+
channelInput,
|
|
1598
1752
|
});
|
|
1599
1753
|
return jsonToolResult({
|
|
1600
1754
|
channel: CHANNEL_ID,
|