@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 +30 -50
- package/es/profile.js +5 -3
- package/es/system.d.ts +3 -3
- package/es/system.js +9 -164
- package/lib/api.cjs +29 -49
- package/lib/profile.cjs +5 -3
- package/lib/system.cjs +8 -163
- package/lib/system.d.ts +3 -3
- package/package.json +2 -2
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,
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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.
|
|
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.
|
|
25
|
+
"@aipanel/core": "1.2.10"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"esbuild": "^0.25.0"
|