From 078dd2b6dffd67b41de0b93e48a680048e3b5892 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 13 Aug 2026 17:57:58 +0800 Subject: [PATCH 1/5] chore(subprocess-local): bump node-pty beta --- .../subprocess/subprocess-local/package.json | 2 +- patches/node-pty@1.1.0.patch | 62 ------------------- patches/node-pty@1.2.0-beta.15.patch | 32 ++++++++++ pnpm-lock.yaml | 12 ++-- pnpm-workspace.yaml | 2 +- 5 files changed, 40 insertions(+), 70 deletions(-) delete mode 100644 patches/node-pty@1.1.0.patch create mode 100644 patches/node-pty@1.2.0-beta.15.patch diff --git a/packages/subprocess/subprocess-local/package.json b/packages/subprocess/subprocess-local/package.json index 74b480ae82..9c46d81225 100644 --- a/packages/subprocess/subprocess-local/package.json +++ b/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:^", diff --git a/patches/node-pty@1.1.0.patch b/patches/node-pty@1.1.0.patch deleted file mode 100644 index 56892a3d58..0000000000 --- a/patches/node-pty@1.1.0.patch +++ /dev/null @@ -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'; diff --git a/patches/node-pty@1.2.0-beta.15.patch b/patches/node-pty@1.2.0-beta.15.patch new file mode 100644 index 0000000000..74eecb16cd --- /dev/null +++ b/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; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1490a0f9f7..0c3f2c4338 100644 --- a/pnpm-lock.yaml +++ b/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 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e8d8ee5bec..ec6cfd3af9 100644 --- a/pnpm-workspace.yaml +++ b/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 From 348a49b62c25aef662cbb0d547b0ba27b6802ff8 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 13 Aug 2026 18:02:39 +0800 Subject: [PATCH 2/5] docs: update node-pty patch notice --- THIRD_PARTY_NOTICES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 92b218ff33..d672d46ac6 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/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 From 1106b0b03df995720ddf7aea75681ba2b0bd654e Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 13 Aug 2026 18:06:36 +0800 Subject: [PATCH 3/5] ci: rebuild node-pty for manylinux --- .github/workflows/build-exe-for-python-sdk.yml | 1 + scripts/ci-workflow.spec.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build-exe-for-python-sdk.yml b/.github/workflows/build-exe-for-python-sdk.yml index a8aec21262..63779282b4 100644 --- a/.github/workflows/build-exe-for-python-sdk.yml +++ b/.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 npm 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" diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index 84ac580f31..63904dc265 100644 --- a/scripts/ci-workflow.spec.ts +++ b/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 npm 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') From a785eb80f7a82b4b5e5f585204441db01981c029 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 13 Aug 2026 18:12:36 +0800 Subject: [PATCH 4/5] fix(python-runtime): fall back to node-pty prebuild --- ...uild-exe-for-python-sdk-native-pty.spec.ts | 49 +++++++++++++++++++ .../build-exe-for-python-sdk-native-pty.ts | 23 +++++++++ scripts/build-exe-for-python-sdk.ts | 15 ++++-- 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 scripts/build-exe-for-python-sdk-native-pty.spec.ts create mode 100644 scripts/build-exe-for-python-sdk-native-pty.ts diff --git a/scripts/build-exe-for-python-sdk-native-pty.spec.ts b/scripts/build-exe-for-python-sdk-native-pty.spec.ts new file mode 100644 index 0000000000..5dd6588955 --- /dev/null +++ b/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 +} diff --git a/scripts/build-exe-for-python-sdk-native-pty.ts b/scripts/build-exe-for-python-sdk-native-pty.ts new file mode 100644 index 0000000000..02fa864d73 --- /dev/null +++ b/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}.`, + ) +} diff --git a/scripts/build-exe-for-python-sdk.ts b/scripts/build-exe-for-python-sdk.ts index da1cea67c4..801a004fd6 100644 --- a/scripts/build-exe-for-python-sdk.ts +++ b/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 { @@ -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 From b11b5359f9b5c345b8e71cfcb1c2ad05da483714 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 13 Aug 2026 18:13:03 +0800 Subject: [PATCH 5/5] ci: use pnpm node-gyp for manylinux rebuild --- .github/workflows/build-exe-for-python-sdk.yml | 2 +- scripts/ci-workflow.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-exe-for-python-sdk.yml b/.github/workflows/build-exe-for-python-sdk.yml index 63779282b4..8c6569fa07 100644 --- a/.github/workflows/build-exe-for-python-sdk.yml +++ b/.github/workflows/build-exe-for-python-sdk.yml @@ -194,7 +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 npm run install) + (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" diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index 63904dc265..df3e983828 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -342,7 +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 npm run install') + 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')