@yahaha-studio/kichi-forwarder 0.0.1-alpha.40 → 0.0.1-alpha.41
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/index.ts +24 -2
- package/package.json +1 -1
- package/src/service.ts +88 -13
- package/src/types.ts +11 -1
package/index.ts
CHANGED
|
@@ -602,7 +602,18 @@ const plugin = {
|
|
|
602
602
|
return { success: false, error: tagsError };
|
|
603
603
|
}
|
|
604
604
|
const result = await service?.join(avatarId, botName, bio, tags ?? []);
|
|
605
|
-
|
|
605
|
+
if (!result) {
|
|
606
|
+
return { success: false, error: "Kichi service is not initialized" };
|
|
607
|
+
}
|
|
608
|
+
if (result.success) {
|
|
609
|
+
return { success: true, authKey: result.authKey };
|
|
610
|
+
}
|
|
611
|
+
return {
|
|
612
|
+
success: false,
|
|
613
|
+
error: result.error,
|
|
614
|
+
...(result.errorCode ? { errorCode: result.errorCode } : {}),
|
|
615
|
+
...(result.errorMessage ? { errorMessage: result.errorMessage } : {}),
|
|
616
|
+
};
|
|
606
617
|
},
|
|
607
618
|
});
|
|
608
619
|
|
|
@@ -631,7 +642,18 @@ const plugin = {
|
|
|
631
642
|
parameters: { type: "object", properties: {} },
|
|
632
643
|
execute: async () => {
|
|
633
644
|
const result = await service?.leave();
|
|
634
|
-
|
|
645
|
+
if (!result) {
|
|
646
|
+
return { success: false, error: "Kichi service is not initialized" };
|
|
647
|
+
}
|
|
648
|
+
if (result.success) {
|
|
649
|
+
return { success: true };
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
success: false,
|
|
653
|
+
error: result.error,
|
|
654
|
+
...(result.errorCode ? { errorCode: result.errorCode } : {}),
|
|
655
|
+
...(result.errorMessage ? { errorMessage: result.errorMessage } : {}),
|
|
656
|
+
};
|
|
635
657
|
},
|
|
636
658
|
});
|
|
637
659
|
|
package/package.json
CHANGED
package/src/service.ts
CHANGED
|
@@ -11,11 +11,13 @@ import type {
|
|
|
11
11
|
CreateMusicAlbumPayload,
|
|
12
12
|
HookNotifyPayload,
|
|
13
13
|
HookNotifyType,
|
|
14
|
+
JoinAckPayload,
|
|
14
15
|
JoinPayload,
|
|
15
16
|
KichiConnectionStatus,
|
|
16
17
|
CreateNotesBoardNotePayload,
|
|
17
18
|
KichiForwarderConfig,
|
|
18
19
|
KichiIdentity,
|
|
20
|
+
LeaveAckPayload,
|
|
19
21
|
PoseType,
|
|
20
22
|
QueryStatusPayload,
|
|
21
23
|
QueryStatusResultPayload,
|
|
@@ -26,12 +28,32 @@ const IDENTITY_DIR = path.join(os.homedir(), ".openclaw", "kichi-world");
|
|
|
26
28
|
const IDENTITY_PATH = path.join(IDENTITY_DIR, "identity.json");
|
|
27
29
|
const MAX_NOTEBOARD_TEXT_LENGTH = 200;
|
|
28
30
|
|
|
31
|
+
type AckFailureResult = {
|
|
32
|
+
success: false;
|
|
33
|
+
error: string;
|
|
34
|
+
errorCode?: string;
|
|
35
|
+
errorMessage?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type JoinResult =
|
|
39
|
+
| {
|
|
40
|
+
success: true;
|
|
41
|
+
authKey: string;
|
|
42
|
+
}
|
|
43
|
+
| AckFailureResult;
|
|
44
|
+
|
|
45
|
+
export type LeaveResult =
|
|
46
|
+
| {
|
|
47
|
+
success: true;
|
|
48
|
+
}
|
|
49
|
+
| AckFailureResult;
|
|
50
|
+
|
|
29
51
|
export class KichiForwarderService {
|
|
30
52
|
private ws: WebSocket | null = null;
|
|
31
53
|
private stopped = false;
|
|
32
54
|
private reconnectTimeout: NodeJS.Timeout | null = null;
|
|
33
55
|
private identity: KichiIdentity | null = null;
|
|
34
|
-
private joinResolve: ((
|
|
56
|
+
private joinResolve: ((result: JoinResult) => void) | null = null;
|
|
35
57
|
private pendingRequests = new Map<
|
|
36
58
|
string,
|
|
37
59
|
{
|
|
@@ -64,7 +86,7 @@ export class KichiForwarderService {
|
|
|
64
86
|
botName: string,
|
|
65
87
|
bio: string,
|
|
66
88
|
tags: string[],
|
|
67
|
-
): Promise<
|
|
89
|
+
): Promise<JoinResult> {
|
|
68
90
|
return new Promise((resolve) => {
|
|
69
91
|
this.identity = { avatarId };
|
|
70
92
|
this.joinResolve = resolve;
|
|
@@ -75,7 +97,12 @@ export class KichiForwarderService {
|
|
|
75
97
|
} else {
|
|
76
98
|
this.ws?.once("open", sendJoin);
|
|
77
99
|
}
|
|
78
|
-
setTimeout(() => {
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
if (this.joinResolve) {
|
|
102
|
+
this.joinResolve = null;
|
|
103
|
+
resolve({ success: false, error: "Timed out waiting for join_ack" });
|
|
104
|
+
}
|
|
105
|
+
}, 10000);
|
|
79
106
|
});
|
|
80
107
|
}
|
|
81
108
|
|
|
@@ -102,27 +129,64 @@ export class KichiForwarderService {
|
|
|
102
129
|
}
|
|
103
130
|
|
|
104
131
|
private handleMessage(data: string): void {
|
|
132
|
+
this.logger.debug(`[kichi ws recv] ${data}`);
|
|
105
133
|
try {
|
|
106
134
|
const msg = JSON.parse(data);
|
|
107
135
|
this.tryResolvePendingRequest(msg);
|
|
108
|
-
if (msg.type === "join_ack"
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
136
|
+
if (msg.type === "join_ack") {
|
|
137
|
+
const joinAck = msg as JoinAckPayload;
|
|
138
|
+
if (joinAck.success === false || !joinAck.authKey) {
|
|
139
|
+
const failure = this.buildAckFailure(joinAck, "Join failed");
|
|
140
|
+
this.logger.warn(`Join failed: ${failure.error}`);
|
|
141
|
+
this.joinResolve?.(failure);
|
|
142
|
+
this.joinResolve = null;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (this.identity) {
|
|
147
|
+
this.identity.authKey = joinAck.authKey;
|
|
148
|
+
this.saveIdentity();
|
|
149
|
+
this.logger.info(`Joined as ${this.identity.avatarId}`);
|
|
150
|
+
}
|
|
151
|
+
this.joinResolve?.({ success: true, authKey: joinAck.authKey });
|
|
113
152
|
this.joinResolve = null;
|
|
114
153
|
} else if (msg.type === "rejoin_failed" || msg.type === "auth_error") {
|
|
115
154
|
// AuthKey invalid/expired, clear it
|
|
116
155
|
this.logger.warn(`Auth failed: ${msg.reason || "unknown"}`);
|
|
117
156
|
this.clearAuthKey();
|
|
118
157
|
} else if (msg.type === "leave_ack") {
|
|
119
|
-
|
|
158
|
+
const leaveAck = msg as LeaveAckPayload;
|
|
159
|
+
if (leaveAck.success === false) {
|
|
160
|
+
const failure = this.buildAckFailure(leaveAck, "Leave failed");
|
|
161
|
+
this.logger.warn(`Leave failed: ${failure.error}`);
|
|
162
|
+
} else {
|
|
163
|
+
this.logger.info("Left Kichi world");
|
|
164
|
+
}
|
|
120
165
|
}
|
|
121
166
|
} catch (e) {
|
|
122
167
|
this.logger.warn(`Failed to parse message: ${e}`);
|
|
123
168
|
}
|
|
124
169
|
}
|
|
125
170
|
|
|
171
|
+
private buildAckFailure(
|
|
172
|
+
msg: { errorCode?: unknown; errorMessage?: unknown },
|
|
173
|
+
fallbackError: string,
|
|
174
|
+
): AckFailureResult {
|
|
175
|
+
const errorCode =
|
|
176
|
+
typeof msg.errorCode === "string" && msg.errorCode.trim().length > 0 ? msg.errorCode : undefined;
|
|
177
|
+
const errorMessage =
|
|
178
|
+
typeof msg.errorMessage === "string" && msg.errorMessage.trim().length > 0
|
|
179
|
+
? msg.errorMessage
|
|
180
|
+
: undefined;
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
success: false,
|
|
184
|
+
error: errorMessage ?? (errorCode ? `${fallbackError} (${errorCode})` : fallbackError),
|
|
185
|
+
errorCode,
|
|
186
|
+
errorMessage,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
126
190
|
private tryResolvePendingRequest(msg: { type?: unknown; requestId?: unknown }): void {
|
|
127
191
|
const requestId = typeof msg.requestId === "string" ? msg.requestId : "";
|
|
128
192
|
if (!requestId) {
|
|
@@ -448,16 +512,24 @@ export class KichiForwarderService {
|
|
|
448
512
|
return "closed";
|
|
449
513
|
}
|
|
450
514
|
|
|
451
|
-
async leave(): Promise<
|
|
452
|
-
if (!this.identity?.avatarId || !this.identity?.authKey || this.ws?.readyState !== WebSocket.OPEN)
|
|
515
|
+
async leave(): Promise<LeaveResult> {
|
|
516
|
+
if (!this.identity?.avatarId || !this.identity?.authKey || this.ws?.readyState !== WebSocket.OPEN) {
|
|
517
|
+
return { success: false, error: "Failed or not connected" };
|
|
518
|
+
}
|
|
519
|
+
|
|
453
520
|
return new Promise((resolve) => {
|
|
454
521
|
const handler = (data: WebSocket.Data) => {
|
|
455
522
|
try {
|
|
456
523
|
const msg = JSON.parse(data.toString());
|
|
457
524
|
if (msg.type === "leave_ack") {
|
|
458
525
|
this.ws?.off("message", handler);
|
|
526
|
+
const leaveAck = msg as LeaveAckPayload;
|
|
527
|
+
if (leaveAck.success === false) {
|
|
528
|
+
resolve(this.buildAckFailure(leaveAck, "Leave failed"));
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
459
531
|
this.clearAuthKey();
|
|
460
|
-
resolve(true);
|
|
532
|
+
resolve({ success: true });
|
|
461
533
|
}
|
|
462
534
|
} catch (e) {
|
|
463
535
|
this.logger.warn(`Failed to parse leave response: ${e}`);
|
|
@@ -467,7 +539,10 @@ export class KichiForwarderService {
|
|
|
467
539
|
this.ws!.send(
|
|
468
540
|
JSON.stringify({ type: "leave", avatarId: this.identity!.avatarId, authKey: this.identity!.authKey }),
|
|
469
541
|
);
|
|
470
|
-
setTimeout(() => {
|
|
542
|
+
setTimeout(() => {
|
|
543
|
+
this.ws?.off("message", handler);
|
|
544
|
+
resolve({ success: false, error: "Timed out waiting for leave_ack" });
|
|
545
|
+
}, 10000);
|
|
471
546
|
});
|
|
472
547
|
}
|
|
473
548
|
}
|
package/src/types.ts
CHANGED
|
@@ -69,7 +69,17 @@ export type JoinPayload = {
|
|
|
69
69
|
|
|
70
70
|
export type JoinAckPayload = {
|
|
71
71
|
type: "join_ack";
|
|
72
|
-
authKey
|
|
72
|
+
authKey?: string;
|
|
73
|
+
success?: boolean;
|
|
74
|
+
errorCode?: string;
|
|
75
|
+
errorMessage?: string;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export type LeaveAckPayload = {
|
|
79
|
+
type: "leave_ack";
|
|
80
|
+
success?: boolean;
|
|
81
|
+
errorCode?: string;
|
|
82
|
+
errorMessage?: string;
|
|
73
83
|
};
|
|
74
84
|
|
|
75
85
|
export type LeavePayload = {
|