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
@@ -38,10 +38,11 @@ var __async = (__this, __arguments, generator) => {
38
38
  });
39
39
  };
40
40
  import { defineComponent as _defineComponent } from "vue";
41
- import { ref, computed, onMounted } from "vue";
41
+ import { ref, computed, onMounted, watch } from "vue";
42
42
  import { OpenCodeWidget } from "@vite-plugin-opencode-assistant/components";
43
43
  import { useHotkey } from "./composables/useHotkey";
44
- import { useSSE } from "./composables/useSSE";
44
+ import { useServerSSE } from "./composables/useServerSSE";
45
+ import { useOpencodeSessionSSE } from "./composables/useOpencodeSessionSSE";
45
46
  import { useSessions } from "./composables/useSessions";
46
47
  import { useTheme } from "./composables/useTheme";
47
48
  import { useSelectedElements } from "./composables/useSelectedElements";
@@ -66,9 +67,14 @@ const __vue_sfc__ = /* @__PURE__ */ _defineComponent({
66
67
  const {
67
68
  theme: initialTheme = "auto",
68
69
  open: autoOpen = false,
69
- hotkey = "ctrl+k"
70
+ hotkey = "ctrl+k",
71
+ proxyPort = 4098,
72
+ proxyHost = "localhost"
70
73
  } = props.config;
71
74
  const widgetTheme = initialTheme;
75
+ const proxyBaseUrl = computed(() => {
76
+ return `http://${proxyHost}:${proxyPort}`;
77
+ });
72
78
  const showNotification = (msg, options) => {
73
79
  var _a, _b;
74
80
  (_b = (_a = widgetRef.value) == null ? void 0 : _a.showNotification) == null ? void 0 : _b.call(_a, msg, options);
@@ -78,11 +84,9 @@ const __vue_sfc__ = /* @__PURE__ */ _defineComponent({
78
84
  chromeMcpFailed,
79
85
  chromeMcpErrorType,
80
86
  chromeMcpErrorMessage,
81
- thinking,
82
87
  loadingText,
83
88
  updateStatusFromTask,
84
- setStarting,
85
- setThinking
89
+ setStarting
86
90
  } = useServiceStatus();
87
91
  const {
88
92
  selectedElements,
@@ -102,13 +106,50 @@ const __vue_sfc__ = /* @__PURE__ */ _defineComponent({
102
106
  loadSessions,
103
107
  createSession,
104
108
  deleteSession,
105
- selectSession
106
- } = useSessions(showNotification);
109
+ selectSession,
110
+ updateSessionInfo
111
+ } = useSessions({ showNotification });
107
112
  const { updateContext } = useContext(serviceStatus, selectedElements);
113
+ const serverSSE = useServerSSE({
114
+ onStatusSync: (data) => {
115
+ if (data.isStarted !== void 0 && data.isStarted && serviceStatus.value === "idle") {
116
+ setStarting();
117
+ }
118
+ if (data.task) {
119
+ updateStatusFromTask(data.task, data.errorType, data.errorMessage);
120
+ }
121
+ if (serviceStatus.value !== "idle") {
122
+ loadSessions();
123
+ }
124
+ },
125
+ onTaskUpdate: (data) => {
126
+ updateStatusFromTask(data.task, data.errorType, data.errorMessage);
127
+ },
128
+ onClearElements: () => clearElements(),
129
+ onConnected: () => updateContext(true)
130
+ });
131
+ const opencodeSSE = useOpencodeSessionSSE({
132
+ proxyBaseUrl: proxyBaseUrl.value,
133
+ currentSessionId,
134
+ onConnected: () => {
135
+ console.log("[OpenCode] Session SSE connected");
136
+ },
137
+ onSessionUpdate: (session) => {
138
+ updateSessionInfo(session);
139
+ }
140
+ });
141
+ const thinking = opencodeSSE.hasAnyThinking;
142
+ const sessionStates = opencodeSSE.sessionStates;
108
143
  const showSessionListSkeleton = computed(() => serviceStatus.value === "starting");
109
144
  const computedLoading = computed(() => {
110
145
  return serviceStatus.value === "starting" || iframeLoading.value;
111
146
  });
147
+ const displayLoadingText = computed(() => {
148
+ if (serviceStatus.value === "starting") {
149
+ return loadingText.value;
150
+ }
151
+ return "\u52A0\u8F7D\u4F1A\u8BDD...";
152
+ });
112
153
  const retryWarmup = () => __async(null, null, function* () {
113
154
  retryingWarmup.value = true;
114
155
  try {
@@ -136,24 +177,6 @@ const __vue_sfc__ = /* @__PURE__ */ _defineComponent({
136
177
  retryingWarmup.value = false;
137
178
  }
138
179
  });
139
- const { setupSSE } = useSSE(
140
- (data) => {
141
- if (data.isStarted !== void 0 && data.isStarted && serviceStatus.value === "idle") {
142
- setStarting();
143
- }
144
- if (data.task) {
145
- updateStatusFromTask(data.task, data.errorType, data.errorMessage);
146
- }
147
- if (serviceStatus.value !== "idle") {
148
- loadSessions();
149
- }
150
- },
151
- (data) => {
152
- updateStatusFromTask(data.task, data.errorType, data.errorMessage);
153
- },
154
- () => clearElements(),
155
- () => updateContext(true)
156
- );
157
180
  const ensureServicesStarted = () => __async(null, null, function* () {
158
181
  if (serviceStatus.value !== "idle") return true;
159
182
  try {
@@ -161,7 +184,7 @@ const __vue_sfc__ = /* @__PURE__ */ _defineComponent({
161
184
  const data = yield res.json();
162
185
  if (data.success) {
163
186
  setStarting();
164
- setupSSE();
187
+ serverSSE.connect();
165
188
  return true;
166
189
  }
167
190
  } catch (e) {
@@ -181,10 +204,17 @@ const __vue_sfc__ = /* @__PURE__ */ _defineComponent({
181
204
  showNotification("Vue Inspector \u672A\u52A0\u8F7D\uFF0C\u65E0\u6CD5\u4F7F\u7528\u5143\u7D20\u9009\u62E9\u529F\u80FD");
182
205
  }
183
206
  });
207
+ watch(serviceStatus, (status, oldStatus) => {
208
+ if (status !== "idle" && oldStatus === "idle") {
209
+ serverSSE.connect();
210
+ opencodeSSE.connect();
211
+ }
212
+ });
184
213
  onMounted(() => {
185
214
  if (serviceStatus.value !== "idle") {
186
215
  loadSessions();
187
- setupSSE();
216
+ serverSSE.connect();
217
+ opencodeSSE.connect();
188
218
  updateContext(true);
189
219
  }
190
220
  if (autoOpen && serviceStatus.value !== "idle") {
@@ -193,11 +223,8 @@ const __vue_sfc__ = /* @__PURE__ */ _defineComponent({
193
223
  }, 1e3);
194
224
  }
195
225
  const handleIframeMessage = (event) => {
196
- var _a, _b;
197
- if (((_a = event.data) == null ? void 0 : _a.type) === "OPENCODE_THINKING_STATE") {
198
- setThinking(event.data.thinking);
199
- }
200
- if (((_b = event.data) == null ? void 0 : _b.type) === "OPENCODE_READY") {
226
+ var _a;
227
+ if (((_a = event.data) == null ? void 0 : _a.type) === "OPENCODE_READY") {
201
228
  sendThemeToIframe();
202
229
  }
203
230
  };
@@ -252,7 +279,7 @@ const __vue_sfc__ = /* @__PURE__ */ _defineComponent({
252
279
  const handleFrameLoaded = () => {
253
280
  iframeLoading.value = false;
254
281
  };
255
- const __returned__ = { props, open, selectMode, sessionListCollapsed, loading, widgetRef, retryingWarmup, initialTheme, autoOpen, hotkey, widgetTheme, showNotification, serviceStatus, chromeMcpFailed, chromeMcpErrorType, chromeMcpErrorMessage, thinking, loadingText, updateStatusFromTask, setStarting, setThinking, selectedElements, removeElement, clearElements, theme, sendThemeToIframe, sessions, loadingSessionList, currentSessionId, iframeSrc, iframeLoading, loadSessions, createSession, deleteSession, selectSession, updateContext, showSessionListSkeleton, computedLoading, retryWarmup, setupSSE, ensureServicesStarted, handleToggle, handleSelectNode, handleClearSelected, handleSelectModeChange, handleSessionListCollapsedChange, handleThemeChange, handleRemoveSelectedNode, handleFrameLoaded, get OpenCodeWidget() {
282
+ const __returned__ = { props, open, selectMode, sessionListCollapsed, loading, widgetRef, retryingWarmup, initialTheme, autoOpen, hotkey, proxyPort, proxyHost, widgetTheme, proxyBaseUrl, showNotification, serviceStatus, chromeMcpFailed, chromeMcpErrorType, chromeMcpErrorMessage, loadingText, updateStatusFromTask, setStarting, selectedElements, removeElement, clearElements, theme, sendThemeToIframe, sessions, loadingSessionList, currentSessionId, iframeSrc, iframeLoading, loadSessions, createSession, deleteSession, selectSession, updateSessionInfo, updateContext, serverSSE, opencodeSSE, thinking, sessionStates, showSessionListSkeleton, computedLoading, displayLoadingText, retryWarmup, ensureServicesStarted, handleToggle, handleSelectNode, handleClearSelected, handleSelectModeChange, handleSessionListCollapsedChange, handleThemeChange, handleRemoveSelectedNode, handleFrameLoaded, get OpenCodeWidget() {
256
283
  return OpenCodeWidget;
257
284
  }, LoadingContent, ChromeWarmupError };
258
285
  Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
@@ -274,6 +301,7 @@ function __vue_render__(_ctx, _cache, $props, $setup, $data, $options) {
274
301
  "iframe-src": $setup.iframeSrc,
275
302
  "current-session-id": $setup.currentSessionId,
276
303
  sessions: $setup.sessions,
304
+ "session-states": $setup.sessionStates,
277
305
  "session-key": "id",
278
306
  "hotkey-label": $setup.hotkey,
279
307
  thinking: $setup.thinking,
@@ -292,7 +320,7 @@ function __vue_render__(_ctx, _cache, $props, $setup, $data, $options) {
292
320
  onFrameLoaded: $setup.handleFrameLoaded
293
321
  }, {
294
322
  loading: _withCtx(() => [
295
- _createVNode($setup["LoadingContent"], { "loading-text": $setup.loadingText }, null, 8, ["loading-text"])
323
+ _createVNode($setup["LoadingContent"], { "loading-text": $setup.displayLoadingText }, null, 8, ["loading-text"])
296
324
  ]),
297
325
  error: _withCtx(() => [
298
326
  $setup.chromeMcpFailed ? (_openBlock(), _createBlock($setup["ChromeWarmupError"], {
@@ -305,7 +333,7 @@ function __vue_render__(_ctx, _cache, $props, $setup, $data, $options) {
305
333
  ]),
306
334
  _: 1
307
335
  /* STABLE */
308
- }, 8, ["theme", "open", "select-mode", "session-list-collapsed", "frame-loading", "loading-session-list", "show-session-list-skeleton", "show-error", "iframe-src", "current-session-id", "sessions", "hotkey-label", "thinking", "onCreateSession", "onDeleteSession", "onSelectSession", "onEmptyAction"]);
336
+ }, 8, ["theme", "open", "select-mode", "session-list-collapsed", "frame-loading", "loading-session-list", "show-session-list-skeleton", "show-error", "iframe-src", "current-session-id", "sessions", "session-states", "hotkey-label", "thinking", "onCreateSession", "onDeleteSession", "onSelectSession", "onEmptyAction"]);
309
337
  }
310
338
  __vue_sfc__.render = __vue_render__;
311
339
  var App_vue_default = __vue_sfc__;
@@ -0,0 +1,10 @@
1
+ export { useSSE } from "./useSSE";
2
+ export { useServerSSE } from "./useServerSSE";
3
+ export { useOpencodeSSE } from "./useOpencodeSSE";
4
+ export { useOpencodeSessionSSE } from "./useOpencodeSessionSSE";
5
+ export { useHotkey } from "./useHotkey";
6
+ export { useSessions } from "./useSessions";
7
+ export { useTheme } from "./useTheme";
8
+ export { useSelectedElements } from "./useSelectedElements";
9
+ export { useServiceStatus } from "./useServiceStatus";
10
+ export { useContext } from "./useContext";
@@ -0,0 +1,22 @@
1
+ import { useSSE } from "./useSSE";
2
+ import { useServerSSE } from "./useServerSSE";
3
+ import { useOpencodeSSE } from "./useOpencodeSSE";
4
+ import { useOpencodeSessionSSE } from "./useOpencodeSessionSSE";
5
+ import { useHotkey } from "./useHotkey";
6
+ import { useSessions } from "./useSessions";
7
+ import { useTheme } from "./useTheme";
8
+ import { useSelectedElements } from "./useSelectedElements";
9
+ import { useServiceStatus } from "./useServiceStatus";
10
+ import { useContext } from "./useContext";
11
+ export {
12
+ useContext,
13
+ useHotkey,
14
+ useOpencodeSSE,
15
+ useOpencodeSessionSSE,
16
+ useSSE,
17
+ useSelectedElements,
18
+ useServerSSE,
19
+ useServiceStatus,
20
+ useSessions,
21
+ useTheme
22
+ };
@@ -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,29 @@
1
+ import { useSSE } from "./useSSE";
2
+ function useOpencodeSSE(options) {
3
+ const { proxyBaseUrl, enabled, onEvent, onConnected, onError } = options;
4
+ const endpoint = `${proxyBaseUrl}/global/event`;
5
+ const { status, isConnected, connect, disconnect } = useSSE({
6
+ endpoint,
7
+ autoConnect: false,
8
+ enabled,
9
+ onMessage: (data) => {
10
+ const message = data;
11
+ const payload = message.payload;
12
+ if (!payload) return;
13
+ onEvent == null ? void 0 : onEvent(payload);
14
+ },
15
+ onConnected,
16
+ onError
17
+ });
18
+ return {
19
+ // 状态
20
+ status,
21
+ isConnected,
22
+ // 方法
23
+ connect,
24
+ disconnect
25
+ };
26
+ }
27
+ export {
28
+ useOpencodeSSE
29
+ };
@@ -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,168 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ import { ref, computed } from "vue";
21
+ import {
22
+ useOpencodeSSE
23
+ } from "./useOpencodeSSE";
24
+ function useOpencodeSessionSSE(options) {
25
+ const { proxyBaseUrl, currentSessionId, enabled, onConnected, onSessionUpdate } = options;
26
+ const sessionStates = ref({});
27
+ function isPendingMessage(info) {
28
+ var _a;
29
+ return (info == null ? void 0 : info.role) === "assistant" && typeof ((_a = info == null ? void 0 : info.time) == null ? void 0 : _a.completed) !== "number";
30
+ }
31
+ function updateThinkingState(sessionID) {
32
+ const state = sessionStates.value[sessionID];
33
+ const isThinking = (state == null ? void 0 : state.hasPending) || (state == null ? void 0 : state.statusType) !== "idle";
34
+ sessionStates.value[sessionID] = {
35
+ thinking: isThinking,
36
+ statusType: (state == null ? void 0 : state.statusType) || "idle",
37
+ hasPending: (state == null ? void 0 : state.hasPending) || false
38
+ };
39
+ }
40
+ function handleEvent(payload) {
41
+ var _a;
42
+ const type = payload.type;
43
+ const props = payload.properties;
44
+ switch (type) {
45
+ case "session.updated": {
46
+ const info = props.info;
47
+ if (!(info == null ? void 0 : info.id)) return;
48
+ onSessionUpdate == null ? void 0 : onSessionUpdate({
49
+ id: info.id,
50
+ title: info.title,
51
+ time: info.time,
52
+ summary: info.summary
53
+ });
54
+ break;
55
+ }
56
+ case "session.status": {
57
+ const sessionID = props.sessionID;
58
+ if (!sessionID) return;
59
+ const statusType = ((_a = props.status) == null ? void 0 : _a.type) || "idle";
60
+ sessionStates.value[sessionID] = {
61
+ thinking: statusType !== "idle",
62
+ statusType,
63
+ hasPending: false
64
+ };
65
+ updateThinkingState(sessionID);
66
+ break;
67
+ }
68
+ case "message.updated": {
69
+ const info = props.info;
70
+ if (!(info == null ? void 0 : info.sessionID)) return;
71
+ const sessionID = info.sessionID;
72
+ if (info.role === "assistant") {
73
+ const hasPending = isPendingMessage(info);
74
+ const current = sessionStates.value[sessionID] || {
75
+ thinking: false,
76
+ statusType: "idle",
77
+ hasPending: false
78
+ };
79
+ sessionStates.value[sessionID] = __spreadProps(__spreadValues({}, current), {
80
+ hasPending
81
+ });
82
+ updateThinkingState(sessionID);
83
+ }
84
+ break;
85
+ }
86
+ case "message.part.delta": {
87
+ const sessionID = props.sessionID;
88
+ if (!sessionID) return;
89
+ const current = sessionStates.value[sessionID];
90
+ if (!(current == null ? void 0 : current.hasPending)) {
91
+ sessionStates.value[sessionID] = {
92
+ thinking: true,
93
+ statusType: (current == null ? void 0 : current.statusType) || "idle",
94
+ hasPending: true
95
+ };
96
+ }
97
+ break;
98
+ }
99
+ }
100
+ }
101
+ const sse = useOpencodeSSE({
102
+ proxyBaseUrl,
103
+ enabled,
104
+ onEvent: handleEvent,
105
+ onConnected
106
+ });
107
+ const currentThinking = computed(() => {
108
+ var _a, _b;
109
+ const id = currentSessionId.value;
110
+ if (!id) return false;
111
+ return (_b = (_a = sessionStates.value[id]) == null ? void 0 : _a.thinking) != null ? _b : false;
112
+ });
113
+ const currentSessionState = computed(() => {
114
+ const id = currentSessionId.value;
115
+ if (!id) return null;
116
+ return sessionStates.value[id] || null;
117
+ });
118
+ const hasAnyThinking = computed(() => {
119
+ return Object.values(sessionStates.value).some((state) => state.thinking);
120
+ });
121
+ const thinkingSessionCount = computed(() => {
122
+ return Object.values(sessionStates.value).filter((state) => state.thinking).length;
123
+ });
124
+ function clearSessionState(sessionID) {
125
+ delete sessionStates.value[sessionID];
126
+ }
127
+ function clearAllSessionStates() {
128
+ sessionStates.value = {};
129
+ }
130
+ function getSessionState(sessionID) {
131
+ return sessionStates.value[sessionID];
132
+ }
133
+ function isSessionThinking(sessionID) {
134
+ var _a, _b;
135
+ return (_b = (_a = sessionStates.value[sessionID]) == null ? void 0 : _a.thinking) != null ? _b : false;
136
+ }
137
+ function getSessionsThinking(sessionIds) {
138
+ return sessionIds.reduce(
139
+ (acc, id) => {
140
+ acc[id] = isSessionThinking(id);
141
+ return acc;
142
+ },
143
+ {}
144
+ );
145
+ }
146
+ return {
147
+ // SSE 基础状态
148
+ status: sse.status,
149
+ isConnected: sse.isConnected,
150
+ // session 状态
151
+ sessionStates,
152
+ currentThinking,
153
+ currentSessionState,
154
+ hasAnyThinking,
155
+ thinkingSessionCount,
156
+ // 方法
157
+ connect: sse.connect,
158
+ disconnect: sse.disconnect,
159
+ clearSessionState,
160
+ clearAllSessionStates,
161
+ getSessionState,
162
+ isSessionThinking,
163
+ getSessionsThinking
164
+ };
165
+ }
166
+ export {
167
+ useOpencodeSessionSSE
168
+ };
@@ -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 {};