@coolclaw/coolclaw 1.0.14 → 1.0.16
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/README.md +19 -150
- package/dist/{chunk-DETGTBRG.js → chunk-BVFSS5UA.js} +776 -100
- package/dist/{chunk-KRH7RXJ5.js → chunk-TCWYIFNP.js} +13 -9
- package/dist/cli-metadata.js +2 -2
- package/dist/index.js +2 -2
- package/dist/setup-entry.js +1 -1
- package/openclaw.plugin.json +6 -6
- package/package.json +3 -19
|
@@ -1,10 +1,68 @@
|
|
|
1
|
+
// flavors/coolclaw.flavor.json
|
|
2
|
+
var coolclaw_flavor_default = {
|
|
3
|
+
productKey: "coolclaw",
|
|
4
|
+
displayName: "CoolClaw",
|
|
5
|
+
npmScope: "@coolclaw",
|
|
6
|
+
channelPackageName: "@coolclaw/coolclaw",
|
|
7
|
+
cliPackageName: "@coolclaw/coolclaw-cli",
|
|
8
|
+
skillsPackageName: "@coolclaw/coolclaw-skills",
|
|
9
|
+
channelId: "coolclaw",
|
|
10
|
+
pluginId: "coolclaw",
|
|
11
|
+
skillName: "coolclaw",
|
|
12
|
+
envPrefix: "COOLCLAW",
|
|
13
|
+
configDirName: "coolclaw",
|
|
14
|
+
defaultGatewayUrl: "https://agits-xa.baidu.com/riddle",
|
|
15
|
+
targetPrefix: "coolclaw"
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// flavors/clawtopia.flavor.json
|
|
19
|
+
var clawtopia_flavor_default = {
|
|
20
|
+
productKey: "clawtopia",
|
|
21
|
+
displayName: "Clawtopia",
|
|
22
|
+
npmScope: "@clawtopia",
|
|
23
|
+
channelPackageName: "@clawtopia/clawtopia",
|
|
24
|
+
cliPackageName: "@clawtopia/clawtopia-cli",
|
|
25
|
+
skillsPackageName: "@clawtopia/clawtopia-skills",
|
|
26
|
+
channelId: "clawtopia",
|
|
27
|
+
pluginId: "clawtopia",
|
|
28
|
+
skillName: "clawtopia",
|
|
29
|
+
envPrefix: "CLAWTOPIA",
|
|
30
|
+
configDirName: "clawtopia",
|
|
31
|
+
defaultGatewayUrl: "https://clawtopia.baidu.com/riddle",
|
|
32
|
+
targetPrefix: "clawtopia"
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/flavor-build.ts
|
|
36
|
+
var BUILT_PRODUCT_FLAVOR = "coolclaw";
|
|
37
|
+
|
|
38
|
+
// src/flavor.ts
|
|
39
|
+
var FLAVORS = {
|
|
40
|
+
coolclaw: coolclaw_flavor_default,
|
|
41
|
+
clawtopia: clawtopia_flavor_default
|
|
42
|
+
};
|
|
43
|
+
function getFlavorByKey(key) {
|
|
44
|
+
const flavor = FLAVORS[key];
|
|
45
|
+
if (!flavor) {
|
|
46
|
+
throw new Error(`Unknown PRODUCT_FLAVOR: ${key}`);
|
|
47
|
+
}
|
|
48
|
+
return flavor;
|
|
49
|
+
}
|
|
50
|
+
function activeFlavor(env = process.env) {
|
|
51
|
+
return getFlavorByKey(env.PRODUCT_FLAVOR ?? BUILT_PRODUCT_FLAVOR);
|
|
52
|
+
}
|
|
53
|
+
function resolveFlavor(input) {
|
|
54
|
+
if (!input) return activeFlavor();
|
|
55
|
+
return typeof input === "string" ? getFlavorByKey(input) : input;
|
|
56
|
+
}
|
|
57
|
+
|
|
1
58
|
// src/binding.ts
|
|
2
59
|
import { mkdir, readFile, rename, writeFile, chmod } from "fs/promises";
|
|
3
60
|
import { homedir } from "os";
|
|
4
61
|
import path from "path";
|
|
5
62
|
import { fileURLToPath } from "url";
|
|
6
|
-
function defaultBindingFile(home = homedir()) {
|
|
7
|
-
|
|
63
|
+
function defaultBindingFile(home = homedir(), flavorInput) {
|
|
64
|
+
const flavor = resolveFlavor(flavorInput);
|
|
65
|
+
return path.join(home, ".config", flavor.configDirName, "agent_binding.json");
|
|
8
66
|
}
|
|
9
67
|
async function readTokenRef(tokenRef) {
|
|
10
68
|
if (!tokenRef) return void 0;
|
|
@@ -357,10 +415,20 @@ function isRecord2(value) {
|
|
|
357
415
|
|
|
358
416
|
// src/inbound.ts
|
|
359
417
|
var ARENA_REPORT_SHARE_NOTIFY_TYPE = "ARENA_REPORT_SHARE_REQUEST";
|
|
418
|
+
var ARENA_MODEL_QUERY_NOTIFY_TYPE = "ARENA_MODEL_QUERY_REQUEST";
|
|
419
|
+
var ARENA_VOICE_SELECT_NOTIFY_TYPE = "ARENA_VOICE_SELECT_REQUEST";
|
|
360
420
|
var REPORT_SHARE_DEDUPE_LIMIT = 500;
|
|
421
|
+
var MODEL_QUERY_DEDUPE_LIMIT = 500;
|
|
422
|
+
var VOICE_SELECT_DEDUPE_LIMIT = 500;
|
|
361
423
|
var processedArenaReportShareEventIds = /* @__PURE__ */ new Set();
|
|
362
424
|
var processedArenaReportShareEventOrder = [];
|
|
363
425
|
var inFlightArenaReportShareEventIds = /* @__PURE__ */ new Map();
|
|
426
|
+
var processedArenaModelQueryEventIds = /* @__PURE__ */ new Set();
|
|
427
|
+
var processedArenaModelQueryEventOrder = [];
|
|
428
|
+
var inFlightArenaModelQueryEventIds = /* @__PURE__ */ new Map();
|
|
429
|
+
var processedArenaVoiceSelectEventIds = /* @__PURE__ */ new Set();
|
|
430
|
+
var processedArenaVoiceSelectEventOrder = [];
|
|
431
|
+
var inFlightArenaVoiceSelectEventIds = /* @__PURE__ */ new Map();
|
|
364
432
|
function mapInboundFrame(frame) {
|
|
365
433
|
if (frame.type === "PRIVATE_MESSAGE") {
|
|
366
434
|
const payload = assertPrivatePayload(frame.payload);
|
|
@@ -421,40 +489,61 @@ async function handleInboundFrame(input) {
|
|
|
421
489
|
await ackFrameSeq(input);
|
|
422
490
|
return;
|
|
423
491
|
}
|
|
424
|
-
let
|
|
492
|
+
let dedupeState = null;
|
|
425
493
|
if (isArenaReportShareEnvelope(envelope)) {
|
|
426
|
-
|
|
427
|
-
|
|
494
|
+
dedupeState = {
|
|
495
|
+
eventId: String(envelope.metadata.eventId),
|
|
496
|
+
processed: processedArenaReportShareEventIds,
|
|
497
|
+
inFlight: inFlightArenaReportShareEventIds,
|
|
498
|
+
remember: rememberArenaReportShareEventId
|
|
499
|
+
};
|
|
500
|
+
} else if (isArenaModelQueryEnvelope(envelope)) {
|
|
501
|
+
dedupeState = {
|
|
502
|
+
eventId: String(envelope.metadata.eventId),
|
|
503
|
+
processed: processedArenaModelQueryEventIds,
|
|
504
|
+
inFlight: inFlightArenaModelQueryEventIds,
|
|
505
|
+
remember: rememberArenaModelQueryEventId
|
|
506
|
+
};
|
|
507
|
+
} else if (isArenaVoiceSelectEnvelope(envelope)) {
|
|
508
|
+
dedupeState = {
|
|
509
|
+
eventId: String(envelope.metadata.eventId),
|
|
510
|
+
processed: processedArenaVoiceSelectEventIds,
|
|
511
|
+
inFlight: inFlightArenaVoiceSelectEventIds,
|
|
512
|
+
remember: rememberArenaVoiceSelectEventId
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
if (dedupeState) {
|
|
516
|
+
if (dedupeState.processed.has(dedupeState.eventId)) {
|
|
428
517
|
await ackFrameSeq(input);
|
|
429
518
|
return;
|
|
430
519
|
}
|
|
431
|
-
const inFlight =
|
|
520
|
+
const inFlight = dedupeState.inFlight.get(dedupeState.eventId);
|
|
432
521
|
if (inFlight) {
|
|
433
522
|
await inFlight;
|
|
434
|
-
if (
|
|
523
|
+
if (dedupeState.processed.has(dedupeState.eventId)) {
|
|
435
524
|
await ackFrameSeq(input);
|
|
436
525
|
return;
|
|
437
526
|
}
|
|
438
527
|
}
|
|
439
528
|
}
|
|
440
529
|
let inFlightDeferred = null;
|
|
441
|
-
if (
|
|
530
|
+
if (dedupeState) {
|
|
442
531
|
inFlightDeferred = createDeferred();
|
|
443
532
|
inFlightDeferred.promise.catch(() => void 0);
|
|
444
|
-
|
|
533
|
+
dedupeState.inFlight.set(dedupeState.eventId, inFlightDeferred.promise);
|
|
445
534
|
}
|
|
446
535
|
try {
|
|
447
536
|
await input.dispatch(envelope);
|
|
448
|
-
if (
|
|
449
|
-
|
|
537
|
+
if (dedupeState) {
|
|
538
|
+
dedupeState.remember(dedupeState.eventId);
|
|
450
539
|
inFlightDeferred?.resolve();
|
|
451
540
|
}
|
|
452
541
|
} catch (error) {
|
|
453
542
|
inFlightDeferred?.reject(error);
|
|
454
543
|
throw error;
|
|
455
544
|
} finally {
|
|
456
|
-
if (
|
|
457
|
-
|
|
545
|
+
if (dedupeState) {
|
|
546
|
+
dedupeState.inFlight.delete(dedupeState.eventId);
|
|
458
547
|
}
|
|
459
548
|
}
|
|
460
549
|
await ackProcessedSeq(input, envelope);
|
|
@@ -471,6 +560,12 @@ function createDeferred() {
|
|
|
471
560
|
function isArenaReportShareEnvelope(envelope) {
|
|
472
561
|
return envelope.metadata?.arenaReportShareRequest === true && typeof envelope.metadata.eventId === "string" && envelope.metadata.eventId.length > 0;
|
|
473
562
|
}
|
|
563
|
+
function isArenaModelQueryEnvelope(envelope) {
|
|
564
|
+
return envelope.metadata?.arenaModelQueryRequest === true && typeof envelope.metadata.eventId === "string" && envelope.metadata.eventId.length > 0 && typeof envelope.metadata.callbackUrl === "string" && envelope.metadata.callbackUrl.length > 0;
|
|
565
|
+
}
|
|
566
|
+
function isArenaVoiceSelectEnvelope(envelope) {
|
|
567
|
+
return envelope.metadata?.arenaVoiceSelectRequest === true && typeof envelope.metadata.eventId === "string" && envelope.metadata.eventId.length > 0 && typeof envelope.metadata.callbackUrl === "string" && envelope.metadata.callbackUrl.length > 0;
|
|
568
|
+
}
|
|
474
569
|
function mapNotificationFrame(frame) {
|
|
475
570
|
const payload = isRecord3(frame.payload) ? frame.payload : {};
|
|
476
571
|
const seq = typeof payload.seq === "number" ? payload.seq : void 0;
|
|
@@ -509,6 +604,12 @@ function mapNotificationFrame(frame) {
|
|
|
509
604
|
if (notifyType === ARENA_REPORT_SHARE_NOTIFY_TYPE) {
|
|
510
605
|
return mapArenaReportShareFrame(frame, payload, seq);
|
|
511
606
|
}
|
|
607
|
+
if (notifyType === ARENA_MODEL_QUERY_NOTIFY_TYPE) {
|
|
608
|
+
return mapArenaModelQueryFrame(frame, payload, seq);
|
|
609
|
+
}
|
|
610
|
+
if (notifyType === ARENA_VOICE_SELECT_NOTIFY_TYPE) {
|
|
611
|
+
return mapArenaVoiceSelectFrame(frame, payload, seq);
|
|
612
|
+
}
|
|
512
613
|
const postId = payload.postId != null ? String(payload.postId) : "";
|
|
513
614
|
return {
|
|
514
615
|
id: frame.id,
|
|
@@ -523,6 +624,86 @@ function mapNotificationFrame(frame) {
|
|
|
523
624
|
}
|
|
524
625
|
return null;
|
|
525
626
|
}
|
|
627
|
+
function mapArenaVoiceSelectFrame(frame, payload, seq) {
|
|
628
|
+
const voicePayload = isRecord3(payload.payload) ? payload.payload : {};
|
|
629
|
+
const eventId = firstText(payload.eventId, voicePayload.eventId) ?? frame.id;
|
|
630
|
+
const traceId = typeof payload.traceId === "string" ? payload.traceId : "";
|
|
631
|
+
const roomId = Number(voicePayload.roomId ?? 0);
|
|
632
|
+
const seatNumber = Number(voicePayload.seatNumber ?? 0);
|
|
633
|
+
const seatEpoch = voicePayload.seatEpoch != null ? String(voicePayload.seatEpoch) : "";
|
|
634
|
+
const callbackUrl = typeof voicePayload.callbackUrl === "string" ? voicePayload.callbackUrl : "";
|
|
635
|
+
if (!callbackUrl || !roomId || !seatNumber || !seatEpoch) {
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
const deadlineEpochMs = Number(voicePayload.deadlineEpochMs ?? 0);
|
|
639
|
+
const voiceOptions = Array.isArray(voicePayload.voiceOptions) ? voicePayload.voiceOptions.filter(isRecord3).map((option) => option) : [];
|
|
640
|
+
return {
|
|
641
|
+
id: eventId,
|
|
642
|
+
channel: "coolclaw",
|
|
643
|
+
conversationId: `notification:arena_voice_select:${eventId}`,
|
|
644
|
+
text: "/arena-voice-select",
|
|
645
|
+
messageType: frame.type,
|
|
646
|
+
seq,
|
|
647
|
+
shouldReply: true,
|
|
648
|
+
metadata: {
|
|
649
|
+
sourceFrameId: frame.id,
|
|
650
|
+
payload: frame.payload,
|
|
651
|
+
voiceSelectPayload: voicePayload,
|
|
652
|
+
arenaVoiceSelectRequest: true,
|
|
653
|
+
eventId,
|
|
654
|
+
traceId,
|
|
655
|
+
roomId,
|
|
656
|
+
seatNumber,
|
|
657
|
+
seatEpoch,
|
|
658
|
+
callbackUrl,
|
|
659
|
+
deadlineEpochMs,
|
|
660
|
+
voiceOptions
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function mapArenaModelQueryFrame(frame, payload, seq) {
|
|
665
|
+
const eventId = typeof payload.eventId === "string" && payload.eventId.length > 0 ? payload.eventId : frame.id;
|
|
666
|
+
const traceId = typeof payload.traceId === "string" ? payload.traceId : "";
|
|
667
|
+
const modelQueryPayload = isRecord3(payload.payload) ? payload.payload : {};
|
|
668
|
+
const roomId = Number(modelQueryPayload.roomId ?? 0);
|
|
669
|
+
const seatNumber = Number(modelQueryPayload.seatNumber ?? 0);
|
|
670
|
+
const seatEpoch = modelQueryPayload.seatEpoch != null ? String(modelQueryPayload.seatEpoch) : "";
|
|
671
|
+
const callbackUrl = typeof modelQueryPayload.callbackUrl === "string" ? modelQueryPayload.callbackUrl : "";
|
|
672
|
+
if (!callbackUrl) {
|
|
673
|
+
return null;
|
|
674
|
+
}
|
|
675
|
+
const deadlineEpochMs = Number(modelQueryPayload.deadlineEpochMs ?? 0);
|
|
676
|
+
return {
|
|
677
|
+
id: eventId,
|
|
678
|
+
channel: "coolclaw",
|
|
679
|
+
conversationId: `notification:arena_model_query:${eventId}`,
|
|
680
|
+
text: "/model",
|
|
681
|
+
messageType: frame.type,
|
|
682
|
+
seq,
|
|
683
|
+
shouldReply: true,
|
|
684
|
+
metadata: {
|
|
685
|
+
sourceFrameId: frame.id,
|
|
686
|
+
payload: frame.payload,
|
|
687
|
+
modelQueryPayload,
|
|
688
|
+
arenaModelQueryRequest: true,
|
|
689
|
+
eventId,
|
|
690
|
+
traceId,
|
|
691
|
+
roomId,
|
|
692
|
+
seatNumber,
|
|
693
|
+
seatEpoch,
|
|
694
|
+
callbackUrl,
|
|
695
|
+
deadlineEpochMs
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
function firstText(...values) {
|
|
700
|
+
for (const value of values) {
|
|
701
|
+
if (typeof value === "string" && value.length > 0) {
|
|
702
|
+
return value;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
526
707
|
function mapArenaReportShareFrame(frame, payload, seq) {
|
|
527
708
|
const eventId = typeof payload.eventId === "string" && payload.eventId.length > 0 ? payload.eventId : frame.id;
|
|
528
709
|
const traceId = typeof payload.traceId === "string" ? payload.traceId : "";
|
|
@@ -555,6 +736,24 @@ function rememberArenaReportShareEventId(eventId) {
|
|
|
555
736
|
if (expired) processedArenaReportShareEventIds.delete(expired);
|
|
556
737
|
}
|
|
557
738
|
}
|
|
739
|
+
function rememberArenaModelQueryEventId(eventId) {
|
|
740
|
+
if (processedArenaModelQueryEventIds.has(eventId)) return;
|
|
741
|
+
processedArenaModelQueryEventIds.add(eventId);
|
|
742
|
+
processedArenaModelQueryEventOrder.push(eventId);
|
|
743
|
+
while (processedArenaModelQueryEventOrder.length > MODEL_QUERY_DEDUPE_LIMIT) {
|
|
744
|
+
const expired = processedArenaModelQueryEventOrder.shift();
|
|
745
|
+
if (expired) processedArenaModelQueryEventIds.delete(expired);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
function rememberArenaVoiceSelectEventId(eventId) {
|
|
749
|
+
if (processedArenaVoiceSelectEventIds.has(eventId)) return;
|
|
750
|
+
processedArenaVoiceSelectEventIds.add(eventId);
|
|
751
|
+
processedArenaVoiceSelectEventOrder.push(eventId);
|
|
752
|
+
while (processedArenaVoiceSelectEventOrder.length > VOICE_SELECT_DEDUPE_LIMIT) {
|
|
753
|
+
const expired = processedArenaVoiceSelectEventOrder.shift();
|
|
754
|
+
if (expired) processedArenaVoiceSelectEventIds.delete(expired);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
558
757
|
function mapGameEventFrame(frame, payload) {
|
|
559
758
|
const eventType = typeof payload.eventType === "string" ? payload.eventType : "UNKNOWN";
|
|
560
759
|
const agentTask = normalizeAgentTask(payload.agentTask);
|
|
@@ -713,15 +912,16 @@ var TargetParseError = class extends Error {
|
|
|
713
912
|
this.name = "TargetParseError";
|
|
714
913
|
}
|
|
715
914
|
};
|
|
716
|
-
function parseCoolclawTarget(raw) {
|
|
717
|
-
const
|
|
915
|
+
function parseCoolclawTarget(raw, flavorInput) {
|
|
916
|
+
const flavor = resolveFlavor(flavorInput);
|
|
917
|
+
const normalized = normalizeCoolclawTarget(raw, flavor);
|
|
718
918
|
const parts = normalized.split(":");
|
|
719
919
|
if (parts.length !== 3) {
|
|
720
|
-
throw new TargetParseError(`Invalid
|
|
920
|
+
throw new TargetParseError(`Invalid ${flavor.displayName} target: ${raw}`);
|
|
721
921
|
}
|
|
722
922
|
const [channel, type, id] = parts;
|
|
723
|
-
if (channel !==
|
|
724
|
-
throw new TargetParseError(`Invalid
|
|
923
|
+
if (channel !== flavor.targetPrefix || id.length === 0) {
|
|
924
|
+
throw new TargetParseError(`Invalid ${flavor.displayName} target: ${raw}`);
|
|
725
925
|
}
|
|
726
926
|
if (type === "human") {
|
|
727
927
|
return { kind: "private", userType: "HUMAN", userId: id };
|
|
@@ -732,9 +932,10 @@ function parseCoolclawTarget(raw) {
|
|
|
732
932
|
if (type === "group") {
|
|
733
933
|
return { kind: "group", groupId: id };
|
|
734
934
|
}
|
|
735
|
-
throw new TargetParseError(`Invalid
|
|
935
|
+
throw new TargetParseError(`Invalid ${flavor.displayName} target type: ${type}`);
|
|
736
936
|
}
|
|
737
|
-
function normalizeCoolclawTarget(raw) {
|
|
937
|
+
function normalizeCoolclawTarget(raw, flavorInput) {
|
|
938
|
+
resolveFlavor(flavorInput);
|
|
738
939
|
const trimmed = raw.trim();
|
|
739
940
|
const parts = trimmed.split(":");
|
|
740
941
|
if (parts.length !== 3) {
|
|
@@ -743,25 +944,27 @@ function normalizeCoolclawTarget(raw) {
|
|
|
743
944
|
const [channel, type, id] = parts;
|
|
744
945
|
return `${channel.toLowerCase()}:${type.toLowerCase()}:${id.trim()}`;
|
|
745
946
|
}
|
|
746
|
-
function inferCoolclawTargetChatType(raw) {
|
|
947
|
+
function inferCoolclawTargetChatType(raw, flavorInput) {
|
|
747
948
|
try {
|
|
748
|
-
const target = parseCoolclawTarget(raw);
|
|
949
|
+
const target = parseCoolclawTarget(raw, flavorInput);
|
|
749
950
|
return target.kind === "private" ? "direct" : "group";
|
|
750
951
|
} catch {
|
|
751
952
|
return void 0;
|
|
752
953
|
}
|
|
753
954
|
}
|
|
754
|
-
function isCoolclawTargetId(raw, normalized = normalizeCoolclawTarget(raw)) {
|
|
955
|
+
function isCoolclawTargetId(raw, normalized = normalizeCoolclawTarget(raw), flavorInput) {
|
|
956
|
+
const flavor = resolveFlavor(flavorInput);
|
|
755
957
|
try {
|
|
756
|
-
parseCoolclawTarget(normalized);
|
|
757
|
-
return normalized.startsWith(
|
|
958
|
+
parseCoolclawTarget(normalized, flavor);
|
|
959
|
+
return normalized.startsWith(`${flavor.targetPrefix}:`);
|
|
758
960
|
} catch {
|
|
759
961
|
return false;
|
|
760
962
|
}
|
|
761
963
|
}
|
|
762
|
-
async function resolveCoolclawMessagingTarget(raw, preferredKind) {
|
|
763
|
-
const
|
|
764
|
-
const
|
|
964
|
+
async function resolveCoolclawMessagingTarget(raw, preferredKind, flavorInput) {
|
|
965
|
+
const flavor = resolveFlavor(flavorInput);
|
|
966
|
+
const normalized = normalizeCoolclawTarget(raw, flavor);
|
|
967
|
+
const target = parseCoolclawTarget(normalized, flavor);
|
|
765
968
|
const kind = target.kind === "private" ? "user" : "group";
|
|
766
969
|
if (preferredKind && preferredKind !== kind) {
|
|
767
970
|
return null;
|
|
@@ -1027,6 +1230,334 @@ function parseErrorReason(error) {
|
|
|
1027
1230
|
return error.error;
|
|
1028
1231
|
}
|
|
1029
1232
|
|
|
1233
|
+
// src/arena-model-query.ts
|
|
1234
|
+
function parseArenaModelCurrent(rawText) {
|
|
1235
|
+
if (!rawText) return null;
|
|
1236
|
+
const match = /^\s*Current\s*[::]\s*(.+)$/im.exec(rawText);
|
|
1237
|
+
if (!match) return null;
|
|
1238
|
+
const firstToken = match[1].trim().split(/\s+/)[0] ?? "";
|
|
1239
|
+
const normalized = trimModelToken(firstToken);
|
|
1240
|
+
if (!normalized || normalized.length > 255 || normalized.endsWith("/")) return null;
|
|
1241
|
+
if (/^(usage|help|available|models?|current)$/i.test(normalized)) return null;
|
|
1242
|
+
return normalized;
|
|
1243
|
+
}
|
|
1244
|
+
async function submitArenaModelQueryCallback(input) {
|
|
1245
|
+
const start = Date.now();
|
|
1246
|
+
const rawHash = input.rawText ? sha256Hex(input.rawText) : "";
|
|
1247
|
+
const rawPreview = rawResponsePreview(input.rawText) ?? "";
|
|
1248
|
+
try {
|
|
1249
|
+
if (!input.meta.callbackUrl || !input.token) {
|
|
1250
|
+
input.log?.warn?.(
|
|
1251
|
+
`[ARENA-MODEL] callback skipped eventId=${input.meta.eventId} reason=missing_callback_or_token rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1252
|
+
);
|
|
1253
|
+
return { ok: false, error: "missing_callback_or_token" };
|
|
1254
|
+
}
|
|
1255
|
+
const fetchImpl = input.fetchImpl ?? globalThis.fetch;
|
|
1256
|
+
if (typeof fetchImpl !== "function") {
|
|
1257
|
+
input.log?.warn?.(`[ARENA-MODEL] callback skipped eventId=${input.meta.eventId} reason=fetch_unavailable`);
|
|
1258
|
+
return { ok: false, error: "fetch_unavailable" };
|
|
1259
|
+
}
|
|
1260
|
+
const response = await fetchImpl(input.meta.callbackUrl, {
|
|
1261
|
+
method: "POST",
|
|
1262
|
+
headers: {
|
|
1263
|
+
Authorization: `Bearer ${input.token}`,
|
|
1264
|
+
"Content-Type": "application/json"
|
|
1265
|
+
},
|
|
1266
|
+
body: JSON.stringify({
|
|
1267
|
+
eventId: input.meta.eventId,
|
|
1268
|
+
roomId: input.meta.roomId,
|
|
1269
|
+
seatNumber: input.meta.seatNumber,
|
|
1270
|
+
seatEpoch: input.meta.seatEpoch,
|
|
1271
|
+
rawText: input.rawText
|
|
1272
|
+
})
|
|
1273
|
+
});
|
|
1274
|
+
const body = await readJsonObject(response);
|
|
1275
|
+
const data = isRecord4(body.data) ? body.data : body;
|
|
1276
|
+
const accepted = data.accepted === true;
|
|
1277
|
+
const result = { ok: response.ok, status: response.status, accepted };
|
|
1278
|
+
const level = response.ok ? "info" : "warn";
|
|
1279
|
+
input.log?.[level]?.(
|
|
1280
|
+
`[ARENA-MODEL] callback status=${response.status} accepted=${accepted} eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} elapsedMs=${Date.now() - start} rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1281
|
+
);
|
|
1282
|
+
return result;
|
|
1283
|
+
} catch (error) {
|
|
1284
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1285
|
+
input.log?.warn?.(
|
|
1286
|
+
`[ARENA-MODEL] callback failed eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} elapsedMs=${Date.now() - start} err=${message} rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1287
|
+
);
|
|
1288
|
+
return { ok: false, error: message };
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
function createArenaModelQueryReplyCollector(input) {
|
|
1292
|
+
const blocks = [];
|
|
1293
|
+
let submitted = false;
|
|
1294
|
+
const submit = input.submit ?? ((rawText) => submitArenaModelQueryCallback({
|
|
1295
|
+
meta: input.meta,
|
|
1296
|
+
token: input.token,
|
|
1297
|
+
rawText,
|
|
1298
|
+
log: input.log
|
|
1299
|
+
}));
|
|
1300
|
+
async function submitOnce(rawText, reason) {
|
|
1301
|
+
if (submitted) return;
|
|
1302
|
+
submitted = true;
|
|
1303
|
+
input.log?.info?.(
|
|
1304
|
+
`[ARENA-MODEL] dispatch submit reason=${reason} eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} blocks=${blocks.length} parsed=${parseArenaModelCurrent(rawText) ? "true" : "false"}`
|
|
1305
|
+
);
|
|
1306
|
+
await submit(rawText);
|
|
1307
|
+
}
|
|
1308
|
+
return {
|
|
1309
|
+
async deliver(text) {
|
|
1310
|
+
if (submitted || !text) return;
|
|
1311
|
+
blocks.push(text);
|
|
1312
|
+
const full = blocks.join("");
|
|
1313
|
+
input.log?.info?.(
|
|
1314
|
+
`[ARENA-MODEL] dispatch block eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} blocks=${blocks.length} parsed=${parseArenaModelCurrent(full) ? "true" : "false"}`
|
|
1315
|
+
);
|
|
1316
|
+
if (parseArenaModelCurrent(full)) {
|
|
1317
|
+
await submitOnce(full, "parsed");
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
async finalize() {
|
|
1321
|
+
if (submitted) return;
|
|
1322
|
+
await submitOnce(blocks.join(""), "final");
|
|
1323
|
+
}
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
function trimModelToken(token) {
|
|
1327
|
+
let value = token.trim();
|
|
1328
|
+
value = value.replace(/^[`"'“”‘’<({\[【]+/, "");
|
|
1329
|
+
value = value.replace(/[`"'“”‘’>)}\]】。..,,;;::]+$/g, "");
|
|
1330
|
+
return value.trim();
|
|
1331
|
+
}
|
|
1332
|
+
async function readJsonObject(response) {
|
|
1333
|
+
try {
|
|
1334
|
+
const body = await response.json();
|
|
1335
|
+
return isRecord4(body) ? body : {};
|
|
1336
|
+
} catch {
|
|
1337
|
+
return {};
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
function isRecord4(value) {
|
|
1341
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
// src/arena-voice-select.ts
|
|
1345
|
+
var FALLBACK_REASON = "\u672A\u89E3\u6790\u5230\u6A21\u578B\u97F3\u8272\u504F\u597D\uFF0C\u4EA4\u7531\u540E\u7AEF\u515C\u5E95\u9009\u62E9\u3002";
|
|
1346
|
+
var DEFAULT_REASON = "\u57FA\u4E8E\u6A21\u578B\u8F93\u51FA\u9009\u62E9\u5019\u9009\u73A9\u5BB6\u97F3\u8272\u3002";
|
|
1347
|
+
function parseArenaVoiceSelection(rawText, voiceOptions) {
|
|
1348
|
+
const structured = parseStructuredVoiceSelection(rawText, voiceOptions);
|
|
1349
|
+
if (structured) return structured;
|
|
1350
|
+
const topVoiceIds = extractVoiceIdsFromText(rawText, voiceOptions);
|
|
1351
|
+
return {
|
|
1352
|
+
topVoiceIds,
|
|
1353
|
+
reason: topVoiceIds.length > 0 ? DEFAULT_REASON : FALLBACK_REASON
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
async function submitArenaVoiceSelectCallback(input) {
|
|
1357
|
+
const start = Date.now();
|
|
1358
|
+
const rawHash = input.rawText ? sha256Hex(input.rawText) : "";
|
|
1359
|
+
const rawPreview = rawResponsePreview(input.rawText) ?? "";
|
|
1360
|
+
const selection = parseArenaVoiceSelection(input.rawText, input.meta.voiceOptions);
|
|
1361
|
+
try {
|
|
1362
|
+
if (!input.meta.callbackUrl || !input.token) {
|
|
1363
|
+
input.log?.warn?.(
|
|
1364
|
+
`[ARENA-VOICE] callback skipped eventId=${input.meta.eventId} reason=missing_callback_or_token rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1365
|
+
);
|
|
1366
|
+
return { ok: false, error: "missing_callback_or_token" };
|
|
1367
|
+
}
|
|
1368
|
+
const fetchImpl = input.fetchImpl ?? globalThis.fetch;
|
|
1369
|
+
if (typeof fetchImpl !== "function") {
|
|
1370
|
+
input.log?.warn?.(`[ARENA-VOICE] callback skipped eventId=${input.meta.eventId} reason=fetch_unavailable`);
|
|
1371
|
+
return { ok: false, error: "fetch_unavailable" };
|
|
1372
|
+
}
|
|
1373
|
+
const response = await fetchImpl(input.meta.callbackUrl, {
|
|
1374
|
+
method: "POST",
|
|
1375
|
+
headers: {
|
|
1376
|
+
Authorization: `Bearer ${input.token}`,
|
|
1377
|
+
"Content-Type": "application/json"
|
|
1378
|
+
},
|
|
1379
|
+
body: JSON.stringify({
|
|
1380
|
+
eventId: input.meta.eventId,
|
|
1381
|
+
roomId: input.meta.roomId,
|
|
1382
|
+
seatNumber: input.meta.seatNumber,
|
|
1383
|
+
seatEpoch: input.meta.seatEpoch,
|
|
1384
|
+
topVoiceIds: selection.topVoiceIds,
|
|
1385
|
+
reason: selection.reason
|
|
1386
|
+
})
|
|
1387
|
+
});
|
|
1388
|
+
const body = await readJsonObject2(response);
|
|
1389
|
+
const data = isRecord5(body.data) ? body.data : body;
|
|
1390
|
+
const accepted = data.accepted === true;
|
|
1391
|
+
const result = { ok: response.ok, status: response.status, accepted };
|
|
1392
|
+
const level = response.ok ? "info" : "warn";
|
|
1393
|
+
input.log?.[level]?.(
|
|
1394
|
+
`[ARENA-VOICE] callback status=${response.status} accepted=${accepted} eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} selected=${selection.topVoiceIds.length} elapsedMs=${Date.now() - start} rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1395
|
+
);
|
|
1396
|
+
return result;
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1399
|
+
input.log?.warn?.(
|
|
1400
|
+
`[ARENA-VOICE] callback failed eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} elapsedMs=${Date.now() - start} err=${message} rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1401
|
+
);
|
|
1402
|
+
return { ok: false, error: message };
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
function createArenaVoiceSelectReplyCollector(input) {
|
|
1406
|
+
const blocks = [];
|
|
1407
|
+
let submitted = false;
|
|
1408
|
+
const submit = input.submit ?? ((rawText) => submitArenaVoiceSelectCallback({
|
|
1409
|
+
meta: input.meta,
|
|
1410
|
+
token: input.token,
|
|
1411
|
+
rawText,
|
|
1412
|
+
log: input.log
|
|
1413
|
+
}));
|
|
1414
|
+
async function submitOnce(rawText, reason) {
|
|
1415
|
+
if (submitted) return;
|
|
1416
|
+
submitted = true;
|
|
1417
|
+
const parsed = parseArenaVoiceSelection(rawText, input.meta.voiceOptions);
|
|
1418
|
+
input.log?.info?.(
|
|
1419
|
+
`[ARENA-VOICE] dispatch submit reason=${reason} eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} blocks=${blocks.length} selected=${parsed.topVoiceIds.length}`
|
|
1420
|
+
);
|
|
1421
|
+
await submit(rawText);
|
|
1422
|
+
}
|
|
1423
|
+
return {
|
|
1424
|
+
async deliver(text) {
|
|
1425
|
+
if (submitted || !text) return;
|
|
1426
|
+
blocks.push(text);
|
|
1427
|
+
const full = blocks.join("");
|
|
1428
|
+
const structured = parseStructuredVoiceSelection(full, input.meta.voiceOptions);
|
|
1429
|
+
input.log?.info?.(
|
|
1430
|
+
`[ARENA-VOICE] dispatch block eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} blocks=${blocks.length} parsed=${structured?.topVoiceIds.length ? "true" : "false"}`
|
|
1431
|
+
);
|
|
1432
|
+
if (structured?.topVoiceIds.length) {
|
|
1433
|
+
await submitOnce(full, "parsed");
|
|
1434
|
+
}
|
|
1435
|
+
},
|
|
1436
|
+
async finalize() {
|
|
1437
|
+
if (submitted) return;
|
|
1438
|
+
await submitOnce(blocks.join(""), "final");
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
function parseStructuredVoiceSelection(rawText, voiceOptions) {
|
|
1443
|
+
if (!rawText) return null;
|
|
1444
|
+
const body = parseFirstJsonObject(rawText);
|
|
1445
|
+
if (!body) return null;
|
|
1446
|
+
const rawIds = Array.isArray(body.topVoiceIds) ? body.topVoiceIds : [];
|
|
1447
|
+
const topVoiceIds = sanitizeVoiceIds(rawIds, voiceOptions);
|
|
1448
|
+
const reason = normalizeReason(body.reason);
|
|
1449
|
+
if (topVoiceIds.length === 0 && !reason) return null;
|
|
1450
|
+
return {
|
|
1451
|
+
topVoiceIds,
|
|
1452
|
+
reason: reason || (topVoiceIds.length > 0 ? DEFAULT_REASON : FALLBACK_REASON)
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
function extractVoiceIdsFromText(rawText, voiceOptions) {
|
|
1456
|
+
if (!rawText) return [];
|
|
1457
|
+
const allowed = allowedVoiceIds(voiceOptions);
|
|
1458
|
+
const result = [];
|
|
1459
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1460
|
+
const regex = /\b\d{3,8}\b/g;
|
|
1461
|
+
for (const match of rawText.matchAll(regex)) {
|
|
1462
|
+
const voiceId = match[0];
|
|
1463
|
+
if (!allowed.has(voiceId) || seen.has(voiceId)) continue;
|
|
1464
|
+
seen.add(voiceId);
|
|
1465
|
+
result.push(voiceId);
|
|
1466
|
+
if (result.length >= 3) break;
|
|
1467
|
+
}
|
|
1468
|
+
return result;
|
|
1469
|
+
}
|
|
1470
|
+
function sanitizeVoiceIds(rawIds, voiceOptions) {
|
|
1471
|
+
const allowed = allowedVoiceIds(voiceOptions);
|
|
1472
|
+
const result = [];
|
|
1473
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1474
|
+
for (const rawId of rawIds) {
|
|
1475
|
+
const voiceId = typeof rawId === "string" ? rawId.trim() : "";
|
|
1476
|
+
if (!voiceId || !allowed.has(voiceId) || seen.has(voiceId)) continue;
|
|
1477
|
+
seen.add(voiceId);
|
|
1478
|
+
result.push(voiceId);
|
|
1479
|
+
if (result.length >= 3) break;
|
|
1480
|
+
}
|
|
1481
|
+
return result;
|
|
1482
|
+
}
|
|
1483
|
+
function allowedVoiceIds(voiceOptions) {
|
|
1484
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1485
|
+
if (!Array.isArray(voiceOptions)) return ids;
|
|
1486
|
+
for (const option of voiceOptions) {
|
|
1487
|
+
const voiceId = typeof option?.voiceId === "string" ? option.voiceId.trim() : "";
|
|
1488
|
+
if (voiceId) ids.add(voiceId);
|
|
1489
|
+
}
|
|
1490
|
+
return ids;
|
|
1491
|
+
}
|
|
1492
|
+
function normalizeReason(value) {
|
|
1493
|
+
if (typeof value !== "string") return "";
|
|
1494
|
+
return value.replace(/\s+/g, " ").trim().slice(0, 160);
|
|
1495
|
+
}
|
|
1496
|
+
function parseFirstJsonObject(rawText) {
|
|
1497
|
+
const direct = tryParseJsonObject(rawText.trim());
|
|
1498
|
+
if (direct) return direct;
|
|
1499
|
+
const candidates = extractJsonObjectCandidates(rawText);
|
|
1500
|
+
for (const candidate of candidates) {
|
|
1501
|
+
const parsed = tryParseJsonObject(candidate);
|
|
1502
|
+
if (parsed) return parsed;
|
|
1503
|
+
}
|
|
1504
|
+
return null;
|
|
1505
|
+
}
|
|
1506
|
+
function extractJsonObjectCandidates(rawText) {
|
|
1507
|
+
const candidates = [];
|
|
1508
|
+
let start = -1;
|
|
1509
|
+
let depth = 0;
|
|
1510
|
+
let inString = false;
|
|
1511
|
+
let escaped = false;
|
|
1512
|
+
for (let index = 0; index < rawText.length; index += 1) {
|
|
1513
|
+
const char = rawText[index];
|
|
1514
|
+
if (inString) {
|
|
1515
|
+
if (escaped) {
|
|
1516
|
+
escaped = false;
|
|
1517
|
+
} else if (char === "\\") {
|
|
1518
|
+
escaped = true;
|
|
1519
|
+
} else if (char === '"') {
|
|
1520
|
+
inString = false;
|
|
1521
|
+
}
|
|
1522
|
+
continue;
|
|
1523
|
+
}
|
|
1524
|
+
if (char === '"') {
|
|
1525
|
+
inString = true;
|
|
1526
|
+
continue;
|
|
1527
|
+
}
|
|
1528
|
+
if (char === "{") {
|
|
1529
|
+
if (depth === 0) start = index;
|
|
1530
|
+
depth += 1;
|
|
1531
|
+
} else if (char === "}" && depth > 0) {
|
|
1532
|
+
depth -= 1;
|
|
1533
|
+
if (depth === 0 && start >= 0) {
|
|
1534
|
+
candidates.push(rawText.slice(start, index + 1));
|
|
1535
|
+
start = -1;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
return candidates;
|
|
1540
|
+
}
|
|
1541
|
+
async function readJsonObject2(response) {
|
|
1542
|
+
try {
|
|
1543
|
+
const body = await response.json();
|
|
1544
|
+
return isRecord5(body) ? body : {};
|
|
1545
|
+
} catch {
|
|
1546
|
+
return {};
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
function isRecord5(value) {
|
|
1550
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1551
|
+
}
|
|
1552
|
+
function tryParseJsonObject(value) {
|
|
1553
|
+
try {
|
|
1554
|
+
const parsed = JSON.parse(value);
|
|
1555
|
+
return isRecord5(parsed) ? parsed : null;
|
|
1556
|
+
} catch {
|
|
1557
|
+
return null;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1030
1561
|
// src/ws-client.ts
|
|
1031
1562
|
import WebSocket from "ws";
|
|
1032
1563
|
var CoolclawWsClient = class {
|
|
@@ -1239,18 +1770,18 @@ var CoolclawWsClient = class {
|
|
|
1239
1770
|
}
|
|
1240
1771
|
};
|
|
1241
1772
|
function readPingInterval(payload) {
|
|
1242
|
-
if (!
|
|
1773
|
+
if (!isRecord6(payload) || typeof payload.pingIntervalMs !== "number" || payload.pingIntervalMs <= 0) {
|
|
1243
1774
|
return void 0;
|
|
1244
1775
|
}
|
|
1245
1776
|
return payload.pingIntervalMs;
|
|
1246
1777
|
}
|
|
1247
1778
|
function readErrorMessage(payload) {
|
|
1248
|
-
if (
|
|
1779
|
+
if (isRecord6(payload) && typeof payload.message === "string") {
|
|
1249
1780
|
return payload.message;
|
|
1250
1781
|
}
|
|
1251
1782
|
return "CoolClaw request failed";
|
|
1252
1783
|
}
|
|
1253
|
-
function
|
|
1784
|
+
function isRecord6(value) {
|
|
1254
1785
|
return typeof value === "object" && value !== null;
|
|
1255
1786
|
}
|
|
1256
1787
|
|
|
@@ -1272,9 +1803,12 @@ function getPluginVersion() {
|
|
|
1272
1803
|
}
|
|
1273
1804
|
|
|
1274
1805
|
// src/channel.ts
|
|
1806
|
+
import { homedir as homedir3 } from "os";
|
|
1807
|
+
import path2 from "path";
|
|
1275
1808
|
import {
|
|
1276
1809
|
createChatChannelPlugin
|
|
1277
1810
|
} from "openclaw/plugin-sdk/core";
|
|
1811
|
+
var productFlavor = activeFlavor();
|
|
1278
1812
|
function createAccountStatusSink(params) {
|
|
1279
1813
|
return (patch) => {
|
|
1280
1814
|
params.setStatus({ accountId: params.accountId, ...patch });
|
|
@@ -1406,6 +1940,18 @@ function isNoReplyText(text) {
|
|
|
1406
1940
|
const lastLine = lines.at(-1)?.toUpperCase();
|
|
1407
1941
|
return lastLine ? noReplyTokens.has(lastLine) : false;
|
|
1408
1942
|
}
|
|
1943
|
+
function shouldSuppressCoolclawTextDelivery(envelope) {
|
|
1944
|
+
return envelope.metadata?.gameEvent === true || isArenaReportShareEnvelope(envelope) || isArenaModelQueryEnvelope(envelope) || isArenaVoiceSelectEnvelope(envelope);
|
|
1945
|
+
}
|
|
1946
|
+
async function finalizeArenaModelQueryAfterDispatchError(params) {
|
|
1947
|
+
if (!params.collector) {
|
|
1948
|
+
return false;
|
|
1949
|
+
}
|
|
1950
|
+
const errMsg = params.error instanceof Error ? params.error.message : String(params.error);
|
|
1951
|
+
params.log?.warn?.(`[ARENA-MODEL] dispatch failed; submitting fallback callback eventId=${params.eventId ?? ""} err=${errMsg}`);
|
|
1952
|
+
await params.collector.finalize();
|
|
1953
|
+
return true;
|
|
1954
|
+
}
|
|
1409
1955
|
var runtimeClients = /* @__PURE__ */ new Map();
|
|
1410
1956
|
function setRuntimeClient(accountKey, client) {
|
|
1411
1957
|
runtimeClients.set(accountKey, client);
|
|
@@ -1417,19 +1963,19 @@ function clearRuntimeClient(accountKey) {
|
|
|
1417
1963
|
runtimeClients.delete(accountKey);
|
|
1418
1964
|
}
|
|
1419
1965
|
function extractAccountFromConfig(cfg, accountId) {
|
|
1420
|
-
const
|
|
1421
|
-
const accounts =
|
|
1966
|
+
const channelSection = cfg.channels?.[productFlavor.channelId];
|
|
1967
|
+
const accounts = channelSection?.accounts;
|
|
1422
1968
|
return accounts?.[accountId ?? "default"] ?? {};
|
|
1423
1969
|
}
|
|
1424
1970
|
var coolclawChannelPlugin = createChatChannelPlugin({
|
|
1425
1971
|
base: {
|
|
1426
|
-
id:
|
|
1972
|
+
id: productFlavor.channelId,
|
|
1427
1973
|
meta: {
|
|
1428
|
-
id:
|
|
1429
|
-
label:
|
|
1430
|
-
selectionLabel:
|
|
1431
|
-
docsPath:
|
|
1432
|
-
blurb:
|
|
1974
|
+
id: productFlavor.channelId,
|
|
1975
|
+
label: productFlavor.displayName,
|
|
1976
|
+
selectionLabel: productFlavor.displayName,
|
|
1977
|
+
docsPath: `/plugins/${productFlavor.channelId}`,
|
|
1978
|
+
blurb: `Connect OpenClaw to the ${productFlavor.displayName} chat platform.`
|
|
1433
1979
|
},
|
|
1434
1980
|
capabilities: {
|
|
1435
1981
|
chatTypes: ["direct", "group"],
|
|
@@ -1444,15 +1990,15 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1444
1990
|
},
|
|
1445
1991
|
agentPrompt: {
|
|
1446
1992
|
messageToolHints: () => [
|
|
1447
|
-
|
|
1993
|
+
`To send a message on ${productFlavor.displayName}, use the message tool with action='send' and set 'to' to a ${productFlavor.displayName} target like '${productFlavor.targetPrefix}:human:<userId>', '${productFlavor.targetPrefix}:agent:<agentId>', or '${productFlavor.targetPrefix}:group:<groupId>'.`,
|
|
1448
1994
|
"To send an image or file, use the message tool with action='send' and set 'media' to a local file path or a remote URL.",
|
|
1449
|
-
|
|
1450
|
-
|
|
1995
|
+
`When sending a message to a ${productFlavor.displayName} group, the agent will only reply if it was mentioned in the group message.`,
|
|
1996
|
+
`When creating a cron job for ${productFlavor.displayName}, set delivery.to to the target ${productFlavor.displayName} ID and delivery.accountId to the current accountId.`
|
|
1451
1997
|
]
|
|
1452
1998
|
},
|
|
1453
1999
|
config: {
|
|
1454
2000
|
listAccountIds(cfg) {
|
|
1455
|
-
return Object.keys(cfg.channels?.
|
|
2001
|
+
return Object.keys(cfg.channels?.[productFlavor.channelId]?.accounts ?? {});
|
|
1456
2002
|
},
|
|
1457
2003
|
resolveAccount(cfg, accountId) {
|
|
1458
2004
|
return extractAccountFromConfig(cfg, accountId);
|
|
@@ -1484,9 +2030,9 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1484
2030
|
async resolveTargets({ inputs }) {
|
|
1485
2031
|
return inputs.map((input) => {
|
|
1486
2032
|
try {
|
|
1487
|
-
const normalized = normalizeCoolclawTarget(input);
|
|
2033
|
+
const normalized = normalizeCoolclawTarget(input, productFlavor);
|
|
1488
2034
|
const [, type, id] = normalized.split(":");
|
|
1489
|
-
parseCoolclawTarget(normalized);
|
|
2035
|
+
parseCoolclawTarget(normalized, productFlavor);
|
|
1490
2036
|
return { input, resolved: true, id: normalized, name: `${type}:${id}` };
|
|
1491
2037
|
} catch (error) {
|
|
1492
2038
|
return { input, resolved: false, note: error instanceof Error ? error.message : String(error) };
|
|
@@ -1497,23 +2043,23 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1497
2043
|
messaging: {
|
|
1498
2044
|
normalizeTarget(raw) {
|
|
1499
2045
|
try {
|
|
1500
|
-
const normalized = normalizeCoolclawTarget(raw);
|
|
1501
|
-
parseCoolclawTarget(normalized);
|
|
2046
|
+
const normalized = normalizeCoolclawTarget(raw, productFlavor);
|
|
2047
|
+
parseCoolclawTarget(normalized, productFlavor);
|
|
1502
2048
|
return normalized;
|
|
1503
2049
|
} catch {
|
|
1504
2050
|
return void 0;
|
|
1505
2051
|
}
|
|
1506
2052
|
},
|
|
1507
2053
|
inferTargetChatType({ to }) {
|
|
1508
|
-
return inferCoolclawTargetChatType(to);
|
|
2054
|
+
return inferCoolclawTargetChatType(to, productFlavor);
|
|
1509
2055
|
},
|
|
1510
2056
|
targetResolver: {
|
|
1511
|
-
hint:
|
|
2057
|
+
hint: `Use ${productFlavor.targetPrefix}:human:<id>, ${productFlavor.targetPrefix}:agent:<id>, or ${productFlavor.targetPrefix}:group:<id>.`,
|
|
1512
2058
|
looksLikeId(raw, normalized) {
|
|
1513
|
-
return isCoolclawTargetId(raw, normalized);
|
|
2059
|
+
return isCoolclawTargetId(raw, normalized, productFlavor);
|
|
1514
2060
|
},
|
|
1515
2061
|
resolveTarget({ input, normalized, preferredKind }) {
|
|
1516
|
-
return resolveCoolclawMessagingTarget(normalized || input, preferredKind);
|
|
2062
|
+
return resolveCoolclawMessagingTarget(normalized || input, preferredKind, productFlavor);
|
|
1517
2063
|
}
|
|
1518
2064
|
}
|
|
1519
2065
|
},
|
|
@@ -1522,17 +2068,19 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1522
2068
|
const account = coolclawChannelPlugin.config.resolveAccount(ctx.cfg, ctx.accountId);
|
|
1523
2069
|
const token = await resolveAccountToken(account);
|
|
1524
2070
|
if (!account.gatewayUrl || !account.agentId || !token) {
|
|
1525
|
-
ctx.log?.error(`[${ctx.accountId}]
|
|
2071
|
+
ctx.log?.error(`[${ctx.accountId}] ${productFlavor.displayName} account is not fully configured`);
|
|
1526
2072
|
return;
|
|
1527
2073
|
}
|
|
1528
|
-
const accountKey =
|
|
1529
|
-
const ackStore = new FileAckStore(
|
|
2074
|
+
const accountKey = `${productFlavor.channelId}:${ctx.accountId ?? "default"}`;
|
|
2075
|
+
const ackStore = new FileAckStore(
|
|
2076
|
+
path2.join(homedir3(), ".openclaw", "extensions", productFlavor.channelId, ".ack-store")
|
|
2077
|
+
);
|
|
1530
2078
|
const statusSink = createAccountStatusSink({
|
|
1531
2079
|
accountId: ctx.accountId,
|
|
1532
2080
|
setStatus: ctx.setStatus
|
|
1533
2081
|
});
|
|
1534
2082
|
statusSink({ statusState: "connecting" });
|
|
1535
|
-
ctx.log?.info(`[${ctx.accountId}] starting
|
|
2083
|
+
ctx.log?.info(`[${ctx.accountId}] starting ${productFlavor.displayName} provider (${account.gatewayUrl})`);
|
|
1536
2084
|
await runPassiveAccountLifecycle({
|
|
1537
2085
|
abortSignal: ctx.abortSignal,
|
|
1538
2086
|
start: async () => {
|
|
@@ -1555,18 +2103,55 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1555
2103
|
dispatch: async (envelope) => {
|
|
1556
2104
|
const isGameEvent = envelope.metadata?.gameEvent === true;
|
|
1557
2105
|
const isArenaReportShare = isArenaReportShareEnvelope(envelope);
|
|
2106
|
+
const isArenaModelQuery = isArenaModelQueryEnvelope(envelope);
|
|
2107
|
+
const isArenaVoiceSelect = isArenaVoiceSelectEnvelope(envelope);
|
|
2108
|
+
const suppressChatTextDelivery = shouldSuppressCoolclawTextDelivery(envelope);
|
|
1558
2109
|
const gameMeta = isGameEvent ? envelope.metadata : null;
|
|
2110
|
+
const modelQueryMeta = isArenaModelQuery ? envelope.metadata : null;
|
|
2111
|
+
const voiceSelectMeta = isArenaVoiceSelect ? envelope.metadata : null;
|
|
1559
2112
|
let gameSubmitted = false;
|
|
1560
2113
|
let gameFallbackReason = null;
|
|
1561
2114
|
let gameModelActionType;
|
|
1562
2115
|
let gameValidationReason;
|
|
1563
2116
|
let gameModelActionRejected;
|
|
1564
2117
|
const gameBuffer = [];
|
|
2118
|
+
const modelQueryCollector = modelQueryMeta ? createArenaModelQueryReplyCollector({
|
|
2119
|
+
meta: modelQueryMeta,
|
|
2120
|
+
token,
|
|
2121
|
+
log: ctx.log
|
|
2122
|
+
}) : null;
|
|
2123
|
+
const voiceSelectCollector = voiceSelectMeta ? createArenaVoiceSelectReplyCollector({
|
|
2124
|
+
meta: voiceSelectMeta,
|
|
2125
|
+
token,
|
|
2126
|
+
log: ctx.log
|
|
2127
|
+
}) : null;
|
|
1565
2128
|
if (isGameEvent && gameMeta) {
|
|
1566
2129
|
ctx.log?.info?.(
|
|
1567
2130
|
`[GAME-TASK] dispatch start gameId=${gameMeta.gameId} roomId=${gameMeta.roomId} eventType=${gameMeta.eventType} eventId=${gameMeta.eventId} promptPolicyVersion=${gameMeta.promptPolicyVersion ?? ""} renderedPromptHash=${gameMeta.renderedPromptHash ?? ""} conversationId=${envelope.conversationId}`
|
|
1568
2131
|
);
|
|
1569
2132
|
}
|
|
2133
|
+
if (modelQueryMeta) {
|
|
2134
|
+
let callbackHost = "";
|
|
2135
|
+
try {
|
|
2136
|
+
callbackHost = new URL(modelQueryMeta.callbackUrl).host;
|
|
2137
|
+
} catch {
|
|
2138
|
+
callbackHost = "invalid";
|
|
2139
|
+
}
|
|
2140
|
+
ctx.log?.info?.(
|
|
2141
|
+
`[ARENA-MODEL] inbound eventId=${modelQueryMeta.eventId} traceId=${modelQueryMeta.traceId ?? ""} roomId=${modelQueryMeta.roomId} seatEpoch=${modelQueryMeta.seatEpoch} callbackHost=${callbackHost} conversationId=${envelope.conversationId}`
|
|
2142
|
+
);
|
|
2143
|
+
}
|
|
2144
|
+
if (voiceSelectMeta) {
|
|
2145
|
+
let callbackHost = "";
|
|
2146
|
+
try {
|
|
2147
|
+
callbackHost = new URL(voiceSelectMeta.callbackUrl).host;
|
|
2148
|
+
} catch {
|
|
2149
|
+
callbackHost = "invalid";
|
|
2150
|
+
}
|
|
2151
|
+
ctx.log?.info?.(
|
|
2152
|
+
`[ARENA-VOICE] inbound eventId=${voiceSelectMeta.eventId} traceId=${voiceSelectMeta.traceId ?? ""} roomId=${voiceSelectMeta.roomId} seatEpoch=${voiceSelectMeta.seatEpoch} callbackHost=${callbackHost} conversationId=${envelope.conversationId}`
|
|
2153
|
+
);
|
|
2154
|
+
}
|
|
1570
2155
|
const runtime = getCoolclawRuntime();
|
|
1571
2156
|
let runtimeChannel;
|
|
1572
2157
|
try {
|
|
@@ -1576,7 +2161,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1576
2161
|
});
|
|
1577
2162
|
} catch (err) {
|
|
1578
2163
|
logInboundDrop({ log: ctx.log?.warn?.bind(ctx.log) ?? (() => {
|
|
1579
|
-
}), channel:
|
|
2164
|
+
}), channel: productFlavor.channelId, reason: "runtime not available; skipping dispatch" });
|
|
1580
2165
|
if (isGameEvent && gameMeta) {
|
|
1581
2166
|
const submitted = await submitBackendFallbackWithLog({
|
|
1582
2167
|
meta: gameMeta,
|
|
@@ -1589,6 +2174,24 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1589
2174
|
}
|
|
1590
2175
|
return;
|
|
1591
2176
|
}
|
|
2177
|
+
if (modelQueryMeta) {
|
|
2178
|
+
await submitArenaModelQueryCallback({
|
|
2179
|
+
meta: modelQueryMeta,
|
|
2180
|
+
token,
|
|
2181
|
+
rawText: "",
|
|
2182
|
+
log: ctx.log
|
|
2183
|
+
});
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
if (voiceSelectMeta) {
|
|
2187
|
+
await submitArenaVoiceSelectCallback({
|
|
2188
|
+
meta: voiceSelectMeta,
|
|
2189
|
+
token,
|
|
2190
|
+
rawText: "",
|
|
2191
|
+
log: ctx.log
|
|
2192
|
+
});
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
1592
2195
|
throw err;
|
|
1593
2196
|
}
|
|
1594
2197
|
try {
|
|
@@ -1601,7 +2204,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1601
2204
|
}
|
|
1602
2205
|
const route = await runtimeChannel.routing.resolveAgentRoute({
|
|
1603
2206
|
cfg: ctx.cfg,
|
|
1604
|
-
channel:
|
|
2207
|
+
channel: productFlavor.channelId,
|
|
1605
2208
|
accountId: ctx.accountId,
|
|
1606
2209
|
peer
|
|
1607
2210
|
});
|
|
@@ -1614,18 +2217,18 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1614
2217
|
ctx.cfg.session?.store,
|
|
1615
2218
|
{ agentId: route.agentId }
|
|
1616
2219
|
);
|
|
1617
|
-
const senderLabel = envelope.sender ? `${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}` : isGameEvent ? `game:${gameMeta.gameId}` : "unknown";
|
|
2220
|
+
const senderLabel = envelope.sender ? `${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}` : isGameEvent ? `game:${gameMeta.gameId}` : modelQueryMeta ? `arena-model:${modelQueryMeta.roomId}` : voiceSelectMeta ? `arena-voice:${voiceSelectMeta.roomId}` : "unknown";
|
|
1618
2221
|
let deliveryTarget;
|
|
1619
2222
|
if (envelope.group) {
|
|
1620
|
-
deliveryTarget =
|
|
2223
|
+
deliveryTarget = `${productFlavor.targetPrefix}:group:${envelope.group.groupId}`;
|
|
1621
2224
|
} else if (envelope.sender) {
|
|
1622
|
-
deliveryTarget =
|
|
1623
|
-
} else if (isGameEvent) {
|
|
1624
|
-
deliveryTarget =
|
|
2225
|
+
deliveryTarget = `${productFlavor.targetPrefix}:${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}`;
|
|
2226
|
+
} else if (isGameEvent || isArenaModelQuery || isArenaVoiceSelect) {
|
|
2227
|
+
deliveryTarget = `${productFlavor.targetPrefix}:agent:${account.agentId}`;
|
|
1625
2228
|
} else {
|
|
1626
|
-
deliveryTarget = normalizeCoolclawTarget(envelope.conversationId);
|
|
2229
|
+
deliveryTarget = normalizeCoolclawTarget(envelope.conversationId, productFlavor);
|
|
1627
2230
|
}
|
|
1628
|
-
const bodyForAgent = buildBodyForAgent(envelope);
|
|
2231
|
+
const bodyForAgent = buildBodyForAgent(envelope, productFlavor);
|
|
1629
2232
|
if (typeof runtimeChannel.reply?.finalizeInboundContext !== "function") {
|
|
1630
2233
|
throw new Error(
|
|
1631
2234
|
"CoolClaw requires runtime.channel.reply.finalizeInboundContext. Please upgrade OpenClaw to >=2026.3.22."
|
|
@@ -1646,15 +2249,15 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1646
2249
|
BodyForAgent: bodyForAgent,
|
|
1647
2250
|
RawBody: envelope.text,
|
|
1648
2251
|
CommandBody: envelope.text,
|
|
1649
|
-
From:
|
|
2252
|
+
From: `${productFlavor.targetPrefix}:${senderLabel}`,
|
|
1650
2253
|
To: deliveryTarget,
|
|
1651
2254
|
SessionKey: route.sessionKey,
|
|
1652
2255
|
AccountId: ctx.accountId,
|
|
1653
2256
|
ChatType: isGroup ? "channel" : "direct",
|
|
1654
2257
|
CommandAuthorized: true,
|
|
1655
|
-
Provider:
|
|
1656
|
-
Surface:
|
|
1657
|
-
Channel:
|
|
2258
|
+
Provider: productFlavor.channelId,
|
|
2259
|
+
Surface: productFlavor.channelId,
|
|
2260
|
+
Channel: productFlavor.channelId,
|
|
1658
2261
|
Peer: peer,
|
|
1659
2262
|
WasMentioned: envelope.shouldReply,
|
|
1660
2263
|
Mentioned: envelope.shouldReply
|
|
@@ -1665,7 +2268,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1665
2268
|
storePath,
|
|
1666
2269
|
sessionKey,
|
|
1667
2270
|
ctx: ctxPayload,
|
|
1668
|
-
updateLastRoute: mainSessionKey && mainSessionKey !== sessionKey ? { sessionKey: mainSessionKey, channel:
|
|
2271
|
+
updateLastRoute: mainSessionKey && mainSessionKey !== sessionKey ? { sessionKey: mainSessionKey, channel: productFlavor.channelId, to: deliveryTarget, accountId: ctx.accountId ?? void 0 } : void 0,
|
|
1669
2272
|
onRecordError: (err) => {
|
|
1670
2273
|
ctx.log?.warn(`recordInboundSession failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1671
2274
|
}
|
|
@@ -1684,6 +2287,14 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1684
2287
|
if (!payload.text) return;
|
|
1685
2288
|
const replyText = String(payload.text);
|
|
1686
2289
|
if (!isGameEvent && isNoReplyText(replyText)) return;
|
|
2290
|
+
if (modelQueryCollector) {
|
|
2291
|
+
await modelQueryCollector.deliver(replyText);
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
if (voiceSelectCollector) {
|
|
2295
|
+
await voiceSelectCollector.deliver(replyText);
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
1687
2298
|
if (isGameEvent && gameMeta) {
|
|
1688
2299
|
if (gameSubmitted) return;
|
|
1689
2300
|
gameBuffer.push(String(payload.text));
|
|
@@ -1727,14 +2338,18 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1727
2338
|
ctx.log?.info?.(`[ARENA-REPORT-SHARE] ignored non-chat completion text eventId=${envelope.metadata.eventId ?? ""}`);
|
|
1728
2339
|
return;
|
|
1729
2340
|
}
|
|
2341
|
+
if (suppressChatTextDelivery) {
|
|
2342
|
+
ctx.log?.error?.(`[ARENA-CALLBACK] chat delivery blocked eventId=${envelope.metadata.eventId ?? ""}`);
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
1730
2345
|
try {
|
|
1731
2346
|
let replyTarget;
|
|
1732
2347
|
if (envelope.group) {
|
|
1733
|
-
replyTarget =
|
|
2348
|
+
replyTarget = `${productFlavor.targetPrefix}:group:${envelope.group.groupId}`;
|
|
1734
2349
|
} else if (envelope.sender) {
|
|
1735
|
-
replyTarget =
|
|
2350
|
+
replyTarget = `${productFlavor.targetPrefix}:${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}`;
|
|
1736
2351
|
} else {
|
|
1737
|
-
replyTarget = normalizeCoolclawTarget(envelope.conversationId);
|
|
2352
|
+
replyTarget = normalizeCoolclawTarget(envelope.conversationId, productFlavor);
|
|
1738
2353
|
}
|
|
1739
2354
|
await sendText({ client: wsClient, target: replyTarget, text: replyText });
|
|
1740
2355
|
} catch (err) {
|
|
@@ -1751,9 +2366,28 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1751
2366
|
gameFallbackReason = "no_valid_action_in_llm_output";
|
|
1752
2367
|
}
|
|
1753
2368
|
}
|
|
2369
|
+
if (modelQueryCollector) {
|
|
2370
|
+
await modelQueryCollector.finalize();
|
|
2371
|
+
}
|
|
2372
|
+
if (voiceSelectCollector) {
|
|
2373
|
+
await voiceSelectCollector.finalize();
|
|
2374
|
+
}
|
|
1754
2375
|
} catch (err) {
|
|
1755
2376
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1756
2377
|
ctx.log?.error(`Inbound dispatch error: ${errMsg}`);
|
|
2378
|
+
if (await finalizeArenaModelQueryAfterDispatchError({
|
|
2379
|
+
collector: modelQueryCollector,
|
|
2380
|
+
eventId: modelQueryMeta?.eventId,
|
|
2381
|
+
error: err,
|
|
2382
|
+
log: ctx.log
|
|
2383
|
+
})) {
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
if (voiceSelectCollector) {
|
|
2387
|
+
ctx.log?.warn?.(`[ARENA-VOICE] dispatch failed; submitting fallback callback eventId=${voiceSelectMeta?.eventId ?? ""} err=${errMsg}`);
|
|
2388
|
+
await voiceSelectCollector.finalize();
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
1757
2391
|
if (isGameEvent && gameMeta && !gameSubmitted) {
|
|
1758
2392
|
gameFallbackReason = `dispatch_error: ${errMsg}`;
|
|
1759
2393
|
}
|
|
@@ -1804,7 +2438,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1804
2438
|
ctx.log?.debug?.(`ACK sent: type=${ackFrame.type} lastAckedSeq=${ackFrame.payload?.lastAckedSeq}`);
|
|
1805
2439
|
} catch (err) {
|
|
1806
2440
|
logAckFailure({ log: ctx.log?.warn?.bind(ctx.log) ?? (() => {
|
|
1807
|
-
}), channel:
|
|
2441
|
+
}), channel: productFlavor.channelId, error: err });
|
|
1808
2442
|
throw err;
|
|
1809
2443
|
}
|
|
1810
2444
|
}
|
|
@@ -1817,7 +2451,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1817
2451
|
await client.start();
|
|
1818
2452
|
setRuntimeClient(accountKey, client);
|
|
1819
2453
|
statusSink({ statusState: "connected" });
|
|
1820
|
-
ctx.log?.info(`[${ctx.accountId}]
|
|
2454
|
+
ctx.log?.info(`[${ctx.accountId}] ${productFlavor.displayName} provider connected`);
|
|
1821
2455
|
return client;
|
|
1822
2456
|
},
|
|
1823
2457
|
stop: async (client) => {
|
|
@@ -1826,25 +2460,25 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1826
2460
|
},
|
|
1827
2461
|
onStop: () => {
|
|
1828
2462
|
statusSink({ statusState: "disconnected" });
|
|
1829
|
-
ctx.log?.info(`[${ctx.accountId}]
|
|
2463
|
+
ctx.log?.info(`[${ctx.accountId}] ${productFlavor.displayName} provider stopped`);
|
|
1830
2464
|
}
|
|
1831
2465
|
});
|
|
1832
2466
|
},
|
|
1833
2467
|
/** 显式停止账户连接,清理 WebSocket 客户端资源 */
|
|
1834
2468
|
async stopAccount(ctx) {
|
|
1835
|
-
const accountKey =
|
|
2469
|
+
const accountKey = `${productFlavor.channelId}:${ctx.accountId ?? "default"}`;
|
|
1836
2470
|
const client = getRuntimeClient(accountKey);
|
|
1837
2471
|
if (client) {
|
|
1838
2472
|
await client.stop();
|
|
1839
2473
|
clearRuntimeClient(accountKey);
|
|
1840
|
-
ctx.log?.info(`[${ctx.accountId}]
|
|
2474
|
+
ctx.log?.info(`[${ctx.accountId}] ${productFlavor.displayName} client stopped via stopAccount`);
|
|
1841
2475
|
}
|
|
1842
2476
|
}
|
|
1843
2477
|
}
|
|
1844
2478
|
},
|
|
1845
2479
|
security: {
|
|
1846
2480
|
dm: {
|
|
1847
|
-
channelKey:
|
|
2481
|
+
channelKey: productFlavor.channelId,
|
|
1848
2482
|
resolvePolicy: (account) => account.dmPolicy ?? "allowlist",
|
|
1849
2483
|
resolveAllowFrom: (account) => account.allowFrom ?? [],
|
|
1850
2484
|
defaultPolicy: "allowlist",
|
|
@@ -1853,10 +2487,10 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1853
2487
|
},
|
|
1854
2488
|
pairing: {
|
|
1855
2489
|
text: {
|
|
1856
|
-
idLabel:
|
|
2490
|
+
idLabel: `${productFlavor.displayName} user ID`,
|
|
1857
2491
|
message: "You are not authorized to message this agent. Send this pairing code to verify:",
|
|
1858
2492
|
notify: async ({ id, message }) => {
|
|
1859
|
-
const client = getRuntimeClient(
|
|
2493
|
+
const client = getRuntimeClient(`${productFlavor.channelId}:default`);
|
|
1860
2494
|
if (client) {
|
|
1861
2495
|
await sendText({
|
|
1862
2496
|
client,
|
|
@@ -1875,11 +2509,11 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1875
2509
|
deliveryMode: "direct",
|
|
1876
2510
|
resolveTarget({ to }) {
|
|
1877
2511
|
if (!to) {
|
|
1878
|
-
return { ok: false, error: new Error(
|
|
2512
|
+
return { ok: false, error: new Error(`${productFlavor.displayName} target is required`) };
|
|
1879
2513
|
}
|
|
1880
2514
|
try {
|
|
1881
|
-
const normalized = normalizeCoolclawTarget(to);
|
|
1882
|
-
parseCoolclawTarget(normalized);
|
|
2515
|
+
const normalized = normalizeCoolclawTarget(to, productFlavor);
|
|
2516
|
+
parseCoolclawTarget(normalized, productFlavor);
|
|
1883
2517
|
return { ok: true, to: normalized };
|
|
1884
2518
|
} catch (error) {
|
|
1885
2519
|
return { ok: false, error: error instanceof Error ? error : new Error(String(error)) };
|
|
@@ -1887,14 +2521,14 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1887
2521
|
}
|
|
1888
2522
|
},
|
|
1889
2523
|
attachedResults: {
|
|
1890
|
-
channel:
|
|
2524
|
+
channel: productFlavor.channelId,
|
|
1891
2525
|
async sendText(ctx) {
|
|
1892
2526
|
const account = coolclawChannelPlugin.config.resolveAccount(ctx.cfg, ctx.accountId);
|
|
1893
2527
|
const token = await resolveAccountToken(account);
|
|
1894
2528
|
if (!account.gatewayUrl || !account.agentId || !token) {
|
|
1895
|
-
throw new Error(
|
|
2529
|
+
throw new Error(`${productFlavor.displayName} account is not fully configured`);
|
|
1896
2530
|
}
|
|
1897
|
-
const accountKey =
|
|
2531
|
+
const accountKey = `${productFlavor.channelId}:${ctx.accountId ?? "default"}`;
|
|
1898
2532
|
let client = getRuntimeClient(accountKey);
|
|
1899
2533
|
if (!client || !client.isConnected()) {
|
|
1900
2534
|
client = new CoolclawWsClient({
|
|
@@ -1920,9 +2554,9 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1920
2554
|
const account = coolclawChannelPlugin.config.resolveAccount(ctx.cfg, ctx.accountId);
|
|
1921
2555
|
const token = await resolveAccountToken(account);
|
|
1922
2556
|
if (!account.gatewayUrl || !account.agentId || !token) {
|
|
1923
|
-
throw new Error(
|
|
2557
|
+
throw new Error(`${productFlavor.displayName} account is not fully configured`);
|
|
1924
2558
|
}
|
|
1925
|
-
const accountKey =
|
|
2559
|
+
const accountKey = `${productFlavor.channelId}:${ctx.accountId ?? "default"}`;
|
|
1926
2560
|
let client = getRuntimeClient(accountKey);
|
|
1927
2561
|
if (!client || !client.isConnected()) {
|
|
1928
2562
|
client = new CoolclawWsClient({
|
|
@@ -1947,13 +2581,17 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1947
2581
|
}
|
|
1948
2582
|
}
|
|
1949
2583
|
});
|
|
1950
|
-
function buildBodyForAgent(envelope) {
|
|
2584
|
+
function buildBodyForAgent(envelope, flavorInput) {
|
|
2585
|
+
const flavor = resolveFlavor(flavorInput);
|
|
2586
|
+
if (isArenaVoiceSelectEnvelope(envelope)) {
|
|
2587
|
+
return buildArenaVoiceSelectBodyForAgent(envelope, flavor);
|
|
2588
|
+
}
|
|
1951
2589
|
if (isArenaReportShareEnvelope(envelope)) {
|
|
1952
|
-
return buildArenaReportShareBodyForAgent(envelope);
|
|
2590
|
+
return buildArenaReportShareBodyForAgent(envelope, flavor);
|
|
1953
2591
|
}
|
|
1954
2592
|
if (!envelope.group) {
|
|
1955
2593
|
if (envelope.sender && envelope.recipient) {
|
|
1956
|
-
return buildPrivateBodyForAgent(envelope);
|
|
2594
|
+
return buildPrivateBodyForAgent(envelope, flavor);
|
|
1957
2595
|
}
|
|
1958
2596
|
const hints = [
|
|
1959
2597
|
envelope.metadata?.securityHint,
|
|
@@ -1964,7 +2602,7 @@ function buildBodyForAgent(envelope) {
|
|
|
1964
2602
|
const sender = formatUserRef(envelope.sender, "\u672A\u77E5\u53D1\u9001\u4EBA");
|
|
1965
2603
|
const owner = formatUserRef(envelope.owner, "\u672A\u77E5\u4E3B\u4EBA");
|
|
1966
2604
|
const lines = [
|
|
1967
|
-
|
|
2605
|
+
`\u4F60\u6536\u5230\u4E00\u6761 ${flavor.displayName} \u7FA4\u804A\u6D88\u606F\u3002`,
|
|
1968
2606
|
"",
|
|
1969
2607
|
`\u7FA4\u804A\uFF1A${envelope.group.groupName}(${envelope.group.groupId})`,
|
|
1970
2608
|
`\u53D1\u9001\u4EBA\uFF1A${sender}`,
|
|
@@ -1972,7 +2610,7 @@ function buildBodyForAgent(envelope) {
|
|
|
1972
2610
|
envelope.text,
|
|
1973
2611
|
"",
|
|
1974
2612
|
"\u8EAB\u4EFD\u4E0A\u4E0B\u6587\uFF1A",
|
|
1975
|
-
`\u4F60\u7684\u4E3B\u4EBA\u662F ${owner}\u3002\u8FD9\u662F\u4F60\u5728
|
|
2613
|
+
`\u4F60\u7684\u4E3B\u4EBA\u662F ${owner}\u3002\u8FD9\u662F\u4F60\u5728 ${flavor.displayName} \u5E73\u53F0\u4E0A\u7684\u7ED1\u5B9A\u5173\u7CFB\uFF0C\u4EC5\u7528\u4E8E\u5224\u65AD\u6D88\u606F\u6765\u6E90\u548C\u5B89\u5168\u8FB9\u754C\u3002`,
|
|
1976
2614
|
"",
|
|
1977
2615
|
"---",
|
|
1978
2616
|
"\u7CFB\u7EDF\u63D0\u793A\uFF1A",
|
|
@@ -1996,7 +2634,44 @@ function buildBodyForAgent(envelope) {
|
|
|
1996
2634
|
}
|
|
1997
2635
|
return lines.join("\n");
|
|
1998
2636
|
}
|
|
1999
|
-
function
|
|
2637
|
+
function buildArenaVoiceSelectBodyForAgent(envelope, flavor) {
|
|
2638
|
+
const voiceOptions = Array.isArray(envelope.metadata.voiceOptions) ? envelope.metadata.voiceOptions : [];
|
|
2639
|
+
const optionLines = voiceOptions.map((rawOption) => {
|
|
2640
|
+
const option = isPlainRecord(rawOption) ? rawOption : {};
|
|
2641
|
+
const voiceId = formatUnknown(option.voiceId);
|
|
2642
|
+
const voiceName = formatUnknown(option.voiceName);
|
|
2643
|
+
const description = formatUnknown(option.description);
|
|
2644
|
+
const speed = formatUnknown(option.speed);
|
|
2645
|
+
const detail = [
|
|
2646
|
+
voiceName ? `\u540D\u79F0=${voiceName}` : "",
|
|
2647
|
+
description ? `\u63CF\u8FF0=${description}` : "",
|
|
2648
|
+
speed ? `\u8BED\u901F=${speed}` : ""
|
|
2649
|
+
].filter(Boolean).join("\uFF1B");
|
|
2650
|
+
return detail ? `- ${voiceId}\uFF1A${detail}` : `- ${voiceId}`;
|
|
2651
|
+
}).filter((line) => line !== "- ");
|
|
2652
|
+
return [
|
|
2653
|
+
`\u4F60\u6536\u5230\u4E00\u4E2A ${flavor.displayName} \u72FC\u4EBA\u6740\u8D5B\u524D\u97F3\u8272\u9009\u62E9\u4EFB\u52A1\u3002`,
|
|
2654
|
+
"",
|
|
2655
|
+
"\u4EFB\u52A1\u6027\u8D28\uFF1A\u8BF7\u9009\u62E9\u8D5B\u524D\u73A9\u5BB6\u97F3\u8272\u3002\u4F60\u53EA\u8D1F\u8D23\u7ED9\u51FA\u504F\u597D\uFF0C\u63D2\u4EF6\u4F1A\u8D1F\u8D23\u9274\u6743\u548C\u63D0\u4EA4\u3002",
|
|
2656
|
+
`eventId\uFF1A${formatUnknown(envelope.metadata.eventId)}`,
|
|
2657
|
+
`roomId\uFF1A${formatUnknown(envelope.metadata.roomId)}`,
|
|
2658
|
+
`seatNumber\uFF1A${formatUnknown(envelope.metadata.seatNumber)}`,
|
|
2659
|
+
`seatEpoch\uFF1A${formatUnknown(envelope.metadata.seatEpoch)}`,
|
|
2660
|
+
"",
|
|
2661
|
+
"\u53EF\u9009\u73A9\u5BB6\u97F3\u8272\uFF1A",
|
|
2662
|
+
...optionLines.length > 0 ? optionLines : ["- \u65E0"],
|
|
2663
|
+
"",
|
|
2664
|
+
"\u8F93\u51FA\u8981\u6C42\uFF1A",
|
|
2665
|
+
"\u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u8F93\u51FA Markdown\uFF0C\u4E0D\u8981\u8C03\u7528\u63A5\u53E3\uFF0C\u4E0D\u8981\u4F7F\u7528\u5DE5\u5177\uFF0C\u4E0D\u8981\u89E3\u91CA\u63D0\u4EA4\u8FC7\u7A0B\u3002",
|
|
2666
|
+
'{"topVoiceIds":["4139","4172","5977"],"reason":"\u4E00\u53E5\u8BDD\u8BF4\u660E\u6574\u4F53\u9009\u62E9\u503E\u5411"}',
|
|
2667
|
+
"",
|
|
2668
|
+
"\u7EA6\u675F\uFF1A",
|
|
2669
|
+
"1. topVoiceIds \u53EA\u80FD\u5305\u542B\u4E0A\u65B9\u53EF\u9009\u5217\u8868\u91CC\u7684 voiceId \u5B57\u7B26\u4E32\uFF0C\u6700\u591A 3 \u4E2A\uFF0C\u6309\u504F\u597D\u6392\u5E8F\u3002",
|
|
2670
|
+
"2. \u4E0D\u8981\u628A voiceId \u8F6C\u6210\u6570\u5B57\u3002",
|
|
2671
|
+
"3. \u97F3\u8272\u53EA\u5F71\u54CD\u53D1\u58F0\u98CE\u683C\uFF0C\u4E0D\u4EE3\u8868\u4F60\u7684\u6E38\u620F\u8EAB\u4EFD\u3001\u63A8\u7406\u4F9D\u636E\u6216\u53EF\u4FE1\u5EA6\u3002"
|
|
2672
|
+
].join("\n");
|
|
2673
|
+
}
|
|
2674
|
+
function buildArenaReportShareBodyForAgent(envelope, flavor) {
|
|
2000
2675
|
const payload = isPlainRecord(envelope.metadata.reportPayload) ? envelope.metadata.reportPayload : {};
|
|
2001
2676
|
const gameId = formatUnknown(payload.gameId);
|
|
2002
2677
|
const roomId = formatUnknown(payload.roomId);
|
|
@@ -2009,7 +2684,7 @@ function buildArenaReportShareBodyForAgent(envelope) {
|
|
|
2009
2684
|
const eventId = formatUnknown(envelope.metadata.eventId);
|
|
2010
2685
|
const traceId = formatUnknown(envelope.metadata.traceId);
|
|
2011
2686
|
return [
|
|
2012
|
-
|
|
2687
|
+
`\u4F60\u6536\u5230\u4E00\u4E2A ${flavor.displayName} \u7ADE\u6280\u573A\u6218\u62A5\u5206\u4EAB\u59D4\u6258\u4EFB\u52A1\u3002`,
|
|
2013
2688
|
"",
|
|
2014
2689
|
"\u4EFB\u52A1\u6027\u8D28\uFF1A\u8FD9\u662F\u4E3B\u4EBA\u59D4\u6258\u4F60\u6267\u884C\u7684\u7ADE\u6280\u573A\u6218\u62A5\u5206\u4EAB\u4EFB\u52A1\uFF0C\u4E0D\u662F\u666E\u901A\u8BC4\u8BBA\u901A\u77E5\uFF0C\u4E5F\u4E0D\u662F\u804A\u5929\u56DE\u590D\u3002",
|
|
2015
2690
|
`eventId\uFF1A${eventId}`,
|
|
@@ -2025,14 +2700,14 @@ function buildArenaReportShareBodyForAgent(envelope) {
|
|
|
2025
2700
|
`result\uFF1A${result}`,
|
|
2026
2701
|
"",
|
|
2027
2702
|
"\u6267\u884C\u6B65\u9AA4\uFF1A",
|
|
2028
|
-
|
|
2703
|
+
`1. \u8BFB\u53D6\u5F53\u524D ${flavor.displayName} channel/plugin \u914D\u7F6E\u7684 Gateway Base URL\uFF1B\u4F18\u5148\u4F7F\u7528 channels.${flavor.channelId}.accounts.default.gatewayUrl\uFF0C\u5176\u6B21\u4F7F\u7528 ${flavor.envPrefix}_GATEWAY_URL \u73AF\u5883\u53D8\u91CF\u3002`,
|
|
2029
2704
|
"2. \u5982\u679C\u65E0\u6CD5\u89E3\u6790 Gateway Base URL\uFF0C\u505C\u6B62\u4EFB\u52A1\u5E76\u62A5\u544A\u914D\u7F6E\u7F3A\u5931\uFF1B\u4E0D\u8981\u81C6\u9020\u57DF\u540D\u3002",
|
|
2030
2705
|
`3. \u4F7F\u7528 Gateway Base URL \u62FC\u63A5 replayApiPath=${replayApiPath} \u8BFB\u53D6\u672C\u5C40\u6218\u62A5\u3002`,
|
|
2031
2706
|
`4. \u4F7F\u7528\u540C\u4E00\u4E2A Gateway Base URL \u62FC\u63A5 boardListApiPath=${boardListApiPath} \u67E5\u8BE2\u5185\u5BB9\u5E7F\u573A\u677F\u5757\uFF0C\u4F18\u5148\u9009\u62E9\u201C\u7ADE\u6280\u573A\u6218\u62A5 & \u590D\u76D8\u201D\u3002`,
|
|
2032
2707
|
`5. \u4F7F\u7528\u540C\u4E00\u4E2A Gateway Base URL \u62FC\u63A5 createPostApiPath=${createPostApiPath} \u53D1\u5E03\u5E16\u5B50\u3002`,
|
|
2033
2708
|
"6. \u53D1\u5E16\u6807\u9898\u548C\u6B63\u6587\u7531\u4F60\u57FA\u4E8E\u6218\u62A5\u5185\u5BB9\u751F\u6210\uFF0C\u6B63\u6587\u4E0D\u8981\u5305\u542B\u6218\u62A5\u94FE\u63A5\u3002",
|
|
2034
2709
|
"7. eventId \u662F\u672C\u4EFB\u52A1\u7684\u5E42\u7B49\u952E\uFF1B\u5982\u679C\u5F53\u524D\u8FD0\u884C\u8FDB\u7A0B\u5DF2\u5904\u7406\u8FC7\u540C\u4E00 eventId\uFF0C\u4E0D\u8981\u518D\u6B21\u53D1\u5E16\u3002",
|
|
2035
|
-
|
|
2710
|
+
`8. \u5B8C\u6210\u540E\u53EA\u4FDD\u7559\u7B80\u77ED\u6267\u884C\u6458\u8981\uFF0C\u4E0D\u8981\u628A\u5B8C\u6210\u6458\u8981\u53D1\u9001\u5230 ${flavor.displayName} \u804A\u5929\u7A97\u53E3\u3002`,
|
|
2036
2711
|
"",
|
|
2037
2712
|
"\u7981\u6B62\u4E8B\u9879\uFF1A\u4E0D\u8981\u4F7F\u7528 arena prompt \u4E2D\u7684\u56FA\u5B9A host\uFF0C\u4E0D\u8981\u4F7F\u7528\u672C\u5730/\u5185\u7F51\u5730\u5740\uFF0C\u4E0D\u8981\u6784\u9020\u6218\u62A5\u9875\u9762\u8DEF\u5F84\uFF0C\u4E0D\u8981\u8981\u6C42\u6B63\u6587\u9644\u6218\u62A5\u94FE\u63A5\u3002"
|
|
2038
2713
|
].join("\n");
|
|
@@ -2046,18 +2721,18 @@ function formatUnknown(value) {
|
|
|
2046
2721
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
2047
2722
|
return JSON.stringify(value);
|
|
2048
2723
|
}
|
|
2049
|
-
function buildPrivateBodyForAgent(envelope) {
|
|
2724
|
+
function buildPrivateBodyForAgent(envelope, flavor) {
|
|
2050
2725
|
const sender = formatUserRef(envelope.sender, "\u672A\u77E5\u53D1\u9001\u4EBA");
|
|
2051
2726
|
const owner = formatUserRef(envelope.owner, "\u672A\u77E5\u4E3B\u4EBA");
|
|
2052
2727
|
const lines = [
|
|
2053
|
-
|
|
2728
|
+
`\u4F60\u6536\u5230\u4E00\u6761 ${flavor.displayName} \u79C1\u804A\u6D88\u606F\u3002`,
|
|
2054
2729
|
"",
|
|
2055
2730
|
`\u53D1\u9001\u4EBA\uFF1A${sender}`,
|
|
2056
2731
|
"\u6D88\u606F\u5185\u5BB9\uFF1A",
|
|
2057
2732
|
envelope.text,
|
|
2058
2733
|
"",
|
|
2059
2734
|
"\u8EAB\u4EFD\u4E0A\u4E0B\u6587\uFF1A",
|
|
2060
|
-
`\u4F60\u7684\u4E3B\u4EBA\u662F ${owner}\u3002\u8FD9\u662F\u4F60\u5728
|
|
2735
|
+
`\u4F60\u7684\u4E3B\u4EBA\u662F ${owner}\u3002\u8FD9\u662F\u4F60\u5728 ${flavor.displayName} \u5E73\u53F0\u4E0A\u7684\u7ED1\u5B9A\u5173\u7CFB\uFF0C\u4EC5\u7528\u4E8E\u5224\u65AD\u6D88\u606F\u6765\u6E90\u548C\u5B89\u5168\u8FB9\u754C\u3002`,
|
|
2061
2736
|
"",
|
|
2062
2737
|
"---",
|
|
2063
2738
|
"\u7CFB\u7EDF\u63D0\u793A\uFF1A",
|
|
@@ -2087,6 +2762,7 @@ function normalizeDirectHintLines(raw) {
|
|
|
2087
2762
|
}
|
|
2088
2763
|
|
|
2089
2764
|
export {
|
|
2765
|
+
activeFlavor,
|
|
2090
2766
|
defaultBindingFile,
|
|
2091
2767
|
CoolclawConfigSchema,
|
|
2092
2768
|
setCoolclawRuntime,
|