@yahaha-studio/kichi-forwarder 0.0.1-alpha.40 → 0.0.1-alpha.42
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/openclaw.plugin.json +2 -13
- package/package.json +1 -1
- package/src/config.ts +4 -4
- package/src/service.ts +89 -16
- package/src/types.ts +11 -3
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/openclaw.plugin.json
CHANGED
|
@@ -8,18 +8,7 @@
|
|
|
8
8
|
"configSchema": {
|
|
9
9
|
"type": "object",
|
|
10
10
|
"additionalProperties": false,
|
|
11
|
-
"properties": {
|
|
12
|
-
"wsUrl": {
|
|
13
|
-
"type": "string"
|
|
14
|
-
},
|
|
15
|
-
"enabled": {
|
|
16
|
-
"type": "boolean",
|
|
17
|
-
"default": true
|
|
18
|
-
}
|
|
19
|
-
}
|
|
11
|
+
"properties": {}
|
|
20
12
|
},
|
|
21
|
-
"uiHints": {
|
|
22
|
-
"wsUrl": { "label": "WebSocket URL", "placeholder": "ws://host:port/ws/openclaw" },
|
|
23
|
-
"enabled": { "label": "Enable Plugin" }
|
|
24
|
-
}
|
|
13
|
+
"uiHints": {}
|
|
25
14
|
}
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { KichiForwarderConfig } from "./types.js";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const FIXED_WS_URL = "ws://43.106.148.251:48870/ws/openclaw";
|
|
4
|
+
|
|
5
|
+
export function parse(_value: unknown): KichiForwarderConfig {
|
|
5
6
|
return {
|
|
6
|
-
wsUrl:
|
|
7
|
-
enabled: config.enabled ?? true,
|
|
7
|
+
wsUrl: FIXED_WS_URL,
|
|
8
8
|
};
|
|
9
9
|
}
|
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
|
{
|
|
@@ -45,7 +67,6 @@ export class KichiForwarderService {
|
|
|
45
67
|
constructor(private config: KichiForwarderConfig, private logger: Logger) {}
|
|
46
68
|
|
|
47
69
|
async start(): Promise<void> {
|
|
48
|
-
if (!this.config.enabled) return;
|
|
49
70
|
this.identity = this.loadIdentity();
|
|
50
71
|
this.stopped = false;
|
|
51
72
|
this.connect();
|
|
@@ -64,7 +85,7 @@ export class KichiForwarderService {
|
|
|
64
85
|
botName: string,
|
|
65
86
|
bio: string,
|
|
66
87
|
tags: string[],
|
|
67
|
-
): Promise<
|
|
88
|
+
): Promise<JoinResult> {
|
|
68
89
|
return new Promise((resolve) => {
|
|
69
90
|
this.identity = { avatarId };
|
|
70
91
|
this.joinResolve = resolve;
|
|
@@ -75,7 +96,12 @@ export class KichiForwarderService {
|
|
|
75
96
|
} else {
|
|
76
97
|
this.ws?.once("open", sendJoin);
|
|
77
98
|
}
|
|
78
|
-
setTimeout(() => {
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
if (this.joinResolve) {
|
|
101
|
+
this.joinResolve = null;
|
|
102
|
+
resolve({ success: false, error: "Timed out waiting for join_ack" });
|
|
103
|
+
}
|
|
104
|
+
}, 10000);
|
|
79
105
|
});
|
|
80
106
|
}
|
|
81
107
|
|
|
@@ -102,27 +128,64 @@ export class KichiForwarderService {
|
|
|
102
128
|
}
|
|
103
129
|
|
|
104
130
|
private handleMessage(data: string): void {
|
|
131
|
+
this.logger.debug(`[kichi ws recv] ${data}`);
|
|
105
132
|
try {
|
|
106
133
|
const msg = JSON.parse(data);
|
|
107
134
|
this.tryResolvePendingRequest(msg);
|
|
108
|
-
if (msg.type === "join_ack"
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
135
|
+
if (msg.type === "join_ack") {
|
|
136
|
+
const joinAck = msg as JoinAckPayload;
|
|
137
|
+
if (joinAck.success === false || !joinAck.authKey) {
|
|
138
|
+
const failure = this.buildAckFailure(joinAck, "Join failed");
|
|
139
|
+
this.logger.warn(`Join failed: ${failure.error}`);
|
|
140
|
+
this.joinResolve?.(failure);
|
|
141
|
+
this.joinResolve = null;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (this.identity) {
|
|
146
|
+
this.identity.authKey = joinAck.authKey;
|
|
147
|
+
this.saveIdentity();
|
|
148
|
+
this.logger.info(`Joined as ${this.identity.avatarId}`);
|
|
149
|
+
}
|
|
150
|
+
this.joinResolve?.({ success: true, authKey: joinAck.authKey });
|
|
113
151
|
this.joinResolve = null;
|
|
114
152
|
} else if (msg.type === "rejoin_failed" || msg.type === "auth_error") {
|
|
115
153
|
// AuthKey invalid/expired, clear it
|
|
116
154
|
this.logger.warn(`Auth failed: ${msg.reason || "unknown"}`);
|
|
117
155
|
this.clearAuthKey();
|
|
118
156
|
} else if (msg.type === "leave_ack") {
|
|
119
|
-
|
|
157
|
+
const leaveAck = msg as LeaveAckPayload;
|
|
158
|
+
if (leaveAck.success === false) {
|
|
159
|
+
const failure = this.buildAckFailure(leaveAck, "Leave failed");
|
|
160
|
+
this.logger.warn(`Leave failed: ${failure.error}`);
|
|
161
|
+
} else {
|
|
162
|
+
this.logger.info("Left Kichi world");
|
|
163
|
+
}
|
|
120
164
|
}
|
|
121
165
|
} catch (e) {
|
|
122
166
|
this.logger.warn(`Failed to parse message: ${e}`);
|
|
123
167
|
}
|
|
124
168
|
}
|
|
125
169
|
|
|
170
|
+
private buildAckFailure(
|
|
171
|
+
msg: { errorCode?: unknown; errorMessage?: unknown },
|
|
172
|
+
fallbackError: string,
|
|
173
|
+
): AckFailureResult {
|
|
174
|
+
const errorCode =
|
|
175
|
+
typeof msg.errorCode === "string" && msg.errorCode.trim().length > 0 ? msg.errorCode : undefined;
|
|
176
|
+
const errorMessage =
|
|
177
|
+
typeof msg.errorMessage === "string" && msg.errorMessage.trim().length > 0
|
|
178
|
+
? msg.errorMessage
|
|
179
|
+
: undefined;
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
success: false,
|
|
183
|
+
error: errorMessage ?? (errorCode ? `${fallbackError} (${errorCode})` : fallbackError),
|
|
184
|
+
errorCode,
|
|
185
|
+
errorMessage,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
126
189
|
private tryResolvePendingRequest(msg: { type?: unknown; requestId?: unknown }): void {
|
|
127
190
|
const requestId = typeof msg.requestId === "string" ? msg.requestId : "";
|
|
128
191
|
if (!requestId) {
|
|
@@ -396,7 +459,7 @@ export class KichiForwarderService {
|
|
|
396
459
|
};
|
|
397
460
|
}
|
|
398
461
|
|
|
399
|
-
if (this.stopped
|
|
462
|
+
if (this.stopped) {
|
|
400
463
|
return {
|
|
401
464
|
accepted: false,
|
|
402
465
|
mode: "unavailable",
|
|
@@ -419,7 +482,6 @@ export class KichiForwarderService {
|
|
|
419
482
|
|
|
420
483
|
getConnectionStatus(): KichiConnectionStatus {
|
|
421
484
|
return {
|
|
422
|
-
enabled: this.config.enabled,
|
|
423
485
|
wsUrl: this.config.wsUrl,
|
|
424
486
|
connected: this.isConnected(),
|
|
425
487
|
websocketState: this.getWebsocketState(),
|
|
@@ -448,16 +510,24 @@ export class KichiForwarderService {
|
|
|
448
510
|
return "closed";
|
|
449
511
|
}
|
|
450
512
|
|
|
451
|
-
async leave(): Promise<
|
|
452
|
-
if (!this.identity?.avatarId || !this.identity?.authKey || this.ws?.readyState !== WebSocket.OPEN)
|
|
513
|
+
async leave(): Promise<LeaveResult> {
|
|
514
|
+
if (!this.identity?.avatarId || !this.identity?.authKey || this.ws?.readyState !== WebSocket.OPEN) {
|
|
515
|
+
return { success: false, error: "Failed or not connected" };
|
|
516
|
+
}
|
|
517
|
+
|
|
453
518
|
return new Promise((resolve) => {
|
|
454
519
|
const handler = (data: WebSocket.Data) => {
|
|
455
520
|
try {
|
|
456
521
|
const msg = JSON.parse(data.toString());
|
|
457
522
|
if (msg.type === "leave_ack") {
|
|
458
523
|
this.ws?.off("message", handler);
|
|
524
|
+
const leaveAck = msg as LeaveAckPayload;
|
|
525
|
+
if (leaveAck.success === false) {
|
|
526
|
+
resolve(this.buildAckFailure(leaveAck, "Leave failed"));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
459
529
|
this.clearAuthKey();
|
|
460
|
-
resolve(true);
|
|
530
|
+
resolve({ success: true });
|
|
461
531
|
}
|
|
462
532
|
} catch (e) {
|
|
463
533
|
this.logger.warn(`Failed to parse leave response: ${e}`);
|
|
@@ -467,7 +537,10 @@ export class KichiForwarderService {
|
|
|
467
537
|
this.ws!.send(
|
|
468
538
|
JSON.stringify({ type: "leave", avatarId: this.identity!.avatarId, authKey: this.identity!.authKey }),
|
|
469
539
|
);
|
|
470
|
-
setTimeout(() => {
|
|
540
|
+
setTimeout(() => {
|
|
541
|
+
this.ws?.off("message", handler);
|
|
542
|
+
resolve({ success: false, error: "Timed out waiting for leave_ack" });
|
|
543
|
+
}, 10000);
|
|
471
544
|
});
|
|
472
545
|
}
|
|
473
546
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export type KichiForwarderConfig = {
|
|
2
2
|
wsUrl: string;
|
|
3
|
-
enabled: boolean;
|
|
4
3
|
};
|
|
5
4
|
|
|
6
5
|
export type PoseType = "stand" | "sit" | "lay" | "floor";
|
|
@@ -38,7 +37,6 @@ export type KichiIdentity = {
|
|
|
38
37
|
};
|
|
39
38
|
|
|
40
39
|
export type KichiConnectionStatus = {
|
|
41
|
-
enabled: boolean;
|
|
42
40
|
wsUrl: string;
|
|
43
41
|
connected: boolean;
|
|
44
42
|
websocketState: "idle" | "connecting" | "open" | "closing" | "closed";
|
|
@@ -69,7 +67,17 @@ export type JoinPayload = {
|
|
|
69
67
|
|
|
70
68
|
export type JoinAckPayload = {
|
|
71
69
|
type: "join_ack";
|
|
72
|
-
authKey
|
|
70
|
+
authKey?: string;
|
|
71
|
+
success?: boolean;
|
|
72
|
+
errorCode?: string;
|
|
73
|
+
errorMessage?: string;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type LeaveAckPayload = {
|
|
77
|
+
type: "leave_ack";
|
|
78
|
+
success?: boolean;
|
|
79
|
+
errorCode?: string;
|
|
80
|
+
errorMessage?: string;
|
|
73
81
|
};
|
|
74
82
|
|
|
75
83
|
export type LeavePayload = {
|