vite-plugin-opencode-assistant 1.0.27 → 1.0.29

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 (37) hide show
  1. package/es/client/App.vue.js +64 -36
  2. package/es/client/composables/index.d.ts +10 -0
  3. package/es/client/composables/index.js +22 -0
  4. package/es/client/composables/useOpencodeSSE.d.ts +64 -0
  5. package/es/client/composables/useOpencodeSSE.js +29 -0
  6. package/es/client/composables/useOpencodeSessionSSE.d.ts +66 -0
  7. package/es/client/composables/useOpencodeSessionSSE.js +168 -0
  8. package/es/client/composables/useSSE.d.ts +85 -18
  9. package/es/client/composables/useSSE.js +101 -44
  10. package/es/client/composables/useServerSSE.d.ts +53 -0
  11. package/es/client/composables/useServerSSE.js +36 -0
  12. package/es/client/composables/useServiceStatus.d.ts +0 -2
  13. package/es/client/composables/useServiceStatus.js +1 -7
  14. package/es/client/composables/useSessions.d.ts +24 -5
  15. package/es/client/composables/useSessions.js +16 -2
  16. package/es/core/proxy-server.js +67 -159
  17. package/es/index.js +3 -1
  18. package/lib/client/App.vue.js +63 -35
  19. package/lib/client/composables/index.d.ts +10 -0
  20. package/lib/client/composables/index.js +54 -0
  21. package/lib/client/composables/useOpencodeSSE.d.ts +64 -0
  22. package/lib/client/composables/useOpencodeSSE.js +52 -0
  23. package/lib/client/composables/useOpencodeSessionSSE.d.ts +66 -0
  24. package/lib/client/composables/useOpencodeSessionSSE.js +187 -0
  25. package/lib/client/composables/useSSE.d.ts +85 -18
  26. package/lib/client/composables/useSSE.js +100 -43
  27. package/lib/client/composables/useServerSSE.d.ts +53 -0
  28. package/lib/client/composables/useServerSSE.js +59 -0
  29. package/lib/client/composables/useServiceStatus.d.ts +0 -2
  30. package/lib/client/composables/useServiceStatus.js +1 -7
  31. package/lib/client/composables/useSessions.d.ts +24 -5
  32. package/lib/client/composables/useSessions.js +16 -2
  33. package/lib/client.js +2823 -2566
  34. package/lib/core/proxy-server.js +67 -159
  35. package/lib/index.js +3 -1
  36. package/lib/style.css +1 -1
  37. package/package.json +4 -4
