@aipanel/provider-deepseek 1.2.7 → 1.2.9

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/es/profile.d.ts CHANGED
@@ -1,21 +1,37 @@
1
+ import type { AIPanelWidgetTheme } from "@aipanel/core";
2
+ import type { DeepSeekBusyEnter, DeepSeekPermissionPreset } from "./types";
1
3
  /** 组装 overlay YAML */
2
4
  export declare function buildDshOverlay(options: {
3
5
  vitePort: number;
4
6
  cwd: string;
5
- /** host 插件(@aipanel/dsh-plugin)是否已被同步到 dsh profile;false 时停用该行,避免 fail-loud */
7
+ /** host 插件(@aipanel/dsh-plugin)是否已同步到 dsh profile;false 时停用该行,避免 fail-loud */
6
8
  pluginAvailable?: boolean;
7
- /** client 插件(@aipanel/dsh-client)是否可被 dsh 解析(provider 已同步到 dsh profile);false 时停用该行,避免 fail-loud */
9
+ /** client 插件(@aipanel/dsh-client)是否可被 dsh 解析;false 时停用该行,避免 fail-loud */
8
10
  clientAvailable?: boolean;
9
11
  /**
10
12
  * 编辑后自动诊断开关(provider option autoDiagnose)。
11
13
  * undefined 时不写入 overlay,由 dsh-plugin 回退到 OPENCODE_ENABLE_LINT=1(与 opencode 一致)。
12
14
  */
13
15
  autoDiagnose?: boolean;
16
+ /**
17
+ * 宿主事件推送令牌(core 每轮启动随机):随 plugin config 注入 dsh-plugin,
18
+ * 使其把归一化 ProviderEvent POST 到 core 的 HOST_EVENTS_API_PATH。
19
+ */
20
+ eventsToken?: string;
14
21
  /**
15
22
  * 诊断功能总开关(provider option enableDiagnostics)。
16
- * true(默认,对齐 opencode enableLsp)时 host 插件注册 run_diagnostics 工具与自动诊断逻辑。
23
+ * true(默认,对齐 opencode enableLsp)时 host 插件注册 run_diagnostics 工具与自动诊断逻辑,
24
+ * client 插件注册诊断卡片视图。
17
25
  */
18
26
  enableDiagnostics?: boolean;
27
+ /** provider option agentPreset:dsh settings agent-presets.default(随 host 插件 config 下发) */
28
+ agentPreset?: string;
29
+ /** provider option permissionPreset:dsh settings permission.defaultPreset(单一来源 ./types) */
30
+ permissionPreset?: DeepSeekPermissionPreset;
31
+ /** provider option busyEnter:dsh settings ui-conversation.busyEnter(单一来源 ./types) */
32
+ busyEnter?: DeepSeekBusyEnter;
33
+ /** AIPanel 侧主题偏好(applyConfig.theme,AIPanelWidgetTheme);auto 不干预(沿用 dsh 持久化偏好),light/dark 随 client 插件 config 下发 */
34
+ theme?: AIPanelWidgetTheme;
19
35
  }): string;
20
36
  /** 将 overlay 写入项目缓存目录(AIPANEL_CACHE_DIR 下按 provider 分二级目录),不污染用户项目根目录。 */
21
37
  export declare function writeDshOverlay(workspaceCwd: string, overlay: string): string;
package/es/profile.js CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  AIPANEL_CACHE_DIR,
5
5
  MCP_API_PATH,
6
6
  CONTEXT_API_PATH,
7
+ HOST_EVENTS_API_PATH,
7
8
  createLogger
8
9
  } from "@aipanel/core/node";
9
10
  const log = createLogger("DeepSeekProfile");
@@ -14,7 +15,12 @@ function buildDshOverlay(options) {
14
15
  pluginAvailable = true,
15
16
  clientAvailable = true,
16
17
  autoDiagnose,
17
- enableDiagnostics = true
18
+ enableDiagnostics = true,
19
+ eventsToken,
20
+ agentPreset,
21
+ permissionPreset,
22
+ busyEnter,
23
+ theme = "auto"
18
24
  } = options;
19
25
  const mcpUrl = `http://127.0.0.1:${vitePort}${MCP_API_PATH}`;
20
26
  const rows = [];
