@sparkelf/dsh-plus 0.2.2-alpha.2.3 → 0.2.2-alpha.2.30
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.md +5 -7
- package/README.zh.md +5 -7
- package/lib/apply.js +1 -1
- package/lib/bin.js +144 -178
- package/lib/types/apply.d.ts +1 -1
- package/lib/types/apply.js +117 -56
- package/lib/types/standalone-cli.js +2 -93
- package/lib/types/standalone-profile.d.ts +1 -8
- package/lib/types/standalone-profile.js +25 -126
- package/package.json +60 -33
- package/patches/ui-schedule-plaza-tab.patch +217 -0
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
This distribution composes official `@deepseek-ai/dsh-base` and `@deepseek-ai/dsh-web-app` with selected Office, MinerU, mobile, market, GenUI, and Supervisor packages, four in-repository Plus capability packages, and
|
|
12
|
+
This distribution composes official `@deepseek-ai/dsh-base` and `@deepseek-ai/dsh-web-app` with selected Office, MinerU, mobile, market, GenUI, and Supervisor packages, four in-repository Plus capability packages, and twelve independent patch packages. It owns only ordered composition, minimum compatibility metadata, dependency closure, explicit materialization, and the credential-free deployment lock; capability behavior and patch payloads stay in their owning packages.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -23,17 +23,15 @@ This distribution composes official `@deepseek-ai/dsh-base` and `@deepseek-ai/ds
|
|
|
23
23
|
<a id="use-this-package"></a>
|
|
24
24
|
## Use this package
|
|
25
25
|
|
|
26
|
-
Install
|
|
26
|
+
Install and materialize Plus without Desktop or the tray:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
|
|
30
|
-
dsh
|
|
29
|
+
dsh plugin --profile plus add @sparkelf/dsh-plus
|
|
30
|
+
dsh plugin --profile plus exec dsh-plus apply --dsh-root /path/to/official-dsh
|
|
31
31
|
dsh --profile plus
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
The
|
|
35
|
-
|
|
36
|
-
For a source checkout, the explicit apply step requires the exact official revision declared by `dshPlus.sourceBase.revision`, applies the embedded `dshPlus.patchSet` to that checkout, configures the npm patches the set declares, and writes `.dsh-plus/patchset.lock.json` with mode `0600`. Patched official source workspaces always replace clean CLI packages in the profile scope. The profile resolves its pinned third-party and Plus capability packages from npm. Version conflicts, source mismatches, missing bundles, and runtime closure drift stop promotion.
|
|
34
|
+
The explicit rc24 apply step requires official revision `fb2c4b9e698e30edb738bca4cf0618587db7d203`, applies every pending source payload, configures exact npm patches, removes retired Plus-owned entries, and writes `.dsh-plus/patchset.lock.json` with mode `0600`. Patched official source workspaces always replace clean CLI packages in the profile scope. The profile resolves SQL Workbench 0.5.0, Workbench Vault 0.1.1, SSH Manager 0.7.0, API Client 0.5.0, Supervisor 0.1.4, GenUI 0.9.9, dshmarket 1.45.1, OfficeCLI 0.1.1, MinerU 0.1.1, and Mobile Bridge 0.2.11 from npm. Version conflicts, source mismatches, missing bundles, and runtime closure drift stop promotion.
|
|
37
35
|
|
|
38
36
|
Official DSH supplies Sidebar navigation, file and text preview, Session search, generic file upload, durable attachment cards, read-only model paths, onboarding, Workspace selection, Session export, and Turn folding. Plus configures the official SQLite Session query provider to open on first search with a durable derived index at `$DSH_HOME/storages/session-query.sqlite`; canonical compressed JSONL remains the history source. Plus adds an OfficeCLI tool for original DOCX/XLSX/PPTX production and a MinerU PDF tool that consumes the official uploaded-file path without a prompt hook. It replaces mixed English `Session` wording in the Simplified-Chinese export UI, places the desktop Session-log action immediately before Trajectory search while retaining its Header position on phones, and keeps composer permission and model popovers inside the center column with 12px horizontal clearance. `@changfenhuang/dsh-genui@0.9.9` now contains the previously patched streaming EChart behavior. The registry-pinned Supervisor manages the materialized runtime and restores Sessions interrupted by its own restart. DataOps and MinerU mount only when their endpoint environment variables exist. Agent Teams remains excluded because official shipped profiles disable those private experimental packages.
|
|
39
37
|
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-bundle"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
该distribution把official `@deepseek-ai/dsh-base`及`@deepseek-ai/dsh-web-app`与selected Office、MinerU、mobile、market、GenUI及Supervisor packages、四个in-repository Plus capability packages
|
|
12
|
+
该distribution把official `@deepseek-ai/dsh-base`及`@deepseek-ai/dsh-web-app`与selected Office、MinerU、mobile、market、GenUI及Supervisor packages、四个in-repository Plus capability packages及十二个independent patch packages组合起来。它只持有ordered composition、minimum compatibility metadata、dependency closure、explicit materialization及credential-free deployment lock;capability behavior和patch payloads仍属于各自package。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -23,17 +23,15 @@ kind: "package-bundle"
|
|
|
23
23
|
<a id="use-this-package"></a>
|
|
24
24
|
## 使用此软件包
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
无需Desktop或tray即可安装并materialize Plus:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
|
|
30
|
-
dsh
|
|
29
|
+
dsh plugin --profile plus add @sparkelf/dsh-plus
|
|
30
|
+
dsh plugin --profile plus exec dsh-plus apply --dsh-root /path/to/official-dsh
|
|
31
31
|
dsh --profile plus
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
source checkout 的显式 apply 步骤要求`dshPlus.sourceBase.revision`声明的精确official revision,把内嵌的`dshPlus.patchSet`应用到该checkout,配置patch set声明的npm patches,并以`0600` mode写入`.dsh-plus/patchset.lock.json`。profile中的patched official source workspaces始终覆盖clean CLI packages。profile从npm解析其pinned third-party与Plus capability packages。version conflict、source mismatch、bundle缺失和runtime closure drift都会阻止promotion。
|
|
34
|
+
rc24 explicit apply步骤固定official revision `fb2c4b9e698e30edb738bca4cf0618587db7d203`,一次应用全部pending source payloads,配置exact npm patches,删除retired Plus-owned entries,并以`0600` mode写入`.dsh-plus/patchset.lock.json`。profile中的patched official source workspaces始终覆盖clean CLI packages。profile从npm解析SQL Workbench 0.5.0、Workbench Vault 0.1.1、SSH Manager 0.7.0、API Client 0.5.0、Supervisor 0.1.4、GenUI 0.9.9、dshmarket 1.45.1、OfficeCLI 0.1.1、MinerU 0.1.1及Mobile Bridge 0.2.11。version conflict、source mismatch、bundle缺失和runtime closure drift都会阻止promotion。
|
|
37
35
|
|
|
38
36
|
Official DSH提供Sidebar navigation、file与text preview、Session search、generic file upload、durable attachment cards、model-visible readonly paths、onboarding、Workspace selection、Session export及Turn folding。Plus把official SQLite Session query provider配置为首次搜索时打开,持久派生索引位于`$DSH_HOME/storages/session-query.sqlite`,canonical compressed JSONL仍为history source。Plus增加用于生成原始DOCX/XLSX/PPTX的OfficeCLI工具,以及无需prompt hook、直接消费official uploaded-file path的MinerU PDF工具;同时把简体中文导出界面中混用的英文`Session`替换为中文,在桌面端把会话日志操作放到Trajectory搜索框紧邻左侧,在手机上保留其Header位置,并让composer permission与model popover保持在center column内且水平边缘各留12px间距。`@changfenhuang/dsh-genui@0.9.9`已内含此前patch的streaming EChart behavior。registry-pinned Supervisor管理materialized runtime并恢复被自身restart中断的Sessions。DataOps和MinerU仅在对应endpoint environment variables存在时mount。Agent Teams仍被排除,因为official shipped profiles禁用这些private experimental packages。
|
|
39
37
|
|
package/lib/apply.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as parseOfficialWorkspacePackages, r as runApply, t as materializeOfficialPackageScope } from "./apply-
|
|
1
|
+
import { n as parseOfficialWorkspacePackages, r as runApply, t as materializeOfficialPackageScope } from "./apply-e0aMC4B4.js";
|
|
2
2
|
export { materializeOfficialPackageScope, parseOfficialWorkspacePackages, runApply };
|
package/lib/bin.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { r as runApply } from "./apply-e0aMC4B4.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
5
5
|
import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { dirname, join, posix, resolve, win32 } from "node:path";
|
|
6
|
+
import { dirname, isAbsolute, join, posix, relative, resolve, win32 } from "node:path";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
|
-
import semver from "semver";
|
|
8
|
+
import semver, { validRange } from "semver";
|
|
9
9
|
import { parseDocument } from "yaml";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { createServer } from "node:net";
|
|
@@ -790,6 +790,125 @@ async function waitForAuthenticatedUrl(logPath, timeoutMilliseconds) {
|
|
|
790
790
|
}
|
|
791
791
|
}
|
|
792
792
|
//#endregion
|
|
793
|
+
//#region lib/types/patch-set.js
|
|
794
|
+
/**
|
|
795
|
+
* Read the Plus distribution's embedded patch set.
|
|
796
|
+
*
|
|
797
|
+
* The distribution owns one patch set for official DSH and, separately, the patches it
|
|
798
|
+
* carries for third-party packages. Both live inside the distribution package: the
|
|
799
|
+
* manifest declares entries, and each entry's payload is a file under `patches/`. A
|
|
800
|
+
* consumer resolves no independently published patch package.
|
|
801
|
+
*
|
|
802
|
+
* The two lists are governed differently. A Plus entry must target an official package
|
|
803
|
+
* that the profile republishes and overrides; a third-party entry must name its upstream
|
|
804
|
+
* and a retirement condition, and never becomes part of the official patch set.
|
|
805
|
+
*/
|
|
806
|
+
function object(value, label) {
|
|
807
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(label + " must be an object");
|
|
808
|
+
return value;
|
|
809
|
+
}
|
|
810
|
+
function string(value, label) {
|
|
811
|
+
if (typeof value !== "string" || value === "") throw new Error(label + " must be a non-empty string");
|
|
812
|
+
return value;
|
|
813
|
+
}
|
|
814
|
+
function range(value, label) {
|
|
815
|
+
const text = string(value, label);
|
|
816
|
+
if (validRange(text) === null) throw new Error(label + " is not a semantic-version range: " + text);
|
|
817
|
+
return text;
|
|
818
|
+
}
|
|
819
|
+
/** A payload stays inside the distribution package it is declared by. */
|
|
820
|
+
function patchFile(distributionDirectory, declared, label) {
|
|
821
|
+
const absolute = resolve(distributionDirectory, declared);
|
|
822
|
+
const local = relative(distributionDirectory, absolute);
|
|
823
|
+
if (local === "" || isAbsolute(local) || local === ".." || local.startsWith("../")) throw new Error(label + " must stay inside the distribution package: " + declared);
|
|
824
|
+
if (!existsSync(absolute)) throw new Error(label + " does not exist: " + absolute);
|
|
825
|
+
return local.replaceAll("\\", "/");
|
|
826
|
+
}
|
|
827
|
+
function sourceTarget(value, sourceBaseRevision, label) {
|
|
828
|
+
const target = object(value, label);
|
|
829
|
+
if (target.kind !== "dsh-source") throw new Error(label + ".kind must be dsh-source");
|
|
830
|
+
if (!Array.isArray(target.paths) || target.paths.length === 0) throw new Error(label + ".paths must be a non-empty array");
|
|
831
|
+
const paths = target.paths.map((entry, index) => {
|
|
832
|
+
const path = string(entry, label + ".paths[" + String(index) + "]");
|
|
833
|
+
if (!path.endsWith("/")) throw new Error(label + ".paths entries must be repository directory prefixes");
|
|
834
|
+
return path;
|
|
835
|
+
});
|
|
836
|
+
if (!Array.isArray(target.packages) || target.packages.length === 0) throw new Error(label + ".packages must be a non-empty array");
|
|
837
|
+
return {
|
|
838
|
+
kind: "dsh-source",
|
|
839
|
+
baseRevision: sourceBaseRevision,
|
|
840
|
+
packages: target.packages.map((entry, index) => {
|
|
841
|
+
const name = string(entry, label + ".packages[" + String(index) + "]");
|
|
842
|
+
if (!name.startsWith("@deepseek-ai/")) throw new Error(label + ".packages entries must be official package names");
|
|
843
|
+
return name;
|
|
844
|
+
}),
|
|
845
|
+
paths
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
function npmTarget(value, label) {
|
|
849
|
+
const target = object(value, label);
|
|
850
|
+
if (target.kind !== "npm") throw new Error(label + ".kind must be npm");
|
|
851
|
+
return {
|
|
852
|
+
kind: "npm",
|
|
853
|
+
name: string(target.name, label + ".name"),
|
|
854
|
+
range: range(target.range, label + ".range")
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
function entry(value, scope, sourceBaseRevision, distributionDirectory, label) {
|
|
858
|
+
const record = object(value, label);
|
|
859
|
+
const id = string(record.id, label + ".id");
|
|
860
|
+
const dsh = range(record.dsh, label + ".dsh");
|
|
861
|
+
const file = patchFile(distributionDirectory, string(record.file, label + ".file"), label + ".file");
|
|
862
|
+
const retireWhen = string(record.retireWhen, label + ".retireWhen");
|
|
863
|
+
const targetLabel = label + ".target";
|
|
864
|
+
const target = scope === "plus" ? object(record.target, targetLabel).kind === "dsh-source" ? sourceTarget(record.target, sourceBaseRevision, targetLabel) : npmTarget(record.target, targetLabel) : npmTarget(record.target, targetLabel);
|
|
865
|
+
if (scope === "third-party") return {
|
|
866
|
+
id,
|
|
867
|
+
scope,
|
|
868
|
+
dsh,
|
|
869
|
+
file,
|
|
870
|
+
retireWhen,
|
|
871
|
+
upstreamUrl: string(record.upstreamUrl, label + ".upstreamUrl"),
|
|
872
|
+
target
|
|
873
|
+
};
|
|
874
|
+
return {
|
|
875
|
+
id,
|
|
876
|
+
scope,
|
|
877
|
+
dsh,
|
|
878
|
+
file,
|
|
879
|
+
retireWhen,
|
|
880
|
+
target
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Read and validate the embedded patch set from an installed distribution.
|
|
885
|
+
* @param distributionDirectory - installed `@sparkelf/dsh-plus` directory.
|
|
886
|
+
* @param manifest - its parsed package manifest.
|
|
887
|
+
* @returns the ordered Plus entries, third-party entries, and the exact source base.
|
|
888
|
+
*/
|
|
889
|
+
function readPatchSet(distributionDirectory, manifest) {
|
|
890
|
+
const plus = object(manifest.dshPlus, "dshPlus");
|
|
891
|
+
const sourceBaseRevision = string(object(plus.sourceBase, "dshPlus.sourceBase").revision, "dshPlus.sourceBase.revision");
|
|
892
|
+
const declaration = object(plus.patchSet, "dshPlus.patchSet");
|
|
893
|
+
if (declaration.formatVersion !== 1) throw new Error("dshPlus.patchSet.formatVersion must be 1");
|
|
894
|
+
if (!Array.isArray(declaration.entries) || declaration.entries.length === 0) throw new Error("dshPlus.patchSet.entries must be a non-empty array");
|
|
895
|
+
const entries = declaration.entries.map((value, index) => entry(value, "plus", sourceBaseRevision, distributionDirectory, "dshPlus.patchSet.entries[" + String(index) + "]"));
|
|
896
|
+
const thirdPartyRaw = plus.thirdPartyPatches;
|
|
897
|
+
const thirdParty = thirdPartyRaw === void 0 ? [] : Array.isArray(thirdPartyRaw) ? thirdPartyRaw.map((value, index) => entry(value, "third-party", sourceBaseRevision, distributionDirectory, "dshPlus.thirdPartyPatches[" + String(index) + "]")) : (() => {
|
|
898
|
+
throw new Error("dshPlus.thirdPartyPatches must be an array");
|
|
899
|
+
})();
|
|
900
|
+
const ids = /* @__PURE__ */ new Set();
|
|
901
|
+
for (const item of [...entries, ...thirdParty]) {
|
|
902
|
+
if (ids.has(item.id)) throw new Error("duplicate patch entry id: " + item.id);
|
|
903
|
+
ids.add(item.id);
|
|
904
|
+
}
|
|
905
|
+
return {
|
|
906
|
+
entries,
|
|
907
|
+
thirdPartyPatches: thirdParty,
|
|
908
|
+
sourceBaseRevision
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
//#endregion
|
|
793
912
|
//#region lib/types/standalone-profile.js
|
|
794
913
|
/**
|
|
795
914
|
* Runtime paths and profile composition for a standalone Plus installation.
|
|
@@ -858,11 +977,6 @@ function requireStringArray(value, label) {
|
|
|
858
977
|
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(label + " must be a string array");
|
|
859
978
|
return value;
|
|
860
979
|
}
|
|
861
|
-
/** Read one non-empty string field. */
|
|
862
|
-
function requireString(value, label) {
|
|
863
|
-
if (typeof value !== "string" || value.trim() === "") throw new Error(label + " must be a non-empty string");
|
|
864
|
-
return value;
|
|
865
|
-
}
|
|
866
980
|
/** Resolve the DSH home, honouring the environment override the launcher itself reads. */
|
|
867
981
|
function resolveHome(env = process.env) {
|
|
868
982
|
const configured = env.DSH_HOME;
|
|
@@ -900,12 +1014,11 @@ function isWithin(candidate, parent, platform = process.platform) {
|
|
|
900
1014
|
* @returns absolute path to the installed distribution.
|
|
901
1015
|
*/
|
|
902
1016
|
function resolveDistributionDirectory(anchor) {
|
|
903
|
-
const ownDirectory = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
904
1017
|
let resolved;
|
|
905
1018
|
try {
|
|
906
1019
|
resolved = dirname(createRequire(anchor).resolve("@sparkelf/dsh-plus/package.json"));
|
|
907
1020
|
} catch {
|
|
908
|
-
|
|
1021
|
+
throw new Error("@sparkelf/dsh-plus is not installed in " + dirname(anchor) + "; run this command from the directory that installed it");
|
|
909
1022
|
}
|
|
910
1023
|
let current = resolve(dirname(anchor));
|
|
911
1024
|
for (;;) {
|
|
@@ -914,14 +1027,20 @@ function resolveDistributionDirectory(anchor) {
|
|
|
914
1027
|
if (parent === current) break;
|
|
915
1028
|
current = parent;
|
|
916
1029
|
}
|
|
917
|
-
|
|
1030
|
+
throw new Error("@sparkelf/dsh-plus resolved outside " + resolve(dirname(anchor)) + " (found " + resolved + "); run this command from the directory that installed it");
|
|
918
1031
|
}
|
|
1032
|
+
/** The reviewed bundle order and pins the installed distribution declares. */
|
|
919
1033
|
function readDistributionProfile(distributionDirectory) {
|
|
920
1034
|
const manifest = requireRecord(JSON.parse(readFileSync(join(distributionDirectory, "package.json"), "utf8")), "Plus distribution manifest");
|
|
921
1035
|
const plus = requireRecord(manifest.dshPlus, "dshPlus");
|
|
922
1036
|
const profile = requireRecord(plus.profile, "dshPlus.profile");
|
|
923
|
-
const
|
|
1037
|
+
const rawDependencies = requireRecord(profile.dependencies, "dshPlus.profile.dependencies");
|
|
924
1038
|
const rawAllowBuilds = requireRecord(profile.allowBuilds, "dshPlus.profile.allowBuilds");
|
|
1039
|
+
const dependencies = {};
|
|
1040
|
+
for (const [name, spec] of Object.entries(rawDependencies)) {
|
|
1041
|
+
if (typeof spec !== "string" || spec === "") throw new Error("dshPlus.profile.dependencies." + name + " must be a non-empty string");
|
|
1042
|
+
dependencies[name] = spec;
|
|
1043
|
+
}
|
|
925
1044
|
const allowBuilds = {};
|
|
926
1045
|
for (const [name, allowed] of Object.entries(rawAllowBuilds)) {
|
|
927
1046
|
if (typeof allowed !== "boolean") throw new Error("dshPlus.profile.allowBuilds." + name + " must be a boolean");
|
|
@@ -933,16 +1052,6 @@ function readDistributionProfile(distributionDirectory) {
|
|
|
933
1052
|
if (typeof spec !== "string" || spec === "") throw new Error("dshPlus.profile.overrides." + name + " must be a non-empty string");
|
|
934
1053
|
overrides[name] = spec;
|
|
935
1054
|
}
|
|
936
|
-
const editions = {};
|
|
937
|
-
const rawEditions = plus.editions === void 0 ? {} : requireRecord(plus.editions, "dshPlus.editions");
|
|
938
|
-
for (const [id, value] of Object.entries(rawEditions)) {
|
|
939
|
-
const edition = requireRecord(value, "dshPlus.editions." + id);
|
|
940
|
-
editions[id] = {
|
|
941
|
-
label: requireString(edition.label, "dshPlus.editions." + id + ".label"),
|
|
942
|
-
bundles: requireStringArray(edition.bundles, "dshPlus.editions." + id + ".bundles"),
|
|
943
|
-
packages: requireDependencyMap(requireRecord(edition.packages, "dshPlus.editions." + id + ".packages"), "dshPlus.editions." + id + ".packages")
|
|
944
|
-
};
|
|
945
|
-
}
|
|
946
1055
|
const compatibility = requireRecord(plus.compatibility, "dshPlus.compatibility");
|
|
947
1056
|
return {
|
|
948
1057
|
name: String(manifest.name),
|
|
@@ -950,20 +1059,10 @@ function readDistributionProfile(distributionDirectory) {
|
|
|
950
1059
|
dependencies,
|
|
951
1060
|
allowBuilds,
|
|
952
1061
|
overrides,
|
|
953
|
-
editions,
|
|
954
1062
|
dshRange: String(compatibility.dsh),
|
|
955
1063
|
version: String(manifest.version)
|
|
956
1064
|
};
|
|
957
1065
|
}
|
|
958
|
-
/** Read one package-name-to-spec map, rejecting empty entries. */
|
|
959
|
-
function requireDependencyMap(value, label) {
|
|
960
|
-
const dependencies = {};
|
|
961
|
-
for (const [name, spec] of Object.entries(value)) {
|
|
962
|
-
if (typeof spec !== "string" || spec === "") throw new Error(label + "." + name + " must be a non-empty string");
|
|
963
|
-
dependencies[name] = spec;
|
|
964
|
-
}
|
|
965
|
-
return dependencies;
|
|
966
|
-
}
|
|
967
1066
|
/**
|
|
968
1067
|
* Give the profile its own installed tree, built from the distribution's declarations.
|
|
969
1068
|
*
|
|
@@ -983,11 +1082,7 @@ function requireDependencyMap(value, label) {
|
|
|
983
1082
|
* @param consumerDirectory - directory whose `node_modules` holds the installation.
|
|
984
1083
|
*/
|
|
985
1084
|
function installProfilePackages(paths, consumerDirectory) {
|
|
986
|
-
if (!existsSync(join(consumerDirectory, "node_modules")))
|
|
987
|
-
installWithRegistryFallback(paths);
|
|
988
|
-
alignReplacedPackageNames(join(paths.profileDirectory, "node_modules"));
|
|
989
|
-
return;
|
|
990
|
-
}
|
|
1085
|
+
if (!existsSync(join(consumerDirectory, "node_modules"))) throw new Error("no node_modules in " + consumerDirectory + "; run npm install there first");
|
|
991
1086
|
const profileModules = join(paths.profileDirectory, "node_modules");
|
|
992
1087
|
if (existsSync(profileModules)) {
|
|
993
1088
|
linkNestedBundles(paths, profileModules);
|
|
@@ -1009,7 +1104,6 @@ function installProfilePackages(paths, consumerDirectory) {
|
|
|
1009
1104
|
function installWithRegistryFallback(paths) {
|
|
1010
1105
|
const registries = registryOrder();
|
|
1011
1106
|
for (const [index, registry] of registries.entries()) {
|
|
1012
|
-
if (index > 0) rmSync(join(paths.profileDirectory, "pnpm-lock.yaml"), { force: true });
|
|
1013
1107
|
const result = spawnSync("pnpm", [
|
|
1014
1108
|
"install",
|
|
1015
1109
|
"--no-frozen-lockfile",
|
|
@@ -1018,8 +1112,7 @@ function installWithRegistryFallback(paths) {
|
|
|
1018
1112
|
], {
|
|
1019
1113
|
cwd: paths.profileDirectory,
|
|
1020
1114
|
stdio: "inherit",
|
|
1021
|
-
shell: process.platform === "win32"
|
|
1022
|
-
timeout: 5 * 6e4
|
|
1115
|
+
shell: process.platform === "win32"
|
|
1023
1116
|
});
|
|
1024
1117
|
if (result.error !== void 0) throw result.error;
|
|
1025
1118
|
if (result.status === 0) return;
|
|
@@ -1078,7 +1171,7 @@ const MAINLAND_REGISTRY = "https://registry.npmmirror.com";
|
|
|
1078
1171
|
*/
|
|
1079
1172
|
function registryOrder() {
|
|
1080
1173
|
const configured = process.env.DSH_PLUS_INSTALL_REGISTRY;
|
|
1081
|
-
if (configured !== void 0 && configured !== "") return [configured,
|
|
1174
|
+
if (configured !== void 0 && configured !== "") return [configured, MAINLAND_REGISTRY];
|
|
1082
1175
|
const locale = [
|
|
1083
1176
|
process.env.LANG,
|
|
1084
1177
|
process.env.LC_ALL,
|
|
@@ -1179,47 +1272,12 @@ function resolvePaths(anchor, env = process.env) {
|
|
|
1179
1272
|
*
|
|
1180
1273
|
* @param paths - resolved standalone paths.
|
|
1181
1274
|
* @param consumerDirectory - directory whose `node_modules` holds the installed packages.
|
|
1182
|
-
* @param edition - edition id whose package list to install, or undefined for the default.
|
|
1183
1275
|
* @returns whether this call created the manifest.
|
|
1184
1276
|
*/
|
|
1185
|
-
function ensureProfile(paths, consumerDirectory
|
|
1277
|
+
function ensureProfile(paths, consumerDirectory) {
|
|
1186
1278
|
const manifestPath = join(paths.profileDirectory, "package.json");
|
|
1187
1279
|
const distribution = readDistributionProfile(paths.distributionDirectory);
|
|
1188
|
-
const composition = edition === void 0 ? void 0 : distribution.editions[edition];
|
|
1189
|
-
if (edition !== void 0 && composition === void 0) {
|
|
1190
|
-
const offered = Object.keys(distribution.editions);
|
|
1191
|
-
throw new Error("unknown edition \"" + edition + "\"; this release offers " + (offered.length === 0 ? "(none)" : offered.join(", ")));
|
|
1192
|
-
}
|
|
1193
|
-
for (const name of Object.keys(composition?.packages ?? {})) if (name.startsWith("@deepseek-ai/")) throw new Error("edition \"" + String(edition) + "\" lists the official package " + name + "; editions contain only third-party or self-developed plugins");
|
|
1194
|
-
const runtime = distribution.dshRange.replace(/^>=?/u, "");
|
|
1195
|
-
const baseBundles = composition === void 0 ? distribution.bundles : distribution.bundles.filter((bundle) => bundle.startsWith("@deepseek-ai/") || bundle === "@sparkelf/dsh-plus");
|
|
1196
|
-
const basePackages = composition === void 0 ? distribution.dependencies : {
|
|
1197
|
-
"@deepseek-ai/dsh": distribution.dshRange,
|
|
1198
|
-
"@sparkelf/dsh-plus": distribution.version,
|
|
1199
|
-
...Object.fromEntries(baseBundles.filter((bundle) => bundle.startsWith("@deepseek-ai/dsh-")).map((bundle) => [bundle, runtime]))
|
|
1200
|
-
};
|
|
1201
|
-
const compositionPackages = composition === void 0 ? basePackages : {
|
|
1202
|
-
...basePackages,
|
|
1203
|
-
...composition.packages
|
|
1204
|
-
};
|
|
1205
|
-
const compositionBundles = composition === void 0 ? baseBundles : [...baseBundles, ...composition.bundles];
|
|
1206
1280
|
if (existsSync(manifestPath)) {
|
|
1207
|
-
if (composition !== void 0) {
|
|
1208
|
-
const manifest = requireRecord(JSON.parse(readFileSync(manifestPath, "utf8")), "Plus profile manifest");
|
|
1209
|
-
manifest.dependencies = {
|
|
1210
|
-
...manifest.dependencies,
|
|
1211
|
-
...compositionPackages
|
|
1212
|
-
};
|
|
1213
|
-
manifest.dsh = {
|
|
1214
|
-
...manifest.dsh,
|
|
1215
|
-
profile: {
|
|
1216
|
-
...manifest.dsh?.profile,
|
|
1217
|
-
bundles: [...compositionBundles],
|
|
1218
|
-
...edition === void 0 ? {} : { plusEdition: edition }
|
|
1219
|
-
}
|
|
1220
|
-
};
|
|
1221
|
-
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
1222
|
-
}
|
|
1223
1281
|
writeProfileOverrides(paths.profileDirectory, distribution.overrides, distribution.allowBuilds, paths.omittedPackages);
|
|
1224
1282
|
installProfilePackages(paths, consumerDirectory);
|
|
1225
1283
|
return false;
|
|
@@ -1229,11 +1287,14 @@ function ensureProfile(paths, consumerDirectory, edition) {
|
|
|
1229
1287
|
name: "dsh-profile-plus",
|
|
1230
1288
|
private: true,
|
|
1231
1289
|
type: "module",
|
|
1232
|
-
dependencies:
|
|
1290
|
+
dependencies: {
|
|
1291
|
+
"@sparkelf/dsh-plus": distribution.version,
|
|
1292
|
+
"@deepseek-ai/dsh": distribution.dshRange,
|
|
1293
|
+
...distribution.dependencies
|
|
1294
|
+
},
|
|
1233
1295
|
dsh: { profile: {
|
|
1234
|
-
bundles:
|
|
1235
|
-
patchReload: "live"
|
|
1236
|
-
...edition === void 0 ? {} : { plusEdition: edition }
|
|
1296
|
+
bundles: distribution.bundles,
|
|
1297
|
+
patchReload: "live"
|
|
1237
1298
|
} }
|
|
1238
1299
|
};
|
|
1239
1300
|
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
@@ -1317,18 +1378,7 @@ function resolveInstalledPackage(from, packageName) {
|
|
|
1317
1378
|
try {
|
|
1318
1379
|
manifestPath = requireFrom.resolve(packageName + "/package.json");
|
|
1319
1380
|
} catch {
|
|
1320
|
-
|
|
1321
|
-
try {
|
|
1322
|
-
directory = dirname(requireFrom.resolve(packageName));
|
|
1323
|
-
} catch {
|
|
1324
|
-
throw new Error(packageName + " is not installed under " + from);
|
|
1325
|
-
}
|
|
1326
|
-
while (!existsSync(resolve(directory, "package.json"))) {
|
|
1327
|
-
const parent = dirname(directory);
|
|
1328
|
-
if (parent === directory) throw new Error(packageName + " has no resolvable package manifest under " + from);
|
|
1329
|
-
directory = parent;
|
|
1330
|
-
}
|
|
1331
|
-
manifestPath = resolve(directory, "package.json");
|
|
1381
|
+
throw new Error(packageName + " is not installed under " + from);
|
|
1332
1382
|
}
|
|
1333
1383
|
const manifest = requireRecord(JSON.parse(readFileSync(manifestPath, "utf8")), packageName + " manifest");
|
|
1334
1384
|
return {
|
|
@@ -1358,7 +1408,6 @@ function parseStartOptions(argv) {
|
|
|
1358
1408
|
let host = "127.0.0.1";
|
|
1359
1409
|
let open = true;
|
|
1360
1410
|
let foreground = false;
|
|
1361
|
-
let edition;
|
|
1362
1411
|
let capabilities;
|
|
1363
1412
|
for (let index = 0; index < argv.length; index += 1) {
|
|
1364
1413
|
const token = argv[index];
|
|
@@ -1384,13 +1433,6 @@ function parseStartOptions(argv) {
|
|
|
1384
1433
|
foreground = true;
|
|
1385
1434
|
continue;
|
|
1386
1435
|
}
|
|
1387
|
-
if (token === "--edition") {
|
|
1388
|
-
const value = argv[index + 1];
|
|
1389
|
-
if (value === void 0 || value === "") throw new Error("--edition requires a value");
|
|
1390
|
-
edition = value;
|
|
1391
|
-
index += 1;
|
|
1392
|
-
continue;
|
|
1393
|
-
}
|
|
1394
1436
|
if (token === "--capabilities") {
|
|
1395
1437
|
const value = argv[index + 1];
|
|
1396
1438
|
if (value === void 0) throw new Error("--capabilities requires a comma-separated list, or an empty string for none");
|
|
@@ -1408,7 +1450,6 @@ function parseStartOptions(argv) {
|
|
|
1408
1450
|
host,
|
|
1409
1451
|
open,
|
|
1410
1452
|
foreground,
|
|
1411
|
-
edition,
|
|
1412
1453
|
capabilities
|
|
1413
1454
|
};
|
|
1414
1455
|
}
|
|
@@ -1494,56 +1535,6 @@ function runForeground(entry, profileName, port, host, open) {
|
|
|
1494
1535
|
if (!open) args.push("--no-open");
|
|
1495
1536
|
return spawnSync(process.execPath, args, { stdio: "inherit" }).status ?? 1;
|
|
1496
1537
|
}
|
|
1497
|
-
/** Parse the install command's edition selection. */
|
|
1498
|
-
function parseInstallOptions(argv) {
|
|
1499
|
-
let edition;
|
|
1500
|
-
let profile;
|
|
1501
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
1502
|
-
const token = argv[index];
|
|
1503
|
-
if (token === "--edition") {
|
|
1504
|
-
const value = argv[index + 1];
|
|
1505
|
-
if (value === void 0 || value === "") throw new Error("--edition requires a value");
|
|
1506
|
-
edition = value;
|
|
1507
|
-
index += 1;
|
|
1508
|
-
continue;
|
|
1509
|
-
}
|
|
1510
|
-
if (token === "--profile") {
|
|
1511
|
-
const value = argv[index + 1];
|
|
1512
|
-
if (value === void 0 || value === "") throw new Error("--profile requires a value");
|
|
1513
|
-
profile = value;
|
|
1514
|
-
index += 1;
|
|
1515
|
-
continue;
|
|
1516
|
-
}
|
|
1517
|
-
throw new Error("unknown option: " + String(token));
|
|
1518
|
-
}
|
|
1519
|
-
if (edition === void 0) throw new Error("install requires --edition personal or --edition team");
|
|
1520
|
-
return {
|
|
1521
|
-
edition,
|
|
1522
|
-
profile
|
|
1523
|
-
};
|
|
1524
|
-
}
|
|
1525
|
-
/**
|
|
1526
|
-
* Install one edition's package manifest into the profile.
|
|
1527
|
-
*
|
|
1528
|
-
* The profile's `package.json` is the install artifact; there is no separate standalone
|
|
1529
|
-
* package. A later `start` uses the same manifest, so changing the edition is a new
|
|
1530
|
-
* install rather than a second product.
|
|
1531
|
-
*
|
|
1532
|
-
* @param argv - arguments after `install`.
|
|
1533
|
-
* @returns the process exit code.
|
|
1534
|
-
*/
|
|
1535
|
-
function install(argv) {
|
|
1536
|
-
const { edition, profile } = parseInstallOptions(argv);
|
|
1537
|
-
const base = resolvePaths(declarationAnchor());
|
|
1538
|
-
const paths = profile === void 0 ? base : {
|
|
1539
|
-
...base,
|
|
1540
|
-
profileName: profile,
|
|
1541
|
-
profileDirectory: join(base.home, "profiles", profile)
|
|
1542
|
-
};
|
|
1543
|
-
ensureProfile(paths, installationRoot(), edition);
|
|
1544
|
-
console.log("Installed the " + edition + " edition into " + paths.profileDirectory);
|
|
1545
|
-
return 0;
|
|
1546
|
-
}
|
|
1547
1538
|
async function start(argv) {
|
|
1548
1539
|
const options = parseStartOptions(argv);
|
|
1549
1540
|
const anchor = installationAnchor();
|
|
@@ -1577,9 +1568,8 @@ async function start(argv) {
|
|
|
1577
1568
|
}
|
|
1578
1569
|
console.log("pnpm installed.");
|
|
1579
1570
|
}
|
|
1580
|
-
const created = ensureProfile(paths, installationRoot()
|
|
1571
|
+
const created = ensureProfile(paths, installationRoot());
|
|
1581
1572
|
console.log(created ? "Created the " + paths.profileName + " profile at " + paths.profileDirectory : "Using the existing " + paths.profileName + " profile");
|
|
1582
|
-
if (options.edition !== void 0) console.log("Edition: " + options.edition);
|
|
1583
1573
|
for (const label of applyProfileNpmPatches(paths.distributionDirectory, paths.profileDirectory)) console.log("Applied the reviewed patch " + label);
|
|
1584
1574
|
const answers = created || !existsSync(join(paths.home, "capabilities.json")) ? options.capabilities === void 0 ? await interviewCapabilities(true) : selectCapabilities(options.capabilities) : void 0;
|
|
1585
1575
|
if (answers !== void 0) {
|
|
@@ -1922,23 +1912,6 @@ async function doctor() {
|
|
|
1922
1912
|
const record = readCapabilityRecord(paths.home);
|
|
1923
1913
|
const drifted = record === void 0 ? [] : CAPABILITIES.filter((capability) => record.enabled.includes(capability.id) !== actual.mounted.includes(capability.id));
|
|
1924
1914
|
check(drifted.length === 0, drifted.length === 0 ? "capabilities: " + (actual.mounted.length === 0 ? "(none enabled)" : actual.mounted.join(", ")) : "capabilities disagree with capabilities.json: " + drifted.map((capability) => capability.id).join(", ") + " (run dsh-plus capabilities)");
|
|
1925
|
-
const profileManifestPath = join(paths.profileDirectory, "package.json");
|
|
1926
|
-
if (existsSync(profileManifestPath)) {
|
|
1927
|
-
const profileManifest = JSON.parse(readFileSync(profileManifestPath, "utf8"));
|
|
1928
|
-
const editionId = profileManifest.dsh?.profile?.plusEdition;
|
|
1929
|
-
if (editionId !== void 0) {
|
|
1930
|
-
const edition = readDistributionProfile(paths.distributionDirectory).editions[editionId];
|
|
1931
|
-
if (edition === void 0) check(false, "edition \"" + editionId + "\" is not declared by this distribution");
|
|
1932
|
-
else {
|
|
1933
|
-
const bundles = new Set(profileManifest.dsh?.profile?.bundles ?? []);
|
|
1934
|
-
const missingBundles = edition.bundles.filter((bundle) => !bundles.has(bundle));
|
|
1935
|
-
const packageNames = Object.keys(edition.packages);
|
|
1936
|
-
const missingPackages = packageNames.filter((name) => !existsSync(join(paths.profileDirectory, "node_modules", ...name.split("/"), "package.json")));
|
|
1937
|
-
check(missingBundles.length === 0, "edition " + editionId + ": " + String(edition.bundles.length - missingBundles.length) + "/" + String(edition.bundles.length) + " bundles mounted" + (missingBundles.length === 0 ? "" : " (missing " + missingBundles.join(", ") + ")"));
|
|
1938
|
-
check(missingPackages.length === 0, "edition " + editionId + ": " + String(packageNames.length - missingPackages.length) + "/" + String(packageNames.length) + " packages installed" + (missingPackages.length === 0 ? "" : " (missing " + missingPackages.join(", ") + ")"));
|
|
1939
|
-
}
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
1915
|
console.log(failures === 0 ? "No problems found." : String(failures) + " problem(s) found.");
|
|
1943
1916
|
return failures === 0 ? 0 : 1;
|
|
1944
1917
|
}
|
|
@@ -1946,7 +1919,6 @@ const USAGE = [
|
|
|
1946
1919
|
"Usage: dsh-plus <command> [options]",
|
|
1947
1920
|
"",
|
|
1948
1921
|
"Commands:",
|
|
1949
|
-
" install install an edition package manifest into the profile",
|
|
1950
1922
|
" start start the server (first run creates the profile)",
|
|
1951
1923
|
" stop stop the server",
|
|
1952
1924
|
" status report whether the server is running",
|
|
@@ -1959,11 +1931,6 @@ const USAGE = [
|
|
|
1959
1931
|
" --host <h> interface to bind (default 127.0.0.1)",
|
|
1960
1932
|
" --no-open do not open a browser",
|
|
1961
1933
|
" --foreground run in this terminal instead of in the background",
|
|
1962
|
-
" --edition <id> install the named edition manifest (personal or team)",
|
|
1963
|
-
"",
|
|
1964
|
-
"install options:",
|
|
1965
|
-
" --edition <id> package manifest to install (personal or team)",
|
|
1966
|
-
" --profile <n> profile name to install into (default: this installation)",
|
|
1967
1934
|
"",
|
|
1968
1935
|
"capabilities options:",
|
|
1969
1936
|
" --enable <ids> enable the comma-separated capability ids",
|
|
@@ -1986,7 +1953,6 @@ async function runStandaloneCli(argv) {
|
|
|
1986
1953
|
console.log(USAGE);
|
|
1987
1954
|
return 0;
|
|
1988
1955
|
}
|
|
1989
|
-
if (command === "install") return install(rest);
|
|
1990
1956
|
if (command === "start") return await start(rest);
|
|
1991
1957
|
if (command === "stop") return stop();
|
|
1992
1958
|
if (command === "status") return status();
|
package/lib/types/apply.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Materialize the Plus distribution's
|
|
1
|
+
/** Materialize the Plus distribution's independent patch packages without Desktop. */
|
|
2
2
|
/** One official workspace package available as a profile peer dependency. */
|
|
3
3
|
export interface OfficialWorkspacePackage {
|
|
4
4
|
name: string;
|