فهرست منبع

Merge pull request #2517 from deepseek-harness/xtr/node-pty-1.2-beta

chore(subprocess-local): bump node-pty beta
_Kerman 1 ماه پیش
والد
کامیت
cc68bbf382

+ 1 - 0
.github/workflows/build-exe-for-python-sdk.yml

@@ -194,6 +194,7 @@ jobs:
             *) echo "::error::Unsupported Linux runner architecture $RUNNER_ARCH"; exit 1 ;;
           esac
           addon_dir="$(realpath packages/subprocess/subprocess-local/node_modules/node-pty)"
+          (cd "$addon_dir" && npm_config_build_from_source=true pnpm run install)
           addon="$addon_dir/build/Release/pty.node"
           [ -f "$addon_dir/build/Makefile" ] || {
             echo "::error::node-pty install did not generate $addon_dir/build/Makefile"

+ 1 - 1
THIRD_PARTY_NOTICES.md

@@ -93,7 +93,7 @@ External packages that a workspace package resolves at runtime. The tier covers
 
 pnpm applies local patches to the following packages at install time, so shipped artifacts carry modified copies; each patch file is the complete record of the modification:
 
-- `node-pty@1.1.0` — [`patches/node-pty@1.1.0.patch`](patches/node-pty@1.1.0.patch)
+- `node-pty@1.2.0-beta.15` — [`patches/node-pty@1.2.0-beta.15.patch`](patches/node-pty@1.2.0-beta.15.patch)
 
 ## Official Claude Code platform payloads
 

+ 1 - 1
packages/subprocess/subprocess-local/package.json

@@ -42,7 +42,7 @@
     "@deepseek-ai/cordis": "workspace:^"
   },
   "dependencies": {
-    "node-pty": "^1.1.0"
+    "node-pty": "1.2.0-beta.15"
   },
   "devDependencies": {
     "@deepseek-ai/dsh-invariants": "workspace:^",

+ 0 - 62
patches/node-pty@1.1.0.patch

@@ -1,62 +0,0 @@
-diff --git a/lib/unixTerminal.js b/lib/unixTerminal.js
-index 1ec12f796a822c78fba9ad7f6448c3987e325c23..5cd6b7d635f4752be5a6c5ff9cf9edf988cf94c5 100644
---- a/lib/unixTerminal.js
-+++ b/lib/unixTerminal.js
-@@ -26,10 +26,23 @@ var terminal_1 = require("./terminal");
- var utils_1 = require("./utils");
- var native = utils_1.loadNativeModule('pty');
- var pty = native.module;
--var helperPath = native.dir + '/spawn-helper';
--helperPath = path.resolve(__dirname, helperPath);
--helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
--helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
-+// A current external embedded-runtime consumer supplies a non-sibling helper.
-+var helperPath = process.env.DSH_NODE_PTY_SPAWN_HELPER;
-+if (helperPath) {
-+    helperPath = path.resolve(helperPath);
-+}
-+else {
-+    var executableSibling = process.execPath + '-spawn-helper';
-+    if (fs.existsSync(executableSibling)) {
-+        helperPath = executableSibling;
-+    }
-+    else {
-+        helperPath = native.dir + '/spawn-helper';
-+        helperPath = path.resolve(__dirname, helperPath);
-+        helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
-+        helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
-+    }
-+}
- var DEFAULT_FILE = 'sh';
- var DEFAULT_NAME = 'xterm';
- var DESTROY_SOCKET_TIMEOUT_MS = 200;
-diff --git a/src/unixTerminal.ts b/src/unixTerminal.ts
-index 98733dc0cd752b554bd94e45904ca341ad141bba..fa234291206617ae5a6d8605abf9771220392d17 100644
---- a/src/unixTerminal.ts
-+++ b/src/unixTerminal.ts
-@@ -14,10 +14,21 @@ import { assign, loadNativeModule } from './utils';
-
- const native = loadNativeModule('pty');
- const pty: IUnixNative = native.module;
--let helperPath = native.dir + '/spawn-helper';
--helperPath = path.resolve(__dirname, helperPath);
--helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
--helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
-+// A current external embedded-runtime consumer supplies a non-sibling helper.
-+let helperPath = process.env.DSH_NODE_PTY_SPAWN_HELPER;
-+if (helperPath) {
-+  helperPath = path.resolve(helperPath);
-+} else {
-+  const executableSibling = process.execPath + '-spawn-helper';
-+  if (fs.existsSync(executableSibling)) {
-+    helperPath = executableSibling;
-+  } else {
-+    helperPath = native.dir + '/spawn-helper';
-+    helperPath = path.resolve(__dirname, helperPath);
-+    helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
-+    helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
-+  }
-+}
-
- const DEFAULT_FILE = 'sh';
- const DEFAULT_NAME = 'xterm';

+ 32 - 0
patches/node-pty@1.2.0-beta.15.patch

@@ -0,0 +1,32 @@
+diff --git a/lib/unixTerminal.js b/lib/unixTerminal.js
+index 6966d24..18f1d25 100644
+--- a/lib/unixTerminal.js
++++ b/lib/unixTerminal.js
+@@ -28,10 +28,23 @@ var terminal_1 = require("./terminal");
+ var utils_1 = require("./utils");
+ var native = (0, utils_1.loadNativeModule)('pty');
+ var pty = native.module;
+-var helperPath = native.dir + '/spawn-helper';
+-helperPath = path.resolve(__dirname, helperPath);
+-helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
+-helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
++// A current external embedded-runtime consumer supplies a non-sibling helper.
++var helperPath = process.env.DSH_NODE_PTY_SPAWN_HELPER;
++if (helperPath) {
++    helperPath = path.resolve(helperPath);
++}
++else {
++    var executableSibling = process.execPath + '-spawn-helper';
++    if (fs.existsSync(executableSibling)) {
++        helperPath = executableSibling;
++    }
++    else {
++        helperPath = native.dir + '/spawn-helper';
++        helperPath = path.resolve(__dirname, helperPath);
++        helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
++        helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
++    }
++}
+ var DEFAULT_FILE = 'sh';
+ var DEFAULT_NAME = 'xterm';
+ var DESTROY_SOCKET_TIMEOUT_MS = 200;

+ 6 - 6
pnpm-lock.yaml

@@ -9,7 +9,7 @@ overrides:
   '@deepseek-ai/schemastery': link:vendor/schemastery
 
 patchedDependencies:
-  node-pty@1.1.0: 7a0c04f1f49d798a9ffe2f7f414c01064a44ca2489772d0c3e1235ab336755e6
+  node-pty@1.2.0-beta.15: b40ae545608897914bd25fb009c97eeac478c34e8a910298ddcb01b746534bb0
 
 importers:
 
@@ -7493,8 +7493,8 @@ importers:
   packages/subprocess/subprocess-local:
     dependencies:
       node-pty:
-        specifier: ^1.1.0
-        version: 1.1.0(patch_hash=7a0c04f1f49d798a9ffe2f7f414c01064a44ca2489772d0c3e1235ab336755e6)
+        specifier: 1.2.0-beta.15
+        version: 1.2.0-beta.15(patch_hash=b40ae545608897914bd25fb009c97eeac478c34e8a910298ddcb01b746534bb0)
     devDependencies:
       '@deepseek-ai/cordis':
         specifier: workspace:^
@@ -13164,8 +13164,8 @@ packages:
     resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
 
-  node-pty@1.1.0:
-    resolution: {integrity: sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==}
+  node-pty@1.2.0-beta.15:
+    resolution: {integrity: sha512-vORSzHXi4Ofl7HemVWpuudLqCPdaQb4LfpRCUpE5HPxhp4JYscl8zZwxh11p26v2wvW24WMwnMfLjhRLixrfxA==}
 
   node-releases@2.0.51:
     resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==}
