openclaw-stepfun 0.2.2

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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/dist/index.d.ts +13 -0
  4. package/dist/index.js +18 -0
  5. package/dist/src/accounts.d.ts +22 -0
  6. package/dist/src/accounts.js +43 -0
  7. package/dist/src/bot.d.ts +16 -0
  8. package/dist/src/bot.js +100 -0
  9. package/dist/src/channel.d.ts +4 -0
  10. package/dist/src/channel.js +206 -0
  11. package/dist/src/client.d.ts +51 -0
  12. package/dist/src/client.js +206 -0
  13. package/dist/src/monitor.d.ts +19 -0
  14. package/dist/src/monitor.js +153 -0
  15. package/dist/src/proto/capy/botauth/auth_common_pb.d.ts +82 -0
  16. package/dist/src/proto/capy/botauth/auth_common_pb.js +35 -0
  17. package/dist/src/proto/capy/botauth/botauth_connect.d.ts +118 -0
  18. package/dist/src/proto/capy/botauth/botauth_connect.js +118 -0
  19. package/dist/src/proto/capy/botauth/botauth_pb.d.ts +1065 -0
  20. package/dist/src/proto/capy/botauth/botauth_pb.js +348 -0
  21. package/dist/src/proto/capy/botauth/public_connect.d.ts +62 -0
  22. package/dist/src/proto/capy/botauth/public_connect.js +62 -0
  23. package/dist/src/proto/capy/botauth/public_pb.d.ts +254 -0
  24. package/dist/src/proto/capy/botauth/public_pb.js +105 -0
  25. package/dist/src/proto/capy/botmsg/botmsg_connect.d.ts +72 -0
  26. package/dist/src/proto/capy/botmsg/botmsg_connect.js +72 -0
  27. package/dist/src/proto/capy/botmsg/botmsg_pb.d.ts +426 -0
  28. package/dist/src/proto/capy/botmsg/botmsg_pb.js +160 -0
  29. package/dist/src/proto/capy/botway/ctrl_connect.d.ts +61 -0
  30. package/dist/src/proto/capy/botway/ctrl_connect.js +61 -0
  31. package/dist/src/proto/capy/botway/ctrl_pb.d.ts +267 -0
  32. package/dist/src/proto/capy/botway/ctrl_pb.js +120 -0
  33. package/dist/src/proto/capy/botway/stream_connect.d.ts +26 -0
  34. package/dist/src/proto/capy/botway/stream_connect.js +26 -0
  35. package/dist/src/proto/capy/botway/stream_pb.d.ts +495 -0
  36. package/dist/src/proto/capy/botway/stream_pb.js +165 -0
  37. package/dist/src/reply-dispatcher.d.ts +17 -0
  38. package/dist/src/reply-dispatcher.js +234 -0
  39. package/dist/src/runtime.d.ts +4 -0
  40. package/dist/src/runtime.js +11 -0
  41. package/dist/src/send.d.ts +19 -0
  42. package/dist/src/send.js +66 -0
  43. package/dist/src/types.d.ts +65 -0
  44. package/dist/src/types.js +2 -0
  45. package/dist/src/websocket/cacheEvent.d.ts +17 -0
  46. package/dist/src/websocket/cacheEvent.js +61 -0
  47. package/dist/src/websocket/connect.d.ts +32 -0
  48. package/dist/src/websocket/connect.js +79 -0
  49. package/dist/src/websocket/constant.d.ts +8 -0
  50. package/dist/src/websocket/constant.js +10 -0
  51. package/dist/src/websocket/eventBus.d.ts +15 -0
  52. package/dist/src/websocket/eventBus.js +46 -0
  53. package/dist/src/websocket/index.d.ts +117 -0
  54. package/dist/src/websocket/index.js +637 -0
  55. package/dist/src/websocket/service.d.ts +36 -0
  56. package/dist/src/websocket/service.js +4 -0
  57. package/dist/src/websocket/stream.d.ts +10 -0
  58. package/dist/src/websocket/stream.js +24 -0
  59. package/dist/src/websocket/streamConnect.d.ts +40 -0
  60. package/dist/src/websocket/streamConnect.js +48 -0
  61. package/openclaw.plugin.json +23 -0
  62. package/package.json +69 -0
  63. package/scripts/setup.mjs +381 -0
  64. package/scripts/switch-env.mjs +98 -0