@@ -40,14 +46,25 @@ function buildDshOverlay(options) {
40
46
  ` vitePort: ${vitePort}`,
41
47
  ` contextApiPath: ${JSON.stringify(CONTEXT_API_PATH)}`,
42
48
  ` enableDiagnostics: ${enableDiagnostics ? "true" : "false"}`,
43
- ...autoDiagnose !== void 0 ? [` autoDiagnose: ${autoDiagnose ? "true" : "false"}`] : []
49
+ ...autoDiagnose !== void 0 ? [` autoDiagnose: ${autoDiagnose ? "true" : "false"}`] : [],
50
+ ...eventsToken ? [
51
+ ` eventsToken: ${JSON.stringify(eventsToken)}`,
52
+ ` eventsPath: ${JSON.stringify(HOST_EVENTS_API_PATH)}`
53
+ ] : [],
54
+ // providerOptions → 启动期设置(dsh-plugin/applyProviderSettings 经 ctx.settings 写入)
55
+ ...agentPreset !== void 0 ? [` agentPreset: ${JSON.stringify(agentPreset)}`] : [],
56
+ ...permissionPreset !== void 0 ? [` permissionPreset: ${JSON.stringify(permissionPreset)}`] : [],
57
+ ...busyEnter !== void 0 ? [` busyEnter: ${JSON.stringify(busyEnter)}`] : []
44
58
  ].join("\n")
45
59
  );
46
60
  rows.push(
47
61
  [
48
62
  " - id: aipanel-client",
49
63
  " name: '@aipanel/dsh-client'",
50
- ...clientAvailable ? [] : [" disabled: true"]
64
+ ...clientAvailable ? [] : [" disabled: true"],
65
+ " config:",
66
+ ` enableDiagnostics: ${enableDiagnostics ? "true" : "false"}`,
67
+ ...theme !== "auto" ? [` theme: ${JSON.stringify(theme)}`] : []
51
68
  ].join("\n")
52
69
  );
53
70
  return ["- insert:", rows.join("\n"), ""].join("\n");
package/es/provider.d.ts CHANGED
@@ -13,7 +13,9 @@ export interface DeepSeekWebProviderDeps {
13
13
  }
14
14
  /**
15
15
  * DeepSeek Harness Web Provider
16
- * 组合 CLI 进程管理、RPC 会话 API、桥接脚本,向核心层暴露 WebProvider 契约。
16
+ * 组合 CLI 进程管理、RPC 会话 API、dsh 侧插件编排(host 插件 + 浏览器 client 插件),
17
+ * 向核心层暴露 WebProvider 契约。dsh 页内行为(会话聚焦/主题/布局/选中元素)全部
18
+ * 由浏览器插件 @aipanel/dsh-client 承担,不再注入 bridge 脚本。
17
19
  */
