dshmarket 1.2.2 → 1.2.3
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 +8 -0
- package/README.zh.md +8 -0
- package/client/client.js +106 -71
- package/client/client.js.map +1 -1
- package/lib/dsh-cli.js +164 -0
- package/lib/http.js +39 -0
- package/lib/install.js +110 -0
- package/lib/pnpm-compat.js +72 -0
- package/lib/profile.js +121 -0
- package/lib/routes.js +63 -552
- package/lib/sources.js +85 -0
- package/lib/themes.js +102 -0
- package/lib/types/dsh-cli.d.ts +61 -0
- package/lib/types/http.d.ts +12 -0
- package/lib/types/install.d.ts +49 -0
- package/lib/types/pnpm-compat.d.ts +42 -0
- package/lib/types/profile.d.ts +24 -0
- package/lib/types/routes.d.ts +7 -21
- package/lib/types/sources.d.ts +42 -0
- package/lib/types/themes.d.ts +40 -0
- package/lib/types/updates.d.ts +16 -0
- package/lib/updates.js +61 -0
- package/package.json +5 -2
- package/src/client/Market.module.css +4 -1
- package/src/client/MarketSection.tsx +23 -27
- package/src/client/locales.ts +2 -0
- package/src/client/market-data.ts +60 -4
- package/src/dsh-cli.ts +194 -0
- package/src/http.ts +41 -0
- package/src/install.ts +121 -0
- package/src/pnpm-compat.ts +83 -0
- package/src/profile.ts +120 -0
- package/src/routes.ts +68 -583
- package/src/sources.ts +84 -0
- package/src/themes.ts +120 -0
- package/src/updates.ts +70 -0
package/src/profile.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile filesystem reads — everything the market learns from a dsh
|
|
3
|
+
* profile directory (manifest, lockfile, installed package trees). Pure
|
|
4
|
+
* functions of the directory contents; no processes, no network.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
8
|
+
import { homedir } from 'node:os'
|
|
9
|
+
import { join } from 'node:path'
|
|
10
|
+
|
|
11
|
+
/** Resolve a profile name to its directory under DSH_HOME (default ~/.dsh). */
|
|
12
|
+
export function profileDir(profile: string): string {
|
|
13
|
+
const home = process.env.DSH_HOME ?? join(homedir(), '.dsh')
|
|
14
|
+
return join(home, 'profiles', profile)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Community dependencies of the profile (official in-box scope filtered out). */
|
|
18
|
+
export function readInstalled(profile: string): Record<string, string> {
|
|
19
|
+
try {
|
|
20
|
+
const manifest = JSON.parse(readFileSync(join(profileDir(profile), 'package.json'), 'utf8')) as {
|
|
21
|
+
dependencies?: Record<string, string>
|
|
22
|
+
}
|
|
23
|
+
const installed: Record<string, string> = {}
|
|
24
|
+
for (const [name, spec] of Object.entries(manifest.dependencies ?? {})) {
|
|
25
|
+
if (!name.startsWith('@deepseek-ai/')) installed[name] = spec
|
|
26
|
+
}
|
|
27
|
+
return installed
|
|
28
|
+
} catch {
|
|
29
|
+
return {}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The version actually present in the profile's node_modules, or null. */
|
|
34
|
+
export function readInstalledVersion(profile: string, name: string): string | null {
|
|
35
|
+
try {
|
|
36
|
+
const manifest = JSON.parse(
|
|
37
|
+
readFileSync(join(profileDir(profile), 'node_modules', name, 'package.json'), 'utf8'),
|
|
38
|
+
) as { version?: string }
|
|
39
|
+
return manifest.version ?? null
|
|
40
|
+
} catch {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Pinned commit per `owner/repo` from the profile lockfile's codeload tarball URLs. */
|
|
46
|
+
export function readLockCommits(profile: string): Map<string, string> {
|
|
47
|
+
const commits = new Map<string, string>()
|
|
48
|
+
try {
|
|
49
|
+
const lock = readFileSync(join(profileDir(profile), 'pnpm-lock.yaml'), 'utf8')
|
|
50
|
+
for (const m of lock.matchAll(/codeload\.github\.com\/([^/\s]+\/[^/\s]+)\/tar\.gz\/([0-9a-f]{40})/g)) {
|
|
51
|
+
commits.set(m[1].toLowerCase(), m[2])
|
|
52
|
+
}
|
|
53
|
+
} catch { /* no lockfile — no git installs to report */ }
|
|
54
|
+
return commits
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** True when the installed package's manifest declares a dsh plugin surface. */
|
|
58
|
+
export function hasDshManifest(dir: string): boolean {
|
|
59
|
+
try {
|
|
60
|
+
const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as { dsh?: unknown }
|
|
61
|
+
return manifest.dsh !== undefined
|
|
62
|
+
} catch {
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* True when the package's declared entry artifact actually exists — github
|
|
69
|
+
* source checkouts of build-required plugins ship no lib/, and promoting one
|
|
70
|
+
* into the bundle layer bricks the next boot (ERR_MODULE_NOT_FOUND kills the
|
|
71
|
+
* whole profile, #18).
|
|
72
|
+
*/
|
|
73
|
+
export function entryArtifactExists(dir: string): boolean {
|
|
74
|
+
try {
|
|
75
|
+
const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as {
|
|
76
|
+
main?: string
|
|
77
|
+
exports?: Record<string, unknown> | string
|
|
78
|
+
}
|
|
79
|
+
const candidates: string[] = []
|
|
80
|
+
if (typeof manifest.main === 'string') candidates.push(manifest.main)
|
|
81
|
+
const rootExport = typeof manifest.exports === 'string'
|
|
82
|
+
? manifest.exports
|
|
83
|
+
: (manifest.exports as Record<string, unknown> | undefined)?.['.']
|
|
84
|
+
if (typeof rootExport === 'string') candidates.push(rootExport)
|
|
85
|
+
else if (rootExport !== null && typeof rootExport === 'object') {
|
|
86
|
+
for (const value of Object.values(rootExport)) if (typeof value === 'string') candidates.push(value)
|
|
87
|
+
}
|
|
88
|
+
if (candidates.length === 0) candidates.push('index.js')
|
|
89
|
+
return candidates.some(rel => existsSync(join(dir, rel)))
|
|
90
|
+
} catch {
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Plugin subdirectories (depth 2) of a collection checkout, as relative paths. */
|
|
96
|
+
export function pluginSubdirs(root: string): string[] {
|
|
97
|
+
const found: string[] = []
|
|
98
|
+
let level1: string[] = []
|
|
99
|
+
try {
|
|
100
|
+
level1 = readdirSync(root, { withFileTypes: true })
|
|
101
|
+
.filter(dirent => dirent.isDirectory() && /^[A-Za-z0-9_.-]+$/.test(dirent.name) && dirent.name !== 'node_modules')
|
|
102
|
+
.map(dirent => dirent.name)
|
|
103
|
+
} catch {
|
|
104
|
+
return found
|
|
105
|
+
}
|
|
106
|
+
for (const sub of level1) {
|
|
107
|
+
if (hasDshManifest(join(root, sub))) {
|
|
108
|
+
found.push(sub)
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
for (const inner of readdirSync(join(root, sub), { withFileTypes: true })) {
|
|
113
|
+
if (!inner.isDirectory() || !/^[A-Za-z0-9_.-]+$/.test(inner.name) || inner.name === 'node_modules') continue
|
|
114
|
+
if (hasDshManifest(join(root, sub, inner.name))) found.push(`${sub}/${inner.name}`)
|
|
115
|
+
}
|
|
116
|
+
} catch { /* unreadable level — skip */ }
|
|
117
|
+
if (found.length >= 8) break
|
|
118
|
+
}
|
|
119
|
+
return found.slice(0, 8)
|
|
120
|
+
}
|