dsh-code-server-app 0.1.37 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -10,7 +10,7 @@
10
10
  > - Microsoft **open-source** extensions (Python, TypeScript debugger, ESLint, …) are mirrored on Open VSX and install normally by search;
11
11
  > - **If you need a proprietary Microsoft extension**: download the `.vsix` from the Marketplace page and install it manually with `code-server --install-extension <file>` (or drop it into `--extensions-dir`).
12
12
 
13
- A static profile plugin (npm package with host + client bundle) that **bundles** [code-server](https://github.com/coder/code-server) in the package itself (a pack-time artifact at `vendor/code-server`, with no install scripts and no postinstall) and runs it in place from the plugin directory. VS Code's inner dependencies and the prebuilt native modules are **all installed by the package manager together with the plugin** — no global npm install, no `bin` configuration, no profile config changes, no second install command.
13
+ A static profile plugin (npm package with host + client bundle) that ships [code-server](https://github.com/coder/code-server) as a **platform-specific sub-package dependency** (a pack-time artifact at `vendor/code-server` → `@jinsiyu/dshcs-code-server-<platform>-<arch>`, with no install scripts and no postinstall) and runs it outside the plugin directory. VS Code's inner dependencies and the prebuilt native modules are **all installed by the package manager together with the plugin** — no global npm install, no `bin` configuration, no profile config changes, no second install command.
14
14
 
15
15
  ## UI carrier (chosen by the DSH version, feature-detected at runtime)
16
16
 
@@ -59,51 +59,56 @@ cd C:\Users\User\Desktop\dsh-code-server-app
59
59
  pnpm install # dev deps (esbuild + motion); allowBuilds is explicit → no postinstall runs
60
60
  pnpm run build:client # src/factory.js → lib/client.js (not committed; must be built first)
61
61
  pnpm run vendor:check # optional: show the bundled code-server version vs npm latest
62
- pnpm run vendor:code-server # produce vendor/code-server (code-server + its own deps)
63
- node scripts/vendor-repacks.mjs --target win32-arm64,win32-x64 --pack # build/repack prebuilt natives
64
- pnpm run publish:repacks # publish @jinsiyu/dshcs-* and the platform aggregators
65
- pnpm pack # dsh-code-server-app-<version>.tgz (~59MB)
66
- npm publish dsh-code-server-app-<version>.tgz --access public # publish the plugin itself (same verified tarball)
62
+ pnpm run vendor:code-server # produce vendor/code-server (code-server + its own deps)
63
+ pnpm run repack:code-server -- --target win32-arm64,win32-x64 --pack # platform sub-packages (incl. argon2)
64
+ node scripts/vendor-repacks.mjs --target win32-arm64,win32-x64 --pack # build/repack the VS Code natives
65
+ pnpm run publish:repacks # publish every @jinsiyu/* sub-package
66
+ pnpm pack # ⑤ → dsh-code-server-app-<version>.tgz (~105KB)
67
+ npm publish dsh-code-server-app-<version>.tgz --access public # ⑥ publish the plugin itself (same verified tarball)
67
68
  ```
68
69
 
69
70
  | Goal | Command |
70
71
  |---|---|
71
- | **Build the latest code-server from npm** | `pnpm run vendor:latest` (= `--force`): snapshots `code-server@latest` into `vendor/code-server`; afterwards you **must** re-run `vendor-repacks.mjs` and republish the prebuilt natives |
72
+ | **Build the latest code-server from npm** | `pnpm run vendor:latest` (= `--force`): snapshots `code-server@latest` into `vendor/code-server`; afterwards you **must** re-run `repack:code-server` (republish the sub-packages) and `vendor-repacks.mjs` (the inner dependency versions change) |
72
73
  | **Pin a version** | `pnpm run vendor:code-server -- --version 4.136.2` |
73
74
  | **Snapshot from an existing tree** | `pnpm run vendor:code-server -- --from <code-server dir>` (seconds) |
75
+ | **Rebuild the code-server sub-packages** | `pnpm run repack:code-server -- --target win32-arm64,win32-x64 --pack` (x64 compiles argon2 and checks the PE machine type) |
74
76
  | **Rebuild prebuilt natives** | `node scripts/vendor-repacks.mjs [--from <existing full install>] --target win32-arm64,win32-x64 --pack` (without `--from` it npm-installs and compiles the source tree itself — slow) |
75
- | **Publish prebuilt natives** | `pnpm run publish:repacks` (`--dry-run` to preview; `--only <substr>` to filter; `--otp <code>` / `--limit N` for 2FA) |
77
+ | **Publish sub-packages** | `pnpm run publish:repacks` (`--dry-run` to preview; `--only <substr>` to filter; `--otp <code>` / `--limit N` for 2FA) |
76
78
  | **Publish the plugin itself** | `npm publish dsh-code-server-app-<version>.tgz --access public` (publishes the exact tarball that was verified; no re-packing) |
77
79
  | **Just report versions** | `pnpm run vendor:check` |
78
80
 
79
81
  > `pnpm pack`'s `prepack` runs the vendor-code-server script once; when `vendor/code-server` already exists it is
80
82
  > a **no-op that takes seconds**, so after ordinary code changes you can just run `pnpm pack` (it will never
81
83
  > silently upgrade code-server). Upgrading code-server requires an explicit `pnpm run vendor:latest`
82
- > (or `--force` / `--version`) **plus** republishing the prebuilt natives.
84
+ > (or `--force` / `--version`) **plus** republishing the sub-packages.
83
85
 
84
86
  ## Install the plugin (one command; all dependencies installed by the package manager)
85
87
 
86
88
  ```powershell
87
89
  # no postinstall in the package → no pnpm approve-builds / allowBuilds; one command installs everything
88
- dsh plugin --profile web add dsh-code-server-app@0.1.36
90
+ dsh plugin --profile web add dsh-code-server-app@0.1.37
89
91
  # a local tarball works the same way:
90
- dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app\dsh-code-server-app-0.1.36.tgz
92
+ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app\dsh-code-server-app-0.1.37.tgz
91
93
  ```
92
94
 
93
- Ready to use immediately — **no second step, no "Install environment", no install-guide modal**:
95
+ Ready to use immediately — **no second step, no "Install environment", no install-guide modal**.
96
+ Since 0.1.37 the main package is only **~105KB** (the plugin's own code); everything else is dependencies:
94
97
 
95
- - the `code-server` tree (`vendor/code-server/`) ships inside the plugin package and **runs in place** from the
96
- plugin directory (`<profile>\node_modules\dsh-code-server-app\vendor\code-server\out\node\entry.js`);
98
+ - **the code-server tree** (`lib/vscode` 196.9MB + its own 136 runtime dependencies) ships as a **platform-specific
99
+ sub-package** `@jinsiyu/dshcs-code-server-<platform>-<arch>@<code-server version>` (gated by `os`/`cpu`, listed in
100
+ the plugin's `optionalDependencies`) → pnpm picks the right one for the architecture; it runs from
101
+ `<profile>\node_modules\@jinsiyu\dshcs-code-server-win32-arm64\code-server`;
97
102
  - the **pure-JS part** of VS Code's inner dependencies (35 packages: xterm / katex / typescript / ws / tar …) is
98
103
  declared in the plugin's `dependencies` and installed by pnpm into the profile's `node_modules` (hoisted);
99
104
  - the **packages that need compiling** (node-pty / @vscode/sqlite3 / kerberos / koffi / ssh2 / cpu-features /
100
105
  @parcel/watcher / @vscode/windows-* … 16 in total) are provided as prebuilt `@jinsiyu/dshcs-*` packages,
101
106
  pulled back under their **original names** by the **platform aggregator**
102
- `@jinsiyu/dsh-code-server-runtime-<platform>-<arch>` using `npm:` aliases. The aggregators sit in the plugin's
103
- `optionalDependencies` (gated by `os`/`cpu`), so pnpm **auto-selects the right one for the current
104
- architecture**;
107
+ `@jinsiyu/dsh-code-server-runtime-<platform>-<arch>` using `npm:` aliases;
105
108
  - consequently the dependency graph contains **no package with pre/install/postinstall or a `binding.gyp`** →
106
- no profile `allowBuilds`, no build script ever runs, and **the user machine needs no C++ toolchain**.
109
+ no profile `allowBuilds`, no build script ever runs, and **the user machine needs no C++ toolchain**;
110
+ - **upgrading the plugin no longer re-downloads code-server**: the sub-package is cached by version, so only a
111
+ code-server version change pulls those ~58MB again.
107
112
 
108
113
  ### Install mechanism (why it is built this way)
109
114
 
@@ -112,11 +117,16 @@ Ready to use immediately — **no second step, no "Install environment", no inst
112
117
  "requires build" and must be approved through the **host profile's** `pnpm-workspace.yaml` (`allowBuilds`),
113
118
  otherwise `dsh plugin add` exits 1 with `[ERR_PNPM_IGNORED_BUILDS]`. A dependency's own `pnpm.allowBuilds`,
114
119
  `.npmrc`, the `patch:` protocol and `optionalDependencies` are all ineffective (measured 2026-09, pnpm 11.25);
115
- - **the code-server tree** therefore ships as plain files instead of a `dependencies` entry. At pack time
116
- `npm install code-server@<version> --ignore-scripts` (skips the official `sh ./postinstall.sh` — there is no
117
- `sh` on Windows and the script only accepts npm/yarn user agents, so it always fails under pnpm) → supply the
118
- argon2 native binary (no Windows prebuild exists; compile with `node-gyp-build`, or reuse an existing
119
- `argon2.node` via `DSHCS_ARGON2_BINARY`) → strip leftover install scripts → snapshot to `vendor/code-server/`;
120
+ - **the code-server tree** is prepared at pack time with `npm install code-server@<version> --ignore-scripts`
121
+ (skips the official `sh ./postinstall.sh` — there is no `sh` on Windows and the script only accepts npm/yarn
122
+ user agents, so it always fails under pnpm) → supply the argon2 native binary (no Windows prebuild exists;
123
+ compile with `node-gyp-build`, or reuse an existing `argon2.node` via `DSHCS_ARGON2_BINARY`) → strip leftover
124
+ install scripts → snapshot to `vendor/code-server/`;
125
+ - `scripts/vendor-code-server-pkg.mjs` then turns it into **platform sub-packages**: the tree lives in an inner
126
+ `code-server/` directory (npm/pnpm packing **always excludes the package root's `node_modules`**; a subdirectory
127
+ covered by `files` is included), and the sub-package declares **no `dependencies`** (those 136 packages are
128
+ already files inside `code-server/node_modules`; declaring them would make pnpm install a second copy).
129
+ `os`/`cpu` gated, each with its own compiled argon2 (arm64 0xaa64 / x64 0x8664);
120
130
  - **packages that need compiling** are repacked by `scripts/vendor-repacks.mjs` into `@jinsiyu/dshcs-*`:
121
131
  copy the already-compiled package directory → **delete `scripts` / `files` / `binding.gyp` / `.hooks` /
122
132
  `.npmignore`** (keeping the compiled `.node` and every runtime file) → rewrite dependencies that belong to the
@@ -125,14 +135,21 @@ Ready to use immediately — **no second step, no "Install environment", no inst
125
135
  `"node-pty": "npm:@jinsiyu/dshcs-node-pty@1.2.0-beta.15"`), so VS Code's `require('node-pty')` needs no change.
126
136
  The aggregator itself is `os`/`cpu` gated, and the plugin declares both win32-arm64 and win32-x64 in
127
137
  `optionalDependencies` → one command picks the right one automatically;
128
- - **resolution path**: from `vendor/code-server/out/node/entry.js` Node walks
129
- `<plugin>/vendor/code-server/node_modules` → `<plugin>/node_modules` `<profile>/node_modules`; the profile's
130
- `nodeLinker: hoisted` puts every dependency into `<profile>/node_modules`, exactly what the runtime sees.
131
-
132
- > **Size note**: the plugin tarball is **~59MB** (bundled code-server is 232MB unpacked); the inner dependencies
133
- > and prebuilt natives are downloaded by pnpm (roughly 300–350MB, ~250MB of it native packages). Neither
134
- > `vendor/` nor `repack/` is tracked by git (see `.gitignore`), but `vendor/` **must** ship in the npm package
135
- > (the main `files` list includes it).
138
+ - **resolution path**: the host resolves the runtime root with
139
+ `require.resolve('@jinsiyu/dshcs-code-server-<platform>-<arch>/package.json')` (then the inner `code-server/`
140
+ directory) and launches `code-server/out/node/entry.js`; VS Code's inner dependencies are resolved upward from
141
+ there: `code-server/node_modules` → the sub-package's `node_modules` → `<profile>/node_modules` (hoisted
142
+ layout), exactly what the runtime sees. The in-package `vendor/code-server` is only a development/legacy
143
+ fallback.
144
+
145
+ > **Size note**: the plugin tarball is **~105KB**; each `@jinsiyu/dshcs-code-server-win32-*` sub-package is
146
+ > **~58–60MB** (249MB unpacked); the inner dependencies and prebuilt natives add ~250MB. A full install downloads
147
+ > roughly 310MB. Neither `vendor/` nor `repack/` is tracked by git (see `.gitignore`).
148
+
149
+ > **Upgrading from ≤ 0.1.36**: the in-package `vendor/code-server/` moved into the platform sub-packages and the
150
+ > main package shrank to ~105KB. The upgrade command is unchanged
151
+ > (`dsh plugin --profile web add dsh-code-server-app@<version>`); the old `vendor/` disappears with the old
152
+ > package directory.
136
153
 
137
154
  > **Upgrading from ≤ 0.1.35**: the old install root `<profile>\.code-server-app` (with ~1.4GB of inner
138
155
  > dependencies) and the "Install environment" step are gone. The new version detects the old root and logs that it
@@ -140,9 +157,10 @@ Ready to use immediately — **no second step, no "Install environment", no inst
140
157
  > `dsh-code-server-app: false` entries in the profile's `pnpm-workspace.yaml` can be removed too (no build
141
158
  > approval is needed any more).
142
159
 
143
- > **Uninstall**: `dsh plugin --profile web remove dsh-code-server-app` is enough (the plugin directory contains the
144
- > code-server tree and is deleted with it); if an old install root is still around, delete
145
- > `<profile>\.code-server-app` manually.
160
+ > **Uninstall**: `dsh plugin --profile web remove dsh-code-server-app` is enough; the code-server sub-package and
161
+ > the natives are separate dependencies, so to remove everything run e.g.
162
+ > `dsh plugin --profile web remove @jinsiyu/dshcs-code-server-win32-arm64` (or `pnpm remove` inside the profile);
163
+ > if an old install root is still around, delete `<profile>\.code-server-app` manually.
146
164
 
147
165
  > After install/dependency changes, **restart `dsh web`** (the static plugin row and host probe paths load at startup).
148
166
 
@@ -196,19 +214,22 @@ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app
196
214
  **latest** version; or use `pnpm run vendor:code-server -- --version 4.136.2` / `DSHCS_CODE_SERVER_VERSION`.
197
215
  With an existing `vendor/code-server`, a plain `pnpm pack` never upgrades (it is a no-op).
198
216
  - **Check first**: `pnpm run vendor:check` prints the bundled version / npm latest.
199
- - **A version bump requires republishing the prebuilt natives** (the inner dependency versions change with
200
- code-server): `node scripts/vendor-repacks.mjs --target win32-arm64,win32-x64 --pack` →
201
- `pnpm run publish:repacks` bump the plugin version `pnpm pack`. The script rewrites the plugin's
202
- `dependencies` (the pure-JS set) and both aggregator versions.
217
+ - **A version bump requires republishing two kinds of sub-packages** (the inner dependency versions change with
218
+ code-server):
219
+ 1. `pnpm run repack:code-server -- --target win32-arm64,win32-x64 --pack`the new code-server sub-packages;
220
+ 2. `node scripts/vendor-repacks.mjs --target win32-arm64,win32-x64 --pack` the prebuilt natives (also rewrites
221
+ the plugin's pure-JS `dependencies` and both aggregator versions);
222
+ 3. `pnpm run publish:repacks` → publish; then bump the plugin version → `pnpm pack` → publish the plugin.
203
223
  - **No runtime auto-upgrade anymore**: nothing fetches latest at startup; the version is fully determined by the bundled artifact.
204
224
  - Bundled locally right now: `code-server@4.136.2` (with Code 1.136.1).
205
225
 
206
226
  ### Compatibility with the old install locations
207
227
 
208
- Host probe order: `<plugin>/vendor/code-server` (**the real layout, runs in place**) > the old install root
209
- `<profile>\.code-server-app\node_modules\code-server` (0.1.35 and earlier, only when the in-package tree is
210
- missing) > plugin-internal `node_modules/code-server` (development) > PATH/config `bin`. The old root is only
211
- mentioned in a startup log line; nothing writes to it any more.
228
+ Host probe order: `@jinsiyu/dshcs-code-server-<platform>-<arch>/code-server` (**the real layout since 0.1.37**) >
229
+ the in-package `vendor/code-server` (0.1.36 and earlier / development) > the old install root
230
+ `<profile>\.code-server-app\node_modules\code-server` (0.1.35 and earlier) > plugin-internal
231
+ `node_modules/code-server` (development) > PATH/config `bin`. The old root is only mentioned in a startup log
232
+ line; nothing writes to it any more.
212
233
 
213
234
  ## Settings card (Settings → Plugins → Code Server)
214
235
 
@@ -233,7 +254,7 @@ package manager, and the card only reports the result.
233
254
 
234
255
  | Key | Default | Description |
235
256
  |---|---|---|
236
- | `bin` | `code-server` (placeholder) | Launch priority: explicit `bin` in config > the in-package `vendor/code-server/out/node/entry.js` > the old install root `.code-server-app` > plugin-internal `node_modules` > `code-server` on PATH. None present → startup error with troubleshooting hints |
257
+ | `bin` | `code-server` (placeholder) | Launch priority: explicit `bin` in config > the platform sub-package `@jinsiyu/dshcs-code-server-<platform>-<arch>/code-server/out/node/entry.js` > the in-package `vendor/code-server` > the old install root `.code-server-app` > plugin-internal `node_modules` > `code-server` on PATH. None present → startup error with troubleshooting hints |
237
258
  | `host` | `127.0.0.1` | Bind address; `auth: none` only allows loopback (localhost/127.0.0.1/::1) |
238
259
  | `port` | `8090` | Port; on conflict startup fails with diagnostics (no automatic port change) |
239
260
  | `auth` | `none` | `none` \| `password`; non-loopback host automatically requires password |
package/README.md CHANGED
@@ -11,7 +11,8 @@
11
11
  > - **需要微软专有扩展时**:从 Marketplace 网页下载 `.vsix`,用 `code-server --install-extension <文件>`(或放入 `--extensions-dir`)手动安装,即可在插件列表使用。
12
12
 
13
13
  静态 profile 插件(npm 包形态,host + client bundle),把 [code-server](https://github.com/coder/code-server)
14
- **内置在插件包里**(打包期产物 `vendor/code-server`,无安装脚本、无 postinstall),插件目录里就地运行;
14
+ **以平台专属子包的形式随插件依赖安装**(打包期产物 `vendor/code-server` → `@jinsiyu/dshcs-code-server-<平台>-<架构>`,
15
+ 无安装脚本、无 postinstall),插件目录外就地运行;
15
16
  VS Code 内部依赖与预编译原生模块**全部由包管理器随插件一起装好** —— 无需全局 npm 安装、无需配置 `bin`、
16
17
  无需改 profile 配置、无需第二条安装命令。
17
18
 
@@ -55,9 +56,10 @@ VS Code 内部依赖与预编译原生模块**全部由包管理器随插件一
55
56
  "打包(如何出包)"执行 `pnpm install` → `pnpm run build:client` → `pnpm run vendor:code-server` →
56
57
  (发布预编译原生包)→ `pnpm pack` + `dsh plugin --profile web add` 即可。
57
58
 
58
- > 本机(BM: Windows 11 ARM64)实测:`code-server@4.136.2`(with Code 1.136.1)随插件包内置、
59
- > 依赖(35 个纯 JS + 16 个预编译原生包,经平台聚合包装回原名)由 pnpm 一条命令装好 →
60
- > 插件就地运行 healthz 200 运行中切换 cwd 重启 停止 回收全链路验证。
59
+ > 本机(BM: Windows 11 ARM64)实测:`code-server@4.136.2`(with Code 1.136.1)由平台子包
60
+ > `@jinsiyu/dshcs-code-server-win32-arm64` 提供(主包 0.1.37 105KB),
61
+ > 35 个纯 JS 内部依赖 + 16 个预编译原生包(经平台聚合包装回原名)由 pnpm 一条命令装好
62
+ > healthz 200 → 运行中切换 cwd 重启 → 停止 → 回收全链路验证。
61
63
 
62
64
  ## 打包(如何出包)
63
65
 
@@ -66,48 +68,54 @@ cd C:\Users\User\Desktop\dsh-code-server-app
66
68
  pnpm install # 开发依赖(esbuild + motion);allowBuilds 已显式声明 → 不执行任何 postinstall
67
69
  pnpm run build:client # src/factory.js → lib/client.js(不入库,必须先构建)
68
70
  pnpm run vendor:check # 可选:查看内置 code-server 版本 vs npm 最新版
69
- pnpm run vendor:code-server # 生成 vendor/code-server(code-server 本体 + 自身依赖)
70
- node scripts/vendor-repacks.mjs --target win32-arm64,win32-x64 --pack # 生成/重打包预编译原生包
71
- pnpm run publish:repacks # 发布 @jinsiyu/dshcs-* 与平台聚合包(需 npm 登录)
72
- pnpm pack # dsh-code-server-app-<version>.tgz( 59MB)
73
- npm publish dsh-code-server-app-<version>.tgz --access public # 发布插件本体(同一次验证的 tarball)
71
+ pnpm run vendor:code-server # 生成 vendor/code-server(code-server 本体 + 自身依赖)
72
+ pnpm run repack:code-server -- --target win32-arm64,win32-x64 --pack # ② 打成平台专属子包(含 argon2 原生)
73
+ node scripts/vendor-repacks.mjs --target win32-arm64,win32-x64 --pack # 生成/重打包 VS Code 原生包
74
+ pnpm run publish:repacks # 发布全部 @jinsiyu/* 子包( npm 登录)
75
+ pnpm pack # ⑤ → dsh-code-server-app-<version>.tgz( 105KB)
76
+ npm publish dsh-code-server-app-<version>.tgz --access public # ⑥ 发布插件本体(同一次验证的 tarball)
74
77
  ```
75
78
 
76
79
  | 目标 | 命令 |
77
80
  |---|---|
78
- | **打 npm 最新版 code-server** | `pnpm run vendor:latest`(= `--force`):从 registry 取 `code-server@latest` 快照到 `vendor/code-server`;之后**必须**重跑 `vendor-repacks.mjs` 更新预编译原生包并发布 |
81
+ | **打 npm 最新版 code-server** | `pnpm run vendor:latest`(= `--force`):从 registry 取 `code-server@latest` 快照到 `vendor/code-server`;之后**必须**重跑 `repack:code-server`(重发子包)与 `vendor-repacks.mjs`(内部依赖版本会变) |
79
82
  | **指定版本** | `pnpm run vendor:code-server -- --version 4.136.2` |
80
83
  | **从已装好的树快照** | `pnpm run vendor:code-server -- --from <code-server 目录>`(秒级) |
84
+ | **重打 code-server 子包** | `pnpm run repack:code-server -- --target win32-arm64,win32-x64 --pack`(x64 会现编 argon2 并校验 PE) |
81
85
  | **重建预编译原生包** | `node scripts/vendor-repacks.mjs [--from <已完整安装的树>] --target win32-arm64,win32-x64 --pack`(不给 `--from` 会自己 npm install 解包 + 编译,耗时) |
82
- | **发布预编译原生包** | `pnpm run publish:repacks`(`--dry-run` 预览;`--only <子串>` 过滤;`--otp <code>` / `--limit N` 应对 2FA) |
86
+ | **发布子包** | `pnpm run publish:repacks`(`--dry-run` 预览;`--only <子串>` 过滤;`--otp <code>` / `--limit N` 应对 2FA) |
83
87
  | **发布插件本体** | `npm publish dsh-code-server-app-<version>.tgz --access public`(发布**已验证过的那份 tarball**,不会重新打包) |
84
88
  | **只报告版本** | `pnpm run vendor:check` |
85
89
 
86
90
  > `pnpm pack` 的 `prepack` 会自动跑一次 `vendor-code-server` 脚本;`vendor/code-server` 已存在时它是
87
91
  > **秒级 no-op**,所以日常只改插件代码的话直接 `pnpm pack` 即可(不会偷偷升级 code-server)。
88
- > 升级 code-server 必须显式 `pnpm run vendor:latest`(或 `--force`/`--version`),并重发预编译包。
92
+ > 升级 code-server 必须显式 `pnpm run vendor:latest`(或 `--force`/`--version`),并重发子包。
89
93
 
90
94
  ## 安装插件(一条命令;依赖全部由包管理器装好)
91
95
 
92
96
  ```powershell
93
97
  # 包内无 postinstall → 无需 pnpm approve-builds / allowBuilds;一条命令装完
94
- dsh plugin --profile web add dsh-code-server-app@0.1.36
98
+ dsh plugin --profile web add dsh-code-server-app@0.1.37
95
99
  # 本地 tarball 同理:
96
- dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app\dsh-code-server-app-0.1.36.tgz
100
+ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app\dsh-code-server-app-0.1.37.tgz
97
101
  ```
98
102
 
99
- 装完即用,**没有第二步、没有「安装环境」、不弹安装指引**:
103
+ 装完即用,**没有第二步、没有「安装环境」、不弹安装指引**。0.1.37 起主包只有 **~105KB**(插件自身代码),
104
+ 其余全部是依赖:
100
105
 
101
- - `code-server` 本体(`vendor/code-server/`)随插件包发布,**就在插件目录里就地运行**
102
- (`<profile>\node_modules\dsh-code-server-app\vendor\code-server\out\node\entry.js`);
106
+ - **code-server 本体**(`lib/vscode` 196.9MB + 它自带的 136 个运行时依赖)打成**平台专属子包**
107
+ `@jinsiyu/dshcs-code-server-<platform>-<arch>@<code-server 版本>`(os/cpu 限定,
108
+ 挂在插件 `optionalDependencies`)→ pnpm 按架构自动选中,运行根在
109
+ `<profile>\node_modules\@jinsiyu\dshcs-code-server-win32-arm64\code-server`;
103
110
  - VS Code 内部依赖里**纯 JS 的部分**(35 个:xterm / katex / typescript / ws / tar …)写在插件
104
- `dependencies`,由 pnpm 随插件一起装到 profile 的 `node_modules`(hoisted);
111
+ `dependencies`,由 pnpm 装到 profile 的 `node_modules`(hoisted);
105
112
  - **需要编译的原生包**(node-pty / @vscode/sqlite3 / kerberos / koffi / ssh2 / cpu-features /
106
113
  @parcel/watcher / @vscode/windows-* …共 16 个)由 `@jinsiyu/dshcs-*` 预编译包提供,
107
114
  经**平台聚合包** `@jinsiyu/dsh-code-server-runtime-<platform>-<arch>` 用 `npm:` 别名装回**原始名字**;
108
- 聚合包挂在插件 `optionalDependencies` 上(os/cpu 限定)→ pnpm **按架构自动选中**当前平台那一份;
109
115
  - 因此依赖图里**没有任何带 pre/install/postinstall 或 binding.gyp 的包** →
110
- 不需要 profile 的 `allowBuilds`、不执行任何构建、**使用者机器不需要 C++ 工具链**。
116
+ 不需要 profile 的 `allowBuilds`、不执行任何构建、**使用者机器不需要 C++ 工具链**;
117
+ - **升级插件不再重下 code-server**:子包版本按 code-server 版本缓存,pnpm 会直接复用;
118
+ 只有 code-server 版本变化时才重新下载那 ~58MB。
111
119
 
112
120
  ### 安装机制(为什么这样设计)
113
121
 
@@ -115,32 +123,43 @@ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app\dsh-code-
115
123
  的包都被判定"需要构建",必须由**宿主 profile** 的 `pnpm-workspace.yaml` 用 `allowBuilds` 批准,
116
124
  否则 `dsh plugin add` 直接 `[ERR_PNPM_IGNORED_BUILDS]` exit 1。依赖包自己的 `pnpm.allowBuilds`、
117
125
  `.npmrc`、`patch:` 协议、`optionalDependencies` 全都不起作用(实测 2026-09,pnpm 11.25);
118
- - **code-server 本体**因此以普通文件随包发布(而不是写进 `dependencies`)。打包期
119
- `npm install code-server@<版本> --ignore-scripts`(跳过官方 `sh ./postinstall.sh`:Windows 无 sh,
120
- 且它只认 npm/yarn user-agent)→ 补齐 argon2 native(win32-arm64 无预编译;可复用本机已有
121
- `argon2.node`,见 `DSHCS_ARGON2_BINARY`)→ 剥掉残留安装脚本 → 快照到 `vendor/code-server/`;
122
- - **需要编译的包**由 `scripts/vendor-repacks.mjs` 在打包期重打包成 `@jinsiyu/dshcs-*`:
126
+ - **code-server 本体**打包期用 `npm install code-server@<版本> --ignore-scripts`(跳过官方
127
+ `sh ./postinstall.sh`:Windows 无 sh,且它只认 npm/yarn 的 user-agent)拿到树 → 补齐 argon2 native
128
+ (win32-arm64 上游无预编译;可复用本机已有 `argon2.node`,见 `DSHCS_ARGON2_BINARY`)→
129
+ 剥掉残留安装脚本 → 快照到 `vendor/code-server/`;
130
+ - 随后 `scripts/vendor-code-server-pkg.mjs` 把它打成**平台子包**:树放在包内子目录 `code-server/`
131
+ (npm/pnpm 打包**永远排除包根目录的 node_modules**,放在被 `files` 覆盖的子目录里才会随包发布),
132
+ 子包**不写 `dependencies`**(那 136 个依赖已经作为文件打包在 `code-server/node_modules` 里,
133
+ 写成 dependencies 会让 pnpm 再装一份);`os`/`cpu` 限定 + 各自编译好的 argon2(arm64 0xaa64 / x64 0x8664);
134
+ - **需要编译的包**由 `scripts/vendor-repacks.mjs` 重打包成 `@jinsiyu/dshcs-*`:
123
135
  复制已编译的包目录 → **删除 `scripts` / `files` / `binding.gyp` / `.hooks` / `.npmignore`**
124
136
  (保留编译好的 `.node` 与全部运行时文件)→ 依赖里的同集包改成 `npm:` 别名 → 平台专属的加
125
137
  `os`/`cpu` 与 `-<platform>-<arch>` 后缀;
126
138
  - **平台聚合包**把重打包包按原始名字装回去(如 `"node-pty": "npm:@jinsiyu/dshcs-node-pty@1.2.0-beta.15"`),
127
139
  于是 VS Code 内部代码 `require('node-pty')` 不用改;聚合包本身 `os`/`cpu` 限定,
128
140
  插件 `optionalDependencies` 同时声明 win32-arm64 与 win32-x64 两份 → 一条命令自动选对;
129
- - **解析路径**:`vendor/code-server/out/node/entry.js` 向上查找 →
130
- `<插件>/vendor/code-server/node_modules` → `<插件>/node_modules` → `<profile>/node_modules`,
131
- profile `nodeLinker: hoisted` 让所有依赖都落在 `<profile>/node_modules`,与运行时一致。
141
+ - **解析路径**:host 用 `require.resolve('@jinsiyu/dshcs-code-server-<平台>-<架构>/package.json')`
142
+ 找到运行根(子目录 `code-server/`),入口 `code-server/out/node/entry.js`;
143
+ VS Code 内部依赖从该运行根向上查找:`code-server/node_modules` 子包 `node_modules` →
144
+ `<profile>/node_modules`(hoisted 布局),与运行时完全一致。包内 `vendor/code-server` 仅作
145
+ 开发期/兼容回退。
132
146
 
133
- > **体积提示**:插件 tarball 约 **59MB**(内置 code-server 解包 232MB);内部依赖与预编译原生包
134
- > 由 pnpm 另行下载( 300–350MB,其中原生包 ~250MB)。`vendor/` `repack/` 都不入 git
135
- > (见 `.gitignore`),但 `vendor/` **必须**随 npm 包发布(`files` 已包含)。
147
+ > **体积提示**:插件 tarball 约 **105KB**;`@jinsiyu/dshcs-code-server-win32-*` 子包各约 **58–60MB**
148
+ > (解包 ~249MB);内部依赖与预编译原生包约 250MB。全部合计安装下载约 310MB。
149
+ > `vendor/` `repack/` 都不入 git(见 `.gitignore`)。
150
+
151
+ > **从 ≤ 0.1.36 升级**:插件包内的 `vendor/code-server/` 已移出到平台子包,主包瘦身到 ~105KB;
152
+ > 升级命令不变(一条 `dsh plugin --profile web add dsh-code-server-app@<版本>`),
153
+ > 旧的 `vendor/` 会随旧版本包目录一起被 pnpm 清掉。
136
154
 
137
155
  > **从 ≤ 0.1.35 升级**:旧版的安装根 `<profile>\.code-server-app`(含约 1.4GB 内部依赖)与
138
156
  > 「安装环境」步骤都不再需要 —— 新版本会检测到它并打一条日志提示可安全删除:
139
157
  > `Remove-Item -Recurse -Force <profile>\.code-server-app`。profile 的 `pnpm-workspace.yaml` 里
140
158
  > 若还留着 `dsh-code-server-app: false` 之类的旧条目,也可以删掉(新版不再需要任何构建许可)。
141
159
 
142
- > **卸载**:`dsh plugin --profile web remove dsh-code-server-app` 即可(插件目录含 code-server 本体,
143
- > 一并删除);若还残留旧安装根,再手动删除 `<profile>\.code-server-app`。
160
+ > **卸载**:`dsh plugin --profile web remove dsh-code-server-app` 即可;code-server 子包与原生包
161
+ > 是独立依赖,若要彻底清干净可再 `dsh plugin --profile web remove @jinsiyu/dshcs-code-server-win32-arm64`
162
+ > (或直接在 profile 里 `pnpm remove`);若还残留旧安装根,再手动删除 `<profile>\.code-server-app`。
144
163
 
145
164
  > 安装/依赖变化后请**重启 `dsh web`**(静态插件行与 host 探测路径在启动时加载)。
146
165
 
@@ -151,8 +170,9 @@ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app
151
170
  ```
152
171
 
153
172
  > 源码路径以 `link:` 安装。开发机上没有 `vendor/code-server` 时先 `pnpm run vendor:code-server`;
154
- > 依赖(内部 JS 依赖 + 平台聚合包)同样由 pnpm 安装 —— 但本地未发布的 `@jinsiyu/*` 需要先发布,
155
- > 或把 `repack/tgz/*.tgz` `file:` 依赖临时装进 profile。
173
+ > 没有平台子包时 host 会回退到包内 `vendor/code-server`(两种布局都支持)
174
+ > 依赖(内部 JS 依赖 + 平台子包 + 聚合包)同样由 pnpm 安装 —— 本地未发布的 `@jinsiyu/*` 需先发布,
175
+ > 或把 `repack/tgz/*.tgz` 以 `file:` 依赖临时装进 profile(见 `.tmp-verify.mjs`)。
156
176
  >
157
177
  > **改动 client bundle**:编辑 `src/factory.js` 后执行 `pnpm run build:client`
158
178
  > 重新生成 `lib/client.js`(仓库不跟踪该产物;浏览器刷新即生效,host 无需重启)。
@@ -191,17 +211,19 @@ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app
191
211
  也可 `pnpm run vendor:code-server -- --version 4.136.2` 或设 `DSHCS_CODE_SERVER_VERSION`。
192
212
  已有 `vendor/code-server` 时,不带 `--force`/`--version` 不会升级(日常 `pnpm pack` 是 no-op)。
193
213
  - **先查再升**:`pnpm run vendor:check` 打印「内置版本 / npm latest」。
194
- - **换版本后必须重发预编译原生包**(内部依赖版本会跟着 code-server 变):
195
- `node scripts/vendor-repacks.mjs --target win32-arm64,win32-x64 --pack` →
196
- `pnpm run publish:repacks` bump 插件版本`pnpm pack`。
197
- 脚本会把新的「纯 JS 直装集」写进插件 `dependencies`,并更新两个聚合包的版本号。
214
+ - **换版本后要重发两类子包**(内部依赖版本会跟着 code-server 变):
215
+ 1. `pnpm run repack:code-server -- --target win32-arm64,win32-x64 --pack` → 新版本号的 code-server 平台子包;
216
+ 2. `node scripts/vendor-repacks.mjs --target win32-arm64,win32-x64 --pack`预编译原生包(并把新的
217
+ 「纯 JS 直装集」写进插件 `dependencies`、更新两个聚合包版本);
218
+ 3. `pnpm run publish:repacks` → 发布;然后 bump 插件版本 → `pnpm pack` → 发布插件。
198
219
  - **不再有运行期自动升级**:不会在启动时联网取 latest;版本完全由内置产物决定。
199
220
  - 本机当前内置:`code-server@4.136.2`(with Code 1.136.1)。
200
221
 
201
222
  ### 兼容旧安装位
202
223
 
203
- host 探测顺序:`<插件>/vendor/code-server`(**正式布局,就地运行**)> 旧版安装根
204
- `<profile>\.code-server-app\node_modules\code-server`(兼容 0.1.35 及更早,仅当插件包内缺失时)> 插件包内
224
+ host 探测顺序:`@jinsiyu/dshcs-code-server-<平台>-<架构>/code-server`(**0.1.37+ 正式布局**)> 插件包内
225
+ `vendor/code-server`(0.1.36 及更早 / 开发期)> 旧版安装根
226
+ `<profile>\.code-server-app\node_modules\code-server`(兼容 0.1.35 及更早)> 插件包内
205
227
  `node_modules/code-server`(开发期)> PATH/配置 `bin`。旧安装根只在启动日志里提示可删除,不再被写入。
206
228
 
207
229
  ## 设置卡片(设置 → 插件 → Code Server)
@@ -226,7 +248,7 @@ host 探测顺序:`<插件>/vendor/code-server`(**正式布局,就地运行**)>
226
248
 
227
249
  | 键 | 默认 | 说明 |
228
250
  |---|---|---|
229
- | `bin` | `code-server`(占位) | 启动优先级:本配置显式 `bin` > 插件包内 `vendor/code-server/out/node/entry.js` > 旧安装根 `.code-server-app` > 插件包内 `node_modules` > PATH 中的 `code-server`。都不存在时启动报错并给出排查提示 |
251
+ | `bin` | `code-server`(占位) | 启动优先级:本配置显式 `bin` > 平台子包 `@jinsiyu/dshcs-code-server-<平台>-<架构>/code-server/out/node/entry.js` > 包内 `vendor/code-server` > 旧安装根 `.code-server-app` > 插件包内 `node_modules` > PATH 中的 `code-server`。都不存在时启动报错并给出排查提示 |
230
252
  | `host` | `127.0.0.1` | 绑定地址;`auth: none` 仅允许回环(localhost/127.0.0.1/::1) |
231
253
  | `port` | `8090` | 端口;被占用时启动失败并给出诊断(不自动换端口) |
232
254
  | `auth` | `none` | `none` \| `password`;非回环 host 自动要求 password |
package/lib/index.js CHANGED
@@ -213,7 +213,9 @@ function declaredInnerDeps() {
213
213
  }
214
214
  }
215
215
 
216
- /** 从 code-server 运行位置解析内部依赖(与运行时同一套向上查找规则)。 */
216
+ /** 从 code-server 运行位置解析内部依赖(与运行时同一套向上查找规则)。
217
+ * 先试 `<name>/package.json`(code-server 本体包没有入口文件,ESM-only 包也没有 require 入口),
218
+ * 再退回 `<name>` 本身。 */
217
219
  function checkInnerDeps() {
218
220
  const declared = declaredInnerDeps();
219
221
  const root = codeServerRoot();
@@ -221,22 +223,42 @@ function checkInnerDeps() {
221
223
  const missing = [];
222
224
  let resolved = 0;
223
225
  for (const name of declared) {
224
- try {
225
- from.resolve(name);
226
- resolved += 1;
227
- } catch {
228
- missing.push(name);
226
+ let ok = false;
227
+ for (const spec of [`${name}/package.json`, name]) {
228
+ try {
229
+ from.resolve(spec);
230
+ ok = true;
231
+ break;
232
+ } catch { /* 试下一个 */ }
229
233
  }
234
+ if (ok) resolved += 1;
235
+ else missing.push(name);
230
236
  }
231
237
  return { declared: declared.length, resolved, missing };
232
238
  }
233
239
 
240
+ /** 从 code-server 运行位置解析某个模块的包目录(与运行时同一套向上查找规则)。
241
+ * 先试 `<name>/package.json`,再退回模块入口。找不到返回 null。 */
242
+ function resolveModuleDir(fromDir, name) {
243
+ try {
244
+ const from = createRequire(path.join(fromDir ?? PACKAGE_ROOT, 'package.json'));
245
+ for (const spec of [`${name}/package.json`, name]) {
246
+ try {
247
+ return path.dirname(from.resolve(spec));
248
+ } catch { /* 试下一个 */ }
249
+ }
250
+ } catch { /* 解析器不可用 */ }
251
+ return null;
252
+ }
253
+
234
254
  /** 环境检测:code-server 入口 + 原生模块(argon2) + 内部依赖 + 预编译原生包。
235
255
  * 返回 { ok, entry, native, vscodeInner, innerDeps, codeServer, vendored, upToDate, nativeRuntime, node, platform, arch }。 */
236
256
  function envCheck() {
237
257
  const entry = bundledRuntimeEntry();
238
258
  const csRoot = entry ? path.dirname(path.dirname(path.dirname(entry))) : null; // .../code-server
239
- const argon2 = csRoot !== null ? path.join(csRoot, 'node_modules', 'argon2') : null;
259
+ // argon2 不在 code-server 本体包里(它由平台包 @<scope>/dshcs-argon2-<平台>-<架构> 提供,
260
+ // 经聚合包用 npm: 别名装回原名)→ 按运行时同一套规则解析它的真实位置。
261
+ const argon2 = resolveModuleDir(csRoot, 'argon2');
240
262
  // 必须按当前平台判定:argon2 的 prebuilds 目录存在不代表本平台有二进制(win32-arm64 就缺)。
241
263
  const nativeOk = argon2 !== null && (
242
264
  fs.existsSync(path.join(argon2, 'build', 'Release', 'argon2.node')) ||
package/lib/vendor.js CHANGED
@@ -37,27 +37,37 @@ export function legacyInstallRoot() {
37
37
  return profileRoot !== null ? join(profileRoot, APP_DIR_NAME) : null;
38
38
  }
39
39
 
40
- /** 本平台 code-server 子包名:优先取插件 optionalDependencies 里匹配 <平台>-<架构> 的声明。 */
41
- export function codeServerPackageName(platform = process.platform, arch = process.arch) {
42
- const key = `${platform}-${arch}`;
40
+ /** code-server 本体包名(0.1.38+ 平台无关):取插件 dependencies 里的 @<scope>/dshcs-code-server。 */
41
+ export function codeServerPackageName() {
43
42
  try {
44
43
  const manifest = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf8'));
45
- const declared = { ...(manifest.optionalDependencies ?? {}), ...(manifest.dependencies ?? {}) };
46
- const hit = Object.keys(declared).find((name) => /dshcs-code-server-/.test(name) && name.endsWith(`-${key}`));
44
+ const declared = { ...(manifest.dependencies ?? {}), ...(manifest.optionalDependencies ?? {}) };
45
+ const hit = Object.keys(declared).find((name) => /^@[^/]+\/dshcs-code-server$/.test(name));
47
46
  if (hit !== undefined) return hit;
48
47
  } catch { /* 回退默认 scope */ }
49
- return `@jinsiyu/dshcs-code-server-${key}`;
48
+ return '@jinsiyu/dshcs-code-server';
50
49
  }
51
50
 
52
- /** code-server 运行根(优先依赖安装的平台子包,其次包内 vendor)。
53
- * @returns {string|null} 绝对路径,如 <profile>\node_modules\@jinsiyu\dshcs-code-server-win32-arm64\code-server */
54
- export function codeServerRoot() {
55
- const name = codeServerPackageName();
51
+ /** 旧版(0.1.37)平台专属 code-server 子包名,仅作兼容回退。 */
52
+ export function legacyCodeServerPackageName(platform = process.platform, arch = process.arch) {
53
+ return `@jinsiyu/dshcs-code-server-${platform}-${arch}`;
54
+ }
55
+
56
+ /** 解析某个包目录下的 code-server 运行根(包内布局:<pkg>/code-server/**)。 */
57
+ function rootOfPackage(name) {
56
58
  try {
57
59
  const manifest = require_.resolve(`${name}/package.json`);
58
60
  const root = join(dirname(manifest), 'code-server');
59
61
  if (existsSync(join(root, 'out', 'node', 'entry.js'))) return root;
60
- } catch { /* 子包未安装 */ }
62
+ } catch { /* 未安装 */ }
63
+ return null;
64
+ }
65
+
66
+ /** code-server 运行根:① 本体包(0.1.38+ 平台无关)> ② 旧平台子包(0.1.37)> ③ 包内 vendor(开发期)。
67
+ * @returns {string|null} 绝对路径,如 <profile>\node_modules\@jinsiyu\dshcs-code-server\code-server */
68
+ export function codeServerRoot() {
69
+ const root = rootOfPackage(codeServerPackageName()) ?? rootOfPackage(legacyCodeServerPackageName());
70
+ if (root !== null) return root;
61
71
  if (existsSync(join(VENDOR_TREE, 'out', 'node', 'entry.js'))) return VENDOR_TREE;
62
72
  return null;
63
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-code-server-app",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "description": "Integrate code-server (VS Code in the browser) into DSH Web and Desktop: right-sidebar tab (DSH >= 0.1.5-alpha.1) with a floating-ball fallback for older hosts, over the Connection /api channel (ctx.connection.fetch; no webServer dependency) with host-side process lifecycle.",
5
5
  "homepage": "https://github.com/jinsiyu/dsh-code-server-app",
6
6
  "repository": {
@@ -46,7 +46,6 @@
46
46
  "assets/favicon.svg",
47
47
  "assets/extensions/dshcs-open-file",
48
48
  "scripts/vendor-code-server.mjs",
49
- "scripts/vendor-code-server-pkg.mjs",
50
49
  "scripts/vendor-repacks.mjs",
51
50
  "vendor/VENDOR.json",
52
51
  "cordis.patch.yml",
@@ -60,6 +59,7 @@
60
59
  },
61
60
  "license": "MIT",
62
61
  "dependencies": {
62
+ "@jinsiyu/dshcs-code-server": "4.136.2",
63
63
  "@microsoft/1ds-core-js": "3.2.13",
64
64
  "@microsoft/1ds-post-js": "3.2.13",
65
65
  "@vscode/iconv-lite-umd": "0.7.1",
@@ -97,17 +97,14 @@
97
97
  "zod": "4.4.3"
98
98
  },
99
99
  "optionalDependencies": {
100
- "@jinsiyu/dsh-code-server-runtime-win32-arm64": "^0.1.37",
101
- "@jinsiyu/dsh-code-server-runtime-win32-x64": "^0.1.37",
102
- "@jinsiyu/dshcs-code-server-win32-arm64": "4.136.2",
103
- "@jinsiyu/dshcs-code-server-win32-x64": "4.136.2"
100
+ "@jinsiyu/dsh-code-server-runtime-win32-arm64": "^0.1.38",
101
+ "@jinsiyu/dsh-code-server-runtime-win32-x64": "^0.1.38"
104
102
  },
105
103
  "scripts": {
106
104
  "vendor:code-server": "node scripts/vendor-code-server.mjs",
107
105
  "vendor:latest": "node scripts/vendor-code-server.mjs --force",
108
106
  "vendor:check": "node scripts/vendor-code-server.mjs --check",
109
107
  "repack:build": "node scripts/vendor-repacks.mjs",
110
- "repack:code-server": "node scripts/vendor-code-server-pkg.mjs",
111
108
  "publish:repacks": "node scripts/publish-repacks.mjs",
112
109
  "build:client": "node scripts/build-client.mjs"
113
110
  }
@@ -1,19 +1,20 @@
1
- // scripts/vendor-repacks.mjs — 打包期把「pnpm 拒绝安装」的包重打包成可安装的预编译包。
1
+ // scripts/vendor-repacks.mjs — 打包期把 code-server 本体与「pnpm 拒绝安装」的原生包全部打成可安装的预编译包。
2
2
  //
3
3
  // 为什么:pnpm 认为「含安装脚本」或「含 binding.gyp / .hooks」的包需要构建,必须由宿主
4
- // pnpm-workspace.yaml 的 allowBuilds 批准,否则 dsh plugin add 直接 exit 1。VS Code 内部
5
- // 依赖里有一批这样的包(含原生模块),既不能在 profile 里声明为依赖,也不能靠 pnpm 装。
4
+ // pnpm-workspace.yaml 的 allowBuilds 批准,否则 dsh plugin add 直接 exit 1。code-server 本体、
5
+ // argon2 VS Code 内部依赖里的一批原生包都属于这一类。
6
6
  //
7
- // 做法(实测可行,见下):
8
- // 1. 从一棵**已完整安装**的 code-server 树出发,算出 needsRepack 闭包(带构建信号,以及依赖链
9
- // 上命中它们的包 —— optionalDependencies,例如 @vscode/proxy-agent → @vscode/windows-ca-certs);
10
- // 2. 每个命中包重新打包成 @<scope>/dshcs-<名字>:
11
- // - 删掉全部 scripts / files 字段、binding.gyp、.hooks、.npmignore(全量打包,保住 build/*.node);
12
- // - 依赖里命中重打包集的,改成 npm: 别名;
13
- // - 含「非 prebuilds .node」的包视为平台专属 名字带 -<platform>-<arch>,并写入 os/cpu;
14
- // 3. 生成平台聚合包 @<scope>/dsh-code-server-runtime-<platform>-<arch>,dependencies 用 npm: 别名
15
- // 把重打包包装回**原始名字**(node-pty / @vscode/sqlite3 / …),这样 VS Code 的 require 不用改;
16
- // 4. 把「纯 JS 直装集」写进插件 package.json dependencies,把两个平台聚合包写进 optionalDependencies。
7
+ // 产物(全部由本脚本统一生成,再由 scripts/publish-repacks.mjs 统一发布):
8
+ // @<scope>/dshcs-code-server@<code-server 版本> 平台无关:vendor/code-server 的树
9
+ // (out/ + lib/vscode + 自带运行时依赖),**去掉 node_modules/argon2**;
10
+ // @<scope>/dshcs-argon2-<platform>-<arch>@<argon2 版本> 平台专属:argon2 + 该架构编译好的 .node
11
+ // (与其它原生包同一套规则:删 scripts/files/binding.gyp、删 prebuilds 统一走 build/Release);
12
+ // ③ @<scope>/dshcs-<名字>[-<platform>-<arch>]@<版本> VS Code 内部依赖里需要构建的包
13
+ // (node-pty / @vscode/sqlite3 / kerberos / koffi / ssh2 / …),
14
+ // 依赖链上命中它们的包也一并重打包(含 optionalDependencies,如 @vscode/proxy-agent → windows-ca-certs);
15
+ // ④ @<scope>/dsh-code-server-runtime-<platform>-<arch>@<插件版本> 平台聚合包:
16
+ // dependencies npm: 别名把 ②③ 装回**原始名字**(argon2 / node-pty / @vscode/sqlite3 / …),
17
+ // 这样 code-server 与 VS Code 的 require 不用改,插件只需按 os/cpu 声明 ①(常规依赖)+ ④(可选依赖)。
17
18
  //
18
19
  // 结果:pnpm install 不再遇到任何带构建信号的包 → 无需 allowBuilds、无需「安装环境」步骤。
19
20
  //
@@ -48,6 +49,10 @@ const argValue = (n) => { const a = argValues(n); return a.length > 0 ? a[a.leng
48
49
  const FROM = argValue('--from');
49
50
  const SCOPE = argValue('--scope') ?? '@jinsiyu';
50
51
  const DO_PACK = process.argv.includes('--pack');
52
+ // --reuse:复用 repack/build 里已有的原生包(不重新 analyze/交叉编译),只重建
53
+ // code-server 本体包、argon2 平台包、平台聚合包与插件 package.json。
54
+ // 适用于「原生包没变、只调整打包结构」的场景(本机已无可分析的完整源树时也用它)。
55
+ const REUSE = process.argv.includes('--reuse');
51
56
  const TARGETS = argValues('--target');
52
57
  // Copilot 整树排除:620MB、依赖链里还有带脚本的包,且 code-server 里用不到。
53
58
  const EXCLUDE = [/^@github\//, /^@vscode\/copilot-api$/];
@@ -309,22 +314,233 @@ function prepareSourceTree() {
309
314
  return tree;
310
315
  }
311
316
 
317
+ /** PE machine(0x8664=x64 / 0xaa64=arm64),用于交叉编译后的静态校验。 */
318
+ function peMachine(file) {
319
+ try {
320
+ const b = readFileSync(file);
321
+ if (b[0] !== 0x4d || b[1] !== 0x5a) return null;
322
+ const off = b.readUInt32LE(0x3c);
323
+ if (b.toString('ascii', off, off + 4) !== 'PE\u0000\u0000') return null;
324
+ return `0x${b.readUInt16LE(off + 4).toString(16)}`;
325
+ } catch { return null; }
326
+ }
327
+
328
+ function nodeGypCli() {
329
+ const cli = join(process.env.APPDATA ?? '', 'npm', 'node_modules', 'node-gyp', 'bin', 'node-gyp.js');
330
+ if (!existsSync(cli)) throw new Error('找不到 node-gyp(需全局安装 node-gyp)');
331
+ return cli;
332
+ }
333
+
334
+ /** ① code-server 本体(平台无关):vendor/code-server → repack/build/code-server/。
335
+ * 去掉平台专属的 node_modules/argon2 —— 它由 @<scope>/dshcs-argon2-<platform>-<arch> 按架构提供。 */
336
+ function buildCodeServerPackage() {
337
+ const vendorTree = join(pkgRoot, 'vendor', 'code-server');
338
+ if (!existsSync(join(vendorTree, 'out', 'node', 'entry.js'))) {
339
+ throw new Error('缺少 vendor/code-server;先运行 `node scripts/vendor-code-server.mjs`');
340
+ }
341
+ const version = readJson(join(vendorTree, 'package.json'))?.version;
342
+ if (typeof version !== 'string') throw new Error('读不到 code-server 版本');
343
+ const dir = join(OUT, 'build', 'code-server');
344
+ rmSync(dir, { recursive: true, force: true });
345
+ mkdirSync(dir, { recursive: true });
346
+ console.log(`[repack] code-server 本体(平台无关)→ ${relative(pkgRoot, join(dir, 'code-server'))}(argon2 由平台包提供)`);
347
+ cpSync(vendorTree, join(dir, 'code-server'), {
348
+ recursive: true,
349
+ dereference: false,
350
+ maxRetries: 6,
351
+ retryDelay: 250,
352
+ });
353
+ // 删掉平台专属的 argon2(整目录,避免嵌套解析遮蔽 <profile>/node_modules/argon2)
354
+ rmSync(join(dir, 'code-server', 'node_modules', 'argon2'), { recursive: true, force: true });
355
+ writeFileSync(join(dir, 'package.json'), JSON.stringify({
356
+ name: `${SCOPE}/dshcs-code-server`,
357
+ version,
358
+ description: `code-server ${version} (out/ + lib/vscode + its runtime dependencies), repacked so pnpm installs it `
359
+ + `without any build script. The platform-specific argon2 binary is provided by ${SCOPE}/dshcs-argon2-<platform>-<arch>.`,
360
+ files: ['code-server'],
361
+ license: 'MIT',
362
+ repository: readJson(join(pkgRoot, 'package.json'))?.repository ?? undefined,
363
+ }, null, 2) + '\n', 'utf8');
364
+ const name = `${SCOPE}/dshcs-code-server`;
365
+ return { dir, name, version, file: tgzName(name, version) };
366
+ }
367
+
368
+ /** ② argon2 平台二进制包(每个目标一个):@<scope>/dshcs-argon2-<target>@<argon2 版本>。
369
+ * 与其它原生包同一套规则:删 scripts/files/binding.gyp,保留 build/Release 里的 .node;
370
+ * prebuilds/ 一律删掉(统一走 build/Release,避免 node-gyp-build 在 win32-x64 选到上游 .glibc 命名文件)。 */
371
+ function buildArgon2Package(target) {
372
+ const [platform, arch] = target.split('-');
373
+ const src = join(pkgRoot, 'vendor', 'code-server', 'node_modules', 'argon2');
374
+ if (!existsSync(join(src, 'package.json'))) throw new Error('缺少 vendor/code-server/node_modules/argon2');
375
+ const version = readJson(join(src, 'package.json')).version;
376
+ const dir = join(OUT, 'build', `argon2-${target}`);
377
+ rmSync(dir, { recursive: true, force: true });
378
+ mkdirSync(dirname(dir), { recursive: true });
379
+ cpSync(src, dir, { recursive: true, maxRetries: 6, retryDelay: 250 }); // dest 不存在 → 内容平铺到 dir
380
+ rmSync(join(dir, 'build'), { recursive: true, force: true }); // 重新编译,避免带走 host 架构产物
381
+
382
+ console.log(`[repack] argon2 ${target}: node-gyp rebuild --arch=${arch} …`);
383
+ const res = spawnSync(process.execPath, [nodeGypCli(), 'rebuild', `--arch=${arch}`], { cwd: dir, stdio: 'inherit' });
384
+ if ((res.status ?? 1) !== 0) throw new Error(`argon2 ${target} 编译失败 (${res.status})`);
385
+ // 编译完成后才剥掉构建信号:删 prebuilds(统一走 build/Release)、binding.gyp、中间产物
386
+ rmSync(join(dir, 'prebuilds'), { recursive: true, force: true });
387
+ rmSync(join(dir, 'binding.gyp'), { force: true });
388
+ rmSync(join(dir, '.npmignore'), { force: true });
389
+ rmSync(join(dir, 'build', 'gyp'), { recursive: true, force: true }); // 中间产物,别进包
390
+ const bin = join(dir, 'build', 'Release', 'argon2.node');
391
+ if (!existsSync(bin)) throw new Error(`argon2 ${target} 缺 build/Release/argon2.node`);
392
+ const want = arch === 'x64' ? '0x8664' : arch === 'arm64' ? '0xaa64' : null;
393
+ const machine = peMachine(bin);
394
+ if (want !== null && machine !== want) throw new Error(`argon2 ${target} PE machine=${machine},期望 ${want}`);
395
+
396
+ const manifest = readJson(join(dir, 'package.json'));
397
+ delete manifest.scripts;
398
+ delete manifest.files;
399
+ manifest.name = `${SCOPE}/dshcs-argon2-${target}`;
400
+ manifest.version = version;
401
+ manifest.os = [platform];
402
+ manifest.cpu = [arch];
403
+ writeFileSync(join(dir, 'package.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf8');
404
+ console.log(` ✓ argon2 ${target} → ${machine} (${manifest.name}@${version})`);
405
+ return { dir, name: manifest.name, version, file: tgzName(manifest.name, version) };
406
+ }
407
+
408
+ /** 从已安装的 profile 的 node_modules 还原 host 平台映射(别名目录名 = 原始包名,清单名 = 重打包名)。
409
+ * 聚合包清单丢失时的兜底(例如上次构建中途失败)。 */
410
+ function hostMapFromProfile() {
411
+ const dshHome = process.env.DSH_HOME || join(process.env.USERPROFILE ?? process.env.HOME ?? '', '.dsh');
412
+ const profiles = join(dshHome, 'profiles');
413
+ if (!existsSync(profiles)) return null;
414
+ let best = null;
415
+ let bestProfile = null;
416
+ for (const name of readdirSync(profiles)) {
417
+ const nm = join(profiles, name, 'node_modules');
418
+ if (!existsSync(nm)) continue;
419
+ const map = new Map();
420
+ const visit = (dir, prefix) => {
421
+ let entries; try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; }
422
+ for (const ent of entries) {
423
+ if (!ent.isDirectory() || ent.name === '.bin' || ent.name === '.pnpm') continue;
424
+ const full = join(dir, ent.name);
425
+ if (ent.name.startsWith('@')) { visit(full, prefix + ent.name + '/'); continue; }
426
+ const m = readJson(join(full, 'package.json'));
427
+ if (m === null || typeof m.name !== 'string') continue;
428
+ if (!/^@[^/]+\/dshcs-/.test(m.name) || /dshcs-code-server/.test(m.name)) continue;
429
+ map.set(`${prefix}${ent.name}`, { pkgName: m.name, version: m.version });
430
+ }
431
+ };
432
+ visit(nm, '');
433
+ if (map.size > 0 && (best === null || map.size > best.size)) { best = map; bestProfile = name; }
434
+ }
435
+ if (best !== null) {
436
+ console.log(`[repack] --reuse:从 profile ${bestProfile} 的别名还原 ${best.size} 个映射`);
437
+ }
438
+ return best;
439
+ }
440
+
441
+ /** --reuse:从现有 repack/build + 聚合包清单还原「重打包集」,不重新分析源树。
442
+ * @returns {{repack: Map<string,{pkg:{version:string},platformSpecific:boolean}>, declare:{name:string,version:string}[], reusedItems:{dir:string,file:string}[]}} */
443
+ function reuseExisting(targets, hostKey) {
444
+ const buildRoot = join(OUT, 'build');
445
+ if (!existsSync(buildRoot)) throw new Error('--reuse 需要 repack/build 已存在(先跑一次完整流程)');
446
+ const byTarget = new Map();
447
+ for (const target of targets) {
448
+ const agg = readJson(join(OUT, 'aggregator', target, 'package.json'));
449
+ if (agg === null) continue;
450
+ const map = new Map();
451
+ for (const [orig, spec] of Object.entries(agg.dependencies ?? {})) {
452
+ if (orig === 'argon2') continue; // argon2 由本脚本重建
453
+ const hit = /^npm:(.+)@([^@]+)$/.exec(spec);
454
+ if (hit === null) throw new Error(`无法解析聚合包依赖 ${orig}: ${spec}`);
455
+ map.set(orig, { pkgName: hit[1], version: hit[2] });
456
+ }
457
+ byTarget.set(target, map);
458
+ }
459
+ if (byTarget.size === 0) {
460
+ const base = hostMapFromProfile();
461
+ if (base === null) {
462
+ throw new Error('--reuse 需要 repack/aggregator/*/package.json 或一个已安装的 profile 用于还原映射');
463
+ }
464
+ byTarget.set(hostKey, base);
465
+ } else {
466
+ // 与已安装 profile 的别名取并集:聚合包清单可能在上一次失败的重建里丢过条目
467
+ const hostTarget = byTarget.has(hostKey) ? hostKey : targets[0];
468
+ const base = hostMapFromProfile();
469
+ if (base !== null) {
470
+ const merged = new Map(byTarget.get(hostTarget));
471
+ let added = 0;
472
+ for (const [orig, info] of base) {
473
+ if (!merged.has(orig)) { merged.set(orig, info); added += 1; }
474
+ }
475
+ if (added > 0) console.log(`[repack] --reuse:聚合包清单缺 ${added} 个条目 → 已用 profile 别名补齐`);
476
+ byTarget.set(hostTarget, merged);
477
+ }
478
+ }
479
+ const known = byTarget.get(hostKey) ?? byTarget.get(targets[0]);
480
+ // 其余目标:按后缀替换推导(平台专属包名只在结尾的 -<platform>-<arch> 上不同)
481
+ const knownTarget = byTarget.has(hostKey) ? hostKey : targets[0];
482
+ for (const target of targets) {
483
+ if (byTarget.has(target)) continue;
484
+ const map = new Map();
485
+ for (const [orig, info] of known) {
486
+ const pkgName = info.pkgName.endsWith(`-${knownTarget}`)
487
+ ? `${info.pkgName.slice(0, -knownTarget.length)}${target}`
488
+ : info.pkgName;
489
+ map.set(orig, { pkgName, version: info.version });
490
+ }
491
+ byTarget.set(target, map);
492
+ }
493
+ const hostSet = byTarget.get(knownTarget);
494
+ const repack = new Map();
495
+ for (const [orig, info] of hostSet) {
496
+ repack.set(orig, { pkg: { version: info.version }, platformSpecific: info.pkgName.endsWith(`-${knownTarget}`) });
497
+ }
498
+ // 已有原生包目录 → plan 项;顺带清掉将被重建的 code-server / argon2 目录
499
+ const reusedItems = [];
500
+ for (const ent of readdirSync(buildRoot)) {
501
+ if (/^code-server/.test(ent) || /^argon2-/.test(ent)) {
502
+ rmSync(join(buildRoot, ent), { recursive: true, force: true });
503
+ continue;
504
+ }
505
+ const m = readJson(join(buildRoot, ent, 'package.json'));
506
+ if (m === null) continue;
507
+ reusedItems.push({ dir: join(buildRoot, ent), file: tgzName(m.name, m.version) });
508
+ }
509
+ // 纯 JS 直装集:插件现有 dependencies 里非 @jinsiyu 的项
510
+ const pluginPkg = readJson(join(pkgRoot, 'package.json'));
511
+ const declare = Object.entries(pluginPkg.dependencies ?? {})
512
+ .filter(([name]) => !/^@jinsiyu\//.test(name))
513
+ .map(([name, version]) => ({ name, version }));
514
+ rmSync(join(OUT, 'aggregator'), { recursive: true, force: true });
515
+ console.log(`[repack] --reuse:复用 ${reusedItems.length} 个已打包目录、${repack.size} 个重打包条目、${declare.length} 个纯 JS 直装依赖`);
516
+ return { repack, declare, reusedItems };
517
+ }
518
+
312
519
  function main() {
313
- const autoSource = FROM === null;
314
- const sourceTree = autoSource ? prepareSourceTree() : FROM;
315
520
  const hostKey = `${process.platform}-${process.arch}`;
316
521
  const targets = TARGETS.length > 0 ? TARGETS : [hostKey];
317
522
  const pluginVersion = readJson(join(pkgRoot, 'package.json'))?.version ?? '0.0.0';
318
523
 
319
- const { repack, declare } = analyze(sourceTree);
524
+ let repack;
525
+ let declare;
526
+ let reusedItems = [];
527
+ let autoSourceTree = null;
528
+ if (REUSE) {
529
+ ({ repack, declare, reusedItems } = reuseExisting(targets, hostKey));
530
+ } else {
531
+ const autoSource = FROM === null;
532
+ const sourceTree = autoSource ? prepareSourceTree() : FROM;
533
+ if (autoSource) autoSourceTree = sourceTree;
534
+ ({ repack, declare } = analyze(sourceTree));
535
+ rmSync(OUT, { recursive: true, force: true });
536
+ }
320
537
  console.log(`\n重打包集(${repack.size}):`);
321
538
  for (const [name, r] of [...repack].sort()) {
322
539
  console.log(` ${name}@${r.pkg.version} ${r.platformSpecific ? '[平台专属]' : '[全平台]'}`);
323
540
  }
324
541
  console.log(`\n直装集(${declare.length}): ${declare.map((d) => d.name).join(', ')}\n`);
325
542
 
326
- rmSync(OUT, { recursive: true, force: true });
327
- mkdirSync(OUT, { recursive: true });
543
+ mkdirSync(join(OUT, 'build'), { recursive: true });
328
544
 
329
545
  // 每个目标的包名映射
330
546
  const byTarget = new Map();
@@ -370,13 +586,27 @@ function main() {
370
586
  };
371
587
 
372
588
  const plan = [];
373
- for (const [name, r] of repack) {
374
- if (r.platformSpecific) continue; // 平台专属的按目标处理
375
- const dir = writeRepack(r.dir, hostMap.get(name), flat(name));
376
- plan.push({ dir, file: tgzName(hostMap.get(name).pkgName, r.pkg.version) });
377
- }
378
- // 2) 平台专属包:host 用现成树,其它目标用交叉安装树
589
+ // 0) 已打包的原生包(--reuse 模式)
590
+ for (const item of reusedItems) plan.push(item);
591
+ // 1) code-server 本体(平台无关,1 个包)+ argon2 平台二进制包(每目标 1 个,与其它原生包同一套规则)
592
+ const codeServerPkg = buildCodeServerPackage();
593
+ plan.push({ dir: codeServerPkg.dir, file: codeServerPkg.file });
594
+ const argon2ByTarget = new Map();
379
595
  for (const target of targets) {
596
+ const pkg = buildArgon2Package(target);
597
+ argon2ByTarget.set(target, pkg);
598
+ plan.push({ dir: pkg.dir, file: pkg.file });
599
+ }
600
+ // 2) 全平台重打包包(从 host 树;--reuse 时用已打包目录)
601
+ if (!REUSE) {
602
+ for (const [name, r] of repack) {
603
+ if (r.platformSpecific) continue; // 平台专属的按目标处理
604
+ const dir = writeRepack(r.dir, hostMap.get(name), flat(name));
605
+ plan.push({ dir, file: tgzName(hostMap.get(name).pkgName, r.pkg.version) });
606
+ }
607
+ }
608
+ // 3) 平台专属包:host 用现成树,其它目标用交叉安装树(--reuse 时用已打包目录)
609
+ if (!REUSE) for (const target of targets) {
380
610
  const map = byTarget.get(target);
381
611
  const specs = [...repack].filter(([, r]) => r.platformSpecific).map(([name, r]) => ({ name, version: r.pkg.version }));
382
612
  let crossTree = null;
@@ -394,7 +624,7 @@ function main() {
394
624
  }
395
625
  if (crossTree !== null) rmSync(crossTree, { recursive: true, force: true });
396
626
  }
397
- // 3) 聚合包(每平台一个):dependencies 用 npm: 别名把重打包包装回原名
627
+ // 3) 聚合包(每平台一个):dependencies 用 npm: 别名把重打包包装回原名(含 argon2)
398
628
  for (const target of targets) {
399
629
  const map = byTarget.get(target);
400
630
  const aggName = `${SCOPE}/dsh-code-server-runtime-${target}`;
@@ -402,10 +632,13 @@ function main() {
402
632
  mkdirSync(aggDir, { recursive: true });
403
633
  const deps = {};
404
634
  for (const [name] of repack) deps[name] = `npm:${map.get(name).pkgName}@${map.get(name).version}`;
635
+ // argon2:code-server 本体包不带原生二进制,由这里按平台装回原名 "argon2"
636
+ const argon2 = argon2ByTarget.get(target);
637
+ if (argon2 !== undefined) deps.argon2 = `npm:${argon2.name}@${argon2.version}`;
405
638
  writeFileSync(join(aggDir, 'package.json'), JSON.stringify({
406
639
  name: aggName,
407
640
  version: pluginVersion,
408
- description: `Prebuilt native modules for code-server on ${target} (node-pty, @vscode/sqlite3, kerberos, …), `
641
+ description: `Prebuilt native modules for code-server on ${target} (argon2, node-pty, @vscode/sqlite3, kerberos, …), `
409
642
  + 'so that installing the VS Code inner dependencies needs no build approval or C++ toolchain.',
410
643
  os: [target.split('-')[0]],
411
644
  cpu: [target.split('-')[1]],
@@ -419,14 +652,19 @@ function main() {
419
652
  writeFileSync(join(OUT, 'pack-plan.json'), JSON.stringify(plan, null, 2) + '\n', 'utf8');
420
653
  console.log(`[repack] 生成 ${plan.length} 个待打包目录 → repack/ (计划:repack/pack-plan.json)`);
421
654
 
422
- // 4) 改写插件 package.json 的依赖
655
+ // 4) 改写插件 package.json 的依赖:纯 JS 直装集 + code-server 本体包(平台无关);
656
+ // 平台专属的(16 个原生包 + argon2)通过聚合包按 os/cpu 自动选。
423
657
  const pkgFile = join(pkgRoot, 'package.json');
424
658
  const pluginPkg = readJson(pkgFile);
425
- pluginPkg.dependencies = Object.fromEntries(declare.map((d) => [d.name, d.version]).sort(([a], [b]) => a.localeCompare(b)));
659
+ pluginPkg.dependencies = Object.fromEntries([
660
+ [codeServerPkg.name, codeServerPkg.version],
661
+ ...declare.map((d) => [d.name, d.version]),
662
+ ].sort(([a], [b]) => a.localeCompare(b)));
426
663
  pluginPkg.optionalDependencies = Object.fromEntries(targets
427
664
  .map((t) => [`${SCOPE}/dsh-code-server-runtime-${t}`, `^${pluginVersion}`]));
428
665
  writeFileSync(pkgFile, JSON.stringify(pluginPkg, null, 2) + '\n', 'utf8');
429
- console.log(`[repack] 已写入 package.json:dependencies ${declare.length} (纯 JS),optionalDependencies ${targets.length} 个平台聚合包`);
666
+ console.log(`[repack] 已写入 package.json:dependencies ${pluginPkg.dependencies ? Object.keys(pluginPkg.dependencies).length : 0} 个`
667
+ + `(code-server 本体 + 纯 JS),optionalDependencies ${targets.length} 个平台聚合包`);
430
668
 
431
669
  // 5) 可选:直接打包
432
670
  if (DO_PACK) {
@@ -440,8 +678,8 @@ function main() {
440
678
  console.log('[repack] 未加 --pack;在普通终端里执行 `node scripts/vendor-repacks.mjs --pack` 生成 tarball');
441
679
  }
442
680
 
443
- if (autoSource) {
444
- rmSync(sourceTree, { recursive: true, force: true });
681
+ if (autoSourceTree !== null) {
682
+ rmSync(autoSourceTree, { recursive: true, force: true });
445
683
  console.log('[repack] 已清理自动准备的源树');
446
684
  }
447
685
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "codeServerVersion": "4.136.2",
3
- "preparedAt": "2026-09-09T19:27:24.058Z",
3
+ "preparedAt": "2026-09-10T06:21:25.184Z",
4
4
  "source": "registry",
5
5
  "node": "v24.13.1",
6
6
  "platform": "win32",
@@ -1,137 +0,0 @@
1
- // scripts/vendor-code-server-pkg.mjs — 把内置 code-server 树打成**平台专属子包**。
2
- //
3
- // 产物(每个目标一个,os/cpu 限定):
4
- // repack/build/code-server-<target>/
5
- // package.json @jinsiyu/dshcs-code-server-<target>@<code-server 版本>
6
- // code-server/** 完整 code-server 树(out/ + lib/vscode + 自带的 136 个依赖)
7
- //
8
- // 为什么要放在子目录 code-server/ 而不是包根:npm/pnpm 打包**永远排除包根目录的 node_modules**,
9
- // 放在被 files 覆盖的子目录里才会随包发布(主包 0.1.36 用的就是同一招)。
10
- //
11
- // 为什么子包不写 dependencies:那 136 个依赖已经作为文件打包在 code-server/node_modules 里,
12
- // 写成 dependencies 会让 pnpm 再去 registry 装一份。留着 `dependencies` 字段的是嵌套的
13
- // code-server/package.json(它只是普通文件,不被解析)。
14
- //
15
- // 平台差异只有 argon2 的原生二进制(win32-arm64 上游无预编译 → 打包期编译;
16
- // win32-x64 用 `node-gyp rebuild --arch=x64` 现编,并校验 PE machine)。
17
- //
18
- // 用法:
19
- // node scripts/vendor-code-server-pkg.mjs [--target win32-arm64,win32-x64] [--pack]
20
- import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
21
- import { dirname, join, relative, resolve } from 'node:path';
22
- import { fileURLToPath } from 'node:url';
23
- import { spawnSync } from 'node:child_process';
24
-
25
- const here = dirname(fileURLToPath(import.meta.url));
26
- const pkgRoot = resolve(here, '..');
27
- const repackDir = join(pkgRoot, 'repack');
28
- const buildDir = join(repackDir, 'build');
29
- const tgzDir = join(repackDir, 'tgz');
30
- const vendorTree = join(pkgRoot, 'vendor', 'code-server');
31
- const npmCache = join(pkgRoot, '.npm-cache');
32
-
33
- const argv = process.argv.slice(2);
34
- const DO_PACK = argv.includes('--pack');
35
- const TARGETS = (() => {
36
- const i = argv.indexOf('--target');
37
- if (i < 0 || argv[i + 1] === undefined) return [`${process.platform}-${process.arch}`];
38
- return argv[i + 1].split(',').map((s) => s.trim()).filter(Boolean);
39
- })();
40
-
41
- const readJson = (p) => { try { return JSON.parse(readFileSync(p, 'utf8')); } catch { return null; } };
42
-
43
- function npm(args, cwd) {
44
- const cli = join(dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
45
- const env = { ...process.env, npm_config_cache: npmCache, npm_config_update_notifier: 'false' };
46
- const hasCli = existsSync(cli);
47
- const cmd = hasCli ? process.execPath : (process.platform === 'win32' ? 'npm.cmd' : 'npm');
48
- const full = hasCli ? [cli, ...args] : args;
49
- const res = spawnSync(cmd, full, { cwd, shell: !hasCli && process.platform === 'win32', env, stdio: 'inherit' });
50
- if ((res.status ?? 1) !== 0) throw new Error(`npm ${args.join(' ')} failed (${res.status})`);
51
- }
52
-
53
- /** PE machine(0x8664=x64 / 0xaa64=arm64),用于交叉编译后的静态校验。 */
54
- function peMachine(file) {
55
- try {
56
- const b = readFileSync(file);
57
- if (b[0] !== 0x4d || b[1] !== 0x5a) return null;
58
- const off = b.readUInt32LE(0x3c);
59
- if (b.toString('ascii', off, off + 4) !== 'PE\u0000\u0000') return null;
60
- return `0x${b.readUInt16LE(off + 4).toString(16)}`;
61
- } catch { return null; }
62
- }
63
-
64
- /** 为某个目标编译 argon2 原生模块(就地覆盖包内 build/Release/argon2.node)。 */
65
- function buildArgon2(tree, arch) {
66
- const argon2 = join(tree, 'node_modules', 'argon2');
67
- if (!existsSync(join(argon2, 'binding.gyp'))) throw new Error(`argon2 不在位: ${argon2}`);
68
- console.log(`[cs-pkg] 编译 argon2 (--arch=${arch}) …`);
69
- const ng = join(process.env.APPDATA ?? '', 'npm', 'node_modules', 'node-gyp', 'bin', 'node-gyp.js');
70
- const cli = existsSync(ng) ? ng : null;
71
- if (cli === null) throw new Error('找不到 node-gyp(需全局安装 node-gyp)');
72
- const res = spawnSync(process.execPath, [cli, 'rebuild', `--arch=${arch}`], { cwd: argon2, stdio: 'inherit' });
73
- if ((res.status ?? 1) !== 0) throw new Error(`argon2 编译失败 (${res.status})`);
74
- const bin = join(argon2, 'build', 'Release', 'argon2.node');
75
- if (!existsSync(bin)) throw new Error(`argon2 编译产物缺失: ${bin}`);
76
- return bin;
77
- }
78
-
79
- function main() {
80
- if (!existsSync(join(vendorTree, 'out', 'node', 'entry.js'))) {
81
- throw new Error('缺少 vendor/code-server;先运行 `node scripts/vendor-code-server.mjs`');
82
- }
83
- const csVersion = readJson(join(vendorTree, 'package.json'))?.version;
84
- if (typeof csVersion !== 'string') throw new Error('读不到 code-server 版本');
85
- const pluginPkg = readJson(join(pkgRoot, 'package.json'));
86
- const scope = '@jinsiyu';
87
- const hostKey = `${process.platform}-${process.arch}`;
88
- const plan = [];
89
-
90
- for (const target of TARGETS) {
91
- const [platform, arch] = target.split('-');
92
- if (platform === undefined || arch === undefined) throw new Error(`--target 需要 <platform>-<arch>,收到 ${target}`);
93
- const outName = `code-server-${target}`;
94
- const dir = join(buildDir, outName);
95
- rmSync(dir, { recursive: true, force: true });
96
- mkdirSync(dir, { recursive: true });
97
- console.log(`[cs-pkg] 复制 vendor/code-server → ${relative(pkgRoot, join(dir, 'code-server'))}`);
98
- cpSync(vendorTree, join(dir, 'code-server'), { recursive: true, dereference: false, maxRetries: 6, retryDelay: 250 });
99
-
100
- const tree = join(dir, 'code-server');
101
- // 目标架构的 argon2 原生二进制
102
- const wantMachine = arch === 'x64' ? '0x8664' : arch === 'arm64' ? '0xaa64' : null;
103
- const have = join(tree, 'node_modules', 'argon2', 'build', 'Release', 'argon2.node');
104
- if (target !== hostKey) {
105
- const bin = buildArgon2(tree, arch);
106
- const m = peMachine(bin);
107
- if (wantMachine !== null && m !== wantMachine) throw new Error(`argon2 PE machine=${m},期望 ${wantMachine}`);
108
- console.log(`[cs-pkg] argon2 → ${relative(pkgRoot, bin)} (${m})`);
109
- } else {
110
- console.log(`[cs-pkg] argon2 沿用本机产物 (${peMachine(have) ?? '?'})`);
111
- }
112
-
113
- writeFileSync(join(dir, 'package.json'), JSON.stringify({
114
- name: `${scope}/dshcs-code-server-${target}`,
115
- version: csVersion,
116
- description: `Bundled code-server ${csVersion} tree for ${target} (out/ + lib/vscode + its ${readdirSync(join(tree, 'node_modules')).length} runtime deps), repacked so pnpm installs it without any build script.`,
117
- os: [platform],
118
- cpu: [arch],
119
- files: ['code-server'],
120
- license: 'MIT',
121
- repository: pluginPkg?.repository ?? undefined,
122
- }, null, 2) + '\n', 'utf8');
123
- plan.push({ dir, file: `${`${scope}/dshcs-code-server-${target}`.replace(/^@/, '').replaceAll('/', '-')}-${csVersion}.tgz` });
124
- }
125
-
126
- if (DO_PACK) {
127
- mkdirSync(tgzDir, { recursive: true });
128
- for (const item of plan) {
129
- console.log(`[cs-pkg] npm pack ${item.file}`);
130
- npm(['pack', '--pack-destination', tgzDir], item.dir);
131
- }
132
- }
133
- console.log(`[cs-pkg] 完成:${plan.length} 个平台子包 → repack/build/${DO_PACK ? ' + repack/tgz/' : ''}`);
134
- console.log('[cs-pkg] 下一步:`node scripts/publish-repacks.mjs --only dshcs-code-server` 发布(或用 --dry-run 预览)');
135
- }
136
-
137
- main();