dsh-plugin-shop 0.8.3 → 0.8.4-beta.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/README.md +11 -8
- package/lib/client.js +1133 -421
- package/lib/index.js +1400 -178
- package/lib/typert.host.js +69 -22
- package/lib/typert.remote-client.js +52 -13
- package/lib/types/client/ShopTab.d.ts +19 -2
- package/lib/types/client/locales.d.ts +42 -2
- package/lib/types/client/module-table.d.ts +131 -0
- package/lib/types/client/present.d.ts +126 -2
- package/lib/types/client/restart-monitor.d.ts +58 -0
- package/lib/types/host/activation.d.ts +12 -9
- package/lib/types/host/bundle-patch.d.ts +35 -0
- package/lib/types/host/catalog.d.ts +9 -3
- package/lib/types/host/compatibility.d.ts +223 -0
- package/lib/types/host/dsh-cli.d.ts +8 -0
- package/lib/types/host/harness.d.ts +109 -0
- package/lib/types/host/hot.d.ts +8 -8
- package/lib/types/host/index.d.ts +197 -28
- package/lib/types/host/install.d.ts +1 -1
- package/lib/types/host/peers.d.ts +181 -10
- package/lib/types/host/profile.d.ts +61 -4
- package/lib/types/host/restart.d.ts +5 -1
- package/lib/types/host/types.d.ts +27 -1
- package/lib/types/own-version.d.ts +18 -2
- package/package.json +2 -1
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/** Harness compatibility as the AUTHOR states it: which `dsh.compatibility`
|
|
2
|
+
* declarations the running installation does not meet (design
|
|
3
|
+
* 2026-09-01-harness-compatibility §8.2, as amended §9.9). The presence check
|
|
4
|
+
* in `peers.ts` infers from what a plugin imports; this reads what its author
|
|
5
|
+
* wrote down. And, from dsh 0.1.7 on, what dsh ITSELF refuses: the harness
|
|
6
|
+
* peers its installer rejects an install on (`peerVerdictsOf`, design
|
|
7
|
+
* 2026-09-26-dsh-017-readiness, B1).
|
|
8
|
+
*
|
|
9
|
+
* Pure: the running version, the running profile, the harness's own
|
|
10
|
+
* template table and its peer check arrive as arguments, so fixtures drive
|
|
11
|
+
* every verdict and the reads of the installation stay outside — the running
|
|
12
|
+
* dsh, its template table and its peer check in `harness.ts`, the running
|
|
13
|
+
* profile's bundles and exemptions in the gateway. */
|
|
14
|
+
import { type EntryIdentity } from '../shared/identity.ts';
|
|
15
|
+
import type { PeerCheck } from './harness.ts';
|
|
16
|
+
/** What an author declared in `dsh.compatibility` that this installation does
|
|
17
|
+
* not meet. Each half is present only when the author declared it AND it is
|
|
18
|
+
* unmet here, and carries both sides, so the reader is told what was declared
|
|
19
|
+
* and what is actually running rather than only that something is wrong. */
|
|
20
|
+
export interface HarnessVerdict {
|
|
21
|
+
/** The declared `dsh` range, and the `@deepseek-ai/dsh` version running. */
|
|
22
|
+
dsh?: {
|
|
23
|
+
range: string;
|
|
24
|
+
running: string;
|
|
25
|
+
};
|
|
26
|
+
/** The declared profile templates, and the name of the profile this dsh was
|
|
27
|
+
* booted with. */
|
|
28
|
+
profile?: {
|
|
29
|
+
declared: string[];
|
|
30
|
+
running: string;
|
|
31
|
+
};
|
|
32
|
+
/** What the running dsh refuses this install on — no author's claim, and
|
|
33
|
+
* the one half the shop blocks an install for (`PeerVerdict`). */
|
|
34
|
+
peers?: PeerVerdict;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* An install the running dsh will refuse (0.1.7 on): the harness peers it
|
|
38
|
+
* refuses, each with the range the package declared; the dsh version it
|
|
39
|
+
* judged them against; and the command that records dsh's exact-version
|
|
40
|
+
* exemption for this package on this dsh, after which dsh installs it. The
|
|
41
|
+
* command is null when dsh would refuse that too (`allowVersionCommand`).
|
|
42
|
+
*/
|
|
43
|
+
export interface PeerVerdict {
|
|
44
|
+
refused: Record<string, string>;
|
|
45
|
+
running: string;
|
|
46
|
+
allowCommand: string | null;
|
|
47
|
+
}
|
|
48
|
+
/** The running profile as the profile half needs it: its name, for the copy,
|
|
49
|
+
* and the bundles it composes, for the comparison. `bundles` is null when the
|
|
50
|
+
* profile manifest could not be read, which is no verdict rather than an
|
|
51
|
+
* accusation. */
|
|
52
|
+
export interface RunningProfile {
|
|
53
|
+
name: string;
|
|
54
|
+
bundles: readonly string[] | null;
|
|
55
|
+
}
|
|
56
|
+
/** The running side of the comparison. `dshVersion` is null when the
|
|
57
|
+
* running dsh's version could not be read — this process was not started by
|
|
58
|
+
* dsh's CLI, or that CLI's manifest carries none (`harness.ts`). The profile
|
|
59
|
+
* is always known, because the gateway asking was booted inside one — but
|
|
60
|
+
* what it composes may not be. */
|
|
61
|
+
export interface HarnessRuntime {
|
|
62
|
+
dshVersion: string | null;
|
|
63
|
+
profile: RunningProfile;
|
|
64
|
+
}
|
|
65
|
+
/** Harness profile template name → the bundles that template composes, or an
|
|
66
|
+
* empty record when the harness's own table could not be read. The names are
|
|
67
|
+
* matched against catalog input, so a reader takes own keys only
|
|
68
|
+
* (`unmetProfile`), and `profileTemplatesOf` builds one with no prototype. */
|
|
69
|
+
export type ProfileTemplates = Readonly<Record<string, readonly string[]>>;
|
|
70
|
+
/**
|
|
71
|
+
* Normalize the harness's OWN `PROFILE_TEMPLATES` into template name → bundle
|
|
72
|
+
* names, or an empty record for a shape this build does not know.
|
|
73
|
+
*
|
|
74
|
+
* Two shapes exist and both are live: 0.1.5-rc.3 maps a name to
|
|
75
|
+
* `{ bundles, patchReload }` (five templates), while the version this package
|
|
76
|
+
* pins as a devDependency maps it to a bare array (two). A name whose value is
|
|
77
|
+
* neither, or whose bundle list holds a non-string, is dropped rather than
|
|
78
|
+
* guessed at — an unreadable table means no profile verdict, never a wrong
|
|
79
|
+
* one. Copied from nowhere: the caller passes the harness's exported object.
|
|
80
|
+
*
|
|
81
|
+
* The record has NO PROTOTYPE, empty answer included. It is indexed by the
|
|
82
|
+
* profile names a catalog entry declares, which are hostile input, and a
|
|
83
|
+
* record built on `{}` answered `constructor`, `__proto__`, `toString`,
|
|
84
|
+
* `hasOwnProperty` and `valueOf` from `Object.prototype`: a value that passed
|
|
85
|
+
* the `undefined` guard and threw out of `compatibilityMap`, so one entry
|
|
86
|
+
* declaring `profiles: ["constructor"]` rejected every `catalog()` call for
|
|
87
|
+
* every user. Without a prototype there is also no `__proto__` setter, so a
|
|
88
|
+
* template exported under that name stays an own key instead of replacing
|
|
89
|
+
* the record's prototype. `unmetProfile` takes own keys only as well — the
|
|
90
|
+
* type is exported, and a caller may hand in a plain object.
|
|
91
|
+
*/
|
|
92
|
+
export declare function profileTemplatesOf(exported: unknown): ProfileTemplates;
|
|
93
|
+
/**
|
|
94
|
+
* Install identity → the half or halves of an entry's declaration this
|
|
95
|
+
* installation does not meet. A key is present only when something was
|
|
96
|
+
* declared AND is unmet, so an absent key means "runs here, or we could not
|
|
97
|
+
* tell" — the rule `incompatibilityMap` follows, and for the same reason: one
|
|
98
|
+
* false warning teaches a reader to ignore every warning.
|
|
99
|
+
*
|
|
100
|
+
* The halves are judged apart, and an unknown on either side silences only its
|
|
101
|
+
* own half:
|
|
102
|
+
*
|
|
103
|
+
* - `dsh` needs a range semver can parse and a running version that is semver,
|
|
104
|
+
* and is then `satisfies` with `includePrerelease`. That option is
|
|
105
|
+
* load-bearing: the harness ships nothing but prereleases, and strict semver
|
|
106
|
+
* refuses a prerelease against any range whose comparators carry none —
|
|
107
|
+
* `0.1.5-rc.3` fails `>=0.1.0`, and even `*` — so every author who wrote a
|
|
108
|
+
* plain range would be told they exclude a harness their range includes.
|
|
109
|
+
*
|
|
110
|
+
* What the option does NOT do is read a range the way its author probably
|
|
111
|
+
* meant it. Under it a prerelease is compared like any other version, and a
|
|
112
|
+
* prerelease sorts BELOW its own release (measured with semver 7.8.5; the
|
|
113
|
+
* rows are pinned in compatibility.test.ts):
|
|
114
|
+
* - An upper bound written without `-0` admits the next line's
|
|
115
|
+
* prereleases: `<0.2.0` and `>=0.1.0 <0.2.0` both admit `0.2.0-rc.1`.
|
|
116
|
+
* Only a bound that desugars to `<0.2.0-0` refuses it — `^0.1.0`,
|
|
117
|
+
* `~0.1.0` and `0.1.x` do, as does `<0.2.0-0` written out.
|
|
118
|
+
* - A floor written without `-0` refuses its own prereleases: `>=0.1.5`,
|
|
119
|
+
* `^0.1.5` and `0.1.5` all refuse `0.1.5-rc.3`, because `0.1.5` sorts
|
|
120
|
+
* above it. `>=0.1.5-0` admits every `0.1.5-rc.N`.
|
|
121
|
+
* So `>=0.1.5-0` and `<0.2.0-0` are the spellings that say what an author
|
|
122
|
+
* usually means. The comparison stays semver's own and is never rewritten
|
|
123
|
+
* here: a verdict that coerced prereleases would answer differently from
|
|
124
|
+
* semver, which is what an author checks a range against. An exact list of
|
|
125
|
+
* other versions — `@xmanrui/dsh-im`'s declaration — is refused, as it
|
|
126
|
+
* should be.
|
|
127
|
+
* - `profile` is met when the running profile's NAME is one of the declared
|
|
128
|
+
* names, or when it composes every bundle of a declared template.
|
|
129
|
+
*
|
|
130
|
+
* The name is trusted for the names dsh ships, because for those it is not
|
|
131
|
+
* the reader's choice. On 0.1.5-rc.3 both paths that create a missing
|
|
132
|
+
* profile build one of a shipped name from that name's template
|
|
133
|
+
* (dsh-app-boot's `loadProfile`, and `dsh plugin`), and dsh refuses a
|
|
134
|
+
* shipped name as the target of `--from-default-profile`, so a profile
|
|
135
|
+
* carrying one was built from that template. That is all the name
|
|
136
|
+
* guarantees: which template the profile was built from, not what it
|
|
137
|
+
* composes now. Nothing holds the bundle list to the template afterwards:
|
|
138
|
+
* `dsh plugin`'s reconcile, not app-boot, appends each bundle it installs,
|
|
139
|
+
* and app-boot's `normalizeShippedProfile` rewrites only a list that is
|
|
140
|
+
* exactly a retired tuple or the current one. Bundles alone could not
|
|
141
|
+
* decide there: a profile keeps the bundle list it was created with, so the
|
|
142
|
+
* first harness release that added a bundle to `web` would badge every
|
|
143
|
+
* `profiles: ["web"]` plugin (`@xmanrui/dsh-im`) on every existing `web`
|
|
144
|
+
* profile, with copy saying it supports web and this dsh was launched with
|
|
145
|
+
* web.
|
|
146
|
+
*
|
|
147
|
+
* Any other name is the reader's choice — `dsh --profile rescue
|
|
148
|
+
* --from-default-profile web` builds a profile called `rescue` from the web
|
|
149
|
+
* bundles, and dsh records nothing about which template it came from — so a
|
|
150
|
+
* running name no declaration matches is judged by what the profile IS:
|
|
151
|
+
* each declared name is read as one of the harness's own templates, met
|
|
152
|
+
* when every bundle of that template is in the running profile's bundles. A
|
|
153
|
+
* declared name that is no template — an own key of the table, never an
|
|
154
|
+
* inherited one — cannot be judged, and a list with such a name and no met
|
|
155
|
+
* template gives no verdict. The half is unmet only when the running name is
|
|
156
|
+
* not declared and every declared name is a template this profile does not
|
|
157
|
+
* compose — see the amendment for the whole rule.
|
|
158
|
+
*
|
|
159
|
+
* The residual, knowingly kept: a custom-named profile is still judged by
|
|
160
|
+
* bundles, so a template that later gained a bundle would badge the custom
|
|
161
|
+
* profiles created before the change. No PUBLISHED
|
|
162
|
+
* `@deepseek-ai/dsh-app-boot` had changed a template's bundle list as of
|
|
163
|
+
* 2026-09-25: all 28 versions, read that day, give every template they
|
|
164
|
+
* share the same list. One list was retired before the first publish:
|
|
165
|
+
* every published app-boot, from the first, carries a retired `headless`
|
|
166
|
+
* tuple (`@deepseek-ai/dsh-base`, `@deepseek-ai/dsh-web-app`,
|
|
167
|
+
* `@deepseek-ai/dsh-headless`) in `INSTALLATION_OWNED_PROFILE_TUPLES`, and
|
|
168
|
+
* `normalizeShippedProfile` rewrites a `headless` profile carrying exactly
|
|
169
|
+
* that list to the current template when it loads.
|
|
170
|
+
*/
|
|
171
|
+
export declare function compatibilityMap(entries: readonly (EntryIdentity & {
|
|
172
|
+
compatibility?: {
|
|
173
|
+
dsh?: string;
|
|
174
|
+
profiles?: string[];
|
|
175
|
+
};
|
|
176
|
+
})[], runtime: HarnessRuntime, templates: ProfileTemplates): Record<string, HarnessVerdict>;
|
|
177
|
+
/**
|
|
178
|
+
* Install identity → the refusal the running dsh will make, for each entry
|
|
179
|
+
* whose harness peers it refuses and the profile has not exempted. A key is
|
|
180
|
+
* present only for a refusal, so an absent key means "dsh installs it, or we
|
|
181
|
+
* could not tell".
|
|
182
|
+
*
|
|
183
|
+
* The rule is dsh's own (`check`, from the running app-boot), handed the
|
|
184
|
+
* manifest dsh's installer reads before it installs a registry spec — `pnpm
|
|
185
|
+
* view <spec> name version peerDependencies`, measured in dsh-plugin-manager
|
|
186
|
+
* 0.1.7-rc.2 — with the catalog's `dshPeers` for the peers. Those are the
|
|
187
|
+
* only peers the rule reads, verbatim. What this cannot see errs toward
|
|
188
|
+
* silence, never toward a refusal dsh would not make: a malformed peer range
|
|
189
|
+
* elsewhere in the manifest, which dsh rejects only after installing, and
|
|
190
|
+
* the plugins a bundle's patch rows name, which it checks then too. Those
|
|
191
|
+
* reach the reader as the install's own failure (`installFailureDetail`).
|
|
192
|
+
*
|
|
193
|
+
* npm entries only. dsh keys an exemption by the INSTALLED manifest's
|
|
194
|
+
* `name@version`, and a github entry's catalog version is a commit: its
|
|
195
|
+
* exemption status cannot be read, and a refusal named here could never be
|
|
196
|
+
* cleared from the shop. dsh refuses such an install itself, and says how
|
|
197
|
+
* to exempt it.
|
|
198
|
+
*
|
|
199
|
+
* A throw from the check forms no verdict for that entry, as a resolver's
|
|
200
|
+
* throw does in `incompatibilityMap`.
|
|
201
|
+
*/
|
|
202
|
+
export declare function peerVerdictsOf(entries: readonly (EntryIdentity & {
|
|
203
|
+
version: string;
|
|
204
|
+
dshPeers?: Record<string, string>;
|
|
205
|
+
})[], check: PeerCheck, exemptions: Record<string, string[]>, profile: string): Record<string, PeerVerdict>;
|
|
206
|
+
/**
|
|
207
|
+
* The command that records dsh's exact-version exemption for one refused
|
|
208
|
+
* install, spelled as dsh's own CLI prints it after a refusal — or null when
|
|
209
|
+
* `dsh plugin allow-version` would refuse the package or either version,
|
|
210
|
+
* which it checks against the grammar above. The name and version come from
|
|
211
|
+
* the catalog, which is hostile input bound for a terminal, and the two
|
|
212
|
+
* grammars admit no whitespace, quote, `$`, backtick, `;`, `|`, `&`,
|
|
213
|
+
* redirection or bracket, so no catalog value can add a command to the line
|
|
214
|
+
* it is pasted into.
|
|
215
|
+
*
|
|
216
|
+
* The profile is written as it is, as dsh prints it: it is the reader's own
|
|
217
|
+
* choice of name, never catalog input.
|
|
218
|
+
*/
|
|
219
|
+
export declare function allowVersionCommand(profile: string, issue: {
|
|
220
|
+
name: string;
|
|
221
|
+
version: string;
|
|
222
|
+
runtimeVersion: string;
|
|
223
|
+
}): string | null;
|
|
@@ -23,6 +23,14 @@
|
|
|
23
23
|
*/
|
|
24
24
|
/** The npm package the dsh CLI is published as. */
|
|
25
25
|
export declare const DSH_PACKAGE = "@deepseek-ai/dsh";
|
|
26
|
+
/**
|
|
27
|
+
* Whether dsh's CLI refuses this profile outright: `desktop`, in any letter
|
|
28
|
+
* case, "is managed exclusively by the Electron application" — refused for a
|
|
29
|
+
* launch and for `dsh plugin` alike (`rejectElectronProfile` in the CLI's
|
|
30
|
+
* `lib/bin.js`, the same in 0.1.5-rc.3 and 0.1.7-rc.2). Every command the
|
|
31
|
+
* shop spawns names a profile, so none of them can run for this one.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isDesktopProfile(profile: string): boolean;
|
|
26
34
|
/**
|
|
27
35
|
* The command that starts `bin` through `execPath` when `bin` names a
|
|
28
36
|
* JavaScript entry, or `null` when `bin` is a program to spawn as given.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The harness this process runs: which `@deepseek-ai/dsh` it is, and that
|
|
3
|
+
* dsh's own profile-template table — the running side of the
|
|
4
|
+
* `dsh.compatibility` verdict (design 2026-09-01-harness-compatibility §9.9).
|
|
5
|
+
*
|
|
6
|
+
* Both used to be read from somewhere that is not the running harness, and
|
|
7
|
+
* both were wrong there:
|
|
8
|
+
*
|
|
9
|
+
* - The version was the first `node_modules/@deepseek-ai/dsh` found walking up
|
|
10
|
+
* from the profile: what the profile can IMPORT, not what RUNS. A plugin
|
|
11
|
+
* that depends on the dsh package gets its copy hoisted into
|
|
12
|
+
* `<profile>/node_modules` — the listed `dsh-claude-tui@0.1.6` pulls
|
|
13
|
+
* 0.1.2-rc.1 — a second install sharing DSH_HOME re-points the link farm,
|
|
14
|
+
* and under a packaged dsh the bin-only package gets no link-farm proxy at
|
|
15
|
+
* all, so the read was dead or stale. The first was reproduced with real
|
|
16
|
+
* installs on 2026-09-25 (the design's section 9.9 amendment): every
|
|
17
|
+
* verdict said "running 0.1.2-rc.1" while 0.1.5-rc.3 ran.
|
|
18
|
+
* - The templates came from the shop's own `import * as appBoot`, which
|
|
19
|
+
* resolves from the SHOP's real path: under a `link:` install that is this
|
|
20
|
+
* repository's devDependency, 0.1.1-rc.2 with two templates, while
|
|
21
|
+
* 0.1.5-rc.3 with five runs — and in production a copy hoisted from some
|
|
22
|
+
* plugin's dependencies can shadow it the same way.
|
|
23
|
+
*
|
|
24
|
+
* Both now come from the one thing that names the running harness: the script
|
|
25
|
+
* this process was started with. Resolved through symlinks, the package that
|
|
26
|
+
* owns it IS the dsh CLI that is running, if it is one at all; its manifest
|
|
27
|
+
* says its version, and the app-boot it imports says its templates.
|
|
28
|
+
*/
|
|
29
|
+
import { type ProfileTemplates } from './compatibility.ts';
|
|
30
|
+
/** One package's harness peers as the running dsh judges them: app-boot's
|
|
31
|
+
* `evaluatePluginCompatibility` result, the shape 0.1.7 returns. */
|
|
32
|
+
export interface PeerIssue {
|
|
33
|
+
name: string;
|
|
34
|
+
version: string;
|
|
35
|
+
/** The dsh version the check judged against — app-boot's own. */
|
|
36
|
+
runtimeVersion: string;
|
|
37
|
+
/** The refused peers, each with the range the package declared. */
|
|
38
|
+
peers: Record<string, string>;
|
|
39
|
+
/** Whether the profile holds dsh's exact-version exemption for this
|
|
40
|
+
* package on this dsh, in which case dsh accepts the install anyway. */
|
|
41
|
+
exempted: boolean;
|
|
42
|
+
}
|
|
43
|
+
/** The running dsh's own install-time check (app-boot, from 0.1.7): the rule
|
|
44
|
+
* it refuses an install by, and the exemptions a profile holds. Taken from the
|
|
45
|
+
* dsh that runs rather than reimplemented, so a verdict cannot drift from the
|
|
46
|
+
* refusal it predicts (design 2026-09-26-dsh-017-readiness, B1). */
|
|
47
|
+
export interface PeerCheck {
|
|
48
|
+
/** `evaluatePluginCompatibility(manifest, exemptions, runtimeVersion)` with
|
|
49
|
+
* app-boot's own version as the runtime — the version dsh's installer
|
|
50
|
+
* judges by, since it passes none and the default reads app-boot's
|
|
51
|
+
* manifest. That version is read once, here, rather than per call: the
|
|
52
|
+
* default re-reads the file on every call, which measured 75 ms against
|
|
53
|
+
* 12 ms for 2,000 manifests on 0.1.7-rc.2. The incompatible harness peers
|
|
54
|
+
* of one manifest, or undefined when there are none. Throws for a manifest
|
|
55
|
+
* it cannot judge. */
|
|
56
|
+
evaluate(manifest: {
|
|
57
|
+
name: string;
|
|
58
|
+
version: string;
|
|
59
|
+
peerDependencies: Record<string, string>;
|
|
60
|
+
}, exemptions: Record<string, string[]>): PeerIssue | undefined;
|
|
61
|
+
/** `readProfileVersionExemptions(profileDir)`: exact `name@version` keys
|
|
62
|
+
* mapped to the dsh versions each is exempted on. */
|
|
63
|
+
exemptions(profileDir: string): Record<string, string[]>;
|
|
64
|
+
}
|
|
65
|
+
/** The running side of the `dsh.compatibility` verdict. The gateway reads it
|
|
66
|
+
* once and keeps it: a running process cannot change which dsh it is. */
|
|
67
|
+
export interface RunningHarness {
|
|
68
|
+
/** The running dsh's own `version`, or null when this process was not
|
|
69
|
+
* started by dsh's CLI or its manifest carries no non-empty string there.
|
|
70
|
+
* Whether it is semver is `compatibilityMap`'s question. */
|
|
71
|
+
dshVersion: string | null;
|
|
72
|
+
/** That dsh's own `PROFILE_TEMPLATES`, normalized by `profileTemplatesOf`;
|
|
73
|
+
* empty whenever the table could not be read. */
|
|
74
|
+
templates: ProfileTemplates;
|
|
75
|
+
/** Whether that dsh reads a LIST-valued `dsh.bundle.patch`: its app-boot
|
|
76
|
+
* exports `bundlePatchFiles`, which 0.1.7 does and 0.1.5 does not. A dsh
|
|
77
|
+
* without it joins the declaration onto a path, so a package declaring a
|
|
78
|
+
* list stops the profile from starting (design 2026-09-26-dsh-017-readiness,
|
|
79
|
+
* B4). Null whenever the app-boot could not be read — unknown, which forms
|
|
80
|
+
* no verdict. */
|
|
81
|
+
patchLists: boolean | null;
|
|
82
|
+
/** That dsh's own install-time peer check, or null when its app-boot
|
|
83
|
+
* exports none (before 0.1.7, which refuses no install on peers) or could
|
|
84
|
+
* not be read — no verdict either way. */
|
|
85
|
+
peerCheck: PeerCheck | null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The harness this process runs, identified from `script` — the gateway's
|
|
89
|
+
* `restartScript`, `process.argv[1]` in production — or nothing, never a
|
|
90
|
+
* guess:
|
|
91
|
+
*
|
|
92
|
+
* 1. `script` is resolved through symlinks, because `argv[1]` is the path the
|
|
93
|
+
* shell ran — `<prefix>/bin/dsh`, a link — and only its target lies inside
|
|
94
|
+
* the dsh package. No script, or one that does not resolve: nothing known.
|
|
95
|
+
* An empty path is no script either: `realpathSync('')` answers the
|
|
96
|
+
* working directory, which says where dsh was started, not what it is.
|
|
97
|
+
* 2. The package that owns it (`owningPackage`) must be `@deepseek-ai/dsh`.
|
|
98
|
+
* Anything else — a test runner, another host embedding the shop — means
|
|
99
|
+
* nothing here can say which harness runs, and both halves of the verdict
|
|
100
|
+
* stay silent.
|
|
101
|
+
* 3. `dshVersion` is that manifest's `version` when it is a non-empty string.
|
|
102
|
+
* 4. `templates`, `patchLists` and `peerCheck` come from that dsh's own
|
|
103
|
+
* app-boot (`appBootFacts`). Any failure there costs all three and keeps
|
|
104
|
+
* the version.
|
|
105
|
+
*
|
|
106
|
+
* Never throws and never rejects: a harness nobody could identify forms no
|
|
107
|
+
* verdict, and must never be the reason the catalog fails to load.
|
|
108
|
+
*/
|
|
109
|
+
export declare function readRunningHarness(script: string | undefined): Promise<RunningHarness>;
|
package/lib/types/host/hot.d.ts
CHANGED
|
@@ -51,19 +51,19 @@ export interface PluginHandle {
|
|
|
51
51
|
* Why a restart is needed after the hot path ran — a stable code the client
|
|
52
52
|
* turns into copy in the reader's own dsh language.
|
|
53
53
|
*
|
|
54
|
-
*
|
|
54
|
+
* Five of the six say the MOUNT could not activate, and distinguish
|
|
55
55
|
* "restart will fix it" (`timeout`, `mount-failed`) from "this package can
|
|
56
56
|
* never hot-mount" (`no-patch`, `not-simple`) and "this harness cannot"
|
|
57
57
|
* (`host-unsupported`).
|
|
58
58
|
*
|
|
59
|
-
* `
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
59
|
+
* `already-loaded` is the one that does not: no mount was attempted, because
|
|
60
|
+
* this process may already hold the package's module, and Node caches a
|
|
61
|
+
* module by its URL — the new files sit at the old URL, so a mount would re-run
|
|
62
|
+
* the old code under the new version's name (design
|
|
63
|
+
* 2026-09-26-market-borrowings §1). The gateway decides it before calling
|
|
64
|
+
* `hotMount`, which never returns it.
|
|
65
65
|
*/
|
|
66
|
-
export type HotRestartReason = 'no-patch' | 'not-simple' | 'host-unsupported' | 'timeout' | 'mount-failed' | '
|
|
66
|
+
export type HotRestartReason = 'no-patch' | 'not-simple' | 'host-unsupported' | 'timeout' | 'mount-failed' | 'already-loaded';
|
|
67
67
|
export interface HotMountResult {
|
|
68
68
|
ok: boolean;
|
|
69
69
|
/** null exactly when ok. */
|