@michengai/dsh-codex-suite 0.1.13 → 0.1.14

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # DSH Codex Suite
2
2
 
3
- `@michengai/dsh-codex-suite` is the one-click combo: Codex UI, expert management, skill management, archive management, IM Assistant, and scheduled tasks.
3
+ `@michengai/dsh-codex-suite` is a one-click installer that adds Codex UI, expert management, skill management, archive management, IM Assistant, and scheduled tasks to one profile as six **direct dependencies**.
4
4
 
5
5
  ## Plugin combo
6
6
 
@@ -13,47 +13,49 @@
13
13
  | IM Assistant | `@michengai/dsh-im-connect` |
14
14
  | Scheduled tasks | `@michengai/dsh-automation` |
15
15
 
16
- `dshmarket` is not part of the suite. Install it separately when needed.
16
+ Each Suite release pins one exact, verified member set. Making the members direct dependencies lets **Settings → About** detect, install, and update them independently. `dshmarket` remains optional.
17
17
 
18
- Each suite release pins an exact, tested version of every member plugin. Component upgrades are delivered by publishing a new suite version rather than resolving floating `latest` dependencies during installation.
18
+ ## One-click installation
19
19
 
20
- ## Installation
21
-
22
- `dsh plugin add` forwards to `pnpm add` in the profile directory. Without a version and official registry, a local mirror or minimum-release-age policy can leave you on an older build.
23
-
24
- Run this from any PowerShell directory:
20
+ Node.js 22+ is required, and the current DSH `dsh` command must be on PATH. Run from any directory:
25
21
 
26
22
  ```powershell
27
23
  [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
28
24
  $OutputEncoding = [System.Text.Encoding]::UTF8
29
- dsh plugin --profile web add @michengai/dsh-codex-suite@latest --registry=https://registry.npmjs.org/
30
- dsh --profile web --dump-config
25
+ npx --yes @michengai/dsh-codex-suite@latest --profile web
31
26
  ```
32
27
 
33
- If `dsh` is not on PATH, replace the leading `dsh` with `npx --yes @deepseek-ai/dsh`.
28
+ The installer:
29
+
30
+ 1. installs all six exact member versions from the official npm registry in one `dsh plugin add`;
31
+ 2. records every member as a direct profile dependency and bundle;
32
+ 3. promotes all members before removing a legacy aggregate Suite dependency;
33
+ 4. validates the result with `dsh --profile web --dump-config`.
34
+
35
+ Restart DSH Web and hard-refresh the browser afterwards. Set `DSH_BIN` when `dsh` is not on PATH.
34
36
 
35
- Restart DSH Web and hard-refresh the browser. The configuration output should contain `codex-ui`, `agency-agents`, `skills-manager`, `archive-manager`, `im-connect`, and `dsh-automation`.
37
+ ## New custom Web profile
36
38
 
37
- Individual and aggregate installation are mutually exclusive. If the profile already has any of the six plugins, uninstall them before adding the suite.
39
+ Keep the same `DSH_HOME` and choose another profile name:
40
+
41
+ ```powershell
42
+ npx --yes @michengai/dsh-codex-suite@latest --profile codex
43
+ ```
44
+
45
+ DSH supplies `@deepseek-ai/dsh-base` when it creates a custom profile. The installer places DSH's built-in `@deepseek-ai/dsh-web-app` before the six member bundles without creating a separate Home or reinstalling the official Web package from npm.
38
46
 
39
47
  ## Install Codex UI only
40
48
 
41
49
  ```powershell
42
- [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
43
- $OutputEncoding = [System.Text.Encoding]::UTF8
44
50
  dsh plugin --profile web add @michengai/dsh-codex-ui@latest --registry=https://registry.npmjs.org/
45
51
  ```
46
52
 
47
53
  ## Local verification
48
54
 
49
- From the repository root:
55
+ Preview the installation plan from a repository checkout:
50
56
 
51
57
  ```powershell
52
- [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
53
- $OutputEncoding = [System.Text.Encoding]::UTF8
54
- dsh plugin --profile web add .\packages\dsh-codex-suite
58
+ node .\packages\dsh-codex-suite\bin.mjs --profile codex --dry-run
55
59
  ```
56
60
 
