@aipanel/provider-deepseek 1.2.9 → 1.2.10

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/api.js CHANGED
@@ -3,7 +3,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import http from "http";
5
5
  import { randomUUID } from "node:crypto";
6
- import { DEFAULT_RETRIES, RETRY_DELAY, sleep } from "@aipanel/core";
6
+ import { DEFAULT_RETRIES, withRetries } from "@aipanel/core";
7
7
  import { PerformanceTimer, createLogger } from "@aipanel/core/node";
8
8
  import { DSH_API_BASE, DSH_REMOTE_MUX_PATH } from "./constants.js";
9
9
  const log = createLogger("DeepSeekAPI");
@@ -149,11 +149,9 @@ class DeepSeekAPI {
149
149
  * sessionId === activeSessionId(当前选中会话,对应 UI 的 New Session 占位行)时展示。
150
150
  */
151
151
  async listSessions(projectDir, activeSessionId, retries = DEFAULT_RETRIES) {
152
- const timer = log.timer("listSessions", { projectDir, activeSessionId, retries });
153
- let lastError = null;
154
- for (let i = 0; i < retries; i++) {
155
- try {
156
- log.debug(`Attempt ${i + 1}/${retries}`, { method: "session/list", projectDir });
152
+ return withRetries(
153
+ async (attempt) => {
154
+ log.debug(`Attempt ${attempt + 1}/${retries}`, { method: "listSessions" });
157
155
  const workspaces = await this.fetchWorkspaceBaseline();
158
156
  const matchedWorkspace = workspaces.items.find((w) => w.path === projectDir);
159
157
  const ownedByWorkspace = new Set(matchedWorkspace?.sessionIds ?? []);
@@ -169,72 +167,54 @@ class DeepSeekAPI {
169
167
  return false;
170
168
  });
171
169
  const result = filtered.sort((a, b) => b.updatedAt - a.updatedAt);
172
- timer.end(`Found ${result.length} sessions`);
173
170
  return result;
174
- } catch (e) {
175
- lastError = e instanceof Error ? e : new Error(String(e));
176
- log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, { method: "listSessions" });
177
- if (i < retries - 1) {
178
- await sleep(RETRY_DELAY);
179
- }
171
+ },
172
+ {
173
+ attempts: retries,
174
+ onRetry: (n, e) => log.debug(`Attempt ${n} failed: ${e instanceof Error ? e.message : String(e)}`, {
175
+ method: "listSessions"
176
+ })
180
177
  }
181
- }
182
- timer.end("\u274C All retries exhausted");
183
- throw lastError;
178
+ );
184
179
  }
185
180
  /** 在当前目录下创建会话(dsh 仅返回 { sessionId, agentPreset? },非完整 SessionSummary)。
186
181
  * 与旧逻辑一致:先确保 projectDir 对应的 workspace 存在(workspace/create 幂等 get-or-create),
187
182
  * 再用 workspaceId 调 session/create,让新会话挂到该 workspace。 */
188
183
  async createSession(projectDir, retries = DEFAULT_RETRIES) {
189
- const timer = log.timer("createSession", { projectDir, retries });
190
- let lastError = null;
191
- for (let i = 0; i < retries; i++) {
192
- try {
193
- log.debug(`Attempt ${i + 1}/${retries}`, {
194
- method: "session/create",
195
- projectDir
196
- });
184
+ return withRetries(
185
+ async (attempt) => {
186
+ log.debug(`Attempt ${attempt + 1}/${retries}`, { method: "createSession" });
197
187
  const { workspace } = await this.call("workspace/create", {
198
188
  request: { path: projectDir }
199
189
  });
200
190
  const session = await this.call("session/create", {
201
191
  request: { workspaceId: workspace.workspaceId }
202
192
  });
203
- timer.end(`Created session: ${session.sessionId}`);
204
193
  return session;
205
- } catch (e) {
206
- lastError = e instanceof Error ? e : new Error(String(e));
207
- log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, { method: "createSession" });
208
- if (i < retries - 1) {
209
- await sleep(RETRY_DELAY);
210
- }
194
+ },
195
+ {
196
+ attempts: retries,
197
+ onRetry: (n, e) => log.debug(`Attempt ${n} failed: ${e instanceof Error ? e.message : String(e)}`, {
198
+ method: "createSession"
199
+ })
211
200
  }
212
- }
213
- timer.end("\u274C All retries exhausted");
214
- throw lastError;
201
+ );
215
202
  }
