Kaynağa Gözat

refactor(native): expose Landlock through its capability subpath

imccyu 2 hafta önce
ebeveyn
işleme
97e7223d5f
34 değiştirilmiş dosya ile 102 ekleme ve 46 silme
  1. 2 2
      .agents/notes/implemented/architecture/2026-09-07-prebuilt-system-primitives.i18n.yaml
  2. 2 2
      .agents/notes/implemented/architecture/2026-09-07-prebuilt-system-primitives.md
  3. 2 2
      .agents/notes/implemented/architecture/2026-09-07-prebuilt-system-primitives.zh.md
  4. 1 1
      native/system/AGENTS.md
  5. 2 2
      native/system/README.i18n.yaml
  6. 1 1
      native/system/README.md
  7. 1 1
      native/system/README.zh.md
  8. 2 2
      native/system/docs/architecture.md
  9. 2 2
      native/system/docs/packaging.md
  10. 2 2
      native/system/packages/entry/README.i18n.yaml
  11. 1 1
      native/system/packages/entry/README.md
  12. 1 1
      native/system/packages/entry/README.zh.md
  13. 1 3
      native/system/packages/entry/package.json
  14. 5 1
      native/system/scripts/verify-packed-install.mjs
  15. 5 1
      native/system/test/entry.test.js
  16. 1 1
      native/system/test/launcher.test.js
  17. 50 2
      native/system/test/package-matrix.test.js
  18. 3 2
      packages/experimental/webworker-packer/tests/image-loadable.spec.ts
  19. 2 2
      packages/experimental/webworker-runtime/README.i18n.yaml
  20. 1 1
      packages/experimental/webworker-runtime/README.md
  21. 1 1
      packages/experimental/webworker-runtime/README.zh.md
  22. 1 1
      packages/experimental/webworker-runtime/tests/node/child-process.spec.ts
  23. 1 0
      packages/experimental/webworker-runtime/tests/node/node-stubs.spec.ts
  24. 2 2
      packages/sandbox/sandbox-local/README.i18n.yaml
  25. 1 1
      packages/sandbox/sandbox-local/README.md
  26. 1 1
      packages/sandbox/sandbox-local/README.zh.md
  27. 1 1
      packages/sandbox/sandbox-local/src/index.ts
  28. 1 1
      packages/sandbox/sandbox-local/src/profiles.ts
  29. 1 1
      packages/sandbox/sandbox-local/tests/landlock.e2e.ts
  30. 1 1
      packages/sandbox/sandbox-local/tests/local.spec.ts
  31. 1 1
      packages/sandbox/sandbox-local/tests/packed-install.e2e.ts
  32. 1 1
      packages/shell/bash-sandbox/tests/landlock.e2e.ts
  33. 1 1
      packages/shell/bash-sandbox/tests/partial-landlock.spec.ts
  34. 1 1
      tsconfig.base.json

+ 2 - 2
.agents/notes/implemented/architecture/2026-09-07-prebuilt-system-primitives.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-07-prebuilt-system-primitives.md
-2026-09-07-prebuilt-system-primitives.md: 23037c2d0ebc0871125cf8057459cc3930bcc57a
-2026-09-07-prebuilt-system-primitives.zh.md: 4e1e6cc061d6169078c25cf6092965edd553f7e8
+2026-09-07-prebuilt-system-primitives.md: 183cd3ea984cd779417493f0de17a770f38fc001
+2026-09-07-prebuilt-system-primitives.zh.md: f051bba92910086601aa48a877f360e276df524d

+ 2 - 2
.agents/notes/implemented/architecture/2026-09-07-prebuilt-system-primitives.md

@@ -12,9 +12,9 @@ The JSONL writer's `fs-ext` dependency compiled a NAN addon during consumer inst
 
 The independently versioned `@deepseek-ai/node-addon-system` family in [native/system](../../../../native/system/README.md) distributes the existing `landlock-run` executable and a stable Node-API v8 `system.node` addon. Platform packages select OS and CPU; Linux carries distinct glibc and musl addon files. macOS carries the addon without a Landlock executable. Neither the entry nor platform packages compile during installation.
 
-The root JavaScript entry retains Landlock's API and [CLI protocol](../../../../native/system/docs/cli-contract.md). The `./flock` entry loads its addon only when `tryLockExclusive(fd)` is called. It runs `flock(fd, LOCK_EX | LOCK_NB)` in asynchronous native work and captures errno on that worker. The caller owns the descriptor through completion and releases its lock by closing it. Missing bindings reject acquisition rather than granting an unprotected lock.
+The package has no root export. The `./landlock-run` JavaScript entry retains Landlock's API and [CLI protocol](../../../../native/system/docs/cli-contract.md). The `./flock` entry loads its addon only when `tryLockExclusive(fd)` is called. It runs `flock(fd, LOCK_EX | LOCK_NB)` in asynchronous native work and captures errno on that worker. The caller owns the descriptor through completion and releases its lock by closing it. Missing bindings reject acquisition rather than granting an unprotected lock.
 
