dsh-update-status 0.1.12 → 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
@@ -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
- /** Generate guidance only; this package never invokes the string it returns. */
129
- function upgradeCommandFor(installKind, packageName = PACKAGE_NAME, channel = "latest") {
130
- 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}`;
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, `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");
218
212
  return {
219
213
  ok: true,
220
- value: await service.getStatus(request.channel, request.cacheTtlMinutes)
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, `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");
226
220
  return {
227
221
  ok: true,
228
- value: await service.check(request.force === true, request.channel, request.cacheTtlMinutes)
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 "channel-unavailable": return `The npm registry does not publish a ${warning.channel} channel.`;
600
- case "version-incomparable": return `Unable to compare the current version ${warning.currentVersion} with ${warning.channel} channel version ${warning.selectedVersion} using SemVer.`;
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 = ["preview-unverified", "stale-schemastery"];
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 version !== null && VERIFIED_DSH_VERSIONS.includes(version) ? "verified" : "unverified";
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 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");
638
654
  const time = record.time;
639
- const timeRecord = time !== null && typeof time === "object" && !Array.isArray(time) ? time : {};
640
- const channels = RELEASE_CHANNELS.map((channel) => {
641
- const raw = tagRecord[channel];
642
- const version = typeof raw === "string" && raw.trim() !== "" ? raw.trim() : null;
643
- return {
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(channel = "latest", cacheTtlMinutes) {
703
- return this.check(false, channel, cacheTtlMinutes);
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, channel = "latest", cacheTtlMinutes) {
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, channel, true, []);
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, channel, false, []);
719
+ return this.statusFromCache(await this.inFlight, false, []);
712
720
  } catch (error) {
713
- return this.statusAfterFailure(channel, error);
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, channel, false, []);
726
+ return this.statusFromCache(await run, false, []);
719
727
  } catch (error) {
720
- return this.statusAfterFailure(channel, error);
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(channel, error) {
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(channel, warnings) : this.statusFromCache(this.cache, channel, true, warnings);
746
+ return this.cache === void 0 ? this.statusWithoutRemoteRelease(warnings) : this.statusFromCache(this.cache, true, warnings);
739
747
  }
740
- statusFromCache(cache, channel, cached, initialWarnings) {
741
- const selected = cache.release.channels.find((release) => release.channel === channel) ?? {
742
- channel,
743
- version: null,
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.version === null) warnings.push({
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
- channel,
757
- selectedVersion: selected.version
756
+ latestVersion: selected.version
758
757
  });
759
- if (channel !== "latest" && selected.version !== null && selected.compatibility !== "verified") warnings.push({
760
- code: "preview-unverified",
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.version,
768
- hasUpdate: comparison !== void 0 && comparison < 0,
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", channel),
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.publishedAt,
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(channel, warnings) {
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", channel),
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.12",
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",
@@ -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
  },