diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1e0359..a7c8e47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,8 +92,18 @@ jobs: run: pnpm typecheck - name: Lint run: pnpm lint + # `test:windows` caps the fork pool at 2 instead of `pnpm test`'s + # unbounded default. Five specs in this suite really start processes + # (tests/agent-pty, pty-deps, pty-helpers, install-powershell, + # smoke): node-pty's ConPTY helper and a cold `powershell.exe` are + # both console-bound, and this 2-core runner made parallel spawning + # the amplifier behind every red `Test` step in the 2026-09-09/10 + # window — six real 5000 ms timeouts (agent-pty `:71`/`:106`, + # install-powershell `:105`, the latter measuring 12.1 s) and two + # silent vitest worker deaths. The test bodies now carry their own + # explicit timeouts as well; this just stops them starving each other. - name: Test - run: pnpm test + run: pnpm test:windows - name: Build run: pnpm build - name: Check consumer type surface @@ -131,11 +141,26 @@ jobs: # Pin the DSH CLI the lane mounts into; the profile's bundle stack # (dsh-base / dsh-web-app) resolves from this installation. Keep in # step with the plugin's @deepseek-ai/* peer range when bumping. - # DSH 0.1.5-rc.1 is the `latest` (and `next`) dist-tag on npm — the - # 0.1.5 line's first release candidate; the lanes speak the slash-only - # RPC dialect (tests/e2e/host-protocol.ts). + # DSH 0.1.5-rc.2 is the `next` dist-tag on npm (`latest` still points + # at rc.1); the lanes speak the slash-only RPC dialect + # (tests/e2e/host-protocol.ts). + # + # `--legacy-peer-deps` + the larger heap are the fix for the two ways + # this step used to die (runs 34492160123 / 34491256626 / 34490912649 + # and 34363627184): the pinned version's TRANSITIVES are declared as + # floating `^` ranges, so while upstream publishes a prerelease in + # stages (0.1.5-rc.2 went out 14:43-14:57 on 2026-09-10) npm resolved a + # mixed rc.1/rc.2 peer graph — 3062 ERESOLVE lines, then "Ineffective + # mark-compacts near heap limit" (exit 134), or a hard ETARGET on a + # sibling package that only appeared minutes later. Skipping peer + # negotiation keeps the install deterministic; the accumulated heap + # covers npm's worst case for this ~520-package tree. Mounting a + # version-mismatched bundle tree is not silent here: the mount smoke + # below boots the real shell and would fail loudly. - name: Install dsh CLI (pinned) - run: npm install -g @deepseek-ai/dsh@0.1.5-rc.1 + env: + NODE_OPTIONS: --max-old-space-size=4096 + run: npm install -g --legacy-peer-deps @deepseek-ai/dsh@0.1.5-rc.2 # lib/ is gitignored; the tarball is produced from this build. - name: Build and pack the npm tarball diff --git a/tests/agent-pty.spec.ts b/tests/agent-pty.spec.ts index 06b842c..c607520 100644 --- a/tests/agent-pty.spec.ts +++ b/tests/agent-pty.spec.ts @@ -27,7 +27,7 @@ async function waitForTranscript( registry: AgentPtyRegistry, uuid: string, needle: string, - timeoutMs = 5000, + timeoutMs = 15_000, ): Promise { const deadline = Date.now() + timeoutMs while (Date.now() < deadline) { @@ -68,7 +68,15 @@ describe('tryResizePty', () => { }) }) -describe('AgentPtyRegistry', () => { +/** + * The registry suite really starts a shell per terminal — a PowerShell + + * ConPTY pair on Windows, /bin/sh elsewhere. vitest's 5000 ms default is + * below a cold PowerShell start on a loaded 2-core CI runner (a sibling spec + * measured 12.1 s on 2026-09-10), which is what turned six ci-windows runs + * red in the 2026-09-09/10 window. Generous, but still finite: a genuinely + * hung spawn fails the case. + */ +describe('AgentPtyRegistry', { timeout: 30_000 }, () => { it('creates a terminal with a uuid, writes the command to stdin, and lists it', async () => { const registry = new AgentPtyRegistry(testShell()) try { diff --git a/tests/install-powershell.spec.ts b/tests/install-powershell.spec.ts index 6a7c3f5..a2091ee 100644 --- a/tests/install-powershell.spec.ts +++ b/tests/install-powershell.spec.ts @@ -69,7 +69,15 @@ afterEach(() => { for (const dir of tempDirs.splice(0)) rmSync(dir, { recursive: true, force: true }) }) -describe('PowerShell installer entry points', () => { +/** + * The Windows cases here spawn a real PowerShell (`powershell.exe` 5.1 or + * `pwsh`) per assertion, and a cold 5.1 start on a loaded 2-core CI runner + * measured 12.1 s (ci-windows run 34494370187) — more than twice vitest's + * 5000 ms default, which is why this file kept losing that case. The + * file-content cases below share the suite budget but finish in + * milliseconds either way. + */ +describe('PowerShell installer entry points', { timeout: 30_000 }, () => { it('keeps the UTF-8 BOM required by Windows PowerShell 5.1 -File decoding', () => { expect(readFileSync(INSTALLER).subarray(0, 3)).toEqual(Buffer.from([0xef, 0xbb, 0xbf])) })