dsh-update-status 0.1.11 → 0.2.0
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/CHANGELOG.md +21 -0
- package/README.md +40 -51
- package/README.zh.md +39 -50
- package/assets/update-panel.png +0 -0
- package/lib/client.js +202 -443
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +47 -59
- package/lib/index.js +76 -89
- package/package.json +4 -2
package/lib/index.d.ts
CHANGED
|
@@ -1,21 +1,42 @@
|
|
|
1
1
|
import { Context } from "@deepseek-ai/cordis";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
//#region src/host/settings.d.ts
|
|
4
|
+
/** Deployment config (ordinary) plus the volatile user preferences the form edits. */
|
|
5
|
+
interface UpdateStatusConfig {
|
|
6
|
+
/** Deployment: on-demand registry cache duration, in hours. */
|
|
7
|
+
cacheTtlHours?: number;
|
|
8
|
+
/** Deployment: registry request timeout, in milliseconds. */
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
/** Deployment: run one metadata check when the process mounts the plugin. */
|
|
11
|
+
autoCheckOnMount?: boolean;
|
|
12
|
+
/** Preference: show the sidebar version chip and its update panel. */
|
|
13
|
+
sidebarEnabled?: boolean;
|
|
14
|
+
/** Preference: on-demand registry cache duration, in minutes. */
|
|
15
|
+
cacheTtlMinutes?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The plugin's Config schema; its volatile fields ARE the settings form.
|
|
19
|
+
*
|
|
20
|
+
* The explicit two-argument annotation is load-bearing: a `.volatile()` field's
|
|
21
|
+
* output is a stable reference (`Volatile<T>`) rather than the bare value the
|
|
22
|
+
* input side takes, so the inferred schema type cannot be named by the emitted
|
|
23
|
+
* `.d.ts` (TS2883) without stating the input side here. The builder preserves
|
|
24
|
+
* that contract even on the degraded path, where no field is volatile.
|
|
25
|
+
*/
|
|
26
|
+
export declare const Config: z<UpdateStatusConfig, Record<string, unknown>>;
|
|
27
|
+
//#endregion
|
|
3
28
|
//#region src/shared/types.d.ts
|
|
4
|
-
declare const RELEASE_CHANNELS: readonly ['latest', 'next', 'alpha'];
|
|
5
|
-
type ReleaseChannel = (typeof RELEASE_CHANNELS)[number];
|
|
6
29
|
type ReleaseCompatibility = 'verified' | 'unverified' | 'incompatible';
|
|
7
30
|
/**
|
|
8
31
|
* Severity of `warning`, so a surface can tell "the plugin could not determine
|
|
9
32
|
* the update state" from "here is something worth knowing".
|
|
10
33
|
*
|
|
11
|
-
* - `failure`: no usable answer — a failed registry read, a
|
|
12
|
-
*
|
|
13
|
-
* repainting the sidebar chip.
|
|
34
|
+
* - `failure`: no usable answer — a failed registry read, or a version SemVer
|
|
35
|
+
* cannot compare. This is what justifies repainting the sidebar chip.
|
|
14
36
|
* - `notice`: the answer is complete and usable; the text is advisory, e.g. a
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* fact.
|
|
37
|
+
* newer release this bundle has not been verified against. The chip must NOT
|
|
38
|
+
* repaint for this — an operator upgrading DSH ahead of the plugin would
|
|
39
|
+
* otherwise see the chip turn red for a plugin-side bookkeeping fact.
|
|
19
40
|
*
|
|
20
41
|
* Optional on purpose: a Host older than this field leaves it `undefined`, and
|
|
21
42
|
* the client then falls back to treating any warning as a failure.
|
|
@@ -26,17 +47,14 @@ type InstallKind = 'npm-global' | 'pnpm-global' | 'source-checkout' | 'unknown';
|
|
|
26
47
|
type UpdateWarning = {
|
|
27
48
|
code: 'registry-unavailable';
|
|
28
49
|
detail: string;
|
|
29
|
-
} | {
|
|
30
|
-
code: 'channel-unavailable';
|
|
31
|
-
channel: ReleaseChannel;
|
|
32
50
|
} | {
|
|
33
51
|
code: 'version-incomparable';
|
|
34
52
|
currentVersion: string;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
53
|
+
latestVersion: string;
|
|
54
|
+
} |
|
|
55
|
+
/** A newer release exists, but this bundle has not been tested against it. */
|
|
56
|
+
{
|
|
57
|
+
code: 'version-unverified';
|
|
40
58
|
version: string;
|
|
41
59
|
} |
|
|
42
60
|
/**
|
|
@@ -51,12 +69,6 @@ type UpdateWarning = {
|
|
|
51
69
|
path: string;
|
|
52
70
|
nodeModulesDir: string | null;
|
|
53
71
|
};
|
|
54
|
-
interface ChannelRelease {
|
|
55
|
-
channel: ReleaseChannel;
|
|
56
|
-
version: string | null;
|
|
57
|
-
publishedAt: string | null;
|
|
58
|
-
compatibility: ReleaseCompatibility;
|
|
59
|
-
}
|
|
60
72
|
/**
|
|
61
73
|
* A lossless, JSON-serializable snapshot used by both browser surfaces.
|
|
62
74
|
* Nulls are intentional: a failed first check must still render a truthful
|
|
@@ -64,8 +76,11 @@ interface ChannelRelease {
|
|
|
64
76
|
*/
|
|
65
77
|
interface UpdateStatus {
|
|
66
78
|
currentVersion: string;
|
|
79
|
+
/** Newest version the registry publishes under any dist-tag; null when unknown. */
|
|
67
80
|
latestVersion: string | null;
|
|
68
81
|
hasUpdate: boolean;
|
|
82
|
+
/** Whether this bundle was verified against `latestVersion`. */
|
|
83
|
+
compatibility: ReleaseCompatibility;
|
|
69
84
|
cached: boolean;
|
|
70
85
|
checkedAt: string | null;
|
|
71
86
|
/** English fallback for older clients; current clients localize `warnings`. */
|
|
@@ -80,54 +95,27 @@ interface UpdateStatus {
|
|
|
80
95
|
changelogUrl: string;
|
|
81
96
|
publishedAt: string | null;
|
|
82
97
|
packageName: string;
|
|
83
|
-
/** Selected npm dist-tag used for comparison and command generation. */
|
|
84
|
-
channel: ReleaseChannel;
|
|
85
|
-
/** All supported dist-tags returned by the same cached registry request. */
|
|
86
|
-
channels: ChannelRelease[];
|
|
87
98
|
/** Phase 1 is informational only; the GUI must never apply an update. */
|
|
88
99
|
canApplyInPlace: false;
|
|
89
100
|
}
|
|
90
101
|
//#endregion
|
|
91
|
-
//#region src/host/settings.d.ts
|
|
92
|
-
/** Deployment config (ordinary) plus the volatile user preferences the form edits. */
|
|
93
|
-
interface UpdateStatusConfig {
|
|
94
|
-
/** Deployment: on-demand registry cache duration, in hours. */
|
|
95
|
-
cacheTtlHours?: number;
|
|
96
|
-
/** Deployment: registry request timeout, in milliseconds. */
|
|
97
|
-
timeoutMs?: number;
|
|
98
|
-
/** Deployment: run one metadata check when the process mounts the plugin. */
|
|
99
|
-
autoCheckOnMount?: boolean;
|
|
100
|
-
/** Preference: show the sidebar version chip and its update panel. */
|
|
101
|
-
sidebarEnabled?: boolean;
|
|
102
|
-
/** Preference: release channel followed for comparison and commands. */
|
|
103
|
-
channel?: ReleaseChannel;
|
|
104
|
-
/** Preference: on-demand registry cache duration, in minutes. */
|
|
105
|
-
cacheTtlMinutes?: number;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* The plugin's Config schema; its volatile fields ARE the settings form.
|
|
109
|
-
*
|
|
110
|
-
* The explicit two-argument annotation is load-bearing: a `.volatile()` field's
|
|
111
|
-
* output is a stable reference (`Volatile<T>`) rather than the bare value the
|
|
112
|
-
* input side takes, so the inferred schema type cannot be named by the emitted
|
|
113
|
-
* `.d.ts` (TS2883) without stating the input side here. The builder preserves
|
|
114
|
-
* that contract even on the degraded path, where no field is volatile.
|
|
115
|
-
*/
|
|
116
|
-
export declare const Config: z<UpdateStatusConfig, Record<string, unknown>>;
|
|
117
|
-
//#endregion
|
|
118
102
|
//#region src/host/installation.d.ts
|
|
119
103
|
interface InstallationInfo {
|
|
120
104
|
currentVersion: string;
|
|
121
105
|
packageName: string;
|
|
122
|
-
channel: string;
|
|
123
106
|
installKind: InstallKind;
|
|
124
107
|
packageRoot?: string;
|
|
125
|
-
upgradeCommand: string;
|
|
126
108
|
}
|
|
127
109
|
//#endregion
|
|
128
110
|
//#region src/host/update-status.d.ts
|
|
111
|
+
/** The newest release the registry publishes, with everything the UI shows. */
|
|
112
|
+
interface RegistryLatest {
|
|
113
|
+
version: string;
|
|
114
|
+
publishedAt: string | null;
|
|
115
|
+
compatibility: ReleaseCompatibility;
|
|
116
|
+
}
|
|
129
117
|
interface RegistryRelease {
|
|
130
|
-
|
|
118
|
+
latest: RegistryLatest | null;
|
|
131
119
|
}
|
|
132
120
|
type RegistryFetcher = () => Promise<RegistryRelease>;
|
|
133
121
|
interface UpdateStatusServiceOptions {
|
|
@@ -152,9 +140,9 @@ export declare class UpdateStatusService {
|
|
|
152
140
|
private cache;
|
|
153
141
|
private inFlight;
|
|
154
142
|
constructor(options: UpdateStatusServiceOptions);
|
|
155
|
-
getStatus(
|
|
143
|
+
getStatus(cacheTtlMinutes?: number): Promise<UpdateStatus>;
|
|
156
144
|
/** `force` bypasses TTL but still joins any registry check already in flight. */
|
|
157
|
-
check(force?: boolean,
|
|
145
|
+
check(force?: boolean, cacheTtlMinutes?: number): Promise<UpdateStatus>;
|
|
158
146
|
private refreshRelease;
|
|
159
147
|
private statusAfterFailure;
|
|
160
148
|
private statusFromCache;
|
package/lib/index.js
CHANGED
|
@@ -17,12 +17,9 @@ const VERIFIED_DSH_VERSIONS = [
|
|
|
17
17
|
"0.1.7-alpha.2",
|
|
18
18
|
"0.1.7-rc.1",
|
|
19
19
|
"0.1.7-rc.2",
|
|
20
|
-
"0.2.0-rc.1"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"latest",
|
|
24
|
-
"next",
|
|
25
|
-
"alpha"
|
|
20
|
+
"0.2.0-rc.1",
|
|
21
|
+
"0.2.0-rc.2",
|
|
22
|
+
"0.2.1-alpha.1"
|
|
26
23
|
];
|
|
27
24
|
function isCacheTtlMinutes(value) {
|
|
28
25
|
return typeof value === "number" && Number.isInteger(value) && value >= 30 && value <= 1440;
|
|
@@ -31,9 +28,6 @@ const UPDATE_ENDPOINTS = {
|
|
|
31
28
|
getStatus: "dsh-update-status.get-status",
|
|
32
29
|
checkUpdate: "dsh-update-status.check-update"
|
|
33
30
|
};
|
|
34
|
-
function isReleaseChannel(value) {
|
|
35
|
-
return value === "latest" || value === "next" || value === "alpha";
|
|
36
|
-
}
|
|
37
31
|
//#endregion
|
|
38
32
|
//#region src/host/installation.ts
|
|
39
33
|
/**
|
|
@@ -124,9 +118,15 @@ function classifyInstallRoot(packageRoot, nodePrefix = process.execPath) {
|
|
|
124
118
|
if (/\/lib\/node_modules\/@deepseek-ai\/dsh$/.test(root)) return "npm-global";
|
|
125
119
|
return "unknown";
|
|
126
120
|
}
|
|
127
|
-
/**
|
|
128
|
-
|
|
129
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Generate guidance only; this package never invokes the string it returns.
|
|
123
|
+
*
|
|
124
|
+
* The target is the exact version the check found, not a dist-tag: with a single
|
|
125
|
+
* release line there is no followed tag left to name, and `@0.2.1-alpha.1` cannot
|
|
126
|
+
* be re-pointed between the check and the terminal.
|
|
127
|
+
*/
|
|
128
|
+
function upgradeCommandFor(installKind, packageName = PACKAGE_NAME, version = null) {
|
|
129
|
+
const specifier = version === null ? packageName : `${packageName}@${version}`;
|
|
130
130
|
switch (installKind) {
|
|
131
131
|
case "npm-global": return `npm install -g ${specifier}`;
|
|
132
132
|
case "pnpm-global": return `pnpm add -g ${specifier}`;
|
|
@@ -146,14 +146,11 @@ function detectInstallation(ctx, selfUrl = import.meta.url) {
|
|
|
146
146
|
const local = own === void 0 ? void 0 : manifestFromResolver(own);
|
|
147
147
|
const found = argvEntry ?? home ?? local;
|
|
148
148
|
const installKind = classifyInstallRoot(found?.root);
|
|
149
|
-
const channel = "latest";
|
|
150
149
|
return {
|
|
151
150
|
currentVersion: found?.version ?? "unknown",
|
|
152
151
|
packageName: PACKAGE_NAME,
|
|
153
|
-
channel,
|
|
154
152
|
installKind,
|
|
155
|
-
...found?.root === void 0 ? {} : { packageRoot: found.root }
|
|
156
|
-
upgradeCommand: upgradeCommandFor(installKind, PACKAGE_NAME, channel)
|
|
153
|
+
...found?.root === void 0 ? {} : { packageRoot: found.root }
|
|
157
154
|
};
|
|
158
155
|
}
|
|
159
156
|
//#endregion
|
|
@@ -173,11 +170,9 @@ function requestOf(value) {
|
|
|
173
170
|
if (typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
174
171
|
const record = value;
|
|
175
172
|
if (record.force !== void 0 && typeof record.force !== "boolean") return void 0;
|
|
176
|
-
if (record.channel !== void 0 && !isReleaseChannel(record.channel)) return void 0;
|
|
177
173
|
if (record.cacheTtlMinutes !== void 0 && !isCacheTtlMinutes(record.cacheTtlMinutes)) return void 0;
|
|
178
174
|
return {
|
|
179
175
|
...record.force === void 0 ? {} : { force: record.force },
|
|
180
|
-
...record.channel === void 0 ? {} : { channel: record.channel },
|
|
181
176
|
...record.cacheTtlMinutes === void 0 ? {} : { cacheTtlMinutes: record.cacheTtlMinutes }
|
|
182
177
|
};
|
|
183
178
|
}
|
|
@@ -213,18 +208,18 @@ function createUpdateStatusRpcHandler(service) {
|
|
|
213
208
|
try {
|
|
214
209
|
if (endpoint === UPDATE_ENDPOINTS.getStatus) {
|
|
215
210
|
const request = requestOf(payload);
|
|
216
|
-
if (request === void 0) return failure("dsh-update-status/bad-request", "`force` must be boolean
|
|
211
|
+
if (request === void 0) return failure("dsh-update-status/bad-request", "`force` must be boolean and `cacheTtlMinutes` must be an integer from 30 to 1440");
|
|
217
212
|
return {
|
|
218
213
|
ok: true,
|
|
219
|
-
value: await service.getStatus(request.
|
|
214
|
+
value: await service.getStatus(request.cacheTtlMinutes)
|
|
220
215
|
};
|
|
221
216
|
}
|
|
222
217
|
if (endpoint === UPDATE_ENDPOINTS.checkUpdate) {
|
|
223
218
|
const request = requestOf(payload);
|
|
224
|
-
if (request === void 0) return failure("dsh-update-status/bad-request", "`force` must be boolean
|
|
219
|
+
if (request === void 0) return failure("dsh-update-status/bad-request", "`force` must be boolean and `cacheTtlMinutes` must be an integer from 30 to 1440");
|
|
225
220
|
return {
|
|
226
221
|
ok: true,
|
|
227
|
-
value: await service.check(request.force === true, request.
|
|
222
|
+
value: await service.check(request.force === true, request.cacheTtlMinutes)
|
|
228
223
|
};
|
|
229
224
|
}
|
|
230
225
|
return failure("dsh-update-status/unknown-endpoint", `unknown endpoint: ${endpoint}`);
|
|
@@ -582,6 +577,21 @@ function compareSemver(leftValue, rightValue) {
|
|
|
582
577
|
}
|
|
583
578
|
return 0;
|
|
584
579
|
}
|
|
580
|
+
/**
|
|
581
|
+
* Highest version in a list, preserving the spelling of the winner.
|
|
582
|
+
*
|
|
583
|
+
* Unparsable entries are skipped rather than failing the whole list: a registry
|
|
584
|
+
* that adds one odd tag must not hide the release line behind it. Returns
|
|
585
|
+
* undefined when nothing in the list is valid SemVer.
|
|
586
|
+
*/
|
|
587
|
+
function newestSemver(versions) {
|
|
588
|
+
let winner;
|
|
589
|
+
for (const candidate of versions) {
|
|
590
|
+
if (parseSemver(candidate) === void 0) continue;
|
|
591
|
+
if (winner === void 0 || compareSemver(candidate, winner) > 0) winner = candidate;
|
|
592
|
+
}
|
|
593
|
+
return winner;
|
|
594
|
+
}
|
|
585
595
|
//#endregion
|
|
586
596
|
//#region src/host/update-status.ts
|
|
587
597
|
const REGISTRY_URL = "https://registry.npmjs.org/@deepseek-ai%2Fdsh";
|
|
@@ -595,9 +605,8 @@ function boundedMessage(error) {
|
|
|
595
605
|
function describeWarning(warning) {
|
|
596
606
|
switch (warning.code) {
|
|
597
607
|
case "registry-unavailable": return `Unable to check the npm registry: ${warning.detail}`;
|
|
598
|
-
case "
|
|
599
|
-
case "version-
|
|
600
|
-
case "preview-unverified": return `${warning.channel} is a preview channel; version ${warning.version} has not been verified as compatible with this plugin.`;
|
|
608
|
+
case "version-incomparable": return `Unable to compare the current version ${warning.currentVersion} with the published version ${warning.latestVersion} using SemVer.`;
|
|
609
|
+
case "version-unverified": return `Version ${warning.version} is newer than this plugin has been verified against.`;
|
|
601
610
|
case "stale-schemastery": {
|
|
602
611
|
const version = warning.version === null ? "" : ` ${warning.version}`;
|
|
603
612
|
const remedy = warning.nodeModulesDir === null ? `Remove the stale ${SCHEMASTERY_NAME} directory there and restart DSH.` : `Remove the stale copy and restart DSH: rm -rf ${warning.nodeModulesDir}`;
|
|
@@ -615,7 +624,7 @@ function warningFallback(warnings) {
|
|
|
615
624
|
* and only the settings form is degraded, so repainting the brand row would
|
|
616
625
|
* misreport a plugin-runtime fact as a failed update check.
|
|
617
626
|
*/
|
|
618
|
-
const ADVISORY_CODES = ["
|
|
627
|
+
const ADVISORY_CODES = ["version-unverified", "stale-schemastery"];
|
|
619
628
|
function warningKindOf(warnings) {
|
|
620
629
|
if (warnings.length === 0) return null;
|
|
621
630
|
return warnings.every((warning) => ADVISORY_CODES.includes(warning.code)) ? "notice" : "failure";
|
|
@@ -626,28 +635,28 @@ function dateOrNull(value) {
|
|
|
626
635
|
return Number.isFinite(time) ? new Date(time).toISOString() : null;
|
|
627
636
|
}
|
|
628
637
|
function compatibilityOf(version) {
|
|
629
|
-
return
|
|
638
|
+
return VERIFIED_DSH_VERSIONS.includes(version) ? "verified" : "unverified";
|
|
630
639
|
}
|
|
640
|
+
/**
|
|
641
|
+
* Reduce one registry document to the single newest release.
|
|
642
|
+
*
|
|
643
|
+
* Every dist-tag contributes, not a fixed `latest`/`next`/`alpha` triple: npm
|
|
644
|
+
* tags are the registry's own vocabulary, and a version published under a tag
|
|
645
|
+
* this plugin has never heard of is still a version users can install.
|
|
646
|
+
*/
|
|
631
647
|
function registryReleaseOf(value) {
|
|
632
648
|
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("npm registry returned an invalid document");
|
|
633
649
|
const record = value;
|
|
634
650
|
const tags = record["dist-tags"];
|
|
635
651
|
if (tags === null || typeof tags !== "object" || Array.isArray(tags)) throw new Error("npm registry response has no dist-tags");
|
|
636
|
-
const
|
|
652
|
+
const version = newestSemver(Object.values(tags).filter((version) => typeof version === "string" && version.trim() !== "").map((version) => version.trim()));
|
|
653
|
+
if (version === void 0) throw new Error("npm registry response has no comparable dist-tags");
|
|
637
654
|
const time = record.time;
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
channel,
|
|
644
|
-
version,
|
|
645
|
-
publishedAt: version === null ? null : dateOrNull(timeRecord[version]),
|
|
646
|
-
compatibility: compatibilityOf(version)
|
|
647
|
-
};
|
|
648
|
-
});
|
|
649
|
-
if (channels.every((release) => release.version === null)) throw new Error("npm registry response has no supported dist-tags");
|
|
650
|
-
return { channels };
|
|
655
|
+
return { latest: {
|
|
656
|
+
version,
|
|
657
|
+
publishedAt: dateOrNull((time !== null && typeof time === "object" && !Array.isArray(time) ? time : {})[version]),
|
|
658
|
+
compatibility: compatibilityOf(version)
|
|
659
|
+
} };
|
|
651
660
|
}
|
|
652
661
|
/** Only the explicitly approved HTTPS npm Registry authority may be contacted. */
|
|
653
662
|
function assertApprovedRegistryUrl(raw) {
|
|
@@ -698,25 +707,25 @@ var UpdateStatusService = class {
|
|
|
698
707
|
this.releaseUrl = options.releaseUrl ?? "https://github.com/deepseek-ai/deepseek-harness/releases";
|
|
699
708
|
this.runtimeWarnings = options.runtimeWarnings ?? [];
|
|
700
709
|
}
|
|
701
|
-
getStatus(
|
|
702
|
-
return this.check(false,
|
|
710
|
+
getStatus(cacheTtlMinutes) {
|
|
711
|
+
return this.check(false, cacheTtlMinutes);
|
|
703
712
|
}
|
|
704
713
|
/** `force` bypasses TTL but still joins any registry check already in flight. */
|
|
705
|
-
async check(force = false,
|
|
714
|
+
async check(force = false, cacheTtlMinutes) {
|
|
706
715
|
const ttlMs = isCacheTtlMinutes(cacheTtlMinutes) ? cacheTtlMinutes * 60 * 1e3 : this.ttlMs;
|
|
707
716
|
const cached = this.cache;
|
|
708
|
-
if (!force && cached !== void 0 && this.now() - cached.checkedAtMs < ttlMs) return this.statusFromCache(cached,
|
|
717
|
+
if (!force && cached !== void 0 && this.now() - cached.checkedAtMs < ttlMs) return this.statusFromCache(cached, true, []);
|
|
709
718
|
if (this.inFlight !== void 0) try {
|
|
710
|
-
return this.statusFromCache(await this.inFlight,
|
|
719
|
+
return this.statusFromCache(await this.inFlight, false, []);
|
|
711
720
|
} catch (error) {
|
|
712
|
-
return this.statusAfterFailure(
|
|
721
|
+
return this.statusAfterFailure(error);
|
|
713
722
|
}
|
|
714
723
|
const run = this.refreshRelease();
|
|
715
724
|
this.inFlight = run;
|
|
716
725
|
try {
|
|
717
|
-
return this.statusFromCache(await run,
|
|
726
|
+
return this.statusFromCache(await run, false, []);
|
|
718
727
|
} catch (error) {
|
|
719
|
-
return this.statusAfterFailure(
|
|
728
|
+
return this.statusAfterFailure(error);
|
|
720
729
|
} finally {
|
|
721
730
|
if (this.inFlight === run) this.inFlight = void 0;
|
|
722
731
|
}
|
|
@@ -729,82 +738,65 @@ var UpdateStatusService = class {
|
|
|
729
738
|
this.cache = cache;
|
|
730
739
|
return cache;
|
|
731
740
|
}
|
|
732
|
-
statusAfterFailure(
|
|
741
|
+
statusAfterFailure(error) {
|
|
733
742
|
const warnings = [{
|
|
734
743
|
code: "registry-unavailable",
|
|
735
744
|
detail: boundedMessage(error)
|
|
736
745
|
}];
|
|
737
|
-
return this.cache === void 0 ? this.statusWithoutRemoteRelease(
|
|
746
|
+
return this.cache === void 0 ? this.statusWithoutRemoteRelease(warnings) : this.statusFromCache(this.cache, true, warnings);
|
|
738
747
|
}
|
|
739
|
-
statusFromCache(cache,
|
|
740
|
-
const selected = cache.release.
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
publishedAt: null,
|
|
744
|
-
compatibility: "unverified"
|
|
745
|
-
};
|
|
746
|
-
const comparison = selected.version === null ? void 0 : compareSemver(this.installation.currentVersion, selected.version);
|
|
748
|
+
statusFromCache(cache, cached, initialWarnings) {
|
|
749
|
+
const selected = cache.release.latest;
|
|
750
|
+
const comparison = selected === null ? void 0 : compareSemver(this.installation.currentVersion, selected.version);
|
|
751
|
+
const hasUpdate = comparison !== void 0 && comparison < 0;
|
|
747
752
|
const warnings = [...initialWarnings];
|
|
748
|
-
if (selected
|
|
749
|
-
code: "channel-unavailable",
|
|
750
|
-
channel
|
|
751
|
-
});
|
|
752
|
-
if (selected.version !== null && comparison === void 0) warnings.push({
|
|
753
|
+
if (selected !== null && comparison === void 0) warnings.push({
|
|
753
754
|
code: "version-incomparable",
|
|
754
755
|
currentVersion: this.installation.currentVersion,
|
|
755
|
-
|
|
756
|
-
selectedVersion: selected.version
|
|
756
|
+
latestVersion: selected.version
|
|
757
757
|
});
|
|
758
|
-
if (
|
|
759
|
-
code: "
|
|
760
|
-
channel,
|
|
758
|
+
if (hasUpdate && selected !== null && selected.compatibility !== "verified") warnings.push({
|
|
759
|
+
code: "version-unverified",
|
|
761
760
|
version: selected.version
|
|
762
761
|
});
|
|
763
762
|
warnings.push(...this.runtimeWarnings);
|
|
764
763
|
return {
|
|
765
764
|
currentVersion: this.installation.currentVersion,
|
|
766
|
-
latestVersion: selected
|
|
767
|
-
hasUpdate
|
|
765
|
+
latestVersion: selected?.version ?? null,
|
|
766
|
+
hasUpdate,
|
|
767
|
+
compatibility: selected?.compatibility ?? "unverified",
|
|
768
768
|
cached,
|
|
769
769
|
checkedAt: new Date(cache.checkedAtMs).toISOString(),
|
|
770
770
|
warning: warningFallback(warnings),
|
|
771
771
|
warningKind: warningKindOf(warnings),
|
|
772
772
|
warnings,
|
|
773
773
|
installKind: this.installation.installKind,
|
|
774
|
-
upgradeCommand: upgradeCommandFor(this.installation.installKind, this.installation.packageName || "@deepseek-ai/dsh",
|
|
774
|
+
upgradeCommand: upgradeCommandFor(this.installation.installKind, this.installation.packageName || "@deepseek-ai/dsh", selected?.version ?? null),
|
|
775
775
|
releaseUrl: this.releaseUrl,
|
|
776
776
|
changelogUrl: this.releaseUrl,
|
|
777
|
-
publishedAt: selected
|
|
777
|
+
publishedAt: selected?.publishedAt ?? null,
|
|
778
778
|
packageName: this.installation.packageName || "@deepseek-ai/dsh",
|
|
779
|
-
channel,
|
|
780
|
-
channels: cache.release.channels,
|
|
781
779
|
canApplyInPlace: false
|
|
782
780
|
};
|
|
783
781
|
}
|
|
784
|
-
statusWithoutRemoteRelease(
|
|
782
|
+
statusWithoutRemoteRelease(warnings) {
|
|
785
783
|
const combined = [...warnings, ...this.runtimeWarnings];
|
|
786
784
|
return {
|
|
787
785
|
currentVersion: this.installation.currentVersion,
|
|
788
786
|
latestVersion: null,
|
|
789
787
|
hasUpdate: false,
|
|
788
|
+
compatibility: "unverified",
|
|
790
789
|
cached: false,
|
|
791
790
|
checkedAt: null,
|
|
792
791
|
warning: warningFallback(combined),
|
|
793
792
|
warningKind: warningKindOf(combined),
|
|
794
793
|
warnings: combined,
|
|
795
794
|
installKind: this.installation.installKind,
|
|
796
|
-
upgradeCommand: upgradeCommandFor(this.installation.installKind, this.installation.packageName || "@deepseek-ai/dsh",
|
|
795
|
+
upgradeCommand: upgradeCommandFor(this.installation.installKind, this.installation.packageName || "@deepseek-ai/dsh", null),
|
|
797
796
|
releaseUrl: this.releaseUrl,
|
|
798
797
|
changelogUrl: this.releaseUrl,
|
|
799
798
|
publishedAt: null,
|
|
800
799
|
packageName: this.installation.packageName || "@deepseek-ai/dsh",
|
|
801
|
-
channel,
|
|
802
|
-
channels: RELEASE_CHANNELS.map((item) => ({
|
|
803
|
-
channel: item,
|
|
804
|
-
version: null,
|
|
805
|
-
publishedAt: null,
|
|
806
|
-
compatibility: "unverified"
|
|
807
|
-
})),
|
|
808
800
|
canApplyInPlace: false
|
|
809
801
|
};
|
|
810
802
|
}
|
|
@@ -833,11 +825,6 @@ function buildConfigSchema(factory, volatileAvailable) {
|
|
|
833
825
|
timeoutMs: factory.number().step(1).min(1e3).max(3e4).default(DEFAULT_TIMEOUT_MS),
|
|
834
826
|
autoCheckOnMount: factory.boolean().default(true),
|
|
835
827
|
sidebarEnabled: volatile(factory.boolean().default(true)),
|
|
836
|
-
channel: volatile(factory.union([
|
|
837
|
-
"latest",
|
|
838
|
-
"next",
|
|
839
|
-
"alpha"
|
|
840
|
-
]).default("latest").loose()),
|
|
841
828
|
cacheTtlMinutes: volatile(factory.number().min(30).max(1440).default(360))
|
|
842
829
|
});
|
|
843
830
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-update-status",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Read-only DeepSeek Harness Web plugin that shows the running DSH version, discovers latest/next/alpha releases, warns about compatibility, and generates copy-only upgrade commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -65,7 +65,9 @@
|
|
|
65
65
|
"0.1.7-alpha.2": "compatible",
|
|
66
66
|
"0.1.7-rc.1": "compatible",
|
|
67
67
|
"0.1.7-rc.2": "compatible",
|
|
68
|
-
"0.2.0-rc.1": "compatible"
|
|
68
|
+
"0.2.0-rc.1": "compatible",
|
|
69
|
+
"0.2.0-rc.2": "compatible",
|
|
70
|
+
"0.2.1-alpha.1": "compatible"
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
},
|