@@ -0,0 +1,234 @@
1
+ import { resolveStepfunAccount } from "./accounts.js";
2
+ import { getStepfunRuntime } from "./runtime.js";
3
+ import { sendMessageStepfun } from "./send.js";
4
+ /**
5
+ * Detect media type from URL and return placeholder message
6
+ */
7
+ function getMediaPlaceholder(url) {
8
+ const lowerUrl = url.toLowerCase();
9
+ // Image extensions
10
+ if (/\.(jpg|jpeg|png|gif|webp|bmp|svg|ico)(\?.*)?$/i.test(lowerUrl)) {
11
+ return "【这是一条图片消息】";
12
+ }
13
+ // Video extensions
14
+ if (/\.(mp4|webm|ogg|mov|avi|mkv|flv|m3u8)(\?.*)?$/i.test(lowerUrl)) {
15
+ return "【这是一条视频消息】";
16
+ }
17
+ // Audio extensions
18
+ if (/\.(mp3|wav|aac|ogg|m4a|flac|wma)(\?.*)?$/i.test(lowerUrl)) {
19
+ return "【这是一条语音/音频消息】";
20
+ }
21
+ // Document extensions
22
+ if (/\.(pdf|doc|docx|xls|xlsx|ppt|pptx|txt|zip|rar)(\?.*)?$/i.test(lowerUrl)) {
23
+ return "【这是一条文件消息】";
24
+ }
25
+ // Default for unknown media types
26
+ return "【这是一条媒体消息】";
27
+ }
28
+ /**
29
+ * List of error messages that should not be sent to users
30
+ * These are typically API/connection errors from the model provider
31
+ * Note: These are exact matches to avoid false positives
32
+ */
33
+ const BLOCKED_ERROR_MESSAGES = [
34
+ "Connection error.",
35
+ "APIConnectionError",
36
+ "Connection timeout",
37
+ "Network error",
38
+ "Request timeout",
39
+ "Service unavailable",
40
+ ];
41
+ /**
42
+ * Maximum length for error messages
43
+ * API errors are typically short (< 50 chars), while normal responses explaining these terms would be longer
44
+ */
45
+ const MAX_ERROR_LENGTH = 100;
46
+ /**
47
+ * Check if content is an error message that should not be sent to users
48
+ * Uses exact match for short messages to avoid false positives
49
+ */
50
+ function isErrorContent(content) {
51
+ const trimmed = content.trim();
52
+ // Only check relatively short messages to avoid false positives
53
+ // Normal explanations of these terms would be longer
54
+ if (trimmed.length > MAX_ERROR_LENGTH) {
55
+ return false;
56
+ }
57
+ // Check for exact match or the content being mostly the error message
58
+ return BLOCKED_ERROR_MESSAGES.some((errorMsg) => {
59
+ // Exact match
60
+ if (trimmed === errorMsg) {
61
+ return true;
62
+ }
63
+ // Content starts with the error message (error followed by punctuation or whitespace)
64
+ if (trimmed.startsWith(errorMsg) && /^[\s.,;:!?]/.test(trimmed.slice(errorMsg.length))) {
65
+ return true;
66
+ }
67
+ // Content is just the error message with minimal additions (like error code)
68
+ if (trimmed.includes(errorMsg) && trimmed.length < errorMsg.length + 20) {
69
+ return true;
70
+ }
71
+ return false;
72
+ });
73
+ }
74
+ /**
75
+ * Build message content with media placeholders if media is present
76
+ */
77
+ function buildMessageContent(payload) {
78
+ const text = payload.text ?? "";
79
+ const mediaUrls = [];
80
+ // Collect all media URLs
81
+ if (payload.mediaUrl) {
82
+ mediaUrls.push(payload.mediaUrl);
83
+ }
84
+ if (payload.mediaUrls && payload.mediaUrls.length > 0) {
85
+ mediaUrls.push(...payload.mediaUrls);
86
+ }
87
+ // Build media placeholders
88
+ const mediaPlaceholders = [];
89
+ for (const url of mediaUrls) {
90
+ const placeholder = getMediaPlaceholder(url);
91
+ // Avoid duplicate placeholders
92
+ if (!mediaPlaceholders.includes(placeholder)) {
93
+ mediaPlaceholders.push(placeholder);
94
+ }
95
+ }
96
+ // Combine text and media placeholders
97
+ const parts = [];
98
+ if (text.trim()) {
99
+ parts.push(text);
100
+ }
101
+ if (mediaPlaceholders.length > 0) {
102
+ parts.push(...mediaPlaceholders);
103
+ }
104
+ return parts.join("\n");
105
+ }
106
+ export function createStepfunReplyDispatcher(params) {
107
+ const core = getStepfunRuntime();
108
+ const { cfg, agentId, runtime, chatSessionId, accountId } = params;
109
+ const account = resolveStepfunAccount({ cfg, accountId });
110
+ // Buffer for accumulating streaming content
111
+ let contentBuffer = "";
112
+ let isProcessing = false;
113
+ let hasPendingFlush = false;
114
+ // Flush the buffer and send accumulated content
115
+ const flushBuffer = async () => {
116
+ if (isProcessing || !contentBuffer.trim()) {
117
+ return;
118
+ }
119
+ isProcessing = true;
120
+ hasPendingFlush = false;
121
+ const textToSend = contentBuffer;
122
+ contentBuffer = ""; // Clear buffer before async operation
123
+ runtime.log?.(`stepfun[${account.accountId}] flush: sending accumulated text=${textToSend.slice(0, 100)}...`);
124
+ const outboundContent = {
125
+ type: "message",
126
+ content: textToSend,
127
+ };
128
+ try {
129
+ const success = await sendMessageStepfun({
130
+ cfg,
131
+ chatSessionId,
132
+ content: outboundContent,
133
+ accountId,
134
+ runtime: { log: runtime.log, error: runtime.error },
135
+ });
136
+ if (!success) {
137
+ // Restore buffer on error so we don't lose content
138
+ contentBuffer = textToSend + contentBuffer;
139
+ runtime.error?.(`stepfun[${account.accountId}] flush failed: message could not be sent`);
140
+ }
141
+ }
142
+ catch (err) {
143
+ const errorMsg = err instanceof Error ? err.message : JSON.stringify(err);
144
+ runtime.error?.(`stepfun[${account.accountId}] flush failed: ${errorMsg}`);
145
+ // Restore buffer on error so we don't lose content
146
+ contentBuffer = textToSend + contentBuffer;
147
+ // Don't rethrow - errors should not propagate to the gateway
148
+ }
149
+ finally {
150
+ isProcessing = false;
151
+ // Check if new content arrived while sending, and flush it
152
+ if (hasPendingFlush && contentBuffer.trim()) {
153
+ runtime.log?.(`stepfun[${account.accountId}] flush: more content pending, flushing again`);
154
+ setImmediate(() => scheduleFlush());
155
+ }
156
+ }
157
+ };
158
+ // Schedule buffer flush - ensures all messages are sent even if they arrive during send
159
+ const scheduleFlush = () => {
160
+ if (isProcessing) {
161
+ // Mark that we have pending content to flush after current send completes
162
+ hasPendingFlush = true;
163
+ runtime.log?.(`stepfun[${account.accountId}] scheduleFlush: send in progress, marking pending`);
164
+ return;
165
+ }
166
+ flushBuffer().catch((err) => {
167
+ runtime.error?.(`stepfun[${account.accountId}] flush error: ${String(err)}`);
168
+ });
169
+ };
170
+ const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
171
+ deliver: async (payload, info) => {
172
+ const { kind } = info;
173
+ // Build message content including media placeholders
174
+ const messageContent = buildMessageContent(payload);
175
+ runtime.log?.(`stepfun[${account.accountId}] deliver called: kind=${kind}, text=${messageContent.slice(0, 100)}, bufferBefore=${contentBuffer.slice(0, 100)}`);
176
+ if (!messageContent.trim()) {
177
+ runtime.log?.(`stepfun[${account.accountId}] deliver: empty content, skipping`);
178
+ return;
179
+ }
180
+ // Check if content is an error message that should not be sent to users
181
+ if (isErrorContent(messageContent)) {
182
+ runtime.error?.(`stepfun[${account.accountId}] deliver: blocked error message from being sent to user: "${messageContent}"`);
183
+ return;
184
+ }
185
+ // For tool results, send immediately without buffering
186
+ if (kind === "tool") {
187
+ runtime.log?.(`stepfun[${account.accountId}] tool result: sending immediately`);
188
+ // Add tool call placeholder prefix
189
+ const toolContent = messageContent.trim()
190
+ ? `【这是一条工具调用消息】\n${messageContent}`
191
+ : "【这是一条工具调用消息】";
192
+ contentBuffer += toolContent;
193
+ scheduleFlush();
194
+ return;
195
+ }
196
+ // For block replies, just accumulate without sending
197
+ // Block replies are streaming chunks that should not be sent individually
198
+ if (kind === "block") {
199
+ contentBuffer += messageContent;
200
+ runtime.log?.(`stepfun[${account.accountId}] block reply: accumulated, buffer=${contentBuffer.slice(0, 100)}...`);
201
+ return;
202
+ }
203
+ // For final replies, flush all accumulated content
204
+ if (kind === "final") {
205
+ contentBuffer += messageContent;
206
+ runtime.log?.(`stepfun[${account.accountId}] final reply: flushing accumulated content, total=${contentBuffer.slice(0, 100)}...`);
207
+ scheduleFlush();
208
+ return;
209
+ }
210
+ },
211
+ onError: (err, info) => {
212
+ const errorMsg = err instanceof Error ? err.message : JSON.stringify(err);
213
+ runtime.error?.(`stepfun[${account.accountId}] ${info.kind} reply failed: ${errorMsg}`);
214
+ },
215
+ });
216
+ // Override markDispatchIdle to flush remaining content when done
217
+ const originalMarkDispatchIdle = markDispatchIdle;
218
+ const wrappedMarkDispatchIdle = () => {
219
+ runtime.log?.(`stepfun[${account.accountId}] markDispatchIdle called, flushing remaining content`);
220
+ // Flush any remaining content
221
+ if (contentBuffer.trim()) {
222
+ flushBuffer().catch((err) => {
223
+ runtime.error?.(`stepfun[${account.accountId}] final flush error: ${String(err)}`);
224
+ });
225
+ }
226
+ originalMarkDispatchIdle();
227
+ };
228
+ return {
229
+ dispatcher,
230
+ replyOptions,
231
+ markDispatchIdle: wrappedMarkDispatchIdle,
232
+ };
233
+ }
234
+ //# sourceMappingURL=reply-dispatcher.js.map
@@ -0,0 +1,4 @@
1
+ import type { PluginRuntime } from "openclaw/plugin-sdk";
2
+ export declare function setStepfunRuntime(next: PluginRuntime): void;
3
+ export declare function getStepfunRuntime(): PluginRuntime;
4
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1,11 @@
1
+ let runtime = null;
2
+ export function setStepfunRuntime(next) {
3
+ runtime = next;
4
+ }
5
+ export function getStepfunRuntime() {
6
+ if (!runtime) {
7
+ throw new Error("Stepfun runtime not initialized");
8
+ }
9
+ return runtime;
10
+ }
11
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1,19 @@
1
+ import type { OpenClawConfig } from "openclaw/plugin-sdk";
2
+ import type { OutboundMessageContent } from "./types.js";
3
+ /**
4
+ * Send a message to Stepfun gateway using Protobuf format.
5
+ * The content is wrapped in ChatMessage and sent via BotMsg service.
6
+ * Note: Returns boolean indicating success/failure instead of throwing,
7
+ * to ensure errors don't propagate to the OpenClaw gateway.
8
+ */
9
+ export declare function sendMessageStepfun(params: {
10
+ cfg: OpenClawConfig;
11
+ chatSessionId: string;
12
+ content: OutboundMessageContent;
13
+ accountId?: string;
14
+ runtime?: {
15
+ log?: (msg: string) => void;
16
+ error?: (msg: string) => void;
17
+ };
18
+ }): Promise<boolean>;
19
+ //# sourceMappingURL=send.d.ts.map
@@ -0,0 +1,66 @@
1
+ import { resolveStepfunAccount } from "./accounts.js";
2
+ import { BotMsgSocketClient } from "./websocket/service.js";
3
+ import { ChatMessage, ChatMessageContent, MessageType, BotType, SendMessagesRequest, } from "./proto/capy/botmsg/botmsg_pb.js";
4
+ import { getSocket } from "./websocket/index.js";
5
+ /**
6
+ * Send a message to Stepfun gateway using Protobuf format.
7
+ * The content is wrapped in ChatMessage and sent via BotMsg service.
8
+ * Note: Returns boolean indicating success/failure instead of throwing,
9
+ * to ensure errors don't propagate to the OpenClaw gateway.
10
+ */
11
+ export async function sendMessageStepfun(params) {
12
+ const { cfg, chatSessionId, content, accountId, runtime } = params;
13
+ const log = runtime?.log ?? console.log;
14
+ const error = runtime?.error ?? console.error;
15
+ const account = resolveStepfunAccount({ cfg, accountId });
16
+ if (!account.configured) {
17
+ error(`[stepfun] sendMessageStepfun: account not configured`);
18
+ return false;
19
+ }
20
+ // Ensure socket is initialized with auth from config
21
+ const socket = getSocket();
22
+ const token = account.config.appToken;
23
+ const appId = account.config.appId;
24
+ if (!token || !appId) {
25
+ error(`[stepfun] sendMessageStepfun: appId and appToken are required`);
26
+ return false;
27
+ }
28
+ socket.setAuth(token, appId);
29
+ // Convert content to string
30
+ let contentStr;
31
+ if (typeof content.content === "string") {
32
+ contentStr = content.content;
33
+ }
34
+ else if (content.event) {
35
+ // Handle streaming events
36
+ contentStr = JSON.stringify(content.event);
37
+ }
38
+ else {
39
+ contentStr = JSON.stringify(content);
40
+ }
41
+ // Build Protobuf message
42
+ const chatMessage = new ChatMessage({
43
+ appId: appId,
44
+ sessionId: chatSessionId,
45
+ senderUid: "0", // Robot sends as 0
46
+ msgType: MessageType.MessageType_BOT_MSG,
47
+ botType: BotType.BotType_OPENCLAW,
48
+ content: new ChatMessageContent({
49
+ content: contentStr,
50
+ }),
51
+ });
52
+ const request = new SendMessagesRequest({
53
+ message: chatMessage,
54
+ });
55
+ // Send via BotMsg service
56
+ try {
57
+ await BotMsgSocketClient.sendMessages(request);
58
+ return true;
59
+ }
60
+ catch (err) {
61
+ const errorMsg = err instanceof Error ? err.message : JSON.stringify(err);
62
+ error(`[stepfun] Failed to send message: ${errorMsg}`);
63
+ return false;
64
+ }
65
+ }
66
+ //# sourceMappingURL=send.js.map
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Stepfun channel configuration in clawdbot config.
3
+ */
4
+ export type StepfunConfig = {
5
+ enabled?: boolean;
6
+ /** App ID for bot authentication */
7
+ appId?: string;
8
+ /** App token for WebSocket connection authentication */
9
+ appToken?: string;
10
+ };
11
+ /**
12
+ * Resolved stepfun account with merged configuration.
13
+ */
14
+ export type ResolvedStepfunAccount = {
15
+ accountId: string;
16
+ enabled: boolean;
17
+ configured: boolean;
18
+ name?: string;
19
+ config: StepfunConfig;
20
+ };
21
+ export type InboundMessageType = "text";
22
+ export type InboundMessageConfig = {};
23
+ /**
24
+ * Inbound message from Stepfun gateway.
25
+ */
26
+ export type StepfunInboundMessage = {
27
+ chatSessionId: string;
28
+ type?: InboundMessageType;
29
+ config?: InboundMessageConfig;
30
+ appId: string;
31
+ messageId: string;
32
+ userId: string;
33
+ userName?: string;
34
+ content: string;
35
+ timestamp?: number;
36
+ };
37
+ /**
38
+ * Outbound message to Stepfun gateway (stepfun format).
39
+ */
40
+ export type StepfunOutboundMessage = {
41
+ chatSessionId: string;
42
+ role: "assistant";
43
+ content: string;
44
+ timestamp?: number;
45
+ };
46
+ /**
47
+ * Message context for internal processing.
48
+ */
49
+ export type StepfunMessageContext = {
50
+ chatSessionId: string;
51
+ messageId: string;
52
+ userId: string;
53
+ userName?: string;
54
+ content: string;
55
+ };
56
+ export type OutboundMessageContent = {
57
+ type: "message" | "stream";
58
+ content?: string;
59
+ messageId?: string;
60
+ event?: {
61
+ eventName: "start" | "text" | "done";
62
+ data: string;
63
+ };
64
+ };
65
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,17 @@
1
+ type CacheStatusType = Record<string, unknown>;
2
+ type WatchListType = Record<string, (() => void)[]>;
3
+ export declare class CacheEvent {
4
+ eventList: any[];
5
+ eventKeys: CacheStatusType;
6
+ watchList: WatchListType;
7
+ cacheStatus: CacheStatusType;
8
+ once(key: string): boolean;
9
+ set(key: string, data: unknown): void;
10
+ get(key: string): unknown;
11
+ do(cb: () => void | Promise<void>, cond: string[], cache?: boolean): void;
12
+ complete(key: string): void;
13
+ clear(): void;
14
+ }
15
+ export declare const globalCache: CacheEvent;
16
+ export {};
17
+ //# sourceMappingURL=cacheEvent.d.ts.map
@@ -0,0 +1,61 @@
1
+ export class CacheEvent {
2
+ eventList = [];
3
+ eventKeys = {};
4
+ watchList = {};
5
+ cacheStatus = {};
6
+ once(key) {
7
+ if (!this.cacheStatus[key]) {
8
+ this.cacheStatus[key] = 1;
9
+ return true;
10
+ }
11
+ return false;
12
+ }
13
+ set(key, data) {
14
+ this.cacheStatus[key] = data;
15
+ }
16
+ get(key) {
17
+ return this.cacheStatus[key];
18
+ }
19
+ do(cb, cond, cache = true) {
20
+ const canDo = cond?.every((i) => this.eventKeys[i]) || !cond;
21
+ if (!canDo) {
22
+ if (cache) {
23
+ cond.forEach((key) => {
24
+ if (!this.watchList[key]) {
25
+ this.watchList[key] = [];
26
+ }
27
+ this.watchList[key].push(() => this.do(cb, cond, false));
28
+ });
29
+ }
30
+ }
31
+ else {
32
+ try {
33
+ const result = cb();
34
+ // Handle async callbacks - catch any promise rejections
35
+ if (result instanceof Promise) {
36
+ result.catch((err) => {
37
+ console.error("[cacheEvent] async callback error:", err);
38
+ });
39
+ }
40
+ }
41
+ catch (err) {
42
+ console.error("[cacheEvent] callback error:", err);
43
+ }
44
+ }
45
+ }
46
+ complete(key) {
47
+ this.eventKeys[key] = 1;
48
+ if (this.watchList[key]) {
49
+ this.watchList[key].forEach((e) => e());
50
+ this.watchList[key] = [];
51
+ }
52
+ }
53
+ clear() {
54
+ this.eventList = [];
55
+ this.eventKeys = {};
56
+ this.watchList = {};
57
+ this.cacheStatus = {};
58
+ }
59
+ }
60
+ export const globalCache = new CacheEvent();
61
+ //# sourceMappingURL=cacheEvent.js.map
@@ -0,0 +1,32 @@
1
+ import type { MethodInfo, MethodInfoBiDiStreaming, MethodInfoClientStreaming, MethodInfoServerStreaming, MethodInfoUnary, PartialMessage, ServiceType } from '@bufbuild/protobuf';
2
+ import { ErrorRes } from './streamConnect.js';
3
+ export interface SocketCallbacks<O, T = any> {
4
+ onStart?: (params: {
5
+ msgId: string;
6
+ method: string;
7
+ }) => void;
8
+ onData?: (params: {
9
+ msgId: string;
10
+ method: string;
11
+ data: O;
12
+ throwError: (error: ErrorRes) => void;
13
+ resolve: (data: T) => void;
14
+ close: () => void;
15
+ }) => void;
16
+ onError?: (params: {
17
+ msgId: string;
18
+ method: string;
19
+ error: ErrorRes | Uint8Array;
20
+ }) => void;
21
+ throwError?: (error: ErrorRes) => void;
22
+ onClose?: () => void;
23
+ }
24
+ type PromiseMethod<I, O> = (request: I | Promise<I>, callbacks?: SocketCallbacks<O>, once?: boolean) => Promise<O>;
25
+ export type PromiseClient<T extends ServiceType> = {
26
+ [P in keyof T['methods']]: T['methods'][P] extends MethodInfoUnary<infer I, infer O> ? PromiseMethod<PartialMessage<I>, O> : T['methods'][P] extends MethodInfoServerStreaming<infer I, infer O> ? PromiseMethod<PartialMessage<I>, O> : T['methods'][P] extends MethodInfoClientStreaming<infer I, infer O> ? PromiseMethod<PartialMessage<I>, O> : T['methods'][P] extends MethodInfoBiDiStreaming<infer I, infer O> ? PromiseMethod<PartialMessage<I>, O> : never;
27
+ };
28
+ export declare function createSocketConnect<T extends ServiceType>(module: string, Service: T): PromiseClient<T>;
29
+ export declare function removeMsgListener(msgId: string): void;
30
+ export declare function createPromiseClient(module: string, methodInfo: MethodInfo): (payload: import("@bufbuild/protobuf").MessageType<import("@bufbuild/protobuf").AnyMessage>, callbacks?: SocketCallbacks<import("@bufbuild/protobuf").MessageType<import("@bufbuild/protobuf").AnyMessage>>, once?: boolean) => Promise<unknown>;
31
+ export {};
32
+ //# sourceMappingURL=connect.d.ts.map
@@ -0,0 +1,79 @@
1
+ import { makeAnyClient } from '@connectrpc/connect';
2
+ import { getSocket } from './index.js';
3
+ import { ErrorRes } from './streamConnect.js';
4
+ export function createSocketConnect(module, Service) {
5
+ return makeAnyClient(Service, (methodInfo) => {
6
+ return createPromiseClient(module, methodInfo);
7
+ });
8
+ }
9
+ export function removeMsgListener(msgId) {
10
+ const socket = getSocket();
11
+ socket?.events.off(msgId);
12
+ socket?.events.off(`error:${msgId}`);
13
+ socket._requestStreams.delete(msgId);
14
+ }
15
+ export function createPromiseClient(module, methodInfo) {
16
+ const { I, O } = methodInfo;
17
+ return (payload, callbacks, once = true) => {
18
+ const command = methodInfo.name;
19
+ const socket = getSocket();
20
+ const msgId = socket.send({
21
+ module,
22
+ command,
23
+ body: new I(payload).toBinary(),
24
+ type: methodInfo.kind,
25
+ });
26
+ return new Promise((resolve, reject) => {
27
+ const throwError = (error) => {
28
+ reject(error);
29
+ };
30
+ const socket = getSocket();
31
+ const closeHandler = () => {
32
+ removeMsgListener(msgId);
33
+ callbacks?.onClose?.();
34
+ };
35
+ socket?.on(msgId, (e) => {
36
+ if (!e || e instanceof ErrorRes) {
37
+ const error = e instanceof ErrorRes ? e : new ErrorRes({ code: 1, reason: 'data error' });
38
+ if (callbacks?.onError) {
39
+ callbacks.onError({ msgId, method: command, error });
40
+ }
41
+ reject(error);
42
+ removeMsgListener(msgId);
43
+ return;
44
+ }
45
+ const data = O.fromBinary(e);
46
+ if (callbacks?.onData) {
47
+ callbacks.onData({
48
+ msgId,
49
+ method: command,
50
+ // @ts-ignore
51
+ data,
52
+ throwError,
53
+ resolve,
54
+ close: closeHandler,
55
+ });
56
+ }
57
+ else {
58
+ resolve({ ...data, traceid: msgId });
59
+ }
60
+ if (once) {
61
+ removeMsgListener(msgId);
62
+ }
63
+ });
64
+ let rejected = 0;
65
+ socket?.on(`error:${msgId}`, (error) => {
66
+ if (callbacks?.onError) {
67
+ callbacks.onError({ msgId, method: command, error });
68
+ }
69
+ if (!rejected) {
70
+ reject(error);
71
+ rejected = 1;
72
+ }
73
+ removeMsgListener(msgId);
74
+ });
75
+ // todo 超时逻辑
76
+ });
77
+ };
78
+ }
79
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1,8 @@
1
+ export declare const BASE_URL: Record<Env, {
2
+ api?: string;
3
+ ws: string;
4
+ }>;
5
+ export declare enum Env {
6
+ Prod = "prod"
7
+ }
8
+ //# sourceMappingURL=constant.d.ts.map
@@ -0,0 +1,10 @@
1
+ export const BASE_URL = {
2
+ prod: {
3
+ ws: "wss://chatapi.stepfun.com/botapi/wss/bot",
4
+ },
5
+ };
6
+ export var Env;
7
+ (function (Env) {
8
+ Env["Prod"] = "prod";
9
+ })(Env || (Env = {}));
10
+ //# sourceMappingURL=constant.js.map
@@ -0,0 +1,15 @@
1
+ type Fn = {
2
+ fn: (args: object | null | undefined) => void;
3
+ once?: boolean;
4
+ };
5
+ type EventType<K extends string> = Record<K, Fn[]>;
6
+ export declare class Event<K extends string> {
7
+ eventList: EventType<K>;
8
+ on(key: K, fn: (args: any) => void, once?: boolean): void;
9
+ emit(key: K, args?: object): boolean;
10
+ off(key: K, fn?: (args: any) => void): void;
11
+ checkListenersCount(key: K): number;
12
+ }
13
+ export declare const commonEventBus: Event<string>;
14
+ export {};
15
+ //# sourceMappingURL=eventBus.d.ts.map
@@ -0,0 +1,46 @@
1
+ export class Event {
2
+ eventList = {};
3
+ on(key, fn, once) {
4
+ if (!this.eventList[key])
5
+ this.eventList[key] = [];
6
+ // console.log('on-------', Object.keys(this.eventList));
7
+ this.eventList[key].push({
8
+ fn,
9
+ once,
10
+ });
11
+ }
12
+ emit(key, args) {
13
+ if (this.eventList?.[key]) {
14
+ this.eventList[key].forEach((fn) => {
15
+ try {
16
+ fn.fn(args);
17
+ }
18
+ catch (err) {
19
+ console.error(`[eventBus] Error in listener for "${key}":`, err);
20
+ // Continue to next listener even if one fails
21
+ }
22
+ });
23
+ this.eventList[key] = this.eventList[key].filter((item) => !item.once);
24
+ return true;
25
+ }
26
+ else {
27
+ return false;
28
+ }
29
+ }
30
+ off(key, fn) {
31
+ if (fn) {
32
+ const index = this.eventList[key]?.findIndex((item) => item.fn === fn);
33
+ if (index !== undefined && index !== -1) {
34
+ this.eventList[key]?.splice(index, 1);
35
+ }
36
+ }
37
+ else {
38
+ this.eventList[key] = [];
39
+ }
40
+ }
41
+ checkListenersCount(key) {
42
+ return this.eventList[key]?.length;
43
+ }
44
+ }
45
+ export const commonEventBus = new Event();
46
+ //# sourceMappingURL=eventBus.js.map