@crazx/dsh 0.1.6-alpha.1.zw.3 → 0.1.6-alpha.2.zw.2

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.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write apps/cli/README.md
5
- README.md: cfab7a4983c31a21b68157c9cefb38c1654571ff
6
- README.zh.md: 67d07fba1fc27bca9c2a696daddde88aac3aa39c
5
+ README.md: 46c6a66336839db443a825c66ebfd71dcce398af
6
+ README.zh.md: 14838114b04977e7668e33479d1ba84a8ddc8434
package/README.md CHANGED
@@ -8,13 +8,13 @@ The `dsh` command is the sole supported Node application launcher: profiles are
8
8
 
9
9
  | Command | Purpose |
10
10
  |---|---|
11
- | `dsh --profile <name>` | Boot the named profile under `$DSH_HOME/profiles/<name>`. |
11
+ | `dsh <name>` / `dsh --profile <name>` | Boot the named profile under `$DSH_HOME/profiles/<name>`. |
12
12
  | `dsh --profile <name> --from-default-profile <template>` | Create a new custom profile from a shipped template, then boot it. |
13
13
  | `dsh --profile acp` | Serve automation clients over ACP stdio until disconnect. |
14
14
  | `dsh --profile headless "job"` | Run one fresh persisted session, print the final answer, and exit. |
15
15
  | `dsh --profile sdk` | Serve SDK clients over JSON-RPC stdio until shutdown or disconnect. |
16
16
  | `dsh --profile sdk-minimal` | Serve SDK clients with the standalone minimal agent tree. |
17
- | `dsh web` | Alias of `--profile web`. |
17
+ | `dsh web` | Boot the Web profile. |
18
18
  | `dsh plugin --profile <name> <pnpm args>` | Manage a profile's plugins by forwarding to pnpm in the profile directory. |
19
19
 
20
20
  The invoking directory is the default workspace root. The `web`, `headless`, `sdk`, `sdk-minimal`, and `acp` profiles auto-initialize on first use from shipped templates. Create another profile at an unused, non-shipped name with `--from-default-profile`, or initialize a base-backed profile through `dsh plugin`. The `desktop` name is reserved for the Electron-owned profile, so the CLI rejects boot, config-dump, and plugin-management requests for it.
@@ -34,7 +34,7 @@ dsh --help # the launcher's own help
34
34
  <a id="profiles"></a>
35
35
  ## Profiles
36
36
 
