wtfai 1.7.3 → 1.8.1

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
@@ -1,7 +1,8 @@
1
- import type { Workflow, WorkflowListResponse } from '@our-llm/shared/types';
1
+ import type { Workflow, WorkflowListResponse, DeviceInfo } from '@our-llm/shared/types';
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
  /**
@@ -47,4 +52,8 @@ export declare class Wtfai {
47
52
  * 创建工作流会话
48
53
  */
49
54
  createSession(workflowId: string, options?: SessionOptions): WorkflowSession;
55
+ /**
56
+ * 获取当前详细的设备信息(本地计算)
57
+ */
58
+ getDeviceInfo(): Promise<DeviceInfo>;
50
59
  }
package/dist/client.js CHANGED
@@ -1,5 +1,7 @@
1
+ import { getDeviceInfo } from "@our-llm/shared/fingerprint";
1
2
  import { WorkflowSession } from "./session.js";
2
3
  import { UploadService } from "./upload.js";
4
+ import { RealtimeService } from "./realtime.js";
3
5
  function _define_property(obj, key, value) {
4
6
  if (key in obj) Object.defineProperty(obj, key, {
5
7
  value: value,
@@ -32,12 +34,16 @@ class Wtfai {
32
34
  createSession(workflowId, options) {
33
35
  return new WorkflowSession(workflowId, this.baseUrl, this.uploadService, this.headers, options);
34
36
  }
37
+ async getDeviceInfo() {
38
+ return getDeviceInfo();
39
+ }
35
40
  constructor(config){
36
41
  _define_property(this, "baseUrl", void 0);
37
42
  _define_property(this, "uploadService", void 0);
38
43
  _define_property(this, "upload", void 0);
44
+ _define_property(this, "realtime", void 0);
39
45
  _define_property(this, "headers", void 0);
40
- this.baseUrl = config.baseUrl.replace(/\/$/, '');
46
+ this.baseUrl = (config.baseUrl || 'https://llm-api.ourschool.cc').replace(/\/$/, '');
41
47
  const headers = {
42
48
  ...config.headers
43
49
  };
@@ -48,6 +54,7 @@ class Wtfai {
48
54
  this.headers = headers;
49
55
  this.uploadService = new UploadService(this.baseUrl, this.headers);
50
56
  this.upload = this.uploadService;
57
+ this.realtime = new RealtimeService(this.baseUrl, this.headers);
51
58
  }
52
59
  }
53
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/session.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { SimpleMessage } from '@our-llm/shared/types';
1
+ import type { SimpleMessage, DeviceInfo } from '@our-llm/shared/types';
2
2
  import type { SessionOptions, SessionState, SessionEventListeners, SendInput, ListRecordsOptions, ListRecordsResult } from './types';
3
3
  import { UploadService } from './upload';
4
4
  /**
@@ -137,6 +137,16 @@ export declare class WorkflowSession {
137
137
  * @param options 请求配置
138
138
  */
139
139
  fetch(url: string, options?: RequestInit): Promise<any>;
140
+ /**
141
+ * 上报设备信息相关(用于日志监控)
142
+ */
143
+ reportDeviceInfo(): Promise<{
144
+ success: boolean;
145
+ }>;
146
+ /**
147
+ * 获取当前详细的设备信息(本地计算)
148
+ */
149
+ getDeviceInfo(): Promise<DeviceInfo>;
140
150
  /**
141
151
  * 发送消息执行工作流
142
152
  */
package/dist/session.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { getDeviceInfo, getFingerprint } from "@our-llm/shared/fingerprint";
1
2
  import { executeWorkflowSSE } from "./sse.js";
2
3
  import { v7 } from "uuid";
3
4
  function _define_property(obj, key, value) {
@@ -371,6 +372,32 @@ class WorkflowSession {
371
372
  }
372
373
  return response.json();
373
374
  }
375
+ async reportDeviceInfo() {
376
+ this.assertNotDisposed();
377
+ const deviceId = await getFingerprint();
378
+ const userAgent = navigator.userAgent;
379
+ const sessionId = this.state.threadId;
380
+ if (!sessionId) throw new Error('No active session (missing threadId)');
381
+ const payload = {
382
+ deviceId,
383
+ userAgent,
384
+ sessionId
385
+ };
386
+ const response = await fetch(`${this.baseUrl}/workflows/device-info`, {
387
+ method: 'POST',
388
+ headers: {
389
+ 'Content-Type': 'application/json',
390
+ ...this.headers
391
+ },
392
+ body: JSON.stringify(payload)
393
+ });
394
+ if (!response.ok) throw new Error('Failed to report device info');
395
+ return response.json();
396
+ }
397
+ async getDeviceInfo() {
398
+ this.assertNotDisposed();
399
+ return getDeviceInfo();
400
+ }
374
401
  async send({ parts: inputParts, ...rest }) {
375
402
  this.assertNotDisposed();
376
403
  if (this.state.isExecuting) throw new Error('工作流正在执行中');
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.7.3",
3
+ "version": "1.8.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -15,16 +15,16 @@
15
15
  "devDependencies": {
16
16
  "@eslint/js": "^9.39.4",
17
17
  "@rsbuild/plugin-react": "^1.4.6",
18
- "@rslib/core": "^0.21.0",
18
+ "@rslib/core": "^0.21.2",
19
19
  "@types/react": "^19.2.14",
20
20
  "eslint": "^9.39.4",
21
21
  "eslint-config-prettier": "^10.1.8",
22
22
  "eslint-plugin-prettier": "^5.5.5",
23
- "globals": "^17.4.0",
24
- "prettier": "^3.8.2",
23
+ "globals": "^17.5.0",
24
+ "prettier": "^3.8.3",
25
25
  "react": "^19.2.5",
26
26
  "typescript": "^5.9.3",
27
- "typescript-eslint": "^8.58.1"
27
+ "typescript-eslint": "^8.58.2"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": ">=16.9.0",
@@ -38,7 +38,7 @@
38
38
  "clsx": "^2.1.1",
39
39
  "compressorjs": "^1.3.0",
40
40
  "cos-js-sdk-v5": "^1.10.1",
41
- "jsonrepair": "^3.13.3",
41
+ "jsonrepair": "^3.14.0",
42
42
  "uuid": "^13.0.0",
43
43
  "@our-llm/shared": "3.0.1"
44
44
  },