@chatcode/cco-market 1.45.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.
Files changed (177) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/README.zh.md +143 -0
  4. package/UPDATE-API-V1.md +132 -0
  5. package/client/client.js +11534 -0
  6. package/cordis.patch.yml +5 -0
  7. package/lib/accelerate.js +184 -0
  8. package/lib/agents.js +36 -0
  9. package/lib/backup.js +572 -0
  10. package/lib/catalog-local-match.js +126 -0
  11. package/lib/catalog-npm.js +111 -0
  12. package/lib/changelog.js +242 -0
  13. package/lib/channels.js +64 -0
  14. package/lib/check.js +1067 -0
  15. package/lib/compatibility.js +198 -0
  16. package/lib/diagnostics.js +49 -0
  17. package/lib/discovery-compatibility.js +260 -0
  18. package/lib/dsh-cli.js +949 -0
  19. package/lib/dsh-install.js +108 -0
  20. package/lib/gist.js +352 -0
  21. package/lib/groups.js +97 -0
  22. package/lib/home-paths.js +42 -0
  23. package/lib/hot.js +519 -0
  24. package/lib/http.js +39 -0
  25. package/lib/index.js +97 -0
  26. package/lib/install.js +387 -0
  27. package/lib/log.js +197 -0
  28. package/lib/ndjson.js +154 -0
  29. package/lib/net.js +98 -0
  30. package/lib/order.js +284 -0
  31. package/lib/patch.js +521 -0
  32. package/lib/pnpm-compat.js +490 -0
  33. package/lib/presets.js +278 -0
  34. package/lib/profile.js +911 -0
  35. package/lib/region-probe.js +93 -0
  36. package/lib/regions.js +253 -0
  37. package/lib/registry.js +204 -0
  38. package/lib/restart.js +373 -0
  39. package/lib/routes.js +4372 -0
  40. package/lib/settings.js +109 -0
  41. package/lib/skill-market/api.js +119 -0
  42. package/lib/skill-market/config.js +29 -0
  43. package/lib/skill-market/installer.js +352 -0
  44. package/lib/skill-market/runtime.js +13 -0
  45. package/lib/skill-market/tui.js +119 -0
  46. package/lib/skill-market/types.js +1 -0
  47. package/lib/skill-market/web-routes.js +123 -0
  48. package/lib/snapshot.js +500 -0
  49. package/lib/source-migration.js +54 -0
  50. package/lib/sources.js +596 -0
  51. package/lib/store.js +91 -0
  52. package/lib/themes.js +102 -0
  53. package/lib/trial.js +116 -0
  54. package/lib/types/accelerate.d.ts +85 -0
  55. package/lib/types/agents.d.ts +22 -0
  56. package/lib/types/backup.d.ts +131 -0
  57. package/lib/types/catalog-local-match.d.ts +50 -0
  58. package/lib/types/catalog-npm.d.ts +47 -0
  59. package/lib/types/changelog.d.ts +102 -0
  60. package/lib/types/channels.d.ts +56 -0
  61. package/lib/types/check.d.ts +266 -0
  62. package/lib/types/compatibility.d.ts +64 -0
  63. package/lib/types/diagnostics.d.ts +29 -0
  64. package/lib/types/discovery-compatibility.d.ts +80 -0
  65. package/lib/types/dsh-cli.d.ts +324 -0
  66. package/lib/types/dsh-install.d.ts +46 -0
  67. package/lib/types/gist.d.ts +53 -0
  68. package/lib/types/groups.d.ts +34 -0
  69. package/lib/types/home-paths.d.ts +16 -0
  70. package/lib/types/hot.d.ts +221 -0
  71. package/lib/types/http.d.ts +12 -0
  72. package/lib/types/index.d.ts +14 -0
  73. package/lib/types/install.d.ts +172 -0
  74. package/lib/types/log.d.ts +43 -0
  75. package/lib/types/ndjson.d.ts +52 -0
  76. package/lib/types/net.d.ts +58 -0
  77. package/lib/types/order.d.ts +100 -0
  78. package/lib/types/patch.d.ts +129 -0
  79. package/lib/types/pnpm-compat.d.ts +93 -0
  80. package/lib/types/presets.d.ts +90 -0
  81. package/lib/types/profile.d.ts +253 -0
  82. package/lib/types/region-probe.d.ts +50 -0
  83. package/lib/types/regions.d.ts +122 -0
  84. package/lib/types/registry.d.ts +86 -0
  85. package/lib/types/restart.d.ts +194 -0
  86. package/lib/types/routes.d.ts +67 -0
  87. package/lib/types/settings.d.ts +78 -0
  88. package/lib/types/skill-market/api.d.ts +10 -0
  89. package/lib/types/skill-market/config.d.ts +3 -0
  90. package/lib/types/skill-market/installer.d.ts +23 -0
  91. package/lib/types/skill-market/runtime.d.ts +10 -0
  92. package/lib/types/skill-market/tui.d.ts +4 -0
  93. package/lib/types/skill-market/types.d.ts +71 -0
  94. package/lib/types/skill-market/web-routes.d.ts +19 -0
  95. package/lib/types/snapshot.d.ts +93 -0
  96. package/lib/types/source-migration.d.ts +11 -0
  97. package/lib/types/sources.d.ts +216 -0
  98. package/lib/types/store.d.ts +30 -0
  99. package/lib/types/themes.d.ts +40 -0
  100. package/lib/types/trial.d.ts +61 -0
  101. package/lib/types/update-api-v1.d.ts +66 -0
  102. package/lib/types/updates.d.ts +123 -0
  103. package/lib/types/verify.d.ts +139 -0
  104. package/lib/update-api-v1.js +215 -0
  105. package/lib/updates.js +361 -0
  106. package/lib/verify.js +453 -0
  107. package/package.json +121 -0
  108. package/src/accelerate.ts +213 -0
  109. package/src/agents.ts +43 -0
  110. package/src/backup.ts +583 -0
  111. package/src/catalog-local-match.ts +144 -0
  112. package/src/catalog-npm.ts +120 -0
  113. package/src/changelog.ts +282 -0
  114. package/src/channels.ts +70 -0
  115. package/src/check.ts +1239 -0
  116. package/src/client/CommentsModal.tsx +119 -0
  117. package/src/client/Diagnostics.tsx +907 -0
  118. package/src/client/ErrorBoundary.tsx +111 -0
  119. package/src/client/InstallToast.tsx +31 -0
  120. package/src/client/Market.module.css +830 -0
  121. package/src/client/MarketSection.tsx +5301 -0
  122. package/src/client/OperationsPanel.tsx +365 -0
  123. package/src/client/SettingsCard.tsx +621 -0
  124. package/src/client/SkillMarket.module.css +21 -0
  125. package/src/client/SkillMarketSection.tsx +162 -0
  126. package/src/client/comments.ts +54 -0
  127. package/src/client/globals.d.ts +13 -0
  128. package/src/client/index.ts +180 -0
  129. package/src/client/locales.ts +1112 -0
  130. package/src/client/market-data.ts +1321 -0
  131. package/src/client/operations.ts +201 -0
  132. package/src/client/preset-panel.tsx +263 -0
  133. package/src/client/primitives.d.ts +140 -0
  134. package/src/client/self-check.ts +147 -0
  135. package/src/client/snapshot-panel.tsx +244 -0
  136. package/src/compatibility.ts +237 -0
  137. package/src/diagnostics.ts +84 -0
  138. package/src/discovery-compatibility.ts +315 -0
  139. package/src/dsh-cli.ts +1126 -0
  140. package/src/dsh-install.ts +118 -0
  141. package/src/gist.ts +362 -0
  142. package/src/groups.ts +111 -0
  143. package/src/home-paths.ts +53 -0
  144. package/src/hot.ts +628 -0
  145. package/src/http.ts +41 -0
  146. package/src/index.ts +128 -0
  147. package/src/install.ts +420 -0
  148. package/src/log.ts +206 -0
  149. package/src/ndjson.ts +185 -0
  150. package/src/net.ts +106 -0
  151. package/src/order.ts +303 -0
  152. package/src/patch.ts +522 -0
  153. package/src/pnpm-compat.ts +527 -0
  154. package/src/presets.ts +344 -0
  155. package/src/profile.ts +940 -0
  156. package/src/region-probe.ts +97 -0
  157. package/src/regions.ts +310 -0
  158. package/src/registry.ts +250 -0
  159. package/src/restart.ts +396 -0
  160. package/src/routes.ts +4506 -0
  161. package/src/settings.ts +141 -0
  162. package/src/skill-market/api.ts +130 -0
  163. package/src/skill-market/config.ts +32 -0
  164. package/src/skill-market/installer.ts +337 -0
  165. package/src/skill-market/runtime.ts +14 -0
  166. package/src/skill-market/tui.ts +157 -0
  167. package/src/skill-market/types.ts +80 -0
  168. package/src/skill-market/web-routes.ts +132 -0
  169. package/src/snapshot.ts +532 -0
  170. package/src/source-migration.ts +61 -0
  171. package/src/sources.ts +565 -0
  172. package/src/store.ts +89 -0
  173. package/src/themes.ts +125 -0
  174. package/src/trial.ts +156 -0
  175. package/src/update-api-v1.ts +277 -0
  176. package/src/updates.ts +400 -0
  177. package/src/verify.ts +492 -0
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The market's release channels: which build of ITSELF it offers.
3
+ *
4
+ * Only the market follows this. Other plugins are never pulled from a
5
+ * prerelease on the strength of a setting the user made about the market —
6
+ * opting into early builds is volunteering to try THIS plugin early, not to
7
+ * be handed every other author's unreleased work.
8
+ *
9
+ * The model lives in its own module because it is the part with rules
10
+ * rather than plumbing: three channels, one of them hidden, a mapping to
11
+ * npm dist-tags, and a resolution order that has already been got wrong
12
+ * once (see `resolveChannel`).
13
+ */
14
+ /** A channel the market can follow. */
15
+ export type Channel = 'stable' | 'beta' | 'dev';
16
+ /**
17
+ * The npm dist-tag each channel installs from.
18
+ *
19
+ * `dev` is published straight from a branch with no git tag behind it, so a
20
+ * version carries a timestamp and a short SHA (`1.15.0-dev.20260818-3f1432e`)
21
+ * and is never reused. That is what makes a dev build disposable: nothing in
22
+ * the repository's history refers to it.
23
+ */
24
+ export declare const DIST_TAG: Record<Channel, string>;
25
+ /**
26
+ * Every channel a user may pick. All three, always.
27
+ *
28
+ * `dev` was behind a developer-mode switch for one version, on the reasoning
29
+ * that a build published straight off a branch should not sit beside
30
+ * "stable" looking like a third degree of caution. The switch cost more than
31
+ * it bought: a stored mode, a route to change it, a rule for what happens to
32
+ * a dev choice when it is turned off, and a control whose own purpose needed
33
+ * explaining. A plainly labelled option a user can read is simpler than a
34
+ * hidden one plus the machinery that hides it — the label does the work the
35
+ * gate was doing.
36
+ */
37
+ export declare const CHANNELS: readonly Channel[];
38
+ /** Narrow an untrusted value to a Channel, or null. */
39
+ export declare function asChannel(value: unknown): Channel | null;
40
+ /**
41
+ * Which channel applies right now.
42
+ *
43
+ * A choice on record always wins — including "stable" while a prerelease is
44
+ * running, which is the only way back off a channel. Only the ABSENCE of a
45
+ * choice is derived, and then from what is actually running: installing
46
+ * `dshmarket@beta` by hand IS the subscription, and treating that as
47
+ * "stable" costs updates rather than just clarity — on the stable channel
48
+ * `latest` (1.13.1) is not newer than an installed 1.14.0-beta.1, so the
49
+ * market answers "up to date" and the next beta is never offered.
50
+ *
51
+ * Which makes `undefined` load-bearing: it has to survive both the settings
52
+ * schema (no `.default`) and state.json (field omitted) or "never chose"
53
+ * silently becomes "chose stable".
54
+ *
55
+ */
56
+ export declare function resolveChannel(setting: Channel | undefined, version: string): Channel;
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Profile composition diagnostics — issue #98 (phase 1): the check-only
3
+ * "plugin loading layer and conflict view".
4
+ *
5
+ * Pure filesystem analysis of one dsh profile directory; no processes, no
6
+ * network, no writes. It answers, for the profile the market is serving:
7
+ *
8
+ * 1. What is the actual bundle stack (dsh.profile.bundles order) and where
9
+ * does each layer come from (official in-box bundle vs community, the
10
+ * dependency spec, the resolved directory)?
11
+ * 2. Which loader entry ids does the composed tree contain, and are any
12
+ * duplicated across layers (the "duplicate loader entry id" boot failure
13
+ * from #98)? Which rows does a later layer override?
14
+ * 3. Does any installed plugin pull a DSH host core package
15
+ * (@deepseek-ai/dsh, @deepseek-ai/dsh-tools, @deepseek-ai/cordis, …) in
16
+ * as an ordinary dependency — the dsh-excel-chat failure mode where the
17
+ * plugin's copy gets hoisted to the profile root and shadows the host's
18
+ * version (tool calls die, minimal preset fails to mount)?
19
+ * 4. Are there multiple versions of one core package in the lockfile, and
20
+ * do plugin peerDependencies ranges match the resolved core version?
21
+ * 5. Do effective user/home patch entries reference npm package roots that
22
+ * are installed in the profile-visible node_modules ancestry?
23
+ *
24
+ * The composition step mirrors @deepseek-ai/dsh-app-boot's applyEntryPatches
25
+ * (same js-yaml dialect incl. `!!js` scalars), so the rows reported here are
26
+ * what actually mounts at boot.
27
+ */
28
+ export { findDshInstallDir } from './dsh-install.ts';
29
+ /** Boot-breaking or confirmed problems vs informational warnings. */
30
+ export interface CheckSummary {
31
+ ok: boolean;
32
+ errors: string[];
33
+ warnings: string[];
34
+ }
35
+ /** One layer of the bundle stack, in `dsh.profile.bundles` order. */
36
+ export interface BundleLayer {
37
+ name: string;
38
+ /** Dependency spec from the profile package.json (npm range, git, link:…). */
39
+ source: string;
40
+ /** 'official' = in-box dsh bundle; 'community' = everything else. */
41
+ kind: 'official' | 'community';
42
+ /** Resolved package directory; null when the package is not installed. */
43
+ directory: string | null;
44
+ /** Absolute path of the layer's patch file; null when undeclared/missing. */
45
+ patchPath: string | null;
46
+ /**
47
+ * An in-box bundle whose directory could not be located — a gap in what
48
+ * this process can see, not a defect in the profile (#369). Distinct from
49
+ * `error`, which asserts the profile will not boot.
50
+ */
51
+ unresolvedInbox?: boolean;
52
+ /** Why this layer cannot load at boot (missing dir / no dsh.bundle / …). */
53
+ error: string | null;
54
+ /** Loader entry ids this bundle's patch inserts. */
55
+ entries: string[];
56
+ /** The patch file exists but could not be parsed as the entry-list dialect. */
57
+ parseError: string | null;
58
+ /** Author-declared ordering constraints (issue #98 phase 2), when present. */
59
+ order?: {
60
+ before?: string[];
61
+ after?: string[];
62
+ /** Violated rules for THIS bundle in the current stack order. */
63
+ conflicts?: Array<{
64
+ name: string;
65
+ reason: string;
66
+ }>;
67
+ };
68
+ }
69
+ /** One loader row of the composed tree, with the layer that introduced it. */
70
+ export interface LoaderRow {
71
+ id: string;
72
+ /** Bundle package name, 'user-patch' (profile cordis.patch.yml) or 'home-patch'. */
73
+ layer: string;
74
+ kind: 'insert' | 'patch';
75
+ name?: string;
76
+ }
77
+ /** An id present in more than one composed row — the #98 duplicate-id boot failure. */
78
+ export interface DuplicateId {
79
+ id: string;
80
+ /** Every layer that inserts/defines this id. */
81
+ layers: string[];
82
+ count: number;
83
+ }
84
+ /** A non-insert patch row that merged into an existing entry (later layer wins). */
85
+ export interface OverrideRow {
86
+ id: string;
87
+ layer: string;
88
+ /** Layers that introduced the targeted entry earlier in the stack. */
89
+ overriddenLayers: string[];
90
+ }
91
+ /** A patch row that matched nothing at boot (dsh warns and skips it). */
92
+ export interface OrphanRow {
93
+ id: string;
94
+ layer: string;
95
+ reason: string;
96
+ }
97
+ /** Directional verdict for one confirmed peer mismatch (issue #201 diagnostics). */
98
+ export interface PeerRisk {
99
+ plugin: string;
100
+ peer: string;
101
+ range: string;
102
+ resolved: string;
103
+ direction: 'belowMin' | 'aboveMax';
104
+ }
105
+ export interface PeerWarning {
106
+ plugin: string;
107
+ peer: string;
108
+ range: string;
109
+ resolved: string;
110
+ reason: 'aboveMax' | 'optional';
111
+ }
112
+ /** classifyPeer result: risk / warning / none — none means informational. */
113
+ export type PeerVerdict = {
114
+ kind: 'risk';
115
+ risk: PeerRisk;
116
+ } | {
117
+ kind: 'warning';
118
+ warning: PeerWarning;
119
+ } | {
120
+ kind: 'none';
121
+ };
122
+ /** A plugin peerDependencies range vs the resolved version. */
123
+ export interface PeerMismatch {
124
+ plugin: string;
125
+ name: string;
126
+ range: string;
127
+ resolved: string | null;
128
+ /** False = confirmed incompatible; null = could not be evaluated. */
129
+ satisfied: boolean | null;
130
+ /**
131
+ * The declaring plugin marked this peer `optional` in
132
+ * `peerDependenciesMeta`. Carried so the summary can hold the same line
133
+ * `classifyPeer` already does: an optional peer that does not match is
134
+ * the plugin saying "I work without this", not a broken install (#275).
135
+ */
136
+ optional?: boolean;
137
+ /**
138
+ * The directional verdict, attached by `/dsh-market/check` for rows that
139
+ * did not match. It is computed on the server because the client cannot
140
+ * read `peerDependenciesMeta` off disk, and the tiering would otherwise
141
+ * have to be guessed from the range string (#201).
142
+ */
143
+ verdict?: PeerVerdict;
144
+ }
145
+ /**
146
+ * Loader entries sharing one NAME across DIFFERENT layers — the Loader
147
+ * registers plugins by name, so a later layer's row with the same name
148
+ * shadows the earlier one at runtime. Same-layer rows sharing a name are
149
+ * routine (a bundle defining several entries under one name) and are never
150
+ * reported here. Unlike duplicate ids (report.duplicates), which fail the
151
+ * boot outright, shadowing names only decide which entry wins at runtime.
152
+ */
153
+ export interface DuplicateName {
154
+ name: string;
155
+ /** Every layer that inserts/defines a row with this name. */
156
+ layers: string[];
157
+ count: number;
158
+ }
159
+ /** Distinct resolved versions of one core package found in the lockfile. */
160
+ export interface MultiVersion {
161
+ name: string;
162
+ versions: string[];
163
+ /** Version hoisted at the profile root, when present. */
164
+ hoisted: string | null;
165
+ }
166
+ /** The full check report for one profile. */
167
+ export interface CheckReport {
168
+ profile: string;
169
+ scannedAt: number;
170
+ bundles: BundleLayer[];
171
+ rows: LoaderRow[];
172
+ duplicates: DuplicateId[];
173
+ duplicateNames: DuplicateName[];
174
+ overrides: OverrideRow[];
175
+ orphans: OrphanRow[];
176
+ peerMismatches: PeerMismatch[];
177
+ multiVersion: MultiVersion[];
178
+ /** Before/after rule conflicts in the CURRENT bundle order (issue #98 phase 2). */
179
+ orderConflicts: Array<{
180
+ name: string;
181
+ reason: string;
182
+ }>;
183
+ /** LOOT-style auto-fix: a community order satisfying every declared rule. */
184
+ suggestedOrder: {
185
+ ok: true;
186
+ order: string[];
187
+ } | {
188
+ ok: false;
189
+ cycle: string[];
190
+ } | null;
191
+ summary: CheckSummary;
192
+ }
193
+ export interface CheckOptions {
194
+ /** DSH host install dir; auto-detected from the CLI entry or Desktop resources when omitted. */
195
+ dshInstallDir?: string;
196
+ /** ChatCode CLI home for the home-level patch layer; legacy DSH_HOME remains a read-only compatibility input. */
197
+ homeDir?: string;
198
+ }
199
+ /** Parse entry-list source with the DSH dialect; null when it is not a list. */
200
+ export declare function parsePatchText(text: string): unknown[] | null;
201
+ /** Parse one entry-list patch file with the DSH dialect; null when unreadable. */
202
+ export declare function parsePatchFile(path: string): unknown[] | null;
203
+ /** DSH host core packages: what the dsh installation ships under @deepseek-ai. */
204
+ export declare function corePackageNames(dshInstallDir: string | null): Set<string>;
205
+ /** Compare two semver strings: negative | zero | positive (prerelease < release of same base). */
206
+ export declare function compareSemver(a: string, b: string): number;
207
+ /**
208
+ * Minimal range matcher for the peer-range check: `*`, exact, ^, ~, >=, >,
209
+ * <=, <, whitespace-separated pairs, and `||` alternatives. Anything else
210
+ * returns null (unknown — reported, not asserted).
211
+ *
212
+ * Prerelease handling follows npm's semver rule, evaluated at the comparator
213
+ * SET level (one `||` alternative is one set): a version carrying a
214
+ * prerelease tag only satisfies a set when at least one comparator in that
215
+ * set shares the version's [major, minor, patch] tuple AND carries a
216
+ * prerelease of its own; then every comparator is checked normally. So
217
+ * `^0.1.0` never matches `0.2.0-rc.1` (nor `0.1.0-rc.5`), while
218
+ * `>=1.2.3-rc.1 <2.0.0` does match `1.2.3-rc.2` (issue #98 analysis).
219
+ * Discovery can opt into npm's `includePrerelease` behaviour because every
220
+ * published DSH host line is itself prerelease; diagnostics retain the npm
221
+ * default unless a caller explicitly asks for that wider admission.
222
+ */
223
+ export declare function satisfiesRange(version: string, range: string, options?: {
224
+ includePrerelease?: boolean;
225
+ }): boolean | null;
226
+ interface ResolvableLoaderRow extends LoaderRow {
227
+ activation: 'required' | 'conditional';
228
+ }
229
+ export interface LayerInput {
230
+ label: string;
231
+ kind: 'bundle' | 'user' | 'home';
232
+ patches: unknown[];
233
+ parseError: string | null;
234
+ }
235
+ interface Composed {
236
+ rows: LoaderRow[];
237
+ resolvableRows: ResolvableLoaderRow[];
238
+ duplicates: DuplicateId[];
239
+ overrides: OverrideRow[];
240
+ orphans: OrphanRow[];
241
+ }
242
+ /**
243
+ * Apply the layer stack over an empty root exactly like the dsh boot include.
244
+ * Exported so the trial-start validation (src/trial.ts) can replay the
245
+ * composition with a candidate bundle order BEFORE anything is written.
246
+ */
247
+ export declare function composeLayers(layers: LayerInput[]): Composed;
248
+ /**
249
+ * Build the bundle layer stack for a profile under a GIVEN bundle order —
250
+ * the manifest order for analyzeProfile, or a candidate order for trial
251
+ * validation (src/trial.ts). Bundle resolution mirrors the boot exactly:
252
+ * the dsh installation anchor first (in-box bundles always come from the
253
+ * running dsh, never a profile-local copy), then Node's module search from
254
+ * the profile directory (covers community bundles and pnpm workspace-root
255
+ * hoisting). A single code path keeps the check report and the trial
256
+ * validation from ever disagreeing about what a bundle is or where it lives.
257
+ */
258
+ export declare function buildBundleLayers(profileDirectory: string, bundleNames: string[], specs: Record<string, string>, dshInstallDir: string | null): {
259
+ bundles: BundleLayer[];
260
+ layers: LayerInput[];
261
+ };
262
+ /**
263
+ * Analyze one profile directory (issue #98, phase 1). Pure function of the
264
+ * directory contents — safe to call on every market open.
265
+ */
266
+ export declare function analyzeProfile(profileDirectory: string, options?: CheckOptions): CheckReport;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Host-contract compatibility preflight for #195.
3
+ *
4
+ * Pure evaluation of what `analyzeProfile()` already reports: a confirmed
5
+ * peer mismatch (`satisfied === false`) is translated into a directional
6
+ * verdict:
7
+ *
8
+ * - `belowMin`: the resolved version is older than every alternative's lower
9
+ * bound — the environment is too old for the plugin's declared contract.
10
+ * - `aboveMax`: the resolved version is newer than every alternative's upper
11
+ * bound (or the exact pin). This is only a risk when the author expressed
12
+ * an explicit upper bound or exact pin; otherwise it is a warning, because
13
+ * the ecosystem currently has many sloppy `^0.0.1`-style declarations that
14
+ * work in practice.
15
+ *
16
+ * Everything else stays informational: `*`, prerelease-vs-`*` artifacts,
17
+ * unparseable ranges, and optional peers never produce a risk here.
18
+ */
19
+ import { type CheckOptions, type DuplicateName, type PeerRisk, type PeerWarning, type PeerVerdict } from './check.ts';
20
+ export type CompatibilityRisk = PeerRisk;
21
+ export type CompatibilityWarning = PeerWarning;
22
+ /** Re-exported so consumers can keep importing PeerVerdict from this module. */
23
+ export type { PeerVerdict };
24
+ export interface CompatibilityAssessment {
25
+ risks: CompatibilityRisk[];
26
+ warnings: CompatibilityWarning[];
27
+ /**
28
+ * Cross-layer duplicate loader NAMES, carried through from the same
29
+ * `analyzeProfile` run the peer checks already pay for (#230).
30
+ *
31
+ * The report has always computed these and deliberately kept them out of
32
+ * `summary.warnings`, because flagging an already-messy but working
33
+ * profile is a false positive nobody can act on. Diffing before against
34
+ * after is what makes them actionable: a collision this operation
35
+ * INTRODUCED is one the operation can also undo.
36
+ */
37
+ duplicateNames: DuplicateName[];
38
+ }
39
+ /** Translate one confirmed peer mismatch into a directional verdict. */
40
+ export declare function classifyPeer(plugin: string, peer: string, range: string, resolved: string | null, optional: boolean): PeerVerdict;
41
+ /** Whether a peer is declared optional in the installed plugin manifest. */
42
+ export declare function isOptionalPeer(profileDirectory: string, plugin: string, peer: string): boolean;
43
+ /** Evaluate the current profile with the same machinery `/dsh-market/check` uses. */
44
+ export declare function assessCompatibility(profileDirectory: string, options?: CheckOptions): CompatibilityAssessment;
45
+ /** Risks present after a mutation but absent before it. */
46
+ export declare function introducedRisks(before: CompatibilityAssessment, after: CompatibilityAssessment): CompatibilityRisk[];
47
+ /**
48
+ * Cross-layer name collisions present after a mutation but absent before it
49
+ * (#230 by @dxc-dxc).
50
+ *
51
+ * Keyed by NAME alone, not by the layer set: a collision the operation made
52
+ * worse — same name, now shadowing across one more layer — is still the same
53
+ * collision the profile already had, and re-reporting it would put the
54
+ * operator back in front of a problem they did not just cause.
55
+ *
56
+ * This is what makes surfacing these safe at all. The underlying
57
+ * `duplicateNames` is informational precisely because a healthy-but-messy
58
+ * profile can carry collisions indefinitely; only the newly introduced ones
59
+ * are attributable to the install that just ran, and therefore undoable by
60
+ * rolling it back.
61
+ */
62
+ export declare function introducedDuplicateNames(before: CompatibilityAssessment, after: CompatibilityAssessment): DuplicateName[];
63
+ /** Convenience wrapper matching the profile helper signature. */
64
+ export declare function assessProfile(profile: string, explicitDir?: string): CompatibilityAssessment;
@@ -0,0 +1,29 @@
1
+ /** Versioned, read-only diagnostics shared by the host route and client. */
2
+ export declare const DIAGNOSTIC_SCHEMA: 'dsh-market/diagnostics/v1';
3
+ /** Conservative first set of known, identity-sensitive host contracts. */
4
+ export declare const KNOWN_SHARED_HOST_PACKAGES: readonly ['@deepseek-ai/cordis', '@deepseek-ai/dsh-attachment', '@deepseek-ai/dsh-llm', '@deepseek-ai/dsh-system-prompt', '@deepseek-ai/dsh-tools'];
5
+ export interface PackageManifestFact {
6
+ packageName: string;
7
+ manifest: unknown;
8
+ }
9
+ export interface SharedHostPackageDependencyFinding {
10
+ code: 'shared-host-package-dependency';
11
+ severity: 'warning';
12
+ subject: {
13
+ kind: 'package';
14
+ name: string;
15
+ };
16
+ evidence: {
17
+ basis: 'manifest-declaration';
18
+ dependency: string;
19
+ declaredRange: string;
20
+ declaredIn: 'dependencies';
21
+ };
22
+ }
23
+ export type DiagnosticFinding = SharedHostPackageDependencyFinding;
24
+ export interface DiagnosticReportV1 {
25
+ schema: typeof DIAGNOSTIC_SCHEMA;
26
+ findings: DiagnosticFinding[];
27
+ }
28
+ /** Build a stable diagnostic envelope from installed package manifests. */
29
+ export declare function diagnosePackageManifests(packages: readonly PackageManifestFact[]): DiagnosticReportV1;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Discovery-time DSH compatibility metadata.
3
+ *
4
+ * The public catalog does not carry npm manifests. Fetching every manifest
5
+ * while the market opens would turn one catalog request into more than a
6
+ * thousand registry requests, so this module supplies a bounded, on-demand
7
+ * index. Successful public manifest facts are cached beside the market's
8
+ * profile state; conclusions are never cached because they depend on the DSH
9
+ * version of the process serving the page.
10
+ */
11
+ export type HostCompatibilityStatus = 'compatible' | 'incompatible' | 'unknown';
12
+ export type HostCompatibilityBasis = 'manifest' | 'undeclared' | 'unavailable';
13
+ export interface HostRequirementDeclaration {
14
+ kind: 'engine' | 'peer';
15
+ /** Present only for a peer-derived declaration. */
16
+ package?: string;
17
+ range: string;
18
+ }
19
+ export interface HostCompatibility {
20
+ status: HostCompatibilityStatus;
21
+ basis: HostCompatibilityBasis;
22
+ /** Human-readable intersection of every raw declaration. */
23
+ requirement: string | null;
24
+ declarations: HostRequirementDeclaration[];
25
+ }
26
+ export interface NpmManifestFacts {
27
+ version: string | null;
28
+ enginesDsh: string | null;
29
+ peerDependencies: Record<string, string>;
30
+ }
31
+ type FetchLike = (url: string, init?: {
32
+ signal?: AbortSignal;
33
+ headers?: Record<string, string>;
34
+ }) => Promise<Response>;
35
+ /** Keep only the small public subset of an npm manifest needed by discovery. */
36
+ export declare function manifestFacts(value: unknown): NpmManifestFacts;
37
+ /**
38
+ * Derive the current host verdict from raw manifest facts.
39
+ *
40
+ * Every valid declaration is conjunctive: an explicit `engines.dsh` and all
41
+ * host peers must agree. A malformed declaration keeps a passing result
42
+ * unknown, but cannot erase a definite mismatch from another declaration.
43
+ */
44
+ export declare function deriveHostCompatibility(facts: NpmManifestFacts | null, hostVersion: string | null, hostPackages: ReadonlySet<string>): HostCompatibility;
45
+ /**
46
+ * Durable, bounded lookup of npm `latest` manifests.
47
+ *
48
+ * Failed requests are intentionally memory-only and short-lived: a mirror
49
+ * outage must not become a day-long false "undeclared" result on disk.
50
+ */
51
+ export declare class DiscoveryManifestIndex {
52
+ private readonly cacheFile;
53
+ private readonly entries;
54
+ private readonly failures;
55
+ private readonly inflight;
56
+ private readonly fetcher;
57
+ private readonly now;
58
+ private readonly ttlMs;
59
+ private readonly concurrency;
60
+ private loaded;
61
+ private consecutiveFailures;
62
+ private unavailableUntil;
63
+ private dirty;
64
+ private activeFetches;
65
+ private readonly fetchWaiters;
66
+ constructor(cacheFile: string, options?: {
67
+ fetcher?: FetchLike;
68
+ now?: () => number;
69
+ ttlMs?: number;
70
+ concurrency?: number;
71
+ });
72
+ private load;
73
+ private persist;
74
+ /** One semaphore for the whole index, including overlapping HTTP batches. */
75
+ private withFetchPermit;
76
+ private fetchOne;
77
+ /** Look up a bounded batch while never exceeding the configured fan-out. */
78
+ lookup(names: readonly string[], registry: string): Promise<Record<string, NpmManifestFacts | null>>;
79
+ }
80
+ export {};