@xmoon76/dsh-pi-tui 0.3.0 → 0.3.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.
@@ -5,6 +5,46 @@ import { Context } from "@deepseek-ai/cordis";
5
5
  declare const name = "tui-startup";
6
6
  /** Services required before the flags can be resolved. */
7
7
  declare const inject: string[];
8
+ /** One incompatible dsh harness range and the guidance it deserves. Add a
9
+ * new entry here whenever a future bundle release stops supporting an
10
+ * older (or newer) harness line; entries are checked in ORDER and the
11
+ * first whose range covers the installed dsh version wins.
12
+ *
13
+ * Range semantics: `min` is inclusive, `max` is EXCLUSIVE. An entry with
14
+ * only `max` covers everything below it (the common "too old" case); an
15
+ * entry with only `min` covers everything at or above it ("too new" —
16
+ * e.g. a harness that removed a seam we still use).
17
+ *
18
+ * `since` names the bundle release line that FIRST imposed the constraint:
19
+ * it is shown as the fallback version label (`>= <since>`) when the
20
+ * bundle's own version cannot be read, so the message stays truthful.
21
+ */
22
+ interface HarnessCompatEntry {
23
+ /** Inclusive lower bound of the incompatible range; absent = unbounded below. */
24
+ min?: string;
25
+ /** Exclusive upper bound of the incompatible range; absent = unbounded above. */
26
+ max?: string;
27
+ /** The bundle release line that first required this constraint. */
28
+ since: string;
29
+ /** Human-readable requirement, e.g. `DeepSeek Harness 0.1.1-rc.1 or later`. */
30
+ requires: string;
31
+ /** What the user must do; placeholders are NOT needed — the message
32
+ * builder fills in the requirement and the installed version. */
33
+ guidance: string;
34
+ }
35
+ /** The compatibility table. */
36
+ declare const HARNESS_COMPAT: readonly HarnessCompatEntry[];
37
+ /** The compat entry covering the installed dsh version, or undefined when
38
+ * the harness is supported. */
39
+ declare function harnessCompatEntryFor(installed: string): HarnessCompatEntry | undefined;
40
+ /** The bundle's own version label for the gate message, read from
41
+ * package.json at runtime (never hardcoded). Falls back to the release
42
+ * line that first imposed the requirement when unreadable. */
43
+ declare function bundleVersionLabel(since: string): string;
44
+ /** The actionable message printed before the loader's raw failure when the
45
+ * installed harness falls in an incompatible range. `entry` MUST be the
46
+ * entry {@link harnessCompatEntryFor} matched for `installed`. */
47
+ declare function incompatibleHarnessMessage(installed: string, entry: HarnessCompatEntry): string;
8
48
  /** Service provided by this plugin and injected by the TUI runner row. */
9
49
  declare const TUI_STARTUP_SERVICE = "tuiStartup";
10
50
  /** What the runner row reads from {@link TUI_STARTUP_SERVICE}. */
