fix(cli): isolate Web runtime context from headless

This commit is contained in:
Yichen Jiang
2026-08-06 20:11:44 +08:00
parent 0cf1ba7f87
commit 50c46be948
9 changed files with 46 additions and 12 deletions

View File

@@ -141,9 +141,15 @@ export interface AppCLIEntryOptions {
*/
overlayPath: string
/**
* Optional explicit overlay applied after {@link overlayPath} and before
* Launcher-owned patches applied after {@link overlayPath} and before the
* personal or explicit overlay, so user configuration can still override
* surface activation choices.
*/
launcherPatches?: readonly PatchOptions[]
/**
* Optional explicit overlay applied after {@link launcherPatches} and before
* this entry's own profile/flag patches. When absent, the personal
* `$DSH_HOME/config.yaml` overlay is applied instead.
* `$DSH_HOME/config.yaml` overlay is applied in the same position instead.
*/
extraOverlayPath?: string
/** Whether to append client-bundle HMR (the Web surface's prod/dev difference). */
@@ -261,10 +267,12 @@ export class AppCLIEntry {
private async bootTree(): Promise<void> {
// One include of the shared base with every overlay as a sibling patch
// list: patches never cross an include boundary, so nesting them would
// silently stop reaching base rows. The surface overlay applies first, then
// this entry's profile-json and CLI-flag patches, which therefore win.
// silently stop reaching base rows. The shared surface overlay applies
// first, then launcher activation, user configuration, and finally this
// entry's profile-json and CLI-flag patches.
const compose = (overlay: PatchOptions[]): PatchOptions[] => [
...loadOverlayPatches('dsh', this.options.overlayPath),
...(this.options.launcherPatches ?? []),
...overlay,
...this.patches,
]

View File

@@ -14,6 +14,7 @@ import {
type ConfigDumpLayer,
} from '@deepseek-ai/dsh-app-boot'
import { resolveDshHome } from '@deepseek-ai/dsh-paths'
import { WEB_RUNTIME_CONTEXT_ENABLE_PATCH } from './web.ts'
const NAME = 'dsh'
const BASE_CONFIG = fileURLToPath(new URL('../config/base.cordis.yml', import.meta.url))
@@ -36,6 +37,7 @@ export function runDumpConfig(surface: 'config' | 'web', defaultOnly: boolean, c
}
} else {
layers.push({ label: basename(WEB_OVERLAY), patches: loadOverlayPatches(NAME, WEB_OVERLAY) })
layers.push({ label: 'dsh web launcher', patches: [WEB_RUNTIME_CONTEXT_ENABLE_PATCH] })
if (!defaultOnly) {
if (config === undefined) {
const personal = loadPersonalPatches(NAME)

View File

@@ -8,6 +8,7 @@
import { fileURLToPath } from 'node:url'
import type { Context } from 'cordis'
import type { PatchOptions } from '@cordisjs/plugin-include'
import { addHarnessSourceSection, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
import type {} from '@deepseek-ai/dsh-host-webserver'
import type {} from '@deepseek-ai/dsh-system-prompt'
@@ -24,6 +25,12 @@ const DSH_WEB_URL = 'DSH_WEB_URL' as const
const DSH_WEB_MODE = 'DSH_WEB_MODE' as const
const WEB_RUNTIME_CONTEXT_BUILTIN = 'web-runtime-context' as const
/** Web-launcher activation applied before personal or explicit configuration. */
export const WEB_RUNTIME_CONTEXT_ENABLE_PATCH = {
id: WEB_RUNTIME_CONTEXT_BUILTIN,
disabled: false,
} as const satisfies PatchOptions
type WebMode = 'production' | 'development'
// Display-only mirror of the webserver schema's loopback host: the address the
@@ -125,6 +132,7 @@ export async function runWeb(
const entry = new AppCLIEntry({
configPath: BASE_CONFIG,
overlayPath: WEB_OVERLAY,
launcherPatches: [WEB_RUNTIME_CONTEXT_ENABLE_PATCH],
...config !== undefined && { extraOverlayPath: resolveConfigPath(config, undefined) },
dev,
prepare: (ctx) => { prepareWebRuntimeContext(ctx, SOURCE_ROOT, mode) },