@@ -0,0 +1,64 @@
1
+ import { type Ref } from "vue";
2
+ /**
3
+ * OpenCode SSE 事件 payload
4
+ */
5
+ export interface OpencodeSSEPayload {
6
+ type: string;
7
+ properties: {
8
+ sessionID?: string;
9
+ status?: {
10
+ type: string;
11
+ };
12
+ info?: {
13
+ sessionID?: string;
14
+ role?: string;
15
+ time?: {
16
+ completed?: number;
17
+ created?: number;
18
+ updated?: number;
19
+ archived?: number;
20
+ compacting?: number;
21
+ };
22
+ title?: string;
23
+ id?: string;
24
+ summary?: {
25
+ additions?: number;
26
+ deletions?: number;
27
+ files?: number;
28
+ };
29
+ };
30
+ [key: string]: unknown;
31
+ };
32
+ }
33
+ /**
34
+ * OpenCode SSE 消息结构
35
+ */
36
+ export interface OpencodeSSEMessage {
37
+ payload: OpencodeSSEPayload;
38
+ }
39
+ /**
40
+ * OpenCode SSE 配置选项
41
+ */
42
+ export interface OpencodeSSEOptions {
43
+ /** OpenCode proxy 基础 URL (例如: http://localhost:4098/xxx) */
44
+ proxyBaseUrl: string;
45
+ /** 是否启用 */
46
+ enabled?: Ref<boolean>;
47
+ /** 事件处理器 */
48
+ onEvent?: (payload: OpencodeSSEPayload) => void;
49
+ /** 连接成功回调 */
50
+ onConnected?: () => void;
51
+ /** 连接错误回调 */
52
+ onError?: (error: Error) => void;
53
+ }
54
+ /**
55
+ * 监听 OpenCode SSE 事件
56
+ * 端点: /global/event (通过 proxy server)
57
+ * 只负责连接管理和消息分发,不包含业务逻辑
58
+ */
59
+ export declare function useOpencodeSSE(options: OpencodeSSEOptions): {
60
+ status: Ref<import("./useSSE").SSEConnectionStatus, import("./useSSE").SSEConnectionStatus>;
61
+ isConnected: import("vue").ComputedRef<boolean>;
62
+ connect: () => void;
63
+ disconnect: () => void;
64
+ };
@@ -0,0 +1,52 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var useOpencodeSSE_exports = {};
19
+ __export(useOpencodeSSE_exports, {
20
+ useOpencodeSSE: () => useOpencodeSSE
21
+ });
22
+ module.exports = __toCommonJS(useOpencodeSSE_exports);
23
+ var import_useSSE = require("./useSSE");
24
+ function useOpencodeSSE(options) {
25
+ const { proxyBaseUrl, enabled, onEvent, onConnected, onError } = options;
26
+ const endpoint = `${proxyBaseUrl}/global/event`;
27
+ const { status, isConnected, connect, disconnect } = (0, import_useSSE.useSSE)({
28
+ endpoint,
29
+ autoConnect: false,
30
+ enabled,
31
+ onMessage: (data) => {
32
+ const message = data;
33
+ const payload = message.payload;
34
+ if (!payload) return;
35
+ onEvent == null ? void 0 : onEvent(payload);
36
+ },
37
+ onConnected,
38
+ onError
39
+ });
40
+ return {
41
+ // 状态
42
+ status,
43
+ isConnected,
44
+ // 方法
45
+ connect,
46
+ disconnect
47
+ };
48
+ }
49
+ // Annotate the CommonJS export names for ESM import in node:
50
+ 0 && (module.exports = {
51
+ useOpencodeSSE
52
+ });
@@ -0,0 +1,66 @@
1
+ import { type Ref, type ComputedRef } from "vue";
2
+ /**
3
+ * Session 状态类型
4
+ */
5
+ export type OpencodeSessionStatusType = "idle" | "running" | "streaming" | "completed";
6
+ /**
7
+ * Session 思考状态
8
+ */
9
+ export interface OpencodeSessionThinkingState {
10
+ thinking: boolean;
11
+ statusType: OpencodeSessionStatusType;
12
+ hasPending: boolean;
13
+ }
14
+ /**
15
+ * Session 更新事件数据
16
+ */
17
+ export interface OpencodeSessionUpdateData {
18
+ id: string;
19
+ title?: string;
20
+ time?: {
21
+ created?: number;
22
+ updated?: number;
23
+ archived?: number;
24
+ compacting?: number;
25
+ };
26
+ summary?: {
27
+ additions?: number;
28
+ deletions?: number;
29
+ files?: number;
30
+ };
31
+ }
32
+ /**
33
+ * OpenCode Session SSE 配置选项
34
+ */
35
+ export interface OpencodeSessionSSEOptions {
36
+ /** OpenCode proxy 基础 URL */
37
+ proxyBaseUrl: string;
38
+ /** 当前 session ID (响应式) */
39
+ currentSessionId: Ref<string | null>;
40
+ /** 是否启用 */
41
+ enabled?: Ref<boolean>;
42
+ /** 连接成功回调 */
43
+ onConnected?: () => void;
44
+ /** Session 更新回调 (包括标题变化) */
45
+ onSessionUpdate?: (session: OpencodeSessionUpdateData) => void;
46
+ }
47
+ /**
48
+ * 监听 OpenCode Session SSE 事件
49
+ * 专注于 session thinking 状态管理和标题更新
50
+ */
51
+ export declare function useOpencodeSessionSSE(options: OpencodeSessionSSEOptions): {
52
+ status: Ref<import("./useSSE").SSEConnectionStatus, import("./useSSE").SSEConnectionStatus>;
53
+ isConnected: ComputedRef<boolean>;
54
+ sessionStates: Ref<Record<string, OpencodeSessionThinkingState>, Record<string, OpencodeSessionThinkingState>>;
55
+ currentThinking: ComputedRef<boolean>;
56
+ currentSessionState: ComputedRef<OpencodeSessionThinkingState | null>;
57
+ hasAnyThinking: ComputedRef<boolean>;
58
+ thinkingSessionCount: ComputedRef<number>;
59
+ connect: () => void;
60
+ disconnect: () => void;
61
+ clearSessionState: (sessionID: string) => void;
62
+ clearAllSessionStates: () => void;
63
+ getSessionState: (sessionID: string) => OpencodeSessionThinkingState | undefined;
64
+ isSessionThinking: (sessionID: string) => boolean;
65
+ getSessionsThinking: (sessionIds: string[]) => Record<string, boolean>;
66
+ };
@@ -0,0 +1,187 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
9
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
+ var __spreadValues = (a, b) => {
11
+ for (var prop in b || (b = {}))
12
+ if (__hasOwnProp.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ if (__getOwnPropSymbols)
15
+ for (var prop of __getOwnPropSymbols(b)) {
16
+ if (__propIsEnum.call(b, prop))
17
+ __defNormalProp(a, prop, b[prop]);
18
+ }
19
+ return a;
20
+ };
21
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
22
+ var __export = (target, all) => {
23
+ for (var name in all)
24
+ __defProp(target, name, { get: all[name], enumerable: true });
25
+ };
26
+ var __copyProps = (to, from, except, desc) => {
27
+ if (from && typeof from === "object" || typeof from === "function") {
28
+ for (let key of __getOwnPropNames(from))
29
+ if (!__hasOwnProp.call(to, key) && key !== except)
30
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
31
+ }
32
+ return to;
33
+ };
34
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
35
+ var useOpencodeSessionSSE_exports = {};
36
+ __export(useOpencodeSessionSSE_exports, {
37
+ useOpencodeSessionSSE: () => useOpencodeSessionSSE
38
+ });
39
+ module.exports = __toCommonJS(useOpencodeSessionSSE_exports);
40
+ var import_vue = require("vue");
41
+ var import_useOpencodeSSE = require("./useOpencodeSSE");
42
+ function useOpencodeSessionSSE(options) {
43
+ const { proxyBaseUrl, currentSessionId, enabled, onConnected, onSessionUpdate } = options;
44
+ const sessionStates = (0, import_vue.ref)({});
45
+ function isPendingMessage(info) {
46
+ var _a;
47
+ return (info == null ? void 0 : info.role) === "assistant" && typeof ((_a = info == null ? void 0 : info.time) == null ? void 0 : _a.completed) !== "number";
48
+ }
49
+ function updateThinkingState(sessionID) {
50
+ const state = sessionStates.value[sessionID];
51
+ const isThinking = (state == null ? void 0 : state.hasPending) || (state == null ? void 0 : state.statusType) !== "idle";
52
+ sessionStates.value[sessionID] = {
53
+ thinking: isThinking,
54
+ statusType: (state == null ? void 0 : state.statusType) || "idle",
55
+ hasPending: (state == null ? void 0 : state.hasPending) || false
56
+ };
57
+ }
58
+ function handleEvent(payload) {
59
+ var _a;
60
+ const type = payload.type;
61
+ const props = payload.properties;
62
+ switch (type) {
63
+ case "session.updated": {
64
+ const info = props.info;
65
+ if (!(info == null ? void 0 : info.id)) return;
66
+ onSessionUpdate == null ? void 0 : onSessionUpdate({
67
+ id: info.id,
68
+ title: info.title,
69
+ time: info.time,
70
+ summary: info.summary
71
+ });
72
+ break;
73
+ }
74
+ case "session.status": {
75
+ const sessionID = props.sessionID;
76
+ if (!sessionID) return;
77
+ const statusType = ((_a = props.status) == null ? void 0 : _a.type) || "idle";
78
+ sessionStates.value[sessionID] = {
79
+ thinking: statusType !== "idle",
80
+ statusType,
81
+ hasPending: false
82
+ };
83
+ updateThinkingState(sessionID);
84
+ break;
85
+ }
86
+ case "message.updated": {
87
+ const info = props.info;
88
+ if (!(info == null ? void 0 : info.sessionID)) return;
89
+ const sessionID = info.sessionID;
90
+ if (info.role === "assistant") {
91
+ const hasPending = isPendingMessage(info);
92
+ const current = sessionStates.value[sessionID] || {
93
+ thinking: false,
94
+ statusType: "idle",
95
+ hasPending: false
96
+ };
97
+ sessionStates.value[sessionID] = __spreadProps(__spreadValues({}, current), {
98
+ hasPending
99
+ });
100
+ updateThinkingState(sessionID);
101
+ }
102
+ break;
103
+ }
104
+ case "message.part.delta": {
105
+ const sessionID = props.sessionID;
106
+ if (!sessionID) return;
107
+ const current = sessionStates.value[sessionID];
108
+ if (!(current == null ? void 0 : current.hasPending)) {
109
+ sessionStates.value[sessionID] = {
110
+ thinking: true,
111
+ statusType: (current == null ? void 0 : current.statusType) || "idle",
112
+ hasPending: true
113
+ };
114
+ }
115
+ break;
116
+ }
117
+ }
118
+ }
119
+ const sse = (0, import_useOpencodeSSE.useOpencodeSSE)({
120
+ proxyBaseUrl,
121
+ enabled,
122
+ onEvent: handleEvent,
123
+ onConnected
124
+ });
125
+ const currentThinking = (0, import_vue.computed)(() => {
126
+ var _a, _b;
127
+ const id = currentSessionId.value;
128
+ if (!id) return false;
129
+ return (_b = (_a = sessionStates.value[id]) == null ? void 0 : _a.thinking) != null ? _b : false;
130
+ });
131
+ const currentSessionState = (0, import_vue.computed)(() => {
132
+ const id = currentSessionId.value;
133
+ if (!id) return null;
134
+ return sessionStates.value[id] || null;
135
+ });
136
+ const hasAnyThinking = (0, import_vue.computed)(() => {
137
+ return Object.values(sessionStates.value).some((state) => state.thinking);
138
+ });
139
+ const thinkingSessionCount = (0, import_vue.computed)(() => {
140
+ return Object.values(sessionStates.value).filter((state) => state.thinking).length;
141
+ });
142
+ function clearSessionState(sessionID) {
143
+ delete sessionStates.value[sessionID];
144
+ }
145
+ function clearAllSessionStates() {
146
+ sessionStates.value = {};
147
+ }
148
+ function getSessionState(sessionID) {
149
+ return sessionStates.value[sessionID];
150
+ }
151
+ function isSessionThinking(sessionID) {
152
+ var _a, _b;
153
+ return (_b = (_a = sessionStates.value[sessionID]) == null ? void 0 : _a.thinking) != null ? _b : false;
154
+ }
155
+ function getSessionsThinking(sessionIds) {
156
+ return sessionIds.reduce(
157
+ (acc, id) => {
158
+ acc[id] = isSessionThinking(id);
159
+ return acc;
160
+ },
161
+ {}
162
+ );
163
+ }
164
+ return {
165
+ // SSE 基础状态
166
+ status: sse.status,
167
+ isConnected: sse.isConnected,
168
+ // session 状态
169
+ sessionStates,
170
+ currentThinking,
171
+ currentSessionState,
172
+ hasAnyThinking,
173
+ thinkingSessionCount,
174
+ // 方法
175
+ connect: sse.connect,
176
+ disconnect: sse.disconnect,
177
+ clearSessionState,
178
+ clearAllSessionStates,
179
+ getSessionState,
180
+ isSessionThinking,
181
+ getSessionsThinking
182
+ };
183
+ }
184
+ // Annotate the CommonJS export names for ESM import in node:
185
+ 0 && (module.exports = {
186
+ useOpencodeSessionSSE
187
+ });
@@ -1,20 +1,87 @@
1
- import { ServiceStartupTask } from "@vite-plugin-opencode-assistant/shared";
2
- interface SSEStatusSyncData {
3
- type: "STATUS_SYNC";
4
- isStarted?: boolean;
5
- task: ServiceStartupTask;
6
- errorType?: string;
7
- errorMessage?: string;
1
+ import { type Ref } from "vue";
2
+ /**
3
+ * SSE 连接状态
4
+ */
5
+ export type SSEConnectionStatus = "idle" | "connecting" | "connected" | "disconnected" | "error";
6
+ /**
7
+ * SSE 配置选项
8
+ */
9
+ export interface SSEOptions {
10
+ /** SSE 端点 URL */
11
+ endpoint: string;
12
+ /** 是否自动连接 */
13
+ autoConnect?: boolean;
14
+ /** 是否启用 (响应式) */
15
+ enabled?: Ref<boolean>;
16
+ /** 最大重试次数 */
17
+ maxRetries?: number;
18
+ /** 重试延迟基数 (ms) */
19
+ retryDelay?: number;
20
+ /** 连接成功回调 */
21
+ onConnected?: () => void;
22
+ /** 连接断开回调 */
23
+ onDisconnected?: () => void;
24
+ /** 连接错误回调 */
25
+ onError?: (error: Error) => void;
26
+ /** 消息处理回调 */
27
+ onMessage?: (data: unknown) => void;
8
28
  }
9
- interface SSETaskUpdateData {
10
- type: "TASK_UPDATE";
11
- task: ServiceStartupTask;
12
- errorType?: string;
13
- errorMessage?: string;
14
- }
15
- export declare function useSSE(onStatusSync: (data: SSEStatusSyncData) => void, onTaskUpdate: (data: SSETaskUpdateData) => void, onClearElements: () => void, onConnected: () => void): {
16
- setupSSE: () => void;
17
- closeSSE: () => void;
18
- sseRetryCount: import("vue").Ref<number, number>;
29
+ /**
30
+ * 通用 SSE 连接管理
31
+ * 提供基础的连接、重连、状态管理功能
32
+ */
33
+ export declare function useSSE(options: SSEOptions): {
34
+ connection: Ref<{
35
+ onerror: ((this: EventSource, ev: Event) => any) | null;
36
+ onmessage: ((this: EventSource, ev: MessageEvent) => any) | null;
37
+ onopen: ((this: EventSource, ev: Event) => any) | null;
38
+ readonly readyState: number;
39
+ readonly url: string;
40
+ readonly withCredentials: boolean;
41
+ close: () => void;
42
+ readonly CONNECTING: 0;
43
+ readonly OPEN: 1;
44
+ readonly CLOSED: 2;
45
+ addEventListener: {
46
+ <K extends keyof EventSourceEventMap>(type: K, listener: (this: EventSource, ev: EventSourceEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
47
+ (type: string, listener: (this: EventSource, event: MessageEvent) => any, options?: boolean | AddEventListenerOptions): void;
48
+ (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
49
+ };
50
+ removeEventListener: {
51
+ <K extends keyof EventSourceEventMap>(type: K, listener: (this: EventSource, ev: EventSourceEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
52
+ (type: string, listener: (this: EventSource, event: MessageEvent) => any, options?: boolean | EventListenerOptions): void;
53
+ (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
54
+ };
55
+ dispatchEvent: (event: Event) => boolean;
56
+ } | null, EventSource | {
57
+ onerror: ((this: EventSource, ev: Event) => any) | null;
58
+ onmessage: ((this: EventSource, ev: MessageEvent) => any) | null;
59
+ onopen: ((this: EventSource, ev: Event) => any) | null;
60
+ readonly readyState: number;
61
+ readonly url: string;
62
+ readonly withCredentials: boolean;
63
+ close: () => void;
64
+ readonly CONNECTING: 0;
65
+ readonly OPEN: 1;
66
+ readonly CLOSED: 2;
67
+ addEventListener: {
68
+ <K extends keyof EventSourceEventMap>(type: K, listener: (this: EventSource, ev: EventSourceEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
69
+ (type: string, listener: (this: EventSource, event: MessageEvent) => any, options?: boolean | AddEventListenerOptions): void;
70
+ (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
71
+ };
72
+ removeEventListener: {
73
+ <K extends keyof EventSourceEventMap>(type: K, listener: (this: EventSource, ev: EventSourceEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
74
+ (type: string, listener: (this: EventSource, event: MessageEvent) => any, options?: boolean | EventListenerOptions): void;
75
+ (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
76
+ };
77
+ dispatchEvent: (event: Event) => boolean;
78
+ } | null>;
79
+ status: Ref<SSEConnectionStatus, SSEConnectionStatus>;
80
+ retryCount: Ref<number, number>;
81
+ isConnected: import("vue").ComputedRef<boolean>;
82
+ isConnecting: import("vue").ComputedRef<boolean>;
83
+ connect: () => void;
84
+ disconnect: () => void;
85
+ reconnect: () => void;
86
+ resetRetryCount: () => void;
19
87
  };
20
- export {};
@@ -21,61 +21,118 @@ __export(useSSE_exports, {
21
21
  });
22
22
  module.exports = __toCommonJS(useSSE_exports);
23
23
  var import_vue = require("vue");
24
- const MAX_SSE_RETRIES = 10;
25
- const SSE_RETRY_DELAY = 1e3;
26
- function useSSE(onStatusSync, onTaskUpdate, onClearElements, onConnected) {
27
- const sseConnection = (0, import_vue.ref)(null);
28
- const sseRetryCount = (0, import_vue.ref)(0);
29
- const setupSSE = () => {
30
- if (sseConnection.value) return;
24
+ const DEFAULT_MAX_RETRIES = 10;
25
+ const DEFAULT_RETRY_DELAY = 1e3;
26
+ function useSSE(options) {
27
+ const {
28
+ endpoint,
29
+ autoConnect = true,
30
+ enabled,
31
+ maxRetries = DEFAULT_MAX_RETRIES,
32
+ retryDelay = DEFAULT_RETRY_DELAY,
33
+ onConnected,
34
+ onDisconnected,
35
+ onError,
36
+ onMessage
37
+ } = options;
38
+ const connection = (0, import_vue.ref)(null);
39
+ const status = (0, import_vue.ref)("idle");
40
+ const retryCount = (0, import_vue.ref)(0);
41
+ function handleMessage(event) {
31
42
  try {
32
- sseConnection.value = new EventSource("/__opencode_events__");
33
- sseConnection.value.onmessage = (event) => {
34
- try {
35
- const data = JSON.parse(event.data);
36
- if (data.type === "CONNECTED") {
37
- onConnected();
38
- sseRetryCount.value = 0;
39
- } else if (data.type === "STATUS_SYNC") {
40
- onStatusSync(data);
41
- } else if (data.type === "TASK_UPDATE") {
42
- onTaskUpdate(data);
43
- } else if (data.type === "CLEAR_ELEMENTS") {
44
- onClearElements();
45
- }
46
- } catch (e) {
47
- }
43
+ const data = JSON.parse(event.data);
44
+ onMessage == null ? void 0 : onMessage(data);
45
+ } catch (e) {
46
+ onMessage == null ? void 0 : onMessage(event.data);
47
+ }
48
+ }
49
+ function connect() {
50
+ if (connection.value || status.value === "connecting") {
51
+ return;
52
+ }
53
+ if ((enabled == null ? void 0 : enabled.value) === false) {
54
+ status.value = "idle";
55
+ return;
56
+ }
57
+ status.value = "connecting";
58
+ retryCount.value = 0;
59
+ try {
60
+ connection.value = new EventSource(endpoint);
61
+ connection.value.onopen = () => {
62
+ status.value = "connected";
63
+ retryCount.value = 0;
64
+ onConnected == null ? void 0 : onConnected();
48
65
  };
49
- sseConnection.value.onerror = () => {
66
+ connection.value.onmessage = handleMessage;
67
+ connection.value.onerror = () => {
50
68
  var _a;
51
- (_a = sseConnection.value) == null ? void 0 : _a.close();
52
- sseConnection.value = null;
53
- if (sseRetryCount.value < MAX_SSE_RETRIES) {
54
- sseRetryCount.value++;
55
- setTimeout(setupSSE, SSE_RETRY_DELAY * sseRetryCount.value);
69
+ const wasConnected = status.value === "connected";
70
+ status.value = "error";
71
+ (_a = connection.value) == null ? void 0 : _a.close();
72
+ connection.value = null;
73
+ const error = new Error(`SSE connection error: ${endpoint}`);
74
+ onError == null ? void 0 : onError(error);
75
+ if (retryCount.value < maxRetries) {
76
+ retryCount.value++;
77
+ const delay = retryDelay * retryCount.value;
78
+ setTimeout(() => {
79
+ if ((enabled == null ? void 0 : enabled.value) !== false && !connection.value) {
80
+ connect();
81
+ }
82
+ }, delay);
83
+ } else if (wasConnected) {
84
+ onDisconnected == null ? void 0 : onDisconnected();
56
85
  }
57
86
  };
58
87
  } catch (e) {
59
- sseConnection.value = null;
60
- if (sseRetryCount.value < MAX_SSE_RETRIES) {
61
- sseRetryCount.value++;
62
- setTimeout(setupSSE, SSE_RETRY_DELAY * sseRetryCount.value);
88
+ status.value = "error";
89
+ const error = e instanceof Error ? e : new Error(String(e));
90
+ onError == null ? void 0 : onError(error);
91
+ if (retryCount.value < maxRetries) {
92
+ retryCount.value++;
93
+ const delay = retryDelay * retryCount.value;
94
+ setTimeout(() => {
95
+ if (!connection.value) {
96
+ connect();
97
+ }
98
+ }, delay);
63
99
  }
64
100
  }
65
- };
66
- const closeSSE = () => {
67
- if (sseConnection.value) {
68
- sseConnection.value.close();
69
- sseConnection.value = null;
101
+ }
102
+ function disconnect() {
103
+ if (connection.value) {
104
+ connection.value.close();
105
+ connection.value = null;
106
+ status.value = "disconnected";
107
+ retryCount.value = 0;
108
+ onDisconnected == null ? void 0 : onDisconnected();
70
109
  }
71
- };
110
+ }
111
+ function reconnect() {
112
+ disconnect();
113
+ connect();
114
+ }
115
+ function resetRetryCount() {
116
+ retryCount.value = 0;
117
+ }
118
+ if (autoConnect && (enabled == null ? void 0 : enabled.value) !== false) {
119
+ connect();
120
+ }
72
121
  (0, import_vue.onUnmounted)(() => {
73
- closeSSE();
122
+ disconnect();
74
123
  });
75
124
  return {
76
- setupSSE,
77
- closeSSE,
78
- sseRetryCount
125
+ // 状态
126
+ connection,
127
+ status,
128
+ retryCount,
129
+ isConnected: (0, import_vue.computed)(() => status.value === "connected"),
130
+ isConnecting: (0, import_vue.computed)(() => status.value === "connecting"),
131
+ // 方法
132
+ connect,
133
+ disconnect,
134
+ reconnect,
135
+ resetRetryCount
79
136
  };
80
137
  }
81
138
  // Annotate the CommonJS export names for ESM import in node: