diff --git a/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.i18n.yaml b/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.i18n.yaml index 90f4d0e145..f922fdaa95 100644 --- a/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.i18n.yaml +++ b/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.md -2026-08-10-source-run-without-managed-installer.md: 35f0c705731147ce8bb64c2e985789aff341996c -2026-08-10-source-run-without-managed-installer.zh.md: 51b336f4c399ebfd24f499288eee477bed9f23cb +2026-08-10-source-run-without-managed-installer.md: ecc4530b90a495f210476f100e894a7ce77cdb63 +2026-08-10-source-run-without-managed-installer.zh.md: 940bc6245bacc91abea4e2c746d81dbce4a50105 diff --git a/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.md b/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.md index 35f0c70573..ecc4530b90 100644 --- a/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.md +++ b/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.md @@ -12,7 +12,7 @@ That lifecycle is not required to run or develop DeepSeek Harness from a source ## Decision -The repository supports source execution through its root `pnpm` scripts. The `pnpm dsh ` script runs the complete repository build before launching the source CLI; users select Web with `pnpm dsh web` and headless execution with `pnpm dsh run`. The independent ACP example remains available through `pnpm run demo:acp`. +The repository supports source execution through its root `pnpm` scripts. The `pnpm dsh ` launcher runs the complete repository build before launching the source CLI. It discards successful build output so CLI stdout remains machine-readable, reports failed build diagnostics on stderr, and sets `NODE_USE_ENV_PROXY=1` for the CLI process. Users select Web with `pnpm dsh web` and headless execution with `pnpm dsh run`. The independent ACP example remains available through `pnpm run demo:acp`. The repository does not distribute a source installer, an installer test suite, or skills that assume a managed `current` symlink and timestamped staging worktrees. Users own source checkout placement, Git updates, and any launcher they create outside the repository. @@ -28,4 +28,4 @@ The repository does not distribute a source installer, an installer test suite, Source users invoke repository scripts rather than an installed `dsh` command. The repository provides no atomic upgrade cutover or preserved staging rollback checkout, and it does not automate the integration or upstream publication of personal source modifications. A future distribution mechanism must justify its ownership of installation and upgrade state, define recovery behavior, and add tests and user documentation without making the source-run path depend on it. Any future publication workflow must isolate one approved feature and obtain explicit approval before its first push and draft PR. -Verification covers repository-wide references to the removed entry points, documentation links, generated third-party-notice freshness, and a clean-artifact source CLI smoke through `pnpm dsh`. +Verification covers repository-wide references to the removed entry points, documentation links, generated third-party-notice freshness, and source CLI smokes that verify the build-first launch and absence of build logs on stdout through `pnpm dsh`. diff --git a/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.zh.md b/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.zh.md index 51b336f4c3..940bc6245b 100644 --- a/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.zh.md +++ b/.agents/notes/implemented/simplification/2026-08-10-source-run-without-managed-installer.zh.md @@ -12,7 +12,7 @@ Status: implemented ## 决策 -仓库通过根目录的 `pnpm` 脚本支持从源码运行。`pnpm dsh ` 脚本会先完成整个仓库的构建,再启动源码 CLI(命令行界面);用户使用 `pnpm dsh web` 选择 Web,使用 `pnpm dsh run` 选择无头执行。独立的 ACP(Agent Client Protocol)示例仍可通过 `pnpm run demo:acp` 运行。 +仓库通过根目录的 `pnpm` 脚本支持从源码运行。`pnpm dsh ` 启动器会先完成整个仓库的构建,再启动源码 CLI(命令行界面)。它会丢弃成功构建的输出,使 CLI stdout 保持机器可读;在 stderr 中报告构建失败的诊断信息;并为 CLI 进程设置 `NODE_USE_ENV_PROXY=1`。用户使用 `pnpm dsh web` 选择 Web,使用 `pnpm dsh run` 选择无头执行。独立的 ACP(Agent Client Protocol)示例仍可通过 `pnpm run demo:acp` 运行。 仓库不分发源码安装器、安装器测试套件,也不分发依赖受管理的 `current` 符号链接和带时间戳 staging worktree 的 skill。源码检出的存放位置、Git 更新,以及用户在仓库外创建的任何启动器均由用户负责。 @@ -28,4 +28,4 @@ Status: implemented 源码用户通过仓库脚本运行程序,而非使用已安装的 `dsh` 命令。仓库不提供原子升级切换,也不保留 staging 回滚检出;仓库同样不会自动集成个人源码修改或将其发布到上游。未来的分发机制必须说明为何应由其管理安装和升级状态,定义恢复行为,并补充测试与用户文档,同时不得让源码运行路径依赖该机制。未来任何发布工作流都必须隔离出一项获批功能,并在首次推送和创建草稿 PR(Pull Request)前取得明确批准。 -验证范围包括仓库内对已移除入口点的所有引用、文档链接、生成的第三方声明文件的新鲜度,以及在无构建产物状态下通过 `pnpm dsh` 启动源码 CLI 的冒烟测试。 +验证范围包括仓库内对已移除入口点的所有引用、文档链接、生成的第三方声明文件的新鲜度,以及通过 `pnpm dsh` 对源码 CLI 进行冒烟测试,验证先构建后启动且 stdout 不含构建日志。 diff --git a/apps/cli/tests/source-launch.compat.spec.ts b/apps/cli/tests/source-launch.compat.spec.ts index 5d92d021f0..68b4f73158 100644 --- a/apps/cli/tests/source-launch.compat.spec.ts +++ b/apps/cli/tests/source-launch.compat.spec.ts @@ -16,6 +16,22 @@ const repoRoot = fileURLToPath(new URL('../../../', import.meta.url)) const dshSourceBin = 'apps/cli/src/bin.ts' describe('dsh SOURCE launcher (node --import tsx/esm)', () => { + it('builds without mixing build logs into CLI stdout', async () => { + const result = await execa('pnpm', ['dsh', '--help'], { + cwd: repoRoot, + timeout: 120_000, + killSignal: 'SIGKILL', + reject: false, + }) + if (result.timedOut) { + throw new Error(`pnpm dsh --help did not exit within 120s. stdout:\n${result.stdout}\nstderr:\n${result.stderr}`) + } + expect(result.exitCode).toBe(0) + expect(result.stdout).toMatch(/^Usage: dsh /) + expect(result.stdout).not.toContain('tsdown') + expect(result.stdout).not.toContain('build:lib') + }, 125_000) + it('boots the source entry and requires a profile', async () => { const result = await execa(process.execPath, ['--import', 'tsx/esm', dshSourceBin], { cwd: repoRoot, diff --git a/docs/user/guide/quickstart.i18n.yaml b/docs/user/guide/quickstart.i18n.yaml index 83eea0ed76..f6c6096f40 100644 --- a/docs/user/guide/quickstart.i18n.yaml +++ b/docs/user/guide/quickstart.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/guide/quickstart.md -quickstart.md: 540eed86bc83e70f3a9724f601fd71170996ce0b -quickstart.zh.md: ab529333eb3c62add8cf422328b0ba838efc82e6 +quickstart.md: f6537c71fc94096b2daf49827d041979346632bb +quickstart.zh.md: fbecabb964e3786929b3b942f49b5b44a4437e05 diff --git a/docs/user/guide/quickstart.md b/docs/user/guide/quickstart.md index 540eed86bc..f6537c71fc 100644 --- a/docs/user/guide/quickstart.md +++ b/docs/user/guide/quickstart.md @@ -19,10 +19,9 @@ pnpm -v ## Step 1: install and configure the API key ```sh -git clone https://github.com/deepseek-ai/deepseek-harness-sdk.git +git clone https://github.com/deepseek-ai/deepseek-harness-sdk.git deepseek-harness cd deepseek-harness pnpm install -pnpm run build ``` Create the gitignored repository-root `.env`: diff --git a/docs/user/guide/quickstart.zh.md b/docs/user/guide/quickstart.zh.md index ab529333eb..fbecabb964 100644 --- a/docs/user/guide/quickstart.zh.md +++ b/docs/user/guide/quickstart.zh.md @@ -19,10 +19,9 @@ pnpm -v ## 第一步:安装并配置 API 密钥 ```sh -git clone https://github.com/deepseek-ai/deepseek-harness-sdk.git +git clone https://github.com/deepseek-ai/deepseek-harness-sdk.git deepseek-harness cd deepseek-harness pnpm install -pnpm run build ``` 在仓库根目录创建已被 Git 忽略的 `.env`: diff --git a/package.json b/package.json index 4acd724112..d34d3322b2 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "release:pack": "tsx scripts/release/pack.ts", "release:verify-packed-install": "tsx scripts/release/verify-packed-install.ts", "release:publish": "tsx scripts/release/publish.ts", - "dsh": "npm run build && node --import tsx/esm apps/cli/src/bin.ts", + "dsh": "tsx scripts/run-source-dsh.ts", "demo:code-mode": "node scripts/demo-code-mode.mjs", "demo:cordis": "node scripts/demo-cordis.mjs", "demo:acp": "node --import tsx packages/examples/acp-demo/src/bin.ts --config examples/acp-agent/cordis.yml", diff --git a/scripts/run-source-dsh.ts b/scripts/run-source-dsh.ts new file mode 100644 index 0000000000..3d97a44faa --- /dev/null +++ b/scripts/run-source-dsh.ts @@ -0,0 +1,48 @@ +/** + * Source-checkout launcher. Successful build output stays out of CLI stdout; + * build failures report their captured diagnostics on stderr before exiting. + */ +import process from 'node:process' +import { fileURLToPath } from 'node:url' + +import { execa } from 'execa' + +const repoRoot = fileURLToPath(new URL('..', import.meta.url)) +const sourceBin = fileURLToPath(new URL('../apps/cli/src/bin.ts', import.meta.url)) + +function completeFrom(result: { readonly exitCode?: number; readonly signal?: string }): void { + if (result.signal !== undefined) { + process.kill(process.pid, result.signal) + return + } + process.exitCode = result.exitCode ?? 1 +} + +function reportBuildFailure(result: { + readonly all: string | undefined + readonly shortMessage: string | undefined +}): void { + const diagnostic = result.all === undefined || result.all.length === 0 + ? result.shortMessage ?? 'Source build failed without diagnostics.' + : result.all + process.stderr.write(diagnostic.endsWith('\n') ? diagnostic : `${diagnostic}\n`) +} + +const build = await execa('pnpm', ['run', 'build'], { + all: true, + cwd: repoRoot, + reject: false, + stripFinalNewline: false, +}) +if (build.failed) { + reportBuildFailure(build) + completeFrom(build) +} else { + const cli = await execa(process.execPath, ['--import', 'tsx/esm', sourceBin, ...process.argv.slice(2)], { + cwd: repoRoot, + env: { ...process.env, NODE_USE_ENV_PROXY: '1' }, + reject: false, + stdio: 'inherit', + }) + completeFrom(cli) +}