Merge remote-tracking branch 'origin/master' into codex/basic-session-search

This commit is contained in:
Hypatia May
2026-07-27 18:38:18 +08:00
32 changed files with 465 additions and 425 deletions

View File

@@ -17,7 +17,7 @@
// the open llm seam post-boot with installLlmReplay on the settled root ctx
// (the plugin-row path discards the ReplayHandle; the direct install keeps
// assertConsumed for the teardown fixture-consumption check).
import { existsSync, readFileSync } from 'node:fs'
import { existsSync } from 'node:fs'
import { mkdtemp, readFile, readdir, realpath, rm, utimes, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
@@ -69,16 +69,6 @@ const CONFIG_PATH = join(REPO_ROOT, 'apps/cli/cordis.yml')
// contextWindow keeps that pressure path provably inert for small fixtures.
const REPLAY_PROVIDERS = [{ id: 'deepseek', name: 'DeepSeek', models: [{ id: 'deepseek-v4-flash', contextWindow: 128_000 }] }]
/** Repo-root .env → process.env for record mode (never overrides set vars); the smoke-real convention. */
function loadRootEnv(): void {
const envPath = join(REPO_ROOT, '.env')
if (!existsSync(envPath)) return
for (const line of readFileSync(envPath, 'utf8').split('\n')) {
const m = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(line.trim())
if (m !== null && process.env[m[1]!] === undefined) process.env[m[1]!] = m[2]
}
}
/** A booted web scaffold: real composition, mode-selected model backend, temp world. */
export interface WebScaffold {
/** The active snapshot mode this scaffold booted under. */
@@ -142,7 +132,8 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
requireDist()
const mode = webSnapshotMode()
if (mode === 'record') {
loadRootEnv()
// Both owning vitest configs (web unconditionally, snapshot in record
// mode) load the repo-root .env before this file runs.
if (process.env.DEEPSEEK_API_KEY === undefined || process.env.DEEPSEEK_API_KEY.length === 0) {
throw new Error('web e2e record mode needs DEEPSEEK_API_KEY (env or repo-root .env)')
}

View File

@@ -1,9 +1,9 @@
// W5 real-host smoke: spawn `dsh web` with a real key, walk the full W5 flow
// list in a real chromium, screenshot every screen into .artifacts/ for the
// figma comparison pass. Self-skips without DEEPSEEK_API_KEY (repo e2e
// convention); the runner loads the repo-root .env explicitly because the CLI
// only auto-loads .env from its cwd (a temp dir here, so sessions never land
// in the repo's .sessions).
// convention); vitest.web.config.ts loads the repo-root .env before this file
// runs (the CLI only auto-loads .env from its cwd a temp dir here, so
// sessions never land in the repo's .sessions).
//
// Selector convention: CSS Modules hash as [hash]_[local], so class-substring
// selectors are unreliable — anchor on data-* attributes (data-variant /
@@ -26,17 +26,6 @@ import { chromium } from 'playwright'
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
import { REPO_ROOT, connectFreshWorkspace, probeFreePort, requireDist, saveFailureShot } from './support.ts'
/** Repo-root .env → process.env (never overrides an already-set variable). */
function loadRootEnv(): void {
const envPath = join(REPO_ROOT, '.env')
if (!existsSync(envPath)) return
for (const line of readFileSync(envPath, 'utf8').split('\n')) {
const m = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(line.trim())
if (m !== null && process.env[m[1]!] === undefined) process.env[m[1]!] = m[2]
}
}
loadRootEnv()
function waitForReadyLine(child: ChildProcess): Promise<string> {
return new Promise((resolveReady, reject) => {
let out = ''