dsh-superagent-console 0.1.0 → 0.1.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.md CHANGED
@@ -33,9 +33,11 @@ vendor 补丁(seams):`sync-client.mjs` 在每次 vendor 复制后,对 `c
33
33
  | L0 | 服务端 fetch `{target}/orchestrator/api/state`(带 `x-orchestrator-request: 1`) | 引擎可达 |
34
34
  | L1 | `~/.dsh/profiles/<profile>/package.json` 依赖含 `dsh-superagent` + checkout 有 `lib/` 产物 | 服务已部署 |
35
35
  | L2 | `scripts/dsh-superagent status`(exit 0 健康 / 1 停止 / 2 组件错误 / 3 等待 setup) | 健康细分 |
36
- | L3 | dsh ≥0.2.1、node ^22.19\|\|≥24、pnpm、git、tmux、python3 | 前置依赖 |
36
+ | L3 | dsh ≥0.2.1、node ^22.19\|\|≥24、pnpm、git、tmux、python3(缺项附补齐提示命令) | 前置依赖 |
37
37
 
38
- 状态 → 动作一一映射:`running` → 官方面板;`installed-stopped` → 启动按钮;`installed-unhealthy` → 重启按钮;`not-installed` → 在线安装;`prereq-missing` → 列出缺项(不提供安装按钮)。
38
+ 状态 → 动作一一映射:`running` → 官方面板;`installed-stopped` → 启动按钮;`installed-unhealthy` → 重启按钮;`not-installed` → 在线安装;`prereq-missing` → 列出缺项与补齐命令(不提供安装按钮)。
39
+
40
+ 探测 PATH 由 `spawnPath` 组装:运行中 node 的 bin 目录 + 配置的 `extraPath` + **桌面 App 自带的 dsh CLI 目录**(从运行中二进制相对推导 `<App>.app/Contents/Resources/runtime/cli/bin`,存在才追加)。因此桌面用户无需自行安装 dsh;`extraPath` 中显式安装的用户版本优先于内置 CLI。
39
41
 
40
42
  ## 在线安装流水线(8 段)
41
43
 
@@ -0,0 +1,4 @@
1
+ # Mount the console plugin itself (UI proxy only; the engine stays in its own profile).
2
+ - insert:
3
+ - id: dsh-superagent-console
4
+ name: 'dsh-superagent-console'
package/lib/client.js CHANGED
@@ -7852,7 +7852,12 @@ window.__ModuleLoader__.load({
7852
7852
  children: props.label
7853
7853
  });
7854
7854
  }
7855
- function PrereqList({ prereqs, title }) {
7855
+ function PrereqList({ prereqs, title, st }) {
7856
+ const copy = (text) => {
7857
+ const clip = navigator.clipboard;
7858
+ if (!clip) return;
7859
+ clip.writeText(text).catch(() => void 0);
7860
+ };
7856
7861
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7857
7862
  style: { marginTop: 16 },
7858
7863
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -7865,13 +7870,45 @@ window.__ModuleLoader__.load({
7865
7870
  },
7866
7871
  children: prereqs.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
7867
7872
  style: { color: item.ok ? "inherit" : "var(--dsh-danger, #d64545)" },
7868
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
7869
- style: monoStyle,
7870
- children: item.name
7871
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7872
- style: { opacity: .75 },
7873
- children: [" — ", item.detail]
7874
- })]
7873
+ children: [
7874
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
7875
+ style: monoStyle,
7876
+ children: item.name
7877
+ }),
7878
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7879
+ style: { opacity: .75 },
7880
+ children: [" — ", item.detail]
7881
+ }),
7882
+ !item.ok && item.hint && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7883
+ style: {
7884
+ display: "inline-flex",
7885
+ alignItems: "center",
7886
+ gap: 6,
7887
+ marginLeft: 8
7888
+ },
7889
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
7890
+ style: {
7891
+ ...monoStyle,
7892
+ opacity: .85
7893
+ },
7894
+ children: item.hint
7895
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7896
+ type: "button",
7897
+ onClick: () => {
7898
+ copy(item.hint ?? "");
7899
+ },
7900
+ style: {
7901
+ ...buttonStyle,
7902
+ padding: "1px 8px",
7903
+ fontSize: 11,
7904
+ borderRadius: 6,
7905
+ color: "inherit",
7906
+ borderColor: "var(--dsh-border, rgba(128,128,128,.4))"
7907
+ },
7908
+ children: st("copyCommand")
7909
+ })]
7910
+ })
7911
+ ]
7875
7912
  }, item.name))
7876
7913
  })]
7877
7914
  });
@@ -8084,7 +8121,8 @@ window.__ModuleLoader__.load({
8084
8121
  }),
8085
8122
  (status.state === "not-installed" || status.state === "prereq-missing" || installing) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrereqList, {
8086
8123
  prereqs: status.prereqs,
8087
- title: st("prereqsTitle")
8124
+ title: st("prereqsTitle"),
8125
+ st
8088
8126
  }),
8089
8127
  (installing || status.setup.current || STAGES.some((id) => status.setup.stages[id]?.status !== "pending")) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StageList, {
8090
8128
  setup: status.setup,
@@ -8166,7 +8204,7 @@ window.__ModuleLoader__.load({
8166
8204
  descStopped: "引擎服务处于停止状态,可以一键拉起(tmux 会话由生命周期脚本托管)。",
8167
8205
  descUnhealthy: "引擎进程存在但管理 API 不可达或组件报错,可尝试重启。",
8168
8206
  descNotInstalled: "引擎运行在独立的 DSH profile 中。点击安装后,控制台会在线拉取官方源码、自动构建并部署到独立 profile(npm 上无此包,源码构建是官方唯一安装路径)。",
8169
- descPrereq: "在线安装需要以下工具,请先补齐后再安装(凭据类操作仍需本人完成)。",
8207
+ descPrereq: "在线安装需要以下工具,红项按提示命令补齐后点「重试」(凭据类操作仍需本人完成)。",
8170
8208
  install: "在线安装引擎",
8171
8209
  installResume: "继续安装",
8172
8210
  installing: "正在安装…",
@@ -8176,6 +8214,7 @@ window.__ModuleLoader__.load({
8176
8214
  starting: "正在启动…",
8177
8215
  restarting: "正在重启…",
8178
8216
  prereqsTitle: "前置依赖",
8217
+ copyCommand: "复制",
8179
8218
  stageTitle: "安装进度",
8180
8219
  setupWait: "引擎已启动,正在等待首次配置(Source 设置需在引擎面板完成)。",
8181
8220
  manuallyStopped: "引擎带有手动停止标记,已按需忽略。",
@@ -8196,7 +8235,7 @@ window.__ModuleLoader__.load({
8196
8235
  descStopped: "The engine service is stopped. Start it with one click (the tmux session is managed by the lifecycle script).",
8197
8236
  descUnhealthy: "The engine process exists but the management API is unreachable or a component reports an error. Try a restart.",
8198
8237
  descNotInstalled: "The engine runs in its own dedicated DSH profile. Installing fetches the official source online, builds it, and deploys it into that profile (the package is not published to npm; source build + link is the only official path).",
8199
- descPrereq: "The online installer needs the tools below. Install them first (credential steps stay yours).",
8238
+ descPrereq: "The online installer needs the tools below — fix failing entries with the suggested command, then Retry (credential steps stay yours).",
8200
8239
  install: "Install engine online",
8201
8240
  installResume: "Resume install",
8202
8241
  installing: "Installing…",
@@ -8206,6 +8245,7 @@ window.__ModuleLoader__.load({
8206
8245
  starting: "Starting…",
8207
8246
  restarting: "Restarting…",
8208
8247
  prereqsTitle: "Prerequisites",
8248
+ copyCommand: "Copy",
8209
8249
  stageTitle: "Install progress",
8210
8250
  setupWait: "Engine is up and waiting for first-time setup (Source configuration happens in the engine panel).",
8211
8251
  manuallyStopped: "The engine carries a manual-stop marker; honored on demand.",
package/lib/index.d.ts CHANGED
@@ -28,6 +28,8 @@ interface Prereq {
28
28
  name: string;
29
29
  ok: boolean;
30
30
  detail: string;
31
+ /** Suggested remediation command for a failed check; absent when none applies. */
32
+ hint?: string;
31
33
  }
32
34
  interface EngineHealth {
33
35
  exit: number;
package/lib/index.js CHANGED
@@ -37,13 +37,31 @@ const Config = z.object({
37
37
  * Every spawn goes through `execFile` with an argv array (never a shell
38
38
  * string), an explicit timeout, and an augmented PATH: the desktop app
39
39
  * process may run with a minimal GUI PATH, so the node installation bin
40
- * directory and the configured `extraPath` entries are prepended.
40
+ * directory, the configured `extraPath` entries, and the app's bundled
41
+ * dsh CLI directory are prepended (in that order; user entries win).
41
42
  */
42
43
  const MAX_BUFFER = 16777216;
43
- /** PATH prepended for every spawned tool: the running node's bin dir plus the configured extras. */
44
+ /**
45
+ * Directory of the dsh CLI shipped inside the desktop app bundle: the
46
+ * Electron binary lives at `<App>.app/Contents/MacOS/<bin>`, so the bundled
47
+ * CLI resolves to `../Resources/runtime/cli/bin`. Null outside an app
48
+ * bundle (plain node runs, tests) or when the bundle carries no CLI.
49
+ * Uncached on purpose: spawns are rare and the probe is one existsSync.
50
+ */
51
+ function bundledCliDir(execPath = process.execPath) {
52
+ const candidate = path.join(path.dirname(execPath), "..", "Resources", "runtime", "cli", "bin");
53
+ return existsSync(path.join(candidate, "dsh")) ? candidate : null;
54
+ }
55
+ /**
56
+ * PATH prepended for every spawned tool: the running node's bin dir, the
57
+ * configured extras, the app's bundled CLI dir (fallback only, so an
58
+ * explicit user install keeps winning), then the inherited PATH.
59
+ */
44
60
  function spawnPath(extraPath) {
45
61
  const parts = [path.dirname(process.execPath)];
46
62
  if (extraPath) parts.push(...extraPath.split(":").filter(Boolean));
63
+ const bundled = bundledCliDir();
64
+ if (bundled) parts.push(bundled);
47
65
  const inherited = process.env.PATH ?? "";
48
66
  return [...parts, inherited].filter(Boolean).join(":");
49
67
  }
@@ -220,7 +238,13 @@ async function engineHealth(engineDir, profileName, target, env = {}) {
220
238
  };
221
239
  }
222
240
  }
223
- /** L3: toolchain probe; never throws, missing tools surface as ok:false entries. */
241
+ /** Locale-neutral remediation commands surfaced next to a failed check (darwin only). */
242
+ function missingHint(name) {
243
+ if (process.platform !== "darwin") return void 0;
244
+ if (name === "tmux") return "brew install tmux";
245
+ if (name === "pnpm") return "npm install -g pnpm";
246
+ }
247
+ /** L3: toolchain probe; never throws, missing tools surface as ok:false entries (with a hint where one applies). */
224
248
  async function prereqCheck(env = {}) {
225
249
  const checks = [nodeOk()];
226
250
  for (const tool of TOOL_CHECKS) {
@@ -233,7 +257,8 @@ async function prereqCheck(env = {}) {
233
257
  checks.push({
234
258
  name: tool.name,
235
259
  ok: false,
236
- detail: result.code === -1 ? "not found on PATH" : tail(output, 200)
260
+ detail: result.code === -1 ? "not found on PATH" : tail(output, 200),
261
+ hint: missingHint(tool.name)
237
262
  });
238
263
  continue;
239
264
  }
@@ -242,7 +267,8 @@ async function prereqCheck(env = {}) {
242
267
  checks.push({
243
268
  name: tool.name,
244
269
  ok,
245
- detail: ok ? version : `${version} (needs >= ${tool.min})`
270
+ detail: ok ? version : `${version} (needs >= ${tool.min})`,
271
+ hint: ok ? void 0 : missingHint(tool.name)
246
272
  });
247
273
  }
248
274
  return checks;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-superagent-console",
3
3
  "description": "Desktop console for the Superagent engine: the official panel embedded over a same-origin proxy. Detects the engine's state, installs it online (source build into a dedicated profile) when missing, and starts/restarts it on demand; the engine stays resident across desktop restarts.",
4
- "version": "0.1.0",
4
+ "version": "0.1.2",
5
5
  "license": "MIT",
6
6
  "author": "Shuai-S",
7
7
  "type": "module",
@@ -16,7 +16,8 @@
16
16
  },
17
17
  "files": [
18
18
  "lib",
19
- "locale/*.json"
19
+ "locale/*.json",
20
+ "cordis.patch.yml"
20
21
  ],
21
22
  "engines": {
22
23
  "node": "^22.19.0 || >=24.0.0"