-The [Session write-lease decision](../feature/2026-08-31-cross-process-session-write-lease.md) continues to own acquisition timing, inode checks, close ownership, and crash semantics. Windows retains its existing koffi semaphore. The browser worker substitutes only the flock subpath; it continues to use the root Landlock JavaScript API.
+The [Session write-lease decision](../feature/2026-08-31-cross-process-session-write-lease.md) continues to own acquisition timing, inode checks, close ownership, and crash semantics. Windows retains its existing koffi semaphore. The browser worker substitutes only the flock subpath; it uses the unchanged `./landlock-run` JavaScript API.
 
 Source builds explicitly compile the host addon before repository tests and builds that need it. Native CI builds the complete platform payload and tests the same addon bytes across Node releases; Linux also exercises the musl payload in Alpine. Platform prepack rejects malformed or incomplete binaries, and an offline npm install rehearsal checks installed bytes and real lock behavior. Native [tests](../../../../native/system/test/flock.test.js) cover descriptor/process contention, close and crash release, independent errno values, and worker teardown.
 

+ 2 - 2
.agents/notes/implemented/architecture/2026-09-07-prebuilt-system-primitives.zh.md

@@ -12,9 +12,9 @@ JSONL 写入方依赖的 `fs-ext` 在用户安装时编译 NAN addon。因此,
 
 [native/system](../../../../native/system/README.zh.md) 中独立版本的 `@deepseek-ai/node-addon-system` 包族分发既有 `landlock-run` 可执行文件和使用稳定 Node-API v8 的 `system.node` addon。平台包按操作系统和 CPU 选择;Linux 分别携带 glibc 与 musl addon 文件。macOS 携带 addon,但不包含 Landlock 可执行文件。入口包和平台包都不在安装期间编译。
 
-根 JavaScript 入口保留 Landlock API 和 [CLI 协议](../../../../native/system/docs/cli-contract.md)。`./flock` 入口仅在调用 `tryLockExclusive(fd)` 时加载 addon。它在异步原生工作中执行 `flock(fd, LOCK_EX | LOCK_NB)`,并在该工作线程保存 errno。调用方在完成前持有描述符,并通过关闭它释放锁。绑定缺失时拒绝获取锁,不授予没有保护的锁。
+包不提供导出。`./landlock-run` JavaScript 入口保留 Landlock API 和 [CLI 协议](../../../../native/system/docs/cli-contract.md)。`./flock` 入口仅在调用 `tryLockExclusive(fd)` 时加载 addon。它在异步原生工作中执行 `flock(fd, LOCK_EX | LOCK_NB)`,并在该工作线程保存 errno。调用方在完成前持有描述符,并通过关闭它释放锁。绑定缺失时拒绝获取锁,不授予没有保护的锁。
 
-[Session 写租约决策](../feature/2026-08-31-cross-process-session-write-lease.zh.md) 继续负责获取时机、inode 校验、关闭所有权和崩溃语义。Windows 保留既有 koffi 信号量。浏览器 worker 仅替换 flock 子路径,继续使用根 Landlock JavaScript API。
+[Session 写租约决策](../feature/2026-08-31-cross-process-session-write-lease.zh.md) 继续负责获取时机、inode 校验、关闭所有权和崩溃语义。Windows 保留既有 koffi 信号量。浏览器 worker 仅替换 flock 子路径,使用未经修改的 `./landlock-run` JavaScript API。
 
 源码构建在需要 addon 的仓库测试与构建之前显式编译当前宿主 addon。Native CI 构建完整平台产物,并让相同 addon 字节跨 Node 版本测试;Linux 还在 Alpine 中执行 musl 产物。平台 prepack 拒绝格式错误或不完整的二进制,离线 npm 安装演练检查安装字节与真实锁行为。Native [测试](../../../../native/system/test/flock.test.js) 覆盖描述符与进程竞争、关闭和崩溃释放、独立 errno 值及 worker 清理。
 

+ 1 - 1
native/system/AGENTS.md

@@ -8,7 +8,7 @@ This workspace owns `@deepseek-ai/node-addon-system`: the Linux `landlock-run` c
 - The launcher uses only libc, statically linked against musl. Its kernel UAPI definitions remain in the reviewed C source.
 - Node bindings use stable Node-API v8, never NAN, V8 C++ APIs, or experimental Node interfaces. Linux glibc and musl addons are distinct binaries; macOS has its own Mach-O bundle.
 - The flock binding attempts only `LOCK_EX | LOCK_NB` in asynchronous work and captures errno on that worker. The caller owns the fd through completion and releases its lock by closing it.
-- Root entry imports must not load the addon. `./flock` loads it only when called; Windows retains the Harness's existing semaphore implementation.
+- `./landlock-run` and `./flock` are independent capability exports; the package has no root export. Neither import loads the addon. `./flock` loads it only when called; Windows retains the Harness's existing semaphore implementation.
 - Runtime binary selection has no environment-variable overrides. `NALR_REQUIRE_LANDLOCK` is a test-only enforcement requirement.
 - There is no install-time compile fallback. Missing Landlock binaries probe unusable; missing flock bindings reject acquisition, never silently grant a lock.
 

