Browse Source

fix(python): stop the packaged runtime from hijacking spawned node commands

@yao-pkg/pkg's SEA bootstrap rewrites child_process commands named node --
including the string after a -c flag, exactly the Bash tool's bash -c form --
to the executable itself and stamps PKG_EXECPATH into every child
environment, so a model-issued 'node --version' silently booted the dsh CLI
instead of the machine's Node. Pin the packager as an exact root
devDependency invoked through pnpm exec and patch out the single
patchChildProcess call from the SEA bootstrap bundle; packaged children now
resolve node through PATH like any other process. The third-party notices
drop the build-time tools section: the packager is now a declared, patched
devDependency, so the manifest and patch tiers disclose it.
imccyu 3 days ago
parent
commit
c732dedc69

+ 2 - 6
THIRD_PARTY_NOTICES.md

@@ -112,6 +112,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:
 
+- `@yao-pkg/pkg@6.21.0` — [`patches/@yao-pkg__pkg@6.21.0.patch`](patches/@yao-pkg__pkg@6.21.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
@@ -161,6 +162,7 @@ External packages **directly declared** only by repository tooling, test infrast
 | [`@types/ws`](https://github.com/DefinitelyTyped/DefinitelyTyped) | MIT |
 | [`@vitejs/plugin-react`](https://github.com/vitejs/vite-plugin-react) | MIT |
 | [`@vitest/coverage-v8`](https://github.com/vitest-dev/vitest) | MIT |
+| [`@yao-pkg/pkg`](https://github.com/yao-pkg/pkg) | MIT |
 | [`@yarnpkg/cli-dist`](https://github.com/yarnpkg/berry) | BSD-2-Clause |
 | [`cytoscape`](https://github.com/cytoscape/cytoscape.js) | MIT |
 | [`cytoscape-cose-bilkent`](https://github.com/cytoscape/cytoscape.js-cose-bilkent) | MIT |
@@ -204,12 +206,6 @@ Direct dependencies of the `pyproject.toml` manifests, plus `uv` as the developm
 | [`pytest`](https://github.com/pytest-dev/pytest) | MIT | test-only |
 | [`uv`](https://github.com/astral-sh/uv) | MIT / Apache-2.0 | development workflow tool |
 
-## Fetched at build time
-
-| Package | License | Role |
-| --- | --- | --- |
-| [`@yao-pkg/pkg`](https://github.com/yao-pkg/pkg) | MIT | invoked by `scripts/build-exe-for-python-sdk.ts` to assemble the single-file SDK runtime executable |
-
 ## First-party native packages
 
 `@deepseek-ai/node-addon-landlock-run` (and its platform packages) is built and released from this repository under BSD 3-Clause. It is listed here for completeness; it is first-party, not third-party.

+ 1 - 0
package.json

@@ -169,6 +169,7 @@
     "@types/node": "^22.20.0",
     "@types/spdx-expression-parse": "^4.0.0",
     "@vitest/coverage-v8": "^4.1.8",
+    "@yao-pkg/pkg": "6.21.0",
     "@yarnpkg/cli-dist": "4.17.1",
     "eslint-plugin-sonarjs": "^4.1.0",
     "execa": "^10.0.0",

+ 12 - 0
patches/@yao-pkg__pkg@6.21.0.patch

@@ -0,0 +1,12 @@
+diff --git a/prelude/sea-bootstrap.bundle.js b/prelude/sea-bootstrap.bundle.js
+index ac14980e953134f2285e326a5111cf52de717cf5..6b0c9d5b5063f447cb8aa3e0fc9d342729f29989 100644
+--- a/prelude/sea-bootstrap.bundle.js
++++ b/prelude/sea-bootstrap.bundle.js
+@@ -5560,7 +5560,6 @@ var require_sea_bootstrap_core = __commonJS({
+     var insideSnapshot = vfs.insideSnapshot;
+     var SNAPSHOT_PREFIX = vfs.SNAPSHOT_PREFIX;
+     shared.patchDlopen(insideSnapshot);
+-    shared.patchChildProcess(entrypoint2);
+     shared.setupProcessPkg(entrypoint2, manifest2.entrypoint);
+     if (manifest2.debug) {
+       shared.installDiagnostic(SNAPSHOT_PREFIX);

File diff suppressed because it is too large
+ 448 - 0
pnpm-lock.yaml


+ 1 - 0
pnpm-workspace.yaml

@@ -75,4 +75,5 @@ minimumReleaseAgeExclude:
   - '@openai/codex@0.149.1||0.149.1-darwin-arm64||0.149.1-darwin-x64||0.149.1-linux-arm64||0.149.1-linux-x64||0.149.1-win32-arm64||0.149.1-win32-x64'
 
 patchedDependencies:
+  '@yao-pkg/pkg@6.21.0': patches/@yao-pkg__pkg@6.21.0.patch
   node-pty@1.2.0-beta.15: patches/node-pty@1.2.0-beta.15.patch

+ 1 - 1
scripts/build-exe-for-python-sdk.spec.ts

@@ -34,7 +34,7 @@ describe('Python runtime executable builder CLI', () => {
     expect(result.status).toBe(0)
     expect(result.stdout).toContain(`${process.execPath} C:\\tools\\pnpm.cjs run verify-runtime-closure`)
     expect(result.stdout).toContain(`${process.execPath} C:\\tools\\pnpm.cjs --filter dsh-python-runtime-closure deploy`)
-    expect(result.stdout).toContain(`${process.execPath} C:\\tools\\pnpm.cjs dlx @yao-pkg/pkg@6.21.0`)
+    expect(result.stdout).toContain(`${process.execPath} C:\\tools\\pnpm.cjs exec pkg`)
     expect(result.stdout).not.toMatch(/pnpm\.cmd/i)
   })
 

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

@@ -23,8 +23,6 @@ const ENTRY_BIN = 'node_modules/@deepseek-ai/dsh/lib/bin.js'
 const OUTPUT_BASENAME = 'deepseek-harness-sdk-runtime'
 /** Default Node major; SEA mode requires at least Node 22. */
 const DEFAULT_NODE_RANGE = 'node24'
-/** Pinned for reproducible builds. */
-const PKG_SPEC = '@yao-pkg/pkg@6.21.0'
 const OUT_DIR = 'dist-exe'
 /** Python package destination; created when absent. */
 const PYTHON_RUNTIME_DIR = 'python/sdk-runtime/src/deepseek_harness_runtime/runtime'
@@ -215,7 +213,7 @@ class BuildCli {
       '  --dry-run              print every command and config patch without executing.',
       '  --help                 print this help.',
       '',
-      `Build route: ${PKG_SPEC} --sea; see .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md.`,
+      'Build route: @yao-pkg/pkg --sea (root devDependency, pnpm-patched); see .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md.',
       `Stages the node carrier in ${PYTHON_RUNTIME_DIR}/${PYTHON_NODE_SUBDIR} and writes executables to ${OUT_DIR}/.`,
     ].join('\n')
   }
@@ -426,8 +424,8 @@ class SingleExeBuild {
     await this.prepareNativePty(target)
     if (!this.cli.dryRun) await mkdir(this.outDir, { recursive: true })
     await this.runPnpm(`pkg ${target.spec}`, [
-      'dlx',
-      PKG_SPEC,
+      'exec',
+      'pkg',
       this.staging,
       '--sea',
       '--targets',

+ 0 - 28
scripts/gen-third-party-notices.ts

@@ -89,17 +89,6 @@ const PYTHON_METADATA: Record<string, { license: string; repo: string; role: str
 
 type PythonMetadata = typeof PYTHON_METADATA
 
-/** Tools fetched by scripts at build time, keyed by the pin the script owns. */
-const BUILD_TIME_TOOLS = [
-  {
-    name: '@yao-pkg/pkg',
-    license: 'MIT',
-    repo: 'https://github.com/yao-pkg/pkg',
-    role: 'invoked by `scripts/build-exe-for-python-sdk.ts` to assemble the single-file SDK runtime executable',
-    pinSource: 'scripts/build-exe-for-python-sdk.ts',
-  },
-]
-
 /** The `package.json` fields this generator reads. */
 export interface Manifest {
   name?: string
@@ -602,16 +591,6 @@ function collectPatched(): { spec: string; patch: string }[] {
   return Object.entries(workspace.patchedDependencies ?? {}).map(([spec, patch]) => ({ spec, patch }))
 }
 
-/** Verify each build-time tool pin still appears in its owning script. */
-function verifyBuildTimePins(): void {
-  for (const tool of BUILD_TIME_TOOLS) {
-    const text = readFileSync(resolve(root, tool.pinSource), 'utf8')
-    if (!text.includes(tool.name)) {
-      throw new Error(`gen-third-party-notices: ${tool.pinSource} no longer references ${tool.name}; update BUILD_TIME_TOOLS.`)
-    }
-  }
-}
-
 /** SPDX identifiers this project may ship without further review. */
 const PERMISSIVE_LICENSES = new Set(['MIT', 'ISC', 'BSD-2-Clause', 'BSD-3-Clause', 'Apache-2.0', '0BSD', 'Unlicense', 'CC0-1.0', 'BlueOak-1.0.0', 'Python-2.0'])
 
@@ -692,7 +671,6 @@ ${rows.join('\n')}
  * @returns the exact bytes `THIRD_PARTY_NOTICES.md` must hold.
  */
 export function render(): string {
-  verifyBuildTimePins()
   // The linked-manifest cache is keyed by name only, so it must not outlive
   // the manifests map it was resolved from; render() owns that single load.
   workspaceLinkedManifestCache.clear()
@@ -765,12 +743,6 @@ Direct dependencies of the \`pyproject.toml\` manifests, plus \`uv\` as the deve
 ${python.map(dep => `| [\`${dep.name}\`](${dep.repo}) | ${dep.license} | ${dep.role} |`).join('\n')}
 | [\`uv\`](https://github.com/astral-sh/uv) | MIT / Apache-2.0 | development workflow tool |
 
-## Fetched at build time
-
-| Package | License | Role |
-| --- | --- | --- |
-${BUILD_TIME_TOOLS.map(tool => `| [\`${tool.name}\`](${tool.repo}) | ${tool.license} | ${tool.role} |`).join('\n')}
-
 ## First-party native packages
 
 \`@deepseek-ai/node-addon-landlock-run\` (and its platform packages) is built and released from this repository under BSD 3-Clause. It is listed here for completeness; it is first-party, not third-party.

Some files were not shown because too many files changed in this diff