@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.
@@ -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
- * 命中白名单版本,任一解析失败或版本漂移一律 dormant。
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
- // 版本探针:单行;生成 cordis.patch.yml 官方行 disabled(scripts/write-patch.mjs)。
54
- // 逐锚点解析 `锚点包/package.json`,版本(路径段快路径或 manifest 回退)全部
55
- // 精确等于 GATE_VERSION 才放行。
56
- export const GATE_PROBE_EXPRESSION = `(() => { try { const internal = ctx.loader && ctx.loader.internal; if (!internal || typeof internal.resolveSync !== 'function') return false; 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)}; 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 false; } let anchorVersion; try { anchorVersion = versionOfUrl(url, patternSource); } catch (gateError) { return false; } if (anchorVersion !== '${GATE_VERSION}') return false; } return true; } catch (gateError) { return false; } })()`;
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 every gate anchor resolves to exactly the whitelisted version.
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
- * 复用官方导出的 HostConnectionService(对认证的全部依赖只有
5
- * isAuthenticated / authorizeIndex / authenticatedUrl 三个方法,由
6
- * trusted-auth 的 stub 提供),复刻官方 apply 的 /api 前缀路由、
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 { HostConnectionService, API_PATH } from '@deepseek-ai/dsh-client-connection';
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 { bridge, DEFAULT_MAX_REQUEST_BODY_BYTES } from './bridge.js';
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
- /** Services required before providing the replacement Connection. */
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
- * (gate.ts 的 GATE_ANCHOR_PACKAGES:宿主 runtime 本体 + 官方 connection 包)
41
- * 的 manifest 本体比对白名单(组合期探针只读 pnpm 路径段,会被
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——否则后续 patch 层
53
- * 强制启用或组合回归把 Replacement 误送进 apply 时将无人拦截。
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 ['${GATE_VERSION}']; disable or uninstall the plugin`);
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 !== GATE_VERSION) {
84
- throw new Error(`@iasiv5/dsh-skip-browser-auth: gate anchor ${anchorPackage} manifest is ${String(manifest.version)}, whitelist is ['${GATE_VERSION}']; disable or uninstall the plugin`);
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 browser
103
- * transport prefix. Every request on the prefix passes the Host/Origin
104
- * browser-trust fence; no identity layer exists beyond it.
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
- // 官方未导出 BrowserAuth 类型,用构造器参数位取回;stub 只需满足
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 connection = new HostConnectionService(ctx, trustedHosts, auth);
122
- const fetchHandler = connection.createSharedFetchHandler(API_PATH);
123
- const route = {
124
- kind: 'prefix',
125
- path: API_PATH,
126
- handler: async (req, res) => {
127
- const rejection = connection.requestRejection(req);
128
- if (rejection !== undefined) {
129
- res.writeHead(rejection);
130
- res.end(rejection === 401 ? 'unauthorized' : 'forbidden');
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.1.3",
4
- "description": "DSH 0.1.2-rc.1 专用:自动跳过 BrowserAuth,免每次在启动 URL 后输入随机 token;其它版本(含 0.1.1-rc.2)自动休眠,不修改任何 DSH 本体与机制",
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:all": "npm run test:rc2-fixture && npm run build && npm run typecheck && npm 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",