@@ -29,4 +69,4 @@ interface TuiStartupValues {
29
69
  */
30
70
  declare function apply(ctx: Context): void;
31
71
  //#endregion
32
- export { TUI_STARTUP_SERVICE, TuiStartupValues, apply, inject, name };
72
+ export { HARNESS_COMPAT, HarnessCompatEntry, TUI_STARTUP_SERVICE, TuiStartupValues, apply, bundleVersionLabel, harnessCompatEntryFor, incompatibleHarnessMessage, inject, name };
package/dist/startup.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import { join } from "node:path";
1
+ import { readFileSync, realpathSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
2
3
  import { Command } from "commander";
3
4
  import { parseCmdline } from "@deepseek-ai/dsh-cmdline";
4
5
  //#region src/startup.ts
@@ -9,10 +10,97 @@ import { parseCmdline } from "@deepseek-ai/dsh-cmdline";
9
10
  * `cmdlineArgs`, providing a service that flag-configured rows inject.
10
11
  * @module @xmoon76/dsh-pi-tui/startup
11
12
  */
13
+ /** The installed dsh version, resolved from the launcher's real path. */
14
+ function installedDshVersion() {
15
+ const bin = process.argv[1];
16
+ if (bin === void 0) return void 0;
17
+ try {
18
+ let dir = dirname(realpathSync(bin));
19
+ for (let depth = 0; depth < 8; depth += 1) {
20
+ try {
21
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
22
+ if (pkg.name === "@deepseek-ai/dsh" && typeof pkg.version === "string") return pkg.version;
23
+ } catch {}
24
+ const parent = dirname(dir);
25
+ if (parent === dir) return void 0;
26
+ dir = parent;
27
+ }
28
+ } catch {}
29
+ }
30
+ /** Prerelease-aware `a >= b` (semver ordering; mirrors src/dsh-version.ts). */
31
+ function versionAtLeast(version, minimum) {
32
+ const parse = (value) => {
33
+ const [core, prerelease = ""] = value.split("-");
34
+ return {
35
+ nums: core.split(".").map(Number),
36
+ pre: prerelease === "" ? [] : prerelease.split(".")
37
+ };
38
+ };
39
+ const a = parse(version);
40
+ const b = parse(minimum);
41
+ for (let i = 0; i < 3; i += 1) {
42
+ const pa = a.nums[i] ?? 0;
43
+ const pb = b.nums[i] ?? 0;
44
+ if (pa !== pb) return pa > pb;
45
+ }
46
+ if (a.pre.length === 0) return true;
47
+ if (b.pre.length === 0) return false;
48
+ const length = Math.max(a.pre.length, b.pre.length);
49
+ for (let i = 0; i < length; i += 1) {
50
+ const pa = a.pre[i];
51
+ const pb = b.pre[i];
52
+ if (pa === void 0) return false;
53
+ if (pb === void 0) return true;
54
+ if (pa === pb) continue;
55
+ const na = Number(pa);
56
+ const nb = Number(pb);
57
+ const naNumeric = !Number.isNaN(na);
58
+ const nbNumeric = !Number.isNaN(nb);
59
+ if (naNumeric && nbNumeric) return na > nb;
60
+ if (naNumeric !== nbNumeric) return !naNumeric;
61
+ return pa > pb;
62
+ }
63
+ return true;
64
+ }
12
65
  /** Stable Cordis plugin name. */
13
66
  const name = "tui-startup";
14
67
  /** Services required before the flags can be resolved. */
15
68
  const inject = ["cmdlineArgs"];
69
+ /** The compatibility table. */
70
+ const HARNESS_COMPAT = [{
71
+ max: "0.1.1-rc.1",
72
+ since: "0.3.0",
73
+ requires: "DeepSeek Harness 0.1.1-rc.1 or later",
74
+ guidance: "Upgrade dsh first, e.g.: npm install -g @deepseek-ai/dsh@0.1.1-rc.1"
75
+ }];
76
+ /** The compat entry covering the installed dsh version, or undefined when
77
+ * the harness is supported. */
78
+ function harnessCompatEntryFor(installed) {
79
+ return HARNESS_COMPAT.find((entry) => (entry.min === void 0 || versionAtLeast(installed, entry.min)) && (entry.max === void 0 || !versionAtLeast(installed, entry.max)));
80
+ }
81
+ /** The bundle's own version label for the gate message, read from
82
+ * package.json at runtime (never hardcoded). Falls back to the release
83
+ * line that first imposed the requirement when unreadable. */
84
+ function bundleVersionLabel(since) {
85
+ try {
86
+ const pkg = JSON.parse(readFileSync(join(import.meta.dirname, "..", "package.json"), "utf8"));
87
+ return pkg.version === void 0 || pkg.version === "" ? `>= ${since}` : `v${pkg.version}`;
88
+ } catch {
89
+ return `>= ${since}`;
90
+ }
91
+ }
92
+ /** The actionable message printed before the loader's raw failure when the
93
+ * installed harness falls in an incompatible range. `entry` MUST be the
94
+ * entry {@link harnessCompatEntryFor} matched for `installed`. */
95
+ function incompatibleHarnessMessage(installed, entry) {
96
+ return [
97
+ `dsh-pi-tui ${bundleVersionLabel(entry.since)} requires ${entry.requires},`,
98
+ `but this installation is running dsh ${installed}.`,
99
+ "",
100
+ `${entry.guidance}`,
101
+ "Then re-run: dsh --profile pi-tui"
102
+ ].join("\n");
103
+ }
16
104
  /** Service provided by this plugin and injected by the TUI runner row. */
17
105
  const TUI_STARTUP_SERVICE = "tuiStartup";
18
106
  /** This app's command: its flags, its description, and its help text. */
@@ -33,6 +121,15 @@ Examples:
33
121
  function apply(ctx) {
34
122
  const program = tuiCommand();
35
123
  program.action(() => {
124
+ const installed = installedDshVersion();
125
+ if (installed !== void 0) {
126
+ const entry = harnessCompatEntryFor(installed);
127
+ if (entry !== void 0) {
128
+ const message = incompatibleHarnessMessage(installed, entry);
129
+ process.stderr.write(`\n${message}\n\n`);
130
+ throw new Error(message);
131
+ }
132
+ }
36
133
  const options = program.opts();
37
134
  ctx.provide(TUI_STARTUP_SERVICE, {
38
135
  ...options.session !== void 0 ? { sessionId: options.session } : {},
@@ -43,4 +140,4 @@ function apply(ctx) {
43
140
  parseCmdline(ctx, program);
44
141
  }
45
142
  //#endregion
46
- export { TUI_STARTUP_SERVICE, apply, inject, name };
143
+ export { HARNESS_COMPAT, TUI_STARTUP_SERVICE, apply, bundleVersionLabel, harnessCompatEntryFor, incompatibleHarnessMessage, inject, name };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmoon76/dsh-pi-tui",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "The dsh-pi-tui bundle: a TUI surface for DeepSeek Harness profiles (dsh --profile pi-tui)",
5
5
  "license": "MIT",
6
6
  "author": "XMoon <xmoonox@gmail.com>",