@deepseek-ai/dsh-plugin-manager 0.2.0-rc.2 → 0.2.1-alpha.1
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.i18n.yaml +6 -6
- package/README.md +10 -5
- package/README.zh.md +10 -5
- package/lib/index.js +58 -8
- package/lib/typert.host.js +20 -13
- package/lib/typert.remote-client.js +15 -8
- package/lib/types/index.d.ts +6 -4
- package/lib/types/index.js +42 -11
- package/lib/types/install-spec.d.ts +13 -0
- package/lib/types/install-spec.js +33 -1
- package/lib/types/types.d.ts +7 -0
- package/package.json +34 -33
package/README.i18n.yaml
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
en: 87679c51aa3693f5
|
|
16
16
|
zh: 11684a48a5806afb
|
|
17
17
|
/deepseek-ai-dsh-plugin-manager/use-this-package:
|
|
18
|
-
en:
|
|
19
|
-
zh:
|
|
18
|
+
en: 2cbe6e4a68994e2f
|
|
19
|
+
zh: ba0328f5538d8746
|
|
20
20
|
/deepseek-ai-dsh-plugin-manager/use-this-package/version-compatibility-and-exemptions:
|
|
21
21
|
en: 920c0acb3b9feaf3
|
|
22
22
|
zh: ba6e832a256880a7
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
en: d5643a06f300c30a
|
|
25
25
|
zh: 9348b6a7676a2849
|
|
26
26
|
/deepseek-ai-dsh-plugin-manager/understand-the-implementation:
|
|
27
|
-
en:
|
|
28
|
-
zh:
|
|
27
|
+
en: 7b54d30e231bb325
|
|
28
|
+
zh: 08df9441fed9927f
|
|
29
29
|
/deepseek-ai-dsh-plugin-manager/further-exploration:
|
|
30
30
|
en: c6538243d8702a0d
|
|
31
31
|
zh: 04a82d923353c75f
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
en: fde2d9491fe5dd4f
|
|
49
49
|
zh: ef6e6db15c4f19d2
|
|
50
50
|
/deepseek-ai-dsh-plugin-manager/known-limitations-and-deferred-work/failure-behavior:
|
|
51
|
-
en:
|
|
52
|
-
zh:
|
|
51
|
+
en: 4447e4df8d62b274
|
|
52
|
+
zh: c76443e2edb97b1d
|
|
53
53
|
/deepseek-ai-dsh-plugin-manager/known-limitations-and-deferred-work/dev-note:
|
|
54
54
|
en: e67f633dc6fe7773
|
|
55
55
|
zh: 3bb0c20c93f16f98
|
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ A plugin toggle updates only `disabled` in the last matching override in the pro
|
|
|
41
41
|
|
|
42
42
|
A selected bundle that cannot load remains in `listBundles` with an `error`; `enabled` records the saved selection, not successful loading. The plugin page shows the error and allows deselection. A broken bundle cannot be enabled. Management bundles remain protected if their files become unreadable.
|
|
43
43
|
|
|
44
|
-
`listBundles` includes optional display `meta` for each bundle and its declared plugin rows, including disabled bundles. The Client selects a language from these values. The separate `description` field is the bundle's raw `package.json.description`; a metadata diagnostic does not block management. The `plugin_manager` tool omits UI display metadata from list results.
|
|
44
|
+
`listBundles` includes optional display `meta` for each bundle and its declared plugin rows, including disabled bundles. The Client selects a language from these values. The separate `description` field is the bundle's raw `package.json.description`; a metadata diagnostic does not block management. `source` gives a profile dependency as a spec `pnpm add` accepts: a git address or URL as recorded without the user information of an http(s) URL (a query string stays as recorded), a `file:` or `link:` path made absolute against the profile or the home directory for `~`, and a registry range, tag, or alias after the package name, as is any spec of a package installed under another name; a bundle the installation supplies has none, even when the profile also depends on it, because the installation's copy is the one that loads. The `plugin_manager` tool omits UI display metadata from list results.
|
|
45
45
|
|
|
46
46
|
`inspect(spec, options)` reads what a spec names before anything installs: a registry name is asked of the registry through `pnpm view`, run in the profile directory so the same proxy and authentication settings apply as to the install; an absolute path has its `package.json` read; a git address or tarball answers only its form and the `host` it is fetched from. The answer carries the name, version, description, whether the package declares a bundle, and the `registry` that answered, or a `problem`: `invalid-spec`, `already-installed`, `not-found`, `not-a-package`, `not-a-bundle`, `network`, or `unknown`, with the `registries` asked. A caller's `signal` or `inspectTimeoutMs` ends the lookup.
|
|
47
47
|
|
|
@@ -51,7 +51,7 @@ Registries are asked in turn. The plan starts at `options.registry`, else the co
|
|
|
51
51
|
|
|
52
52
|
The browser-safe `@deepseek-ai/dsh-plugin-manager/registry` entry exports `OFFICIAL_NPM_REGISTRY` and `NPMMIRROR_REGISTRY` for consumers that identify those public registries.
|
|
53
53
|
|
|
54
|
-
`installBundle` accepts a caller-generated `requestId`, under which `plugin-manager/install-log` streams each pnpm run's output and `plugin-manager/install-state` announces `installing`, `cancelling`, and `applying`; `installing` is announced once per registry asked, with the `attempt`'s registry, position, and the plan's length. The install asks the same plan as the lookup, from `options.registry`, running `pnpm add` with `--registry` and restoring the profile files between attempts; it moves on for the failures another registry can change and stops at any other, and at a failure whose error line names the host a git or tarball spec is fetched from, which no registry stands in for; `failedAt` says which of the two the last failed run could not reach. `cancelInstall(requestId)` stops the run and answers `cancelled` only after the Git check or pnpm exited and the files are back, `too-late` once the bundle is being applied, and `not-running` for any other id; the install call then reports `application: 'cancelled'`. A run that fails, is cancelled, or adds a package without a bundle patch restores `package.json` and `pnpm-lock.yaml` as they were; `packageResult.kind` classifies the last run from its exit and output, `registries` names every registry asked,
|
|
54
|
+
`installBundle` accepts a caller-generated `requestId`, under which `plugin-manager/install-log` streams each pnpm run's output and `plugin-manager/install-state` announces `installing`, `cancelling`, and `applying`; `installing` is announced once per registry asked, with the `attempt`'s registry, position, and the plan's length. The install asks the same plan as the lookup, from `options.registry`, running `pnpm add` with `--registry` and restoring the profile files between attempts; it moves on for the failures another registry can change and stops at any other, and at a failure whose error line names the host a git or tarball spec is fetched from, which no registry stands in for; `failedAt` says which of the two the last failed run could not reach. `cancelInstall(requestId)` stops the run and answers `cancelled` only after the Git check or pnpm exited and the files are back, `too-late` once the bundle is being applied, and `not-running` for any other id; the install call then reports `application: 'cancelled'`. A run that fails, is cancelled, or adds a package without a bundle patch restores `package.json` and `pnpm-lock.yaml` as they were; `packageResult.kind` classifies the last run from its exit and output, `registries` names every registry asked, `bundle` names the package a finished run added, and `version` its manifest version, which can differ from the version `inspect` read, for example when pnpm's `minimumReleaseAge` holds back a recent release. `listBundles` carries each bundle's one-liner (the package `description`), the rows its patch declares with their live entries, and the built-in rows it overrides; it lists the profile's own bundles, the bundles the installation supplies, and a selected name without a bundle patch as a `not-bundle` problem, while an unselected plain dependency is left out. A bundle the launcher's `OPTIONAL_BUNDLES` names is `optional`: shipped switched off for the person to turn on, never removable, and selected by no shipped template ([rationale](../../../.agents/notes/implemented/process/2026-09-15-shipped-optional-bundles.md)). Every completed operation emits `plugin-manager/changed`; a patch generation applied outside the manager, by HMR's watcher after a CLI or hand edit, announces nothing, so the page learns of it on its next read.
|
|
55
55
|
|
|
56
56
|
`waitForInstall(requestId)` lets a client recover a lost response by waiting for the active installation, including its non-cancellable application phase. It returns the same outcome as the original call, or `null` if the request is not active. Completed results are not retained; `null` establishes neither success nor cancellation.
|
|
57
57
|
|
|
@@ -89,9 +89,13 @@ The CLI exposes `dsh plugin --profile <profile> version-exemptions`, `allow-vers
|
|
|
89
89
|
<details>
|
|
90
90
|
<summary>Implementation internals — click to expand</summary>
|
|
91
91
|
|
|
92
|
-
The service and `dsh plugin` share the package operations in [operations.ts](src/operations.ts). The launcher supplies the current profile; [DSH HMR](../hmr/README.md) serializes module reloads, file watching and management writes. Each refresh re-reads bundle selection and patch layers, updates the original root Include, and awaits removed plugin resources as well as the remaining Loader tree. CLI and service operations share the profile manifest writer lock to prevent concurrent package and manifest writes. HMR does not acquire that lock. Pnpm runs outside the HMR queue; installation selects the bundle after pnpm succeeds, while removal deselects and unloads the bundle before pnpm runs.
|
|
92
|
+
The service and `dsh plugin` share the package operations in [operations.ts](src/operations.ts). The launcher supplies the current profile; [DSH HMR](../hmr/README.md) serializes module reloads, file watching and management writes. Each configuration refresh re-reads bundle selection and patch layers, updates the original root Include, and awaits removed plugin resources as well as the remaining Loader tree. CLI and service operations share the profile manifest writer lock to prevent concurrent package and manifest writes. HMR does not acquire that lock. Pnpm runs outside the HMR queue; installation selects the bundle after pnpm succeeds, while removal deselects and unloads the bundle before pnpm runs.
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
Installing a new package, enabling a bundle, disabling it with HMR after its plugins stop, and a successful removal publish the profile's runtime resolution. Shared dependencies may pass to another bundle's declaration when their normalized directory, version, and scope stay unchanged. Without HMR, a deselected startup bundle still runs: while any such bundle is absent from the saved selection, subsequent package operations retain the entire current table and report `restart-required`. Other new installations and removals of non-running bundles can still publish without HMR; activation waits for restart. Overwriting an installed package publishes nothing and reports `restart-required` ([decision](../../../.agents/notes/implemented/architecture/2026-09-30-profile-package-refresh-and-manifest-invalidation.md)).
|
|
95
|
+
|
|
96
|
+
A service run whose captured output stays silent for `idleTimeoutMs` is terminated, reports `timedOut` alongside its exit status, is classified `timeout` whatever status the signal left behind, and is not retried on the next registry, which bounds how long one operation can hold the profile lock; the CLI inherits the terminal, captures no output, and stays unbounded for its operator to interrupt. A run completes when its process exits, and the pipes then drain under a bounded grace period, so a descendant that inherited them cannot hold the operation open. A terminated run stops its whole process tree and waits for it, because a lifecycle script outlives the pnpm process that started it (issue #4981). Each operation records the pnpm run it starts in `.plugin-manager/run.json` and removes the record when the run ends. A lock whose holder process exited is taken over by the next writer, but that process's pnpm tree can still be running, so an operation that finds a record waits up to five seconds for the recorded run to stop and otherwise fails with a diagnostic that names the process and the record, without running pnpm. Dependency-only changes do not trigger configuration reloads.
|
|
97
|
+
|
|
98
|
+
Results contain the last attempted stage, target, saved-state change, application status and error codes. Web dictionaries render management text; pnpm and Loader diagnostics remain unmodified. Unrelated pre-existing inactive entries return warnings; new or changed failures and inactive explicit enablement targets fail the operation. A failed or cancelled package run, or failed bundle validation, restores the manifest and lockfile snapshotted before pnpm ran ([rationale](../../../.agents/notes/implemented/architecture/2026-09-15-guided-plugin-installation.md)); later failures retain saved changes as described in [Failure behavior](#failure-behavior). Installations are tracked by request id until their call settles, so a cancellation names one run and joins its settlement without taking the profile lock. The CLI inherits authentication variables and terminal descriptors; service operations use a scrubbed environment and captured output.
|
|
95
99
|
|
|
96
100
|
</details>
|
|
97
101
|
|
|
@@ -143,9 +147,10 @@ Failures preserve completed steps and report the actual remaining state. Profile
|
|
|
143
147
|
|---|---|
|
|
144
148
|
| Install: pnpm or bundle validation fails | Restore `package.json` and `pnpm-lock.yaml` as snapshotted before pnpm ran; files pnpm downloaded may remain. Report installation failure. |
|
|
145
149
|
| Enable: saving selection or loading fails | Keep the installed dependency and any saved selection. Report enablement failure; allow repair, disablement or removal. |
|
|
150
|
+
| Resolution publication after a successful package operation fails | Keep completed disk changes and the previous runtime table. Report `application: 'failed'` with the publication diagnostic; pnpm's zero exit code does not mean publication succeeded. |
|
|
146
151
|
| Remove: any step fails | Stop at the failed step. Preserve completed changes, retain remaining dependencies for retry, and report removal failure. Do not re-enable the bundle. |
|
|
147
152
|
|
|
148
|
-
Installation finishes after pnpm and bundle validation succeed; subsequent enablement failure does not undo installation. Removal proceeds in order: remove the bundle from `dsh.profile.bundles`, unload its runtime contributions,
|
|
153
|
+
Installation finishes after pnpm and bundle validation succeed; subsequent publication or enablement failure does not undo installation. Removal proceeds in order: remove the bundle from `dsh.profile.bundles`, unload its runtime contributions, run `pnpm remove`, then publish runtime resolution unless a deselected startup bundle still runs without HMR. A failed step prevents subsequent steps; publication failure after removal does not restore deleted packages. A selected name that neither the profile's dependencies nor the installation holds, such as a retired bundle, is also removable; its removal stops after deselection and runs no pnpm.
|
|
149
154
|
|
|
150
155
|
Restoration rewrites only the two snapshotted files; user-authored patch configuration, application data, diagnostic logs and files pnpm downloaded remain untouched, and the next package operation prunes packages no manifest references.
|
|
151
156
|
|
package/README.zh.md
CHANGED
|
@@ -41,7 +41,7 @@ kind: "package-reference"
|
|
|
41
41
|
|
|
42
42
|
已选择但无法加载的组合包仍会出现在 `listBundles` 中,并携带 `error`;`enabled` 表示保存的选择,不代表加载成功。插件页面显示错误并允许取消选择。损坏的组合包无法启用。管理组合包的文件变得不可读后仍受保护。
|
|
43
43
|
|
|
44
|
-
`listBundles` 为各组合包及其声明的插件行提供可选的展示 `meta`,包括已禁用的组合包。Client 从这些值中选择语言。单独的 `description` 字段是该组合包原始的 `package.json.description`;元信息诊断不会阻止管理操作。`plugin_manager` 工具的列表结果不包含 UI 展示元信息。
|
|
44
|
+
`listBundles` 为各组合包及其声明的插件行提供可选的展示 `meta`,包括已禁用的组合包。Client 从这些值中选择语言。单独的 `description` 字段是该组合包原始的 `package.json.description`;元信息诊断不会阻止管理操作。`source` 以 `pnpm add` 接受的 spec 给出 profile 依赖:git 地址或 URL 按记录给出,但去掉 http(s) URL 的 user information(查询字符串保持原样);`file:` 或 `link:` 路径按 profile 目录解析为绝对路径,`~` 展开为主目录;注册表版本范围、标签或别名接在包名的 `@` 之后,以别名安装的包的 spec 也是如此。安装提供的组合包没有该字段,即使 profile 也依赖它,因为实际加载的是安装中的副本。`plugin_manager` 工具的列表结果不包含 UI 展示元信息。
|
|
45
45
|
|
|
46
46
|
`inspect(spec, options)` 在任何东西安装之前读出 spec 指向什么:注册表包名通过 `pnpm view` 询问注册表,在 profile 目录中运行,因而与安装使用同样的代理与认证设置;绝对路径读取其 `package.json`;git 地址或 tarball 只答复自己的形式和它被拉取的 `host`。答复携带名称、版本、描述、该包是否声明组合包,以及作答的 `registry`,否则给出 `problem`:`invalid-spec`、`already-installed`、`not-found`、`not-a-package`、`not-a-bundle`、`network` 或 `unknown`,并附上问过的 `registries`。调用方的 `signal` 或 `inspectTimeoutMs` 会结束查询。
|
|
47
47
|
|
|
@@ -51,7 +51,7 @@ kind: "package-reference"
|
|
|
51
51
|
|
|
52
52
|
可在浏览器使用的 `@deepseek-ai/dsh-plugin-manager/registry` 入口导出 `OFFICIAL_NPM_REGISTRY` 和 `NPMMIRROR_REGISTRY`,供消费方识别这两个公共注册表。
|
|
53
53
|
|
|
54
|
-
`installBundle` 接受调用方生成的 `requestId`,`plugin-manager/install-log` 在其下流式转发每次 pnpm 运行的输出,`plugin-manager/install-state` 通告 `installing`、`cancelling` 与 `applying`;每问一个注册表通告一次 `installing`,`attempt` 带上这次的注册表、序号和计划长度。安装沿用查询的计划,从 `options.registry` 开始,以 `--registry` 运行 `pnpm add`,两次尝试之间恢复 profile 文件;换一个注册表能改变的失败才转问下一个,其他失败即停,错误行点名 git 或 tarball spec 自身拉取主机的失败也停,因为没有注册表能替代那台主机;`failedAt` 说明最后一次失败的运行连不上的是二者中的哪一个。`cancelInstall(requestId)` 停止运行,只在 Git 检查或 pnpm 退出且文件恢复后答复 `cancelled`,组合包已在应用时答复 `too-late`,其他 id 答复 `not-running`;安装调用随后报告 `application: 'cancelled'`。失败、被取消或装入了没有组合包 patch 的包的运行,会把 `package.json` 与 `pnpm-lock.yaml` 恢复原样;`packageResult.kind` 按退出方式与输出对最后一次运行分类,`registries` 列出问过的每个注册表,`bundle`
|
|
54
|
+
`installBundle` 接受调用方生成的 `requestId`,`plugin-manager/install-log` 在其下流式转发每次 pnpm 运行的输出,`plugin-manager/install-state` 通告 `installing`、`cancelling` 与 `applying`;每问一个注册表通告一次 `installing`,`attempt` 带上这次的注册表、序号和计划长度。安装沿用查询的计划,从 `options.registry` 开始,以 `--registry` 运行 `pnpm add`,两次尝试之间恢复 profile 文件;换一个注册表能改变的失败才转问下一个,其他失败即停,错误行点名 git 或 tarball spec 自身拉取主机的失败也停,因为没有注册表能替代那台主机;`failedAt` 说明最后一次失败的运行连不上的是二者中的哪一个。`cancelInstall(requestId)` 停止运行,只在 Git 检查或 pnpm 退出且文件恢复后答复 `cancelled`,组合包已在应用时答复 `too-late`,其他 id 答复 `not-running`;安装调用随后报告 `application: 'cancelled'`。失败、被取消或装入了没有组合包 patch 的包的运行,会把 `package.json` 与 `pnpm-lock.yaml` 恢复原样;`packageResult.kind` 按退出方式与输出对最后一次运行分类,`registries` 列出问过的每个注册表,`bundle` 给出完成的运行新增的包,`version` 给出它的清单版本,它可能与 `inspect` 读到的版本不同,例如 pnpm 的 `minimumReleaseAge` 暂缓了最近的发布。`listBundles` 携带每个组合包的一句话简介(包的 `description`)、其 patch 声明的行及其存活条目,以及它覆盖的内置行;它列出 profile 自己的组合包、安装提供的组合包,以及被选中却没有组合包 patch 的名字(作为 `not-bundle` 问题),未选中的普通依赖不列出。启动器的 `OPTIONAL_BUNDLES` 点名的组合包是 `optional`:随安装提供、默认关闭、由用户开启,永不可卸载,也不被任何随附模板选中([理由](../../../.agents/notes/implemented/process/2026-09-15-shipped-optional-bundles.zh.md))。每个完成的操作都会发出 `plugin-manager/changed`;在管理器之外应用的一代 patch(HMR 监视到 CLI 或手工编辑后)不发通知,页面要到下一次读取才知道。
|
|
55
55
|
|
|
56
56
|
`waitForInstall(requestId)` 让客户端在响应丢失后等待活动安装的结果,包括不可取消的应用阶段。它返回与原调用相同的结果,请求不在活动中时返回 `null`。已完成的结果不保留;`null` 不表示成功或已取消。
|
|
57
57
|
|
|
@@ -89,9 +89,13 @@ CLI 提供 `dsh plugin --profile <profile> version-exemptions`、`allow-version
|
|
|
89
89
|
<details>
|
|
90
90
|
<summary>实现细节——点击展开</summary>
|
|
91
91
|
|
|
92
|
-
服务与 `dsh plugin` 共用 [operations.ts](src/operations.ts) 中的包管理操作。启动器提供当前 profile;[DSH HMR](../hmr/README.zh.md)
|
|
92
|
+
服务与 `dsh plugin` 共用 [operations.ts](src/operations.ts) 中的包管理操作。启动器提供当前 profile;[DSH HMR](../hmr/README.zh.md) 串行执行模块重载、文件监听和管理写入。每次配置刷新重新读取组合包选择与 patch 层,更新原有根 Include,并等待已移除插件释放资源及剩余 Loader 树稳定。CLI 与 service 操作共用 profile manifest 写锁,防止并发包操作和 manifest 写入。HMR 不获取该锁。pnpm 在 HMR 队列之外执行;安装在 pnpm 成功后选入组合包,删除则在执行 pnpm 前取消选入并完成卸载。
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
安装新包、启用组合包、有 HMR 时停用组合包并等插件退出后、以及删除成功后,都会发布 profile 的 runtime resolution。共享依赖的规范化目录、版本和作用域不变时,可以改由另一个组合包声明。没有 HMR 时,已取消选入的启动组合包仍在运行:只要保存的选择列表中缺少任何这样的组合包,后续包操作就保留整张当前表,并报告 `restart-required`。其他新安装和未运行组合包的删除仍可在没有 HMR 时发布;激活等待重启。覆盖已安装的包不发布,并报告 `restart-required`([决策](../../../.agents/notes/implemented/architecture/2026-09-30-profile-package-refresh-and-manifest-invalidation.zh.md))。
|
|
95
|
+
|
|
96
|
+
service 运行若在 `idleTimeoutMs` 内没有任何捕获输出即被终止,与退出状态一并报告 `timedOut`,不论信号留下什么退出状态都归类为 `timeout`,且不再转问下一个注册表,因此单次操作占用 profile 锁的时长有上界;CLI 继承终端、不捕获输出,因此不受此上界约束,由操作者中断。运行以进程退出为完成点,随后只在一个有界的宽限窗口内排空管道,因此继承管道的孙进程无法让操作挂起。被终止的运行会停止整棵进程树并等待其消失,因为生命周期脚本的存活时间超过启动它的 pnpm 进程(issue #4981)。每个操作把它启动的 pnpm 运行记录在 `.plugin-manager/run.json` 中,并在运行结束时删除该记录。持有者进程已退出的锁会被下一个写入方接管,但该进程的 pnpm 进程树可能仍在运行,因此发现记录的操作最多等待五秒让记录中的运行停止,否则不运行 pnpm,并以指明该进程与记录文件的诊断失败。仅依赖字段变化不会触发配置重载。
|
|
97
|
+
|
|
98
|
+
结果包含最后尝试的阶段、目标、磁盘变化、应用状态和错误码。Web 词典呈现管理文案;pnpm 与 Loader 的诊断保持原样。无关的已有故障作为警告返回;新出现、配置变化后的故障,以及显式启用目标未激活,都会使操作失败。包管理运行失败或被取消,或组合包校验失败时,会恢复 pnpm 运行前快照的 manifest 与 lockfile([理由](../../../.agents/notes/implemented/architecture/2026-09-15-guided-plugin-installation.zh.md));后续失败按[失败行为](#failure-behavior)所述保留已保存的改动。安装按 request id 跟踪到调用结束,因此取消只针对一次运行,并且不取 profile 锁就能等待它结束。CLI 继承认证环境和终端描述符;service 使用清理后的环境并捕获输出。管理器直接读取文件和 Loader 状态,不维护第二份目标状态注册表,因此不发布单独的运行时不变式伴生入口。
|
|
95
99
|
|
|
96
100
|
</details>
|
|
97
101
|
|
|
@@ -143,9 +147,10 @@ CLI 提供 `dsh plugin --profile <profile> version-exemptions`、`allow-version
|
|
|
143
147
|
|---|---|
|
|
144
148
|
| 安装:pnpm 执行或组合包校验失败 | 恢复 pnpm 运行前快照的 `package.json` 与 `pnpm-lock.yaml`;pnpm 已下载的文件可能保留。报告安装失败。 |
|
|
145
149
|
| 启用:保存选择项或加载失败 | 保留已安装的依赖和已保存的选择项。报告启用失败,允许修正、停用或卸载。 |
|
|
150
|
+
| 包操作成功后解析表发布失败 | 保留已完成的磁盘改动和上一张运行时表。报告 `application: 'failed'` 及发布诊断;pnpm 的零退出码不代表发布成功。 |
|
|
146
151
|
| 卸载:任一步失败 | 停在失败步骤,保留已完成的改动和待重试删除的依赖,报告卸载失败。不重新启用组合包。 |
|
|
147
152
|
|
|
148
|
-
pnpm
|
|
153
|
+
pnpm 执行和组合包校验成功即完成安装,后续发布或启用失败不撤销安装。卸载依次执行:从 `dsh.profile.bundles` 移除组合包、卸载运行时贡献、执行 `pnpm remove`,然后发布 runtime resolution,除非没有 HMR 且已取消选入的启动组合包仍在运行。任一步失败都不继续执行后续步骤;删除后的发布失败不会恢复已删除的包。 profile 依赖和安装都不持有的已选名称(例如已下线的组合包)同样可卸载;其卸载在取消选择后结束,不运行 pnpm。
|
|
149
154
|
|
|
150
155
|
恢复只重写这两份快照文件;用户编写的 patch 配置、应用数据、诊断日志以及 pnpm 已下载的文件保持原样,没有 manifest 引用的包由下一次包操作清理。
|
|
151
156
|
|
package/lib/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { OPTIONAL_BUNDLES, PROFILE_COMPATIBILITY_FILENAME, bundlePatchPaths, com
|
|
|
11
11
|
import { once } from "node:events";
|
|
12
12
|
import { execa } from "execa";
|
|
13
13
|
import { scrubbedParentEnv } from "@deepseek-ai/dsh-subprocess";
|
|
14
|
+
import { homedir } from "node:os";
|
|
14
15
|
import { isAlias, isMap, isNode, isScalar, isSeq, parseDocument, visit } from "yaml";
|
|
15
16
|
//#region lib/types/install-spec.js
|
|
16
17
|
/**
|
|
@@ -115,6 +116,32 @@ function parseInstallSpec(raw) {
|
|
|
115
116
|
range
|
|
116
117
|
};
|
|
117
118
|
}
|
|
119
|
+
/** A recorded dependency value pnpm resolves outside a registry: a protocol other than a registry alias, or an scp-like git address. */
|
|
120
|
+
const NON_REGISTRY_VALUE = /^(?!(?:npm|jsr|workspace|catalog):)[a-z][a-z0-9+.-]*:|^[^@/:\s]+@[^:/\s]+:/i;
|
|
121
|
+
/** The user information of an http(s) URL, up to the last `@` before the path, which holds a password or access token when present. */
|
|
122
|
+
const HTTP_USER_INFO = /^((?:git\+)?https?:\/\/)[^/]*@/i;
|
|
123
|
+
/**
|
|
124
|
+
* The spec `pnpm add` accepts for a dependency the profile manifest records. A git address, URL, or other
|
|
125
|
+
* non-registry protocol is the recorded value without the user information of an http(s) URL, whose query string
|
|
126
|
+
* is kept; a `file:` or `link:` path, which pnpm records relative to the profile or with `~` for the home
|
|
127
|
+
* directory, becomes absolute. A registry range, tag, or alias follows the dependency name after `@`, as does a
|
|
128
|
+
* non-registry value installed under a name other than the package's own.
|
|
129
|
+
* @param name - the dependency name.
|
|
130
|
+
* @param recorded - the value the profile manifest records for it.
|
|
131
|
+
* @param profileDir - the profile directory a relative local path is resolved against.
|
|
132
|
+
* @param packageName - the name the installed package's manifest declares, when it could be read.
|
|
133
|
+
* @returns the installable spec.
|
|
134
|
+
*/
|
|
135
|
+
function dependencySpec(name, recorded, profileDir, packageName = name) {
|
|
136
|
+
const local = /^(file|link):(.*)$/s.exec(recorded);
|
|
137
|
+
let spec;
|
|
138
|
+
if (local !== null) {
|
|
139
|
+
const path = local[2].replace(/^~(?=$|[\\/])/, () => homedir());
|
|
140
|
+
spec = `${local[1]}:${resolve(profileDir, path)}`;
|
|
141
|
+
} else if (NON_REGISTRY_VALUE.test(recorded)) spec = recorded.replace(HTTP_USER_INFO, "$1");
|
|
142
|
+
else return `${name}@${recorded}`;
|
|
143
|
+
return packageName === name ? spec : `${name}@${spec}`;
|
|
144
|
+
}
|
|
118
145
|
//#endregion
|
|
119
146
|
//#region lib/types/run-tree.js
|
|
120
147
|
/** Waiting for one package run's process tree to disappear before its caller touches the profile. */
|
|
@@ -1450,14 +1477,15 @@ let PluginManager = (() => {
|
|
|
1450
1477
|
}
|
|
1451
1478
|
/** Read the profile's installed bundles, the bundles this dsh installation supplies, and the selected names that are not bundles.
|
|
1452
1479
|
* A dependency without a bundle patch is listed, as a `not-bundle` problem, only while it is selected.
|
|
1453
|
-
* @returns Package versions, manifest descriptions,
|
|
1454
|
-
* whether the installation offers the bundle, and removal availability.
|
|
1480
|
+
* @returns Package versions, manifest descriptions, the installable spec of profile dependencies, rows, optional
|
|
1481
|
+
* display metadata, activation selections, whether the installation offers the bundle, and removal availability.
|
|
1455
1482
|
*/
|
|
1456
1483
|
listBundles() {
|
|
1457
1484
|
const manifest = readProfileManifest("dsh", this.profile.dir);
|
|
1458
1485
|
const exemptions = readProfileVersionExemptions(this.profile.dir);
|
|
1459
1486
|
const selected = manifest.dsh?.profile?.bundles ?? [];
|
|
1460
|
-
const
|
|
1487
|
+
const recorded = manifest.dependencies ?? {};
|
|
1488
|
+
const dependencies = Object.keys(recorded);
|
|
1461
1489
|
const installation = JSON.parse(readFileSync(this.profile.installAnchor, "utf8"));
|
|
1462
1490
|
const names = [...new Set([
|
|
1463
1491
|
...selected,
|
|
@@ -1468,14 +1496,18 @@ let PluginManager = (() => {
|
|
|
1468
1496
|
for (const name of names) {
|
|
1469
1497
|
const installed = dependencies.includes(name);
|
|
1470
1498
|
const optional = OPTIONAL_BUNDLES.includes(name);
|
|
1471
|
-
const removable = installed && !Object.hasOwn(installation.dependencies ?? {}, name);
|
|
1472
1499
|
const enabled = selected.includes(name);
|
|
1500
|
+
const shipped = Object.hasOwn(installation.dependencies ?? {}, name);
|
|
1501
|
+
const owned = installed && !shipped;
|
|
1502
|
+
const removable = owned || enabled && !installed && !shipped;
|
|
1503
|
+
const sourceOf = (packageName) => owned ? { source: dependencySpec(name, recorded[name], this.profile.dir, packageName) } : {};
|
|
1473
1504
|
const readOnlyReason = this.protectsManager(name) ? "management-required" : void 0;
|
|
1474
1505
|
try {
|
|
1475
1506
|
const info = bundleManifest(name, this.profile.dir, this.profile.installAnchor);
|
|
1476
1507
|
if (info === void 0) {
|
|
1477
1508
|
if (enabled) bundles.push({
|
|
1478
1509
|
name,
|
|
1510
|
+
...sourceOf(),
|
|
1479
1511
|
enabled,
|
|
1480
1512
|
installed,
|
|
1481
1513
|
optional,
|
|
@@ -1496,6 +1528,7 @@ let PluginManager = (() => {
|
|
|
1496
1528
|
...info.version === void 0 ? {} : { version: info.version },
|
|
1497
1529
|
...info.description === void 0 || info.description === "" ? {} : { description: info.description },
|
|
1498
1530
|
...meta === void 0 ? {} : { meta },
|
|
1531
|
+
...sourceOf(info.name),
|
|
1499
1532
|
enabled,
|
|
1500
1533
|
installed,
|
|
1501
1534
|
optional,
|
|
@@ -1506,6 +1539,7 @@ let PluginManager = (() => {
|
|
|
1506
1539
|
} catch (error) {
|
|
1507
1540
|
if (enabled || installed) bundles.push({
|
|
1508
1541
|
name,
|
|
1542
|
+
...sourceOf(),
|
|
1509
1543
|
enabled,
|
|
1510
1544
|
installed,
|
|
1511
1545
|
optional,
|
|
@@ -1670,7 +1704,9 @@ let PluginManager = (() => {
|
|
|
1670
1704
|
setBundleEnabled(name, enabled) {
|
|
1671
1705
|
return this.change((result) => this.configure(async () => {
|
|
1672
1706
|
await this.selectBundle(name, enabled);
|
|
1707
|
+
if (enabled) await this.refreshPackages();
|
|
1673
1708
|
result.warnings = await this.reload(enabled ? this.bundleRows(name).map((row) => row.id) : []);
|
|
1709
|
+
if (!enabled && this.ownerContext.get("hmr") !== void 0) await this.refreshPackages();
|
|
1674
1710
|
}), {
|
|
1675
1711
|
stage: "enable",
|
|
1676
1712
|
target: name,
|
|
@@ -1714,6 +1750,7 @@ let PluginManager = (() => {
|
|
|
1714
1750
|
const files = await this.readRestoredFiles();
|
|
1715
1751
|
const before = readProfileManifest("dsh", this.profile.dir).dependencies ?? {};
|
|
1716
1752
|
let name;
|
|
1753
|
+
let version;
|
|
1717
1754
|
try {
|
|
1718
1755
|
result.registries = [];
|
|
1719
1756
|
const connection = checkGithubConnection(parsedForRegistry(spec), this.profile.dir, {
|
|
@@ -1787,6 +1824,7 @@ let PluginManager = (() => {
|
|
|
1787
1824
|
const compatibility = evaluatePluginCompatibility(manifest, readProfileVersionExemptions(this.profile.dir));
|
|
1788
1825
|
if (compatibility !== void 0 && !compatibility.exempted) throw new ManagementFailure("incompatible-version", [incompatiblePlugin(compatibility)]);
|
|
1789
1826
|
for (const file of bundlePatchPaths(dir, manifest.dsh.bundle)) loadOverlayPatches("dsh", file);
|
|
1827
|
+
version = manifest.version;
|
|
1790
1828
|
} catch (error) {
|
|
1791
1829
|
await this.restoreFiles(files);
|
|
1792
1830
|
throw error;
|
|
@@ -1794,11 +1832,13 @@ let PluginManager = (() => {
|
|
|
1794
1832
|
control.phase = "applying";
|
|
1795
1833
|
announce("applying");
|
|
1796
1834
|
result.bundle = name;
|
|
1835
|
+
if (version !== void 0) result.version = version;
|
|
1797
1836
|
result.target = name;
|
|
1798
1837
|
result.stage = "enable";
|
|
1799
1838
|
return this.configure(async () => {
|
|
1800
1839
|
if (options?.enabled !== false) await this.selectBundle(name, true);
|
|
1801
1840
|
if (Object.hasOwn(before, name)) return "restart-required";
|
|
1841
|
+
await this.refreshPackages();
|
|
1802
1842
|
if (options?.enabled !== false) result.warnings = await this.reload();
|
|
1803
1843
|
});
|
|
1804
1844
|
}, {
|
|
@@ -1837,13 +1877,14 @@ let PluginManager = (() => {
|
|
|
1837
1877
|
await control.result.then(() => void 0, () => void 0);
|
|
1838
1878
|
return { status: "cancelled" };
|
|
1839
1879
|
}
|
|
1840
|
-
/** Unload and remove a profile-owned bundle dependency through dsh plugin's pnpm path
|
|
1841
|
-
*
|
|
1880
|
+
/** Unload and remove a profile-owned bundle dependency through dsh plugin's pnpm path; a selected name no
|
|
1881
|
+
* dependency holds is only deselected.
|
|
1882
|
+
* @param name Installed dependency or selected bundle name.
|
|
1842
1883
|
* @returns Removal diagnostics and the remaining profile state.
|
|
1843
1884
|
*/
|
|
1844
1885
|
removeBundle(name) {
|
|
1845
1886
|
return this.change(async (result) => {
|
|
1846
|
-
await this.configure(async () => {
|
|
1887
|
+
if (!await this.configure(async () => {
|
|
1847
1888
|
const bundle = (await this.listBundles()).find((item) => item.name === name);
|
|
1848
1889
|
if (bundle === void 0 || !bundle.removable) throw new ManagementFailure("not-removable");
|
|
1849
1890
|
if (this.ownerContext.get("hmr") === void 0 && (this.profile.startedBundles.includes(name) || bundle.error === void 0 && this.bundleRows(name).some((row) => [...this.ctx.loader.entries()].some((entry) => entry.options.id === row.id && entry.fiber !== void 0)))) throw new ManagementFailure("stop-profile");
|
|
@@ -1853,9 +1894,11 @@ let PluginManager = (() => {
|
|
|
1853
1894
|
result.warnings = await this.reload();
|
|
1854
1895
|
}
|
|
1855
1896
|
if ([...this.ctx.loader.entries()].some((entry) => entry.fiber?.uid != null && contributions.some((row) => row.id === entry.options.id && row.name === entry.options.name))) throw new ManagementFailure("bundle-in-use");
|
|
1856
|
-
|
|
1897
|
+
return bundle.installed;
|
|
1898
|
+
})) return;
|
|
1857
1899
|
result.packageResult = await this.runPnpm(["remove", name]);
|
|
1858
1900
|
if (result.packageResult.exitCode !== 0 || result.packageResult.timedOut === true) throw new Error(result.packageResult.output);
|
|
1901
|
+
await this.configure(() => this.refreshPackages());
|
|
1859
1902
|
}, {
|
|
1860
1903
|
stage: "remove",
|
|
1861
1904
|
target: name
|
|
@@ -2028,6 +2071,13 @@ let PluginManager = (() => {
|
|
|
2028
2071
|
};
|
|
2029
2072
|
return hmr === void 0 ? apply() : hmr.runExclusive(apply);
|
|
2030
2073
|
}
|
|
2074
|
+
async refreshPackages() {
|
|
2075
|
+
if (this.ownerContext.get("hmr") === void 0) {
|
|
2076
|
+
const selected = readProfileManifest("dsh", this.profile.dir).dsh?.profile?.bundles ?? [];
|
|
2077
|
+
if (this.profile.startedBundles.some((name) => !selected.includes(name))) return;
|
|
2078
|
+
}
|
|
2079
|
+
await this.ownerContext.get("pluginPackages")?.refresh();
|
|
2080
|
+
}
|
|
2031
2081
|
async reload(requiredIds = []) {
|
|
2032
2082
|
if (this.ownerContext.get("hmr") === void 0) return [];
|
|
2033
2083
|
return reconcileProfilePatches(this.ownerContext.root, readProfilePatches("dsh", this.profile), "dsh", requiredIds);
|
package/lib/typert.host.js
CHANGED
|
@@ -71,6 +71,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_installBundle_result$schema
|
|
|
71
71
|
})).optional(),
|
|
72
72
|
}).optional(),
|
|
73
73
|
'bundle': z.string().optional(),
|
|
74
|
+
'version': z.string().optional(),
|
|
74
75
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
75
76
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
76
77
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -93,6 +94,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_listBundles_result$schema =
|
|
|
93
94
|
'description': z.string().optional(),
|
|
94
95
|
'enabled': z.boolean(),
|
|
95
96
|
'installed': z.boolean(),
|
|
97
|
+
'source': z.string().optional(),
|
|
96
98
|
'optional': z.boolean(),
|
|
97
99
|
'removable': z.boolean(),
|
|
98
100
|
'readOnlyReason': z.union([z.literal("management-required"), z.literal("unaddressable")]).optional(),
|
|
@@ -207,6 +209,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_removeBundle_result$schema =
|
|
|
207
209
|
})).optional(),
|
|
208
210
|
}).optional(),
|
|
209
211
|
'bundle': z.string().optional(),
|
|
212
|
+
'version': z.string().optional(),
|
|
210
213
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
211
214
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
212
215
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -249,6 +252,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_setBundleEnabled_result$sche
|
|
|
249
252
|
})).optional(),
|
|
250
253
|
}).optional(),
|
|
251
254
|
'bundle': z.string().optional(),
|
|
255
|
+
'version': z.string().optional(),
|
|
252
256
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
253
257
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
254
258
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -291,6 +295,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_setPluginEnabled_result$sche
|
|
|
291
295
|
})).optional(),
|
|
292
296
|
}).optional(),
|
|
293
297
|
'bundle': z.string().optional(),
|
|
298
|
+
'version': z.string().optional(),
|
|
294
299
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
295
300
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
296
301
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -337,6 +342,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_setVersionExemption_result$s
|
|
|
337
342
|
})).optional(),
|
|
338
343
|
}).optional(),
|
|
339
344
|
'bundle': z.string().optional(),
|
|
345
|
+
'version': z.string().optional(),
|
|
340
346
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
341
347
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
342
348
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -377,6 +383,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_waitForInstall_result$schema
|
|
|
377
383
|
})).optional(),
|
|
378
384
|
}).optional(),
|
|
379
385
|
'bundle': z.string().optional(),
|
|
386
|
+
'version': z.string().optional(),
|
|
380
387
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
381
388
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
382
389
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -412,7 +419,7 @@ export const TYPERT = {
|
|
|
412
419
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#PluginInstallCancellation',
|
|
413
420
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_cancelInstall_result$schema,
|
|
414
421
|
},
|
|
415
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
422
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":600,"column":9},
|
|
416
423
|
},
|
|
417
424
|
{
|
|
418
425
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/inspect',
|
|
@@ -449,7 +456,7 @@ export const TYPERT = {
|
|
|
449
456
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#PluginSpecInspection',
|
|
450
457
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_inspect_result$schema,
|
|
451
458
|
},
|
|
452
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
459
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":350,"column":9},
|
|
453
460
|
},
|
|
454
461
|
{
|
|
455
462
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/installBundle',
|
|
@@ -485,7 +492,7 @@ export const TYPERT = {
|
|
|
485
492
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#ChangeResult',
|
|
486
493
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_installBundle_result$schema,
|
|
487
494
|
},
|
|
488
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
495
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":473,"column":3},
|
|
489
496
|
},
|
|
490
497
|
{
|
|
491
498
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/listBundles',
|
|
@@ -545,7 +552,7 @@ export const TYPERT = {
|
|
|
545
552
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#PluginRegistries',
|
|
546
553
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_registries_result$schema,
|
|
547
554
|
},
|
|
548
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
555
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":334,"column":9},
|
|
549
556
|
},
|
|
550
557
|
{
|
|
551
558
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/removeBundle',
|
|
@@ -570,7 +577,7 @@ export const TYPERT = {
|
|
|
570
577
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#ChangeResult',
|
|
571
578
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_removeBundle_result$schema,
|
|
572
579
|
},
|
|
573
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
580
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":617,"column":3},
|
|
574
581
|
},
|
|
575
582
|
{
|
|
576
583
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/setBundleEnabled',
|
|
@@ -605,7 +612,7 @@ export const TYPERT = {
|
|
|
605
612
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#ChangeResult',
|
|
606
613
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_setBundleEnabled_result$schema,
|
|
607
614
|
},
|
|
608
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
615
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":452,"column":3},
|
|
609
616
|
},
|
|
610
617
|
{
|
|
611
618
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/setPluginEnabled',
|
|
@@ -640,7 +647,7 @@ export const TYPERT = {
|
|
|
640
647
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#ChangeResult',
|
|
641
648
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_setPluginEnabled_result$schema,
|
|
642
649
|
},
|
|
643
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
650
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":434,"column":3},
|
|
644
651
|
},
|
|
645
652
|
{
|
|
646
653
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/setVersionExemption',
|
|
@@ -721,7 +728,7 @@ export const TYPERT = {
|
|
|
721
728
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager#pluginManager/waitForInstall:result',
|
|
722
729
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_waitForInstall_result$schema,
|
|
723
730
|
},
|
|
724
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
731
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":590,"column":9},
|
|
725
732
|
},
|
|
726
733
|
],
|
|
727
734
|
model: {
|
|
@@ -760,7 +767,7 @@ export const TYPERT = {
|
|
|
760
767
|
"name": "listBundles",
|
|
761
768
|
"signature": "@Remote listBundles(): Promise<BundleInfo[]>",
|
|
762
769
|
"summary": "Read the profile's installed bundles, the bundles this dsh installation supplies, and the selected names that are not bundles.",
|
|
763
|
-
"jsDoc": "/** Read the profile's installed bundles, the bundles this dsh installation supplies, and the selected names that are not bundles.\n * A dependency without a bundle patch is listed, as a `not-bundle` problem, only while it is selected.\n * @returns Package versions, manifest descriptions, rows, optional display metadata, activation selections
|
|
770
|
+
"jsDoc": "/** Read the profile's installed bundles, the bundles this dsh installation supplies, and the selected names that are not bundles.\n * A dependency without a bundle patch is listed, as a `not-bundle` problem, only while it is selected.\n * @returns Package versions, manifest descriptions, the installable spec of profile dependencies, rows, optional\n * display metadata, activation selections, whether the installation offers the bundle, and removal availability.\n */"
|
|
764
771
|
},
|
|
765
772
|
{
|
|
766
773
|
"kind": "method",
|
|
@@ -815,8 +822,8 @@ export const TYPERT = {
|
|
|
815
822
|
"kind": "method",
|
|
816
823
|
"name": "removeBundle",
|
|
817
824
|
"signature": "@Remote removeBundle(name: string): Promise<ChangeResult>",
|
|
818
|
-
"summary": "Unload and remove a profile-owned bundle dependency through dsh plugin's pnpm path.",
|
|
819
|
-
"jsDoc": "/** Unload and remove a profile-owned bundle dependency through dsh plugin's pnpm path.\n * @param name Installed dependency name.\n * @returns Removal diagnostics and the remaining profile state.\n */"
|
|
825
|
+
"summary": "Unload and remove a profile-owned bundle dependency through dsh plugin's pnpm path; a selected name no dependency holds is only deselected.",
|
|
826
|
+
"jsDoc": "/** Unload and remove a profile-owned bundle dependency through dsh plugin's pnpm path; a selected name no\n * dependency holds is only deselected.\n * @param name Installed dependency or selected bundle name.\n * @returns Removal diagnostics and the remaining profile state.\n */"
|
|
820
827
|
}
|
|
821
828
|
],
|
|
822
829
|
"types": [
|
|
@@ -826,7 +833,7 @@ export const TYPERT = {
|
|
|
826
833
|
},
|
|
827
834
|
{
|
|
828
835
|
"name": "BundleInfo",
|
|
829
|
-
"declaration": "export interface BundleInfo {\n name: string;\n version?: string;\n meta?: PluginLocalizedMeta;\n description?: string;\n enabled: boolean;\n installed: boolean;\n optional: boolean;\n removable: boolean;\n readOnlyReason?: ReadOnlyReason;\n error?: ManagementError;\n rows: BundleRowInfo[];\n overrides: string[];\n}"
|
|
836
|
+
"declaration": "export interface BundleInfo {\n name: string;\n version?: string;\n meta?: PluginLocalizedMeta;\n description?: string;\n enabled: boolean;\n installed: boolean;\n source?: string;\n optional: boolean;\n removable: boolean;\n readOnlyReason?: ReadOnlyReason;\n error?: ManagementError;\n rows: BundleRowInfo[];\n overrides: string[];\n}"
|
|
830
837
|
},
|
|
831
838
|
{
|
|
832
839
|
"name": "BundleRowInfo",
|
|
@@ -834,7 +841,7 @@ export const TYPERT = {
|
|
|
834
841
|
},
|
|
835
842
|
{
|
|
836
843
|
"name": "ChangeResult",
|
|
837
|
-
"declaration": "export interface ChangeResult {\n changed: boolean;\n application: 'applied' | 'restart-required' | 'overridden' | 'failed' | 'cancelled';\n stage: 'install' | 'enable' | 'remove';\n target: string;\n enabled?: boolean;\n error?: ManagementError;\n warnings?: string[];\n packageResult?: PackageResult;\n bundle?: string;\n pendingBuilds?: string[];\n approvedBuilds?: string[];\n registries?: Registry[];\n failedAt?: 'registry' | 'spec-host';\n}"
|
|
844
|
+
"declaration": "export interface ChangeResult {\n changed: boolean;\n application: 'applied' | 'restart-required' | 'overridden' | 'failed' | 'cancelled';\n stage: 'install' | 'enable' | 'remove';\n target: string;\n enabled?: boolean;\n error?: ManagementError;\n warnings?: string[];\n packageResult?: PackageResult;\n bundle?: string;\n version?: string;\n pendingBuilds?: string[];\n approvedBuilds?: string[];\n registries?: Registry[];\n failedAt?: 'registry' | 'spec-host';\n}"
|
|
838
845
|
},
|
|
839
846
|
{
|
|
840
847
|
"name": "IncompatiblePlugin",
|
|
@@ -71,6 +71,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_installBundle_result$schema
|
|
|
71
71
|
})).optional(),
|
|
72
72
|
}).optional(),
|
|
73
73
|
'bundle': z.string().optional(),
|
|
74
|
+
'version': z.string().optional(),
|
|
74
75
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
75
76
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
76
77
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -93,6 +94,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_listBundles_result$schema =
|
|
|
93
94
|
'description': z.string().optional(),
|
|
94
95
|
'enabled': z.boolean(),
|
|
95
96
|
'installed': z.boolean(),
|
|
97
|
+
'source': z.string().optional(),
|
|
96
98
|
'optional': z.boolean(),
|
|
97
99
|
'removable': z.boolean(),
|
|
98
100
|
'readOnlyReason': z.union([z.literal("management-required"), z.literal("unaddressable")]).optional(),
|
|
@@ -207,6 +209,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_removeBundle_result$schema =
|
|
|
207
209
|
})).optional(),
|
|
208
210
|
}).optional(),
|
|
209
211
|
'bundle': z.string().optional(),
|
|
212
|
+
'version': z.string().optional(),
|
|
210
213
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
211
214
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
212
215
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -249,6 +252,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_setBundleEnabled_result$sche
|
|
|
249
252
|
})).optional(),
|
|
250
253
|
}).optional(),
|
|
251
254
|
'bundle': z.string().optional(),
|
|
255
|
+
'version': z.string().optional(),
|
|
252
256
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
253
257
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
254
258
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -291,6 +295,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_setPluginEnabled_result$sche
|
|
|
291
295
|
})).optional(),
|
|
292
296
|
}).optional(),
|
|
293
297
|
'bundle': z.string().optional(),
|
|
298
|
+
'version': z.string().optional(),
|
|
294
299
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
295
300
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
296
301
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -337,6 +342,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_setVersionExemption_result$s
|
|
|
337
342
|
})).optional(),
|
|
338
343
|
}).optional(),
|
|
339
344
|
'bundle': z.string().optional(),
|
|
345
|
+
'version': z.string().optional(),
|
|
340
346
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
341
347
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
342
348
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -377,6 +383,7 @@ const _deepseek_ai_dsh_plugin_manager_pluginManager_waitForInstall_result$schema
|
|
|
377
383
|
})).optional(),
|
|
378
384
|
}).optional(),
|
|
379
385
|
'bundle': z.string().optional(),
|
|
386
|
+
'version': z.string().optional(),
|
|
380
387
|
'pendingBuilds': z.array(z.string()).optional(),
|
|
381
388
|
'approvedBuilds': z.array(z.string()).optional(),
|
|
382
389
|
'registries': z.array(z.union([z.literal(null), z.string()])).optional(),
|
|
@@ -409,7 +416,7 @@ export const TYPERT_REMOTE = {
|
|
|
409
416
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#PluginInstallCancellation',
|
|
410
417
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_cancelInstall_result$schema,
|
|
411
418
|
},
|
|
412
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
419
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":600,"column":9},
|
|
413
420
|
},
|
|
414
421
|
{
|
|
415
422
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/inspect',
|
|
@@ -446,7 +453,7 @@ export const TYPERT_REMOTE = {
|
|
|
446
453
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#PluginSpecInspection',
|
|
447
454
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_inspect_result$schema,
|
|
448
455
|
},
|
|
449
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
456
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":350,"column":9},
|
|
450
457
|
},
|
|
451
458
|
{
|
|
452
459
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/installBundle',
|
|
@@ -482,7 +489,7 @@ export const TYPERT_REMOTE = {
|
|
|
482
489
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#ChangeResult',
|
|
483
490
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_installBundle_result$schema,
|
|
484
491
|
},
|
|
485
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
492
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":473,"column":3},
|
|
486
493
|
},
|
|
487
494
|
{
|
|
488
495
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/listBundles',
|
|
@@ -542,7 +549,7 @@ export const TYPERT_REMOTE = {
|
|
|
542
549
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#PluginRegistries',
|
|
543
550
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_registries_result$schema,
|
|
544
551
|
},
|
|
545
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
552
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":334,"column":9},
|
|
546
553
|
},
|
|
547
554
|
{
|
|
548
555
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/removeBundle',
|
|
@@ -567,7 +574,7 @@ export const TYPERT_REMOTE = {
|
|
|
567
574
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#ChangeResult',
|
|
568
575
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_removeBundle_result$schema,
|
|
569
576
|
},
|
|
570
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
577
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":617,"column":3},
|
|
571
578
|
},
|
|
572
579
|
{
|
|
573
580
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/setBundleEnabled',
|
|
@@ -602,7 +609,7 @@ export const TYPERT_REMOTE = {
|
|
|
602
609
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#ChangeResult',
|
|
603
610
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_setBundleEnabled_result$schema,
|
|
604
611
|
},
|
|
605
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
612
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":452,"column":3},
|
|
606
613
|
},
|
|
607
614
|
{
|
|
608
615
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/setPluginEnabled',
|
|
@@ -637,7 +644,7 @@ export const TYPERT_REMOTE = {
|
|
|
637
644
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager/types#ChangeResult',
|
|
638
645
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_setPluginEnabled_result$schema,
|
|
639
646
|
},
|
|
640
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
647
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":434,"column":3},
|
|
641
648
|
},
|
|
642
649
|
{
|
|
643
650
|
id: '@deepseek-ai/dsh-plugin-manager#pluginManager/setVersionExemption',
|
|
@@ -718,7 +725,7 @@ export const TYPERT_REMOTE = {
|
|
|
718
725
|
typeSymbol: '@deepseek-ai/dsh-plugin-manager#pluginManager/waitForInstall:result',
|
|
719
726
|
create: _deepseek_ai_dsh_plugin_manager_pluginManager_waitForInstall_result$schema,
|
|
720
727
|
},
|
|
721
|
-
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":
|
|
728
|
+
sourceLocation: {"file":"packages/boot/plugin-manager/src/index.ts","line":590,"column":9},
|
|
722
729
|
},
|
|
723
730
|
],
|
|
724
731
|
}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -77,8 +77,8 @@ export declare class PluginManager extends TypertRemoteService {
|
|
|
77
77
|
listPlugins(): Promise<PluginInfo[]>;
|
|
78
78
|
/** Read the profile's installed bundles, the bundles this dsh installation supplies, and the selected names that are not bundles.
|
|
79
79
|
* A dependency without a bundle patch is listed, as a `not-bundle` problem, only while it is selected.
|
|
80
|
-
* @returns Package versions, manifest descriptions,
|
|
81
|
-
* whether the installation offers the bundle, and removal availability.
|
|
80
|
+
* @returns Package versions, manifest descriptions, the installable spec of profile dependencies, rows, optional
|
|
81
|
+
* display metadata, activation selections, whether the installation offers the bundle, and removal availability.
|
|
82
82
|
*/
|
|
83
83
|
listBundles(): Promise<BundleInfo[]>;
|
|
84
84
|
/** Read the registries this manager asks: the configured first one, its fallbacks in order, and what pnpm's own configuration names.
|
|
@@ -128,8 +128,9 @@ export declare class PluginManager extends TypertRemoteService {
|
|
|
128
128
|
* applied, `not-running` for any other id.
|
|
129
129
|
*/
|
|
130
130
|
cancelInstall(requestId: PluginInstallRequestId): Promise<PluginInstallCancellation>;
|
|
131
|
-
/** Unload and remove a profile-owned bundle dependency through dsh plugin's pnpm path
|
|
132
|
-
*
|
|
131
|
+
/** Unload and remove a profile-owned bundle dependency through dsh plugin's pnpm path; a selected name no
|
|
132
|
+
* dependency holds is only deselected.
|
|
133
|
+
* @param name Installed dependency or selected bundle name.
|
|
133
134
|
* @returns Removal diagnostics and the remaining profile state.
|
|
134
135
|
*/
|
|
135
136
|
removeBundle(name: string): Promise<ChangeResult>;
|
|
@@ -145,6 +146,7 @@ export declare class PluginManager extends TypertRemoteService {
|
|
|
145
146
|
private bundleRows;
|
|
146
147
|
private protectsManager;
|
|
147
148
|
private configure;
|
|
149
|
+
private refreshPackages;
|
|
148
150
|
private reload;
|
|
149
151
|
private change;
|
|
150
152
|
private diskState;
|
package/lib/types/index.js
CHANGED
|
@@ -45,7 +45,7 @@ import { pluginEntryId, readPluginInventory } from '@deepseek-ai/dsh-host-plugin
|
|
|
45
45
|
import { readPluginMeta, readProfileManifest, resolveBundleDir, loadOverlayPatches, composeEntries, reconcileProfilePatches, readProfilePatches, OPTIONAL_BUNDLES, bundlePatchPaths, evaluatePluginCompatibility, readProfileCompatibility, readProfileVersionExemptions, setProfileVersionExemption, PROFILE_COMPATIBILITY_FILENAME, } from '@deepseek-ai/dsh-app-boot';
|
|
46
46
|
import { bundleManifest, readProfileRegistry, registryArguments, runProfilePnpm, saveManifest, viewProfilePackage } from "./operations.js";
|
|
47
47
|
import { classifyInstallFailure } from "./install-failure.js";
|
|
48
|
-
import { InvalidInstallSpecError, parseInstallSpec } from "./install-spec.js";
|
|
48
|
+
import { dependencySpec, InvalidInstallSpecError, parseInstallSpec } from "./install-spec.js";
|
|
49
49
|
import { attributeFailure, normalizeRegistry, NPMMIRROR_REGISTRY, registryPlan } from "./registry.js";
|
|
50
50
|
import { writePluginEnabled } from "./patch.js";
|
|
51
51
|
import { incompatiblePlugin, ManagementFailure } from "./failure.js";
|
|
@@ -276,28 +276,36 @@ let PluginManager = (() => {
|
|
|
276
276
|
}
|
|
277
277
|
/** Read the profile's installed bundles, the bundles this dsh installation supplies, and the selected names that are not bundles.
|
|
278
278
|
* A dependency without a bundle patch is listed, as a `not-bundle` problem, only while it is selected.
|
|
279
|
-
* @returns Package versions, manifest descriptions,
|
|
280
|
-
* whether the installation offers the bundle, and removal availability.
|
|
279
|
+
* @returns Package versions, manifest descriptions, the installable spec of profile dependencies, rows, optional
|
|
280
|
+
* display metadata, activation selections, whether the installation offers the bundle, and removal availability.
|
|
281
281
|
*/
|
|
282
282
|
listBundles() {
|
|
283
283
|
const manifest = readProfileManifest('dsh', this.profile.dir);
|
|
284
284
|
const exemptions = readProfileVersionExemptions(this.profile.dir);
|
|
285
285
|
const selected = manifest.dsh?.profile?.bundles ?? [];
|
|
286
|
-
const
|
|
286
|
+
const recorded = manifest.dependencies ?? {};
|
|
287
|
+
const dependencies = Object.keys(recorded);
|
|
287
288
|
const installation = JSON.parse(readFileSync(this.profile.installAnchor, 'utf8'));
|
|
288
289
|
const names = [...new Set([...selected, ...dependencies, ...Object.keys(installation.dependencies ?? {})])];
|
|
289
290
|
const bundles = [];
|
|
290
291
|
for (const name of names) {
|
|
291
292
|
const installed = dependencies.includes(name);
|
|
292
293
|
const optional = OPTIONAL_BUNDLES.includes(name);
|
|
293
|
-
const removable = installed && !Object.hasOwn(installation.dependencies ?? {}, name);
|
|
294
294
|
const enabled = selected.includes(name);
|
|
295
|
+
const shipped = Object.hasOwn(installation.dependencies ?? {}, name);
|
|
296
|
+
// Bundle resolution reads the installation first, so a profile dependency the installation manifest also names,
|
|
297
|
+
// like one it forbids removing, is not the loaded copy.
|
|
298
|
+
const owned = installed && !shipped;
|
|
299
|
+
// A selection neither the profile nor the installation holds, such as a retired bundle, is removed by deselecting it.
|
|
300
|
+
const removable = owned || (enabled && !installed && !shipped);
|
|
301
|
+
const sourceOf = (packageName) => owned ? { source: dependencySpec(name, recorded[name], this.profile.dir, packageName) } : {};
|
|
295
302
|
const readOnlyReason = this.protectsManager(name) ? 'management-required' : undefined;
|
|
296
303
|
try {
|
|
297
304
|
const info = bundleManifest(name, this.profile.dir, this.profile.installAnchor);
|
|
298
305
|
if (info === undefined) {
|
|
299
306
|
if (enabled)
|
|
300
|
-
bundles.push({ name, enabled, installed, optional,
|
|
307
|
+
bundles.push({ name, ...sourceOf(), enabled, installed, optional,
|
|
308
|
+
removable: removable && readOnlyReason === undefined,
|
|
301
309
|
...(readOnlyReason === undefined ? {} : { readOnlyReason }), error: { code: 'not-bundle' }, rows: [], overrides: [] });
|
|
302
310
|
continue;
|
|
303
311
|
}
|
|
@@ -309,13 +317,13 @@ let PluginManager = (() => {
|
|
|
309
317
|
bundles.push({ name, ...(info.version === undefined ? {} : { version: info.version }),
|
|
310
318
|
...(info.description === undefined || info.description === '' ? {} : { description: info.description }),
|
|
311
319
|
...meta === undefined ? {} : { meta },
|
|
312
|
-
enabled, installed, optional, removable: removable && readOnlyReason === undefined,
|
|
320
|
+
...sourceOf(info.name), enabled, installed, optional, removable: removable && readOnlyReason === undefined,
|
|
313
321
|
...(readOnlyReason === undefined ? {} : { readOnlyReason }),
|
|
314
322
|
...this.declaredRows(name, info) });
|
|
315
323
|
}
|
|
316
324
|
catch (error) {
|
|
317
325
|
if (enabled || installed) {
|
|
318
|
-
bundles.push({ name, enabled, installed, optional, removable: removable && readOnlyReason === undefined,
|
|
326
|
+
bundles.push({ name, ...sourceOf(), enabled, installed, optional, removable: removable && readOnlyReason === undefined,
|
|
319
327
|
...(readOnlyReason === undefined ? {} : { readOnlyReason }), error: managementError(error), rows: [], overrides: [] });
|
|
320
328
|
}
|
|
321
329
|
}
|
|
@@ -456,7 +464,11 @@ let PluginManager = (() => {
|
|
|
456
464
|
setBundleEnabled(name, enabled) {
|
|
457
465
|
return this.change(result => this.configure(async () => {
|
|
458
466
|
await this.selectBundle(name, enabled);
|
|
467
|
+
if (enabled)
|
|
468
|
+
await this.refreshPackages();
|
|
459
469
|
result.warnings = await this.reload(enabled ? this.bundleRows(name).map(row => row.id) : []);
|
|
470
|
+
if (!enabled && this.ownerContext.get('hmr') !== undefined)
|
|
471
|
+
await this.refreshPackages();
|
|
460
472
|
}), { stage: 'enable', target: name, enabled }, 'bundle');
|
|
461
473
|
}
|
|
462
474
|
/**
|
|
@@ -492,6 +504,7 @@ let PluginManager = (() => {
|
|
|
492
504
|
const files = await this.readRestoredFiles();
|
|
493
505
|
const before = readProfileManifest('dsh', this.profile.dir).dependencies ?? {};
|
|
494
506
|
let name;
|
|
507
|
+
let version;
|
|
495
508
|
try {
|
|
496
509
|
result.registries = [];
|
|
497
510
|
const connection = checkGithubConnection(parsedForRegistry(spec), this.profile.dir, {
|
|
@@ -584,6 +597,7 @@ let PluginManager = (() => {
|
|
|
584
597
|
throw new ManagementFailure('incompatible-version', [incompatiblePlugin(compatibility)]);
|
|
585
598
|
for (const file of bundlePatchPaths(dir, manifest.dsh.bundle))
|
|
586
599
|
loadOverlayPatches('dsh', file);
|
|
600
|
+
version = manifest.version;
|
|
587
601
|
}
|
|
588
602
|
catch (error) {
|
|
589
603
|
// pnpm has exited by now, so the files it rewrote go back as they were.
|
|
@@ -593,6 +607,8 @@ let PluginManager = (() => {
|
|
|
593
607
|
control.phase = 'applying';
|
|
594
608
|
announce('applying');
|
|
595
609
|
result.bundle = name;
|
|
610
|
+
if (version !== undefined)
|
|
611
|
+
result.version = version;
|
|
596
612
|
result.target = name;
|
|
597
613
|
result.stage = 'enable';
|
|
598
614
|
return this.configure(async () => {
|
|
@@ -600,6 +616,7 @@ let PluginManager = (() => {
|
|
|
600
616
|
await this.selectBundle(name, true);
|
|
601
617
|
if (Object.hasOwn(before, name))
|
|
602
618
|
return 'restart-required';
|
|
619
|
+
await this.refreshPackages();
|
|
603
620
|
if (options?.enabled !== false)
|
|
604
621
|
result.warnings = await this.reload();
|
|
605
622
|
});
|
|
@@ -633,13 +650,14 @@ let PluginManager = (() => {
|
|
|
633
650
|
await control.result.then(() => undefined, () => undefined);
|
|
634
651
|
return { status: 'cancelled' };
|
|
635
652
|
}
|
|
636
|
-
/** Unload and remove a profile-owned bundle dependency through dsh plugin's pnpm path
|
|
637
|
-
*
|
|
653
|
+
/** Unload and remove a profile-owned bundle dependency through dsh plugin's pnpm path; a selected name no
|
|
654
|
+
* dependency holds is only deselected.
|
|
655
|
+
* @param name Installed dependency or selected bundle name.
|
|
638
656
|
* @returns Removal diagnostics and the remaining profile state.
|
|
639
657
|
*/
|
|
640
658
|
removeBundle(name) {
|
|
641
659
|
return this.change(async (result) => {
|
|
642
|
-
await this.configure(async () => {
|
|
660
|
+
const installed = await this.configure(async () => {
|
|
643
661
|
const bundle = (await this.listBundles()).find(item => item.name === name);
|
|
644
662
|
if (bundle === undefined || !bundle.removable)
|
|
645
663
|
throw new ManagementFailure('not-removable');
|
|
@@ -657,11 +675,15 @@ let PluginManager = (() => {
|
|
|
657
675
|
&& contributions.some(row => row.id === entry.options.id && row.name === entry.options.name))) {
|
|
658
676
|
throw new ManagementFailure('bundle-in-use');
|
|
659
677
|
}
|
|
678
|
+
return bundle.installed;
|
|
660
679
|
});
|
|
680
|
+
if (!installed)
|
|
681
|
+
return;
|
|
661
682
|
result.packageResult = await this.runPnpm(['remove', name]);
|
|
662
683
|
if (result.packageResult.exitCode !== 0 || result.packageResult.timedOut === true) {
|
|
663
684
|
throw new Error(result.packageResult.output);
|
|
664
685
|
}
|
|
686
|
+
await this.configure(() => this.refreshPackages());
|
|
665
687
|
}, { stage: 'remove', target: name }, 'remove');
|
|
666
688
|
}
|
|
667
689
|
/** The rows a bundle's patch inserts and the existing rows it changes; an unreadable patch throws. */
|
|
@@ -806,6 +828,15 @@ let PluginManager = (() => {
|
|
|
806
828
|
const apply = () => { this.abort.signal.throwIfAborted(); return operation(); };
|
|
807
829
|
return hmr === undefined ? apply() : hmr.runExclusive(apply);
|
|
808
830
|
}
|
|
831
|
+
async refreshPackages() {
|
|
832
|
+
if (this.ownerContext.get('hmr') === undefined) {
|
|
833
|
+
const selected = readProfileManifest('dsh', this.profile.dir).dsh?.profile?.bundles ?? [];
|
|
834
|
+
// Deselected startup bundles still run without HMR and need the existing package table.
|
|
835
|
+
if (this.profile.startedBundles.some(name => !selected.includes(name)))
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
await this.ownerContext.get('pluginPackages')?.refresh();
|
|
839
|
+
}
|
|
809
840
|
async reload(requiredIds = []) {
|
|
810
841
|
if (this.ownerContext.get('hmr') === undefined)
|
|
811
842
|
return [];
|
|
@@ -47,4 +47,17 @@ export declare class InvalidInstallSpecError extends Error {
|
|
|
47
47
|
* registry would refuse, or a URL that is neither a git host nor a tarball.
|
|
48
48
|
*/
|
|
49
49
|
export declare function parseInstallSpec(raw: string): ParsedInstallSpec;
|
|
50
|
+
/**
|
|
51
|
+
* The spec `pnpm add` accepts for a dependency the profile manifest records. A git address, URL, or other
|
|
52
|
+
* non-registry protocol is the recorded value without the user information of an http(s) URL, whose query string
|
|
53
|
+
* is kept; a `file:` or `link:` path, which pnpm records relative to the profile or with `~` for the home
|
|
54
|
+
* directory, becomes absolute. A registry range, tag, or alias follows the dependency name after `@`, as does a
|
|
55
|
+
* non-registry value installed under a name other than the package's own.
|
|
56
|
+
* @param name - the dependency name.
|
|
57
|
+
* @param recorded - the value the profile manifest records for it.
|
|
58
|
+
* @param profileDir - the profile directory a relative local path is resolved against.
|
|
59
|
+
* @param packageName - the name the installed package's manifest declares, when it could be read.
|
|
60
|
+
* @returns the installable spec.
|
|
61
|
+
*/
|
|
62
|
+
export declare function dependencySpec(name: string, recorded: string, profileDir: string, packageName?: string): string;
|
|
50
63
|
//# sourceMappingURL=install-spec.d.ts.map
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* takes, and for a registry name whether it is one the registry can accept.
|
|
4
4
|
* @module @deepseek-ai/dsh-plugin-manager/install-spec
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
7
8
|
/** The forms pnpm resolves through a git host: a host shorthand, a git URL, or a hosted repository URL. */
|
|
8
9
|
const GIT_SHORTHAND = /^(?:github|gitlab|bitbucket|gist):/i;
|
|
9
10
|
const GIT_URL = /^git(?:\+[a-z]+)?:\/\/|^git@[^:]+:/i;
|
|
@@ -84,4 +85,35 @@ export function parseInstallSpec(raw) {
|
|
|
84
85
|
throw invalid(spec, 'a version after @ must not be empty');
|
|
85
86
|
return range === undefined ? { kind: 'registry', spec, name } : { kind: 'registry', spec, name, range };
|
|
86
87
|
}
|
|
88
|
+
/** A recorded dependency value pnpm resolves outside a registry: a protocol other than a registry alias, or an scp-like git address. */
|
|
89
|
+
const NON_REGISTRY_VALUE = /^(?!(?:npm|jsr|workspace|catalog):)[a-z][a-z0-9+.-]*:|^[^@/:\s]+@[^:/\s]+:/i;
|
|
90
|
+
/** The user information of an http(s) URL, up to the last `@` before the path, which holds a password or access token when present. */
|
|
91
|
+
const HTTP_USER_INFO = /^((?:git\+)?https?:\/\/)[^/]*@/i;
|
|
92
|
+
/**
|
|
93
|
+
* The spec `pnpm add` accepts for a dependency the profile manifest records. A git address, URL, or other
|
|
94
|
+
* non-registry protocol is the recorded value without the user information of an http(s) URL, whose query string
|
|
95
|
+
* is kept; a `file:` or `link:` path, which pnpm records relative to the profile or with `~` for the home
|
|
96
|
+
* directory, becomes absolute. A registry range, tag, or alias follows the dependency name after `@`, as does a
|
|
97
|
+
* non-registry value installed under a name other than the package's own.
|
|
98
|
+
* @param name - the dependency name.
|
|
99
|
+
* @param recorded - the value the profile manifest records for it.
|
|
100
|
+
* @param profileDir - the profile directory a relative local path is resolved against.
|
|
101
|
+
* @param packageName - the name the installed package's manifest declares, when it could be read.
|
|
102
|
+
* @returns the installable spec.
|
|
103
|
+
*/
|
|
104
|
+
export function dependencySpec(name, recorded, profileDir, packageName = name) {
|
|
105
|
+
const local = /^(file|link):(.*)$/s.exec(recorded);
|
|
106
|
+
let spec;
|
|
107
|
+
if (local !== null) {
|
|
108
|
+
const path = local[2].replace(/^~(?=$|[\\/])/, () => homedir());
|
|
109
|
+
spec = `${local[1]}:${resolve(profileDir, path)}`;
|
|
110
|
+
}
|
|
111
|
+
else if (NON_REGISTRY_VALUE.test(recorded)) {
|
|
112
|
+
spec = recorded.replace(HTTP_USER_INFO, '$1');
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
return `${name}@${recorded}`;
|
|
116
|
+
}
|
|
117
|
+
return packageName === name ? spec : `${name}@${spec}`;
|
|
118
|
+
}
|
|
87
119
|
//# sourceMappingURL=install-spec.js.map
|
package/lib/types/types.d.ts
CHANGED
|
@@ -52,6 +52,11 @@ export interface BundleInfo {
|
|
|
52
52
|
enabled: boolean;
|
|
53
53
|
/** Whether the profile's own dependencies hold the package; false for a bundle the dsh installation supplies. */
|
|
54
54
|
installed: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Present for a profile dependency the installation does not also supply: the spec `pnpm add` accepts, with local
|
|
57
|
+
* paths made absolute and the user information of an http(s) URL removed.
|
|
58
|
+
*/
|
|
59
|
+
source?: string;
|
|
55
60
|
/**
|
|
56
61
|
* Whether the installation ships the bundle for the person to switch on: named by the launcher's `OPTIONAL_BUNDLES`,
|
|
57
62
|
* held by the installation's dependencies, selected by no shipped template, and never removable.
|
|
@@ -104,6 +109,8 @@ export interface ChangeResult {
|
|
|
104
109
|
packageResult?: PackageResult;
|
|
105
110
|
/** The bundle an installation added, once pnpm and the bundle check accepted it. */
|
|
106
111
|
bundle?: string;
|
|
112
|
+
/** The installed bundle's manifest version, when declared; pnpm's `minimumReleaseAge` can make it older than the newest release. */
|
|
113
|
+
version?: string;
|
|
107
114
|
/** Exact package names awaiting explicit script approval in the profile's pnpm settings, read after a failed run. */
|
|
108
115
|
pendingBuilds?: string[];
|
|
109
116
|
/** Package script permissions saved before this installation attempt. */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-plugin-manager",
|
|
3
3
|
"description": "Current-profile plugin and bundle management shared by dsh CLI, Web and agent tools",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -59,23 +59,23 @@
|
|
|
59
59
|
"execa": "^10.0.0",
|
|
60
60
|
"yaml": "^2.9.0",
|
|
61
61
|
"zod": "^4.4.3",
|
|
62
|
-
"@deepseek-ai/dsh-app-boot": "0.2.
|
|
63
|
-
"@deepseek-ai/dsh-atomic-write": "0.2.
|
|
64
|
-
"@deepseek-ai/schemastery": "~3.18.
|
|
65
|
-
"@deepseek-ai/dsh-util-values": "0.2.
|
|
62
|
+
"@deepseek-ai/dsh-app-boot": "0.2.1-alpha.1",
|
|
63
|
+
"@deepseek-ai/dsh-atomic-write": "0.2.1-alpha.1",
|
|
64
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.1",
|
|
65
|
+
"@deepseek-ai/dsh-util-values": "0.2.1-alpha.1"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
|
-
"@deepseek-ai/cordis": "~4.0.
|
|
69
|
-
"@deepseek-ai/cordis-plugin-loader": "~1.0.
|
|
70
|
-
"@deepseek-ai/dsh-host-plugin-inventory": "0.2.
|
|
71
|
-
"@deepseek-ai/dsh-subprocess": "0.2.
|
|
72
|
-
"@deepseek-ai/dsh-typert-protocol": "0.2.
|
|
73
|
-
"@deepseek-ai/dsh-tools": "0.2.
|
|
74
|
-
"@deepseek-ai/dsh-
|
|
75
|
-
"@deepseek-ai/dsh-
|
|
76
|
-
"@deepseek-ai/dsh-sandbox": "0.2.
|
|
77
|
-
"@deepseek-ai/dsh-sandbox
|
|
78
|
-
"@deepseek-ai/dsh-user-approval": "0.2.
|
|
68
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
69
|
+
"@deepseek-ai/cordis-plugin-loader": "~1.0.6-alpha.1",
|
|
70
|
+
"@deepseek-ai/dsh-host-plugin-inventory": "0.2.1-alpha.1",
|
|
71
|
+
"@deepseek-ai/dsh-subprocess": "0.2.1-alpha.1",
|
|
72
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.1",
|
|
73
|
+
"@deepseek-ai/dsh-tools": "0.2.1-alpha.1",
|
|
74
|
+
"@deepseek-ai/dsh-hmr": "0.2.1-alpha.1",
|
|
75
|
+
"@deepseek-ai/dsh-brand": "0.2.1-alpha.1",
|
|
76
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.1",
|
|
77
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-alpha.1",
|
|
78
|
+
"@deepseek-ai/dsh-user-approval": "0.2.1-alpha.1"
|
|
79
79
|
},
|
|
80
80
|
"peerDependenciesMeta": {
|
|
81
81
|
"@deepseek-ai/dsh-hmr": {
|
|
@@ -86,22 +86,23 @@
|
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
-
"
|
|
90
|
-
"@deepseek-ai/
|
|
91
|
-
"@deepseek-ai/
|
|
92
|
-
"@deepseek-ai/
|
|
93
|
-
"@deepseek-ai/dsh-
|
|
94
|
-
"@deepseek-ai/dsh-
|
|
95
|
-
"@deepseek-ai/dsh-
|
|
96
|
-
"@deepseek-ai/
|
|
97
|
-
"@deepseek-ai/
|
|
98
|
-
"@deepseek-ai/dsh-
|
|
99
|
-
"@deepseek-ai/
|
|
100
|
-
"@deepseek-ai/dsh-
|
|
101
|
-
"@deepseek-ai/dsh-
|
|
102
|
-
"@deepseek-ai/dsh-sandbox": "0.2.
|
|
103
|
-
"@deepseek-ai/dsh-user-approval": "0.2.
|
|
104
|
-
"@deepseek-ai/dsh-
|
|
105
|
-
"@deepseek-ai/dsh-
|
|
89
|
+
"chokidar": "^4.0.3",
|
|
90
|
+
"@deepseek-ai/dsh-package-manifest": "0.2.1-alpha.1",
|
|
91
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
92
|
+
"@deepseek-ai/cordis-plugin-loader": "~1.0.6-alpha.1",
|
|
93
|
+
"@deepseek-ai/dsh-host-plugin-inventory": "0.2.1-alpha.1",
|
|
94
|
+
"@deepseek-ai/dsh-subprocess": "0.2.1-alpha.1",
|
|
95
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.1",
|
|
96
|
+
"@deepseek-ai/dsh-tools": "0.2.1-alpha.1",
|
|
97
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-alpha.1",
|
|
98
|
+
"@deepseek-ai/dsh-hmr": "0.2.1-alpha.1",
|
|
99
|
+
"@deepseek-ai/cordis-plugin-timer": "~1.1.7-alpha.1",
|
|
100
|
+
"@deepseek-ai/dsh-brand": "0.2.1-alpha.1",
|
|
101
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.1",
|
|
102
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-alpha.1",
|
|
103
|
+
"@deepseek-ai/dsh-user-approval": "0.2.1-alpha.1",
|
|
104
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.1",
|
|
105
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-alpha.1",
|
|
106
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.1"
|
|
106
107
|
}
|
|
107
108
|
}
|