wtfai 1.8.0 → 1.8.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.
package/README.md CHANGED
@@ -680,6 +680,50 @@ Content-Type: application/json
680
680
 
681
681
  ---
682
682
 
683
+ ---
684
+
685
+ ### RealtimeService
686
+
687
+ 提供跨端的实时消息总线功能(通过 `client.realtime` 访问)。
688
+
689
+ ##### `subscribe(topic: string, onEvent: (event: RealtimeEvent) => void)`
690
+
691
+ 订阅实时消息流。适合大屏端监听由其他端发送的指令或通知。
692
+
693
+ - **自动管理**:方法内部会自动计算并附加当前浏览器的设备指纹(deviceId),激活在线状态追踪。
694
+ - **返回值**:返回一个对象,包含 `unsubscribe()` 方法用于断开连接。
695
+
696
+ ```typescript
697
+ const sub = await client.realtime.subscribe('class:101', (event) => {
698
+ console.log('收到实时更新:', event.data);
699
+ // event.event 为事件名称,event.data 为业务数据
700
+ });
701
+
702
+ // 当页面销毁或不需要监听时,请务必手动释放:
703
+ // sub.unsubscribe();
704
+ ```
705
+
706
+ ##### `push(topic: string, event: string, data: any)`
707
+
708
+ 推送实时消息。适合手机端(或其他客户端)发送指令。
709
+
710
+ ```typescript
711
+ await client.realtime.push('class:101', 'msgA', {
712
+ foo: 'bar'
713
+ });
714
+ ```
715
+
716
+ ##### `getPresence(topic: string)`
717
+
718
+ 获取指定主题下的在线状态详情(如当前有多少个大屏在线)。
719
+
720
+ ```typescript
721
+ const { onlineCount, clients } = await client.realtime.getPresence('class:101');
722
+ console.log(`当前在线设备数: ${onlineCount}`);
723
+ ```
724
+
725
+ ---
726
+
683
727
  ### UploadService
684
728
 
685
729
  文件上传服务(通过 `client.upload` 访问)。
package/dist/client.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { Workflow, WorkflowListResponse, DeviceInfo } from '@our-llm/shared
2
2
  import type { ClientConfig, SessionOptions } from './types';
3
3
  import { WorkflowSession } from './session';
4
4
  import { UploadService } from './upload';
5
+ import { RealtimeService } from './realtime';
5
6
  /**
6
7
  * Wtfai 客户端
7
8
  *
@@ -33,6 +34,10 @@ export declare class Wtfai {
33
34
  * 文件上传服务
34
35
  */
35
36
  readonly upload: UploadService;
37
+ /**
38
+ * 实时同步服务
39
+ */
40
+ readonly realtime: RealtimeService;
36
41
  private readonly headers;
37
42
  constructor(config: ClientConfig);