@@ -18588,7 +18588,7 @@ snapshots:
       fetch-blob: 3.2.0
       formdata-polyfill: 4.0.10
 
-  node-pty@1.1.0(patch_hash=7a0c04f1f49d798a9ffe2f7f414c01064a44ca2489772d0c3e1235ab336755e6):
+  node-pty@1.2.0-beta.15(patch_hash=b40ae545608897914bd25fb009c97eeac478c34e8a910298ddcb01b746534bb0):
     dependencies:
       node-addon-api: 7.1.1
 

+ 1 - 1
pnpm-workspace.yaml

@@ -69,4 +69,4 @@ minimumReleaseAgeExclude:
   - node-addon-require-builtin@0.1.4
 
 patchedDependencies:
-  node-pty@1.1.0: patches/node-pty@1.1.0.patch
+  node-pty@1.2.0-beta.15: patches/node-pty@1.2.0-beta.15.patch

+ 49 - 0
scripts/build-exe-for-python-sdk-native-pty.spec.ts

@@ -0,0 +1,49 @@
+import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
+import { tmpdir } from 'node:os'
+import { dirname, join } from 'node:path'
+import { afterEach, describe, expect, it } from 'vitest'
+import { resolveLinuxNodePtyAddon } from './build-exe-for-python-sdk-native-pty.ts'
+
+const roots: string[] = []
+
+afterEach(() => {
+  for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
+})
+
+describe('resolveLinuxNodePtyAddon', () => {
+  it('prefers the manylinux build produced by the release workflow', () => {
+    const root = temporaryPackage()
+    const built = createAddon(root, 'build', 'Release', 'pty.node')
+    createAddon(root, 'prebuilds', 'linux-x64', 'pty.node')
+
+    expect(resolveLinuxNodePtyAddon(root, 'x64')).toBe(built)
+  })
+
+  it('uses the target prebuild after an ordinary beta install', () => {
+    const root = temporaryPackage()
+    const prebuilt = createAddon(root, 'prebuilds', 'linux-arm64', 'pty.node')
+
+    expect(resolveLinuxNodePtyAddon(root, 'arm64')).toBe(prebuilt)
+  })
+
+  it('reports both expected locations when no addon is installed', () => {
+    const root = temporaryPackage()
+
+    expect(() => resolveLinuxNodePtyAddon(root, 'x64')).toThrow(
+      `node-pty addon is absent from both ${join(root, 'build', 'Release', 'pty.node')} and ${join(root, 'prebuilds', 'linux-x64', 'pty.node')}`,
+    )
+  })
+})
+
+function temporaryPackage(): string {
+  const root = mkdtempSync(join(tmpdir(), 'dsh-node-pty-addon-'))
+  roots.push(root)
+  return root
+}
+
+function createAddon(root: string, ...segments: string[]): string {
+  const path = join(root, ...segments)
+  mkdirSync(dirname(path), { recursive: true })
+  writeFileSync(path, '')
+  return path
+}

