@shgroup/dsh-serenity-hooks 1.28.1 → 1.28.2
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/dsh.plugin.json +1 -1
- package/lib/gateway-dsh-auth.d.ts +47 -0
- package/lib/gateway-proxy.d.ts +18 -1
- package/lib/gateway.d.ts +2 -2
- package/lib/index.js +159 -12
- package/package.json +1 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.28.
|
|
3
|
+
"version": "1.28.2",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 cc_fs/session/acc_msm/eap/neat/cce/handyman/session_rebuild/skiff_admin + 拦截缝机械约束(safe-mode/路径守卫)+ 高级设定面板(双端口网关/账号管理)+ Skiff 认知子集角色(实验性)。适配 DSH 公开版(0.1.0-rc,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gateway-dsh-auth.ts — 3081 网关对 DSH 主端口 BrowserAuth 的适配(v1.28.2,S142 用户 bug)
|
|
3
|
+
*
|
|
4
|
+
* 背景:DSH 0.1.2-rc.1 新增 BrowserAuth(browser-auth.ts)——主端口所有 /api 请求与
|
|
5
|
+
* index 渲染前必须持有 authority 绑定的签名 cookie(`dsh-auth-<sha256(Host)>`)。
|
|
6
|
+
* 3081 网关反代请求到主端口时 Host 已改写为 loopback(127.0.0.1:<mainPort>,过信任栅栏),
|
|
7
|
+
* 但**没有 dsh browser cookie** → 上游 401 "dsh web authentication required"。
|
|
8
|
+
*
|
|
9
|
+
* 方案(S142 用户拍板:不用 cookie 落盘绕,直接解决——内存换取 + 缓存,用户无感):
|
|
10
|
+
* dsp 与 DSH **同进程** → 经 `ctx.connection`(HostConnectionHandle)官方通道
|
|
11
|
+
* (authenticatedUrl + authorizeIndex——web-app openBrowser 同款)在**内存**换取
|
|
12
|
+
* authority=127.0.0.1:<mainPort> 的 dsh cookie,缓存后注入所有反代上游请求。
|
|
13
|
+
* 零落盘、无 HTTP 往返、不复制官方 HMAC 逻辑(版本漂移风险为零)。
|
|
14
|
+
*
|
|
15
|
+
* 兼容性:connection 服务缺失(旧 dsh / 非 web 装配)→ cookie 换取不可用 →
|
|
16
|
+
* getDshCookie 返回 undefined → 反代不注入(旧形态无 BrowserAuth,天然兼容)。
|
|
17
|
+
*/
|
|
18
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
19
|
+
/** ctx.connection 服务的最小可测面(HostConnectionHandle 子集;避免硬依赖官方类型) */
|
|
20
|
+
export interface DshConnectionLike {
|
|
21
|
+
/** 给干净 baseUrl 追加本进程 launch token → 首次登录 URL */
|
|
22
|
+
authenticatedUrl(baseUrl: string): string;
|
|
23
|
+
/** 认证一个 index 请求:token 匹配则种 cookie(写 res)并返回 false;cookie 有效返回 true */
|
|
24
|
+
authorizeIndex(request: IncomingMessage, response: ServerResponse): boolean;
|
|
25
|
+
}
|
|
26
|
+
/** 从一组 set-cookie 头提取 `dsh-auth-*` cookie(browser-auth sessionCookie 首段) */
|
|
27
|
+
export declare function pickDshCookie(setCookieHeader: string | string[] | undefined): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* 把 dsh browser cookie 合并进现有 Cookie 头(保持外部既有 cookie——serenity_session 等)。
|
|
30
|
+
* 现有头可为 string / string[](node 头形态);无现有 → 只返回 dsh cookie。
|
|
31
|
+
*/
|
|
32
|
+
export declare function mergeCookieHeader(existing: string | string[] | number | undefined, dshCookie: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* 内存换取 authority 绑定的 dsh browser cookie(S142 拍板方案核心)。
|
|
35
|
+
* 用官方通道在进程内完成 token→cookie 交换:构造一个对 `http://<authority>/?token=…`
|
|
36
|
+
* 的 index 请求 → connection.authorizeIndex 校验 token → 写 set-cookie → 提取 cookie。
|
|
37
|
+
* @returns cookie 头值(如 `dsh-auth-xxx=v1.…`);authority/connection 异常 → undefined
|
|
38
|
+
*/
|
|
39
|
+
export declare function exchangeDshCookie(connection: DshConnectionLike, authority: string): string | undefined;
|
|
40
|
+
/** 可注入的 cookie 提供者(gateway 反代上游请求头用;无 connection/换取失败 → undefined) */
|
|
41
|
+
export type DshCookieProvider = () => string | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* 构建一个带内存缓存的 dsh cookie 提供者(S142 拍板:内存缓存,不落盘)。
|
|
44
|
+
* 惰性换取:首次调用才向 connection 换;失败返回 undefined(不缓存失败——下次重试)。
|
|
45
|
+
* 调用方可周期性失效缓存重换(cookie 30 天有效;进程重启 token 仍在 → 换一次长期可用)。
|
|
46
|
+
*/
|
|
47
|
+
export declare function createDshCookieProvider(connection: DshConnectionLike | undefined, authority: string): DshCookieProvider;
|
package/lib/gateway-proxy.d.ts
CHANGED
|
@@ -29,8 +29,25 @@ export declare function filterWorkspaceList(body: string, allowPrefixes: readonl
|
|
|
29
29
|
export declare function workspaceAllowed(allowPrefixes: readonly string[], path: string | undefined): boolean;
|
|
30
30
|
/** 构造 workspace.create 拒绝的 JSON RPC 响应体(code=forbidden) */
|
|
31
31
|
export declare function workspaceDenyResponse(rpcId: string): string;
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* 在 HTML 的 </head> 前注入 polyfill(幂等:含 marker 则跳过)
|
|
34
|
+
*/
|
|
33
35
|
export declare function injectPolyfillHtml(html: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* 反代 HTML 响应注入变换(v1.28.2 修复,S142 白屏根因):
|
|
38
|
+
* 上游按 Accept-Encoding 可能返回 gzip/br/deflate 压缩的 HTML——注入必须在**明文**上进行,
|
|
39
|
+
* 把压缩字节 toString 当文本注入会破坏压缩流(找不到 </head> → polyfill 前置 → HTML 损坏
|
|
40
|
+
* → 浏览器白屏,用户实测 https://dsh.notfoundhome.cc 登录后白屏)。
|
|
41
|
+
*
|
|
42
|
+
* @param raw - 上游响应完整 body(可能压缩)
|
|
43
|
+
* @param upstreamHeaders - 上游响应头(node IncomingHttpHeaders 形态)
|
|
44
|
+
* @returns { body, headers } 注入后的明文 body + 修正后的响应头(去 content-encoding/transfer-encoding、
|
|
45
|
+
* 重算 content-length);解压失败返回 null(调用方应原样透传避免破坏)
|
|
46
|
+
*/
|
|
47
|
+
export declare function transformHtmlForProxy(raw: Buffer, upstreamHeaders: Record<string, unknown>): {
|
|
48
|
+
body: string;
|
|
49
|
+
headers: Record<string, string | number | string[]>;
|
|
50
|
+
} | null;
|
|
34
51
|
/**
|
|
35
52
|
* 反代请求头构造(v1.22.1 信任栅栏修复,纯逻辑可测):
|
|
36
53
|
* DSH isTrustedApiRequest 要求 Origin.host === Host.host——Host 改写为 loopback 后
|
package/lib/gateway.d.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import type { Context } from 'cordis';
|
|
29
29
|
import { type Server } from 'node:http';
|
|
30
|
-
export { RANDOM_UUID_POLYFILL, injectPolyfillHtml, filterWorkspaceList, workspaceAllowed, workspaceDenyResponse, buildProxyHeaders, } from './gateway-proxy.js';
|
|
30
|
+
export { RANDOM_UUID_POLYFILL, injectPolyfillHtml, filterWorkspaceList, workspaceAllowed, workspaceDenyResponse, buildProxyHeaders, transformHtmlForProxy, } from './gateway-proxy.js';
|
|
31
31
|
export { loginPageHtml, verifyGatewayLogin, SESSION_TTL_MS, issueToken, revokeToken, validateToken, FAIL_LOCK_THRESHOLD, FAIL_LOCK_BASE_MS, FAIL_LOCK_MAX_MS, getFailState, resetFailState, isAccountLocked, recordLoginFailure, accountLockRemaining, newCsrfToken, isCsrfValid, csrfFromRequest, safeEqual, originAllowed, cookieValue, } from './gateway-auth.js';
|
|
32
32
|
declare module 'cordis' {
|
|
33
33
|
interface Events {
|
|
@@ -66,7 +66,7 @@ export declare function startGateway(config: GatewayConfig, getAccounts: () => r
|
|
|
66
66
|
user: string;
|
|
67
67
|
passHash: string;
|
|
68
68
|
totpSecret?: string;
|
|
69
|
-
}[]): {
|
|
69
|
+
}[], getDshCookie?: () => string | undefined): {
|
|
70
70
|
server: Server;
|
|
71
71
|
dispose: () => void;
|
|
72
72
|
};
|
package/lib/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
|
18
18
|
import { createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
|
|
19
19
|
import { deriveEventMessage } from "@deepseek-ai/dsh-session";
|
|
20
20
|
import { createServer, request } from "node:http";
|
|
21
|
+
import * as zlib from "node:zlib";
|
|
21
22
|
//#region src/fs-ops.ts
|
|
22
23
|
/**
|
|
23
24
|
* fs-ops.ts — cc_fs 纯操作层(零 DSH 依赖,可独立单测)
|
|
@@ -7453,7 +7454,9 @@ function workspaceDenyResponse(rpcId) {
|
|
|
7453
7454
|
}
|
|
7454
7455
|
});
|
|
7455
7456
|
}
|
|
7456
|
-
/**
|
|
7457
|
+
/**
|
|
7458
|
+
* 在 HTML 的 </head> 前注入 polyfill(幂等:含 marker 则跳过)
|
|
7459
|
+
*/
|
|
7457
7460
|
function injectPolyfillHtml(html) {
|
|
7458
7461
|
if (html.includes(POLYFILL_MARKER)) return html;
|
|
7459
7462
|
const head = RANDOM_UUID_POLYFILL.replace("<script>", `<script ${POLYFILL_MARKER}="1">`);
|
|
@@ -7461,6 +7464,37 @@ function injectPolyfillHtml(html) {
|
|
|
7461
7464
|
return `${head}\n${html}`;
|
|
7462
7465
|
}
|
|
7463
7466
|
/**
|
|
7467
|
+
* 反代 HTML 响应注入变换(v1.28.2 修复,S142 白屏根因):
|
|
7468
|
+
* 上游按 Accept-Encoding 可能返回 gzip/br/deflate 压缩的 HTML——注入必须在**明文**上进行,
|
|
7469
|
+
* 把压缩字节 toString 当文本注入会破坏压缩流(找不到 </head> → polyfill 前置 → HTML 损坏
|
|
7470
|
+
* → 浏览器白屏,用户实测 https://dsh.notfoundhome.cc 登录后白屏)。
|
|
7471
|
+
*
|
|
7472
|
+
* @param raw - 上游响应完整 body(可能压缩)
|
|
7473
|
+
* @param upstreamHeaders - 上游响应头(node IncomingHttpHeaders 形态)
|
|
7474
|
+
* @returns { body, headers } 注入后的明文 body + 修正后的响应头(去 content-encoding/transfer-encoding、
|
|
7475
|
+
* 重算 content-length);解压失败返回 null(调用方应原样透传避免破坏)
|
|
7476
|
+
*/
|
|
7477
|
+
function transformHtmlForProxy(raw, upstreamHeaders) {
|
|
7478
|
+
let body = raw;
|
|
7479
|
+
const enc = String(upstreamHeaders["content-encoding"] ?? "").toLowerCase().trim();
|
|
7480
|
+
const outHeaders = { ...upstreamHeaders };
|
|
7481
|
+
if (enc === "gzip" || enc === "x-gzip" || enc === "deflate" || enc === "br") try {
|
|
7482
|
+
if (enc === "br") body = zlib.brotliDecompressSync(body);
|
|
7483
|
+
else if (enc === "deflate") body = zlib.inflateSync(body);
|
|
7484
|
+
else body = zlib.gunzipSync(body);
|
|
7485
|
+
delete outHeaders["content-encoding"];
|
|
7486
|
+
} catch {
|
|
7487
|
+
return null;
|
|
7488
|
+
}
|
|
7489
|
+
const transformed = injectPolyfillHtml(body.toString("utf-8"));
|
|
7490
|
+
outHeaders["content-length"] = Buffer.byteLength(transformed);
|
|
7491
|
+
delete outHeaders["transfer-encoding"];
|
|
7492
|
+
return {
|
|
7493
|
+
body: transformed,
|
|
7494
|
+
headers: outHeaders
|
|
7495
|
+
};
|
|
7496
|
+
}
|
|
7497
|
+
/**
|
|
7464
7498
|
* 反代请求头构造(v1.22.1 信任栅栏修复,纯逻辑可测):
|
|
7465
7499
|
* DSH isTrustedApiRequest 要求 Origin.host === Host.host——Host 改写为 loopback 后
|
|
7466
7500
|
* Origin 必须同步改写(浏览器 POST 必带 Origin,透传外部地址 → 403)。
|
|
@@ -7475,6 +7509,69 @@ function buildProxyHeaders(reqHeaders, mainPort, bodyOverride) {
|
|
|
7475
7509
|
return headers;
|
|
7476
7510
|
}
|
|
7477
7511
|
//#endregion
|
|
7512
|
+
//#region src/gateway-dsh-auth.ts
|
|
7513
|
+
/** 从一组 set-cookie 头提取 `dsh-auth-*` cookie(browser-auth sessionCookie 首段) */
|
|
7514
|
+
function pickDshCookie(setCookieHeader) {
|
|
7515
|
+
const entries = Array.isArray(setCookieHeader) ? setCookieHeader : setCookieHeader === void 0 ? [] : [setCookieHeader];
|
|
7516
|
+
for (const entry of entries) {
|
|
7517
|
+
const first = entry.split(";", 1)[0].trim();
|
|
7518
|
+
if (first.startsWith("dsh-auth-") && first.includes("=")) return first;
|
|
7519
|
+
}
|
|
7520
|
+
}
|
|
7521
|
+
/**
|
|
7522
|
+
* 把 dsh browser cookie 合并进现有 Cookie 头(保持外部既有 cookie——serenity_session 等)。
|
|
7523
|
+
* 现有头可为 string / string[](node 头形态);无现有 → 只返回 dsh cookie。
|
|
7524
|
+
*/
|
|
7525
|
+
function mergeCookieHeader(existing, dshCookie) {
|
|
7526
|
+
const existingStr = Array.isArray(existing) ? existing.join("; ") : typeof existing === "string" && existing !== "" ? existing : void 0;
|
|
7527
|
+
return existingStr !== void 0 ? `${existingStr}; ${dshCookie}` : dshCookie;
|
|
7528
|
+
}
|
|
7529
|
+
/**
|
|
7530
|
+
* 内存换取 authority 绑定的 dsh browser cookie(S142 拍板方案核心)。
|
|
7531
|
+
* 用官方通道在进程内完成 token→cookie 交换:构造一个对 `http://<authority>/?token=…`
|
|
7532
|
+
* 的 index 请求 → connection.authorizeIndex 校验 token → 写 set-cookie → 提取 cookie。
|
|
7533
|
+
* @returns cookie 头值(如 `dsh-auth-xxx=v1.…`);authority/connection 异常 → undefined
|
|
7534
|
+
*/
|
|
7535
|
+
function exchangeDshCookie(connection, authority) {
|
|
7536
|
+
try {
|
|
7537
|
+
const url = new URL(connection.authenticatedUrl(`http://${authority}`));
|
|
7538
|
+
if (url.searchParams.get("token") === null) return void 0;
|
|
7539
|
+
const headers = { host: authority };
|
|
7540
|
+
const request = {
|
|
7541
|
+
url: `${url.pathname}${url.search}`,
|
|
7542
|
+
method: "GET",
|
|
7543
|
+
headers
|
|
7544
|
+
};
|
|
7545
|
+
let captured;
|
|
7546
|
+
const response = {
|
|
7547
|
+
writeHead(_status, headers) {
|
|
7548
|
+
if (headers !== void 0) captured = headers["set-cookie"];
|
|
7549
|
+
return response;
|
|
7550
|
+
},
|
|
7551
|
+
end: () => response
|
|
7552
|
+
};
|
|
7553
|
+
connection.authorizeIndex(request, response);
|
|
7554
|
+
return pickDshCookie(captured);
|
|
7555
|
+
} catch {
|
|
7556
|
+
return;
|
|
7557
|
+
}
|
|
7558
|
+
}
|
|
7559
|
+
/**
|
|
7560
|
+
* 构建一个带内存缓存的 dsh cookie 提供者(S142 拍板:内存缓存,不落盘)。
|
|
7561
|
+
* 惰性换取:首次调用才向 connection 换;失败返回 undefined(不缓存失败——下次重试)。
|
|
7562
|
+
* 调用方可周期性失效缓存重换(cookie 30 天有效;进程重启 token 仍在 → 换一次长期可用)。
|
|
7563
|
+
*/
|
|
7564
|
+
function createDshCookieProvider(connection, authority) {
|
|
7565
|
+
let cached;
|
|
7566
|
+
return () => {
|
|
7567
|
+
if (connection === void 0) return void 0;
|
|
7568
|
+
if (cached !== void 0) return cached;
|
|
7569
|
+
const cookie = exchangeDshCookie(connection, authority);
|
|
7570
|
+
if (cookie !== void 0) cached = cookie;
|
|
7571
|
+
return cookie;
|
|
7572
|
+
};
|
|
7573
|
+
}
|
|
7574
|
+
//#endregion
|
|
7478
7575
|
//#region src/gateway.ts
|
|
7479
7576
|
/** 读取请求体(≤ maxBytes;超限 reject) */
|
|
7480
7577
|
function readBody$1(req, maxBytes) {
|
|
@@ -7497,7 +7594,7 @@ function readBody$1(req, maxBytes) {
|
|
|
7497
7594
|
* @param config - 监听/代理配置。
|
|
7498
7595
|
* @param getAccounts - 运行时读取账号列表(plugin 全局文件;gateway.enabled 开关在调用方判)。
|
|
7499
7596
|
*/
|
|
7500
|
-
function startGateway(config, getAccounts) {
|
|
7597
|
+
function startGateway(config, getAccounts, getDshCookie) {
|
|
7501
7598
|
const { host, port, cookieName, mainPort, loginDelayMs } = config;
|
|
7502
7599
|
const allowWorkspaces = config.allowWorkspaces ?? [];
|
|
7503
7600
|
const cookieSecure = config.cookieSecure === true;
|
|
@@ -7506,6 +7603,18 @@ function startGateway(config, getAccounts) {
|
|
|
7506
7603
|
/** 校验请求 Cookie 是否含有效 token(v1.22.4:滑动过期 + 返回会话供审计) */
|
|
7507
7604
|
const authed = (req) => validateToken(cookieValue(req.headers.cookie, cookieName));
|
|
7508
7605
|
/**
|
|
7606
|
+
* 构造上游请求头(buildProxyHeaders 基准 + dsh browser cookie 注入)。
|
|
7607
|
+
* v1.28.2(0.1.2-rc.1 BrowserAuth 适配):主端口 /api + index 需 dsh browser cookie。
|
|
7608
|
+
* 反代 Host 已改写 loopback(过信任栅栏)→ 只缺 cookie → 注入内存换取 cookie
|
|
7609
|
+
*(createDshCookieProvider 官方通道,零落盘)。无 cookie(旧 dsh/换取失败)→ 不注入向后兼容。
|
|
7610
|
+
*/
|
|
7611
|
+
const upstreamHeaders = (req, bodyOverride) => {
|
|
7612
|
+
const headers = buildProxyHeaders(req.headers, mainPort, bodyOverride);
|
|
7613
|
+
const dshCookie = getDshCookie?.();
|
|
7614
|
+
if (dshCookie !== void 0) headers.cookie = mergeCookieHeader(headers.cookie, dshCookie);
|
|
7615
|
+
return headers;
|
|
7616
|
+
};
|
|
7617
|
+
/**
|
|
7509
7618
|
* 反向代理:改写 Host + Origin 头 → 主端口(loopback 过信任栅栏)。
|
|
7510
7619
|
* v1.22.1 修复:DSH 信任栅栏(api-request-trust.ts isTrustedApiRequest)要求
|
|
7511
7620
|
* **Origin.host === Host.host**——仅改写 Host(127.0.0.1:3080)而 Origin 透传
|
|
@@ -7516,7 +7625,7 @@ function startGateway(config, getAccounts) {
|
|
|
7516
7625
|
* @param bodyOverride - workspace.create 已读 body 时的重放(白名单检查后转发)
|
|
7517
7626
|
*/
|
|
7518
7627
|
const proxy = (req, res, bodyOverride) => {
|
|
7519
|
-
const headers =
|
|
7628
|
+
const headers = upstreamHeaders(req, bodyOverride);
|
|
7520
7629
|
const target = request({
|
|
7521
7630
|
host: "127.0.0.1",
|
|
7522
7631
|
port: mainPort,
|
|
@@ -7530,13 +7639,15 @@ function startGateway(config, getAccounts) {
|
|
|
7530
7639
|
const chunks = [];
|
|
7531
7640
|
upstream.on("data", (c) => chunks.push(c));
|
|
7532
7641
|
upstream.on("end", () => {
|
|
7533
|
-
|
|
7534
|
-
const
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7642
|
+
let raw = Buffer.concat(chunks);
|
|
7643
|
+
const transformedRes = transformHtmlForProxy(raw, upstream.headers);
|
|
7644
|
+
if (transformedRes === null) {
|
|
7645
|
+
res.writeHead(status, upstream.headers);
|
|
7646
|
+
res.end(raw);
|
|
7647
|
+
return;
|
|
7648
|
+
}
|
|
7649
|
+
res.writeHead(status, transformedRes.headers);
|
|
7650
|
+
res.end(transformedRes.body);
|
|
7540
7651
|
});
|
|
7541
7652
|
upstream.on("error", () => {
|
|
7542
7653
|
try {
|
|
@@ -7767,7 +7878,7 @@ function startGateway(config, getAccounts) {
|
|
|
7767
7878
|
port: mainPort,
|
|
7768
7879
|
path: req.url ?? "/",
|
|
7769
7880
|
method: req.method,
|
|
7770
|
-
headers:
|
|
7881
|
+
headers: upstreamHeaders(req)
|
|
7771
7882
|
});
|
|
7772
7883
|
upstream.on("upgrade", (ures, usock, uhead) => {
|
|
7773
7884
|
usocket = usock;
|
|
@@ -7841,6 +7952,42 @@ function startGateway(config, getAccounts) {
|
|
|
7841
7952
|
}
|
|
7842
7953
|
};
|
|
7843
7954
|
}
|
|
7955
|
+
/**
|
|
7956
|
+
* 构造 dsh browser cookie 提供者(v1.28.2 0.1.2-rc.1 BrowserAuth 适配):
|
|
7957
|
+
* 经 ctx.connection(HostConnectionHandle)官方通道在内存换取 authority=127.0.0.1:<mainPort>
|
|
7958
|
+
* 的 cookie 并缓存(createDshCookieProvider 惰性换取)。connection 缺失(旧 dsh/非 web 装配)
|
|
7959
|
+
* → provider 返回 undefined → 反代不注入(旧形态无 BrowserAuth,天然兼容)。
|
|
7960
|
+
*
|
|
7961
|
+
* v1.28.2 修复:**connection 每次调用现取**(不绑定 sync 时点)——gateway sync 在 apply
|
|
7962
|
+
* 早期跑,此时 connection 服务可能尚未装配(web-app 的 URL 打印在其后);首次反代请求
|
|
7963
|
+
* 到达时 connection 必已就绪。cachedProvider 缓存换取结果(同一 connection 实例)。
|
|
7964
|
+
*/
|
|
7965
|
+
function buildDshCookieProvider(ctx, mainPort) {
|
|
7966
|
+
const authority = `127.0.0.1:${mainPort}`;
|
|
7967
|
+
let cachedProvider;
|
|
7968
|
+
let diagnosed = false;
|
|
7969
|
+
return () => {
|
|
7970
|
+
try {
|
|
7971
|
+
const connection = ctx.get?.("connection");
|
|
7972
|
+
if (!connection || typeof connection.authenticatedUrl !== "function" || typeof connection.authorizeIndex !== "function") {
|
|
7973
|
+
if (!diagnosed) {
|
|
7974
|
+
diagnosed = true;
|
|
7975
|
+
console.log("[serenity-hooks] dsh browser-auth 适配: connection ✗ 不可取(不注入 dsh cookie——旧 dsh/非 web 装配兼容)");
|
|
7976
|
+
}
|
|
7977
|
+
return;
|
|
7978
|
+
}
|
|
7979
|
+
if (cachedProvider === void 0) cachedProvider = createDshCookieProvider(connection, authority);
|
|
7980
|
+
const cookie = cachedProvider();
|
|
7981
|
+
if (!diagnosed) {
|
|
7982
|
+
diagnosed = true;
|
|
7983
|
+
console.log(`[serenity-hooks] dsh browser-auth 适配: connection ✓ 可取 + cookie ${cookie === void 0 ? "✗ 换取失败(上游 401 时检查)" : "✓ 已内存换取(注入反代)"}`);
|
|
7984
|
+
}
|
|
7985
|
+
return cookie;
|
|
7986
|
+
} catch {
|
|
7987
|
+
return;
|
|
7988
|
+
}
|
|
7989
|
+
};
|
|
7990
|
+
}
|
|
7844
7991
|
/** 注册 gateway(index.ts apply 调用)。
|
|
7845
7992
|
* 归属原则(v1.22):gateway 是 plugin 全局能力——enabled 开关读 DSH settings
|
|
7846
7993
|
* (readSimpleSettings().gatewayEnabled),host/port/accounts 读 plugin 全局文件
|
|
@@ -7878,7 +8025,7 @@ function registerGateway(ctx) {
|
|
|
7878
8025
|
cookieSecure: settings.gateway.cookieSecure === true,
|
|
7879
8026
|
allowWorkspaceCreate: settings.gateway.allowWorkspaceCreate !== false,
|
|
7880
8027
|
totpEnabled: settings.gateway.totpEnabled === true
|
|
7881
|
-
}, () => readAdvancedSettings().gateway.accounts);
|
|
8028
|
+
}, () => readAdvancedSettings().gateway.accounts, buildDshCookieProvider(ctx, webServer.port));
|
|
7882
8029
|
const accounts = settings.gateway.accounts.length;
|
|
7883
8030
|
const wsNote = settings.gateway.workspaces.length === 0 ? "" : `;工作区白名单 ${settings.gateway.workspaces.length} 条`;
|
|
7884
8031
|
console.log(`[serenity-hooks] gateway 已启动: http://${settings.gateway.host}:${settings.gateway.port} → 127.0.0.1:${webServer.port}` + (accounts === 0 ? "(⚠️ 未配置账号,登录页将提示)" : `(${accounts} 个账号)`) + wsNote);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.28.
|
|
3
|
+
"version": "1.28.2",
|
|
4
4
|
"description": "宁静号 ACC harness — Native Cordis 插件(DeepSeek Harness 运行时)。真实 DSH 工具注册(cc_fs/session/acc_msm 等 9 工具)+ 拦截缝机械约束(safe-mode/路径守卫/会话落盘)+ 系统提示词注入(ACC/CCE/Constraints/SKILL/Session 五块)。适配 DSH 公开版(deepseek-ai/deepseek-harness 0.1.0-rc)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|