57
- ## pnpm layout
58
-
59
- DSH resolves the suite patch from the profile root. If the profile uses a strict pnpm layout, set `nodeLinker: hoisted` in the profile `pnpm-workspace.yaml` and reinstall. Otherwise the packages may install but fail to resolve at startup.
61
+ Remove `--dry-run` to apply it. The checkout path resolves the local `workspace:*` entry to the root package version.
package/README.zh-CN.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DSH Codex Suite
2
2
 
3
- `@michengai/dsh-codex-suite` 是一键套件:一次安装 Codex UI、专家、技能、归档、IM 助理和定时任务。
3
+ `@michengai/dsh-codex-suite` 是一键安装器:把 Codex UI、专家、技能、归档、IM 助理和定时任务作为目标 profile 的六个**直接依赖**安装。
4
4
 
5
5
  ## 插件组合
6
6
 
@@ -13,49 +13,49 @@
13
13
  | IM 助理 | `@michengai/dsh-im-connect` |
14
14
  | 定时任务 | `@michengai/dsh-automation` |
15
15
 
16
- `dshmarket` 不在套件内,需要时单独安装。
16
+ 每个 Suite 版本锁定一组经过验证的成员精确版本。成员成为直接依赖后,「设置 → 关于」可以分别检测、安装和升级它们。`dshmarket` 不在套件内,需要时单独安装。
17
17
 
18
- 每个套件版本都会锁定一组经过验证的子插件精确版本。子插件升级通过发布新的套件版本统一交付,不在用户安装时动态解析浮动的 `latest`。
18
+ ## 一键安装
19
19
 
20
- ## 安装
21
-
22
- `dsh plugin add` 会转发到 profile 目录里的 `pnpm add`。不写版本、不指定官方源时,本机镜像和最短发布间隔可能让你停在旧版。
23
-
24
- 在任意 PowerShell 目录执行:
20
+ 需要 Node.js 22+,并确保当前 DSH 的 `dsh` 命令在 PATH 中。在任意目录执行:
25
21
 
26
22
  ```powershell
27
23
  [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
28
24
  $OutputEncoding = [System.Text.Encoding]::UTF8
29
- dsh plugin --profile web add @michengai/dsh-codex-suite@latest --registry=https://registry.npmjs.org/
30
- dsh --profile web --dump-config
25
+ npx --yes @michengai/dsh-codex-suite@latest --profile web
31
26
  ```
32
27
 
33
- 未把 `dsh` 装进 PATH 时,把开头的 `dsh` 换成 `npx --yes @deepseek-ai/dsh`。
28
+ 安装器会:
34
29
 
35
- 重启 DSH Web 并在浏览器硬刷新。配置输出中应包含 `codex-ui`、`agency-agents`、`skills-manager`、`archive-manager`、`im-connect` `dsh-automation`。
30
+ 1. 用官方 npm 源和一次 `dsh plugin add` 安装六个精确版本成员;
31
+ 2. 将成员写成 profile 的直接 dependencies 与 bundles;
32
+ 3. 若存在旧版聚合 Suite,先提升全部成员,再移除旧聚合依赖;
33
+ 4. 运行 `dsh --profile web --dump-config` 验证最终配置。
36
34
 
37
- 独立安装与聚合安装互斥。同一 profile 已单独安装任一子插件时,先卸载全部六个子插件,再安装本聚合包;不要让两套 patch 同时生效。
35
+ 完成后重启 DSH Web 并在浏览器硬刷新。若 `dsh` 不在 PATH,可通过 `DSH_BIN` 指定其可执行文件。
38
36
 
39
- ## 独立安装
37
+ ## 新建自定义 Web profile
40
38
 
41
- 不需要完整套件时,仍可独立安装 Codex UI:
39
+ 保持同一个 `DSH_HOME`,只需换一个 profile 名:
40
+
41
+ ```powershell
42
+ npx --yes @michengai/dsh-codex-suite@latest --profile codex
43
+ ```
44
+
45
+ DSH 创建自定义 profile 时会提供 `@deepseek-ai/dsh-base`。安装器会再把 DSH 自带的 `@deepseek-ai/dsh-web-app` 放到六个成员 bundle 之前,不会创建独立 Home,也不会通过 npm 重装官方 Web 包。
46
+
47
+ ## 只安装 Codex UI
42
48
 
43
49
  ```powershell
44
- [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
45
- $OutputEncoding = [System.Text.Encoding]::UTF8
46
50
  dsh plugin --profile web add @michengai/dsh-codex-ui@latest --registry=https://registry.npmjs.org/
47
51
  ```
