diff --git a/apps/desktop/README.i18n.yaml b/apps/desktop/README.i18n.yaml index 12ea49fc20..ec85ddebce 100644 --- a/apps/desktop/README.i18n.yaml +++ b/apps/desktop/README.i18n.yaml @@ -21,8 +21,8 @@ en: 0d11af8639977a6d zh: 86b086b12d2210d3 /deepseek-harness-desktop/develop: - en: 9b9b362a9865e763 - zh: c748901168e2db8c + en: 40d2ad4014bf445b + zh: 6a08de06df9ed042 /deepseek-harness-desktop/develop/startup-onboarding: en: a51346b8d880ddba zh: 7913214c1c66063c diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 5075f86f1b..8109ebd577 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -134,7 +134,7 @@ pnpm run start:desktop The Web counterparts are `pnpm run dev:web` and `pnpm run start:web`, documented in the [development guide](../../docs/development.md). Workspace development runs the current CLI and private Desktop Host packages under Electron RunAsNode. Plugin management and recovery use `$DSH_HOME/profiles/desktop`, separate from the disposable workspace runtime. The Host uses runtime module resolution in both development and packaged builds without creating official-package fallback links; developer-installed packages, including links, retain native priority. Use an unpacked application to exercise Electron RunAsNode, bundled pnpm, bundled dsh resources, plugin installation and repair paths. -The [native/renderer keyboard tests](tests/keyboard.spec.ts) compile in [their own Client test project](../../tsconfig.desktop-keyboard-tests.json), reached by the repository Client typecheck. Its Desktop imports are limited to Cordis-free input, persistence, IPC, browser-guest, and overlay modules. +The [native/renderer keyboard tests](tests/keyboard.spec.ts) compile as part of the repository Client typecheck. Their Desktop imports are limited to Cordis-free input, persistence, IPC, browser-guest, and overlay modules. ### Startup onboarding diff --git a/apps/desktop/README.zh.md b/apps/desktop/README.zh.md index 28aedba731..f6c9667ad7 100644 --- a/apps/desktop/README.zh.md +++ b/apps/desktop/README.zh.md @@ -136,7 +136,7 @@ pnpm run start:desktop Web 侧的对应命令是 `pnpm run dev:web` 与 `pnpm run start:web`,见[开发指南](../../docs/development.zh.md)。Workspace 开发使用 Electron RunAsNode 运行当前 CLI 与私有 Desktop Host 包,插件管理和恢复使用 `$DSH_HOME/profiles/desktop`,与一次性工作区运行时分离。Host 在开发与打包构建中都使用 runtime 模块解析,不创建官方包的 fallback 链接;开发者安装的包(包括链接)保留原生优先级。需要验证 Electron RunAsNode、内置 pnpm、内置 dsh 资源、插件安装和修复时,应运行未封装安装器的应用目录。 -[原生输入与渲染进程键盘测试](tests/keyboard.spec.ts)在[独立的 Client 测试项目](../../tsconfig.desktop-keyboard-tests.json)中编译,由仓库 Client 类型检查纳入。它只导入不依赖 Cordis 的 Desktop 输入、持久化、IPC、浏览器 guest 和蒙层模块。 +[原生输入与渲染进程键盘测试](tests/keyboard.spec.ts)直接纳入仓库 Client 类型检查。它只导入不依赖 Cordis 的 Desktop 输入、持久化、IPC、浏览器 guest 和蒙层模块。 ### 启动引导 diff --git a/scripts/install-lefthook.spec.ts b/scripts/install-lefthook.spec.ts index c4e9af44da..8c994bf50b 100644 --- a/scripts/install-lefthook.spec.ts +++ b/scripts/install-lefthook.spec.ts @@ -7,6 +7,7 @@ import { mkdtempSync, lstatSync, readFileSync, + realpathSync, renameSync, rmSync, symlinkSync, @@ -158,9 +159,15 @@ function gitDirectory(fixture: Fixture, root: string): string { return git(fixture, root, ['rev-parse', '--absolute-git-dir']) } +// The installer resolves the common directory against Git's own top-level path, and the host +// spells that path differently from the fixture directory this file created: Git canonicalizes +// Windows 8.3 short names and the macOS `/var` symlink, while `mkdtempSync` returns the temp +// directory's own spelling. Canonicalizing the fixture-side directory collapses both spellings +// onto the one directory the installer touches, so an injected failure on this path reaches the +// installer's own lock instead of a differently spelled name for it. function commonDirectory(fixture: Fixture): string { const output = git(fixture, fixture.main, ['rev-parse', '--git-common-dir']) - return isAbsolute(output) ? output : resolve(fixture.main, output) + return realpathSync.native(isAbsolute(output) ? output : resolve(fixture.main, output)) } function hooksPath(fixture: Fixture, root: string): string { @@ -321,14 +328,11 @@ describe('worktree-local Lefthook installer', { timeout: 90_000 }, () => { expect(existsSync(join(hooksPath(fixture, fixture.main), '.fake-lefthook-running'))).toBe(false) }) - it.each([ - { operation: 'openSync', code: 'EPERM', expires: false }, - { operation: 'readFileSync', code: 'EPERM', expires: false }, - { operation: 'lstatSync', code: 'EPERM', expires: false }, - { operation: 'openSync', code: 'EPERM', expires: true }, - { operation: 'openSync', code: 'EACCES', expires: false }, - ])('handles $operation $code with expired deadline=$expires', async ({ operation, code, expires }) => { - const fixture = createFixture() + /** Inject one lock-access failure and assert the installer's documented outcome. */ + async function expectInjectedLockAccessFailure( + fixture: Fixture, + { operation, code, expires }: { operation: string; code: string; expires: boolean }, + ): Promise { const lockPath = installLockPath(fixture) const probe = join(fixture.container, 'lock-access-probe') const preload = join(fixture.container, 'lock-access.cjs') @@ -369,6 +373,28 @@ syncBuiltinESMExports() expect(result.stderr).toContain('injected lock access failure') expect(existsSync(hooksPath(fixture, fixture.main))).toBe(false) } + } + + it.each([ + { operation: 'openSync', code: 'EPERM', expires: false }, + { operation: 'readFileSync', code: 'EPERM', expires: false }, + { operation: 'lstatSync', code: 'EPERM', expires: false }, + { operation: 'openSync', code: 'EPERM', expires: true }, + { operation: 'openSync', code: 'EACCES', expires: false }, + ])('handles $operation $code with expired deadline=$expires', async ({ operation, code, expires }) => { + await expectInjectedLockAccessFailure(createFixture(), { operation, code, expires }) + }) + + // The alias forces the installer's resolved common directory to differ from the path this spec + // composes on every host — a junction on Windows, a directory symlink elsewhere — instead of + // depending on a runner whose temp directory happens to carry a short name. + it('handles an injected lock failure through an aliased worktree root', async () => { + const fixture = createFixture() + const alias = join(fixture.container, 'main-alias') + symlinkSync(fixture.main, alias, process.platform === 'win32' ? 'junction' : 'dir') + fixture.main = alias + + await expectInjectedLockAccessFailure(fixture, { operation: 'openSync', code: 'EPERM', expires: false }) }) it('waits for a concurrent installer to finish publishing its lock record', async () => { diff --git a/tsconfig.client.json b/tsconfig.client.json index 0471bfa13b..2e27307ddb 100644 --- a/tsconfig.client.json +++ b/tsconfig.client.json @@ -14,6 +14,13 @@ "types": ["node"] }, "include": [ + // The native/renderer keyboard fixture imports only these Cordis-free Desktop modules. + "apps/desktop/tests/keyboard.spec.ts", + "apps/desktop/src/keyboard.ts", + "apps/desktop/src/keybindings.ts", + "apps/desktop/src/browser-guests.ts", + "apps/desktop/src/ipc.ts", + "apps/desktop/src/update-overlay.ts", "apps/desktop/tests/**/*.client.spec.tsx", // Source-subpath test imports can arrive through workspace links whose // realpath semantics vary by host. Load package CSS declarations directly. @@ -43,8 +50,8 @@ "packages/client/*/tests/**/*.host.spec.ts" ], "references": [ - { "path": "./tsconfig.desktop-keyboard-tests.json" }, { "path": "./apps/desktop/tsconfig.client.json" }, + { "path": "./packages/util/atomic-write" }, { "path": "./packages/experimental/client-ui-voice-input" }, { "path": "./packages/client/ui-sidebar-terminal" }, diff --git a/tsconfig.desktop-keyboard-tests.json b/tsconfig.desktop-keyboard-tests.json deleted file mode 100644 index 28cdd1bf9c..0000000000 --- a/tsconfig.desktop-keyboard-tests.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - // The native/renderer integration fixture imports only Cordis-free Desktop modules. - "extends": "./tsconfig.base.client.json", - "compilerOptions": { - "rootDir": ".", - "outDir": "lib/desktop-keyboard-test-types", - "emitDeclarationOnly": true, - "types": ["node"] - }, - "files": [ - "apps/desktop/tests/keyboard.spec.ts", - "apps/desktop/src/keyboard.ts", - "apps/desktop/src/keybindings.ts", - "apps/desktop/src/browser-guests.ts", - "apps/desktop/src/ipc.ts", - "apps/desktop/src/update-overlay.ts" - ], - "include": [], - "references": [ - { "path": "./packages/client/shortcuts/tsconfig.client.json" }, - { "path": "./packages/client/ui-sidebar-browser/tsconfig.client.json" }, - { "path": "./packages/util/atomic-write" } - ] -}