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/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 channel the registry
12
- * does not publish, or a version SemVer cannot compare. This is what justifies
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
- * preview channel whose release this bundle has not been verified against.
16
- * The chip must NOT repaint for this — an operator upgrading DSH ahead of the
17
- * plugin would otherwise see the chip turn red for a plugin-side bookkeeping
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
- channel: ReleaseChannel;
36
- selectedVersion: string;
37
- } | {
38
- code: 'preview-unverified';
39
- channel: ReleaseChannel;
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
- channels: ChannelRelease[];
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(channel?: ReleaseChannel, cacheTtlMinutes?: number): Promise<UpdateStatus>;
143
+ getStatus(cacheTtlMinutes?: number): Promise<UpdateStatus>;
156
144
  /** `force` bypasses TTL but still joins any registry check already in flight. */
157
- check(force?: boolean, channel?: ReleaseChannel, cacheTtlMinutes?: number): Promise<UpdateStatus>;
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
- const RELEASE_CHANNELS = [
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
- /** Generate guidance only; this package never invokes the string it returns. */
128
- function upgradeCommandFor(installKind, packageName = PACKAGE_NAME, channel = "latest") {
129
- const specifier = `${packageName}@${channel}`;
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, `channel` must be latest, next, or alpha, and `cacheTtlMinutes` must be an integer from 30 to 1440");
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.channel, request.cacheTtlMinutes)
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, `channel` must be latest, next, or alpha, and `cacheTtlMinutes` must be an integer from 30 to 1440");
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.channel, request.cacheTtlMinutes)
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 "channel-unavailable": return `The npm registry does not publish a ${warning.channel} channel.`;
599
- case "version-incomparable": return `Unable to compare the current version ${warning.currentVersion} with ${warning.channel} channel version ${warning.selectedVersion} using SemVer.`;
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 = ["preview-unverified", "stale-schemastery"];
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 version !== null && VERIFIED_DSH_VERSIONS.includes(version) ? "verified" : "unverified";
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 tagRecord = tags;
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
- const timeRecord = time !== null && typeof time === "object" && !Array.isArray(time) ? time : {};
639
- const channels = RELEASE_CHANNELS.map((channel) => {
640
- const raw = tagRecord[channel];
641
- const version = typeof raw === "string" && raw.trim() !== "" ? raw.trim() : null;
642
- return {
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(channel = "latest", cacheTtlMinutes) {
702
- return this.check(false, channel, cacheTtlMinutes);
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, channel = "latest", cacheTtlMinutes) {
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, channel, true, []);
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, channel, false, []);
719
+ return this.statusFromCache(await this.inFlight, false, []);
711
720
  } catch (error) {
712
- return this.statusAfterFailure(channel, error);
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, channel, false, []);
726
+ return this.statusFromCache(await run, false, []);
718
727
  } catch (error) {
719
- return this.statusAfterFailure(channel, error);
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(channel, error) {
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(channel, warnings) : this.statusFromCache(this.cache, channel, true, warnings);
746
+ return this.cache === void 0 ? this.statusWithoutRemoteRelease(warnings) : this.statusFromCache(this.cache, true, warnings);
738
747
  }
739
- statusFromCache(cache, channel, cached, initialWarnings) {
740
- const selected = cache.release.channels.find((release) => release.channel === channel) ?? {
741
- channel,
742
- version: null,
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.version === null) warnings.push({
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
- channel,
756
- selectedVersion: selected.version
756
+ latestVersion: selected.version
757
757
  });
758
- if (channel !== "latest" && selected.version !== null && selected.compatibility !== "verified") warnings.push({
759
- code: "preview-unverified",
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.version,
767
- hasUpdate: comparison !== void 0 && comparison < 0,
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", channel),
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.publishedAt,
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(channel, warnings) {
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", channel),
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.1.11",
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
  },