+ 2 - 2
native/system/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write native/system/README.md
-README.md: 674e47c79726d0212ab17468d90e0a73e6c63db6
-README.zh.md: b9770453e25620add25a67d93a89abd0f41c6554
+README.md: 5a01754cb96fabbdfd2dbf4f068f19addd7a2347
+README.zh.md: dabd00cecd05ded13a50d2efd000985afed713d4

+ 1 - 1
native/system/README.md

@@ -18,7 +18,7 @@ Use the Linux `landlock-run` executable to confine subprocesses, or the `./flock
 
 ## Use
 
-The root entry exports `launcherPath`, `probe`, and `grantArgs` for Landlock. Its executable name, flags, and failure semantics are defined by the [CLI contract](docs/cli-contract.md).
+`@deepseek-ai/node-addon-system/landlock-run` exports `launcherPath`, `probe`, and `grantArgs` for Landlock. Its executable name, flags, and failure semantics are defined by the [CLI contract](docs/cli-contract.md).
 
 The [flock behavior contract](docs/flock-contract.md) maps descriptor, process, and advisory-lock semantics to independent native tests.
 

+ 1 - 1
native/system/README.zh.md

@@ -18,7 +18,7 @@ kind: "package-library"
 
 ## Use
 
-根入口为 Landlock 导出 `launcherPath`、`probe` 和 `grantArgs`。其可执行文件名、参数和失败语义由 [CLI 约定](docs/cli-contract.md) 定义。
+`@deepseek-ai/node-addon-system/landlock-run` 为 Landlock 导出 `launcherPath`、`probe` 和 `grantArgs`。其可执行文件名、参数和失败语义由 [CLI 约定](docs/cli-contract.md) 定义。
 
 [flock 行为约定](docs/flock-contract.md) 将描述符、进程和咨询式锁语义对应到独立原生测试。
 

+ 2 - 2
native/system/docs/architecture.md

@@ -4,9 +4,9 @@ The system package family supplies native mechanisms to Node callers: a Linux co
 
 ## Package family
 
-The ESM entry `@deepseek-ai/node-addon-system` and its optional platform packages share one version. Platform metadata chooses the operating system and CPU; each package's `prebuilds.json` declares the files it must contain.
+The ESM package `@deepseek-ai/node-addon-system` and its optional platform packages share one version. Platform metadata chooses the operating system and CPU; each package's `prebuilds.json` declares the files it must contain.
 
-The root entry owns the existing Landlock path resolution, grant argv, and functional probe. It does not load native addons. The `./flock` entry lazily loads `system.node` only when `tryLockExclusive(fd)` is called. Importing either JavaScript entry therefore works without a matching native payload.
+The `./landlock-run` entry owns Landlock path resolution, grant argv, and the functional probe. It does not load native addons. The `./flock` entry lazily loads `system.node` only when `tryLockExclusive(fd)` is called. Importing either JavaScript entry therefore works without a matching native payload. The package exposes these capability subpaths and its manifest, without a root export.
 
 ## Separate mechanisms
 

+ 2 - 2
native/system/docs/packaging.md

@@ -4,7 +4,7 @@ The family publishes one ESM entry package plus OS/CPU-selected optional platfor
 
 ## Payloads
 
-The entry exports the Landlock API at its root and the asynchronous lock API at `./flock`, with C sources included for auditability. Platform packages contain no JavaScript.
+The entry package exports the Landlock API at `./landlock-run` and the asynchronous lock API at `./flock`, with C sources included for auditability. There is no root export. Platform packages contain no JavaScript.
 
 - Linux: `bin/landlock-run`, `bin/glibc/system.node`, and `bin/musl/system.node`.
 - macOS: `bin/system.node`.
@@ -15,7 +15,7 @@ The entry exports the Landlock API at its root and the asynchronous lock API at
 
 Neither entry nor platform packages have installation lifecycle scripts. The entry resolves its matching optional package when a native operation needs it. Optional means that the package manager selects a platform, not that a requested lock can succeed without its binding.
 
-The root Landlock API stays importable without native payloads and reports unavailable enforcement through its probe. The flock entry is also lazy at import; acquisition reports a missing or unloadable addon instead of compiling or granting an unprotected lock.
+The `./landlock-run` API stays importable without native payloads and reports unavailable enforcement through its probe. The flock entry is also lazy at import; acquisition reports a missing or unloadable addon instead of compiling or granting an unprotected lock.
 
 ## Pack verification
 

+ 2 - 2
native/system/packages/entry/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write native/system/packages/entry/README.md
-README.md: af3f5ac4df5883d7d9b4f65d273f9d45fae874e8
-README.zh.md: 55f428568cb7b5e7a9956c842931f52e0feeaf0a
+README.md: 8f6a4d341c8f9e4c104e5706e08d9a7d45c065e1
+README.zh.md: 152257916df0cf40511b9743d389ae753ab6c85c

+ 1 - 1
native/system/packages/entry/README.md

@@ -6,7 +6,7 @@ kind: "package-library"
 
 English | [中文](README.zh.md)
 
-The root entry exports the Landlock launcher path, enforcement probe, grant arguments, and protocol constants. Its independent `./flock` entry exports `tryLockExclusive(fd): Promise<void>`; importing either entry does not load `system.node`.
+The `./landlock-run` entry exports the Landlock launcher path, enforcement probe, grant arguments, and protocol constants. The independent `./flock` entry exports `tryLockExclusive(fd): Promise<void>`; importing either entry does not load `system.node`. The package has no root export.
 
 The lock operation attempts `LOCK_EX | LOCK_NB` asynchronously. Keep the caller-owned descriptor open until completion; contention rejects with `EAGAIN`/`EWOULDBLOCK`, other syscall failures also reject, and errors carry their code, positive errno, and `syscall: 'flock'`. Native setup errors reject the same promise. Closing the last descriptor for the open file description releases the lock. The binding does not open, duplicate, close, or explicitly unlock descriptors.
 

+ 1 - 1
native/system/packages/entry/README.zh.md

@@ -6,7 +6,7 @@ kind: "package-library"
 
 [English](README.md) | 中文
 
-入口导出 Landlock 启动器路径、强制执行探测、授权参数和协议常量。独立的 `./flock` 入口导出 `tryLockExclusive(fd): Promise<void>`;导入任一入口都不会加载 `system.node`。
+`./landlock-run` 入口导出 Landlock 启动器路径、强制执行探测、授权参数和协议常量。独立的 `./flock` 入口导出 `tryLockExclusive(fd): Promise<void>`;导入任一入口都不会加载 `system.node`。包不提供根导出。
 
 锁操作异步尝试 `LOCK_EX | LOCK_NB`。在完成前保持调用方拥有的描述符打开;竞争以 `EAGAIN`/`EWOULDBLOCK` 拒绝,其他系统调用失败也会拒绝,错误携带 code、正 errno 和 `syscall: 'flock'`。原生调用准备阶段的错误也会拒绝同一个 promise。关闭该打开文件描述的最后一个描述符即释放锁。绑定不打开、复制、关闭或显式解锁描述符。
 

+ 1 - 3
native/system/packages/entry/package.json

@@ -8,10 +8,8 @@
     "url": "git+https://github.com/deepseek-harness/deepseek-harness.git",
     "directory": "native/system/packages/entry"
   },
-  "main": "lib/index.js",
-  "types": "lib/index.d.ts",
   "exports": {
-    ".": {
+    "./landlock-run": {
       "types": "./lib/index.d.ts",
       "default": "./lib/index.js"
     },

+ 5 - 1
native/system/scripts/verify-packed-install.mjs

@@ -166,9 +166,13 @@ import { spawnSync } from 'node:child_process';
 import fs from 'node:fs';
 import os from 'node:os';
 import path from 'node:path';
-import { grantArgs, launcherPath, probe } from '@deepseek-ai/node-addon-system';
+import { grantArgs, launcherPath, probe } from '@deepseek-ai/node-addon-system/landlock-run';
 import { tryLockExclusive } from '@deepseek-ai/node-addon-system/flock';
 
+await assert.rejects(import('@deepseek-ai/node-addon-system'), {
+  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED',
+});
+
 const requireLandlock = process.env.NALR_REQUIRE_LANDLOCK === '1';
 const platformPackage = '@deepseek-ai/node-addon-system-' + process.platform + '-' + process.arch;
 const resolved = launcherPath();

+ 5 - 1
native/system/test/entry.test.js

@@ -15,7 +15,11 @@ import {
   grantArgs,
   launcherPath,
   probe,
-} from '@deepseek-ai/node-addon-system';
+} from '@deepseek-ai/node-addon-system/landlock-run';
+
+await assert.rejects(import('@deepseek-ai/node-addon-system'), {
+  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED',
+});
 
 // --- constants are part of the CLI contract ---
 assert.equal(LAUNCHER_BIN, 'landlock-run');

+ 1 - 1
native/system/test/launcher.test.js

@@ -22,7 +22,7 @@ import {
   grantArgs,
   launcherPath,
   probe,
-} from '@deepseek-ai/node-addon-system';
+} from '@deepseek-ai/node-addon-system/landlock-run';
 
 const FATAL_PREFIX = 'landlock-run: ';
 const PARTIAL_NOTICE = 'landlock-run: partial enforcement (older Landlock ABI)';

+ 50 - 2
native/system/test/package-matrix.test.js

@@ -7,6 +7,54 @@ import { spawnSync } from 'node:child_process';
 import { fileURLToPath } from 'node:url';
 import { verifyPlatformBinaries } from '../scripts/repo.mjs';
 
+test('the real Landlock subpath imports without platform packages or dlopen, while the root is unexported', { timeout: 120_000 }, (t) => {
+  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'system-landlock-entry-'));
+  t.after(() => fs.rmSync(dir, { recursive: true, force: true }), { timeout: 120_000 });
+  const entry = fileURLToPath(new URL('../packages/entry/', import.meta.url));
+  const installed = path.join(dir, 'node_modules', '@deepseek-ai', 'node-addon-system');
+  fs.mkdirSync(installed, { recursive: true });
+  fs.copyFileSync(path.join(entry, 'package.json'), path.join(installed, 'package.json'));
+  // Only the real entry payload is present; no platform package or addon is copied.
+  fs.cpSync(path.join(entry, 'lib'), path.join(installed, 'lib'), { recursive: true });
+  const manifest = JSON.parse(fs.readFileSync(path.join(installed, 'package.json'), 'utf8'));
+  assert.equal(manifest.main, undefined);
+  assert.equal(manifest.types, undefined);
+
+  const result = spawnSync(process.execPath, ['--no-addons', '--input-type=module', '--eval', `
+    import assert from 'node:assert/strict';
+    import { createRequire } from 'node:module';
+    const originalDlopen = process.dlopen;
+    let dlopenCalls = 0;
+    try {
+      process.dlopen = () => {
+        dlopenCalls++;
+        throw new Error('Landlock import attempted dlopen');
+      };
+      const api = await import('@deepseek-ai/node-addon-system/landlock-run');
+      assert.equal(api.LAUNCHER_BIN, 'landlock-run');
+      assert.deepEqual(api.grantArgs({}), []);
+      assert.equal(dlopenCalls, 0);
+      await assert.rejects(import('@deepseek-ai/node-addon-system'), {
+        code: 'ERR_PACKAGE_PATH_NOT_EXPORTED',
+      });
+      assert.throws(() => createRequire(import.meta.url).resolve('@deepseek-ai/node-addon-system'), {
+        code: 'ERR_PACKAGE_PATH_NOT_EXPORTED',
+      });
+    } finally {
+      process.dlopen = originalDlopen;
+    }
+  `], {
+    cwd: dir,
+    encoding: 'utf8',
+    timeout: 120_000,
+    env: Object.fromEntries(Object.entries(process.env)
+      .filter(([key]) => !/KEY|TOKEN|SECRET|PASSWORD|^NODE_PATH$/i.test(key))),
+  });
+  assert.equal(result.error, undefined);
+  assert.equal(result.signal, null, result.stderr);
+  assert.equal(result.status, 0, result.stderr);
+});
+
 // These minimal headers exercise format rejection, not executable behavior.
 // flock.test.js and packed-install verification execute the real addon.
 function fixture(t, { platform = 'linux', arch = 'x64', kind = 'node-api' } = {}) {
@@ -106,14 +154,14 @@ test('rejects a symbolic-link payload', { skip: process.platform === 'win32' },
   assert.throws(() => verifyPlatformBinaries(f.dir), /not a regular file/);
 });
 
-test('entry prepack rejects a missing exported flock file even when the root entry exists', (t) => {
+test('entry prepack rejects a missing exported flock file even when the Landlock entry exists', (t) => {
   const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'system-entry-'));
   t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
   fs.mkdirSync(path.join(dir, 'lib'));
   fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({
     name: 'entry-fixture',
     exports: {
-      '.': { types: './lib/index.d.ts', default: './lib/index.js' },
+      './landlock-run': { types: './lib/index.d.ts', default: './lib/index.js' },
       './flock': { types: './lib/flock.d.ts', default: './lib/flock.js' },
     },
   }));

+ 3 - 2
packages/experimental/webworker-packer/tests/image-loadable.spec.ts

@@ -36,6 +36,7 @@ const repoRoot = fileURLToPath(new URL('../../../../', import.meta.url))
 /** A leaf workspace package: real build output, no dependencies to drag in. */
 const SUBJECT = '@deepseek-ai/dsh-timeout'
 const LANDLOCK = '@deepseek-ai/node-addon-system'
+const LANDLOCK_ENTRY = `${LANDLOCK}/landlock-run`
 const PLUGIN_INVENTORY = '@deepseek-ai/dsh-plugin-package-inventory-deepseek'
 const WEB_SERVER = '@deepseek-ai/dsh-host-webserver'
 
@@ -240,7 +241,7 @@ const archive = async (): Promise<Uint8Array> =>
     expect(result.packages.has(LANDLOCK)).toBe(true)
     expect(result.missing).toEqual([])
     expect(Object.hasOwn(result.files, `node_modules/${LANDLOCK}/lib/index.js`)).toBe(true)
-    expect(createNodeBuiltins()[LANDLOCK]).toBeUndefined()
+    expect(createNodeBuiltins()[LANDLOCK_ENTRY]).toBeUndefined()
 
     const vfs = loadVfsImage(await inflateImage(result.image, 'the packed Landlock package'), DEFAULT_ROOT)
     const loader = new WorkerModuleLoader({
@@ -251,7 +252,7 @@ const archive = async (): Promise<Uint8Array> =>
     })
     setActiveVfs(vfs)
     setActiveModuleLoader(loader)
-    const landlock = loader.requireFrom(`${DEFAULT_ROOT}/workspace`)(LANDLOCK) as {
+    const landlock = loader.requireFrom(`${DEFAULT_ROOT}/workspace`)(LANDLOCK_ENTRY) as {
       LAUNCHER_BIN: string
       LAUNCHER_FAILURE_EXIT: number
       launcherPath(): string

+ 2 - 2
packages/experimental/webworker-runtime/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/experimental/webworker-runtime/README.md
-README.md: 0bfac2891e14d7c6f9947c62acf968bef8220565
-README.zh.md: b31fd325c79c82d3992a62cbe4c9fb8c5506211b
+README.md: df61667329deacf99c320cd4121715115403d6f5
+README.zh.md: 7faff0584764978ba30cb8f832adf25a98237500

+ 1 - 1
packages/experimental/webworker-runtime/README.md

@@ -50,7 +50,7 @@ None; this package neither assembles nor sends a provider request.
 - **The worker composition writes plaintext session logs** (`compression: 'none'` boot patch): it carries no Zstandard codec, so exported logs are `.jsonl`, never `.jsonl.zstd`.
 - **`node:dns/promises`, `node:vm`, `node:net`, `node:sqlite`, `node:worker_threads` are structural stubs**: every call reports its refusal on the console and throws. Rows needing native DNS, a real process, or realm isolation cannot run here.
 - **Filesystem watchers observe only the mounted VFS**: image seeding is silent and the VFS has no symlinks or external writers. `persistent`, `ref()`, and `unref()` preserve the Node API but cannot control a dedicated Worker's lifetime because browsers expose no ref-counted event loop.
-- **Worker confinement is a VFS boundary, not kernel Landlock**: `read-only` and `workspace-write` run the unchanged `@deepseek-ai/node-addon-system` JavaScript and launcher argv, but the process layer implements the logical `landlock-run` executable and enforces its grants on every shell filesystem request. `full` therefore covers the Worker command table and mounted VFS only; it does not claim arbitrary native-process execution or Linux kernel isolation.
+- **Worker confinement is a VFS boundary, not kernel Landlock**: `read-only` and `workspace-write` run the unchanged `@deepseek-ai/node-addon-system/landlock-run` JavaScript and launcher argv, but the process layer implements the logical `landlock-run` executable and enforces its grants on every shell filesystem request. `full` therefore covers the Worker command table and mounted VFS only; it does not claim arbitrary native-process execution or Linux kernel isolation.
 - **The worker bundle pins a path inside `@yarnpkg/parsers`** — the build resolves the package's own `lib/shell.js` instead of its root, whose barrel also re-exports the Syml parser and so drags js-yaml into a bundle that never parses that format (around 175 kB, plus its module body at worker start). The path is derived from the package manifest, so a layout change fails the build rather than reinstating the barrel; upgrading the dependency means re-checking that the shell parser still lives there.
 - **The shell is not bash**: no loops, functions, `case`, job control, or process substitution — the grammar stops at pipelines, `&&`/`||`, subshells, groups, redirections, and expansion. `&` runs its command to completion in place, `sed` accepts only substitution scripts, patterns are JavaScript regular expressions, and the command table holds coreutils only (no `git`, no network tools).
 - **A shell process has no synchronous filesystem**: it reads and writes the host's VFS by message, because blocking on a reply would need `SharedArrayBuffer`, which requires a cross-origin isolation GitHub Pages cannot grant. Directory-walking commands therefore cost one round trip per entry, and two concurrent commands can interleave their writes.

+ 1 - 1
packages/experimental/webworker-runtime/README.zh.md

@@ -50,7 +50,7 @@ kind: "package-library"
 - **worker 组合写明文会话日志**(`compression: 'none'` boot patch):不带 Zstandard 编解码器,导出日志是 `.jsonl`,不会是 `.jsonl.zstd`。
 - **`node:dns/promises`、`node:vm`、`node:net`、`node:sqlite`、`node:worker_threads` 是结构化 stub**:每次调用在 console 报告拒绝并抛出。需要原生 DNS、真进程或真 realm 隔离的行在此无法运行。
 - **文件 watcher 只能观察已挂载的 VFS**:镜像 seed 不产生事件,VFS 也没有符号链接或外部写入方。`persistent`、`ref()` 和 `unref()` 保留 Node API,但浏览器没有引用计数事件循环,因此这些接口不能控制 dedicated Worker 的生存期。
-- **Worker confinement 是 VFS 边界,不是内核 Landlock**:`read-only` 和 `workspace-write` 运行未经修改的 `@deepseek-ai/node-addon-system` JavaScript 与 launcher argv,进程层则实现逻辑 `landlock-run` 可执行文件,并在 shell 的每次文件系统请求上执行其授权。`full` 仅覆盖 Worker 命令表和已挂载 VFS,不表示能够执行任意 native 进程,也不表示 Linux 内核隔离。
+- **Worker confinement 是 VFS 边界,不是内核 Landlock**:`read-only` 和 `workspace-write` 运行未经修改的 `@deepseek-ai/node-addon-system/landlock-run` JavaScript 与 launcher argv,进程层则实现逻辑 `landlock-run` 可执行文件,并在 shell 的每次文件系统请求上执行其授权。`full` 仅覆盖 Worker 命令表和已挂载 VFS,不表示能够执行任意 native 进程,也不表示 Linux 内核隔离。
 - **worker 束钉住了 `@yarnpkg/parsers` 的包内路径**——构建解析到该包自己的 `lib/shell.js` 而非包根,因为包根 barrel 还 re-export 了 Syml 解析器,会把 js-yaml 拖进一个从不解析该格式的束(约 175 kB,外加 worker 启动时的模块体求值)。该路径由包 manifest 派生,包内布局一变即构建期失败、不会静默退回 barrel;升级这个依赖时须复核 shell 解析器是否仍在那里。
 - **这个 shell 不是 bash**:没有循环、函数、`case`、作业控制或进程替换——语法止步于管道、`&&`/`||`、子 shell、group、重定向与展开。`&` 会就地把命令跑完,`sed` 只接受替换脚本,模式是 JavaScript 正则,命令表只有 coreutils(没有 `git`,没有网络工具)。
 - **shell 进程没有同步文件面**:它靠消息读写宿主的 VFS,因为阻塞等待回帧需要 `SharedArrayBuffer`,而那要求 GitHub Pages 给不了的跨源隔离。因此目录遍历类命令每个条目一次往返,并发的两条命令写入可以交错。

+ 1 - 1
packages/experimental/webworker-runtime/tests/node/child-process.spec.ts

@@ -18,7 +18,7 @@ import { setActiveVfs } from '@deepseek-ai/dsh-experimental-webworker-runtime/sr
 import { spawn, spawnSync } from '@deepseek-ai/dsh-experimental-webworker-runtime/src/node/builtin_modules/implemented/child_process.ts'
 import {
   LAUNCHER_FAILURE_EXIT, grantArgs, launcherPath, probe,
-} from '@deepseek-ai/node-addon-system'
+} from '@deepseek-ai/node-addon-system/landlock-run'
 import { processAlive, signalProcess } from '@deepseek-ai/dsh-experimental-webworker-runtime/src/node/process-table.ts'
 import { hostFileSystem } from '@deepseek-ai/dsh-experimental-webworker-runtime/src/shell/fs-access.ts'
 import {

+ 1 - 0
packages/experimental/webworker-runtime/tests/node/node-stubs.spec.ts

@@ -119,6 +119,7 @@ describe('replaced external packages', () => {
   it('lists the packages the loader serves from the bundle', () => {
     expect(REPLACED_EXTERNAL_PACKAGES).not.toContain('chokidar')
     expect(REPLACED_EXTERNAL_PACKAGES).not.toContain('@deepseek-ai/node-addon-system')
+    expect(REPLACED_EXTERNAL_PACKAGES).not.toContain('@deepseek-ai/node-addon-system/landlock-run')
     expect(REPLACED_EXTERNAL_PACKAGES).toContain('ws')
   })
 

+ 2 - 2
packages/sandbox/sandbox-local/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/sandbox/sandbox-local/README.md
-README.md: 16deeef790f28b232dc517784ca45ecb38749c6f
-README.zh.md: 4d163766ff6c8097c60cfb8376a21d2a9a441100
+README.md: 33853b68407c2d3294ad1323e47b5473419b0e12
+README.zh.md: 8d62c20c65e2d7555844a1ba81e13694c5c6a8df

+ 1 - 1
packages/sandbox/sandbox-local/README.md

@@ -74,7 +74,7 @@ Selection is by platform first, probes second: each platform has a runner chain
 
 The bwrap profile combines a read-only host root, a fresh `/dev`, and `/proc` from a private PID namespace — commands manage their descendants but cannot see host processes, so procfs magic links cannot bypass the mounts; `workspace-write` adds an ephemeral `/tmp` and a writable workspace bind. The [private-PID note](../../../.agents/notes/implemented/bug-fix/2026-08-06-bwrap-private-pid-namespace.md) records the boundary.
 
-The Landlock launcher ships as an npm-distributed native addon (`@deepseek-ai/node-addon-system`) that supplies the platform launcher, functional probe, and grant vocabulary; this provider maps mode to grants only, keeping path resolution and probe parsing with the versioned binary.
+The `@deepseek-ai/node-addon-system/landlock-run` API supplies the platform launcher, functional probe, and grant vocabulary; this provider maps mode to grants only, keeping path resolution and probe parsing with the versioned binary.
 
 The Seatbelt profile is allow-default with `(deny file-write*)` plus write allow-lists derived from the shared `writableRoots` helper, so exactly the mode's promised file effects are governed; every root is canonicalized because Seatbelt matches resolved paths (`/tmp` IS `/private/tmp`).
 

+ 1 - 1
packages/sandbox/sandbox-local/README.zh.md

@@ -74,7 +74,7 @@ kind: "package-reference"
 
 bwrap profile 组合只读宿主根目录、全新 `/dev` 与私有 PID 命名空间中的 `/proc`——命令可管理其后代,但看不到宿主进程,因此 procfs 魔法链接无法绕过挂载;`workspace-write` 另加临时的 `/tmp` 与可写工作区绑定挂载。[私有 PID 笔记](../../../.agents/notes/implemented/bug-fix/2026-08-06-bwrap-private-pid-namespace.zh.md)记录该边界。
 
-Landlock launcher 以 npm 分发的原生插件(`@deepseek-ai/node-addon-system`)提供平台 launcher、功能探测与授权词汇;此提供方只做模式到授权的映射,把路径解析与探测解析保留在带版本的 binary 中。
+`@deepseek-ai/node-addon-system/landlock-run` API 提供平台 launcher、功能探测与授权词汇;此提供方只做模式到授权的映射,把路径解析与探测解析保留在带版本的 binary 中。
 
 Seatbelt profile 默认允许,带 `(deny file-write*)` 与来自共享 `writableRoots` 辅助函数的写入 allow-list,因此恰好管辖模式承诺的文件操作;每个根目录都经过规范化,因为 Seatbelt 匹配解析后的路径(`/tmp` 就是 `/private/tmp`)。
 

+ 1 - 1
packages/sandbox/sandbox-local/src/index.ts

@@ -30,7 +30,7 @@ import {
   LAUNCHER_FAILURE_EXIT,
   launcherPath as landlockLauncherPath,
   probe as defaultProbeLandlock,
-} from '@deepseek-ai/node-addon-system'
+} from '@deepseek-ai/node-addon-system/landlock-run'
 import { Context } from '@deepseek-ai/cordis'
 import z from '@deepseek-ai/schemastery'
 import { SandboxProvider, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'

+ 1 - 1
packages/sandbox/sandbox-local/src/profiles.ts

@@ -4,7 +4,7 @@
  * @module @deepseek-ai/dsh-sandbox-local/profiles
  */
 
-import { grantArgs as landlockGrantArgs } from '@deepseek-ai/node-addon-system'
+import { grantArgs as landlockGrantArgs } from '@deepseek-ai/node-addon-system/landlock-run'
 import { writableRoots } from '@deepseek-ai/dsh-sandbox'
 import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
 

+ 1 - 1
packages/sandbox/sandbox-local/tests/landlock.e2e.ts

@@ -6,7 +6,7 @@ import { join } from 'node:path'
 import { afterEach, describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
 import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
-import { launcherPath } from '@deepseek-ai/node-addon-system'
+import { launcherPath } from '@deepseek-ai/node-addon-system/landlock-run'
 import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
 
 /**

+ 1 - 1
packages/sandbox/sandbox-local/tests/local.spec.ts

@@ -12,7 +12,7 @@ import { tmpdir } from 'node:os'
 import { join } from 'node:path'
 import { afterEach, describe, expect, it, vi } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import { LAUNCHER_FAILURE_EXIT } from '@deepseek-ai/node-addon-system'
+import { LAUNCHER_FAILURE_EXIT } from '@deepseek-ai/node-addon-system/landlock-run'
 import { SANDBOX_UNAVAILABLE, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
 import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
 import {

+ 1 - 1
packages/sandbox/sandbox-local/tests/packed-install.e2e.ts

@@ -109,7 +109,7 @@ describe.skipIf(!packable)('sandbox-local: packed-tarball distribution (publish-
       import { spawnSync } from 'node:child_process'
       import { existsSync } from 'node:fs'
       import { Context } from '@deepseek-ai/cordis'
-      import { launcherPath } from '@deepseek-ai/node-addon-system'
+      import { launcherPath } from '@deepseek-ai/node-addon-system/landlock-run'
       import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
       const ctx = new Context()
       await ctx.plugin(LocalSandboxProvider, {})

+ 1 - 1
packages/shell/bash-sandbox/tests/landlock.e2e.ts

@@ -5,7 +5,7 @@ import { homedir, tmpdir } from 'node:os'
 import { join } from 'node:path'
 import { afterEach, describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
-import { launcherPath } from '@deepseek-ai/node-addon-system'
+import { launcherPath } from '@deepseek-ai/node-addon-system/landlock-run'
 import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
 import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
 import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'

+ 1 - 1
packages/shell/bash-sandbox/tests/partial-landlock.spec.ts

@@ -10,7 +10,7 @@ import { join } from 'node:path'
 import { afterEach, describe, expect, it } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
 import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
-import { LAUNCHER_FAILURE_EXIT } from '@deepseek-ai/node-addon-system'
+import { LAUNCHER_FAILURE_EXIT } from '@deepseek-ai/node-addon-system/landlock-run'
 import { SANDBOX_UNAVAILABLE, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
 import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
 import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'

+ 1 - 1
tsconfig.base.json

@@ -37,7 +37,7 @@
       "@deepseek-ai/cordis-plugin-timer": ["./vendor/timer/src"],
       "@deepseek-ai/cordis-plugin-hmr": ["./vendor/hmr/src"],
       "@deepseek-ai/cordis-plugin-logger-console": ["./vendor/logger-console/src"],
-      "@deepseek-ai/node-addon-system": ["./native/system/packages/entry/src/index.ts"],
+      "@deepseek-ai/node-addon-system/landlock-run": ["./native/system/packages/entry/src/index.ts"],
       "@deepseek-ai/node-addon-system/flock": ["./native/system/packages/entry/src/flock.ts"],
       "@deepseek-ai/dsh-invariants": ["./packages/runtime-diagnostics/invariants/src/index.ts"],
       "@deepseek-ai/dsh-tool-call-timeout-policy": ["./packages/guard/timeout-policy/src"],