48
52
 
49
53
  ## 本地验证
50
54
 
51
- 在仓库根目录执行:
55
+ 仓库检出状态下可先查看安装计划:
52
56
 
53
57
  ```powershell
54
- [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
55
- $OutputEncoding = [System.Text.Encoding]::UTF8
56
- dsh plugin --profile web add .\packages\dsh-codex-suite
58
+ node .\packages\dsh-codex-suite\bin.mjs --profile codex --dry-run
57
59
  ```
58
60
 
59
- ## pnpm 布局
60
-
61
- DSH 从 profile 顶层解析本包 patch 中的子插件。若 profile 使用 pnpm 严格隔离布局,请在 profile 的 `pnpm-workspace.yaml` 设置 `nodeLinker: hoisted` 后重新安装;否则子包可能安装成功但启动时无法解析。
61
+ 正式执行时去掉 `--dry-run`。安装器会读取工作区根包版本替换本地的 `workspace:*`。
package/bin.mjs ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { installSuite } from './installer.mjs'
4
+
5
+ try {
6
+ installSuite()
7
+ } catch (error) {
8
+ process.stderr.write(`dsh-codex-suite: ${error instanceof Error ? error.message : String(error)}\n`)
9
+ process.exitCode = 1
10
+ }
package/installer.mjs ADDED
@@ -0,0 +1,169 @@
1
+ import { spawnSync } from 'node:child_process'
2
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
3
+ import { homedir } from 'node:os'
4
+ import { resolve } from 'node:path'
5
+
6
+ export const SUITE_PACKAGE = '@michengai/dsh-codex-suite'
7
+ export const BASE_BUNDLE = '@deepseek-ai/dsh-base'
8
+ export const WEB_BUNDLE = '@deepseek-ai/dsh-web-app'
9
+
10
+ const suiteManifest = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'))
11
+
12
+ /** Keep the same layer order as the legacy aggregate patch. */
13
+ export const MEMBER_PACKAGES = [
14
+ '@michengai/dsh-archive-manager',
15
+ '@michengai/dsh-codex-ui',
16
+ '@michengai/dsh-skills-manager',
17
+ '@michengai/dsh-agency-agents',
18
+ '@michengai/dsh-im-connect',
19
+ '@michengai/dsh-automation',
20
+ ]
21
+
22
+ export function memberSpecs(manifest = suiteManifest) {
23
+ return MEMBER_PACKAGES.map((packageName) => {
24
+ let version = manifest.dependencies?.[packageName]
25
+ if (version?.startsWith('workspace:') === true && packageName === '@michengai/dsh-codex-ui') {
26
+ version = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version
27
+ }
28
+ if (typeof version !== 'string' || version === '' || version.startsWith('workspace:')) {
29
+ throw new Error(`Suite manifest does not contain a published version for ${packageName}.`)
30
+ }
31
+ return `${packageName}@${version}`
32
+ })
33
+ }
34
+
35
+ export function parseArgs(argv) {
36
+ const options = {
37
+ profile: 'web',
38
+ registry: 'https://registry.npmjs.org/',
39
+ dryRun: false,
40
+ help: false,
41
+ }
42
+ for (let index = 0; index < argv.length; index += 1) {
43
+ const argument = argv[index]
44
+ if (argument === '--help' || argument === '-h') options.help = true
45
+ else if (argument === '--dry-run') options.dryRun = true
46
+ else if (argument === '--profile') options.profile = argv[++index]
47
+ else if (argument.startsWith('--profile=')) options.profile = argument.slice('--profile='.length)
48
+ else if (argument === '--registry') options.registry = argv[++index]
49
+ else if (argument.startsWith('--registry=')) options.registry = argument.slice('--registry='.length)
50
+ else throw new Error(`Unknown argument: ${argument}`)
51
+ }
52
+ if (typeof options.profile !== 'string' || !/^[0-9A-Za-z][0-9A-Za-z._-]*$/.test(options.profile) || options.profile === 'node_modules') {
53
+ throw new Error(`Invalid DSH profile name: ${JSON.stringify(options.profile)}`)
54
+ }
55
+ let registry
56
+ try {
57
+ registry = new URL(options.registry)
58
+ } catch {
59
+ throw new Error(`Invalid npm registry URL: ${JSON.stringify(options.registry)}`)
60
+ }
61
+ if (registry.protocol !== 'https:' && registry.protocol !== 'http:') throw new Error('npm registry must use HTTP or HTTPS.')
62
+ if (registry.username !== '' || registry.password !== '' || registry.search !== '' || registry.hash !== '' || !/^[0-9A-Za-z._~/-]*$/.test(registry.pathname)) {
63
+ throw new Error('npm registry URL contains unsupported characters.')
64
+ }
65
+ options.registry = registry.href
66
+ return options
67
+ }
68
+
69
+ export function profileDirectory(profile, env = process.env, home = homedir()) {
70
+ const dshHome = env.DSH_HOME === undefined || env.DSH_HOME === '' ? resolve(home, '.dsh') : resolve(env.DSH_HOME)
71
+ return resolve(dshHome, 'profiles', profile)
72
+ }
73
+
74
+ export function normalizeBundles(bundles = []) {
75
+ const managed = new Set([BASE_BUNDLE, WEB_BUNDLE, SUITE_PACKAGE, ...MEMBER_PACKAGES])
76
+ const unrelated = bundles.filter((name, index) => typeof name === 'string' && !managed.has(name) && bundles.indexOf(name) === index)
77
+ return [BASE_BUNDLE, WEB_BUNDLE, ...unrelated, ...MEMBER_PACKAGES]
78
+ }
79
+
80
+ export function allowRequiredBuilds(source) {
81
+ const eol = source.includes('\r\n') ? '\r\n' : '\n'
82
+ const lines = source.split(/\r?\n/)
83
+ const start = lines.findIndex(line => line === 'allowBuilds:')
84
+ if (start === -1) return `${source}${source === '' || source.endsWith('\n') ? '' : eol}allowBuilds:${eol} protobufjs: false${eol} koffi: false${eol}`
85
+ let end = start + 1
86
+ while (end < lines.length && (lines[end] === '' || /^\s/.test(lines[end]))) end += 1
87
+ const body = lines.slice(start + 1, end).filter(line => !/^\s{2}(?:protobufjs|koffi):/.test(line))
88
+ lines.splice(start + 1, end - start - 1, ' protobufjs: false', ' koffi: false', ...body)
89
+ return lines.join(eol)
90
+ }
91
+
92
+ export function directInstallArgs(profile, registry, manifest = suiteManifest) {
93
+ return ['plugin', '--profile', profile, 'add', ...memberSpecs(manifest), '--save-exact', `--registry=${registry}`]
94
+ }
95
+
96
+ export function helpText() {
97
+ return `Usage: dsh-codex-suite [options]\n\nInstall every Codex Suite member as a direct dependency of one DSH profile.\n\nOptions:\n --profile <name> target profile (default: web)\n --registry <url> npm registry (default: https://registry.npmjs.org/)\n --dry-run print commands without changing the profile\n -h, --help show this help\n`
98
+ }
99
+
100
+ function quote(argument) {
101
+ return /[\s"']/u.test(argument) ? JSON.stringify(argument) : argument
102
+ }
103
+
104
+ function runDsh(args, { dryRun = false, command = process.env.DSH_BIN || 'dsh' } = {}) {
105
+ process.stdout.write(`> dsh ${args.map(quote).join(' ')}\n`)
106
+ if (dryRun) return
107
+ const result = process.platform === 'win32'
108
+ ? spawnSync(process.env.ComSpec || 'cmd.exe', ['/d', '/s', '/c', `call ${/\s/.test(command) ? `"${command.replaceAll('"', '""')}"` : command} ${args.join(' ')}`], { encoding: 'utf8', stdio: 'inherit', windowsHide: true })
109
+ : spawnSync(command, args, { encoding: 'utf8', stdio: 'inherit' })
110
+ if (result.error !== undefined) throw new Error(`Unable to run dsh: ${result.error.message}`)
111
+ if (result.status !== 0) throw new Error(`dsh exited with code ${result.status ?? 1}.`)
112
+ }
113
+
114
+ function readProfileManifest(path) {
115
+ return JSON.parse(readFileSync(path, 'utf8'))
116
+ }
117
+
118
+ function suiteDeclared(manifest) {
119
+ return manifest.dependencies?.[SUITE_PACKAGE] !== undefined
120
+ || manifest.devDependencies?.[SUITE_PACKAGE] !== undefined
121
+ || manifest.dsh?.profile?.bundles?.includes(SUITE_PACKAGE) === true
122
+ }
123
+
124
+ function legacyDirectPackages(manifest) {
125
+ const declared = { ...manifest.devDependencies, ...manifest.dependencies }
126
+ return [
127
+ ...(suiteDeclared(manifest) ? [SUITE_PACKAGE] : []),
128
+ ...(declared[WEB_BUNDLE] !== undefined ? [WEB_BUNDLE] : []),
129
+ ]
130
+ }
131
+
132
+ function writeNormalizedManifest(path) {
133
+ const manifest = readProfileManifest(path)
134
+ manifest.dsh ??= {}
135
+ manifest.dsh.profile ??= {}
136
+ manifest.dsh.profile.bundles = normalizeBundles(manifest.dsh.profile.bundles)
137
+ writeFileSync(path, `${JSON.stringify(manifest, undefined, 2)}\n`, 'utf8')
138
+ }
139
+
140
+ export function installSuite(argv = process.argv.slice(2)) {
141
+ const options = parseArgs(argv)
142
+ if (options.help) {
143
+ process.stdout.write(helpText())
144
+ return
145
+ }
146
+ const profilePath = profileDirectory(options.profile)
147
+ const manifestPath = resolve(profilePath, 'package.json')
148
+ const workspacePath = resolve(profilePath, 'pnpm-workspace.yaml')
149
+ if (!existsSync(manifestPath)) runDsh(['plugin', '--profile', options.profile, 'install', '--lockfile-only', '--ignore-scripts'], options)
150
+ if (options.dryRun) {
151
+ process.stdout.write(`> ensure protobufjs build scripts stay explicitly disabled in ${workspacePath}\n`)
152
+ } else {
153
+ const workspace = existsSync(workspacePath) ? readFileSync(workspacePath, 'utf8') : ''
154
+ const nextWorkspace = allowRequiredBuilds(workspace)
155
+ if (nextWorkspace !== workspace) writeFileSync(workspacePath, nextWorkspace, 'utf8')
156
+ }
157
+ runDsh(directInstallArgs(options.profile, options.registry), options)
158
+ if (options.dryRun) {
159
+ process.stdout.write(`> ensure ${WEB_BUNDLE} precedes the six member bundles in ${manifestPath}\n`)
160
+ process.stdout.write(`> remove legacy ${SUITE_PACKAGE} and redundant direct ${WEB_BUNDLE} dependencies when present\n`)
161
+ return
162
+ }
163
+ const installedManifest = readProfileManifest(manifestPath)
164
+ const legacyPackages = legacyDirectPackages(installedManifest)
165
+ if (legacyPackages.length > 0) runDsh(['plugin', '--profile', options.profile, 'remove', ...legacyPackages], options)
166
+ writeNormalizedManifest(manifestPath)
167
+ runDsh(['--profile', options.profile, '--dump-config'], options)
168
+ process.stdout.write(`\nCodex Suite members are direct plugins in profile ${options.profile}. Restart DSH and hard-refresh the browser.\n`)
169
+ }
package/package.json CHANGED
@@ -1,14 +1,22 @@
1
1
  {
2
2
  "name": "@michengai/dsh-codex-suite",
3
- "version": "0.1.13",
4
- "description": "DSH Codex UI 聚合包:一键安装侧栏、专家、技能、归档、IM 助理和定时任务",
3
+ "version": "0.1.14",
4
+ "description": "DSH Codex UI 一键安装器:将侧栏、专家、技能、归档、IM 助理和定时任务装为直接插件",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
+ "engines": {
8
+ "node": ">=22"
9
+ },
7
10
  "files": [
11
+ "bin.mjs",
12
+ "installer.mjs",
8
13
  "cordis.patch.yml",
9
14
  "README.md",
10
15
  "README.zh-CN.md"
11
16
  ],
17
+ "bin": {
18
+ "dsh-codex-suite": "./bin.mjs"
19
+ },
12
20
  "dsh": {
13
21
  "bundle": {
14
22
  "patch": "./cordis.patch.yml"
@@ -24,6 +32,6 @@
24
32
  "@michengai/dsh-automation": "0.1.14",
25
33
  "@michengai/dsh-im-connect": "0.1.23",
26
34
  "@michengai/dsh-skills-manager": "0.1.24",
27
- "@michengai/dsh-codex-ui": "0.2.81"
35
+ "@michengai/dsh-codex-ui": "0.2.82"
28
36
  }
29
37
  }