@iasiv5/dsh-skip-browser-auth 0.1.3 → 0.2.0
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 +72 -25
- package/cordis.patch.yml +13 -8
- package/lib/adapters/carrier-neutral.js +23 -0
- package/lib/adapters/legacy-web.js +6 -0
- package/lib/adapters/types.js +20 -0
- package/lib/bridge.js +48 -23
- package/lib/client.js +6433 -2
- package/lib/compatibility.js +53 -0
- package/lib/connection-runtime.js +52 -0
- package/lib/gate.js +13 -7
- package/lib/host.js +90 -60
- package/package.json +5 -3
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compatibility generations for the DSH BrowserAuth replacement.
|
|
3
|
+
*
|
|
4
|
+
* A profile is the unit of compatibility: a complete runtime/connection
|
|
5
|
+
* version pair plus the host/client implementation generation that knows how
|
|
6
|
+
* to serve it. Do not replace this table with independent version `includes`
|
|
7
|
+
* checks: a runtime from one generation and a connection package from another
|
|
8
|
+
* generation must never activate together.
|
|
9
|
+
*/
|
|
10
|
+
export const COMPATIBILITY_PROFILES = [
|
|
11
|
+
{
|
|
12
|
+
id: 'legacy-web-v1',
|
|
13
|
+
pairs: [{ runtime: '0.1.2-rc.1', connection: '0.1.2-rc.1' }],
|
|
14
|
+
hostAdapter: 'legacy-web',
|
|
15
|
+
clientVariant: 'rc12',
|
|
16
|
+
requiresRecoveryGlobal: false,
|
|
17
|
+
status: 'active',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'carrier-neutral-v2',
|
|
21
|
+
pairs: [{ runtime: '0.1.5-rc.1', connection: '0.1.5-rc.1' }],
|
|
22
|
+
hostAdapter: 'carrier-neutral',
|
|
23
|
+
clientVariant: 'rc15',
|
|
24
|
+
requiresRecoveryGlobal: true,
|
|
25
|
+
// Promoted after the carrier-neutral host/client adapter and fixture path
|
|
26
|
+
// are implemented; real deployment validation remains documented separately.
|
|
27
|
+
status: 'active',
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
/** All exact versions represented by profiles, for diagnostics and fixtures. */
|
|
31
|
+
export const GATE_VERSIONS = [...new Set(COMPATIBILITY_PROFILES.flatMap((profile) => profile.pairs.flatMap((pair) => [pair.runtime, pair.connection])))];
|
|
32
|
+
/** Backward-compatible internal alias for the original 0.1.2 build/test pin. */
|
|
33
|
+
export const GATE_VERSION = GATE_VERSIONS[0];
|
|
34
|
+
/** Profile ids whose host/client implementations are currently safe to activate. */
|
|
35
|
+
export const ACTIVE_COMPATIBILITY_PROFILE_IDS = COMPATIBILITY_PROFILES
|
|
36
|
+
.filter((profile) => profile.status === 'active')
|
|
37
|
+
.map((profile) => profile.id);
|
|
38
|
+
/** Resolve a complete runtime/connection pair, including candidate profiles. */
|
|
39
|
+
export function resolveCompatibilityProfile(runtimeVersion, connectionVersion) {
|
|
40
|
+
if (typeof runtimeVersion !== 'string' || typeof connectionVersion !== 'string')
|
|
41
|
+
return undefined;
|
|
42
|
+
return COMPATIBILITY_PROFILES.find((profile) => profile.pairs.some((pair) => pair.runtime === runtimeVersion && pair.connection === connectionVersion));
|
|
43
|
+
}
|
|
44
|
+
/** Resolve only a profile whose implementation is promoted to active. */
|
|
45
|
+
export function resolveActiveCompatibilityProfile(runtimeVersion, connectionVersion) {
|
|
46
|
+
const profile = resolveCompatibilityProfile(runtimeVersion, connectionVersion);
|
|
47
|
+
return profile?.status === 'active' ? profile : undefined;
|
|
48
|
+
}
|
|
49
|
+
export function isActiveCompatibilityProfileId(value) {
|
|
50
|
+
return typeof value === 'string' && ACTIVE_COMPATIBILITY_PROFILE_IDS.includes(value);
|
|
51
|
+
}
|
|
52
|
+
/** Small JSON-safe table embedded into the Loader `!!js` expression. */
|
|
53
|
+
export const GATE_PROFILE_TABLE = COMPATIBILITY_PROFILES.map(({ id, pairs }) => ({ id, pairs }));
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Runtime connection module loader used by compatibility adapters. */
|
|
2
|
+
import { API_PATH as PINNED_API_PATH, HostConnectionService as PinnedHostConnectionService } from '@deepseek-ai/dsh-client-connection';
|
|
3
|
+
import { CONNECTION_PACKAGE } from './gate.js';
|
|
4
|
+
function isRuntimeConnectionModule(value) {
|
|
5
|
+
if (typeof value !== 'object' || value === null)
|
|
6
|
+
return false;
|
|
7
|
+
const candidate = value;
|
|
8
|
+
return typeof candidate.HostConnectionService === 'function'
|
|
9
|
+
&& typeof candidate.API_PATH === 'string';
|
|
10
|
+
}
|
|
11
|
+
function describeError(error) {
|
|
12
|
+
return error instanceof Error ? error.message : String(error);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the connection module from the active product loader.
|
|
16
|
+
*
|
|
17
|
+
* 产品组合必须消费宿主自己的 connection module 实例:有 loader 服务时
|
|
18
|
+
* internal.import 缺失或调用失败都 fail loud——绝不静默回退到插件自带的
|
|
19
|
+
* pinned dev 副本(那会重演 2026-09-05「探针自我满足」一类代际错配)。
|
|
20
|
+
* pinned 副本只允许出现在完全没有 loader 服务的直接单元测试上下文。
|
|
21
|
+
*/
|
|
22
|
+
export async function loadRuntimeConnectionModule(ctx) {
|
|
23
|
+
const loader = ctx.get('loader');
|
|
24
|
+
if (loader !== undefined) {
|
|
25
|
+
const importer = loader.internal?.import;
|
|
26
|
+
if (typeof importer !== 'function') {
|
|
27
|
+
throw new Error('@iasiv5/dsh-skip-browser-auth: product loader is present but loader.internal.import is unavailable; refusing to fall back to the pinned dev copy');
|
|
28
|
+
}
|
|
29
|
+
const baseUrl = ctx.baseUrl;
|
|
30
|
+
if (typeof baseUrl !== 'string' || baseUrl === '') {
|
|
31
|
+
throw new Error('@iasiv5/dsh-skip-browser-auth: loader.internal.import requires the plugin context baseUrl (profile directory URL); refusing to import without it');
|
|
32
|
+
}
|
|
33
|
+
let loaded;
|
|
34
|
+
try {
|
|
35
|
+
// 必须以 loader.internal 为接收者调用(bound call)。把方法摘下来
|
|
36
|
+
// detached 调用会丢失 this,Node 内部 ESM loader 在 getOrCreateModuleJob
|
|
37
|
+
// 前即抛 "Cannot read properties of undefined"(2026-09-10 崩溃循环根因)。
|
|
38
|
+
loaded = await importer.call(loader.internal, CONNECTION_PACKAGE, baseUrl, {});
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
throw new Error(`@iasiv5/dsh-skip-browser-auth: failed to import runtime module ${CONNECTION_PACKAGE} via loader.internal.import(name, baseUrl, options): ${describeError(error)}`);
|
|
42
|
+
}
|
|
43
|
+
if (!isRuntimeConnectionModule(loaded)) {
|
|
44
|
+
throw new Error(`@iasiv5/dsh-skip-browser-auth: runtime module ${CONNECTION_PACKAGE} has an incompatible export shape`);
|
|
45
|
+
}
|
|
46
|
+
return loaded;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
HostConnectionService: PinnedHostConnectionService,
|
|
50
|
+
API_PATH: PINNED_API_PATH,
|
|
51
|
+
};
|
|
52
|
+
}
|
package/lib/gate.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* 它只可能来自 runtime fallback,不可能被任何 profile 插件的依赖
|
|
18
18
|
* 抬升污染;其版本即宿主 runtime 版本。
|
|
19
19
|
* 2. 第二锚点仍是官方 connection 包(被替换对象本体);双锚点必须同时
|
|
20
|
-
*
|
|
20
|
+
* 命中同一个 active compatibility profile pair,任一解析失败、混合版本或版本漂移一律 dormant。
|
|
21
21
|
* 配套必要条件:本插件的 dependencies 不得声明任何 `@deepseek-ai` 运行时
|
|
22
22
|
* 包(connection 仅作 devDependency 供编译期类型与构建锚点),由
|
|
23
23
|
* tests/manifest.test.mjs 作为不变量看护。
|
|
@@ -33,10 +33,11 @@
|
|
|
33
33
|
* 两分支都拿不到精确版本 → dormant。任何异常一律 dormant(组合期绝不
|
|
34
34
|
* fail loud;最后防线在 apply 内 backstop)。
|
|
35
35
|
*/
|
|
36
|
+
import { ACTIVE_COMPATIBILITY_PROFILE_IDS, GATE_PROFILE_TABLE, } from './compatibility.js';
|
|
37
|
+
export { ACTIVE_COMPATIBILITY_PROFILE_IDS, COMPATIBILITY_PROFILES, GATE_VERSION, GATE_VERSIONS, isActiveCompatibilityProfileId, resolveActiveCompatibilityProfile, resolveCompatibilityProfile, } from './compatibility.js';
|
|
36
38
|
export const CONNECTION_PACKAGE = '@deepseek-ai/dsh-client-connection';
|
|
37
39
|
/** 宿主 runtime 本体包:探针的第一锚点,版本即宿主 dsh 版本。 */
|
|
38
40
|
export const GATE_RUNTIME_PACKAGE = '@deepseek-ai/dsh';
|
|
39
|
-
export const GATE_VERSION = '0.1.2-rc.1';
|
|
40
41
|
/** 探针锚点(顺序即求值顺序):宿主 runtime 本体在前,被替换对象在后。 */
|
|
41
42
|
export const GATE_ANCHOR_PACKAGES = [GATE_RUNTIME_PACKAGE, CONNECTION_PACKAGE];
|
|
42
43
|
/**
|
|
@@ -50,10 +51,15 @@ export function versionSegmentPatternSource(pkg) {
|
|
|
50
51
|
}
|
|
51
52
|
/** 探针内嵌的锚点表:`[包名, 版本段正则源]` 对,全部由上方常量派生。 */
|
|
52
53
|
const GATE_ANCHOR_TABLE = GATE_ANCHOR_PACKAGES.map((pkg) => [pkg, versionSegmentPatternSource(pkg)]);
|
|
53
|
-
//
|
|
54
|
-
// 逐锚点解析 `锚点包/package.json
|
|
55
|
-
//
|
|
56
|
-
|
|
54
|
+
// Profile probe:单行;生成 cordis.patch.yml 的 profile-aware disabled 表达式。
|
|
55
|
+
// 逐锚点解析 `锚点包/package.json`,返回完整 runtime/connection pair 对应的
|
|
56
|
+
// profile id;candidate profile 也可被识别,是否允许激活由下方 boolean probe
|
|
57
|
+
// 根据 profile status 决定。解析失败、未知 pair 或混合 pair 一律返回 null。
|
|
58
|
+
export const GATE_PROFILE_EXPRESSION = `(() => { try { const internal = ctx.loader && ctx.loader.internal; if (!internal || typeof internal.resolveSync !== 'function') return null; const versionOfUrl = (url, patternSource) => { if (typeof url !== 'string') return null; const decoded = decodeURIComponent(url); const segmented = decoded.match(new RegExp(patternSource)); if (segmented !== null) return segmented[1]; if (decoded.slice(0, 5) === 'file:') { const processRef = globalThis.process; if (processRef === null || typeof processRef !== 'object' || typeof processRef.getBuiltinModule !== 'function') return null; const fsModule = processRef.getBuiltinModule('node:fs'); const urlModule = processRef.getBuiltinModule('node:url'); if (!fsModule || typeof fsModule.readFileSync !== 'function' || !urlModule || typeof urlModule.fileURLToPath !== 'function') return null; const manifest = JSON.parse(fsModule.readFileSync(urlModule.fileURLToPath(url), 'utf8')); return manifest !== null && typeof manifest === 'object' && typeof manifest.version === 'string' ? manifest.version : null; } return null; }; const anchors = ${JSON.stringify(GATE_ANCHOR_TABLE)}; const versions = []; for (const [anchorPackage, patternSource] of anchors) { const specifier = anchorPackage + '/package.json'; let url; try { url = internal.version === 'v2' ? internal.resolveSync(ctx.baseUrl, { specifier, attributes: {} }).url : internal.resolveSync(specifier, ctx.baseUrl, {}).url; } catch (gateError) { return null; } let anchorVersion; try { anchorVersion = versionOfUrl(url, patternSource); } catch (gateError) { return null; } if (anchorVersion === null) return null; versions.push(anchorVersion); } const profiles = ${JSON.stringify(GATE_PROFILE_TABLE)}; for (const profile of profiles) { if (profile.pairs.some((pair) => pair.runtime === versions[0] && pair.connection === versions[1])) return profile.id; } return null; } catch (gateError) { return null; } })()`;
|
|
59
|
+
// Boolean activation probe: only active profiles disable the official row.
|
|
60
|
+
// A known candidate profile remains dormant until its host/client adapter is
|
|
61
|
+
// promoted, while the profile expression still exposes its exact pair id.
|
|
62
|
+
export const GATE_PROBE_EXPRESSION = `(() => { try { const profile = (${GATE_PROFILE_EXPRESSION}); return profile !== null && ${JSON.stringify(ACTIVE_COMPATIBILITY_PROFILE_IDS)}.includes(profile); } catch (gateError) { return false; } })()`;
|
|
57
63
|
// 行绑定放行:自身探针为真 + 同组 sibling 存在且名字匹配且其 effective disabled 为真。
|
|
58
64
|
// sibling 必须从当前 entry(Symbol.for('cordis.entry'))所属子树解析,
|
|
59
65
|
// 根 Loader 的 resolve 看不到 Include 子树内的行。
|
|
@@ -61,7 +67,7 @@ export const GATE_ROW_ALLOWED_EXPRESSION = `(() => { try { const self = ctx[Symb
|
|
|
61
67
|
/**
|
|
62
68
|
* Evaluate the activation probe against a loader context scope.
|
|
63
69
|
* @param ctx - the `!!js` evaluation scope (same object the Loader passes).
|
|
64
|
-
* @returns true only when
|
|
70
|
+
* @returns true only when both gate anchors resolve to one active compatibility profile pair.
|
|
65
71
|
*/
|
|
66
72
|
export function evaluateGate(ctx) {
|
|
67
73
|
// 与 vendor/loader/src/config/utils.ts 的 evaluate 同语义:with(ctx) + eval。
|
package/lib/host.js
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @iasiv5/dsh-skip-browser-auth host half:trusted-network Connection Replacement。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 请求体上限与 image capacity 断言语义,并在启动时输出固定警告。
|
|
8
|
-
* apply 内 backstop 是最后防线:版本白名单(读全部门控锚点的 manifest,
|
|
9
|
-
* 与探针同源常量)与门控绑定状态(同组官方行确被禁用且无活跃 fiber)
|
|
10
|
-
* 任一不满足即 fail loud 拒绝运行。
|
|
4
|
+
* 兼容性按 profile 分代:0.1.2 使用 legacy-web adapter,0.1.5 使用
|
|
5
|
+
* carrier-neutral adapter。公共 apply 只负责 profile/backstop、信任配置、
|
|
6
|
+
* 生命周期和固定 warning;版本差异藏在 adapter 与 runtime connection loader。
|
|
11
7
|
*/
|
|
12
8
|
import { readFileSync } from 'node:fs';
|
|
13
9
|
import { fileURLToPath } from 'node:url';
|
|
14
10
|
import z from '@deepseek-ai/schemastery';
|
|
15
|
-
import {
|
|
11
|
+
import { CONNECTION_PACKAGE, GATE_ANCHOR_PACKAGES, GATE_VERSIONS, isActiveCompatibilityProfileId, resolveActiveCompatibilityProfile, } from './gate.js';
|
|
12
|
+
import { COMPATIBILITY_PROFILES } from './compatibility.js';
|
|
13
|
+
import { loadRuntimeConnectionModule } from './connection-runtime.js';
|
|
14
|
+
import { createLegacyWebRuntime } from './adapters/legacy-web.js';
|
|
15
|
+
import { createCarrierNeutralRuntime } from './adapters/carrier-neutral.js';
|
|
16
16
|
import { assertTrustedAuthority } from './trust-fence.js';
|
|
17
17
|
import { createTrustedAuth } from './trusted-auth.js';
|
|
18
|
-
import {
|
|
19
|
-
import { CONNECTION_PACKAGE, GATE_ANCHOR_PACKAGES, GATE_VERSION } from './gate.js';
|
|
18
|
+
import { DEFAULT_MAX_REQUEST_BODY_BYTES } from './bridge.js';
|
|
20
19
|
/** Stable Cordis plugin name. */
|
|
21
20
|
export const name = '@iasiv5/dsh-skip-browser-auth';
|
|
22
|
-
/**
|
|
21
|
+
/** The replacement owns a web carrier so dedicated RPC channels have a route owner. */
|
|
23
22
|
export const inject = ['webServer'];
|
|
24
23
|
/** 启动警告固定文案(全局约束:Replacement 激活时逐字输出)。 */
|
|
25
24
|
const SKIP_WARNING = '@iasiv5/dsh-skip-browser-auth: BrowserAuth has been skipped. DSH Web is using the trusted-network behavior; this plugin does not verify any upstream proxy.';
|
|
26
25
|
/** Headroom for RPC JSON fields around aggregate base64 image payloads. */
|
|
27
26
|
const REQUEST_ENVELOPE_HEADROOM_BYTES = 1024 * 1024;
|
|
27
|
+
const PROFILE_GLOBAL = '__DSH_SKIP_BROWSER_AUTH_PROFILE__';
|
|
28
|
+
const RECOVERY_GLOBAL = '__DSH_CONNECTION_RECOVERY__';
|
|
29
|
+
const WHITELIST_TEXT = `[${GATE_VERSIONS.map((version) => `'${version}'`).join(', ')}]`;
|
|
28
30
|
function assertImageBodyCapacity(ctx, maxRequestBodyBytes) {
|
|
29
31
|
const attachments = ctx.get('attachments');
|
|
30
32
|
if (attachments === undefined)
|
|
@@ -35,28 +37,38 @@ function assertImageBodyCapacity(ctx, maxRequestBodyBytes) {
|
|
|
35
37
|
+ `${String(requiredImageBodyBytes)} for the configured aggregate image limit`);
|
|
36
38
|
}
|
|
37
39
|
}
|
|
40
|
+
function profileById(value) {
|
|
41
|
+
if (!isActiveCompatibilityProfileId(value))
|
|
42
|
+
return undefined;
|
|
43
|
+
return COMPATIBILITY_PROFILES.find((profile) => profile.id === value && profile.status === 'active');
|
|
44
|
+
}
|
|
45
|
+
function directProfile(requestedProfile) {
|
|
46
|
+
if (requestedProfile !== undefined) {
|
|
47
|
+
const requested = profileById(requestedProfile);
|
|
48
|
+
if (requested !== undefined)
|
|
49
|
+
return requested;
|
|
50
|
+
throw new Error(`@iasiv5/dsh-skip-browser-auth: compatibility profile ${String(requestedProfile)} is not active; refusing to run`);
|
|
51
|
+
}
|
|
52
|
+
const legacy = profileById('legacy-web-v1');
|
|
53
|
+
if (legacy === undefined) {
|
|
54
|
+
throw new Error('@iasiv5/dsh-skip-browser-auth: no active compatibility profile is available for direct context');
|
|
55
|
+
}
|
|
56
|
+
return legacy;
|
|
57
|
+
}
|
|
38
58
|
/**
|
|
39
|
-
* apply 内 backstop(最后防线,fail loud
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* 「路径真、manifest 假」的漂移骗过);绑定断言确认当前 entry 子树内的
|
|
43
|
-
* 官方 connection 行确实被本插件禁用、同组同名且无活跃 fiber。
|
|
59
|
+
* apply 内 backstop(最后防线,fail loud):版本 pair 读取全部门控锚点的
|
|
60
|
+
* manifest,并解析到 active compatibility profile;绑定断言确认当前 entry
|
|
61
|
+
* 子树内的官方 connection 行确实被本插件禁用、同组同名且无活跃 fiber。
|
|
44
62
|
*
|
|
45
|
-
* 锚点解析与探针同基准(ctx.baseUrl,即 profile 根):发布安装的插件不携带
|
|
46
|
-
* 任何 @deepseek-ai 运行时依赖(见 tests/manifest.test.mjs 不变量),解析只
|
|
47
|
-
* 可能命中 runtime fallback;探针基准污染的历史事故由此不变量 + 双锚点共同
|
|
48
|
-
* 封死。
|
|
49
|
-
*
|
|
50
|
-
* 跳过 seam 仅限「完全没有 Loader、也没有 Loader entry」的直接单元测试调用;
|
|
51
63
|
* 产品组合中 loader / internal / current 任一缺失都意味着最后防线无法执行,
|
|
52
|
-
* 必须在创建 Connection 服务与注册 /api 之前 fail loud
|
|
53
|
-
*
|
|
64
|
+
* 必须在创建 Connection 服务与注册 /api 之前 fail loud。没有 loader 与 entry
|
|
65
|
+
* 的直接单元测试上下文才跳过本 backstop。
|
|
54
66
|
*/
|
|
55
|
-
function assertGateBackstop(ctx) {
|
|
67
|
+
function assertGateBackstop(ctx, requestedProfile) {
|
|
56
68
|
const loader = ctx.get('loader');
|
|
57
69
|
const current = ctx.fiber?.entry;
|
|
58
70
|
if (loader === undefined && current === undefined)
|
|
59
|
-
return;
|
|
71
|
+
return undefined;
|
|
60
72
|
if (loader === undefined) {
|
|
61
73
|
throw new Error('@iasiv5/dsh-skip-browser-auth: gate backstop unavailable: loader service is missing');
|
|
62
74
|
}
|
|
@@ -66,10 +78,9 @@ function assertGateBackstop(ctx) {
|
|
|
66
78
|
if (current === undefined) {
|
|
67
79
|
throw new Error('@iasiv5/dsh-skip-browser-auth: gate backstop unavailable: current loader entry is missing');
|
|
68
80
|
}
|
|
69
|
-
// (a) 版本断言:与探针同一解析锚点集合(loader.internal + ctx.baseUrl),
|
|
70
|
-
// 逐锚点读 manifest 比对白名单。
|
|
71
81
|
const baseUrl = ctx.baseUrl ?? '';
|
|
72
82
|
const internal = loader.internal;
|
|
83
|
+
const versions = [];
|
|
73
84
|
for (const anchorPackage of GATE_ANCHOR_PACKAGES) {
|
|
74
85
|
const specifier = `${anchorPackage}/package.json`;
|
|
75
86
|
const resolved = internal.version === 'v2'
|
|
@@ -77,14 +88,21 @@ function assertGateBackstop(ctx) {
|
|
|
77
88
|
: internal.resolveSync(specifier, baseUrl, {});
|
|
78
89
|
const manifestUrl = resolved?.url;
|
|
79
90
|
if (typeof manifestUrl !== 'string') {
|
|
80
|
-
throw new Error(`@iasiv5/dsh-skip-browser-auth: gate anchor ${anchorPackage} did not resolve; whitelist is
|
|
91
|
+
throw new Error(`@iasiv5/dsh-skip-browser-auth: gate anchor ${anchorPackage} did not resolve; whitelist is ${WHITELIST_TEXT}; disable or uninstall the plugin`);
|
|
81
92
|
}
|
|
82
93
|
const manifest = JSON.parse(readFileSync(fileURLToPath(manifestUrl), 'utf8'));
|
|
83
|
-
if (manifest.version !==
|
|
84
|
-
throw new Error(`@iasiv5/dsh-skip-browser-auth: gate anchor ${anchorPackage} manifest
|
|
94
|
+
if (typeof manifest.version !== 'string') {
|
|
95
|
+
throw new Error(`@iasiv5/dsh-skip-browser-auth: gate anchor ${anchorPackage} manifest has no version, whitelist is ${WHITELIST_TEXT}; disable or uninstall the plugin`);
|
|
85
96
|
}
|
|
97
|
+
versions.push(manifest.version);
|
|
98
|
+
}
|
|
99
|
+
const profile = resolveActiveCompatibilityProfile(versions[0], versions[1]);
|
|
100
|
+
if (profile === undefined) {
|
|
101
|
+
throw new Error(`@iasiv5/dsh-skip-browser-auth: gate anchors ${GATE_ANCHOR_PACKAGES[0]}=${String(versions[0])}, ${GATE_ANCHOR_PACKAGES[1]}=${String(versions[1])} do not resolve to one active compatibility profile; whitelist is ${WHITELIST_TEXT}; disable or uninstall the plugin`);
|
|
102
|
+
}
|
|
103
|
+
if (requestedProfile !== undefined && requestedProfile !== profile.id) {
|
|
104
|
+
throw new Error(`@iasiv5/dsh-skip-browser-auth: compatibility profile mismatch: patch selected ${String(requestedProfile)}, anchors selected ${profile.id}; refusing to run`);
|
|
86
105
|
}
|
|
87
|
-
// (b) 绑定断言:官方行被禁用是 Replacement 放行的前提,且不得有活跃 fiber。
|
|
88
106
|
const official = current.parent.tree.resolve('connection');
|
|
89
107
|
if (official.parent !== current.parent
|
|
90
108
|
|| official.options?.id !== 'connection'
|
|
@@ -93,50 +111,62 @@ function assertGateBackstop(ctx) {
|
|
|
93
111
|
|| official.fiber !== undefined) {
|
|
94
112
|
throw new Error('@iasiv5/dsh-skip-browser-auth: gate binding violated; refusing to run');
|
|
95
113
|
}
|
|
114
|
+
return profile;
|
|
115
|
+
}
|
|
116
|
+
function installBrowserGlobals(ctx, profile) {
|
|
117
|
+
ctx.inject(['webServer'], (webCtx) => {
|
|
118
|
+
const carrier = webCtx;
|
|
119
|
+
if (typeof carrier.on !== 'function')
|
|
120
|
+
return;
|
|
121
|
+
carrier.on('webserver/index-inject', (table) => {
|
|
122
|
+
table.push({ kind: 'global', name: PROFILE_GLOBAL, value: profile.id });
|
|
123
|
+
if (profile.requiresRecoveryGlobal) {
|
|
124
|
+
table.push({ kind: 'global', name: RECOVERY_GLOBAL, value: {} });
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
});
|
|
96
128
|
}
|
|
97
129
|
export const Config = z.object({
|
|
98
130
|
trustedHosts: z.array(String).default([]),
|
|
99
131
|
maxRequestBodyBytes: z.natural().min(1).default(DEFAULT_MAX_REQUEST_BODY_BYTES),
|
|
132
|
+
compatibilityProfile: z.string().default('legacy-web-v1'),
|
|
100
133
|
});
|
|
134
|
+
function createHostRuntime(profile, input) {
|
|
135
|
+
switch (profile.hostAdapter) {
|
|
136
|
+
case 'legacy-web':
|
|
137
|
+
return createLegacyWebRuntime(input);
|
|
138
|
+
case 'carrier-neutral':
|
|
139
|
+
return createCarrierNeutralRuntime(input);
|
|
140
|
+
default:
|
|
141
|
+
throw new Error('@iasiv5/dsh-skip-browser-auth: unsupported host adapter');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
101
144
|
/**
|
|
102
|
-
* Mounts the trusted-network Connection replacement under the
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* @param ctx - Host plugin context.
|
|
106
|
-
* @param config - resolved plugin config (schema defaults applied).
|
|
145
|
+
* Mounts the trusted-network Connection replacement under the profile's Web
|
|
146
|
+
* carrier. Every request passes the Host/Origin browser-trust fence; no
|
|
147
|
+
* identity layer exists beyond it.
|
|
107
148
|
*/
|
|
108
149
|
export async function apply(ctx, config) {
|
|
109
|
-
// The Loader resolves schema defaults; hand-built test contexts may pass none.
|
|
110
150
|
const trustedHosts = config?.trustedHosts ?? [];
|
|
111
151
|
const maxRequestBodyBytes = config?.maxRequestBodyBytes ?? DEFAULT_MAX_REQUEST_BODY_BYTES;
|
|
112
|
-
// Config boundary: a malformed entry fails the load loudly here rather than
|
|
113
|
-
// silently authorizing its hostname prefix at request time.
|
|
114
152
|
for (const entry of trustedHosts)
|
|
115
153
|
assertTrustedAuthority(entry);
|
|
116
154
|
assertImageBodyCapacity(ctx, maxRequestBodyBytes);
|
|
117
|
-
assertGateBackstop(ctx);
|
|
118
|
-
|
|
119
|
-
// HostConnectionService 实际消费的三个方法。
|
|
155
|
+
const profile = assertGateBackstop(ctx, config?.compatibilityProfile) ?? directProfile(config?.compatibilityProfile);
|
|
156
|
+
const module = await loadRuntimeConnectionModule(ctx);
|
|
120
157
|
const auth = createTrustedAuth(trustedHosts);
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
await bridge(req, res, fetchHandler, maxRequestBodyBytes);
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
ctx.effect(() => ctx.webServer.register(route), '@iasiv5/dsh-skip-browser-auth: /api route');
|
|
158
|
+
const runtime = createHostRuntime(profile, {
|
|
159
|
+
ctx,
|
|
160
|
+
module,
|
|
161
|
+
trustedHosts,
|
|
162
|
+
auth,
|
|
163
|
+
maxRequestBodyBytes,
|
|
164
|
+
profileId: profile.id,
|
|
165
|
+
});
|
|
166
|
+
ctx.effect(() => ctx.webServer.register(runtime.route), `@iasiv5/dsh-skip-browser-auth: ${runtime.adapterId} ${runtime.route.path} route`);
|
|
167
|
+
installBrowserGlobals(ctx, profile);
|
|
137
168
|
ctx.inject(['attachments'], (attachmentCtx) => {
|
|
138
169
|
assertImageBodyCapacity(attachmentCtx, maxRequestBodyBytes);
|
|
139
170
|
});
|
|
140
|
-
// 固定文案启动警告(全局约束):Replacement 已激活、无上游代理校验。
|
|
141
171
|
console.warn(SKIP_WARNING);
|
|
142
172
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iasiv5/dsh-skip-browser-auth",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "DSH 0.1.2-rc.1
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "DSH 0.1.2-rc.1 与 0.1.5-rc.1 的分代兼容插件:按精确 compatibility profile 自动跳过 BrowserAuth;其它版本自动休眠,不修改任何 DSH 本体与机制",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
7
7
|
"deepseek-harness",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
41
41
|
"test": "node --test tests/*.test.mjs tests/composition/*.test.mjs",
|
|
42
42
|
"test:rc2-fixture": "node scripts/install-rc2-fixture.mjs",
|
|
43
|
-
"test:
|
|
43
|
+
"test:rc15-fixture": "node scripts/install-rc15-fixture.mjs",
|
|
44
|
+
"test:all": "npm run test:rc2-fixture && npm run test:rc15-fixture && npm run build && npm run typecheck && npm test",
|
|
44
45
|
"prepare": "npm run build"
|
|
45
46
|
},
|
|
46
47
|
"dsh": {
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
"@deepseek-ai/cordis-plugin-include": "1.0.7",
|
|
63
64
|
"@deepseek-ai/cordis-plugin-loader": "1.0.3",
|
|
64
65
|
"@deepseek-ai/dsh-client-connection": "0.1.2-rc.1",
|
|
66
|
+
"dsh-client-connection-rc15": "npm:@deepseek-ai/dsh-client-connection@0.1.5-rc.1",
|
|
65
67
|
"@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
|
|
66
68
|
"@deepseek-ai/dsh-credentials-local": "0.1.2-rc.1",
|
|
67
69
|
"@deepseek-ai/dsh-host-frontend-static": "0.1.2-rc.1",
|