1
0
Эх сурвалжийг харах

fix(subprocess): preserve literal spawn error stacks

pku-xht 2 долоо хоног өмнө
parent
commit
aaa5117ebd

+ 2 - 2
packages/subprocess/subprocess-local/src/index.ts

@@ -35,7 +35,7 @@ import {
   probeLinuxNative,
 } from './linux-scope.ts'
 import { launchWindowsJob, probeWindowsJob } from './windows-job.ts'
-import { targetEnvironment, validateTerminalTarget } from './runner-launch.ts'
+import { targetEnvironment } from './runner-launch.ts'
 import { createProcessInspector } from './process-inspector.ts'
 import type { ProcessInspector } from './process-inspector.ts'
 import { LocalTerminalHandle } from './terminal.ts'
@@ -230,7 +230,7 @@ export class LocalSubprocessRuntime extends SubprocessRuntime {
       throw new Error('subprocess-local: terminal argv must contain a program')
     }
     spec.signal?.throwIfAborted()
-    const env = validateTerminalTarget(spec)
+    const env = targetEnvironment(spec)
     const options: IPtyForkOptions = {
       name: 'dumb',
       rows: spec.rows,

+ 1 - 10
packages/subprocess/subprocess-local/src/runner-launch.ts

@@ -5,7 +5,7 @@ import { accessSync, constants as fsConstants, statSync } from 'node:fs'
 import { extname, isAbsolute } from 'node:path'
 import { inspect } from 'node:util'
 import { fileURLToPath } from 'node:url'
-import type { SubprocessSpawnSpec, SubprocessTerminalSpawnSpec } from '@deepseek-ai/dsh-subprocess'
+import type { SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess'
 import { childEnv } from './spawn.ts'
 
 /** The one private environment variable consumed before target state is restored. */
@@ -289,12 +289,3 @@ export function targetEnvironment(
   }
   return env
 }
-
-/**
- * Validate Linux PTY target strings before creating its request or terminal.
- * @param spec - terminal subprocess request to validate.
- * @returns complete validated target environment.
- */
-export function validateTerminalTarget(spec: SubprocessTerminalSpawnSpec): Record<string, string> {
-  return targetEnvironment(spec)
-}

+ 1 - 1
packages/subprocess/subprocess-local/src/spawn-runner.ts

@@ -78,7 +78,7 @@ function nodeSpawnError(
     name: 'Error',
     message,
     ...source.stack === undefined ? {} : {
-      stack: source.stack.replace(/^[^\n]*/, `Error: ${message}`),
+      stack: source.stack.replace(/^[^\n]*/, () => `Error: ${message}`),
     },
     code,
     ...errno === undefined ? {} : { errno },

+ 13 - 14
packages/subprocess/subprocess-local/tests/spawn-runner.spec.ts

@@ -42,7 +42,6 @@ import {
   spawnRunnerInvocation,
   SUBPROCESS_RUNNER_ENV,
   targetEnvironment,
-  validateTerminalTarget,
   WINDOWS_RUNNER_SELECTION,
 } from '../src/runner-launch.ts'
 import {
@@ -114,10 +113,11 @@ async function runWindows(
   host: FakeRunnerHost,
   native: SpawnRunnerInternals,
   start: unknown = { type: 'start', cwd: 'C:\\target', env: { TARGET: 'yes', dsh_subprocess_runner: 'restored' } },
+  targetArgv: string[] = ['tool.exe', 'literal arg'],
 ): Promise<void> {
   const running = runSpawnRunner(
     WINDOWS_RUNNER_SELECTION,
-    ['--', 'tool.exe', 'literal arg'],
+    ['--', ...targetArgv],
     hostArgument(host),
     native,
   )
@@ -276,7 +276,6 @@ describe('runner launch inputs', () => {
     expect(targetEnvironment(spec)).toMatchObject({ EXPLICIT: 'yes' })
     expect(targetEnvironment({ ...spec, env: { '=C:': 'C:\\target' } }))
       .toMatchObject({ '=C:': 'C:\\target' })
-    expect(validateTerminalTarget({ ...spec, rows: 24, cols: 80 })).toMatchObject({ EXPLICIT: 'yes' })
     for (const invalid of [
       { ...spec, argv: ['node\0'] },
       { ...spec, argv: ['node', 'a\0'] },
@@ -602,20 +601,20 @@ describe('Windows Job runner protocol owner', () => {
   })
 
   it('maps the bounded Win32 process-creation error classes', async () => {
-    for (const [win32Code, code, errno, enriched] of [
-      [2, 'ENOENT', -4058, true],
-      [3, 'ENOENT', -4058, true],
-      [267, 'ENOENT', -4058, true],
-      [740, 'EACCES', -4092, true],
-      [5, 'EPERM', -4048, false],
-      [193, 'EFTYPE', -4028, false],
-      [999, 'UNKNOWN', -4094, false],
+    for (const [win32Code, code, errno, enriched, program] of [
+      [2, 'ENOENT', -4058, true, 'tool.exe'],
+      [3, 'ENOENT', -4058, true, 'tool.exe'],
+      [267, 'ENOENT', -4058, true, 'tool.exe'],
+      [740, 'EACCES', -4092, true, '$&.exe'],
+      [5, 'EPERM', -4048, false, 'tool.exe'],
+      [193, 'EFTYPE', -4028, false, 'tool.exe'],
+      [999, 'UNKNOWN', -4094, false, 'tool.exe'],
     ] as const) {
       const host = new FakeRunnerHost()
       await runWindows(host, internals({
         spawnCurrentTokenJobProcess: vi.fn(() => { throw new Win32Error('CreateProcessW', win32Code) }),
-      }))
-      const syscall = enriched ? 'spawn tool.exe' : 'spawn'
+      }), undefined, [program, 'literal arg'])
+      const syscall = enriched ? `spawn ${program}` : 'spawn'
       expect(host.sent).toMatchObject([{
         type: 'error',
         error: {
@@ -630,7 +629,7 @@ describe('Windows Job runner protocol owner', () => {
       if (result.type !== 'error') throw new Error('expected runner error')
       expect(result.error.stack?.split('\n')[0]).toBe(`Error: ${syscall} ${code}`)
       if (enriched) {
-        expect(result.error).toMatchObject({ path: 'tool.exe', spawnargs: ['literal arg'] })
+        expect(result.error).toMatchObject({ path: program, spawnargs: ['literal arg'] })
       } else {
         expect(result.error).not.toHaveProperty('path')
         expect(result.error).not.toHaveProperty('spawnargs')