dsh-code-server-app 0.3.44 → 0.3.45
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 +29 -20
- package/README.md +40 -20
- package/lib/index.js +15 -23
- package/lib/native.js +111 -83
- package/lib/vendor.js +3 -1
- package/lib/vendored.json +99 -0
- package/package.json +49 -24
- package/scripts/vendor-repacks.mjs +129 -100
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 ships the **VS Code server tree** from a [code-server](https://github.com/coder/code-server) release as a **platform-independent dependency package** (pack-time artifact `vendor/vscode` → `@jinsiyu/dshcs-vscode-server`, no install scripts, no postinstall). The code-server **Node service layer is replaced by the plugin's own `lib/launcher.mjs`**: it drives `<tree>/lib/vscode/out/server-main.js` (`loadCodeWithNls()` / `createServer()` / `handleRequest()` / `handleUpgrade()`) directly and re-adds the few HTTP endpoints code-server used to provide (`/healthz`, `/manifest.json`, `/_static/*`, `/proxy/:port`). The 16 native modules (node-pty / @vscode/sqlite3 / spdlog / …) come from `@jinsiyu/dshcs
|
|
13
|
+
A static profile plugin (npm package with host + client bundle) that ships the **VS Code server tree** from a [code-server](https://github.com/coder/code-server) release as a **platform-independent dependency package** (pack-time artifact `vendor/vscode` → `@jinsiyu/dshcs-vscode-server`, no install scripts, no postinstall). The code-server **Node service layer is replaced by the plugin's own `lib/launcher.mjs`**: it drives `<tree>/lib/vscode/out/server-main.js` (`loadCodeWithNls()` / `createServer()` / `handleRequest()` / `handleUpgrade()`) directly and re-adds the few HTTP endpoints code-server used to provide (`/healthz`, `/manifest.json`, `/_static/*`, `/proxy/:port`). The 16 native modules (node-pty / @vscode/sqlite3 / spdlog / …) come from `@jinsiyu/dshcs-*` sub-packages declared **directly on the plugin's dependency table** under their real names (os/cpu-gated per target), with the original import names restored by runtime junctions. 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, **no argon2/C++ toolchain**.
|
|
14
14
|
|
|
15
15
|
> Since 0.3.22 the ask panel renders the session's new content with **DSH's own Markdown renderer** (the same
|
|
16
16
|
> renderer and design tokens as the DSH UI; new content only) and can **answer approval requests in place**
|
|
@@ -390,11 +390,12 @@ Diagnostics: `GET /api/code-server/status` exposes
|
|
|
390
390
|
"Install the plugin (script-free install; code-server bundled)" below — `pnpm install` → `pnpm run build:client` →
|
|
391
391
|
`pnpm run vendor:vscode` → `pnpm pack` + `dsh plugin --profile web add`.
|
|
392
392
|
|
|
393
|
-
> Verified locally (BM: Windows 11 ARM64): the whole tree/dependency chain
|
|
394
|
-
>
|
|
395
|
-
> the pure-JS inner dependencies
|
|
396
|
-
>
|
|
397
|
-
>
|
|
393
|
+
> Verified locally (BM: Windows 11 ARM64): the whole tree/dependency chain hangs directly off the plugin's
|
|
394
|
+
> dependency table — the tree package `@jinsiyu/dshcs-vscode-server` (currently 4.137.0, a 50.8 MB tarball),
|
|
395
|
+
> the pure-JS inner dependencies plus the 8 platform-independent repacks in `dependencies`, and the 8
|
|
396
|
+
> platform-specific repacks (win32-arm64 / win32-x64) in `optionalDependencies` with their own os/cpu gates;
|
|
397
|
+
> the original names are restored by junctions created at runtime (`lib/native.js`)
|
|
398
|
+
> → healthz 200 → stopped → fully recycled.
|
|
398
399
|
> (The 0.1.37-era "one big platform package" layout is gone — see "Upgrading the VS Code tree" below.)
|
|
399
400
|
|
|
400
401
|
## Packaging (how to build the tarball)
|
|
@@ -419,7 +420,7 @@ pnpm run promote -- <version>
|
|
|
419
420
|
> `pnpm run promote -- <version>` (= `npm dist-tag add dsh-code-server-app@<version> latest`)
|
|
420
421
|
> **after the user restarts `dsh web` and confirms it works**. That way
|
|
421
422
|
> `dsh plugin add dsh-code-server-app` (no version) — and anything else resolving `latest` — never picks up an
|
|
422
|
-
> unverified build. Sub-packages (`@jinsiyu/dshcs
|
|
423
|
+
> unverified build. Sub-packages (`@jinsiyu/dshcs-*`) are referenced by exact versions,
|
|
423
424
|
> so their dist-tags do not affect resolution, but they default to `next` as well.
|
|
424
425
|
> Inspect the current tags with `npm dist-tag ls dsh-code-server-app`.
|
|
425
426
|
|
|
@@ -436,8 +437,8 @@ pnpm run promote -- <version>
|
|
|
436
437
|
| Sub-package | Content | os/cpu |
|
|
437
438
|
|---|---|---|
|
|
438
439
|
| `@jinsiyu/dshcs-vscode-server@<code-server version>` | the trimmed VS Code tree (`lib/vscode` + `out/browser` + `src/browser`; **without** code-server's `out/node` and its 136 runtime deps) | platform-independent |
|
|
439
|
-
| `@jinsiyu/dshcs-<name>[-win32-<arch>]` ×
|
|
440
|
-
|
|
|
440
|
+
| `@jinsiyu/dshcs-<name>[-win32-<arch>]` ×16 | the VS Code inner packages that need building (node-pty / @vscode/sqlite3 / kerberos / koffi / ssh2 / …) | gated when platform-specific |
|
|
441
|
+
| `lib/vendored.json` (**not** a package) | the "original name → repack sub-package" table shipped inside the plugin; `lib/native.js` uses it to create the junctions. Since 0.3.45 there is **no platform aggregator** | — |
|
|
441
442
|
|
|
442
443
|
| Goal | Command |
|
|
443
444
|
|---|---|
|
|
@@ -445,7 +446,7 @@ pnpm run promote -- <version>
|
|
|
445
446
|
| **Pin a version** | `pnpm run vendor:vscode -- --version 4.137.0` |
|
|
446
447
|
| **Snapshot from an existing tree** | `pnpm run vendor:vscode -- --from <code-server dir>` (seconds) |
|
|
447
448
|
| **Rebuild every sub-package** | `pnpm run repack:build -- --target win32-arm64,win32-x64 --pack` (without `--from` it npm-installs and compiles the source tree itself — slow) |
|
|
448
|
-
| **Rebuild only the tree
|
|
449
|
+
| **Rebuild only the tree + dependency table** | `node scripts/vendor-repacks.mjs --reuse --target win32-arm64,win32-x64 --pack` (reuses the natives already in `repack/build`; also rewrites `lib/vendored.json` and the plugin dependency table) |
|
|
449
450
|
| **Publish sub-packages** | `pnpm run publish:repacks` (`--dry-run` to preview; `--only <substr>` to filter; `--otp <code>` / `--limit N` for 2FA) |
|
|
450
451
|
| **Publish the plugin itself** | `pnpm run publish:plugin` (publishes the exact tarball that was verified; no re-packing; default dist-tag `next`) |
|
|
451
452
|
| **Promote `latest`** | `pnpm run promote -- <version>` (only after the user restarted and confirmed; `--dry-run` shows the current tags first) |
|
|
@@ -474,10 +475,13 @@ The main package is only **~110KB** (the plugin's own code plus the launcher); e
|
|
|
474
475
|
`@jinsiyu/dshcs-code-server/code-server` is still recognised as a fallback);
|
|
475
476
|
- the **pure-JS part** of VS Code's inner dependencies (35 packages: xterm / katex / typescript / ws / tar …) is
|
|
476
477
|
declared in the plugin's `dependencies` and installed by pnpm into the profile's `node_modules` (hoisted);
|
|
477
|
-
- the **binary part** comes entirely from `@jinsiyu/dshcs-*`
|
|
478
|
-
|
|
479
|
-
`@
|
|
480
|
-
`
|
|
478
|
+
- the **binary part** comes entirely from `@jinsiyu/dshcs-*` sub-packages, declared **directly on the plugin's own
|
|
479
|
+
dependency table** (since 0.3.45): the 8 platform-independent repacks (`node-pty` / `koffi` / `ssh2` /
|
|
480
|
+
`cpu-features` / `@parcel/watcher` / `@vscode/fs-copyfile` / `@vscode/proxy-agent` / `@microsoft/mxc-sdk`) go into
|
|
481
|
+
`dependencies` under their real names; the 8 platform-specific ones (`@vscode/sqlite3` / `spdlog` / `kerberos` /
|
|
482
|
+
`deviceid` / `native-watchdog` / `windows-registry` / `windows-process-tree` / `windows-ca-certs`) go into
|
|
483
|
+
`optionalDependencies` once per target (real names + their own os/cpu gates), so one command picks the right arch;
|
|
484
|
+
the **original names** are restored at runtime by junctions created from `lib/vendored.json`;
|
|
481
485
|
- consequently the dependency graph contains **no package with pre/install/postinstall or a `binding.gyp`** →
|
|
482
486
|
no profile `allowBuilds`, no build script ever runs, and **the user machine needs no C++ toolchain**;
|
|
483
487
|
- **upgrading the plugin no longer re-downloads the tree**: the tree package is cached by version
|
|
@@ -500,10 +504,15 @@ The main package is only **~110KB** (the plugin's own code plus the launcher); e
|
|
|
500
504
|
**removed** (the built `.node` and every runtime file stay) → sibling packages in its dependency list become `npm:`
|
|
501
505
|
aliases → platform-specific ones get `os`/`cpu` plus a `-<platform>-<arch>` suffix. For win32 targets the script also
|
|
502
506
|
verifies each `.node` PE machine (0x8664=x64 / 0xaa64=arm64) so a cross-compiled artifact cannot ship the wrong arch;
|
|
503
|
-
- **the
|
|
504
|
-
`
|
|
505
|
-
|
|
506
|
-
|
|
507
|
+
- **how the original names come back** (since 0.3.45): a repack's real name is `@<scope>/dshcs-<name>` while VS Code
|
|
508
|
+
imports `node-pty` / `@vscode/sqlite3`; pack time writes the "original name → real name" table into
|
|
509
|
+
`lib/vendored.json` (shipped with the plugin) and `lib/native.js` creates `<tree>/node_modules/<original name>`
|
|
510
|
+
junctions to the real directories (idempotent, self-healing).
|
|
511
|
+
**Why the old "platform aggregator + `npm:` aliases" is gone**: pnpm's incremental hoisted install drops those
|
|
512
|
+
aliased packages when they sit inside an **optional subtree** (measured: 9 of 16 missing) while dsh-desktop
|
|
513
|
+
validates the dependency graph right after the install ⇒ the first install always failed with `requires missing`;
|
|
514
|
+
with real-name direct dependencies the same install command plus the validator's own predicate passes end to end
|
|
515
|
+
(reproduction in `docs/desktop-first-install-root-cause.md`);
|
|
507
516
|
- **resolution path**: the host finds the tree with `require.resolve('@jinsiyu/dshcs-vscode-server/package.json')`
|
|
508
517
|
(then the inner `vscode/` directory) and the entry is `vscode/lib/vscode/out/server-main.js`; VS Code's inner deps are
|
|
509
518
|
resolved upwards from that root (`vscode/lib/vscode/node_modules` → package `node_modules` → `<profile>/node_modules`).
|
|
@@ -511,8 +520,8 @@ The main package is only **~110KB** (the plugin's own code plus the launcher); e
|
|
|
511
520
|
- **runtime layout self-healing** (`ensureRuntimeLayout()` in `lib/native.js`, idempotent, run **at activation before
|
|
512
521
|
`envCheck` and again before every start**): the host adds two kinds of **junctions** (Windows junctions / POSIX dir
|
|
513
522
|
symlinks) into the tree:
|
|
514
|
-
1. `ensureAliasLinks()`:
|
|
515
|
-
|
|
523
|
+
1. `ensureAliasLinks()`: links the 16 **original names** listed in `lib/vendored.json` into `<tree>/node_modules`
|
|
524
|
+
— the real-name packages live in the plugin's dependency graph, and `lib/vscode/out/server-main.js` uses
|
|
516
525
|
**ESM imports** (ESM ignores `NODE_PATH`), so a missing link means an immediate 500;
|
|
517
526
|
2. `ensureInnerModuleLinks()`: restores VS Code's **inner dependency directories**
|
|
518
527
|
`lib/vscode/node_modules` and `lib/vscode/extensions/node_modules` from the two `package.json` files — the trimmed
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
无安装脚本、无 postinstall);**code-server 的 Node 服务层已由插件自带的 `lib/launcher.mjs` 取代**
|
|
16
16
|
(它直接驱动 `<树>/lib/vscode/out/server-main.js` 的 `loadCodeWithNls()` / `createServer()` / `handleRequest()` /
|
|
17
17
|
`handleUpgrade()`,并补上 `/healthz`、`/manifest.json`、`/_static/*`、`/proxy/:port` 这几条 code-server 原本提供的 HTTP 面);
|
|
18
|
-
原生模块(node-pty / @vscode/sqlite3 / spdlog …)由 `@jinsiyu/dshcs
|
|
18
|
+
原生模块(node-pty / @vscode/sqlite3 / spdlog …)由 `@jinsiyu/dshcs-*` 子包按**真名直接挂在插件依赖上**、按 os/cpu 自动选中 ——
|
|
19
19
|
**无需全局 npm 安装、无需配置 `bin`、无需改 profile 配置、无需第二条安装命令、无需 argon2/C++ 工具链**。
|
|
20
20
|
|
|
21
21
|
> 0.2.0 起:**argon2 与 code-server 的 136 个运行时依赖(express / proxy-agent / js-yaml / pem / limiter …)
|
|
@@ -365,10 +365,11 @@ host 反向请求不到它。所以编辑器状态只能在扩展主动发起的
|
|
|
365
365
|
"打包(如何出包)"执行 `pnpm install` → `pnpm run build:client` → `pnpm run vendor:vscode` →
|
|
366
366
|
(发布预编译原生包)→ `pnpm pack` + `dsh plugin --profile web add` 即可。
|
|
367
367
|
|
|
368
|
-
> 本机(BM: Windows 11 ARM64)实测:树/依赖全链路是"
|
|
369
|
-
> 树包 `@jinsiyu/dshcs-vscode-server`(当前 4.137.0,50.8 MB tgz)、纯 JS
|
|
370
|
-
> `dependencies`、
|
|
371
|
-
> (
|
|
368
|
+
> 本机(BM: Windows 11 ARM64)实测:树/依赖全链路是"平台子包直挂插件依赖"供给 ——
|
|
369
|
+
> 树包 `@jinsiyu/dshcs-vscode-server`(当前 4.137.0,50.8 MB tgz)、纯 JS 内部依赖与 8 个平台无关
|
|
370
|
+
> 重打包包直接进插件 `dependencies`、8 个平台专属重打包包按 win32-arm64/x64 进
|
|
371
|
+
> `optionalDependencies`(包自带 os/cpu 自动选),原始名字由 `lib/native.js` 补 junction 还原
|
|
372
|
+
> → healthz 200 → 停止 → 回收全链路验证。
|
|
372
373
|
> (0.1.37 时代的主包形态已废弃,见下方"升级 VS Code 树"。)
|
|
373
374
|
|
|
374
375
|
## 打包(如何出包)
|
|
@@ -398,8 +399,18 @@ pnpm run promote -- <version>
|
|
|
398
399
|
> `latest` 只保留「最近一个确认无 bug 的版本」,由 `pnpm run promote -- <version>`
|
|
399
400
|
> (= `npm dist-tag add dsh-code-server-app@<version> latest`)在**用户重启 dsh web 确认无误后**才推进。
|
|
400
401
|
> 这样 `dsh plugin add dsh-code-server-app`(不带版本)和任何按 latest 安装的流程都不会拿到未验证的版本。
|
|
401
|
-
> 子包(`@jinsiyu/dshcs
|
|
402
|
+
> 子包(`@jinsiyu/dshcs-*`)被依赖以**精确版本**引用(平台专属的按目标各钉一份),dist-tag 不影响解析,但同样默认发 `next`。
|
|
402
403
|
> 查看当前标签:`npm dist-tag ls dsh-code-server-app`。
|
|
404
|
+
>
|
|
405
|
+
> **desktop profile 不走命令行安装**(2026-09-13 起的约定):对 desktop 只做 `pnpm pack` + `publish:plugin`(发 `next`),
|
|
406
|
+
> 由用户在 DSH Desktop 里用**官方安装方式**自行安装;不要再把 tarball 文件级覆盖进 `~/.dsh/profiles/desktop` ——
|
|
407
|
+
> 那条路会绕过 desktop 应用自己的依赖闭包检查,把真实的解析问题掩盖成"装上了但行为怪"。
|
|
408
|
+
> web profile 仍可照旧安装验证。
|
|
409
|
+
>
|
|
410
|
+
> **0.3.45 起没有平台聚合包**,`requires missing @microsoft/mxc-sdk@npm:…` 那类报错不会再出现。根因(实测):
|
|
411
|
+
> pnpm 的**增量 hoisted 安装**会漏链「可选子树里的 `npm:` 别名包」,16 个里漏 9 个(第一个就是 mxc-sdk),而
|
|
412
|
+
> dsh-desktop 在 `pnpm add` 之后**立刻**校验依赖图 ⇒ 首次安装必失败;重启后应用走「删 node_modules + 完整安装」
|
|
413
|
+
> 才补齐 ⇒ 就是你看到的"重启自己装好了"。复现命令与两条修法见 `docs/desktop-first-install-root-cause.md`。
|
|
403
414
|
|
|
404
415
|
`repack:build`(`scripts/vendor-repacks.mjs`)是**唯一的子包产出脚本**,一次生成:
|
|
405
416
|
|
|
@@ -407,7 +418,7 @@ pnpm run promote -- <version>
|
|
|
407
418
|
|---|---|---|
|
|
408
419
|
| `@jinsiyu/dshcs-vscode-server@<code-server 版本>` | 精简 VS Code 树(`lib/vscode` + `out/browser` + `src/browser`,**不含** code-server 的 `out/node` 与 136 个运行时依赖) | 平台无关 |
|
|
409
420
|
| `@jinsiyu/dshcs-<名字>[-win32-<arch>]` ×16 | VS Code 内部依赖里需要构建的原生包(node-pty / @vscode/sqlite3 / kerberos / koffi / ssh2 / spdlog / …) | 平台专属带 os/cpu |
|
|
410
|
-
|
|
|
421
|
+
| `lib/vendored.json`(**不是包**) | 「原名 → 重打包子包」表,随插件发布;运行时由 `lib/native.js` 据此补 junction。0.3.45 起**不再产出平台聚合包** | — |
|
|
411
422
|
|
|
412
423
|
> argon2 已随 code-server 服务层一起移除(0.2.0):`auth` 固定 `none`,需要对外访问请用 `serve: dsh`。
|
|
413
424
|
|
|
@@ -418,7 +429,7 @@ pnpm run promote -- <version>
|
|
|
418
429
|
| **从已装好的树快照** | `pnpm run vendor:vscode -- --from <code-server 目录>`(秒级) |
|
|
419
430
|
| **开发期让树可直接跑** | `pnpm run vendor:vscode -- --dev-links`(额外把 `lib/vscode/node_modules` 用 junction 补上) |
|
|
420
431
|
| **完整重打子包** | `pnpm run repack:build -- --target win32-arm64,win32-x64 --pack`(不给 `--from` 会自动 npm install 解包 + 编译,耗时) |
|
|
421
|
-
|
|
|
432
|
+
| **只重打树包 + 依赖表** | `node scripts/vendor-repacks.mjs --reuse --target win32-arm64,win32-x64 --pack`(复用 `repack/build` 里已有的原生包,不重新分析源树;顺带重写 `lib/vendored.json` 与插件依赖表) |
|
|
422
433
|
| **发布子包** | `pnpm run publish:repacks`(`--dry-run` 预览;`--only <子串>` 过滤;`--otp <code>` / `--limit N` 应对 2FA) |
|
|
423
434
|
| **发布插件本体** | `pnpm run publish:plugin`(发布**已验证过的那份 tarball**,不会重新打包;默认 dist-tag = `next`) |
|
|
424
435
|
| **推进 latest** | `pnpm run promote -- <version>`(用户重启确认无误后;`--dry-run` 先看当前标签) |
|
|
@@ -439,6 +450,7 @@ pnpm test:webview # 面板 webview 产物:官方渲染器与令牌打
|
|
|
439
450
|
pnpm test:launcher-routes # launcher 的 HTTP 面(起真进程,较慢)
|
|
440
451
|
pnpm test:workspace-switch # 切工作区不重启进程
|
|
441
452
|
pnpm test:fullscreen # 打开标签即全屏
|
|
453
|
+
pnpm test:vendored # 重打包表 ↔ 插件依赖表一致(无 npm: 别名 / 无聚合包 / vendored.json 进了 files)
|
|
442
454
|
```
|
|
443
455
|
|
|
444
456
|
> `test:bridge-routes` 会把 `DSH_HOME` 指向临时目录(否则它会 adopt 开发机上正在跑的那个实例,
|
|
@@ -461,9 +473,13 @@ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app\dsh-code-
|
|
|
461
473
|
`<profile>\node_modules\@jinsiyu\dshcs-vscode-server\vscode`;
|
|
462
474
|
- VS Code 内部依赖里**纯 JS 的部分**(35 个:xterm / katex / typescript / ws / tar …)也写在插件
|
|
463
475
|
`dependencies`,由 pnpm 装到 profile 的 `node_modules`(hoisted);
|
|
464
|
-
- **二进制部分**全部由 `@jinsiyu/dshcs-*`
|
|
465
|
-
|
|
466
|
-
|
|
476
|
+
- **二进制部分**全部由 `@jinsiyu/dshcs-*` 子包提供,且**直接挂在插件依赖上**(0.3.45 起):
|
|
477
|
+
平台无关的 8 个(`node-pty` / `koffi` / `ssh2` / `cpu-features` / `@parcel/watcher` /
|
|
478
|
+
`@vscode/fs-copyfile` / `@vscode/proxy-agent` / `@microsoft/mxc-sdk`)写进插件 `dependencies`(真名);
|
|
479
|
+
平台专属的 8 个(`@vscode/sqlite3` / `spdlog` / `kerberos` / `deviceid` / `native-watchdog` /
|
|
480
|
+
`windows-registry` / `windows-process-tree` / `windows-ca-certs`)按 win32-arm64 与 win32-x64
|
|
481
|
+
各一份写进 `optionalDependencies`(真名 + 包自带 os/cpu)→ 一条命令自动选对架构;
|
|
482
|
+
**原始名字**由 `lib/native.js` 运行时补 junction 还原(见下「运行时布局自愈」);
|
|
467
483
|
- 因此依赖图里**没有任何带 pre/install/postinstall 或 binding.gyp 的包** →
|
|
468
484
|
不需要 profile 的 `allowBuilds`、不执行任何构建、**使用者机器不需要 C++ 工具链**;
|
|
469
485
|
- **升级插件不再重下树**:树包版本按上游 code-server 版本缓存,pnpm 直接复用(约 60MB,解包 ≈197MB)。
|
|
@@ -484,18 +500,21 @@ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app\dsh-code-
|
|
|
484
500
|
(保留编译好的 `.node` 与全部运行时文件)→ 依赖里的同集包改成 `npm:` 别名 → 平台专属的加
|
|
485
501
|
`os`/`cpu` 与 `-<platform>-<arch>` 后缀;win32 目标还会校验 `.node` 的 PE machine
|
|
486
502
|
(0x8664=x64 / 0xaa64=arm64),防交叉编译产物装错架构;
|
|
487
|
-
-
|
|
488
|
-
|
|
489
|
-
|
|
503
|
+
- **原始名字怎么还原**(0.3.45 起):重打包包的真名是 `@<scope>/dshcs-<名字>`,而 VS Code `import` 的是
|
|
504
|
+
`node-pty` / `@vscode/sqlite3` 这类**原名**;打包期把「原名 → 真名」写进 `lib/vendored.json`(随插件发布),
|
|
505
|
+
运行时由 `lib/native.js` 在 `<树>/node_modules/<原名>` 补 junction 指向真名包(幂等、可自愈)。
|
|
506
|
+
**为什么不再用「平台聚合包 + `npm:` 别名」**:pnpm 的增量 hoisted 安装会漏链**可选子树**里的别名包
|
|
507
|
+
(实测 16 个漏 9 个),而 dsh-desktop 安装后立刻校验依赖图 ⇒ 首次安装必报 requires missing;
|
|
508
|
+
改成真名直接依赖后,同一条安装命令 + 校验器判据实测全部通过(复现见 `docs/desktop-first-install-root-cause.md`);
|
|
490
509
|
- **解析路径**:host 用 `require.resolve('@jinsiyu/dshcs-vscode-server/package.json')` 找到运行根
|
|
491
510
|
(包内子目录 `vscode/`),入口 `vscode/lib/vscode/out/server-main.js`;VS Code 内部依赖从该运行根向上查找
|
|
492
511
|
(`vscode/lib/vscode/node_modules` → 包 `node_modules` → `<profile>/node_modules`)。
|
|
493
512
|
(旧全量树 `@jinsiyu/dshcs-code-server/code-server` 仍作为回退被识别。)
|
|
494
513
|
- **运行时布局自愈**(`lib/native.js` 的 `ensureRuntimeLayout()`,**激活时(先于 envCheck)与每次启动前**幂等执行):
|
|
495
514
|
host 会在 VS Code 树里补两类 **junction**(Windows junction / POSIX 目录软链):
|
|
496
|
-
1. `ensureAliasLinks()
|
|
497
|
-
——
|
|
498
|
-
|
|
515
|
+
1. `ensureAliasLinks()`:按 `lib/vendored.json` 把 16 个**原始名字**补到 `<树>/node_modules`
|
|
516
|
+
—— 真名子包装在插件依赖图里,而 VS Code 的 `lib/vscode/out/server-main.js` 用 **ESM import**
|
|
517
|
+
(ESM 不认 `NODE_PATH`),缺了就直接 500;
|
|
499
518
|
2. `ensureInnerModuleLinks()`:把 VS Code 的**内部依赖目录** `lib/vscode/node_modules` 与
|
|
500
519
|
`lib/vscode/extensions/node_modules` 按两个 `package.json` 的 `dependencies` 补回老布局
|
|
501
520
|
—— 精简树里没有这两个目录,**用显式路径拼依赖的代码**
|
|
@@ -531,7 +550,7 @@ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app
|
|
|
531
550
|
|
|
532
551
|
> 源码路径以 `link:` 安装。开发机上没有 `vendor/vscode` 时先 `pnpm run vendor:vscode -- --dev-links`;
|
|
533
552
|
> 没有平台子包时 host 会回退到包内 `vendor/code-server`(两种布局都支持)。
|
|
534
|
-
> 依赖(内部 JS 依赖 +
|
|
553
|
+
> 依赖(内部 JS 依赖 + 重打包子包)同样由 pnpm 安装 —— 本地未发布的 `@jinsiyu/*` 需先发布,
|
|
535
554
|
> 或把 `repack/tgz/*.tgz` 以 `file:` 依赖临时装进 profile(见 `.tmp-verify.mjs`)。
|
|
536
555
|
>
|
|
537
556
|
> **改动 client bundle**:编辑 `src/factory.js` 后执行 `pnpm run build:client`
|
|
@@ -582,7 +601,8 @@ dsh plugin --profile web add C:\Users\User\Desktop\dsh-code-server-app
|
|
|
582
601
|
- **换版本后重新出子包并发布**(全部由同一个脚本):
|
|
583
602
|
1. `pnpm run repack:build -- --target win32-arm64,win32-x64 --pack` → 新的树包
|
|
584
603
|
(`@jinsiyu/dshcs-vscode-server@<新版本>`)以及按新内部依赖重建的原生包
|
|
585
|
-
(脚本会把新的「纯 JS 直装集」写进插件 `dependencies
|
|
604
|
+
(脚本会把新的「纯 JS 直装集」写进插件 `dependencies`,并把 16 个重打包包按真名写进
|
|
605
|
+
`dependencies` / `optionalDependencies`、重写 `lib/vendored.json`);
|
|
586
606
|
2. `pnpm run publish:repacks` → 发布;然后 bump 插件版本 → `pnpm pack` → 发布插件。
|
|
587
607
|
- `productPath`(`<quality>-<commit>`,客户端 WS 路径的组成)**从 `lib/vscode/product.json` 现算**,
|
|
588
608
|
升级树后无需改代码 —— 但也意味着切版本后必须重启 dsh web(路由在激活期注册)。
|
|
@@ -612,7 +632,7 @@ host 探测顺序:`@jinsiyu/dshcs-vscode-server/vscode`(**0.2.0+ 正式布局**)
|
|
|
612
632
|
|
|
613
633
|
0.2.7 起卡片**没有**「入口」「依赖安装」「环境检测」三行:入口在右侧栏「开始」页的 Code Server 入口框(或官方的文件点击),
|
|
614
634
|
诊断信息不再进 UI —— `/api/code-server/status` 的 `env` 字段仍返回
|
|
615
|
-
树版本 / `productPath` / server 入口、VS Code 内部依赖、**预编译原生包**(
|
|
635
|
+
树版本 / `productPath` / server 入口、VS Code 内部依赖、**预编译原生包**(重打包子包名 + 已解析模块数),需要时用脚本查或看 host 日志。
|
|
616
636
|
|
|
617
637
|
> 卡片改动经 `scope.watch` 实时生效(host 端 status API 同步返回 `keepResident`、`claimExtensions` 与
|
|
618
638
|
> `fullscreenOnOpen`,客户端立即生效);无需重启 dsh。**新增设置键后首次使用前需重启 dsh web**,
|
package/lib/index.js
CHANGED
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
vsRoot,
|
|
36
36
|
vsServerEntry,
|
|
37
37
|
} from './vendor.js';
|
|
38
|
-
import {
|
|
38
|
+
import { ensureRuntimeLayout, nativeRuntimeStatus } from './native.js';
|
|
39
39
|
import { MOUNT_PATH, mountOnWebServer } from './serve-dsh.mjs';
|
|
40
40
|
import { DEFAULT_CLAIM_EXTENSIONS, describeClaimPolicy, normalizeClaimExtensions } from './claim-types.js';
|
|
41
41
|
import {
|
|
@@ -427,13 +427,12 @@ function envCheck() {
|
|
|
427
427
|
const root = vsRoot();
|
|
428
428
|
const entry = vsServerEntry();
|
|
429
429
|
const inner = checkInnerDeps();
|
|
430
|
-
const
|
|
431
|
-
const natives = verifyNatives(runtime !== null ? runtime.modules : []);
|
|
430
|
+
const natives = nativeRuntimeStatus();
|
|
432
431
|
const installed = root !== null ? readTreeVersion(root) : null;
|
|
433
432
|
const vendored = vendoredVersion();
|
|
434
433
|
return {
|
|
435
434
|
ok: entry !== null && inner.missing.length === 0
|
|
436
|
-
&&
|
|
435
|
+
&& natives.resolved > 0 && natives.missing.length === 0,
|
|
437
436
|
tree: root !== null ? root.replace(/\\/g, '/') : null,
|
|
438
437
|
entry: entry !== null ? entry.replace(/\\/g, '/') : null,
|
|
439
438
|
productPath: productPath(root),
|
|
@@ -442,16 +441,14 @@ function envCheck() {
|
|
|
442
441
|
treeVersion: installed,
|
|
443
442
|
vendored,
|
|
444
443
|
upToDate: installed !== null && vendored !== null && installed === vendored,
|
|
445
|
-
// 本平台预编译原生产物(
|
|
446
|
-
nativeRuntime:
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
: { name: runtimePackageName(), source: null, version: null, packages: 0, missing: [] },
|
|
444
|
+
// 本平台预编译原生产物(重打包子包按真名装进依赖图,运行时按原名补 junction)
|
|
445
|
+
nativeRuntime: {
|
|
446
|
+
name: natives.name,
|
|
447
|
+
source: natives.source,
|
|
448
|
+
version: natives.version,
|
|
449
|
+
packages: natives.resolved,
|
|
450
|
+
missing: natives.missing,
|
|
451
|
+
},
|
|
455
452
|
node: process.version,
|
|
456
453
|
platform: process.platform,
|
|
457
454
|
arch: process.arch,
|
|
@@ -1650,14 +1647,9 @@ export async function apply(ctx, config) {
|
|
|
1650
1647
|
// ?v= 缓存击穿标记:按"插件版本 + VS Code 树"生成 —— 只在真升级时让渲染器丢掉旧 bundle,
|
|
1651
1648
|
// 平时正常命中缓存(workbench.js 有 18MB,每次启动都重拉不划算)。
|
|
1652
1649
|
env.DSHCS_HTML_TAG = `${pluginVersion()}-${productPath() ?? 'dev'}`.replace(/[^A-Za-z0-9._-]/g, '');
|
|
1653
|
-
//
|
|
1654
|
-
//
|
|
1655
|
-
//
|
|
1656
|
-
const nodePathDirs = aliasNodePathDirs();
|
|
1657
|
-
if (nodePathDirs.length > 0) {
|
|
1658
|
-
const existing = typeof env.NODE_PATH === 'string' && env.NODE_PATH !== '' ? env.NODE_PATH.split(path.delimiter) : [];
|
|
1659
|
-
env.NODE_PATH = [...new Set([...nodePathDirs, ...existing])].join(path.delimiter);
|
|
1660
|
-
}
|
|
1650
|
+
// 0.3.45 起不再需要给子进程加 NODE_PATH:重打包原生包按**真名**挂在插件依赖上,而它们要的
|
|
1651
|
+
// **原始名字**(node-pty / @vscode/sqlite3 …)由 ensureRuntimeLayout() 在树里补 junction ——
|
|
1652
|
+
// 目录链同时满足 ESM import 与 CJS require,NODE_PATH 只对 CJS 生效且属兜底。
|
|
1661
1653
|
|
|
1662
1654
|
let proc;
|
|
1663
1655
|
try {
|
|
@@ -1781,7 +1773,7 @@ export async function apply(ctx, config) {
|
|
|
1781
1773
|
parts.push(`内部依赖未装全(缺 ${missing.length} 个:${missing.slice(0, 5).join(', ')}${missing.length > 5 ? ' …' : ''})`);
|
|
1782
1774
|
}
|
|
1783
1775
|
if (env.nativeRuntime !== null && env.nativeRuntime.source === null) {
|
|
1784
|
-
parts.push(
|
|
1776
|
+
parts.push(`预编译原生模块未安装(${env.nativeRuntime.name})`);
|
|
1785
1777
|
} else if (Array.isArray(env.nativeRuntime?.missing) && env.nativeRuntime.missing.length > 0) {
|
|
1786
1778
|
parts.push(`原生模块解析失败:${env.nativeRuntime.missing.slice(0, 5).join(', ')}`);
|
|
1787
1779
|
}
|
package/lib/native.js
CHANGED
|
@@ -1,90 +1,93 @@
|
|
|
1
|
-
// lib/native.js —
|
|
1
|
+
// lib/native.js — 预编译原生模块(重打包子包)的定位、校验与目录链还原。
|
|
2
2
|
//
|
|
3
|
-
// 模型(0.1.36
|
|
4
|
-
//
|
|
5
|
-
// @vscode/windows-* …)在打包期由
|
|
6
|
-
// @<scope>/dshcs-<名字>[-<平台>-<架构>]
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// optionalDependencies
|
|
3
|
+
// 模型(0.3.45 起,取代 0.1.36 的「平台聚合包 + npm: 别名」):
|
|
4
|
+
// VS Code 内部依赖里那批「pnpm 拒绝安装」的原生包(node-pty / @vscode/sqlite3 / kerberos /
|
|
5
|
+
// koffi / ssh2 / cpu-features / @parcel/watcher / @vscode/windows-* …)在打包期由
|
|
6
|
+
// scripts/vendor-repacks.mjs 重打包成 @<scope>/dshcs-<名字>[-<平台>-<架构>],并**直接挂在插件
|
|
7
|
+
// 依赖上**:
|
|
8
|
+
// · 平台无关的重打包包 → 插件 `dependencies`(真名,如 @<scope>/dshcs-node-pty);
|
|
9
|
+
// · 平台专属的重打包包 → 插件 `optionalDependencies`(真名 + 包自带 os/cpu,每平台一份,
|
|
10
|
+
// 包管理器按架构自动选中,不匹配的在 lockfile 里是 optional ⇒ 缺失是允许的)。
|
|
10
11
|
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
12
|
+
// 为什么不沿用聚合包:聚合包把 16 个包挂在**可选子树**里,靠 `npm:` 别名还原原始名字;
|
|
13
|
+
// 而 dsh-desktop 的依赖图校验器在 `pnpm add` 之后立刻校验,偏偏 pnpm 的增量 hoisted 安装会
|
|
14
|
+
// 漏链这类「可选子树里的别名包」(实测 16 个漏 9 个,报 requires missing @microsoft/mxc-sdk)。
|
|
15
|
+
// 直接依赖(真名)在同一套设置下 100% 装上 ⇒ 改用直接依赖 + 运行时补 junction。
|
|
16
|
+
// 实测证据与复现命令见 docs/desktop-first-install-root-cause.md。
|
|
17
|
+
//
|
|
18
|
+
// 「原名 → 真包」的表由打包期写进 lib/vendored.json(随包发布),本模块运行时只读它。
|
|
19
|
+
//
|
|
20
|
+
// 本模块只做四件事:
|
|
21
|
+
// 1. 读 lib/vendored.json → 本平台期望可解析的「原名 → 真包」表;
|
|
22
|
+
// 2. 从 code-server 运行位置出发校验这些原名是否真的能 require/import 到;
|
|
23
|
+
// 3. 解析不到的在树里补 junction(ESM 不认 NODE_PATH,只认目录链);
|
|
24
|
+
// 4. 给环境检测卡片一份状态汇总。
|
|
15
25
|
import { createRequire } from 'node:module';
|
|
16
26
|
import { existsSync, readFileSync, mkdirSync, symlinkSync, lstatSync, rmSync } from 'node:fs';
|
|
17
27
|
import { dirname, join } from 'node:path';
|
|
18
|
-
import { PACKAGE_ROOT, codeServerRoot
|
|
28
|
+
import { PACKAGE_ROOT, codeServerRoot } from './vendor.js';
|
|
19
29
|
|
|
20
30
|
const require_ = createRequire(import.meta.url);
|
|
31
|
+
/** 打包期由 scripts/vendor-repacks.mjs 写出的重打包表。 */
|
|
32
|
+
const VENDORED_FILE = join(PACKAGE_ROOT, 'lib', 'vendored.json');
|
|
21
33
|
|
|
22
34
|
/** 平台目录名,如 win32-arm64。 */
|
|
23
35
|
export function platformKey(platform = process.platform, arch = process.arch) {
|
|
24
36
|
return `${platform}-${arch}`;
|
|
25
37
|
}
|
|
26
38
|
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
/** 读重打包表。
|
|
40
|
+
* @returns {{scope:string, targets:string[], modules:{alias:string,package:string,version?:string,platform?:boolean}[]}} */
|
|
41
|
+
export function readVendoredTable() {
|
|
30
42
|
try {
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
const doc = JSON.parse(readFileSync(VENDORED_FILE, 'utf8'));
|
|
44
|
+
const modules = Array.isArray(doc?.modules)
|
|
45
|
+
? doc.modules.filter((m) => m !== null && typeof m === 'object'
|
|
46
|
+
&& typeof m.alias === 'string' && m.alias !== ''
|
|
47
|
+
&& typeof m.package === 'string' && m.package !== '')
|
|
48
|
+
: [];
|
|
49
|
+
return {
|
|
50
|
+
scope: typeof doc?.scope === 'string' && doc.scope !== '' ? doc.scope : '@jinsiyu',
|
|
51
|
+
targets: Array.isArray(doc?.targets) ? doc.targets.filter((t) => typeof t === 'string') : [],
|
|
52
|
+
modules,
|
|
53
|
+
};
|
|
54
|
+
} catch {
|
|
55
|
+
return { scope: '@jinsiyu', targets: [], modules: [] };
|
|
56
|
+
}
|
|
37
57
|
}
|
|
38
58
|
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
56
|
-
const modules = Object.keys({ ...(manifest.dependencies ?? {}), ...(manifest.optionalDependencies ?? {}) });
|
|
57
|
-
return { name, version: typeof manifest.version === 'string' ? manifest.version : null, root, modules };
|
|
58
|
-
} catch { /* 尝试下一个 */ }
|
|
59
|
-
}
|
|
60
|
-
return null;
|
|
59
|
+
/** 某个条目在指定平台的**真包名**(平台专属的按 `-<平台>-<架构>` 拼后缀)。 */
|
|
60
|
+
export function vendoredPackageName(entry, platform = process.platform, arch = process.arch) {
|
|
61
|
+
return entry.platform === true ? `${entry.package}-${platformKey(platform, arch)}` : entry.package;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** 本平台期望装在依赖图里的重打包子包(原名 + 真名),按原名排序。
|
|
65
|
+
* @returns {{alias:string, packageName:string, version:string|null, platform:boolean}[]} */
|
|
66
|
+
export function vendoredEntries(platform = process.platform, arch = process.arch) {
|
|
67
|
+
return readVendoredTable().modules
|
|
68
|
+
.map((m) => ({
|
|
69
|
+
alias: m.alias,
|
|
70
|
+
packageName: vendoredPackageName(m, platform, arch),
|
|
71
|
+
version: typeof m.version === 'string' ? m.version : null,
|
|
72
|
+
platform: m.platform === true,
|
|
73
|
+
}))
|
|
74
|
+
.sort((a, b) => a.alias.localeCompare(b.alias));
|
|
61
75
|
}
|
|
62
76
|
|
|
63
|
-
/**
|
|
77
|
+
/** 解析原生模块时的锚点目录:
|
|
64
78
|
* ① code-server 运行根(树自带 node_modules);
|
|
65
|
-
* ②
|
|
66
|
-
*
|
|
67
|
-
* 既不在 profile 根也不在 code-server 树里,所以要单独作为锚点(运行时用 NODE_PATH 覆盖同一批目录)。
|
|
68
|
-
* @returns {string[]} */
|
|
79
|
+
* ② 插件包根 —— 重打包子包挂在插件依赖上(profile 根或插件自己的 node_modules),
|
|
80
|
+
* 从插件清单出发的解析链才看得到它们。 */
|
|
69
81
|
export function nativeSearchRoots() {
|
|
70
82
|
const roots = [];
|
|
71
83
|
const cs = codeServerRoot();
|
|
72
84
|
if (cs !== null) roots.push(cs);
|
|
73
|
-
|
|
74
|
-
if (runtime !== null) roots.push(runtime.root);
|
|
85
|
+
roots.push(PACKAGE_ROOT);
|
|
75
86
|
return roots;
|
|
76
87
|
}
|
|
77
88
|
|
|
78
|
-
/** 运行时需要加进 NODE_PATH 的目录(平台聚合包下嵌套的别名包)。
|
|
79
|
-
* @returns {string[]} */
|
|
80
|
-
export function aliasNodePathDirs() {
|
|
81
|
-
const runtime = resolveRuntime();
|
|
82
|
-
if (runtime === null) return [];
|
|
83
|
-
const dir = join(runtime.root, 'node_modules');
|
|
84
|
-
return existsSync(dir) ? [dir] : [];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
89
|
/** 解析某个模块的包目录(依次尝试各锚点;先 `<name>/package.json`,再模块入口)。
|
|
90
|
+
* @param {string} name 真包名(重打包子包名),如 `@jinsiyu/dshcs-node-pty`
|
|
88
91
|
* @returns {string|null} */
|
|
89
92
|
export function resolveNativeDir(name) {
|
|
90
93
|
for (const root of nativeSearchRoots()) {
|
|
@@ -101,22 +104,23 @@ export function resolveNativeDir(name) {
|
|
|
101
104
|
return null;
|
|
102
105
|
}
|
|
103
106
|
|
|
104
|
-
/** 从 code-server
|
|
105
|
-
* 先试 `<name>/package.json`(ESM-only 包没有 require 入口,如 @microsoft/mxc-sdk)
|
|
106
|
-
*
|
|
107
|
-
* @param {string[]} modules 期望的原生模块名(通常来自聚合包依赖清单)
|
|
107
|
+
/** 从 code-server 运行位置校验原生模块能否解析(名字用「原名」,即 VS Code 实际 import 的名字)。
|
|
108
|
+
* 先试 `<name>/package.json`(ESM-only 包没有 require 入口,如 @microsoft/mxc-sdk),再退回 `<name>` 本身。
|
|
109
|
+
* @param {string[]} modules 期望的原生模块名(别名)
|
|
108
110
|
* @returns {{resolved:string[], missing:string[]}} */
|
|
109
111
|
export function verifyNatives(modules) {
|
|
112
|
+
const csRoot = codeServerRoot();
|
|
110
113
|
const resolved = [];
|
|
111
114
|
const missing = [];
|
|
112
115
|
for (const name of modules) {
|
|
113
|
-
|
|
116
|
+
const ok = csRoot !== null && resolvesFromRoot(csRoot, name);
|
|
117
|
+
if (ok) resolved.push(name);
|
|
114
118
|
else missing.push(name);
|
|
115
119
|
}
|
|
116
120
|
return { resolved, missing };
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
/**
|
|
123
|
+
/** 某个名字能否**只靠 code-server 运行根**(含其祖先 node_modules)解析。 */
|
|
120
124
|
function resolvesFromRoot(root, name) {
|
|
121
125
|
try {
|
|
122
126
|
const from = createRequire(join(root, 'package.json'));
|
|
@@ -130,34 +134,33 @@ function resolvesFromRoot(root, name) {
|
|
|
130
134
|
return false;
|
|
131
135
|
}
|
|
132
136
|
|
|
133
|
-
/**
|
|
137
|
+
/** 确保重打包子包能被 **code-server 树**按**原始名字**解析。
|
|
134
138
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* 为每个「从树里解析不到」的别名补一个 junction,指向聚合包内的真实目录。
|
|
139
|
+
* 为什么必须做:重打包子包的真名是 `@<scope>/dshcs-<名字>`,而 VS Code 的
|
|
140
|
+
* `lib/vscode/out/server-main.js` 用 **ESM `import`** 加载 `node-pty` / `@vscode/sqlite3` 这类原名 ——
|
|
141
|
+
* ESM 不认 `NODE_PATH`(那是 CJS 的兜底),只认目录链。所以这里在 `<树根>/node_modules` 下为每个
|
|
142
|
+
* 「从树里解析不到」的原名补一个 junction,指向插件依赖图里的真实目录。
|
|
140
143
|
* 幂等、可重复调用;插件重装后由下次激活/启动自愈。
|
|
141
144
|
* @returns {{created:string[], failed:string[]}} */
|
|
142
145
|
export function ensureAliasLinks() {
|
|
143
|
-
const runtime = resolveRuntime();
|
|
144
146
|
const csRoot = codeServerRoot();
|
|
145
|
-
if (
|
|
147
|
+
if (csRoot === null) return { created: [], failed: [] };
|
|
146
148
|
const linkRoot = join(csRoot, 'node_modules');
|
|
147
149
|
const created = [];
|
|
148
150
|
const failed = [];
|
|
149
|
-
for (const
|
|
150
|
-
if (resolvesFromRoot(csRoot, alias)) continue; // 目录链已能看到(profile 根或树内),无需补
|
|
151
|
-
const target = resolveNativeDir(
|
|
152
|
-
if (target === null) continue; //
|
|
153
|
-
const link = join(linkRoot, alias);
|
|
151
|
+
for (const entry of vendoredEntries()) {
|
|
152
|
+
if (resolvesFromRoot(csRoot, entry.alias)) continue; // 目录链已能看到(profile 根或树内),无需补
|
|
153
|
+
const target = resolveNativeDir(entry.packageName);
|
|
154
|
+
if (target === null) continue; // 该平台子包没装上 → 交给 envCheck 报缺
|
|
155
|
+
const link = join(linkRoot, entry.alias);
|
|
154
156
|
try {
|
|
155
|
-
if (existsSync(link)) continue;
|
|
157
|
+
if (existsSync(join(link, 'package.json'))) continue; // 链接有效(真实目录或有效 junction)
|
|
158
|
+
if (pathEntryExists(link)) rmSync(link, { recursive: true, force: true }); // 断链(树被重装过)→ 清掉重建
|
|
156
159
|
mkdirSync(dirname(link), { recursive: true });
|
|
157
160
|
symlinkSync(target, link, process.platform === 'win32' ? 'junction' : 'dir');
|
|
158
|
-
created.push(alias);
|
|
161
|
+
created.push(entry.alias);
|
|
159
162
|
} catch (error) {
|
|
160
|
-
failed.push(`${alias}: ${error && error.message ? error.message : String(error)}`);
|
|
163
|
+
failed.push(`${entry.alias}: ${error && error.message ? error.message : String(error)}`);
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
return { created, failed };
|
|
@@ -251,7 +254,7 @@ export function ensureInnerModuleLinks() {
|
|
|
251
254
|
return { created, failed };
|
|
252
255
|
}
|
|
253
256
|
|
|
254
|
-
/** 一次性补齐两类链接:VS Code 内部依赖目录 +
|
|
257
|
+
/** 一次性补齐两类链接:VS Code 内部依赖目录 + 重打包子包的原始名字。 */
|
|
255
258
|
export function ensureRuntimeLayout() {
|
|
256
259
|
const inner = ensureInnerModuleLinks();
|
|
257
260
|
const aliases = ensureAliasLinks();
|
|
@@ -260,3 +263,28 @@ export function ensureRuntimeLayout() {
|
|
|
260
263
|
failed: [...inner.failed, ...aliases.failed],
|
|
261
264
|
};
|
|
262
265
|
}
|
|
266
|
+
|
|
267
|
+
/** 本平台预编译原生包的状态汇总(环境检测卡片用)。
|
|
268
|
+
* @returns {{name:string, source:'package'|null, version:string|null, modules:number, packages:number,
|
|
269
|
+
* resolved:number, missing:string[], missingPackages:string[]}} */
|
|
270
|
+
export function nativeRuntimeStatus(platform = process.platform, arch = process.arch) {
|
|
271
|
+
const table = readVendoredTable();
|
|
272
|
+
const entries = vendoredEntries(platform, arch);
|
|
273
|
+
const installed = [];
|
|
274
|
+
const missingPackages = [];
|
|
275
|
+
for (const entry of entries) {
|
|
276
|
+
if (resolveNativeDir(entry.packageName) !== null) installed.push(entry);
|
|
277
|
+
else missingPackages.push(entry);
|
|
278
|
+
}
|
|
279
|
+
const { resolved, missing } = verifyNatives(entries.map((entry) => entry.alias));
|
|
280
|
+
return {
|
|
281
|
+
name: `${table.scope}/dshcs-*-${platformKey(platform, arch)}`,
|
|
282
|
+
source: installed.length > 0 ? 'package' : null,
|
|
283
|
+
version: null,
|
|
284
|
+
modules: entries.length,
|
|
285
|
+
packages: installed.length,
|
|
286
|
+
resolved: resolved.length,
|
|
287
|
+
missing,
|
|
288
|
+
missingPackages: missingPackages.map((entry) => entry.packageName),
|
|
289
|
+
};
|
|
290
|
+
}
|
package/lib/vendor.js
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
// - VS Code 内部依赖(约 1GB)与需编译的原生模块仍由包管理器安装:
|
|
11
11
|
// 纯 JS 部分写在插件 package.json 的 dependencies;
|
|
12
12
|
// pnpm 拒绝安装的原生包由 scripts/vendor-repacks.mjs 重打包成 @<scope>/dshcs-*,
|
|
13
|
-
//
|
|
13
|
+
// 并**直接挂在插件依赖上**(平台无关的 → dependencies;平台专属的 → optionalDependencies,
|
|
14
|
+
// 包自带 os/cpu 按架构自动选)。原始名字由 lib/native.js 按 lib/vendored.json 补 junction ——
|
|
15
|
+
// 0.3.45 起不再有平台聚合包,原因见 docs/desktop-first-install-root-cause.md;
|
|
14
16
|
// - 插件包内 `vendor/vscode/` 只作为**开发期/兼容**回退(发布包不含 vendor)。
|
|
15
17
|
import { createRequire } from 'node:module';
|
|
16
18
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"generatedBy": "scripts/vendor-repacks.mjs",
|
|
4
|
+
"scope": "@jinsiyu",
|
|
5
|
+
"targets": [
|
|
6
|
+
"win32-arm64",
|
|
7
|
+
"win32-x64"
|
|
8
|
+
],
|
|
9
|
+
"modules": [
|
|
10
|
+
{
|
|
11
|
+
"alias": "@microsoft/mxc-sdk",
|
|
12
|
+
"package": "@jinsiyu/dshcs-microsoft-mxc-sdk",
|
|
13
|
+
"version": "0.8.0"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"alias": "@parcel/watcher",
|
|
17
|
+
"package": "@jinsiyu/dshcs-parcel-watcher",
|
|
18
|
+
"version": "2.5.6"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"alias": "@vscode/deviceid",
|
|
22
|
+
"package": "@jinsiyu/dshcs-vscode-deviceid",
|
|
23
|
+
"version": "0.1.5",
|
|
24
|
+
"platform": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"alias": "@vscode/fs-copyfile",
|
|
28
|
+
"package": "@jinsiyu/dshcs-vscode-fs-copyfile",
|
|
29
|
+
"version": "2.0.0"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"alias": "@vscode/native-watchdog",
|
|
33
|
+
"package": "@jinsiyu/dshcs-vscode-native-watchdog",
|
|
34
|
+
"version": "1.4.6",
|
|
35
|
+
"platform": true
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"alias": "@vscode/proxy-agent",
|
|
39
|
+
"package": "@jinsiyu/dshcs-vscode-proxy-agent",
|
|
40
|
+
"version": "0.44.0"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"alias": "@vscode/spdlog",
|
|
44
|
+
"package": "@jinsiyu/dshcs-vscode-spdlog",
|
|
45
|
+
"version": "0.15.8",
|
|
46
|
+
"platform": true
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"alias": "@vscode/sqlite3",
|
|
50
|
+
"package": "@jinsiyu/dshcs-vscode-sqlite3",
|
|
51
|
+
"version": "5.1.12-vscode",
|
|
52
|
+
"platform": true
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"alias": "@vscode/windows-ca-certs",
|
|
56
|
+
"package": "@jinsiyu/dshcs-vscode-windows-ca-certs",
|
|
57
|
+
"version": "0.3.4",
|
|
58
|
+
"platform": true
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"alias": "@vscode/windows-process-tree",
|
|
62
|
+
"package": "@jinsiyu/dshcs-vscode-windows-process-tree",
|
|
63
|
+
"version": "0.8.0",
|
|
64
|
+
"platform": true
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"alias": "@vscode/windows-registry",
|
|
68
|
+
"package": "@jinsiyu/dshcs-vscode-windows-registry",
|
|
69
|
+
"version": "1.2.0",
|
|
70
|
+
"platform": true
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"alias": "cpu-features",
|
|
74
|
+
"package": "@jinsiyu/dshcs-cpu-features",
|
|
75
|
+
"version": "0.0.10"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"alias": "kerberos",
|
|
79
|
+
"package": "@jinsiyu/dshcs-kerberos",
|
|
80
|
+
"version": "2.1.1-dshcs.1",
|
|
81
|
+
"platform": true
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"alias": "koffi",
|
|
85
|
+
"package": "@jinsiyu/dshcs-koffi",
|
|
86
|
+
"version": "3.1.2"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"alias": "node-pty",
|
|
90
|
+
"package": "@jinsiyu/dshcs-node-pty",
|
|
91
|
+
"version": "1.2.0-beta.15"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"alias": "ssh2",
|
|
95
|
+
"package": "@jinsiyu/dshcs-ssh2",
|
|
96
|
+
"version": "1.17.0"
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-code-server-app",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.45",
|
|
4
4
|
"description": "VS Code (from a code-server release) inside DSH: a right-sidebar tab driven by the plugin's own launcher over the in-process VS Code server (lib/launcher.mjs). Since 0.3.0 the bundle also ships an editor bridge (assets/extensions/dshcs-editor-bridge): a read-only channel between the in-tree VS Code extension host and DSH, giving the agent what only the editor knows (unsaved buffers, language-server diagnostics, the active selection) and letting editor gestures drive the session. Since 0.3.22 the ask panel renders that session's new content with DSH's own Markdown renderer (pinned to the deployed DSH UI version); since 0.3.23 it is a full-width dialog with collapsible thinking rows and can answer approval requests in place — the bridge's only non-read-only route (POST /approve, constrained to pending in-process requests and the outcomes allowed-once/rejected; panel-first for 5 minutes, handed back to the official card the moment the panel closes). The tab claims DSH file addresses (dsh-resource://file/**) by file type (setting claimExtensions), so the product's own produced-file chips, delivered-file previews and inline prose mentions open in the workbench. The IDE is a resident surface moved with Element.moveBefore instead of being remounted, so switching sidebar tabs no longer reloads it. Opening the tab switches the right sidebar to fullscreen by default (setting fullscreenOnOpen). Following a workspace switch is lightweight: the workbench re-navigates with the new ?folder= and the IDE process is not restarted (since 0.2.12). Requires a DSH with the right-sidebar services (sidebarRightTabs/sidebarRight, >= 0.1.5-alpha.1); older DSH versions get a single upgrade notice on the settings page and no other UI. Two serving modes: loopback port (default) or same-origin mount on DSH's own webServer (/code-server, protected by ctx.connection.requestRejection). No code-server Node layer, no argon2, no C++ toolchain.",
|
|
5
5
|
"homepage": "https://github.com/jinsiyu/dsh-code-server-app",
|
|
6
6
|
"repository": {
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"lib/serve-dsh.mjs",
|
|
51
51
|
"lib/vendor.js",
|
|
52
52
|
"lib/native.js",
|
|
53
|
+
"lib/vendored.json",
|
|
53
54
|
"assets/favicon.ico",
|
|
54
55
|
"assets/favicon.svg",
|
|
55
56
|
"assets/extensions/dshcs-open-file",
|
|
@@ -63,6 +64,28 @@
|
|
|
63
64
|
"README.md",
|
|
64
65
|
"README.en.md"
|
|
65
66
|
],
|
|
67
|
+
"scripts": {
|
|
68
|
+
"vendor:vscode": "node scripts/vendor-vscode-server.mjs",
|
|
69
|
+
"vendor:latest": "node scripts/vendor-vscode-server.mjs --force",
|
|
70
|
+
"vendor:check": "node scripts/vendor-vscode-server.mjs --check",
|
|
71
|
+
"vendor:code-server": "node scripts/vendor-code-server.mjs",
|
|
72
|
+
"repack:build": "node scripts/vendor-repacks.mjs",
|
|
73
|
+
"publish:repacks": "node scripts/publish-repacks.mjs",
|
|
74
|
+
"publish:plugin": "node scripts/publish-plugin.mjs",
|
|
75
|
+
"promote": "node scripts/promote.mjs",
|
|
76
|
+
"prepack": "node scripts/vendor-vscode-server.mjs && node scripts/build-webview.mjs",
|
|
77
|
+
"build:client": "node scripts/build-client.mjs",
|
|
78
|
+
"build:webview": "node scripts/build-webview.mjs",
|
|
79
|
+
"test:apply": "node scripts/test-plugin-apply.mjs",
|
|
80
|
+
"test:launcher-routes": "node scripts/test-launcher-routes.mjs",
|
|
81
|
+
"test:fullscreen": "node scripts/test-sidebar-fullscreen.mjs",
|
|
82
|
+
"test:claim-types": "node scripts/test-claim-types.mjs",
|
|
83
|
+
"test:workspace-switch": "node scripts/test-workspace-switch.mjs",
|
|
84
|
+
"test:bridge-routes": "node scripts/test-bridge-routes.mjs",
|
|
85
|
+
"test:bridge-extension": "node scripts/test-bridge-extension.mjs",
|
|
86
|
+
"test:webview": "node scripts/test-webview-bundle.mjs",
|
|
87
|
+
"test:vendored": "node scripts/test-vendored-table.mjs"
|
|
88
|
+
},
|
|
66
89
|
"devDependencies": {
|
|
67
90
|
"@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.2",
|
|
68
91
|
"@deepseek-ai/dsh-client-ui-theme": "0.1.5-rc.2",
|
|
@@ -89,6 +112,14 @@
|
|
|
89
112
|
},
|
|
90
113
|
"license": "MIT",
|
|
91
114
|
"dependencies": {
|
|
115
|
+
"@jinsiyu/dshcs-cpu-features": "0.0.10",
|
|
116
|
+
"@jinsiyu/dshcs-koffi": "3.1.2",
|
|
117
|
+
"@jinsiyu/dshcs-microsoft-mxc-sdk": "0.8.0",
|
|
118
|
+
"@jinsiyu/dshcs-node-pty": "1.2.0-beta.15",
|
|
119
|
+
"@jinsiyu/dshcs-parcel-watcher": "2.5.6",
|
|
120
|
+
"@jinsiyu/dshcs-ssh2": "1.17.0",
|
|
121
|
+
"@jinsiyu/dshcs-vscode-fs-copyfile": "2.0.0",
|
|
122
|
+
"@jinsiyu/dshcs-vscode-proxy-agent": "0.44.0",
|
|
92
123
|
"@jinsiyu/dshcs-vscode-server": "4.137.0",
|
|
93
124
|
"@microsoft/1ds-core-js": "3.2.13",
|
|
94
125
|
"@microsoft/1ds-post-js": "3.2.13",
|
|
@@ -127,27 +158,21 @@
|
|
|
127
158
|
"zod": "4.4.3"
|
|
128
159
|
},
|
|
129
160
|
"optionalDependencies": {
|
|
130
|
-
"@jinsiyu/
|
|
131
|
-
"@jinsiyu/
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
"
|
|
142
|
-
"
|
|
143
|
-
"
|
|
144
|
-
"
|
|
145
|
-
"
|
|
146
|
-
"test:fullscreen": "node scripts/test-sidebar-fullscreen.mjs",
|
|
147
|
-
"test:claim-types": "node scripts/test-claim-types.mjs",
|
|
148
|
-
"test:workspace-switch": "node scripts/test-workspace-switch.mjs",
|
|
149
|
-
"test:bridge-routes": "node scripts/test-bridge-routes.mjs",
|
|
150
|
-
"test:bridge-extension": "node scripts/test-bridge-extension.mjs",
|
|
151
|
-
"test:webview": "node scripts/test-webview-bundle.mjs"
|
|
161
|
+
"@jinsiyu/dshcs-kerberos-win32-arm64": "2.1.1-dshcs.1",
|
|
162
|
+
"@jinsiyu/dshcs-kerberos-win32-x64": "2.1.1-dshcs.1",
|
|
163
|
+
"@jinsiyu/dshcs-vscode-deviceid-win32-arm64": "0.1.5",
|
|
164
|
+
"@jinsiyu/dshcs-vscode-deviceid-win32-x64": "0.1.5",
|
|
165
|
+
"@jinsiyu/dshcs-vscode-native-watchdog-win32-arm64": "1.4.6",
|
|
166
|
+
"@jinsiyu/dshcs-vscode-native-watchdog-win32-x64": "1.4.6",
|
|
167
|
+
"@jinsiyu/dshcs-vscode-spdlog-win32-arm64": "0.15.8",
|
|
168
|
+
"@jinsiyu/dshcs-vscode-spdlog-win32-x64": "0.15.8",
|
|
169
|
+
"@jinsiyu/dshcs-vscode-sqlite3-win32-arm64": "5.1.12-vscode",
|
|
170
|
+
"@jinsiyu/dshcs-vscode-sqlite3-win32-x64": "5.1.12-vscode",
|
|
171
|
+
"@jinsiyu/dshcs-vscode-windows-ca-certs-win32-arm64": "0.3.4",
|
|
172
|
+
"@jinsiyu/dshcs-vscode-windows-ca-certs-win32-x64": "0.3.4",
|
|
173
|
+
"@jinsiyu/dshcs-vscode-windows-process-tree-win32-arm64": "0.8.0",
|
|
174
|
+
"@jinsiyu/dshcs-vscode-windows-process-tree-win32-x64": "0.8.0",
|
|
175
|
+
"@jinsiyu/dshcs-vscode-windows-registry-win32-arm64": "1.2.0",
|
|
176
|
+
"@jinsiyu/dshcs-vscode-windows-registry-win32-x64": "1.2.0"
|
|
152
177
|
}
|
|
153
|
-
}
|
|
178
|
+
}
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
// + src/browser),不含 code-server 的 out/node 与它的 136 个依赖(那层由 lib/launcher.mjs 取代);
|
|
10
10
|
// ② @<scope>/dshcs-<名字>[-<platform>-<arch>]@<版本> 需要构建的原生包(node-pty / @vscode/sqlite3 /
|
|
11
11
|
// kerberos / koffi / ssh2 / …),依赖链上命中它们的包也一并重打包(含 optionalDependencies);
|
|
12
|
-
// ③
|
|
13
|
-
//
|
|
12
|
+
// ③ 插件依赖表 + lib/vendored.json —— **0.3.45 起不再有平台聚合包**:平台无关的重打包包写进插件
|
|
13
|
+
// `dependencies`(真名),平台专属的写进 `optionalDependencies`(真名 + 包自带 os/cpu,每目标一份);
|
|
14
|
+
// 原始名字(node-pty / @vscode/sqlite3 …)由 lib/native.js 在运行时按 lib/vendored.json 补 junction。
|
|
15
|
+
// 为什么弃用「平台聚合包 + npm: 别名」:见 docs/desktop-first-install-root-cause.md。
|
|
14
16
|
//
|
|
15
17
|
// 用法:node scripts/vendor-repacks.mjs [--from <已完整安装的 code-server 树>]
|
|
16
18
|
// [--target win32-arm64,win32-x64] [--scope @jinsiyu] [--pack]
|
|
@@ -395,63 +397,15 @@ function hostMapFromProfile() {
|
|
|
395
397
|
return best;
|
|
396
398
|
}
|
|
397
399
|
|
|
398
|
-
/** --reuse:从现有 repack/build +
|
|
399
|
-
* @returns {{
|
|
400
|
-
|
|
400
|
+
/** --reuse:从现有 repack/build + 重打包表还原「重打包集」,不重新分析源树。
|
|
401
|
+
* @returns {{modules:{alias:string,package:string,version:string,platform:boolean}[],
|
|
402
|
+
* declare:{name:string,version:string}[], reusedItems:{dir:string,file:string}[]}} */
|
|
403
|
+
function reuseExisting(targets) {
|
|
401
404
|
const buildRoot = join(OUT, 'build');
|
|
402
405
|
if (!existsSync(buildRoot)) throw new Error('--reuse 需要 repack/build 已存在(先跑一次完整流程)');
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
if (agg === null) continue;
|
|
407
|
-
const map = new Map();
|
|
408
|
-
for (const [orig, spec] of Object.entries(agg.dependencies ?? {})) {
|
|
409
|
-
// 旧的聚合包清单里可能仍带 argon2(已随 code-server 服务层移除)
|
|
410
|
-
if (orig === 'argon2') continue;
|
|
411
|
-
const hit = /^npm:(.+)@([^@]+)$/.exec(spec);
|
|
412
|
-
if (hit === null) throw new Error(`无法解析聚合包依赖 ${orig}: ${spec}`);
|
|
413
|
-
map.set(orig, { pkgName: hit[1], version: hit[2] });
|
|
414
|
-
}
|
|
415
|
-
byTarget.set(target, map);
|
|
416
|
-
}
|
|
417
|
-
if (byTarget.size === 0) {
|
|
418
|
-
const base = hostMapFromProfile();
|
|
419
|
-
if (base === null) {
|
|
420
|
-
throw new Error('--reuse 需要 repack/aggregator/*/package.json 或一个已安装的 profile 用于还原映射');
|
|
421
|
-
}
|
|
422
|
-
byTarget.set(hostKey, base);
|
|
423
|
-
} else {
|
|
424
|
-
// 与已安装 profile 的别名取并集:聚合包清单可能在上一次失败的重建里丢过条目
|
|
425
|
-
const hostTarget = byTarget.has(hostKey) ? hostKey : targets[0];
|
|
426
|
-
const base = hostMapFromProfile();
|
|
427
|
-
if (base !== null) {
|
|
428
|
-
const merged = new Map(byTarget.get(hostTarget));
|
|
429
|
-
let added = 0;
|
|
430
|
-
for (const [orig, info] of base) {
|
|
431
|
-
if (!merged.has(orig)) { merged.set(orig, info); added += 1; }
|
|
432
|
-
}
|
|
433
|
-
if (added > 0) console.log(`[repack] --reuse:聚合包清单缺 ${added} 个条目 → 已用 profile 别名补齐`);
|
|
434
|
-
byTarget.set(hostTarget, merged);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
const known = byTarget.get(hostKey) ?? byTarget.get(targets[0]);
|
|
438
|
-
// 其余目标:按后缀替换推导(平台专属包名只在结尾的 -<platform>-<arch> 上不同)
|
|
439
|
-
const knownTarget = byTarget.has(hostKey) ? hostKey : targets[0];
|
|
440
|
-
for (const target of targets) {
|
|
441
|
-
if (byTarget.has(target)) continue;
|
|
442
|
-
const map = new Map();
|
|
443
|
-
for (const [orig, info] of known) {
|
|
444
|
-
const pkgName = info.pkgName.endsWith(`-${knownTarget}`)
|
|
445
|
-
? `${info.pkgName.slice(0, -knownTarget.length)}${target}`
|
|
446
|
-
: info.pkgName;
|
|
447
|
-
map.set(orig, { pkgName, version: info.version });
|
|
448
|
-
}
|
|
449
|
-
byTarget.set(target, map);
|
|
450
|
-
}
|
|
451
|
-
const hostSet = byTarget.get(knownTarget);
|
|
452
|
-
const repack = new Map();
|
|
453
|
-
for (const [orig, info] of hostSet) {
|
|
454
|
-
repack.set(orig, { pkg: { version: info.version }, platformSpecific: info.pkgName.endsWith(`-${knownTarget}`) });
|
|
406
|
+
const modules = readModules(targets);
|
|
407
|
+
if (modules.length === 0) {
|
|
408
|
+
throw new Error('--reuse 需要 lib/vendored.json、repack/aggregator/*/package.json 或一个已安装的 profile 用于还原重打包表');
|
|
455
409
|
}
|
|
456
410
|
// 已有原生包目录 → plan 项;顺带清掉将被重建的树目录(旧 code-server / argon2 目录一并清理)
|
|
457
411
|
const reusedItems = [];
|
|
@@ -464,50 +418,125 @@ function reuseExisting(targets, hostKey) {
|
|
|
464
418
|
if (m === null) continue;
|
|
465
419
|
reusedItems.push({ dir: join(buildRoot, ent), file: tgzName(m.name, m.version) });
|
|
466
420
|
}
|
|
467
|
-
// 纯 JS 直装集:插件现有 dependencies
|
|
421
|
+
// 纯 JS 直装集:插件现有 dependencies 里既不是树包、也不是重打包子包的项
|
|
422
|
+
const vendoredNames = new Set(modules.map((m) => m.package));
|
|
468
423
|
const pluginPkg = readJson(join(pkgRoot, 'package.json'));
|
|
469
424
|
const declare = Object.entries(pluginPkg.dependencies ?? {})
|
|
470
|
-
.filter(([name]) =>
|
|
425
|
+
.filter(([name]) => !vendoredNames.has(name) && !/\/(?:dshcs-vscode-server|dshcs-code-server)$/.test(name))
|
|
471
426
|
.map(([name, version]) => ({ name, version }));
|
|
472
427
|
rmSync(join(OUT, 'aggregator'), { recursive: true, force: true });
|
|
473
|
-
console.log(`[repack] --reuse:复用 ${reusedItems.length} 个已打包目录、${
|
|
474
|
-
return {
|
|
428
|
+
console.log(`[repack] --reuse:复用 ${reusedItems.length} 个已打包目录、${modules.length} 个重打包条目、${declare.length} 个纯 JS 直装依赖`);
|
|
429
|
+
return { modules, declare, reusedItems };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** 重打包子包的平台后缀(平台专属包才有),如 `@<scope>/dshcs-kerberos-win32-arm64`。 */
|
|
433
|
+
const PLATFORM_SUFFIX = /-(win32|darwin|linux)-(arm64|x64)$/;
|
|
434
|
+
|
|
435
|
+
/** 由「原名 + 真包名 + 版本」得到重打包条目(平台专属的存**基名**,运行时按目标拼后缀)。 */
|
|
436
|
+
function moduleEntry(alias, pkgName, version) {
|
|
437
|
+
const hit = PLATFORM_SUFFIX.exec(pkgName);
|
|
438
|
+
return hit === null
|
|
439
|
+
? { alias, package: pkgName, version, platform: false }
|
|
440
|
+
: { alias, package: pkgName.slice(0, -hit[0].length), version, platform: true };
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** 读「原名 → 重打包子包」表:优先 lib/vendored.json(0.3.45 起),
|
|
444
|
+
* 否则从平台聚合包清单(0.3.44 及更早)或已安装 profile 迁移,再否则返回空表。 */
|
|
445
|
+
function readModules(targets) {
|
|
446
|
+
const vendoredFile = join(pkgRoot, 'lib', 'vendored.json');
|
|
447
|
+
const doc = readJson(vendoredFile);
|
|
448
|
+
if (doc !== null && Array.isArray(doc.modules) && doc.modules.length > 0) {
|
|
449
|
+
const modules = doc.modules
|
|
450
|
+
.filter((m) => m !== null && typeof m === 'object' && typeof m.alias === 'string' && typeof m.package === 'string')
|
|
451
|
+
.map((m) => ({
|
|
452
|
+
alias: m.alias,
|
|
453
|
+
package: m.package,
|
|
454
|
+
version: typeof m.version === 'string' ? m.version : null,
|
|
455
|
+
platform: m.platform === true,
|
|
456
|
+
}))
|
|
457
|
+
.sort((a, b) => a.alias.localeCompare(b.alias));
|
|
458
|
+
if (modules.length > 0) {
|
|
459
|
+
console.log(`[repack] 读 lib/vendored.json:${modules.length} 个重打包条目`);
|
|
460
|
+
return modules;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
// 迁移:旧的聚合包清单(每个目标一份,dependencies 里是 npm: 别名)
|
|
464
|
+
let source = null;
|
|
465
|
+
for (const target of targets) {
|
|
466
|
+
const agg = readJson(join(OUT, 'aggregator', target, 'package.json'));
|
|
467
|
+
if (agg === null) continue;
|
|
468
|
+
const map = new Map();
|
|
469
|
+
for (const [orig, spec] of Object.entries(agg.dependencies ?? {})) {
|
|
470
|
+
// 旧的聚合包清单里可能仍带 argon2(已随 code-server 服务层移除)
|
|
471
|
+
if (orig === 'argon2') continue;
|
|
472
|
+
const hit = /^npm:(.+)@([^@]+)$/.exec(spec);
|
|
473
|
+
if (hit === null) throw new Error(`无法解析聚合包依赖 ${orig}: ${spec}`);
|
|
474
|
+
map.set(orig, { pkgName: hit[1], version: hit[2] });
|
|
475
|
+
}
|
|
476
|
+
if (map.size > 0) { source = { label: `repack/aggregator/${target}`, map }; break; }
|
|
477
|
+
}
|
|
478
|
+
// 与已安装 profile 的别名取并集:聚合包清单可能在上一次失败的重建里丢过条目
|
|
479
|
+
const base = hostMapFromProfile();
|
|
480
|
+
if (source === null && base === null) return [];
|
|
481
|
+
const merged = new Map(source !== null ? source.map : []);
|
|
482
|
+
if (base !== null) {
|
|
483
|
+
let added = 0;
|
|
484
|
+
for (const [orig, info] of base) {
|
|
485
|
+
if (!merged.has(orig)) { merged.set(orig, info); added += 1; }
|
|
486
|
+
}
|
|
487
|
+
if (added > 0) console.log(`[repack] 重打包表缺 ${added} 个条目 → 已用 profile 别名补齐`);
|
|
488
|
+
}
|
|
489
|
+
const modules = [...merged]
|
|
490
|
+
.map(([alias, info]) => moduleEntry(alias, info.pkgName, info.version))
|
|
491
|
+
.sort((a, b) => a.alias.localeCompare(b.alias));
|
|
492
|
+
console.log(`[repack] 迁移 ${modules.length} 个重打包条目(${source !== null ? source.label : 'profile 别名'})→ 将写入 lib/vendored.json`);
|
|
493
|
+
return modules;
|
|
475
494
|
}
|
|
476
495
|
|
|
477
496
|
function main() {
|
|
478
497
|
const hostKey = `${process.platform}-${process.arch}`;
|
|
479
498
|
const targets = TARGETS.length > 0 ? TARGETS : [hostKey];
|
|
480
|
-
const pluginVersion = readJson(join(pkgRoot, 'package.json'))?.version ?? '0.0.0';
|
|
481
499
|
|
|
482
|
-
let repack;
|
|
500
|
+
let repack = null;
|
|
501
|
+
let modules;
|
|
483
502
|
let declare;
|
|
484
503
|
let reusedItems = [];
|
|
485
504
|
let autoSourceTree = null;
|
|
486
505
|
if (REUSE) {
|
|
487
|
-
({
|
|
506
|
+
({ modules, declare, reusedItems } = reuseExisting(targets));
|
|
488
507
|
} else {
|
|
489
508
|
const autoSource = FROM === null;
|
|
490
509
|
const sourceTree = autoSource ? prepareSourceTree() : FROM;
|
|
491
510
|
if (autoSource) autoSourceTree = sourceTree;
|
|
492
511
|
({ repack, declare } = analyze(sourceTree));
|
|
512
|
+
modules = [...repack]
|
|
513
|
+
.map(([name, r]) => ({
|
|
514
|
+
alias: name,
|
|
515
|
+
package: `${SCOPE}/dshcs-${flat(name)}`,
|
|
516
|
+
version: r.pkg.version,
|
|
517
|
+
platform: r.platformSpecific,
|
|
518
|
+
}))
|
|
519
|
+
.sort((a, b) => a.alias.localeCompare(b.alias));
|
|
493
520
|
rmSync(OUT, { recursive: true, force: true });
|
|
494
521
|
}
|
|
495
|
-
console.log(`\n重打包集(${
|
|
496
|
-
for (const
|
|
497
|
-
console.log(` ${
|
|
522
|
+
console.log(`\n重打包集(${modules.length}):`);
|
|
523
|
+
for (const m of modules) {
|
|
524
|
+
console.log(` ${m.alias}@${m.version} → ${m.package}${m.platform ? `-<平台>` : ''} ${m.platform ? '[平台专属]' : '[全平台]'}`);
|
|
498
525
|
}
|
|
499
526
|
console.log(`\n直装集(${declare.length}): ${declare.map((d) => d.name).join(', ')}\n`);
|
|
500
527
|
|
|
501
528
|
mkdirSync(join(OUT, 'build'), { recursive: true });
|
|
502
529
|
|
|
503
|
-
//
|
|
530
|
+
// 每个目标的包名映射:平台专属的按目标拼后缀,平台无关的到处同名
|
|
504
531
|
const byTarget = new Map();
|
|
505
532
|
for (const target of targets) {
|
|
506
|
-
const
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
533
|
+
const map = new Map(); // 原包名 -> { pkgName, version, platformSpecific }
|
|
534
|
+
for (const m of modules) {
|
|
535
|
+
map.set(m.alias, {
|
|
536
|
+
pkgName: `${m.package}${m.platform ? `-${target}` : ''}`,
|
|
537
|
+
version: m.version,
|
|
538
|
+
platformSpecific: m.platform,
|
|
539
|
+
});
|
|
511
540
|
}
|
|
512
541
|
byTarget.set(target, map);
|
|
513
542
|
}
|
|
@@ -599,45 +628,45 @@ function main() {
|
|
|
599
628
|
}
|
|
600
629
|
if (crossTree !== null) rmSync(crossTree, { recursive: true, force: true });
|
|
601
630
|
}
|
|
602
|
-
// 3)
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
dependencies: deps,
|
|
619
|
-
license: 'MIT',
|
|
620
|
-
repository: readJson(join(pkgRoot, 'package.json'))?.repository ?? undefined,
|
|
621
|
-
}, null, 2) + '\n', 'utf8');
|
|
622
|
-
plan.push({ dir: aggDir, file: tgzName(aggName, pluginVersion), aggregator: true, target });
|
|
623
|
-
}
|
|
631
|
+
// 3) 重打包表(lib/vendored.json,随插件发布):运行时用它把真名子包补成原始名字
|
|
632
|
+
const vendoredDoc = {
|
|
633
|
+
schemaVersion: 1,
|
|
634
|
+
generatedBy: 'scripts/vendor-repacks.mjs',
|
|
635
|
+
scope: SCOPE,
|
|
636
|
+
targets,
|
|
637
|
+
modules: modules.map((m) => {
|
|
638
|
+
const entry = { alias: m.alias, package: m.package, version: m.version };
|
|
639
|
+
if (m.platform) entry.platform = true;
|
|
640
|
+
return entry;
|
|
641
|
+
}),
|
|
642
|
+
};
|
|
643
|
+
writeFileSync(join(pkgRoot, 'lib', 'vendored.json'), `${JSON.stringify(vendoredDoc, null, 2)}\n`, 'utf8');
|
|
644
|
+
const independent = modules.filter((m) => !m.platform);
|
|
645
|
+
const specific = modules.filter((m) => m.platform);
|
|
646
|
+
console.log(`[repack] 重打包表 → lib/vendored.json(${modules.length} 个:全平台 ${independent.length} / 平台专属 ${specific.length})`);
|
|
624
647
|
|
|
625
648
|
writeFileSync(join(OUT, 'pack-plan.json'), JSON.stringify(plan, null, 2) + '\n', 'utf8');
|
|
626
649
|
console.log(`[repack] 生成 ${plan.length} 个待打包目录 → repack/ (计划:repack/pack-plan.json)`);
|
|
627
650
|
|
|
628
|
-
// 4) 改写插件 package.json
|
|
629
|
-
//
|
|
651
|
+
// 4) 改写插件 package.json 的依赖:树 + 纯 JS 直装集 + **全平台**重打包包写 dependencies(真名);
|
|
652
|
+
// 平台专属的重打包包按目标写 optionalDependencies(真名 + 包自带 os/cpu,包管理器按架构自动选)。
|
|
653
|
+
// 这样每个包在依赖图里都是「根项目的直接依赖」,不依赖 pnpm 对可选子树别名的处理
|
|
654
|
+
// (见 docs/desktop-first-install-root-cause.md)。
|
|
630
655
|
const pkgFile = join(pkgRoot, 'package.json');
|
|
631
656
|
const pluginPkg = readJson(pkgFile);
|
|
632
657
|
pluginPkg.dependencies = Object.fromEntries([
|
|
633
658
|
[vscodePkg.name, vscodePkg.version],
|
|
634
659
|
...declare.map((d) => [d.name, d.version]),
|
|
660
|
+
...independent.map((m) => [m.package, m.version]),
|
|
635
661
|
].sort(([a], [b]) => a.localeCompare(b)));
|
|
636
662
|
pluginPkg.optionalDependencies = Object.fromEntries(targets
|
|
637
|
-
.
|
|
663
|
+
.flatMap((t) => specific.map((m) => [`${m.package}-${t}`, m.version]))
|
|
664
|
+
.sort(([a], [b]) => a.localeCompare(b)));
|
|
638
665
|
writeFileSync(pkgFile, JSON.stringify(pluginPkg, null, 2) + '\n', 'utf8');
|
|
639
|
-
console.log(`[repack] 已写入 package.json:dependencies ${
|
|
640
|
-
+ `(VS Code 树 + 纯 JS
|
|
666
|
+
console.log(`[repack] 已写入 package.json:dependencies ${Object.keys(pluginPkg.dependencies).length} 个`
|
|
667
|
+
+ `(VS Code 树 + 纯 JS + ${independent.length} 个全平台重打包包),`
|
|
668
|
+
+ `optionalDependencies ${Object.keys(pluginPkg.optionalDependencies).length} 个`
|
|
669
|
+
+ `(${specific.length} 个平台专属重打包包 × ${targets.length} 个目标)`);
|
|
641
670
|
|
|
642
671
|
// 5) 可选:直接打包
|
|
643
672
|
if (DO_PACK) {
|