@michengai/dsh-codex-ui 0.2.49 → 0.2.53
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/README.md +4 -1
- package/README.zh-CN.md +5 -2
- package/dist/client.js +3914 -1325
- package/dist/index.d.mts +13 -1
- package/dist/index.mjs +72 -6
- package/package.json +11 -9
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { Context } from "@deepseek-ai/cordis";
|
|
2
2
|
//#region src/index.d.ts
|
|
3
|
+
type HostRequest = {
|
|
4
|
+
method?: string;
|
|
5
|
+
url?: string;
|
|
6
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* 判断浏览器请求是否跨站。依赖安装会改写用户配置并拉起子进程,
|
|
10
|
+
* 恶意网页只需一个表单就能跨站触发,必须先按 Sec-Fetch-Site(优先,
|
|
11
|
+
* 且无法被页面伪造)再按 Origin 与 Host 的比对阻断;无这些头的
|
|
12
|
+
* 非浏览器客户端(curl、CLI)仍然放行。
|
|
13
|
+
*/
|
|
14
|
+
declare function crossSiteRequest(request: HostRequest): boolean;
|
|
3
15
|
declare const inject: string[];
|
|
4
16
|
/** 提供不泄露地址、命令和凭证的连接器目录。 */
|
|
5
17
|
declare function apply(ctx: Context): void;
|
|
6
18
|
//#endregion
|
|
7
|
-
export { apply, inject };
|
|
19
|
+
export { apply, crossSiteRequest, inject };
|
package/dist/index.mjs
CHANGED
|
@@ -20,6 +20,18 @@ const MANAGED_DEPENDENCIES = [
|
|
|
20
20
|
{
|
|
21
21
|
id: "archive",
|
|
22
22
|
packageName: "@michengai/dsh-archive-manager"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "im",
|
|
26
|
+
packageName: "@michengai/dsh-im-connect"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "schedule",
|
|
30
|
+
packageName: "@michengai/dsh-automation"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "market",
|
|
34
|
+
packageName: "dshmarket"
|
|
23
35
|
}
|
|
24
36
|
];
|
|
25
37
|
function managedDependency(id) {
|
|
@@ -48,12 +60,22 @@ async function installedPackageVersion(packageName) {
|
|
|
48
60
|
throw error;
|
|
49
61
|
}
|
|
50
62
|
}
|
|
63
|
+
/** npm latest 查询缓存有效期:避免每次打开“关于”页都打 7 个 registry 请求。 */
|
|
64
|
+
const LATEST_CACHE_TTL_MS = 3e5;
|
|
65
|
+
const latestCache = /* @__PURE__ */ new Map();
|
|
51
66
|
async function npmLatestVersion(packageName) {
|
|
67
|
+
const hit = latestCache.get(packageName);
|
|
68
|
+
if (hit !== void 0 && Date.now() - hit.at < LATEST_CACHE_TTL_MS) return hit.version;
|
|
52
69
|
try {
|
|
53
70
|
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, { signal: AbortSignal.timeout(5e3) });
|
|
54
71
|
if (!response.ok) return void 0;
|
|
55
72
|
const manifest = await response.json();
|
|
56
|
-
|
|
73
|
+
if (typeof manifest.version !== "string") return void 0;
|
|
74
|
+
latestCache.set(packageName, {
|
|
75
|
+
version: manifest.version,
|
|
76
|
+
at: Date.now()
|
|
77
|
+
});
|
|
78
|
+
return manifest.version;
|
|
57
79
|
} catch {
|
|
58
80
|
return;
|
|
59
81
|
}
|
|
@@ -61,8 +83,13 @@ async function npmLatestVersion(packageName) {
|
|
|
61
83
|
function escapeRegExp(value) {
|
|
62
84
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
63
85
|
}
|
|
86
|
+
/** 允许写入 YAML 单引号白名单的版本:semver 及常见预发布后缀,禁止引号与空白。 */
|
|
87
|
+
function isSafeReleaseVersion(version) {
|
|
88
|
+
return /^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(version);
|
|
89
|
+
}
|
|
64
90
|
/** 将用户确认的精确版本合并进 Profile 的 pnpm 发布时间保护例外。 */
|
|
65
91
|
function applyReleaseExclude(source, packageName, version) {
|
|
92
|
+
if (!isSafeReleaseVersion(version)) throw new Error("npm 返回了无法识别的最新版本。");
|
|
66
93
|
const eol = source.includes("\r\n") ? "\r\n" : "\n";
|
|
67
94
|
const existing = new RegExp(`^ - '${escapeRegExp(packageName)}@([^']*)'\\s*$`, "m").exec(source);
|
|
68
95
|
if (existing !== null) {
|
|
@@ -183,8 +210,19 @@ function runDshPlugin(args) {
|
|
|
183
210
|
});
|
|
184
211
|
});
|
|
185
212
|
}
|
|
213
|
+
/** 并发安装互斥:pnpm 锁文件竞争会触发 EPERM/EBUSY,同一时间只允许一个安装进程。 */
|
|
214
|
+
let installing = false;
|
|
186
215
|
/** 仅允许安装固定依赖,避免把浏览器输入转成任意命令。 */
|
|
187
216
|
async function installDependency(id) {
|
|
217
|
+
if (installing) throw new Error("已有依赖安装正在进行,请等待完成后再试。");
|
|
218
|
+
installing = true;
|
|
219
|
+
try {
|
|
220
|
+
return await installDependencyLocked(id);
|
|
221
|
+
} finally {
|
|
222
|
+
installing = false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async function installDependencyLocked(id) {
|
|
188
226
|
const dependency = managedDependency(id);
|
|
189
227
|
if (dependency === void 0) throw new Error("不支持安装该依赖。");
|
|
190
228
|
const latestVersion = await npmLatestVersion(dependency.packageName);
|
|
@@ -206,7 +244,6 @@ function requireService(ctx, key, method) {
|
|
|
206
244
|
function hostServices(ctx) {
|
|
207
245
|
return {
|
|
208
246
|
webServer: requireService(ctx, "webServer", "register"),
|
|
209
|
-
sessions: requireService(ctx, "sessions", "get"),
|
|
210
247
|
agents: requireService(ctx, "agents", "get"),
|
|
211
248
|
tools: requireService(ctx, "tools", "schemas")
|
|
212
249
|
};
|
|
@@ -215,9 +252,30 @@ function hostServices(ctx) {
|
|
|
215
252
|
//#region src/index.ts
|
|
216
253
|
const connectorsEndpoint = "/api/michengai/codex-ui/connectors";
|
|
217
254
|
const dependenciesEndpoint = "/api/michengai/codex-ui/dependencies";
|
|
255
|
+
function headerValue(headers, name) {
|
|
256
|
+
const value = headers[name];
|
|
257
|
+
if (typeof value === "string") return value;
|
|
258
|
+
if (Array.isArray(value)) return value[0];
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* 判断浏览器请求是否跨站。依赖安装会改写用户配置并拉起子进程,
|
|
262
|
+
* 恶意网页只需一个表单就能跨站触发,必须先按 Sec-Fetch-Site(优先,
|
|
263
|
+
* 且无法被页面伪造)再按 Origin 与 Host 的比对阻断;无这些头的
|
|
264
|
+
* 非浏览器客户端(curl、CLI)仍然放行。
|
|
265
|
+
*/
|
|
266
|
+
function crossSiteRequest(request) {
|
|
267
|
+
const headers = request.headers;
|
|
268
|
+
if (headers === void 0) return false;
|
|
269
|
+
const site = headerValue(headers, "sec-fetch-site");
|
|
270
|
+
if (site === "same-origin" || site === "none") return false;
|
|
271
|
+
if (site !== void 0) return true;
|
|
272
|
+
const origin = headerValue(headers, "origin");
|
|
273
|
+
if (origin === void 0) return false;
|
|
274
|
+
const host = headerValue(headers, "host");
|
|
275
|
+
return origin !== new URL(request.url ?? "/", `http://${host ?? "localhost"}`).origin;
|
|
276
|
+
}
|
|
218
277
|
const inject = [
|
|
219
278
|
"webServer",
|
|
220
|
-
"sessions",
|
|
221
279
|
"agents",
|
|
222
280
|
"tools"
|
|
223
281
|
];
|
|
@@ -283,6 +341,14 @@ function apply(ctx) {
|
|
|
283
341
|
return;
|
|
284
342
|
}
|
|
285
343
|
if (request.method === "POST") {
|
|
344
|
+
if (crossSiteRequest(request)) {
|
|
345
|
+
response.writeHead(403, {
|
|
346
|
+
"content-type": "application/json; charset=utf-8",
|
|
347
|
+
"cache-control": "no-store"
|
|
348
|
+
});
|
|
349
|
+
response.end(JSON.stringify({ error: "已拒绝跨站请求。" }));
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
286
352
|
const dependencies = await installDependency(url.searchParams.get("dependency"));
|
|
287
353
|
response.writeHead(200, {
|
|
288
354
|
"content-type": "application/json; charset=utf-8",
|
|
@@ -297,12 +363,12 @@ function apply(ctx) {
|
|
|
297
363
|
response.writeHead(405);
|
|
298
364
|
response.end();
|
|
299
365
|
} catch (error) {
|
|
300
|
-
|
|
366
|
+
ctx.logger.warn("dependencies endpoint failed: %s", error);
|
|
301
367
|
response.writeHead(503, {
|
|
302
368
|
"content-type": "application/json; charset=utf-8",
|
|
303
369
|
"cache-control": "no-store"
|
|
304
370
|
});
|
|
305
|
-
response.end(JSON.stringify({ error:
|
|
371
|
+
response.end(JSON.stringify({ error: "依赖管理暂不可用,请查看服务端日志。" }));
|
|
306
372
|
}
|
|
307
373
|
}
|
|
308
374
|
});
|
|
@@ -313,4 +379,4 @@ function apply(ctx) {
|
|
|
313
379
|
}, "michengai-codex-ui: catalogs");
|
|
314
380
|
}
|
|
315
381
|
//#endregion
|
|
316
|
-
export { apply, inject };
|
|
382
|
+
export { apply, crossSiteRequest, inject };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@michengai/dsh-codex-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.53",
|
|
4
4
|
"description": "以 Codex 风格重构 DSH Web 侧栏的独立客户端插件",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "tsc --noEmit && tsdown",
|
|
56
|
-
"test": "tsx tests/pinned-sessions.assert.ts && tsx tests/session-manager.assert.ts && tsx tests/workspace-browser.assert.ts && tsx tests/sidebar-search.assert.ts && vitest run tests/client-runtime.integration.spec.ts && tsx tests/settings-integration.assert.ts && tsx tests/about-dependencies.assert.ts && tsx tests/dependency-manager.assert.ts && tsx tests/conversation-visuals.assert.ts && tsx tests/client-bundle.assert.ts && tsx tests/codex-suite.assert.ts"
|
|
56
|
+
"test": "tsx tests/permission-i18n.assert.ts && tsx tests/hover-tip.assert.ts && tsx tests/companion-slots.assert.ts && tsx tests/channel-api.assert.ts && tsx tests/schedule-sessions.assert.ts && tsx tests/session-tree.assert.ts && tsx tests/pinned-sessions.assert.ts && tsx tests/session-manager.assert.ts && tsx tests/workspace-browser.assert.ts && tsx tests/sidebar-search.assert.ts && tsx tests/settings-navigation.assert.ts && tsx tests/settings-nav-icons.assert.ts && tsx tests/locales.assert.ts && vitest run tests/client-runtime.integration.spec.ts && tsx tests/settings-integration.assert.ts && tsx tests/about-dependencies.assert.ts && tsx tests/dependency-manager.assert.ts && tsx tests/conversation-bubbles.assert.ts && tsx tests/conversation-header.assert.ts && tsx tests/conversation-visuals.assert.ts && tsdown && tsx tests/client-bundle.assert.ts && tsx tests/codex-suite.assert.ts"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@deepseek-ai/cordis": ">=4.0.1 <5.0.0",
|
|
@@ -71,8 +71,12 @@
|
|
|
71
71
|
"react": "^18.2.0"
|
|
72
72
|
},
|
|
73
73
|
"peerDependenciesMeta": {
|
|
74
|
-
"@michengai/dsh-agency-agents": {
|
|
75
|
-
|
|
74
|
+
"@michengai/dsh-agency-agents": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"@michengai/dsh-skills-manager": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
76
80
|
},
|
|
77
81
|
"devDependencies": {
|
|
78
82
|
"@deepseek-ai/cordis": "4.0.1",
|
|
@@ -86,17 +90,15 @@
|
|
|
86
90
|
"@deepseek-ai/dsh-client-ui-settings-general": "0.1.0-rc.6",
|
|
87
91
|
"@deepseek-ai/dsh-client-ui-sidebar": "0.1.0-rc.6",
|
|
88
92
|
"@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6",
|
|
93
|
+
"@types/jsdom": "^30.0.0",
|
|
89
94
|
"@types/node": "^26.0.0",
|
|
90
95
|
"@types/react": "~18.3.1",
|
|
96
|
+
"jsdom": "^29.1.1",
|
|
91
97
|
"react": "^18.3.1",
|
|
92
98
|
"react-dom": "^18.3.1",
|
|
93
99
|
"tsdown": "^0.22.2",
|
|
94
100
|
"tsx": "^4.22.4",
|
|
95
101
|
"typescript": "^6.0.3",
|
|
96
|
-
"vitest": "^4.1.8"
|
|
97
|
-
"jsdom": "^29.1.1"
|
|
102
|
+
"vitest": "^4.1.8"
|
|
98
103
|
}
|
|
99
104
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|