38
43
  /**
package/dist/client.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { getDeviceInfo } from "@our-llm/shared/fingerprint";
2
2
  import { WorkflowSession } from "./session.js";
3
3
  import { UploadService } from "./upload.js";
4
+ import { RealtimeService } from "./realtime.js";
4
5
  function _define_property(obj, key, value) {
5
6
  if (key in obj) Object.defineProperty(obj, key, {
6
7
  value: value,
@@ -40,8 +41,9 @@ class Wtfai {
40
41
  _define_property(this, "baseUrl", void 0);
41
42
  _define_property(this, "uploadService", void 0);
42
43
  _define_property(this, "upload", void 0);
44
+ _define_property(this, "realtime", void 0);
43
45
  _define_property(this, "headers", void 0);
44
- this.baseUrl = config.baseUrl.replace(/\/$/, '');
46
+ this.baseUrl = (config.baseUrl || 'https://llm-api.ourschool.cc').replace(/\/$/, '');
45
47
  const headers = {
46
48
  ...config.headers
47
49
  };
@@ -52,6 +54,7 @@ class Wtfai {
52
54
  this.headers = headers;
53
55
  this.uploadService = new UploadService(this.baseUrl, this.headers);
54
56
  this.upload = this.uploadService;
57
+ this.realtime = new RealtimeService(this.baseUrl, this.headers);
55
58
  }
56
59
  }
57
60
  export { Wtfai };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,8 @@ import { Wtfai } from './client';
2
2
  export default Wtfai;
3
3
  export { WorkflowSession } from './session';
4
4
  export { UploadService } from './upload';
5
+ export { RealtimeService } from './realtime';
5
6
  export type { ClientConfig, SessionOptions, SessionState, SessionEventListeners, SendInput, UploadParams, UploadImageParams, CompressOptions, WorkflowInfo, ContentPart, ContentAction, } from './types';
6
- export type { SimpleMessage, Workflow, WorkflowConfig, } from '@our-llm/shared/types';
7
+ export type { SimpleMessage, Workflow, WorkflowConfig, RealtimeEvent, RealtimePresence, RealtimePushDto, } from '@our-llm/shared/types';
7
8
  export { Markdown, type MarkdownProps, WorkflowRegistry } from './ui/markdown';
8
9
  export type { WorkflowInfoMapping } from './ui/context';
package/dist/index.js CHANGED
@@ -2,5 +2,6 @@ import { Wtfai } from "./client.js";
2
2
  const src = Wtfai;
3
3
  export { WorkflowSession } from "./session.js";
4
4
  export { UploadService } from "./upload.js";
5
+ export { RealtimeService } from "./realtime.js";
5
6
  export { Markdown, WorkflowRegistry } from "./ui/markdown.js";
6
7
  export default src;
@@ -0,0 +1,23 @@
1
+ import type { RealtimeEvent, RealtimePresence } from '@our-llm/shared/types';
2
+ export declare class RealtimeService {
3
+ private readonly baseUrl;
4
+ private readonly headers;
5
+ constructor(baseUrl: string, headers: Record<string, string>);
6
+ /**
7
+ * 订阅实时消息流 (适合大屏端)
8
+ * @param topic 主题名称
9
+ * @param onEvent 事件回调
10
+ * @returns 返回一个包含 unsubscribe 方法的对象
11
+ */
12
+ subscribe(topic: string, onEvent: (event: RealtimeEvent) => void): Promise<{
13
+ unsubscribe: () => void;
14
+ }>;
15
+ /**
16
+ * 推送实时消息 (适合手机端)
17
+ */
18
+ push(topic: string, event: string, data: any): Promise<void>;
19
+ /**
20
+ * 获取当前主题的在线状态
21
+ */
22
+ getPresence(topic: string): Promise<RealtimePresence>;
23
+ }
@@ -0,0 +1,76 @@
1
+ import { fetchEventSource } from "@microsoft/fetch-event-source";
2
+ import { getFingerprint } from "@our-llm/shared/fingerprint";
3
+ function _define_property(obj, key, value) {
4
+ if (key in obj) Object.defineProperty(obj, key, {
5
+ value: value,
6
+ enumerable: true,
7
+ configurable: true,
8
+ writable: true
9
+ });
10
+ else obj[key] = value;
11
+ return obj;
12
+ }
13
+ class RealtimeService {
14
+ async subscribe(topic, onEvent) {
15
+ const deviceId = await getFingerprint();
16
+ const controller = new AbortController();
17
+ const query = `?deviceId=${encodeURIComponent(deviceId)}`;
18
+ const url = `${this.baseUrl}/workflows/realtime/streams/${topic}${query}`;
19
+ fetchEventSource(url, {
20
+ method: 'GET',
21
+ headers: {
22
+ Accept: 'text/event-stream',
23
+ ...this.headers
24
+ },
25
+ signal: controller.signal,
26
+ onmessage (msg) {
27
+ if (!msg.data) return;
28
+ if ('message' === msg.event || !msg.event) try {
29
+ const event = JSON.parse(msg.data);
30
+ onEvent(event);
31
+ } catch (e) {
32
+ console.error('[SDK Realtime] Failed to parse message', e);
33
+ }
34
+ },
35
+ onerror (err) {
36
+ console.error('[SDK Realtime] Connection error', err);
37
+ }
38
+ });
39
+ return {
40
+ unsubscribe: ()=>{
41
+ controller.abort();
42
+ }
43
+ };
44
+ }
45
+ async push(topic, event, data) {
46
+ const dto = {
47
+ topic,
48
+ event,
49
+ data
50
+ };
51
+ const response = await fetch(`${this.baseUrl}/workflows/realtime/push`, {
52
+ method: 'POST',
53
+ headers: {
54
+ 'Content-Type': 'application/json',
55
+ ...this.headers
56
+ },
57
+ body: JSON.stringify(dto)
58
+ });
59
+ if (!response.ok) throw new Error(`推送实时消息失败: ${response.statusText}`);
60
+ }
61
+ async getPresence(topic) {
62
+ const response = await fetch(`${this.baseUrl}/workflows/realtime/presence/${topic}`, {
63
+ method: 'GET',
64
+ headers: this.headers
65
+ });
66
+ if (!response.ok) throw new Error(`获取在线状态失败: ${response.statusText}`);
67
+ return response.json();
68
+ }
69
+ constructor(baseUrl, headers){
70
+ _define_property(this, "baseUrl", void 0);
71
+ _define_property(this, "headers", void 0);
72
+ this.baseUrl = baseUrl;
73
+ this.headers = headers;
74
+ }
75
+ }
76
+ export { RealtimeService };
package/dist/types.d.ts CHANGED
@@ -2,8 +2,8 @@
2
2
  * SDK 配置类型
3
3
  */
4
4
  export interface ClientConfig {
5
- /** API 服务器地址 */
6
- baseUrl: string;
5
+ /** API 服务器地址,默认为 https://llm-api.ourschool.cc */
6
+ baseUrl?: string;
7
7
  /** 请求超时时间(毫秒) */
8
8
  timeout?: number;
9
9
  /** 自定义请求头 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wtfai",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {