dshmarket 1.66.30 → 1.66.31
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/profile.js +49 -1
- package/lib/verify.js +23 -9
- package/package.json +1 -1
- package/src/profile.ts +51 -1
- package/src/verify.ts +28 -14
package/lib/profile.js
CHANGED
|
@@ -139,7 +139,13 @@ export function readManifestDeps(profile, explicitDir) {
|
|
|
139
139
|
export function readDependencyOwners(profile, names, explicitDir) {
|
|
140
140
|
const installed = new Set(names);
|
|
141
141
|
const owners = {};
|
|
142
|
-
|
|
142
|
+
// Who may be an OWNER is not the same question as who gets a card (#892).
|
|
143
|
+
// pnpm hoists a transitive dependency's directory to `node_modules/<name>`
|
|
144
|
+
// as well, so `ws` — a dependency of a plugin, never a profile dependency —
|
|
145
|
+
// is where `bufferutil` and `utf-8-validate` are declared. Scanning only the
|
|
146
|
+
// profile's own entries could therefore never answer "who asked for these",
|
|
147
|
+
// and the two native peers stayed on the list as plugins nobody chose.
|
|
148
|
+
for (const owner of topLevelPackageNames(profile, names, explicitDir)) {
|
|
143
149
|
const manifest = readInstalledManifest(profile, owner, explicitDir);
|
|
144
150
|
if (typeof manifest !== 'object' || manifest === null)
|
|
145
151
|
continue;
|
|
@@ -156,6 +162,48 @@ export function readDependencyOwners(profile, names, explicitDir) {
|
|
|
156
162
|
}
|
|
157
163
|
return owners;
|
|
158
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Every package that could be an owner, sorted: the profile's own entries
|
|
167
|
+
* plus every package sitting at the profile's top level.
|
|
168
|
+
*
|
|
169
|
+
* The manifest's keys are the floor, not the ceiling — a missing or
|
|
170
|
+
* unreadable `node_modules` leaves the answer exactly as it was before the
|
|
171
|
+
* top level was scanned, so this can only ever find MORE owners.
|
|
172
|
+
*
|
|
173
|
+
* One manifest read per top-level package, measured at ~30 ms for 300
|
|
174
|
+
* packages on a warm cache, once per `/installed` request — which already
|
|
175
|
+
* reads a manifest, a patch file and an entry artifact for every card.
|
|
176
|
+
*/
|
|
177
|
+
function topLevelPackageNames(profile, names, explicitDir) {
|
|
178
|
+
const found = new Set(names);
|
|
179
|
+
const root = join(profileDir(profile, explicitDir), 'node_modules');
|
|
180
|
+
let entries = [];
|
|
181
|
+
try {
|
|
182
|
+
entries = readdirSync(root, { withFileTypes: true })
|
|
183
|
+
// A scope directory is named `@scope`, so the leading `@` belongs here;
|
|
184
|
+
// the package's own namespace is added below.
|
|
185
|
+
.filter(dirent => dirent.isDirectory() && /^@?[A-Za-z0-9._~-]+$/.test(dirent.name))
|
|
186
|
+
.map(dirent => dirent.name);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return [...found].sort();
|
|
190
|
+
}
|
|
191
|
+
for (const entry of entries) {
|
|
192
|
+
if (!entry.startsWith('@')) {
|
|
193
|
+
found.add(entry);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
// A scope directory holds the packages themselves.
|
|
197
|
+
try {
|
|
198
|
+
for (const inner of readdirSync(join(root, entry), { withFileTypes: true })) {
|
|
199
|
+
if (inner.isDirectory() && /^[A-Za-z0-9._~-]+$/.test(inner.name))
|
|
200
|
+
found.add(`${entry}/${inner.name}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch { /* unreadable scope — its packages simply cannot own anything */ }
|
|
204
|
+
}
|
|
205
|
+
return [...found].sort();
|
|
206
|
+
}
|
|
159
207
|
function objectRecord(value) {
|
|
160
208
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
161
209
|
? value
|
package/lib/verify.js
CHANGED
|
@@ -33,7 +33,7 @@ import { join } from 'node:path';
|
|
|
33
33
|
import { listHotMounts, parseSimplePatch } from './hot.js';
|
|
34
34
|
import { userPatchPackageReferences } from './patch.js';
|
|
35
35
|
import { nameMatchesPackage } from './entry-identity.js';
|
|
36
|
-
import { bundlePatchInsertedIds, declaredBundlePatchFiles, hasDshManifest, hasLoadableEntry, profileDir, readInstalled } from './profile.js';
|
|
36
|
+
import { bundlePatchInsertedIds, declaredBundlePatchFiles, hasDshManifest, hasLoadableEntry, pluginSubdirs, profileDir, readInstalled } from './profile.js';
|
|
37
37
|
import { compareSemver, composeLayers, parsePatchFile, satisfiesRange } from './check.js';
|
|
38
38
|
import { dshHostInfo } from './dsh-install.js';
|
|
39
39
|
/**
|
|
@@ -422,19 +422,33 @@ export function verifyActivation(profile, name, live = new Set(listHotMounts()),
|
|
|
422
422
|
hot: false,
|
|
423
423
|
};
|
|
424
424
|
}
|
|
425
|
-
|
|
426
|
-
|
|
425
|
+
if (inBundles) {
|
|
426
|
+
return {
|
|
427
427
|
state: 'restart',
|
|
428
428
|
reasons: ['由你自己的 cordis.patch.yml 按名加载,重启后生效 / loaded by name from your own cordis.patch.yml — live after a restart'],
|
|
429
429
|
bundle: true,
|
|
430
430
|
hot: false,
|
|
431
|
-
}
|
|
432
|
-
: {
|
|
433
|
-
state: 'inert',
|
|
434
|
-
reasons: ['普通依赖(未声明 dsh 元数据),不是 profile 层插件;若它由某个 bundle patch 按名加载,启动后会显示为已加载 / a plain dependency with no dsh metadata — not a profile-layer plugin; if some bundle patch loads it by name it will read as live once running'],
|
|
435
|
-
bundle: false,
|
|
436
|
-
hot: false,
|
|
437
431
|
};
|
|
432
|
+
}
|
|
433
|
+
// A collection repository's ROOT package says nothing about itself and
|
|
434
|
+
// holds the plugins one level down (#892). Installed whole — which the
|
|
435
|
+
// market used to do before it learned to retarget one, and which a user
|
|
436
|
+
// can still do by hand — it lands on this list as "installed, not in
|
|
437
|
+
// effect" with no way out of that sentence. Name the plugins it does
|
|
438
|
+
// carry and the way to install one: the dead end is the bug.
|
|
439
|
+
const carried = pluginSubdirs(dir);
|
|
440
|
+
const shown = carried.slice(0, 3).join('、');
|
|
441
|
+
const more = carried.length > 3 ? `等 ${carried.length} 个` : '';
|
|
442
|
+
const shownEn = carried.slice(0, 3).join(', ');
|
|
443
|
+
const moreEn = carried.length > 3 ? ` and ${carried.length - 3} more` : '';
|
|
444
|
+
return {
|
|
445
|
+
state: 'inert',
|
|
446
|
+
reasons: [carried.length > 0
|
|
447
|
+
? `这是集合仓库的根包,本身不是插件。可安装的子包:${shown}${more}。请装子包而不是它(仓库地址后加 #path:/子包目录) / this is a collection repository's root package, not a plugin itself; install one of its plugins instead — ${shownEn}${moreEn} — by adding #path:/<subdirectory> to the repository URL`
|
|
448
|
+
: '普通依赖(未声明 dsh 元数据),不是 profile 层插件;若它由某个 bundle patch 按名加载,启动后会显示为已加载 / a plain dependency with no dsh metadata — not a profile-layer plugin; if some bundle patch loads it by name it will read as live once running'],
|
|
449
|
+
bundle: false,
|
|
450
|
+
hot: false,
|
|
451
|
+
};
|
|
438
452
|
}
|
|
439
453
|
// Carrier bundles (#103) ship no entry of their own — what they mount is
|
|
440
454
|
// the point — so judge by "is anything loadable", not by this package's
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dshmarket",
|
|
3
3
|
"description": "Visual plugin market inside DeepSeek Harness — browse, search, and one-click install community plugins. · DSH 可视化插件市场:逛一逛,点一下,装好。",
|
|
4
|
-
"version": "1.66.
|
|
4
|
+
"version": "1.66.31",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
package/src/profile.ts
CHANGED
|
@@ -151,7 +151,13 @@ export function readDependencyOwners(
|
|
|
151
151
|
): Record<string, string> {
|
|
152
152
|
const installed = new Set(names)
|
|
153
153
|
const owners: Record<string, string> = {}
|
|
154
|
-
|
|
154
|
+
// Who may be an OWNER is not the same question as who gets a card (#892).
|
|
155
|
+
// pnpm hoists a transitive dependency's directory to `node_modules/<name>`
|
|
156
|
+
// as well, so `ws` — a dependency of a plugin, never a profile dependency —
|
|
157
|
+
// is where `bufferutil` and `utf-8-validate` are declared. Scanning only the
|
|
158
|
+
// profile's own entries could therefore never answer "who asked for these",
|
|
159
|
+
// and the two native peers stayed on the list as plugins nobody chose.
|
|
160
|
+
for (const owner of topLevelPackageNames(profile, names, explicitDir)) {
|
|
155
161
|
const manifest = readInstalledManifest(profile, owner, explicitDir)
|
|
156
162
|
if (typeof manifest !== 'object' || manifest === null) continue
|
|
157
163
|
const declared = manifest as { dependencies?: unknown; peerDependencies?: unknown }
|
|
@@ -166,6 +172,50 @@ export function readDependencyOwners(
|
|
|
166
172
|
return owners
|
|
167
173
|
}
|
|
168
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Every package that could be an owner, sorted: the profile's own entries
|
|
177
|
+
* plus every package sitting at the profile's top level.
|
|
178
|
+
*
|
|
179
|
+
* The manifest's keys are the floor, not the ceiling — a missing or
|
|
180
|
+
* unreadable `node_modules` leaves the answer exactly as it was before the
|
|
181
|
+
* top level was scanned, so this can only ever find MORE owners.
|
|
182
|
+
*
|
|
183
|
+
* One manifest read per top-level package, measured at ~30 ms for 300
|
|
184
|
+
* packages on a warm cache, once per `/installed` request — which already
|
|
185
|
+
* reads a manifest, a patch file and an entry artifact for every card.
|
|
186
|
+
*/
|
|
187
|
+
function topLevelPackageNames(
|
|
188
|
+
profile: string,
|
|
189
|
+
names: readonly string[],
|
|
190
|
+
explicitDir?: string,
|
|
191
|
+
): string[] {
|
|
192
|
+
const found = new Set(names)
|
|
193
|
+
const root = join(profileDir(profile, explicitDir), 'node_modules')
|
|
194
|
+
let entries: string[] = []
|
|
195
|
+
try {
|
|
196
|
+
entries = readdirSync(root, { withFileTypes: true })
|
|
197
|
+
// A scope directory is named `@scope`, so the leading `@` belongs here;
|
|
198
|
+
// the package's own namespace is added below.
|
|
199
|
+
.filter(dirent => dirent.isDirectory() && /^@?[A-Za-z0-9._~-]+$/.test(dirent.name))
|
|
200
|
+
.map(dirent => dirent.name)
|
|
201
|
+
} catch {
|
|
202
|
+
return [...found].sort()
|
|
203
|
+
}
|
|
204
|
+
for (const entry of entries) {
|
|
205
|
+
if (!entry.startsWith('@')) {
|
|
206
|
+
found.add(entry)
|
|
207
|
+
continue
|
|
208
|
+
}
|
|
209
|
+
// A scope directory holds the packages themselves.
|
|
210
|
+
try {
|
|
211
|
+
for (const inner of readdirSync(join(root, entry), { withFileTypes: true })) {
|
|
212
|
+
if (inner.isDirectory() && /^[A-Za-z0-9._~-]+$/.test(inner.name)) found.add(`${entry}/${inner.name}`)
|
|
213
|
+
}
|
|
214
|
+
} catch { /* unreadable scope — its packages simply cannot own anything */ }
|
|
215
|
+
}
|
|
216
|
+
return [...found].sort()
|
|
217
|
+
}
|
|
218
|
+
|
|
169
219
|
/** Exact rollback state owned by one profile package operation. */
|
|
170
220
|
export interface ProfileManifestSnapshot {
|
|
171
221
|
dependencies: Record<string, string>
|
package/src/verify.ts
CHANGED
|
@@ -34,7 +34,7 @@ import { join } from 'node:path'
|
|
|
34
34
|
import { listHotMounts, parseSimplePatch } from './hot.ts'
|
|
35
35
|
import { userPatchPackageReferences } from './patch.ts'
|
|
36
36
|
import { nameMatchesPackage } from './entry-identity.ts'
|
|
37
|
-
import { bundlePatchInsertedIds, declaredBundlePatchFiles, hasDshManifest, hasLoadableEntry, profileDir, readInstalled } from './profile.ts'
|
|
37
|
+
import { bundlePatchInsertedIds, declaredBundlePatchFiles, hasDshManifest, hasLoadableEntry, pluginSubdirs, profileDir, readInstalled } from './profile.ts'
|
|
38
38
|
import { compareSemver, composeLayers, parsePatchFile, satisfiesRange } from './check.ts'
|
|
39
39
|
import { dshHostInfo } from './dsh-install.ts'
|
|
40
40
|
|
|
@@ -498,19 +498,33 @@ export function verifyActivation(
|
|
|
498
498
|
hot: false,
|
|
499
499
|
}
|
|
500
500
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
501
|
+
if (inBundles) {
|
|
502
|
+
return {
|
|
503
|
+
state: 'restart',
|
|
504
|
+
reasons: ['由你自己的 cordis.patch.yml 按名加载,重启后生效 / loaded by name from your own cordis.patch.yml — live after a restart'],
|
|
505
|
+
bundle: true,
|
|
506
|
+
hot: false,
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// A collection repository's ROOT package says nothing about itself and
|
|
510
|
+
// holds the plugins one level down (#892). Installed whole — which the
|
|
511
|
+
// market used to do before it learned to retarget one, and which a user
|
|
512
|
+
// can still do by hand — it lands on this list as "installed, not in
|
|
513
|
+
// effect" with no way out of that sentence. Name the plugins it does
|
|
514
|
+
// carry and the way to install one: the dead end is the bug.
|
|
515
|
+
const carried = pluginSubdirs(dir)
|
|
516
|
+
const shown = carried.slice(0, 3).join('、')
|
|
517
|
+
const more = carried.length > 3 ? `等 ${carried.length} 个` : ''
|
|
518
|
+
const shownEn = carried.slice(0, 3).join(', ')
|
|
519
|
+
const moreEn = carried.length > 3 ? ` and ${carried.length - 3} more` : ''
|
|
520
|
+
return {
|
|
521
|
+
state: 'inert',
|
|
522
|
+
reasons: [carried.length > 0
|
|
523
|
+
? `这是集合仓库的根包,本身不是插件。可安装的子包:${shown}${more}。请装子包而不是它(仓库地址后加 #path:/子包目录) / this is a collection repository's root package, not a plugin itself; install one of its plugins instead — ${shownEn}${moreEn} — by adding #path:/<subdirectory> to the repository URL`
|
|
524
|
+
: '普通依赖(未声明 dsh 元数据),不是 profile 层插件;若它由某个 bundle patch 按名加载,启动后会显示为已加载 / a plain dependency with no dsh metadata — not a profile-layer plugin; if some bundle patch loads it by name it will read as live once running'],
|
|
525
|
+
bundle: false,
|
|
526
|
+
hot: false,
|
|
527
|
+
}
|
|
514
528
|
}
|
|
515
529
|
// Carrier bundles (#103) ship no entry of their own — what they mount is
|
|
516
530
|
// the point — so judge by "is anything loadable", not by this package's
|