Files
deepseek-harness/packages/client/web/src/boot-client.ts
T
winewill cafb9b93b4 fix: address adversarial review of the fatal-diagnostics branch
client-modules: a batch that already executed is never requested again,
even when its first importer was a row it did register — every executed
batch URL is remembered and a still-missing row goes straight to its
one-resource URL. The in-process module system test in test-support is
updated to the aggregated arrival message. describeError replaces three
inline non-Error ternaries and is covered.

subprocess-local: the spill path is published only after the exclusive
open succeeds, so a failed open (EEXIST on a planted entry included) can no
longer unlink a path this process did not create; a reporter that throws is
contained and written to stderr; logSpillFailure builds the owner reporter
for both the runtime and the SSH helper so the logging path is exercised
once instead of duplicated. Tests cover the runtime logger, the bare-caller
stderr default, EEXIST, and a throwing reporter.

desktop: a fatal failure during shutdown still writes its report; the
report phase follows the backend state so a failure during a restart is
'startup' again; writeReport is a required recovery operation and the
dialog tests await the write; the shell pid is labelled; the withheld
response-header list states why set-cookie is dropped.

app-boot, READMEs, Agent Note: "never resumes" is restated as control never
returning to the failed operation while the loop runs until release
settles; inspect output is bounded; incident inferences (who removed the
spill directory, which batch failure occurred) are written as inferences.
2026-09-22 14:41:10 +08:00

90 lines
4.0 KiB
TypeScript

/**
* Production client composition without the page: mount the Loader over a
* module system, create every manifest row, wait for quiescence, and audit
* activation. `AppWebEntry` and the whole-client test carrier both call it.
* @module @deepseek-ai/dsh-client-web/src/boot-client
*/
import type { Context } from '@deepseek-ai/cordis'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import type { BootManifest, ClientModuleLoader } from '@deepseek-ai/dsh-client-modules/client'
import { STATE_LABELS } from './loader-status.ts'
/** Entry state label as the boot page renders it. */
export type EntryStateLabel = (typeof STATE_LABELS)[keyof typeof STATE_LABELS] | 'loading' | 'failed'
/** Inputs of {@link bootClient}. */
export interface ClientBootOptions {
/** Fresh root Context that will own the plugin tree. */
readonly ctx: Context
/** Module system installed as `loader.internal`. */
readonly modules: ClientModuleLoader
/** Parsed manifest whose `plugins` rows become Loader entries (entry name = row id). */
readonly manifest: BootManifest
/** Per-entry state reporting (the boot page); omitted when no one renders progress. */
readonly onEntryState?: (name: string, state: EntryStateLabel) => void
}
/**
* Compose the client: `ctx.plugin(Loader)`, `loader.internal = modules`, one
* `loader.create({ name })` per manifest row, `loader.await()`, then
* {@link assertEntriesActive}. A row whose module cannot be imported is marked
* failed; the Loader logs its import error, the module system records it, and
* the audit rejects startup with that error text per entry.
* @param options - context, module system, manifest, optional progress sink.
* @returns resolves after every entry is active; rejects with the audit report otherwise.
*/
export async function bootClient(options: ClientBootOptions): Promise<void> {
const { ctx, manifest, onEntryState } = options
await ctx.plugin(Loader)
const loader = ctx.loader
loader.internal = options.modules as never
ctx.on('internal/status', (fiber) => {
const entry = fiber.entry
if (entry === undefined || entry.fiber === undefined) return
onEntryState?.(entry.options.name, STATE_LABELS[entry.fiber.state])
})
const rows = manifest.plugins.map(row => row.id)
for (const name of rows) onEntryState?.(name, 'loading')
await options.modules.entries.start(loader, manifest)
for (const entry of loader.entries()) {
if (entry.fiber === undefined) onEntryState?.(entry.options.name, 'failed')
}
await loader.await()
assertEntriesActive(ctx, options.modules)
}
/**
* Reject entries that failed import/apply or still wait on missing services.
* @param ctx - root Context carrying the Loader.
* @param modules - the module system whose recorded import failures name why an entry
* has no fiber; a row with no record points at the console.
* @throws {Error} listing every non-active entry with its reason.
*/
export function assertEntriesActive(ctx: Context, modules: Pick<ClientModuleLoader, 'importError'>): void {
const failures: string[] = []
for (const entry of ctx.loader.entries()) {
const name = entry.options.name
if (entry.fiber === undefined) {
const importError = modules.importError(name)
failures.push(importError === undefined
? `${name}: import failed (see console for the import error)`
: `${name}: import failed: ${importError.message}`)
continue
}
const state = STATE_LABELS[entry.fiber.state]
if (state === 'active') continue
if (state === 'pending') {
const missing = Object.keys(entry.fiber.inject).filter(service => ctx.get(service) === undefined)
failures.push(`${name}: pending (waiting for service${missing.length === 1 ? '' : 's'}: ${missing.join(', ') || 'unknown'})`)
} else {
failures.push(`${name}: ${state}`)
}
}
if (failures.length > 0) {
throw new Error(`web boot: ${String(failures.length)} entr${failures.length === 1 ? 'y' : 'ies'} did not activate\n${failures.join('\n')}`)
}
}