37
- A profile directory holds a `package.json` (out-of-tree plugin dependencies plus the profile manifest `dsh.profile` with its ordered `bundles` list and `patchReload` lifecycle) and a `cordis.patch.yml` (the user's own patch layer). `patchReload: live` watches the profile and home-level patch files; `startup` applies them once.
37
+ A profile directory holds a `package.json` (out-of-tree plugin dependencies plus the profile manifest `dsh.profile` with its ordered `bundles` list) and a `cordis.patch.yml` (the user's own patch layer). `dsh-hmr`, when enabled in YAML, watches the profile manifest and both profile and home patch files, then recomposes all layers through one serialized reload. Without HMR, changes apply on restart. Edits arriving during watcher registration use the same nonfatal reload reporting as later edits. [Plugin Manager](../../packages/boot/plugin-manager/README.md) shares package operations and the profile write lock with `dsh plugin`; package updates retain disabled bundle selections. CLI package commands inherit authentication variables and terminal descriptors, including interactive build approval; service calls retain their scrubbed environment and captured diagnostics.
38
38
 
39
39
  The tree composes over an empty root:
40
40
  - each bundle's patch in `dsh.profile.bundles` order
@@ -55,4 +55,6 @@ The [CLI behavior reference](reference/README.md) owns exact layer precedence, f
55
55
 
56
56
  Production runs require built package and frontend artifacts. From the repository root, run `pnpm run build` separately, then use `pnpm dsh <args...>` to run the TypeScript entry and forward every argument; the [source-execution reference](reference/README.md#source-execution) owns the module-resolution contract.
57
57
 
58
+ The `@deepseek-ai/dsh/profile-boot` export provides the shared profile lifecycle to the Desktop host. A resolved application profile supplies its own installation anchor for runtime package resolution while retaining the Harness home patch, proxy environment, telemetry switch, patch reload, and bounded shutdown.
59
+
58
60
  The [Web failure matrix](tests/profiles/web/tests/web-failure-matrix.expected.e2e.ts) runs the built CLI through startup failures and native configuration HMR with `awaitWriteFinish` enabled in `test:expected`. It verifies authenticated HTTP responses, diagnostics, recovery, process exits, and disposal without model API calls; the [startup acceptance](tests/profiles/web/tests/web-best-effort-startup.expected.e2e.ts) also covers the shipped required Web dependencies and port conflicts.
package/README.zh.md CHANGED
@@ -8,13 +8,13 @@
8
8
 
9
9
  | 命令 | 用途 |
10
10
  |---|---|
11
- | `dsh --profile <name>` | 启动位于 `$DSH_HOME/profiles/<name>` 的指定 profile。 |
11
+ | `dsh <name>` / `dsh --profile <name>` | 启动位于 `$DSH_HOME/profiles/<name>` 的指定 profile。 |
12
12
  | `dsh --profile <name> --from-default-profile <template>` | 从随附模板创建新的自定义 profile,然后启动它。 |
13
13
  | `dsh --profile acp` | 通过 ACP stdio 为自动化客户端提供服务,直至断开连接。 |
14
14
  | `dsh --profile headless "job"` | 运行一个全新的持久化会话,打印最终答案并退出。 |
15
15
  | `dsh --profile sdk` | 通过 JSON-RPC stdio 为 SDK 客户端提供服务,直至关闭或断开连接。 |
16
16
  | `dsh --profile sdk-minimal` | 以独立极简 agent(智能体)配置树为 SDK 客户端提供服务。 |
17
- | `dsh web` | `--profile web` 的别名。 |
17
+ | `dsh web` | 启动 Web profile。 |
18
18
  | `dsh plugin --profile <name> <pnpm args>` | 通过在 profile 目录中转发给 pnpm 来管理该 profile 的插件。 |
19
19
 
20
20
  运行命令时所在的目录将作为默认 workspace 根目录。`web`、`headless`、`sdk`、`sdk-minimal` 和 `acp` profile 在首次使用时会从随附模板自动初始化。使用 `--from-default-profile` 可以基于这些模板之一,在尚未使用的非内置名称处创建其他 profile;通过 `dsh plugin` 则可以初始化一个以 base 为基础的 profile。`desktop` 名称保留给 Electron 持有的 profile,因此 CLI(命令行界面)会拒绝针对它的启动、配置 dump 和插件管理请求。
@@ -34,7 +34,7 @@ dsh --help # the launcher's own help
34
34
  <a id="profiles"></a>
35
35
  ## Profile
36
36
 
37
- profile 目录包含一个 `package.json`,其中记录树外插件依赖,以及 profile manifest(元数据清单)`dsh.profile`、其中按顺序排列的 `bundles` 列表与 `patchReload` 生命周期;还包含一个 `cordis.patch.yml`,其中保存用户自己的 patch 层。`patchReload: live` 监视 profile 与 home 级 patch 文件,`startup` 则只应用一次。
37
+ profile 目录包含一个 `package.json`,其中记录树外插件依赖,以及 profile manifest(元数据清单)`dsh.profile`、其中按顺序排列的 `bundles` 列表;还包含一个 `cordis.patch.yml`,其中保存用户自己的 patch 层。在 YAML 中启用的 `dsh-hmr` 监视 profile manifest、profile 与 home 级 patch 文件,再通过统一串行重载重新组合所有层。未启用 HMR 时,更改在重启后生效。监听器注册期间发生的编辑与后续编辑使用相同的非致命重载错误报告。[插件管理器](../../packages/boot/plugin-manager/README.zh.md) 与 `dsh plugin` 共享包操作和 profile 写锁;更新依赖会保留已停用的组合包选择。CLI 包操作继承认证环境和终端描述符,支持交互式构建批准;service 调用保留清理后的环境并捕获诊断。
38
38
 
39
39
  配置树以空根为起点,依次叠加以下配置层:
40
40
  - `dsh.profile.bundles` 中各组合包的 patch
@@ -55,4 +55,6 @@ profile 目录包含一个 `package.json`,其中记录树外插件依赖,以
55
55
 
56
56
  生产运行需要已构建的包与前端产物。请在仓库根目录单独运行 `pnpm run build`,然后使用 `pnpm dsh <args...>` 运行 TypeScript 入口并转发所有参数;模块解析约定以[源码执行参考](reference/README.zh.md#source-execution)为准。
57
57
 
58
+ `@deepseek-ai/dsh/profile-boot` 导出向 Desktop Host 提供共享 profile 生命周期。已解析的应用 profile 为运行时包解析指定自己的安装锚点,同时沿用 Harness home patch、代理环境、遥测开关、patch 热重载和有界关闭。
59
+
58
60
  [Web 失败矩阵](tests/profiles/web/tests/web-failure-matrix.expected.e2e.ts)在 `test:expected` 中通过构建后的 CLI 验证启动失败与启用 `awaitWriteFinish` 的原生配置 HMR。它不调用模型 API,而是检查经过认证的 HTTP 响应、诊断、恢复、进程退出与 dispose;[启动验收测试](tests/profiles/web/tests/web-best-effort-startup.expected.e2e.ts)还覆盖随附 Web 的必需依赖与端口冲突。
package/lib/bin.js CHANGED
@@ -1,8 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from "node:fs";
3
3
  import { fileURLToPath } from "node:url";
4
- import { loadLayeredEnv } from "@deepseek-ai/dsh-app-boot";
5
- import { Command, CommanderError } from "commander";
4
+ import { StartupError, loadLayeredEnv } from "@deepseek-ai/dsh-app-boot";
5
+ import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
6
+ import { Command, CommanderError, InvalidArgumentError } from "commander";
7
+ import { randomUUID } from "node:crypto";
8
+ import { mkdir, writeFile } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { inspect } from "node:util";
6
11
  //#region lib/types/args.js
7
12
  /**
8
13
  * Commander adapter for the `dsh` command line.
@@ -16,9 +21,9 @@ import { Command, CommanderError } from "commander";
16
21
  * `dsh --profile tui --resume abc` boots the tui profile with `--resume abc`,
17
22
  * and `dsh --profile web -h` prints the web app's help, not this one's.
18
23
  *
19
- * `web` is a hardcoded alias for `--profile web`; `plugin` manages a profile's
20
- * plugin dependencies by forwarding to pnpm. The fork adds `web:log` /
21
- * `web:log:tmp`, which boot the web profile while teeing output to a log file.
24
+ * `dsh <name>` abbreviates `dsh --profile <name>`; `plugin` manages a profile's
25
+ * plugin dependencies by forwarding to pnpm.
26
+ * The fork adds `web:log` / `web:log:tmp`, which boot the web profile while teeing output to a log file.
22
27
  * @module @deepseek-ai/dsh/args
23
28
  */
24
29
  /**
@@ -26,26 +31,30 @@ import { Command, CommanderError } from "commander";
26
31
  * variadic — a variadic `--patch` would swallow the inner arguments.
27
32
  */
28
33
  const collect = (value, previous = []) => [...previous, value];
34
+ function selectProfile(value, previous) {
35
+ if (previous !== void 0) throw new InvalidArgumentError("select a profile only once");
36
+ return value;
37
+ }
29
38
  function rejectElectronProfile(program, profile) {
30
39
  if (profile.toLowerCase() === "desktop") program.error("error: profile \"desktop\" is managed exclusively by the Electron application");
31
40
  }
32
41
  /** The launcher's own help text; each app prints its own. */
33
42
  const HELP_EXAMPLES = `
34
43
  Examples:
35
- dsh --profile web boot the web profile (same as: dsh web)
36
- dsh --profile rescue --from-default-profile web
37
- create rescue from the shipped web template, then boot it
38
- dsh --profile headless "run the tests" answer one task, print the result, and exit
39
- dsh --profile tui --patch ./extra.yml boot a custom profile with one extra overlay
40
- dsh --profile tui --resume <session> arguments after the launcher flags reach the app
41
- dsh --profile web --help the web app's own flags and help
42
- dsh plugin --profile tui add <package> install a plugin into the tui profile
44
+ dsh web boot the web profile (same as: dsh --profile web)
45
+ dsh rescue --from-default-profile web
46
+ create rescue from the shipped web template, then boot it
47
+ dsh headless "run the tests" answer one task, print the result, and exit
48
+ dsh tui --patch ./extra.yml boot a custom profile with one extra overlay
49
+ dsh tui --resume <session> arguments after the launcher flags reach the app
50
+ dsh web --help the web app's own flags and help
51
+ dsh plugin --profile tui add <package> install a plugin into the tui profile
43
52
  dsh web:log --port 0 boot web with output teed to a timestamped log file (fork-local)
44
53
  `;
45
54
  /**
46
55
  * Resolve a boot or dump invocation from the launcher flags and the leftover
47
56
  * inner arguments.
48
- * @param program - the command whose options were parsed (the root, or the `web` alias).
57
+ * @param program - the command whose options were parsed.
49
58
  * @param profile - the profile these flags boot.
50
59
  * @param options - the launcher flags commander collected.
51
60
  * @param args - the leftover arguments, in argv order.
@@ -82,9 +91,10 @@ function resolveBoot(program, profile, options, args) {
82
91
  * @returns the resolved invocation.
83
92
  */
84
93
  function parseDshArgs(argv, version) {
94
+ const first = argv[0];
85
95
  let resolved;
86
96
  const program = new Command();
87
- program.name("dsh").version(version, "-V, --version", "output the version number").description("dsh: boot a DeepSeek Harness profile — an ordered stack of plugin-bundle patch layers under your own overrides.").addHelpText("after", HELP_EXAMPLES).exitOverride().helpOption(false).allowUnknownOption().passThroughOptions().enablePositionalOptions().argument("[args...]", "arguments for the booted profile's app (see: dsh --profile <name> --help)").option("--profile <name>", "the profile under $DSH_HOME/profiles to boot").option("--from-default-profile <name>", "initialize a new custom profile from a shipped profile template").option("--patch <path>", "extra patch-list overlay applied after the profile layer (repeatable)", collect).option("--dump-config", "print the composed profile tree and exit").option("--dump-default-config", "print the profile tree without its user layer or --patch overlays and exit").action((args, options) => {
97
+ program.name("dsh").version(version, "-V, --version", "output the version number").usage("[--profile] <name> [options] [app-args...]\n dsh plugin --profile <name> <pnpm-args...>").description("dsh: boot a DeepSeek Harness profile — an ordered stack of plugin-bundle patch layers under your own overrides.").addHelpText("after", HELP_EXAMPLES).exitOverride().helpOption(false).helpCommand(false).allowUnknownOption().passThroughOptions().enablePositionalOptions().argument("[args...]", "arguments for the booted profile's app (see: dsh --profile <name> --help)").option("--profile <name>", "the profile under $DSH_HOME/profiles to boot", selectProfile).option("--from-default-profile <name>", "initialize a new custom profile from a shipped profile template").option("--patch <path>", "extra patch-list overlay applied after the profile layer (repeatable)", collect).option("--dump-config", "print the composed profile tree and exit").option("--dump-default-config", "print the profile tree without its user layer or --patch overlays and exit").action((args, options) => {
88
98
  if (options.profile === void 0) {
89
99
  if (args.some((argument) => argument === "-h" || argument === "--help")) program.help();
90
100
  program.error("error: --profile <name> is required");
@@ -99,11 +109,6 @@ function parseDshArgs(argv, version) {
99
109
  const parent = program.opts();
100
110
  if (parent.profile !== void 0 || parent.patch !== void 0 || parent.dumpConfig !== void 0 || parent.dumpDefaultConfig !== void 0 || parent.fromDefaultProfile !== void 0) program.error(`error: ${command} takes none of parent --profile, --from-default-profile, --patch, --dump-config, or --dump-default-config`);
101
111
  };
102
- const web = program.command("web").description("boot the web profile (alias of --profile web); the web app's own flags follow");
103
- web.helpOption(false).allowUnknownOption().passThroughOptions().enablePositionalOptions().argument("[args...]", "arguments for the web app (see: dsh web --help)").option("--patch <path>", "extra patch-list overlay applied after the profile layer (repeatable)", collect).option("--dump-config", "print the composed web-profile tree (with the user layer and any --patch) and exit").option("--dump-default-config", "print the web profile's bundle layers (no user layer) and exit").action((args, options) => {
104
- rejectParentOptions("web");
105
- resolved = resolveBoot(web, "web", options, args);
106
- });
107
112
  /**
108
113
  * Fork-local logged web boots: `dsh web:log` tees the web app's output to a
109
114
  * timestamped file under $DSH_HOME/logs, `dsh web:log:tmp` under the OS temp
@@ -124,20 +129,23 @@ function parseDshArgs(argv, version) {
124
129
  };
125
130
  registerWebLog("web:log", false);
126
131
  registerWebLog("web:log:tmp", true);
127
- const plugin = program.command("plugin").description("manage a profile's plugins by forwarding the remaining arguments to pnpm in the profile directory");
128
- plugin.requiredOption("--profile <name>", "the profile whose plugins to manage (initialized on first use)").allowUnknownOption().argument("[args...]", "pnpm arguments, forwarded verbatim (add <pkg>, remove <pkg>, why <pkg>, ...)").action((args, options) => {
129
- rejectParentOptions("plugin");
130
- if (options.profile === "") program.error("error: --profile needs a name");
131
- rejectElectronProfile(plugin, options.profile);
132
- if (args.length === 0) program.error("error: plugin needs pnpm arguments to forward (e.g. add <package>)");
133
- resolved = {
134
- mode: "plugin",
135
- profile: options.profile,
136
- args
137
- };
138
- });
132
+ if (first === "plugin") {
133
+ const plugin = program.command("plugin").description("manage a profile's plugins by forwarding the remaining arguments to pnpm in the profile directory");
134
+ plugin.requiredOption("--profile <name>", "the profile whose plugins to manage (initialized on first use)", selectProfile).allowUnknownOption().argument("[args...]", "pnpm arguments, forwarded verbatim (add <pkg>, remove <pkg>, why <pkg>, ...)").action((args, options) => {
135
+ if (options.profile === "") program.error("error: --profile needs a name");
136
+ rejectElectronProfile(plugin, options.profile);
137
+ if (args.length === 0) program.error("error: plugin needs pnpm arguments to forward (e.g. add <package>)");
138
+ resolved = {
139
+ mode: "plugin",
140
+ profile: options.profile,
141
+ args
142
+ };
143
+ });
144
+ }
139
145
  try {
140
- program.parse(argv, { from: "user" });
146
+ const forkCommand = first === "plugin" || first === "web:log" || first === "web:log:tmp";
147
+ const expanded = first !== void 0 && !first.startsWith("-") && !forkCommand ? ["--profile", ...argv] : argv;
148
+ program.parse(expanded, { from: "user" });
141
149
  } catch (error) {
142
150
  return process.exit(error instanceof CommanderError ? error.exitCode : 1);
143
151
  }
@@ -146,6 +154,63 @@ function parseDshArgs(argv, version) {
146
154
  return resolved;
147
155
  }
148
156
  //#endregion
157
+ //#region lib/types/startup-diagnostics.js
158
+ /** Save original startup diagnostics while keeping the terminal report concise. */
159
+ /** Wait for stderr to finish the write before the failed process exits. */
160
+ function writeStderr(text) {
161
+ return new Promise((resolve, reject) => {
162
+ process.stderr.write(text, (error) => {
163
+ if (error) reject(error);
164
+ else resolve();
165
+ });
166
+ });
167
+ }
168
+ /**
169
+ * Print the startup summary and save a private, uniquely named report under DSH_HOME/logs.
170
+ * Failed writes print the complete report to stderr instead of claiming a saved path.
171
+ * @param error - startup audit failure retaining plugin metadata and original errors.
172
+ * @param context - resolved Harness home, application version, and selected profile.
173
+ * @param write - terminal output sink; awaited before returning, defaults to stderr.
174
+ * @returns after saving or printing the report and completing terminal writes.
175
+ */
176
+ async function reportStartupFailure(error, context, write = writeStderr) {
177
+ const now = (/* @__PURE__ */ new Date()).toISOString();
178
+ const report = "WARNING: Raw diagnostics may contain configuration or credential values from plugin errors. Review before sharing.\n\n" + inspect({
179
+ timestamp: now,
180
+ dshVersion: context.version,
181
+ nodeVersion: process.version,
182
+ platform: process.platform,
183
+ arch: process.arch,
184
+ profile: context.profile,
185
+ error
186
+ }, {
187
+ depth: null,
188
+ maxArrayLength: null,
189
+ maxStringLength: null,
190
+ showHidden: true,
191
+ customInspect: false,
192
+ getters: false,
193
+ colors: false
194
+ }) + "\n";
195
+ await write(`${error.message}\n`);
196
+ const logDir = join(context.home, "logs");
197
+ const logPath = join(logDir, `startup-${now.replaceAll(":", "-")}-${randomUUID()}.log`);
198
+ try {
199
+ await mkdir(logDir, {
200
+ recursive: true,
201
+ mode: 448
202
+ });
203
+ await writeFile(logPath, report, {
204
+ flag: "wx",
205
+ mode: 384
206
+ });
207
+ } catch (writeError) {
208
+ await write(`\ndsh: warning: could not write startup diagnostics: ${String(writeError)}\nFull diagnostics:\n${report}`);
209
+ return;
210
+ }
211
+ await write(`\nFull diagnostics: ${logPath}\n`);
212
+ }
213
+ //#endregion
149
214
  //#region lib/types/bin.js
150
215
  /**
151
216
  * Command-line entry for dsh.
@@ -161,22 +226,33 @@ function readVersion() {
161
226
  * @returns a promise that settles when the selected command mode finishes.
162
227
  */
163
228
  async function runCli() {
164
- const invocation = parseDshArgs(process.argv.slice(2), readVersion());
229
+ const version = readVersion();
230
+ const invocation = parseDshArgs(process.argv.slice(2), version);
165
231
  switch (invocation.mode) {
166
232
  case "profile": {
167
- const { runProfile } = await import("./profile-boot-DB8eVKBx.js");
168
- await runProfile({
169
- environment: loadLayeredEnv("dsh"),
170
- profile: invocation.profile,
171
- fromDefaultProfile: invocation.fromDefaultProfile,
172
- patchFiles: invocation.patches,
173
- args: invocation.args
174
- });
233
+ const { runProfile } = await import("./profile-boot.js");
234
+ try {
235
+ await runProfile({
236
+ environment: loadLayeredEnv("dsh"),
237
+ profile: invocation.profile,
238
+ fromDefaultProfile: invocation.fromDefaultProfile,
239
+ patchFiles: invocation.patches,
240
+ args: invocation.args
241
+ });
242
+ } catch (error) {
243
+ if (!(error instanceof StartupError)) throw error;
244
+ await reportStartupFailure(error, {
245
+ home: resolveDshHome(),
246
+ version,
247
+ profile: invocation.profile
248
+ });
249
+ process.exit(1);
250
+ }
175
251
  break;
176
252
  }
177
253
  case "plugin": {
178
- const { runPlugin } = await import("./plugin-Bk_PbPwP.js");
179
- process.exit(runPlugin(invocation.profile, invocation.args));
254
+ const { runPlugin } = await import("./plugin-DJ-rVHUS.js");
255
+ process.exit(await runPlugin(invocation.profile, invocation.args));
180
256
  break;
181
257
  }
182
258
  case "web-log": {
@@ -189,7 +265,7 @@ async function runCli() {
189
265
  break;
190
266
  }
191
267
  case "dump-config": {
192
- const { runDumpConfig } = await import("./dump-config-CpQgpJa2.js");
268
+ const { runDumpConfig } = await import("./dump-config-Bb5YvFIX.js");
193
269
  runDumpConfig(invocation.profile, invocation.defaultOnly, invocation.patches, invocation.fromDefaultProfile);
194
270
  break;
195
271
  }
@@ -1,4 +1,4 @@
1
- import { a as prepareProfile, n as PROFILE_ROOT_FILENAME, r as homePatchPath } from "./profile-boot-CuwbWsnH.js";
1
+ import { a as prepareProfile, n as PROFILE_ROOT_FILENAME, r as homePatchPath } from "./profile-boot-BNu17Y9U.js";
2
2
  import { existsSync } from "node:fs";
3
3
  import { loadOptionalPatches, loadOverlayPatches, renderConfigDump } from "@deepseek-ai/dsh-app-boot";
4
4
  import { join, resolve } from "node:path";
@@ -0,0 +1,31 @@
1
+ import { t as INSTALL_ANCHOR } from "./profile-boot-BNu17Y9U.js";
2
+ import { resolveProfileDir } from "@deepseek-ai/dsh-app-boot";
3
+ import { join } from "node:path";
4
+ import { runPluginCommand } from "@deepseek-ai/dsh-plugin-manager/operations";
5
+ //#region lib/types/plugin.js
6
+ /** dsh plugin forwards pnpm through the shared profile package operations. */
7
+ /** Run package management for a profile.
8
+ * @param profile Profile name.
9
+ * @param args Pnpm arguments relative to the invoking directory.
10
+ * @returns Pnpm exit code.
11
+ */
12
+ async function runPlugin(profile, args) {
13
+ const result = await runPluginCommand({
14
+ profile,
15
+ installAnchor: INSTALL_ANCHOR,
16
+ cwd: process.cwd()
17
+ }, args, {
18
+ execution: "cli",
19
+ outputBytes: 16384,
20
+ lockWaitMs: 12e4,
21
+ onOutput: (text, stream) => {
22
+ process[stream].write(text);
23
+ }
24
+ });
25
+ if (result.exitCode === 127) process.stderr.write("dsh: pnpm was not found; install pnpm and make it available on PATH.\n");
26
+ if (result.exitCode !== 0) process.stderr.write(`dsh: pnpm failed; diagnostics: ${result.logPath}\n`);
27
+ if (result.exitCode !== 0 && args.some((argument) => /^git\+|^github:|\.git(?:#|$)/.test(argument))) process.stderr.write(`dsh: git-hosted plugins build on install via their prepare script, which pnpm blocks until allowed — add the exact key pnpm printed above under allowBuilds in ${join(resolveProfileDir(profile), "pnpm-workspace.yaml")}, then re-run\n`);
28
+ return result.exitCode;
29
+ }
30
+ //#endregion
31
+ export { runPlugin };