216
203
  /** 归档会话(dsh 无硬删除,仅归档;幂等) */
217
204
  async archiveSession(sessionId, retries = DEFAULT_RETRIES) {
218
- const timer = log.timer("archiveSession", { sessionId, retries });
219
- let lastError = null;
220
- for (let i = 0; i < retries; i++) {
221
- try {
222
- log.debug(`Attempt ${i + 1}/${retries}`, { method: "workspace/archiveSession" });
205
+ return withRetries(
206
+ async (attempt) => {
207
+ log.debug(`Attempt ${attempt + 1}/${retries}`, { method: "archiveSession" });
223
208
  await this.call("workspace/archiveSession", { request: { sessionId } });
224
- timer.end(`Archived session: ${sessionId}`);
225
209
  return;
226
- } catch (e) {
227
- lastError = e instanceof Error ? e : new Error(String(e));
228
- log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, {
210
+ },
211
+ {
212
+ attempts: retries,
213
+ onRetry: (n, e) => log.debug(`Attempt ${n} failed: ${e instanceof Error ? e.message : String(e)}`, {
229
214
  method: "archiveSession"
230
- });
231
- if (i < retries - 1) {
232
- await sleep(RETRY_DELAY);
233
- }
215
+ })
234
216
  }
235
- }
236
- timer.end("\u274C All retries exhausted");
237
- throw lastError;
217
+ );
238
218
  }
239
219
  /**
240
220
  * 取 workspace/follow 流的 baseline(等价旧 workspace.list 的快照:{ items, archivedSessionIds })。
package/es/profile.js CHANGED
@@ -7,6 +7,8 @@ import {
7
7
  HOST_EVENTS_API_PATH,
8
8
  createLogger
9
9
  } from "@aipanel/core/node";
10
+ import { DSH_LOOPBACK_HOST } from "./constants.js";
11
+ import { DSH_CLIENT_PACKAGE, DSH_PLUGIN_PACKAGE } from "./dsh-install.js";
10
12
  const log = createLogger("DeepSeekProfile");
11
13
  function buildDshOverlay(options) {
12
14
  const {
@@ -22,7 +24,7 @@ function buildDshOverlay(options) {
22
24
  busyEnter,
23
25
  theme = "auto"
24
26
  } = options;
25
- const mcpUrl = `http://127.0.0.1:${vitePort}${MCP_API_PATH}`;
27
+ const mcpUrl = `http://${DSH_LOOPBACK_HOST}:${vitePort}${MCP_API_PATH}`;
26
28
  const rows = [];
27
29
  rows.push(
28
30
  [
@@ -38,7 +40,7 @@ function buildDshOverlay(options) {
38
40
  rows.push(
39
41
  [
40
42
  " - id: aipanel",
41
- " name: '@aipanel/dsh-plugin'",
43
+ ` name: ${JSON.stringify(DSH_PLUGIN_PACKAGE)}`,
42
44
  ...pluginAvailable ? [] : [" disabled: true"],
43
45
  " inject: [tools]",
44
46
  " config:",
@@ -60,7 +62,7 @@ function buildDshOverlay(options) {
60
62
  rows.push(
61
63
  [
62
64
  " - id: aipanel-client",
63
- " name: '@aipanel/dsh-client'",
65
+ ` name: ${JSON.stringify(DSH_CLIENT_PACKAGE)}`,
64
66
  ...clientAvailable ? [] : [" disabled: true"],
65
67
  " config:",
66
68
  ` enableDiagnostics: ${enableDiagnostics ? "true" : "false"}`,
package/es/system.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  export declare function checkDeepSeekInstalled(): Promise<boolean>;
2
+ export declare function getDeepSeekVersion(): Promise<string | null>;
3
+ export declare function killOrphanDeepSeekProcesses(): Promise<number>;
2
4
  /** 本 provider 要求的 dsh 最低版本(0.1.2 起:browser-session 认证 + {args} Remote RPC + remote.mux,协议不向下兼容) */
