dsh-update-status 0.1.12 → 0.2.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/CHANGELOG.md +20 -0
- package/README.md +41 -52
- package/README.zh.md +40 -51
- package/assets/update-panel.png +0 -0
- package/lib/client.js +225 -449
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +47 -59
- package/lib/index.js +75 -89
- package/package.json +3 -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
|
@@ -18,12 +18,8 @@ const VERIFIED_DSH_VERSIONS = [
|
|
|
18
18
|
"0.1.7-rc.1",
|
|
19
19
|
"0.1.7-rc.2",
|
|
20
20
|
"0.2.0-rc.1",
|
|
21
|
-
"0.2.0-rc.2"
|
|
22
|
-
|
|
23
|
-
const RELEASE_CHANNELS = [
|
|
24
|
-
"latest",
|
|
25
|
-
"next",
|
|
26
|
-
"alpha"
|
|
21
|
+
"0.2.0-rc.2",
|
|
22
|
+
"0.2.1-alpha.1"
|
|
27
23
|
];
|
|
28
24
|
function isCacheTtlMinutes(value) {
|
|
29
25
|
return typeof value === "number" && Number.isInteger(value) && value >= 30 && value <= 1440;
|
|
@@ -32,9 +28,6 @@ const UPDATE_ENDPOINTS = {
|
|
|
32
28
|
getStatus: "dsh-update-status.get-status",
|
|
33
29
|
checkUpdate: "dsh-update-status.check-update"
|
|
34
30
|
};
|
|
35
|
-
function isReleaseChannel(value) {
|
|
36
|
-
return value === "latest" || value === "next" || value === "alpha";
|
|
37
|
-
}
|
|
38
31
|
//#endregion
|
|
39
32
|
//#region src/host/installation.ts
|
|
40
33
|
/**
|
|
@@ -125,9 +118,15 @@ function classifyInstallRoot(packageRoot, nodePrefix = process.execPath) {
|
|
|
125
118
|
if (/\/lib\/node_modules\/@deepseek-ai\/dsh$/.test(root)) return "npm-global";
|
|
126
119
|
return "unknown";
|
|
127
120
|
}
|
|
128
|
-
/**
|
|
129
|
-
|
|
130
|
-
|
|
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}`;
|
|
131
130
|
switch (installKind) {
|
|
132
131
|
case "npm-global": return `npm install -g ${specifier}`;
|
|
133
132
|
case "pnpm-global": return `pnpm add -g ${specifier}`;
|
|
@@ -147,14 +146,11 @@ function detectInstallation(ctx, selfUrl = import.meta.url) {
|
|
|
147
146
|
const local = own === void 0 ? void 0 : manifestFromResolver(own);
|
|
148
147
|
const found = argvEntry ?? home ?? local;
|
|
149
148
|
const installKind = classifyInstallRoot(found?.root);
|
|
150
|
-
const channel = "latest";
|
|
151
149
|
return {
|
|
152
150
|
currentVersion: found?.version ?? "unknown",
|
|
153
151
|
packageName: PACKAGE_NAME,
|
|
154
|
-
channel,
|
|
155
152
|
installKind,
|
|
156
|
-
...found?.root === void 0 ? {} : { packageRoot: found.root }
|
|
157
|
-
upgradeCommand: upgradeCommandFor(installKind, PACKAGE_NAME, channel)
|
|
153
|
+
...found?.root === void 0 ? {} : { packageRoot: found.root }
|
|
158
154
|
};
|
|
159
155
|
}
|
|
160
156
|
//#endregion
|
|
@@ -174,11 +170,9 @@ function requestOf(value) {
|
|
|
174
170
|
if (typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
175
171
|
const record = value;
|
|
176
172
|
if (record.force !== void 0 && typeof record.force !== "boolean") return void 0;
|
|
177
|
-
if (record.channel !== void 0 && !isReleaseChannel(record.channel)) return void 0;
|
|
178
173
|
if (record.cacheTtlMinutes !== void 0 && !isCacheTtlMinutes(record.cacheTtlMinutes)) return void 0;
|
|
179
174
|
return {
|
|
180
175
|
...record.force === void 0 ? {} : { force: record.force },
|
|
181
|
-
...record.channel === void 0 ? {} : { channel: record.channel },
|
|
182
176
|
...record.cacheTtlMinutes === void 0 ? {} : { cacheTtlMinutes: record.cacheTtlMinutes }
|
|
183
177
|
};
|
|
184
178
|
}
|
|
@@ -214,18 +208,18 @@ function createUpdateStatusRpcHandler(service) {
|
|
|
214
208
|
try {
|
|
215
209
|
if (endpoint === UPDATE_ENDPOINTS.getStatus) {
|
|
216
210
|
const request = requestOf(payload);
|
|
217
|
-
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");
|
|
218
212
|
return {
|
|
219
213
|
ok: true,
|
|
220
|
-
value: await service.getStatus(request.
|
|
214
|
+
value: await service.getStatus(request.cacheTtlMinutes)
|
|
221
215
|
};
|
|
222
216
|
}
|
|
223
217
|
if (endpoint === UPDATE_ENDPOINTS.checkUpdate) {
|
|
224
218
|
const request = requestOf(payload);
|
|
225
|
-
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");
|
|
226
220
|
return {
|
|
227
221
|
ok: true,
|
|
228
|
-
value: await service.check(request.force === true, request.
|
|
222
|
+
value: await service.check(request.force === true, request.cacheTtlMinutes)
|
|
229
223
|
};
|
|
230
224
|
}
|
|
231
225
|
return failure("dsh-update-status/unknown-endpoint", `unknown endpoint: ${endpoint}`);
|
|
@@ -583,6 +577,21 @@ function compareSemver(leftValue, rightValue) {
|
|
|
583
577
|
}
|
|
584
578
|
return 0;
|
|
585
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
|
+
}
|
|
586
595
|
//#endregion
|
|
587
596
|
//#region src/host/update-status.ts
|
|
588
597
|
const REGISTRY_URL = "https://registry.npmjs.org/@deepseek-ai%2Fdsh";
|
|
@@ -596,9 +605,8 @@ function boundedMessage(error) {
|
|
|
596
605
|
function describeWarning(warning) {
|
|
597
606
|
switch (warning.code) {
|
|
598
607
|
case "registry-unavailable": return `Unable to check the npm registry: ${warning.detail}`;
|
|
599
|
-
case "
|
|
600
|
-
case "version-
|
|
601
|
-
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.`;
|
|
602
610
|
case "stale-schemastery": {
|
|
603
611
|
const version = warning.version === null ? "" : ` ${warning.version}`;
|
|
604
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}`;
|
|
@@ -616,7 +624,7 @@ function warningFallback(warnings) {
|
|
|
616
624
|
* and only the settings form is degraded, so repainting the brand row would
|
|
617
625
|
* misreport a plugin-runtime fact as a failed update check.
|
|
618
626
|
*/
|
|
619
|
-
const ADVISORY_CODES = ["
|
|
627
|
+
const ADVISORY_CODES = ["version-unverified", "stale-schemastery"];
|
|
620
628
|
function warningKindOf(warnings) {
|
|
621
629
|
if (warnings.length === 0) return null;
|
|
622
630
|
return warnings.every((warning) => ADVISORY_CODES.includes(warning.code)) ? "notice" : "failure";
|
|
@@ -627,28 +635,28 @@ function dateOrNull(value) {
|
|
|
627
635
|
return Number.isFinite(time) ? new Date(time).toISOString() : null;
|
|
628
636
|
}
|
|
629
637
|
function compatibilityOf(version) {
|
|
630
|
-
return
|
|
638
|
+
return VERIFIED_DSH_VERSIONS.includes(version) ? "verified" : "unverified";
|
|
631
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
|
+
*/
|
|
632
647
|
function registryReleaseOf(value) {
|
|
633
648
|
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("npm registry returned an invalid document");
|
|
634
649
|
const record = value;
|
|
635
650
|
const tags = record["dist-tags"];
|
|
636
651
|
if (tags === null || typeof tags !== "object" || Array.isArray(tags)) throw new Error("npm registry response has no dist-tags");
|
|
637
|
-
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");
|
|
638
654
|
const time = record.time;
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
channel,
|
|
645
|
-
version,
|
|
646
|
-
publishedAt: version === null ? null : dateOrNull(timeRecord[version]),
|
|
647
|
-
compatibility: compatibilityOf(version)
|
|
648
|
-
};
|
|
649
|
-
});
|
|
650
|
-
if (channels.every((release) => release.version === null)) throw new Error("npm registry response has no supported dist-tags");
|
|
651
|
-
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
|
+
} };
|
|
652
660
|
}
|
|
653
661
|
/** Only the explicitly approved HTTPS npm Registry authority may be contacted. */
|
|
654
662
|
function assertApprovedRegistryUrl(raw) {
|
|
@@ -699,25 +707,25 @@ var UpdateStatusService = class {
|
|
|
699
707
|
this.releaseUrl = options.releaseUrl ?? "https://github.com/deepseek-ai/deepseek-harness/releases";
|
|
700
708
|
this.runtimeWarnings = options.runtimeWarnings ?? [];
|
|
701
709
|
}
|
|
702
|
-
getStatus(
|
|
703
|
-
return this.check(false,
|
|
710
|
+
getStatus(cacheTtlMinutes) {
|
|
711
|
+
return this.check(false, cacheTtlMinutes);
|
|
704
712
|
}
|
|
705
713
|
/** `force` bypasses TTL but still joins any registry check already in flight. */
|
|
706
|
-
async check(force = false,
|
|
714
|
+
async check(force = false, cacheTtlMinutes) {
|
|
707
715
|
const ttlMs = isCacheTtlMinutes(cacheTtlMinutes) ? cacheTtlMinutes * 60 * 1e3 : this.ttlMs;
|
|
708
716
|
const cached = this.cache;
|
|
709
|
-
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, []);
|
|
710
718
|
if (this.inFlight !== void 0) try {
|
|
711
|
-
return this.statusFromCache(await this.inFlight,
|
|
719
|
+
return this.statusFromCache(await this.inFlight, false, []);
|
|
712
720
|
} catch (error) {
|
|
713
|
-
return this.statusAfterFailure(
|
|
721
|
+
return this.statusAfterFailure(error);
|
|
714
722
|
}
|
|
715
723
|
const run = this.refreshRelease();
|
|
716
724
|
this.inFlight = run;
|
|
717
725
|
try {
|
|
718
|
-
return this.statusFromCache(await run,
|
|
726
|
+
return this.statusFromCache(await run, false, []);
|
|
719
727
|
} catch (error) {
|
|
720
|
-
return this.statusAfterFailure(
|
|
728
|
+
return this.statusAfterFailure(error);
|
|
721
729
|
} finally {
|
|
722
730
|
if (this.inFlight === run) this.inFlight = void 0;
|
|
723
731
|
}
|
|
@@ -730,82 +738,65 @@ var UpdateStatusService = class {
|
|
|
730
738
|
this.cache = cache;
|
|
731
739
|
return cache;
|
|
732
740
|
}
|
|
733
|
-
statusAfterFailure(
|
|
741
|
+
statusAfterFailure(error) {
|
|
734
742
|
const warnings = [{
|
|
735
743
|
code: "registry-unavailable",
|
|
736
744
|
detail: boundedMessage(error)
|
|
737
745
|
}];
|
|
738
|
-
return this.cache === void 0 ? this.statusWithoutRemoteRelease(
|
|
746
|
+
return this.cache === void 0 ? this.statusWithoutRemoteRelease(warnings) : this.statusFromCache(this.cache, true, warnings);
|
|
739
747
|
}
|
|
740
|
-
statusFromCache(cache,
|
|
741
|
-
const selected = cache.release.
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
publishedAt: null,
|
|
745
|
-
compatibility: "unverified"
|
|
746
|
-
};
|
|
747
|
-
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;
|
|
748
752
|
const warnings = [...initialWarnings];
|
|
749
|
-
if (selected
|
|
750
|
-
code: "channel-unavailable",
|
|
751
|
-
channel
|
|
752
|
-
});
|
|
753
|
-
if (selected.version !== null && comparison === void 0) warnings.push({
|
|
753
|
+
if (selected !== null && comparison === void 0) warnings.push({
|
|
754
754
|
code: "version-incomparable",
|
|
755
755
|
currentVersion: this.installation.currentVersion,
|
|
756
|
-
|
|
757
|
-
selectedVersion: selected.version
|
|
756
|
+
latestVersion: selected.version
|
|
758
757
|
});
|
|
759
|
-
if (
|
|
760
|
-
code: "
|
|
761
|
-
channel,
|
|
758
|
+
if (hasUpdate && selected !== null && selected.compatibility !== "verified") warnings.push({
|
|
759
|
+
code: "version-unverified",
|
|
762
760
|
version: selected.version
|
|
763
761
|
});
|
|
764
762
|
warnings.push(...this.runtimeWarnings);
|
|
765
763
|
return {
|
|
766
764
|
currentVersion: this.installation.currentVersion,
|
|
767
|
-
latestVersion: selected
|
|
768
|
-
hasUpdate
|
|
765
|
+
latestVersion: selected?.version ?? null,
|
|
766
|
+
hasUpdate,
|
|
767
|
+
compatibility: selected?.compatibility ?? "unverified",
|
|
769
768
|
cached,
|
|
770
769
|
checkedAt: new Date(cache.checkedAtMs).toISOString(),
|
|
771
770
|
warning: warningFallback(warnings),
|
|
772
771
|
warningKind: warningKindOf(warnings),
|
|
773
772
|
warnings,
|
|
774
773
|
installKind: this.installation.installKind,
|
|
775
|
-
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),
|
|
776
775
|
releaseUrl: this.releaseUrl,
|
|
777
776
|
changelogUrl: this.releaseUrl,
|
|
778
|
-
publishedAt: selected
|
|
777
|
+
publishedAt: selected?.publishedAt ?? null,
|
|
779
778
|
packageName: this.installation.packageName || "@deepseek-ai/dsh",
|
|
780
|
-
channel,
|
|
781
|
-
channels: cache.release.channels,
|
|
782
779
|
canApplyInPlace: false
|
|
783
780
|
};
|
|
784
781
|
}
|
|
785
|
-
statusWithoutRemoteRelease(
|
|
782
|
+
statusWithoutRemoteRelease(warnings) {
|
|
786
783
|
const combined = [...warnings, ...this.runtimeWarnings];
|
|
787
784
|
return {
|
|
788
785
|
currentVersion: this.installation.currentVersion,
|
|
789
786
|
latestVersion: null,
|
|
790
787
|
hasUpdate: false,
|
|
788
|
+
compatibility: "unverified",
|
|
791
789
|
cached: false,
|
|
792
790
|
checkedAt: null,
|
|
793
791
|
warning: warningFallback(combined),
|
|
794
792
|
warningKind: warningKindOf(combined),
|
|
795
793
|
warnings: combined,
|
|
796
794
|
installKind: this.installation.installKind,
|
|
797
|
-
upgradeCommand: upgradeCommandFor(this.installation.installKind, this.installation.packageName || "@deepseek-ai/dsh",
|
|
795
|
+
upgradeCommand: upgradeCommandFor(this.installation.installKind, this.installation.packageName || "@deepseek-ai/dsh", null),
|
|
798
796
|
releaseUrl: this.releaseUrl,
|
|
799
797
|
changelogUrl: this.releaseUrl,
|
|
800
798
|
publishedAt: null,
|
|
801
799
|
packageName: this.installation.packageName || "@deepseek-ai/dsh",
|
|
802
|
-
channel,
|
|
803
|
-
channels: RELEASE_CHANNELS.map((item) => ({
|
|
804
|
-
channel: item,
|
|
805
|
-
version: null,
|
|
806
|
-
publishedAt: null,
|
|
807
|
-
compatibility: "unverified"
|
|
808
|
-
})),
|
|
809
800
|
canApplyInPlace: false
|
|
810
801
|
};
|
|
811
802
|
}
|
|
@@ -834,11 +825,6 @@ function buildConfigSchema(factory, volatileAvailable) {
|
|
|
834
825
|
timeoutMs: factory.number().step(1).min(1e3).max(3e4).default(DEFAULT_TIMEOUT_MS),
|
|
835
826
|
autoCheckOnMount: factory.boolean().default(true),
|
|
836
827
|
sidebarEnabled: volatile(factory.boolean().default(true)),
|
|
837
|
-
channel: volatile(factory.union([
|
|
838
|
-
"latest",
|
|
839
|
-
"next",
|
|
840
|
-
"alpha"
|
|
841
|
-
]).default("latest").loose()),
|
|
842
828
|
cacheTtlMinutes: volatile(factory.number().min(30).max(1440).default(360))
|
|
843
829
|
});
|
|
844
830
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-update-status",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
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",
|
|
@@ -66,7 +66,8 @@
|
|
|
66
66
|
"0.1.7-rc.1": "compatible",
|
|
67
67
|
"0.1.7-rc.2": "compatible",
|
|
68
68
|
"0.2.0-rc.1": "compatible",
|
|
69
|
-
"0.2.0-rc.2": "compatible"
|
|
69
|
+
"0.2.0-rc.2": "compatible",
|
|
70
|
+
"0.2.1-alpha.1": "compatible"
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
},
|