+ 23 - 0
scripts/build-exe-for-python-sdk-native-pty.ts

@@ -0,0 +1,23 @@
+/** Resolve the native node-pty input used by the Python SDK runtime builder. */
+
+import { existsSync } from 'node:fs'
+import { join } from 'node:path'
+
+/**
+ * Prefer the workflow's manylinux build and fall back to node-pty's target prebuild.
+ * @param packageDirectory - installed node-pty package directory.
+ * @param arch - Linux target architecture.
+ * @returns the existing addon path.
+ */
+export function resolveLinuxNodePtyAddon(
+  packageDirectory: string,
+  arch: 'x64' | 'arm64',
+): string {
+  const built = join(packageDirectory, 'build', 'Release', 'pty.node')
+  if (existsSync(built)) return built
+  const prebuilt = join(packageDirectory, 'prebuilds', `linux-${arch}`, 'pty.node')
+  if (existsSync(prebuilt)) return prebuilt
+  throw new Error(
+    `build-exe-for-python-sdk: node-pty addon is absent from both ${built} and ${prebuilt}.`,
+  )
+}

+ 12 - 3
scripts/build-exe-for-python-sdk.ts

@@ -11,6 +11,7 @@ import { existsSync, statSync } from 'node:fs'
 import { chmod, copyFile, cp, lstat, mkdir, readFile, readdir, realpath, rm, writeFile } from 'node:fs/promises'
 import { basename, dirname, join, resolve, sep } from 'node:path'
 import { parseArgs } from 'node:util'
+import { resolveLinuxNodePtyAddon } from './build-exe-for-python-sdk-native-pty.ts'
 
 const root = resolve(import.meta.dirname, '..')
 
@@ -409,8 +410,8 @@ class SingleExeBuild {
   }
 
   /**
-   * Put the target node-pty addon in the staged closure. Linux npm installs
-   * build it from source, but legacy deploy omits that side-effect directory.
+   * Put the target node-pty addon in the staged closure. The release workflow
+   * provides a manylinux build; ordinary installs use node-pty's target prebuild.
    * @param target - the pkg target whose native addon is being staged.
    */
   private async prepareNativePty(target: Target): Promise<void> {
@@ -418,8 +419,16 @@ class SingleExeBuild {
     if (this.cli.dryRun) console.log(`build-exe-for-python-sdk: [dry-run] rm -rf ${stagedBuild}`)
     else await rm(stagedBuild, { recursive: true, force: true })
     if (target.platform !== 'linux') return
-    const source = join(root, 'packages', 'subprocess', 'subprocess-local', 'node_modules', 'node-pty', 'build', 'Release', 'pty.node')
+    const packageDirectory = join(
+      root,
+      'packages',
+      'subprocess',
+      'subprocess-local',
+      'node_modules',
+      'node-pty',
+    )
     const destination = join(stagedBuild, 'Release', 'pty.node')
+    const source = resolveLinuxNodePtyAddon(packageDirectory, target.arch)
     if (this.cli.dryRun) {
       console.log(`build-exe-for-python-sdk: [dry-run] cp ${source} ${destination}`)
       return

+ 1 - 0
scripts/ci-workflow.spec.ts

@@ -342,6 +342,7 @@ describe('Python release workflows', () => {
     expect(manylinuxAddon).toMatchObject({ if: "runner.os == 'Linux'" })
     expect(JSON.stringify(manylinuxAddon)).toContain('manylinux_2_28_x86_64')
     expect(JSON.stringify(manylinuxAddon)).toContain('manylinux_2_28_aarch64')
+    expect(JSON.stringify(manylinuxAddon)).toContain('npm_config_build_from_source=true pnpm run install')
     expect(JSON.stringify(manylinuxAddon)).toContain('$HOME/setup-pnpm:$HOME/setup-pnpm:ro')
     expect(JSON.stringify(manylinuxAddon)).toContain('node-pty-glibc-versions.txt')
     expect(JSON.stringify(manylinuxAddon)).toContain('le 2.28')