18
20
  export declare class DeepSeekWebProvider implements WebProvider {
19
21
  readonly id = "deepseek";
@@ -25,20 +27,17 @@ export declare class DeepSeekWebProvider implements WebProvider {
25
27
  private readonly api;
26
28
  private deps;
27
29
  private process;
28
- private bridgeOptions;
30
+ /** AIPanel 侧下发的主题偏好(AIPanelWidgetTheme,default auto):随 client 插件 config 注入,作为启动初值 */
31
+ private uiTheme;
29
32
  private readonly opts;
30
33
  constructor(config: DeepSeekWebProviderConfig, deps: DeepSeekWebProviderDeps, options?: Record<string, unknown>);
31
- /** 代理注入到 HTML 的桥接脚本(Provider 资产) */
32
- get bridgeScript(): string | undefined;
33
- /** 初始化桥接配置(主题、诊断开关等) */
34
+ /** 记录 AIPanel 侧主题偏好(applyConfig start 前调用;start 时随 client 插件 config 下发) */
34
35
  applyConfig(config: ProviderConfig): void;
35
36
  checkEnvironment(): Promise<ProviderEnvironmentInfo>;
36
37
  start(options: ProviderStartOptions): Promise<ProviderStartResult>;
37
- /** 把 providerOptions 配置映射为 dsh settings 命名空间 patch(仅含用户显式配置项) */
38
- private buildSettingsToApply;
39
38
  stop(): Promise<void>;
40
39
  killOrphans(): Promise<number>;
41
- listSessions(projectDir: string): Promise<ChatSession[]>;
40
+ listSessions(projectDir: string, activeSessionId?: string): Promise<ChatSession[]>;
42
41
  createSession(projectDir: string, title?: string): Promise<ChatSession>;
43
42
  deleteSession(sessionId: string): Promise<void>;
44
43
  buildSessionUrl(projectDir: string, sessionId: string): string;
package/es/provider.js CHANGED
@@ -1,14 +1,20 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { sleep } from "@aipanel/core";
4
5
  import { createLogger } from "@aipanel/core/node";
5
6
  import { DEFAULT_DEEPSEEK_PROVIDER_OPTIONS } from "./constants.js";
6
- import { DSH_LOOPBACK_HOST, DSH_MUX_EVENTS_PATH, DSH_HOST_EVENTS_PATH } from "./constants.js";
7
+ import { DSH_LOOPBACK_HOST } from "./constants.js";
7
8
  import { DeepSeekAPI } from "./api.js";
8
- import { generateBridgeScript } from "./bridge-script.js";
9
- import { startDeepSeekWeb } from "./deepseek-web.js";
9
+ import { LaunchToken, startDeepSeekWeb } from "./deepseek-web.js";
10
10
  import { buildDshOverlay, writeDshOverlay } from "./profile.js";
11
- import { checkDeepSeekInstalled, getDeepSeekVersion, killOrphanDeepSeekProcesses } from "./system.js";
11
+ import {
12
+ checkDeepSeekInstalled,
13
+ getDeepSeekVersion,
14
+ isDeepSeekVersionAtLeast,
15
+ killOrphanDeepSeekProcesses,
16
+ MIN_DSH_VERSION
17
+ } from "./system.js";
12
18
  import {
13
19
  DSH_CLIENT_PACKAGE,
14
20
  DSH_PLUGIN_PACKAGE,
@@ -26,22 +32,17 @@ class DeepSeekWebProvider {
26
32
  __publicField(this, "api");
27
33
  __publicField(this, "deps");
28
34
  __publicField(this, "process", null);
29
- __publicField(this, "bridgeOptions", {});
35
+ /** AIPanel 侧下发的主题偏好(AIPanelWidgetTheme,default auto):随 client 插件 config 注入,作为启动初值 */
36
+ __publicField(this, "uiTheme", "auto");
30
37
  __publicField(this, "opts");
31
38
  this.deps = deps;
32
39
  this.opts = resolveDeepSeekOptions(options);
33
40
  this.api = new DeepSeekAPI(DSH_LOOPBACK_HOST, deps.getWebPort);
34
41
  }
35
- /** 代理注入到 HTML 的桥接脚本(Provider 资产) */
36
- get bridgeScript() {
37
- return generateBridgeScript(this.bridgeOptions);
38
- }
39
- /** 初始化桥接配置(主题、诊断开关等) */
42
+ /** 记录 AIPanel 侧主题偏好(applyConfig start 前调用;start 时随 client 插件 config 下发) */
40
43
  applyConfig(config) {
41
- this.bridgeOptions = {
42
- theme: config.theme ?? "auto",
43
- diagnosticsEnabled: this.opts.enableDiagnostics ?? false
44
- };
44
+ const t = config.theme;
45
+ this.uiTheme = t === "light" || t === "dark" || t === "auto" ? t : "auto";
45
46
  }
46
47
  async checkEnvironment() {
47
48
  if (!await checkDeepSeekInstalled()) {
@@ -60,6 +61,18 @@ or run without installing:
60
61
  };
61
62
  }
62
63
  const version = await getDeepSeekVersion();
64
+ const compatible = version === null ? true : isDeepSeekVersionAtLeast(version);
65
+ if (compatible === false) {
66
+ return {
67
+ ok: false,
68
+ message: `DeepSeek Harness (dsh) ${version} is too old: this provider requires dsh >= ${MIN_DSH_VERSION} (browser-session auth / Remote RPC protocol).
69
+
70
+ Please upgrade:
71
+
72
+ npm install -g @deepseek-ai/dsh@latest
73
+ `
74
+ };
75
+ }
63
76
  return { ok: true, version: version ?? void 0 };
64
77
  }
65
78
  async start(options) {
@@ -90,7 +103,7 @@ or run without installing:
90
103
  this.opts.home
91
104
  );
92
105
  if (!pluginAvailable) {
93
- log.warn("@aipanel/dsh-plugin unavailable; run_diagnostics & auto-diagnose disabled", {
106
+ log.warn("@aipanel/dsh-plugin unavailable; run_diagnostics & settings application disabled", {
94
107
  metaUrl: import.meta.url
95
108
  });
96
109
  }
@@ -100,48 +113,55 @@ or run without installing:
100
113
  pluginAvailable,
101
114
  clientAvailable,
102
115
  autoDiagnose: this.opts.autoDiagnose,
103
- enableDiagnostics: this.opts.enableDiagnostics
116
+ enableDiagnostics: this.opts.enableDiagnostics,
117
+ // 宿主事件推送令牌(core 每轮启动随机):随 plugin config 注入 dsh-plugin 用于回推鉴权
118
+ eventsToken: options.eventsToken,
119
+ agentPreset: this.opts.agentPreset,
120
+ permissionPreset: this.opts.permissionPreset,
121
+ busyEnter: this.opts.busyEnter,
122
+ theme: this.uiTheme
104
123
  });
105
124
  const patchPath = writeDshOverlay(options.cwd, overlay);
125
+ const launchToken = new LaunchToken();
126
+ this.api.setLaunchTokenSource(() => launchToken.wait());
106
127
  const proc = startDeepSeekWeb({
107
128
  port: options.port,
108
129
  hostname: DSH_LOOPBACK_HOST,
109
130
  cwd: options.cwd,
110
131
  patchPath,
111
132
  home: this.opts.home,
112
- verbose: options.verbose
133
+ verbose: options.verbose,
134
+ launchToken
113
135
  });
114
136
  this.process = proc;
115
- const settings = this.buildSettingsToApply();
116
- if (Object.keys(settings).length > 0) {
117
- void this.api.applySettings(settings).catch((e) => {
118
- log.warn("failed to apply provider settings to dsh", {
119
- settings,
137
+ let webAuthCookie;
138
+ try {
139
+ const token = await launchToken.wait();
140
+ this.api.setLaunchToken(token);
141
+ const maxAuthAttempts = 3;
142
+ for (let attempt = 1; ; attempt++) {
143
+ try {
144
+ await this.api.authenticate();
145
+ break;
146
+ } catch (e) {
147
+ if (attempt >= maxAuthAttempts) throw e;
148
+ log.debug("dsh browser-session auth bootstrap attempt failed, retrying", {
149
+ attempt,
150
+ error: e instanceof Error ? e.message : String(e)
151
+ });
152
+ await sleep(250 * attempt);
153
+ }
154
+ }
155
+ webAuthCookie = this.api.getAuthCookie();
156
+ } catch (e) {
157
+ log.warn(
158
+ "failed to establish dsh web browser-session auth; UI may show authentication required",
159
+ {
120
160
  error: e instanceof Error ? e.message : String(e)
121
- });
122
- });
123
- }
124
- return { url: this.api.shellUrl, processHandle: proc };
125
- }
126
- /** 把 providerOptions 配置映射为 dsh settings 命名空间 patch(仅含用户显式配置项) */
127
- buildSettingsToApply() {
128
- const sections = {};
129
- if (this.opts.agentPreset !== void 0) {
130
- sections["agent-presets"] = { ...sections["agent-presets"], default: this.opts.agentPreset };
131
- }
132
- if (this.opts.permissionPreset !== void 0) {
133
- sections.permission = {
134
- ...sections.permission,
135
- defaultPreset: this.opts.permissionPreset
136
- };
137
- }
138
- if (this.opts.busyEnter !== void 0) {
139
- sections["ui-conversation"] = {
140
- ...sections["ui-conversation"],
141
- busyEnter: this.opts.busyEnter
142
- };
161
+ }
162
+ );
143
163
  }
144
- return sections;
164
+ return { url: this.api.shellUrl, processHandle: proc, webAuthCookie };
145
165
  }
146
166
  async stop() {
147
167
  if (this.process) {
@@ -153,8 +173,8 @@ or run without installing:
153
173
  async killOrphans() {
154
174
  return killOrphanDeepSeekProcesses();
155
175
  }
156
- async listSessions(projectDir) {
157
- const sessions = await this.api.listSessions(projectDir);
176
+ async listSessions(projectDir, activeSessionId) {
177
+ const sessions = await this.api.listSessions(projectDir, activeSessionId);
158
178
  const url = this.buildSessionUrl(projectDir, "");
159
179
  return sessions.map((s) => toChatSession(s, url));
160
180
  }
@@ -179,104 +199,12 @@ or run without installing:
179
199
  return `http://${DSH_LOOPBACK_HOST}:${this.deps.getProxyPort()}/`;
180
200
  }
181
201
  subscribeEvents(handler) {
182
- const port = this.deps.getWebPort();
183
- const base = `ws://${DSH_LOOPBACK_HOST}:${port}`;
184
- const endpoints = [DSH_MUX_EVENTS_PATH, DSH_HOST_EVENTS_PATH];
185
- log.debug("Subscribing to dsh event streams (WebSocket)", {
186
- endpoints: endpoints.map((p) => base + p)
187
- });
188
- let aborted = false;
189
- const sockets = /* @__PURE__ */ new Set();
190
- const retryTimers = /* @__PURE__ */ new Set();
191
- const cleanupAll = () => {
192
- for (const timer of retryTimers) clearTimeout(timer);
193
- retryTimers.clear();
194
- for (const ws of sockets) {
195
- try {
196
- ws.close();
197
- } catch {
198
- }
199
- }
200
- sockets.clear();
201
- };
202
- const connect = (path, attempt = 0) => {
203
- if (aborted) return;
204
- let socket;
205
- try {
206
- socket = new WebSocket(base + path);
207
- } catch (e) {
208
- log.warn("dsh event WebSocket create failed", { path, error: String(e) });
209
- scheduleReconnect(path, attempt + 1);
210
- return;
211
- }
212
- sockets.add(socket);
213
- socket.onmessage = (ev) => {
214
- try {
215
- const frame = JSON.parse(String(ev.data));
216
- const event = mapEvent(frame);
217
- if (event) handler(event);
218
- } catch {
219
- }
220
- };
221
- socket.onclose = () => {
222
- sockets.delete(socket);
223
- scheduleReconnect(path, attempt + 1);
224
- };
225
- socket.onerror = () => {
226
- try {
227
- socket.close();
228
- } catch {
229
- }
230
- };
231
- };
232
- const scheduleReconnect = (path, attempt) => {
233
- if (aborted) return;
234
- const delays = [250, 500, 1e3, 2e3, 5e3];
235
- const after = delays[Math.min(attempt, delays.length - 1)];
236
- const timer = setTimeout(() => {
237
- retryTimers.delete(timer);
238
- connect(path);
239
- }, after);
240
- retryTimers.add(timer);
241
- };
242
- endpoints.forEach((p) => connect(p));
202
+ void handler;
203
+ log.debug("dsh \u4E8B\u4EF6\u7ECF\u5BBF\u4E3B\u63D2\u4EF6(dsh-plugin)\u4E2D\u7EE7\u63A8\u9001\uFF0Cprovider \u76F4\u8FDE\u4E8B\u4EF6\u901A\u9053\u4E3A\u7A7A");
243
204
  return () => {
244
- aborted = true;
245
- cleanupAll();
246
205
  };
247
206
  }
248
207
  }
249
- function mapEvent(frame) {
250
- if (!frame || typeof frame.method !== "string" || !frame.payload) return null;
251
- const payload = frame.payload;
252
- const sessionId = payload.sessionId ?? "";
253
- switch (frame.method) {
254
- case "host/session-status": {
255
- if (!sessionId) return null;
256
- const running = payload.running === true;
257
- return { type: "session.status", sessionId, status: running ? "running" : "idle" };
258
- }
259
- case "session/event": {
260
- const event = payload.event;
261
- const type = event?.type ?? "";
262
- if (!sessionId || !type) return null;
263
- switch (type) {
264
- case "assistant/message":
265
- case "turn/end":
266
- return { type: "thinking", sessionId, thinking: false };
267
- case "assistant/chunk":
268
- case "thinking/delta":
269
- case "turn/start":
270
- case "step/start":
271
- return { type: "thinking", sessionId, thinking: true };
272
- default:
273
- return null;
274
- }
275
- }
276
- default:
277
- return null;
278
- }
279
- }
280
208
  function toChatSession(s, url) {
281
209
  return {
282
210
  id: s.sessionId,
@@ -293,7 +221,7 @@ function resolveDeepSeekOptions(options) {
293
221
  return {
294
222
  ...DEFAULT_DEEPSEEK_PROVIDER_OPTIONS,
295
223
  home: po.home ?? options.home,
296
- agentPreset: po.agentPreset ?? options.agentPreset ?? DEFAULT_DEEPSEEK_PROVIDER_OPTIONS.agentPreset,
224
+ agentPreset: po.agentPreset ?? options.agentPreset,
297
225
  permissionPreset: po.permissionPreset ?? options.permissionPreset,
298
226
  busyEnter: po.busyEnter ?? options.busyEnter,
299
227
  autoDiagnose: po.autoDiagnose ?? options.autoDiagnose ?? DEFAULT_DEEPSEEK_PROVIDER_OPTIONS.autoDiagnose,
package/es/system.d.ts CHANGED
@@ -1,3 +1,10 @@
1
1
  export declare function checkDeepSeekInstalled(): Promise<boolean>;
2
+ /** 本 provider 要求的 dsh 最低版本(0.1.2 起:browser-session 认证 + {args} Remote RPC + remote.mux,协议不向下兼容) */
3
+ export declare const MIN_DSH_VERSION = "0.1.2-rc.1";
4
+ /**
5
+ * 判定 version 是否 >= minimum(semver 风格,含 pre-release 比较:0.1.2 > 0.1.2-rc.1)。
6
+ * @returns true/false;任一版本无法解析时返回 null(调用方按"无法确认"放行)。
7
+ */
8
+ export declare function isDeepSeekVersionAtLeast(version: string, minimum?: string): boolean | null;
2
9
  export declare function getDeepSeekVersion(): Promise<string | null>;
3
10
  export declare function killOrphanDeepSeekProcesses(): Promise<number>;
package/es/system.js CHANGED
@@ -18,6 +18,45 @@ async function checkDeepSeekInstalled() {
18
18
  });
19
19
  });
20
20
  }
21
+ const MIN_DSH_VERSION = "0.1.2-rc.1";
22
+ function parseDshVersion(version) {
23
+ const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(version.trim());
24
+ if (!match) return null;
25
+ return {
26
+ major: Number(match[1]),
27
+ minor: Number(match[2]),
28
+ patch: Number(match[3]),
29
+ pre: match[4]
30
+ };
31
+ }
32
+ function isDeepSeekVersionAtLeast(version, minimum = MIN_DSH_VERSION) {
33
+ const a = parseDshVersion(version);
34
+ const b = parseDshVersion(minimum);
35
+ if (!a || !b) return null;
36
+ for (const key of ["major", "minor", "patch"]) {
37
+ if (a[key] !== b[key]) return a[key] > b[key];
38
+ }
39
+ if (a.pre === void 0 && b.pre === void 0) return true;
40
+ if (a.pre === void 0) return true;
41
+ if (b.pre === void 0) return false;
42
+ const pa = a.pre.split(".");
43
+ const pb = b.pre.split(".");
44
+ const len = Math.max(pa.length, pb.length);
45
+ for (let i = 0; i < len; i++) {
46
+ const x = pa[i];
47
+ const y = pb[i];
48
+ if (x === void 0) return true;
49
+ if (y === void 0) return false;
50
+ const xn = /^\d+$/.test(x) ? Number(x) : NaN;
51
+ const yn = /^\d+$/.test(y) ? Number(y) : NaN;
52
+ if (!Number.isNaN(xn) && !Number.isNaN(yn)) {
53
+ if (xn !== yn) return xn > yn;
54
+ } else if (x !== y) {
55
+ return x > y;
56
+ }
57
+ }
58
+ return true;
59
+ }
21
60
  function getDeepSeekVersion() {
22
61
  return new Promise((resolve) => {
23
62
  const proc = spawn("dsh", ["--version"], { stdio: "pipe", shell: true });
@@ -164,7 +203,9 @@ function killOrphanProcessesOnUnix(resolve, timer) {
164
203
  });
165
204
  }
166
205
  export {
206
+ MIN_DSH_VERSION,
167
207
  checkDeepSeekInstalled,
168
208
  getDeepSeekVersion,
209
+ isDeepSeekVersionAtLeast,
169
210
  killOrphanDeepSeekProcesses
170
211
  };