3
5
  export declare const MIN_DSH_VERSION = "0.1.2-rc.1";
4
6
  /**
5
7
  * 判定 version 是否 >= minimum(semver 风格,含 pre-release 比较:0.1.2 > 0.1.2-rc.1)。
6
- * @returns true/false;任一版本无法解析时返回 null(调用方按"无法确认"放行)。
8
+ * @returns true/false;任一版本无法解析时返回 null(调用方按“无法确认”放行)。
7
9
  */
8
10
  export declare function isDeepSeekVersionAtLeast(version: string, minimum?: string): boolean | null;
9
- export declare function getDeepSeekVersion(): Promise<string | null>;
10
- export declare function killOrphanDeepSeekProcesses(): Promise<number>;
package/es/system.js CHANGED
@@ -1,22 +1,12 @@
1
- import { spawn } from "child_process";
2
- import { createLogger } from "@aipanel/core/node";
3
- const log = createLogger("DeepSeekSystem");
4
- async function checkDeepSeekInstalled() {
5
- const timer = log.timer("checkDeepSeekInstalled");
6
- return new Promise((resolve) => {
7
- log.debug("Checking if dsh is installed...");
8
- const proc = spawn("dsh", ["--version"], { stdio: "ignore", shell: true });
9
- proc.on("close", (code) => {
10
- const installed = code === 0;
11
- timer.end(installed ? "\u2713 dsh is installed" : "\u274C dsh not found");
12
- resolve(installed);
13
- });
14
- proc.on("error", (err) => {
15
- log.debug("Failed to check dsh installation", { error: err.message });
16
- timer.end("\u274C Check failed");
17
- resolve(false);
18
- });
19
- });
1
+ import { checkCliInstalled, getCliVersion, killOrphanCliProcesses } from "@aipanel/core/node";
2
+ function checkDeepSeekInstalled() {
3
+ return checkCliInstalled("dsh");
4
+ }
5
+ function getDeepSeekVersion() {
6
+ return getCliVersion("dsh");
7
+ }
8
+ function killOrphanDeepSeekProcesses() {
9
+ return killOrphanCliProcesses("dsh", { match: "dsh", winName: "node.exe", label: "dsh" });
20
10
  }
21
11
  const MIN_DSH_VERSION = "0.1.2-rc.1";
22
12
  function parseDshVersion(version) {
@@ -57,151 +47,6 @@ function isDeepSeekVersionAtLeast(version, minimum = MIN_DSH_VERSION) {
57
47
  }
58
48
  return true;
59
49
  }
60
- function getDeepSeekVersion() {
61
- return new Promise((resolve) => {
62
- const proc = spawn("dsh", ["--version"], { stdio: "pipe", shell: true });
63
- let output = "";
64
- proc.stdout?.on("data", (data) => {
65
- output += data.toString();
66
- });
67
- proc.on("close", (code) => {
68
- if (code === 0 && output.trim()) {
69
- resolve(output.trim());
70
- } else {
71
- resolve(null);
72
- }
73
- });
74
- proc.on("error", () => {
75
- resolve(null);
76
- });
77
- });
78
- }
79
- const KILL_ORPHAN_TIMEOUT = 5e3;
80
- async function killOrphanDeepSeekProcesses() {
81
- const timer = log.timer("killOrphanDeepSeekProcesses");
82
- log.debug("Looking for orphan dsh processes (PPID=1)");
83
- return new Promise((resolve) => {
84
- let settled = false;
85
- const done = (count) => {
86
- if (settled) return;
87
- settled = true;
88
- resolve(count);
89
- };
90
- const timeout = setTimeout(() => {
91
- log.warn("Kill orphan processes timed out, skipping");
92
- timer.end("\u26A0 Timeout, skipped");
93
- done(0);
94
- }, KILL_ORPHAN_TIMEOUT);
95
- const wrappedResolve = (count) => {
96
- clearTimeout(timeout);
97
- done(count);
98
- };
99
- if (process.platform === "win32") {
100
- killOrphanProcessesOnWindows(wrappedResolve, timer);
101
- } else {
102
- killOrphanProcessesOnUnix(wrappedResolve, timer);
103
- }
104
- });
105
- }
106
- function killOrphanProcessesOnWindows(resolve, timer) {
107
- log.debug("Using Windows method to find orphan processes");
108
- const proc = spawn(
109
- "wmic",
110
- ["process", "where", 'name="node.exe"', "get", "processid,parentprocessid,commandline"],
111
- { stdio: "pipe" }
112
- );
113
- let output = "";
114
- proc.stdout?.on("data", (data) => {
115
- output += data.toString();
116
- });
117
- proc.on("close", () => {
118
- const pidsToKill = [];
119
- output.split("\n").forEach((rawLine) => {
120
- const line = rawLine.trim();
121
- if (!line.includes("dsh")) return;
122
- const parts = line.trim().split(/\s+/);
123
- if (parts.length >= 2) {
124
- const ppid = parts[0];
125
- const pid = parts[1];
126
- if (ppid === "1" && pid && !isNaN(Number(pid))) {
127
- pidsToKill.push(pid);
128
- }
129
- }
130
- });
131
- if (pidsToKill.length > 0) {
132
- log.debug(`Found ${pidsToKill.length} orphan processes`, { pids: pidsToKill });
133
- let killedCount = 0;
134
- let completedCount = 0;
135
- pidsToKill.forEach((pid) => {
136
- const killProc = spawn("taskkill", ["/F", "/PID", pid], { stdio: "ignore" });
137
- killProc.on("close", (code) => {
138
- completedCount++;
139
- if (code === 0) killedCount++;
140
- if (completedCount === pidsToKill.length) {
141
- timer.end(`\u2713 Killed ${killedCount} orphan processes`);
142
- resolve(killedCount);
143
- }
144
- });
145
- });
146
- } else {
147
- log.debug("No orphan processes found");
148
- timer.end("No orphan processes found");
149
- resolve(0);
150
- }
151
- });
152
- proc.on("error", (err) => {
153
- log.debug("Failed to find orphan processes", { error: err.message });
154
- timer.end("\u274C Failed to find orphan processes");
155
- resolve(0);
156
- });
157
- }
158
- function killOrphanProcessesOnUnix(resolve, timer) {
159
- log.debug("Using Unix method to find orphan processes");
160
- const proc = spawn("ps", ["-e", "-o", "pid,ppid,args"], { stdio: "pipe" });
161
- let output = "";
162
- proc.stdout?.on("data", (data) => {
163
- output += data.toString();
164
- });
165
- proc.on("close", () => {
166
- const lines = output.split("\n");
167
- const pidsToKill = [];
168
- lines.forEach((line) => {
169
- if (!line.includes("dsh")) return;
170
- const parts = line.trim().split(/\s+/);
171
- if (parts.length >= 3) {
172
- const pid = parts[0];
173
- const ppid = parts[1];
174
- if (ppid === "1") {
175
- pidsToKill.push(pid);
176
- }
177
- }
178
- });
179
- if (pidsToKill.length > 0) {
180
- log.debug(`Found ${pidsToKill.length} orphan processes`, { pids: pidsToKill });
181
- const killProc = spawn("kill", ["-9", ...pidsToKill], { stdio: "ignore" });
182
- killProc.on("close", (code) => {
183
- const killedCount = code === 0 ? pidsToKill.length : 0;
184
- timer.end(
185
- killedCount > 0 ? `\u2713 Killed ${killedCount} orphan processes` : "\u274C Failed to kill processes"
186
- );
187
- resolve(killedCount);
188
- });
189
- killProc.on("error", () => {
190
- timer.end("\u274C Failed to kill processes");
191
- resolve(0);
192
- });
193
- } else {
194
- log.debug("No orphan processes found");
195
- timer.end("No orphan processes found");
196
- resolve(0);
197
- }
198
- });
199
- proc.on("error", (err) => {
200
- log.debug("Failed to find orphan processes", { error: err.message });
201
- timer.end("\u274C Failed to find orphan processes");
202
- resolve(0);
203
- });
204
- }
205
50
  export {
206
51
  MIN_DSH_VERSION,
207
52
  checkDeepSeekInstalled,
package/lib/api.cjs CHANGED
@@ -180,11 +180,9 @@ class DeepSeekAPI {
180
180
  * sessionId === activeSessionId(当前选中会话,对应 UI 的 New Session 占位行)时展示。
181
181
  */
182
182
  async listSessions(projectDir, activeSessionId, retries = import_core.DEFAULT_RETRIES) {
183
- const timer = log.timer("listSessions", { projectDir, activeSessionId, retries });
184
- let lastError = null;
185
- for (let i = 0; i < retries; i++) {
186
- try {
187
- log.debug(`Attempt ${i + 1}/${retries}`, { method: "session/list", projectDir });
183
+ return (0, import_core.withRetries)(
184
+ async (attempt) => {
185
+ log.debug(`Attempt ${attempt + 1}/${retries}`, { method: "listSessions" });
188
186
  const workspaces = await this.fetchWorkspaceBaseline();
189
187
  const matchedWorkspace = workspaces.items.find((w) => w.path === projectDir);
190
188
  const ownedByWorkspace = new Set(matchedWorkspace?.sessionIds ?? []);
@@ -200,72 +198,54 @@ class DeepSeekAPI {
200
198
  return false;
201
199
  });
202
200
  const result = filtered.sort((a, b) => b.updatedAt - a.updatedAt);
203
- timer.end(`Found ${result.length} sessions`);
204
201
  return result;
205
- } catch (e) {
206
- lastError = e instanceof Error ? e : new Error(String(e));
207
- log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, { method: "listSessions" });
208
- if (i < retries - 1) {
209
- await (0, import_core.sleep)(import_core.RETRY_DELAY);
210
- }
202
+ },
203
+ {
204
+ attempts: retries,
205
+ onRetry: (n, e) => log.debug(`Attempt ${n} failed: ${e instanceof Error ? e.message : String(e)}`, {
206
+ method: "listSessions"
207
+ })
211
208
  }
212
- }
213
- timer.end("\u274C All retries exhausted");
214
- throw lastError;
209
+ );
215
210
  }
216
211
  /** 在当前目录下创建会话(dsh 仅返回 { sessionId, agentPreset? },非完整 SessionSummary)。
217
212
  * 与旧逻辑一致:先确保 projectDir 对应的 workspace 存在(workspace/create 幂等 get-or-create),
218
213
  * 再用 workspaceId 调 session/create,让新会话挂到该 workspace。 */
219
214
  async createSession(projectDir, retries = import_core.DEFAULT_RETRIES) {
220
- const timer = log.timer("createSession", { projectDir, retries });
221
- let lastError = null;
222
- for (let i = 0; i < retries; i++) {
223
- try {
224
- log.debug(`Attempt ${i + 1}/${retries}`, {
225
- method: "session/create",
226
- projectDir
227
- });
215
+ return (0, import_core.withRetries)(
216
+ async (attempt) => {
217
+ log.debug(`Attempt ${attempt + 1}/${retries}`, { method: "createSession" });
228
218
  const { workspace } = await this.call("workspace/create", {
229
219
  request: { path: projectDir }
230
220
  });
231
221
  const session = await this.call("session/create", {
232
222
  request: { workspaceId: workspace.workspaceId }
233
223
  });
234
- timer.end(`Created session: ${session.sessionId}`);
235
224
  return session;
236
- } catch (e) {
237
- lastError = e instanceof Error ? e : new Error(String(e));
238
- log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, { method: "createSession" });
239
- if (i < retries - 1) {
240
- await (0, import_core.sleep)(import_core.RETRY_DELAY);
241
- }
225
+ },
226
+ {
227
+ attempts: retries,
228
+ onRetry: (n, e) => log.debug(`Attempt ${n} failed: ${e instanceof Error ? e.message : String(e)}`, {
229
+ method: "createSession"
230
+ })
242
231
  }
243
- }
244
- timer.end("\u274C All retries exhausted");
245
- throw lastError;
232
+ );
246
233
  }
247
234
  /** 归档会话(dsh 无硬删除,仅归档;幂等) */
248
235
  async archiveSession(sessionId, retries = import_core.DEFAULT_RETRIES) {
249
- const timer = log.timer("archiveSession", { sessionId, retries });
250
- let lastError = null;
251
- for (let i = 0; i < retries; i++) {
252
- try {
253
- log.debug(`Attempt ${i + 1}/${retries}`, { method: "workspace/archiveSession" });
236
+ return (0, import_core.withRetries)(
237
+ async (attempt) => {
238
+ log.debug(`Attempt ${attempt + 1}/${retries}`, { method: "archiveSession" });
254
239
  await this.call("workspace/archiveSession", { request: { sessionId } });
255
- timer.end(`Archived session: ${sessionId}`);
256
240
  return;
257
- } catch (e) {
258
- lastError = e instanceof Error ? e : new Error(String(e));
259
- log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, {
241
+ },
242
+ {
243
+ attempts: retries,
244
+ onRetry: (n, e) => log.debug(`Attempt ${n} failed: ${e instanceof Error ? e.message : String(e)}`, {
260
245
  method: "archiveSession"
261
- });
262
- if (i < retries - 1) {
263
- await (0, import_core.sleep)(import_core.RETRY_DELAY);
264
- }
246
+ })
265
247
  }
266
- }
267
- timer.end("\u274C All retries exhausted");
268
- throw lastError;
248
+ );
269
249
  }
270
250
  /**
271
251
  * 取 workspace/follow 流的 baseline(等价旧 workspace.list 的快照:{ items, archivedSessionIds })。
package/lib/profile.cjs CHANGED
@@ -34,6 +34,8 @@ module.exports = __toCommonJS(profile_exports);
34
34
  var import_fs = __toESM(require("fs"));
35
35
  var import_path = __toESM(require("path"));
36
36
  var import_node = require("@aipanel/core/node");
37
+ var import_constants = require("./constants.cjs");
38
+ var import_dsh_install = require("./dsh-install.cjs");
37
39
  const log = (0, import_node.createLogger)("DeepSeekProfile");
38
40
  function buildDshOverlay(options) {
39
41
  const {
@@ -49,7 +51,7 @@ function buildDshOverlay(options) {
49
51
  busyEnter,
50
52
  theme = "auto"
51
53
  } = options;
52
- const mcpUrl = `http://127.0.0.1:${vitePort}${import_node.MCP_API_PATH}`;
54
+ const mcpUrl = `http://${import_constants.DSH_LOOPBACK_HOST}:${vitePort}${import_node.MCP_API_PATH}`;
53
55
  const rows = [];
54
56
  rows.push(
55
57
  [
@@ -65,7 +67,7 @@ function buildDshOverlay(options) {
65
67
  rows.push(
66
68
  [
67
69
  " - id: aipanel",
68
- " name: '@aipanel/dsh-plugin'",
70
+ ` name: ${JSON.stringify(import_dsh_install.DSH_PLUGIN_PACKAGE)}`,
69
71
  ...pluginAvailable ? [] : [" disabled: true"],
70
72
  " inject: [tools]",
71
73
  " config:",
@@ -87,7 +89,7 @@ function buildDshOverlay(options) {
87
89
  rows.push(
88
90
  [
89
91
  " - id: aipanel-client",
90
- " name: '@aipanel/dsh-client'",
92
+ ` name: ${JSON.stringify(import_dsh_install.DSH_CLIENT_PACKAGE)}`,
91
93
  ...clientAvailable ? [] : [" disabled: true"],
92
94
  " config:",
93
95
  ` enableDiagnostics: ${enableDiagnostics ? "true" : "false"}`,
package/lib/system.cjs CHANGED
@@ -24,25 +24,15 @@ __export(system_exports, {
24
24
  killOrphanDeepSeekProcesses: () => killOrphanDeepSeekProcesses
25
25
  });
26
26
  module.exports = __toCommonJS(system_exports);
27
- var import_child_process = require("child_process");
28
27
  var import_node = require("@aipanel/core/node");
29
- const log = (0, import_node.createLogger)("DeepSeekSystem");
30
- async function checkDeepSeekInstalled() {
31
- const timer = log.timer("checkDeepSeekInstalled");
32
- return new Promise((resolve) => {
33
- log.debug("Checking if dsh is installed...");
34
- const proc = (0, import_child_process.spawn)("dsh", ["--version"], { stdio: "ignore", shell: true });
35
- proc.on("close", (code) => {
36
- const installed = code === 0;
37
- timer.end(installed ? "\u2713 dsh is installed" : "\u274C dsh not found");
38
- resolve(installed);
39
- });
40
- proc.on("error", (err) => {
41
- log.debug("Failed to check dsh installation", { error: err.message });
42
- timer.end("\u274C Check failed");
43
- resolve(false);
44
- });
45
- });
28
+ function checkDeepSeekInstalled() {
29
+ return (0, import_node.checkCliInstalled)("dsh");
30
+ }
31
+ function getDeepSeekVersion() {
32
+ return (0, import_node.getCliVersion)("dsh");
33
+ }
34
+ function killOrphanDeepSeekProcesses() {
35
+ return (0, import_node.killOrphanCliProcesses)("dsh", { match: "dsh", winName: "node.exe", label: "dsh" });
46
36
  }
47
37
  const MIN_DSH_VERSION = "0.1.2-rc.1";
48
38
  function parseDshVersion(version) {
@@ -83,151 +73,6 @@ function isDeepSeekVersionAtLeast(version, minimum = MIN_DSH_VERSION) {
83
73
  }
84
74
  return true;
85
75
  }
86
- function getDeepSeekVersion() {
87
- return new Promise((resolve) => {
88
- const proc = (0, import_child_process.spawn)("dsh", ["--version"], { stdio: "pipe", shell: true });
89
- let output = "";
90
- proc.stdout?.on("data", (data) => {
91
- output += data.toString();
92
- });
93
- proc.on("close", (code) => {
94
- if (code === 0 && output.trim()) {
95
- resolve(output.trim());
96
- } else {
97
- resolve(null);
98
- }
99
- });
100
- proc.on("error", () => {
101
- resolve(null);
102
- });
103
- });
104
- }
105
- const KILL_ORPHAN_TIMEOUT = 5e3;
106
- async function killOrphanDeepSeekProcesses() {
107
- const timer = log.timer("killOrphanDeepSeekProcesses");
108
- log.debug("Looking for orphan dsh processes (PPID=1)");
109
- return new Promise((resolve) => {
110
- let settled = false;
111
- const done = (count) => {
112
- if (settled) return;
113
- settled = true;
114
- resolve(count);
115
- };
116
- const timeout = setTimeout(() => {
117
- log.warn("Kill orphan processes timed out, skipping");
118
- timer.end("\u26A0 Timeout, skipped");
119
- done(0);
120
- }, KILL_ORPHAN_TIMEOUT);
121
- const wrappedResolve = (count) => {
122
- clearTimeout(timeout);
123
- done(count);
124
- };
125
- if (process.platform === "win32") {
126
- killOrphanProcessesOnWindows(wrappedResolve, timer);
127
- } else {
128
- killOrphanProcessesOnUnix(wrappedResolve, timer);
129
- }
130
- });
131
- }
132
- function killOrphanProcessesOnWindows(resolve, timer) {
133
- log.debug("Using Windows method to find orphan processes");
134
- const proc = (0, import_child_process.spawn)(
135
- "wmic",
136
- ["process", "where", 'name="node.exe"', "get", "processid,parentprocessid,commandline"],
137
- { stdio: "pipe" }
138
- );
139
- let output = "";
140
- proc.stdout?.on("data", (data) => {
141
- output += data.toString();
142
- });
143
- proc.on("close", () => {
144
- const pidsToKill = [];
145
- output.split("\n").forEach((rawLine) => {
146
- const line = rawLine.trim();
147
- if (!line.includes("dsh")) return;
148
- const parts = line.trim().split(/\s+/);
149
- if (parts.length >= 2) {
150
- const ppid = parts[0];
151
- const pid = parts[1];
152
- if (ppid === "1" && pid && !isNaN(Number(pid))) {
153
- pidsToKill.push(pid);
154
- }
155
- }
156
- });
157
- if (pidsToKill.length > 0) {
158
- log.debug(`Found ${pidsToKill.length} orphan processes`, { pids: pidsToKill });
159
- let killedCount = 0;
160
- let completedCount = 0;
161
- pidsToKill.forEach((pid) => {
162
- const killProc = (0, import_child_process.spawn)("taskkill", ["/F", "/PID", pid], { stdio: "ignore" });
163
- killProc.on("close", (code) => {
164
- completedCount++;
165
- if (code === 0) killedCount++;
166
- if (completedCount === pidsToKill.length) {
167
- timer.end(`\u2713 Killed ${killedCount} orphan processes`);
168
- resolve(killedCount);
169
- }
170
- });
171
- });
172
- } else {
173
- log.debug("No orphan processes found");
174
- timer.end("No orphan processes found");
175
- resolve(0);
176
- }
177
- });
178
- proc.on("error", (err) => {
179
- log.debug("Failed to find orphan processes", { error: err.message });
180
- timer.end("\u274C Failed to find orphan processes");
181
- resolve(0);
182
- });
183
- }
184
- function killOrphanProcessesOnUnix(resolve, timer) {
185
- log.debug("Using Unix method to find orphan processes");
186
- const proc = (0, import_child_process.spawn)("ps", ["-e", "-o", "pid,ppid,args"], { stdio: "pipe" });
187
- let output = "";
188
- proc.stdout?.on("data", (data) => {
189
- output += data.toString();
190
- });
191
- proc.on("close", () => {
192
- const lines = output.split("\n");
193
- const pidsToKill = [];
194
- lines.forEach((line) => {
195
- if (!line.includes("dsh")) return;
196
- const parts = line.trim().split(/\s+/);
197
- if (parts.length >= 3) {
198
- const pid = parts[0];
199
- const ppid = parts[1];
200
- if (ppid === "1") {
201
- pidsToKill.push(pid);
202
- }
203
- }
204
- });
205
- if (pidsToKill.length > 0) {
206
- log.debug(`Found ${pidsToKill.length} orphan processes`, { pids: pidsToKill });
207
- const killProc = (0, import_child_process.spawn)("kill", ["-9", ...pidsToKill], { stdio: "ignore" });
208
- killProc.on("close", (code) => {
209
- const killedCount = code === 0 ? pidsToKill.length : 0;
210
- timer.end(
211
- killedCount > 0 ? `\u2713 Killed ${killedCount} orphan processes` : "\u274C Failed to kill processes"
212
- );
213
- resolve(killedCount);
214
- });
215
- killProc.on("error", () => {
216
- timer.end("\u274C Failed to kill processes");
217
- resolve(0);
218
- });
219
- } else {
220
- log.debug("No orphan processes found");
221
- timer.end("No orphan processes found");
222
- resolve(0);
223
- }
224
- });
225
- proc.on("error", (err) => {
226
- log.debug("Failed to find orphan processes", { error: err.message });
227
- timer.end("\u274C Failed to find orphan processes");
228
- resolve(0);
229
- });
230
- }
231
76
  // Annotate the CommonJS export names for ESM import in node:
232
77
  0 && (module.exports = {
233
78
  MIN_DSH_VERSION,
package/lib/system.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  export declare function checkDeepSeekInstalled(): Promise<boolean>;
2
+ export declare function getDeepSeekVersion(): Promise<string | null>;
3
+ export declare function killOrphanDeepSeekProcesses(): Promise<number>;
2
4
  /** 本 provider 要求的 dsh 最低版本(0.1.2 起:browser-session 认证 + {args} Remote RPC + remote.mux,协议不向下兼容) */
3
5
  export declare const MIN_DSH_VERSION = "0.1.2-rc.1";
4
6
  /**
5
7
  * 判定 version 是否 >= minimum(semver 风格,含 pre-release 比较:0.1.2 > 0.1.2-rc.1)。
6
- * @returns true/false;任一版本无法解析时返回 null(调用方按"无法确认"放行)。
8
+ * @returns true/false;任一版本无法解析时返回 null(调用方按“无法确认”放行)。
7
9
  */
8
10
  export declare function isDeepSeekVersionAtLeast(version: string, minimum?: string): boolean | null;
9
- export declare function getDeepSeekVersion(): Promise<string | null>;
10
- export declare function killOrphanDeepSeekProcesses(): Promise<number>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipanel/provider-deepseek",
3
- "version": "1.2.9",
3
+ "version": "1.2.10",
4
4
  "type": "module",
5
5
  "main": "lib/index.cjs",
6
6
  "module": "es/index.js",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "execa": "^9.6.1",
25
- "@aipanel/core": "1.2.9"
25
+ "@aipanel/core": "1.2.10"
26
26
  },
27
27
  "devDependencies": {
28
28
  "esbuild": "^0.25.0"