jishushell 0.4.10 → 0.4.24

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.
Files changed (248) hide show
  1. package/Dockerfile.hermes-slim +193 -0
  2. package/INSTALL-NOTICE +10 -12
  3. package/apps/hermes-container.yaml +35 -0
  4. package/apps/ollama-binary.yaml +200 -0
  5. package/apps/ollama-cpu-container.yaml +37 -0
  6. package/apps/ollama-with-hollama-binary.yaml +195 -0
  7. package/apps/openclaw-binary.yaml +69 -0
  8. package/apps/openclaw-container.yaml +37 -0
  9. package/apps/openclaw-with-ollama-container.yaml +42 -0
  10. package/apps/openclaw-with-searxng-container.yaml +136 -0
  11. package/apps/openwebui-container.yaml +53 -0
  12. package/apps/playwright-container.yaml +120 -0
  13. package/apps/searxng-container.yaml +115 -0
  14. package/dist/auth.d.ts +1 -0
  15. package/dist/auth.js +15 -14
  16. package/dist/auth.js.map +1 -1
  17. package/dist/cli/app.d.ts +4 -0
  18. package/dist/cli/app.js +814 -0
  19. package/dist/cli/app.js.map +1 -0
  20. package/dist/cli/backup.d.ts +3 -0
  21. package/dist/cli/backup.js +434 -0
  22. package/dist/cli/backup.js.map +1 -0
  23. package/dist/{doctor.d.ts → cli/doctor.d.ts} +7 -1
  24. package/dist/{doctor.js → cli/doctor.js} +377 -22
  25. package/dist/cli/doctor.js.map +1 -0
  26. package/dist/cli/helpers.d.ts +4 -0
  27. package/dist/cli/helpers.js +32 -0
  28. package/dist/cli/helpers.js.map +1 -0
  29. package/dist/cli/job.d.ts +4 -0
  30. package/dist/cli/job.js +198 -0
  31. package/dist/cli/job.js.map +1 -0
  32. package/dist/cli/llm.d.ts +25 -0
  33. package/dist/cli/llm.js +599 -0
  34. package/dist/cli/llm.js.map +1 -0
  35. package/dist/cli/managed-list.d.ts +30 -0
  36. package/dist/cli/managed-list.js +129 -0
  37. package/dist/cli/managed-list.js.map +1 -0
  38. package/dist/cli/panel.d.ts +26 -0
  39. package/dist/cli/panel.js +804 -0
  40. package/dist/cli/panel.js.map +1 -0
  41. package/dist/cli/version.d.ts +1 -0
  42. package/dist/cli/version.js +12 -0
  43. package/dist/cli/version.js.map +1 -0
  44. package/dist/cli.js +48 -776
  45. package/dist/cli.js.map +1 -1
  46. package/dist/config.d.ts +69 -0
  47. package/dist/config.js +268 -7
  48. package/dist/config.js.map +1 -1
  49. package/dist/control.d.ts +17 -41
  50. package/dist/control.js +61 -1323
  51. package/dist/control.js.map +1 -1
  52. package/dist/install.d.ts +16 -0
  53. package/dist/install.js +75 -26
  54. package/dist/install.js.map +1 -1
  55. package/dist/routes/agent-apps.d.ts +15 -0
  56. package/dist/routes/agent-apps.js +78 -0
  57. package/dist/routes/agent-apps.js.map +1 -0
  58. package/dist/routes/apps.d.ts +3 -0
  59. package/dist/routes/apps.js +278 -0
  60. package/dist/routes/apps.js.map +1 -0
  61. package/dist/routes/backup.js +3 -3
  62. package/dist/routes/backup.js.map +1 -1
  63. package/dist/routes/instances.d.ts +6 -0
  64. package/dist/routes/instances.js +863 -874
  65. package/dist/routes/instances.js.map +1 -1
  66. package/dist/routes/llm.d.ts +15 -0
  67. package/dist/routes/llm.js +247 -0
  68. package/dist/routes/llm.js.map +1 -0
  69. package/dist/routes/runtime.d.ts +15 -0
  70. package/dist/routes/runtime.js +69 -0
  71. package/dist/routes/runtime.js.map +1 -0
  72. package/dist/routes/setup.js +131 -9
  73. package/dist/routes/setup.js.map +1 -1
  74. package/dist/routes/system.js +56 -9
  75. package/dist/routes/system.js.map +1 -1
  76. package/dist/server.js +107 -7
  77. package/dist/server.js.map +1 -1
  78. package/dist/services/agent-apps/catalog.d.ts +30 -0
  79. package/dist/services/agent-apps/catalog.js +60 -0
  80. package/dist/services/agent-apps/catalog.js.map +1 -0
  81. package/dist/services/agent-apps/index.d.ts +36 -0
  82. package/dist/services/agent-apps/index.js +171 -0
  83. package/dist/services/agent-apps/index.js.map +1 -0
  84. package/dist/services/agent-apps/installers/adapter-probes.d.ts +49 -0
  85. package/dist/services/agent-apps/installers/adapter-probes.js +223 -0
  86. package/dist/services/agent-apps/installers/adapter-probes.js.map +1 -0
  87. package/dist/services/agent-apps/installers/adapter.d.ts +30 -0
  88. package/dist/services/agent-apps/installers/adapter.js +171 -0
  89. package/dist/services/agent-apps/installers/adapter.js.map +1 -0
  90. package/dist/services/agent-apps/installers/registry-probe.d.ts +38 -0
  91. package/dist/services/agent-apps/installers/registry-probe.js +183 -0
  92. package/dist/services/agent-apps/installers/registry-probe.js.map +1 -0
  93. package/dist/services/agent-apps/installers/shell-script.d.ts +47 -0
  94. package/dist/services/agent-apps/installers/shell-script.js +471 -0
  95. package/dist/services/agent-apps/installers/shell-script.js.map +1 -0
  96. package/dist/services/agent-apps/types.d.ts +125 -0
  97. package/dist/services/agent-apps/types.js +17 -0
  98. package/dist/services/agent-apps/types.js.map +1 -0
  99. package/dist/services/app/app-compiler.d.ts +15 -0
  100. package/dist/services/app/app-compiler.js +172 -0
  101. package/dist/services/app/app-compiler.js.map +1 -0
  102. package/dist/services/app/app-manager.d.ts +142 -0
  103. package/dist/services/app/app-manager.js +1988 -0
  104. package/dist/services/app/app-manager.js.map +1 -0
  105. package/dist/services/app/custom-manager.d.ts +27 -0
  106. package/dist/services/app/custom-manager.js +285 -0
  107. package/dist/services/app/custom-manager.js.map +1 -0
  108. package/dist/services/app/hermes-agent-manager.d.ts +20 -0
  109. package/dist/services/app/hermes-agent-manager.js +289 -0
  110. package/dist/services/app/hermes-agent-manager.js.map +1 -0
  111. package/dist/services/app/id-normalizer.d.ts +27 -0
  112. package/dist/services/app/id-normalizer.js +77 -0
  113. package/dist/services/app/id-normalizer.js.map +1 -0
  114. package/dist/services/app/ollama-manager.d.ts +18 -0
  115. package/dist/services/app/ollama-manager.js +207 -0
  116. package/dist/services/app/ollama-manager.js.map +1 -0
  117. package/dist/services/app/openclaw-manager.d.ts +63 -0
  118. package/dist/services/app/openclaw-manager.js +1178 -0
  119. package/dist/services/app/openclaw-manager.js.map +1 -0
  120. package/dist/services/app/paths.d.ts +47 -0
  121. package/dist/services/app/paths.js +68 -0
  122. package/dist/services/app/paths.js.map +1 -0
  123. package/dist/services/app/registry.d.ts +17 -0
  124. package/dist/services/app/registry.js +31 -0
  125. package/dist/services/app/registry.js.map +1 -0
  126. package/dist/services/app/remote-spec.d.ts +14 -0
  127. package/dist/services/app/remote-spec.js +58 -0
  128. package/dist/services/app/remote-spec.js.map +1 -0
  129. package/dist/services/app/terminal-session-manager.d.ts +27 -0
  130. package/dist/services/app/terminal-session-manager.js +157 -0
  131. package/dist/services/app/terminal-session-manager.js.map +1 -0
  132. package/dist/services/app/types.d.ts +72 -0
  133. package/dist/services/app/types.js +16 -0
  134. package/dist/services/app/types.js.map +1 -0
  135. package/dist/services/backup-manager.js +60 -22
  136. package/dist/services/backup-manager.js.map +1 -1
  137. package/dist/services/instance-manager.d.ts +125 -34
  138. package/dist/services/instance-manager.js +679 -1043
  139. package/dist/services/instance-manager.js.map +1 -1
  140. package/dist/services/llm-proxy/adapters.js +5 -1
  141. package/dist/services/llm-proxy/adapters.js.map +1 -1
  142. package/dist/services/llm-proxy/circuit-breaker.js +10 -2
  143. package/dist/services/llm-proxy/circuit-breaker.js.map +1 -1
  144. package/dist/services/llm-proxy/index.d.ts +43 -0
  145. package/dist/services/llm-proxy/index.js +120 -5
  146. package/dist/services/llm-proxy/index.js.map +1 -1
  147. package/dist/services/llm-proxy/ssrf.js +1 -1
  148. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  149. package/dist/services/nomad-manager.d.ts +260 -3
  150. package/dist/services/nomad-manager.js +2921 -341
  151. package/dist/services/nomad-manager.js.map +1 -1
  152. package/dist/services/panel-manager.d.ts +50 -0
  153. package/dist/services/panel-manager.js +443 -0
  154. package/dist/services/panel-manager.js.map +1 -0
  155. package/dist/services/plugin-installer.js +28 -2
  156. package/dist/services/plugin-installer.js.map +1 -1
  157. package/dist/services/process-manager.js +42 -7
  158. package/dist/services/process-manager.js.map +1 -1
  159. package/dist/services/runtime/adapters/custom.d.ts +20 -0
  160. package/dist/services/runtime/adapters/custom.js +90 -0
  161. package/dist/services/runtime/adapters/custom.js.map +1 -0
  162. package/dist/services/runtime/adapters/hermes.d.ts +174 -0
  163. package/dist/services/runtime/adapters/hermes.js +1316 -0
  164. package/dist/services/runtime/adapters/hermes.js.map +1 -0
  165. package/dist/services/runtime/adapters/openclaw-routes.d.ts +17 -0
  166. package/dist/services/runtime/adapters/openclaw-routes.js +946 -0
  167. package/dist/services/runtime/adapters/openclaw-routes.js.map +1 -0
  168. package/dist/services/runtime/adapters/openclaw.d.ts +188 -0
  169. package/dist/services/runtime/adapters/openclaw.js +2195 -0
  170. package/dist/services/runtime/adapters/openclaw.js.map +1 -0
  171. package/dist/services/runtime/errors.d.ts +28 -0
  172. package/dist/services/runtime/errors.js +31 -0
  173. package/dist/services/runtime/errors.js.map +1 -0
  174. package/dist/services/runtime/index.d.ts +34 -0
  175. package/dist/services/runtime/index.js +51 -0
  176. package/dist/services/runtime/index.js.map +1 -0
  177. package/dist/services/runtime/instance.d.ts +24 -0
  178. package/dist/services/runtime/instance.js +143 -0
  179. package/dist/services/runtime/instance.js.map +1 -0
  180. package/dist/services/runtime/migrations.d.ts +15 -0
  181. package/dist/services/runtime/migrations.js +25 -0
  182. package/dist/services/runtime/migrations.js.map +1 -0
  183. package/dist/services/runtime/registry.d.ts +13 -0
  184. package/dist/services/runtime/registry.js +32 -0
  185. package/dist/services/runtime/registry.js.map +1 -0
  186. package/dist/services/runtime/types.d.ts +545 -0
  187. package/dist/services/runtime/types.js +14 -0
  188. package/dist/services/runtime/types.js.map +1 -0
  189. package/dist/services/setup-manager.d.ts +70 -29
  190. package/dist/services/setup-manager.js +591 -625
  191. package/dist/services/setup-manager.js.map +1 -1
  192. package/dist/services/task-registry.d.ts +44 -0
  193. package/dist/services/task-registry.js +74 -0
  194. package/dist/services/task-registry.js.map +1 -0
  195. package/dist/services/telemetry/heartbeat.d.ts +6 -6
  196. package/dist/services/telemetry/heartbeat.js +29 -30
  197. package/dist/services/telemetry/heartbeat.js.map +1 -1
  198. package/dist/services/update-manager.d.ts +47 -0
  199. package/dist/services/update-manager.js +305 -0
  200. package/dist/services/update-manager.js.map +1 -0
  201. package/dist/types.d.ts +222 -0
  202. package/dist/utils/docker-host.d.ts +15 -0
  203. package/dist/utils/docker-host.js +64 -0
  204. package/dist/utils/docker-host.js.map +1 -0
  205. package/install/jishu-install.sh +303 -37
  206. package/install/post-install.sh +64 -5
  207. package/package.json +19 -5
  208. package/public/assets/Dashboard-B-JoOjBQ.js +1 -0
  209. package/public/assets/HermesChatPanel-mFSureyc.js +1 -0
  210. package/public/assets/HermesConfigForm-DvR05LK1.js +4 -0
  211. package/public/assets/InitPassword-CVA8wQA6.js +1 -0
  212. package/public/assets/InstanceDetail-DcZW2QGO.js +91 -0
  213. package/public/assets/{Login-CUoEZOWR.js → Login-BWsZH2mu.js} +1 -1
  214. package/public/assets/NewInstance-BCIrAd86.js +1 -0
  215. package/public/assets/Settings-xkDcduFz.js +1 -0
  216. package/public/assets/Setup-Cfuwj4gV.js +1 -0
  217. package/public/assets/WeixinLoginPanel-CnjR8xMu.js +9 -0
  218. package/public/assets/index-CPhVFEsx.css +1 -0
  219. package/public/assets/index-DQsM6Joa.js +19 -0
  220. package/public/assets/input-paste-CrNVAyOy.js +1 -0
  221. package/public/assets/{providers-lBSOjUWy.js → providers-V-vwrExZ.js} +1 -1
  222. package/public/assets/registry-B4UFJdpA.js +2 -0
  223. package/public/assets/{usePolling-CK0DfI4h.js → usePolling-Do5Erqm_.js} +1 -1
  224. package/public/assets/vendor-i18n-ucpM0OR0.js +9 -0
  225. package/public/assets/{vendor-react-B1-3Yrt-.js → vendor-react-Bk1hRGiY.js} +1 -1
  226. package/public/favicon.png +0 -0
  227. package/public/index.html +9 -4
  228. package/public/logos/hermes.png +0 -0
  229. package/public/logos/ollama.png +0 -0
  230. package/public/logos/openclaw.svg +60 -0
  231. package/scripts/build-hermes-image.sh +21 -0
  232. package/scripts/build-local.sh +54 -0
  233. package/scripts/check-adapter-isolation.ts +293 -0
  234. package/scripts/fixtures/instances/hermes-sample/instance.json +37 -0
  235. package/scripts/fixtures/instances/legacy-openclaw-sample/instance.json +7 -0
  236. package/scripts/smoke/hermes-bootstrap.sh +195 -0
  237. package/templates/hermes-entrypoint.sh +154 -0
  238. package/dist/doctor.js.map +0 -1
  239. package/install/jishu-install-china.sh +0 -3092
  240. package/public/assets/Dashboard-DhsrzJ4F.js +0 -1
  241. package/public/assets/InitPassword-BjubiVdd.js +0 -1
  242. package/public/assets/InstanceDetail-DMcywsof.js +0 -17
  243. package/public/assets/NewInstance-Bk0G4EiJ.js +0 -1
  244. package/public/assets/Settings-D5tHL_h5.js +0 -1
  245. package/public/assets/Setup-4t6E3Rut.js +0 -1
  246. package/public/assets/index-BJ47MWpF.css +0 -1
  247. package/public/assets/index-DbX85irc.js +0 -16
  248. package/public/assets/vendor-i18n-CfW0RvgE.js +0 -9
package/dist/cli.js CHANGED
@@ -6,801 +6,73 @@ if (process.platform === "darwin") {
6
6
  if (missing.length > 0)
7
7
  process.env.PATH = [...missing, current].join(":");
8
8
  }
9
- import { existsSync } from "fs";
10
9
  import { join } from "path";
11
10
  import { homedir } from "os";
12
- import { exec } from "child_process";
11
+ import { buildDockerClientEnv } from "./utils/docker-host.js";
13
12
  if (process.platform === "darwin") {
14
13
  const jHome = process.env.JISHUSHELL_HOME || join(homedir(), ".jishushell");
15
- const sock = join(jHome, "colima", "jishushell", "docker.sock");
16
- if (existsSync(sock))
17
- process.env.DOCKER_HOST = `unix://${sock}`;
18
- }
19
- import { createServer } from "./server.js";
20
- import { runInstall, parseInstallArgs } from "./install.js";
21
- import { startPanel, stopPanel, stopWithJobs, restartPanel, runDoctor, runOnboard, showStatus, resetAll, checkUpdate, runUpdate } from "./control.js";
22
- const JISHUSHELL_HOME = process.env.JISHUSHELL_HOME || join(homedir(), ".jishushell");
23
- const NOMAD_BIN = join(JISHUSHELL_HOME, "bin", "nomad");
24
- const PANEL_PORT = 8090;
25
- function openBrowser(url) {
26
- const platform = process.platform;
27
- const cmd = platform === "darwin" ? `open "${url}"`
28
- : platform === "win32" ? `start "" "${url}"`
29
- : `xdg-open "${url}"`;
30
- exec(cmd, () => { });
31
- }
32
- const [subcommand, ...rest] = process.argv.slice(2);
33
- // ── HTTP thin-client helpers for backup commands ───────────────────────────
34
- async function apiCall(path, opts = {}) {
35
- const port = process.env.JISHUSHELL_PORT || "8090";
36
- const url = `http://127.0.0.1:${port}/api${path}`;
37
- const res = await fetch(url, {
38
- method: opts.method || "GET",
39
- headers: opts.body ? { "Content-Type": "application/json" } : {},
40
- body: opts.body ? JSON.stringify(opts.body) : undefined,
41
- });
42
- if (!res.ok) {
43
- const err = await res.json().catch(() => ({ detail: res.statusText }));
44
- throw new Error(err.detail || `HTTP ${res.status}`);
45
- }
46
- return res.json();
47
- }
48
- async function waitForJob(jobId) {
49
- while (true) {
50
- const job = await apiCall(`/backup/jobs/${jobId}`);
51
- if (job.status === "completed")
52
- return job;
53
- if (job.status === "failed")
54
- throw new Error(job.error || "Job failed");
55
- process.stdout.write(`\r ${job.progress || job.status}...`);
56
- await new Promise(r => setTimeout(r, 1500));
57
- }
58
- }
59
- // ── Helper to parse --port from remaining args ─────────────────────────────
60
- function parsePort(args) {
61
- for (let i = 0; i < args.length; i++) {
62
- if (args[i] === "--port" && args[i + 1]) {
63
- const p = parseInt(args[i + 1], 10);
64
- if (!isNaN(p) && p > 0 && p < 65536)
65
- return p;
66
- }
67
- }
68
- return undefined;
69
- }
70
- // ── install subcommand ─────────────────────────────────────────────────────
71
- if (subcommand === "install") {
72
- const opts = parseInstallArgs(rest);
73
- runInstall(opts).catch((err) => {
74
- console.error("Installation failed:", err);
75
- process.exit(1);
76
- });
77
- // runInstall is async and calls process.exit() itself
78
- // ── start ──────────────────────────────────────────────────────────────────
79
- }
80
- else if (subcommand === "start") {
81
- startPanel(parsePort(rest)).then(() => process.exit(0)).catch((err) => {
82
- console.error("start failed:", err.message);
83
- process.exit(1);
84
- });
85
- // ── stop ───────────────────────────────────────────────────────────────────
86
- }
87
- else if (subcommand === "stop") {
88
- // --jobs: also stop (but not purge) all Nomad jobs before stopping the panel
89
- const withJobs = rest.includes("--jobs") || rest.includes("-j");
90
- const stopFn = withJobs ? stopWithJobs : stopPanel;
91
- stopFn().then(() => process.exit(0)).catch((err) => {
92
- console.error("stop failed:", err.message);
93
- process.exit(1);
94
- });
95
- // ── restart ────────────────────────────────────────────────────────────────
96
- }
97
- else if (subcommand === "restart") {
98
- restartPanel(parsePort(rest)).then(() => process.exit(0)).catch((err) => {
99
- console.error("restart failed:", err.message);
100
- process.exit(1);
101
- });
102
- // ── status ─────────────────────────────────────────────────────────────────
103
- }
104
- else if (subcommand === "status") {
105
- showStatus().then(() => process.exit(0)).catch((err) => {
106
- console.error("status failed:", err.message);
107
- process.exit(1);
108
- });
109
- // ── doctor ─────────────────────────────────────────────────────────────────
110
- }
111
- else if (subcommand === "doctor") {
112
- const fix = rest.includes("--fix") || rest.includes("-f");
113
- runDoctor({ fix }).then((ok) => process.exit(ok ? 0 : 1)).catch((err) => {
114
- console.error("doctor failed:", err.message);
115
- process.exit(1);
116
- });
117
- // ── onboard ────────────────────────────────────────────────────────────────
118
- }
119
- else if (subcommand === "onboard") {
120
- const force = rest.includes("--force") || rest.includes("-f");
121
- runOnboard({ force }).then(() => process.exit(0)).catch((err) => {
122
- console.error("onboard failed:", err.message);
123
- process.exit(1);
124
- });
125
- // ── reset ──────────────────────────────────────────────────────────────────
126
- }
127
- else if (subcommand === "reset") {
128
- const yes = rest.includes("--yes") || rest.includes("-y");
129
- resetAll({ yes }).then(() => process.exit(0)).catch((err) => {
130
- console.error("reset failed:", err.message);
131
- process.exit(1);
132
- });
133
- // ── update-check ───────────────────────────────────────────────────────────
134
- }
135
- else if (subcommand === "update-check") {
136
- checkUpdate().then((info) => {
137
- console.log(JSON.stringify(info));
138
- process.exit(info.hasUpdate ? 0 : 1);
139
- }).catch((err) => {
140
- console.error("update-check failed:", err.message);
141
- process.exit(2);
142
- });
143
- // ── update-run ─────────────────────────────────────────────────────────────
144
- }
145
- else if (subcommand === "update") {
146
- runUpdate().then(() => process.exit(0)).catch((err) => {
147
- console.error("update-run failed:", err.message);
148
- process.exit(1);
149
- });
150
- // ── pairing: approve / list DM pairing requests inside running containers ──
14
+ const dockerEnv = buildDockerClientEnv({ jishuHome: jHome });
15
+ if (dockerEnv.DOCKER_HOST)
16
+ process.env.DOCKER_HOST = dockerEnv.DOCKER_HOST;
17
+ else
18
+ delete process.env.DOCKER_HOST;
19
+ }
20
+ const argv = process.argv.slice(2);
21
+ const [subcommand] = argv;
22
+ const CLI_MODULE_LOADERS = [
23
+ () => import("./cli/panel.js"),
24
+ () => import("./cli/doctor.js"),
25
+ () => import("./cli/job.js"),
26
+ () => import("./cli/app.js"),
27
+ () => import("./cli/llm.js"),
28
+ () => import("./cli/backup.js"),
29
+ ];
30
+ function cliError(err) {
31
+ console.error(err.message || err);
32
+ process.exit(1);
151
33
  }
152
- else if (subcommand === "pairing") {
153
- const action = rest[0];
154
- // Parse --instance <id> flag
155
- let instanceId;
156
- const instIdx = rest.indexOf("--instance");
157
- if (instIdx !== -1 && rest[instIdx + 1])
158
- instanceId = rest[instIdx + 1];
159
- if (action === "list") {
160
- const { runPairingList } = await import("./control.js");
161
- runPairingList({ instanceId }).then((ok) => process.exit(ok ? 0 : 1)).catch((err) => {
162
- console.error("pairing list failed:", err.message);
163
- process.exit(1);
164
- });
165
- }
166
- else if (action === "approve") {
167
- // args after "approve": [channel, code] or [code] (single-arg form)
168
- const actionArgs = rest.slice(1).filter((a) => !a.startsWith("--") && a !== instanceId);
169
- let channel;
170
- let code;
171
- if (actionArgs.length >= 2) {
172
- [channel, code] = actionArgs;
173
- }
174
- else if (actionArgs.length === 1) {
175
- channel = "feishu";
176
- code = actionArgs[0];
177
- }
178
- else {
179
- console.error("Usage: jishushell pairing approve <channel> <code> [--instance <id>]");
180
- process.exit(1);
181
- }
182
- const notify = rest.includes("--notify");
183
- const { runPairingApprove } = await import("./control.js");
184
- runPairingApprove({ instanceId, channel, code, notify }).then((ok) => process.exit(ok ? 0 : 1)).catch((err) => {
185
- console.error("pairing approve failed:", err.message);
186
- process.exit(1);
187
- });
188
- }
189
- else {
190
- console.log(`
191
- Usage: jishushell pairing <command> [options]
34
+ async function printTopLevelHelp() {
35
+ const modules = await Promise.all(CLI_MODULE_LOADERS.map((load) => load()));
36
+ const briefs = modules
37
+ .map((module) => module.brief?.())
38
+ .filter((brief) => Boolean(brief))
39
+ .join("\n");
40
+ console.log(`
41
+ JishuShell 实例管理面板
192
42
 
193
- Commands:
194
- list 列出等待审批的配对请求
195
- approve <channel> <code> 审批配对码(例: feishu LVU7PNYK)
196
- approve <code> 默认 channel=feishu 简写形式
43
+ Usage:
44
+ jishushell <command> [options]
197
45
 
198
- Options:
199
- --instance <id> 指定实例 ID(只有一个实例时可省略)
200
- --notify 审批后通知申请方
46
+ Commands:
47
+ ${briefs}
48
+ help 显示此帮助
201
49
 
202
- Examples:
203
- jishushell pairing list
204
- jishushell pairing approve feishu LVU7PNYK
205
- jishushell pairing approve LVU7PNYK
206
- jishushell pairing approve feishu LVU7PNYK --instance my-instance
50
+ 运行 'jishushell <command> help' 查看详细用法。
207
51
  `);
208
- process.exit(1);
209
- }
210
- // ── serve: foreground server (used by systemd/launchd ExecStart) ──────────
211
- }
212
- else if (subcommand === "serve") {
213
- const args = rest;
214
- let port = PANEL_PORT;
215
- let host = "0.0.0.0";
216
- for (let i = 0; i < args.length; i++) {
217
- if (args[i] === "--port" && args[i + 1]) {
218
- const parsed = parseInt(args[i + 1], 10);
219
- if (isNaN(parsed) || parsed < 1 || parsed > 65535) {
220
- console.error(`Invalid port: ${args[i + 1]}. Must be 1-65535.`);
221
- process.exit(1);
222
- }
223
- port = parsed;
224
- i++;
225
- }
226
- else if (args[i] === "--host" && args[i + 1]) {
227
- host = args[i + 1];
228
- i++;
229
- }
230
- }
231
- createServer({ port, host }).catch((err) => {
232
- console.error("serve failed:", err.message);
233
- process.exit(1);
234
- });
235
- // ── uninstall ─────────────────────────────────────────────────────────────
236
- }
237
- else if (subcommand === "uninstall") {
238
- const yes = rest.includes("--yes") || rest.includes("-y");
239
- const { spawnSync } = await import("child_process");
240
- const uninstallSh = join(JISHUSHELL_HOME, "install", "post-uninstall.sh");
241
- if (!existsSync(uninstallSh)) {
242
- console.error(`Uninstall script not found: ${uninstallSh}`);
243
- console.error("Please run: sudo bash ~/.jishushell/install/jishu-uninstall.sh --data --yes");
244
- process.exit(1);
245
- }
246
- if (!yes) {
247
- const { createInterface } = await import("readline");
248
- const rl = createInterface({ input: process.stdin, output: process.stdout });
249
- await new Promise((resolve) => {
250
- rl.question("This will stop all services, remove auto-start and delete ~/.jishushell. Continue? [y/N] ", (ans) => {
251
- rl.close();
252
- if (ans.toLowerCase() !== "y") {
253
- console.log("Cancelled.");
254
- process.exit(0);
255
- }
256
- resolve();
257
- });
258
- });
259
- }
260
- // Step 1: stop services and remove ~/.jishushell via the backed-up script
261
- const cleanResult = spawnSync("bash", [uninstallSh], { stdio: "inherit" });
262
- // Step 2: remove the npm global package (now ~/.jishushell is already gone)
263
- const npmResult = spawnSync("npm", ["uninstall", "-g", "jishushell"], { stdio: "inherit" });
264
- process.exit((cleanResult.status ?? 0) || (npmResult.status ?? 0));
265
- // ── backup ─────────────────────────────────────────────────────────────────
266
52
  }
267
- else if (subcommand === "backup") {
268
- // Handle "backup verify <instance-id> <filename>" subcommand
269
- if (rest[0] === "verify") {
270
- const instanceId = rest[1];
271
- const filename = rest[2];
272
- if (!instanceId || !filename) {
273
- console.error("Usage: jishushell backup verify <instance-id> <filename>");
274
- process.exit(1);
275
- }
276
- try {
277
- const result = await apiCall(`/instances/${instanceId}/backup/verify/${encodeURIComponent(filename)}`, { method: "POST" });
278
- console.log(result.valid ? "Verification PASSED." : "Verification FAILED.");
279
- for (const check of result.checks || []) {
280
- console.log(` ${check.passed ? "+" : "x"} ${check.name}${check.detail ? ": " + check.detail : ""}`);
281
- }
282
- if (rest.includes("--json"))
283
- console.log(JSON.stringify(result, null, 2));
284
- }
285
- catch (e) {
286
- console.error(`Verify failed: ${e.message}`);
287
- process.exit(1);
288
- }
289
- process.exit(0);
290
- }
291
- const id = rest[0];
292
- if (!id) {
293
- console.error("Usage: jishushell backup <id> [--with-sessions] [--only-config] [--state-only] [--no-workspace]");
294
- process.exit(1);
295
- }
296
- const withSessions = rest.includes("--with-sessions");
297
- const onlyConfig = rest.includes("--only-config");
298
- const stateOnly = rest.includes("--state-only");
299
- const noWorkspace = rest.includes("--no-workspace");
300
- try {
301
- console.log(`Creating backup for ${id}...`);
302
- const result = await apiCall(`/instances/${id}/backup`, {
303
- method: "POST",
304
- body: {
305
- include_sessions: withSessions,
306
- only_config: onlyConfig,
307
- scope: stateOnly ? "state" : "home",
308
- // Only send include_workspace when the user explicitly opted out;
309
- // otherwise let the backend default (include=true unless only_config).
310
- ...(noWorkspace ? { include_workspace: false } : {}),
311
- },
312
- });
313
- if (result.job_id) {
314
- const job = await waitForJob(result.job_id);
315
- console.log(`\nBackup complete: ${job.result?.filename} (${job.result?.size} bytes)`);
316
- }
317
- if (rest.includes("--verify") && result.job_id) {
318
- const job = await apiCall(`/backup/jobs/${result.job_id}`);
319
- if (job.result?.filename) {
320
- console.log("Verifying...");
321
- const v = await apiCall(`/instances/${id}/backup/verify/${encodeURIComponent(job.result.filename)}`, { method: "POST" });
322
- console.log(v.valid ? "Verification passed." : "Verification FAILED.");
323
- }
324
- }
325
- }
326
- catch (e) {
327
- console.error(`Backup failed: ${e.message}`);
328
- process.exit(1);
329
- }
53
+ if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
54
+ await printTopLevelHelp();
330
55
  process.exit(0);
331
- // ── restore ────────────────────────────────────────────────────────────────
332
56
  }
333
- else if (subcommand === "restore") {
334
- // Handle restore --new (create new instance from backup, no existing instance ID required)
335
- if (rest.includes("--new")) {
336
- const fromIdx = rest.indexOf("--from");
337
- const idxNewId = rest.indexOf("--id");
338
- if (fromIdx !== -1) {
339
- // restore --new --from <src-id> --latest|--pick [--id <new-id>]
340
- const sourceId = rest[fromIdx + 1];
341
- if (!sourceId) {
342
- console.error("Usage: jishushell restore --new --from <src-id> --latest|--pick [--id <new-id>]");
343
- process.exit(1);
344
- }
345
- let backups;
346
- try {
347
- backups = await apiCall(`/instances/${sourceId}/backups`);
348
- }
349
- catch (e) {
350
- console.error(`Restore failed: ${e.message}`);
351
- process.exit(1);
352
- }
353
- if (!backups.length) {
354
- console.error("No backups found.");
355
- process.exit(1);
356
- }
357
- let backupFile = "";
358
- if (rest.includes("--latest")) {
359
- backupFile = backups[0].filename;
360
- }
361
- else {
362
- console.log("Available backups:");
363
- backups.forEach((b, i) => {
364
- console.log(` ${i + 1}. [${b.type}] ${new Date(b.created_at).toLocaleString()} (${(b.size / 1024 / 1024).toFixed(1)}MB)`);
365
- });
366
- const readline = await import("readline");
367
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
368
- const answer = await new Promise(r => rl.question("Select number: ", r));
369
- rl.close();
370
- const idx = parseInt(answer, 10) - 1;
371
- if (idx < 0 || idx >= backups.length) {
372
- console.error("Invalid selection.");
373
- process.exit(1);
374
- }
375
- backupFile = backups[idx].filename;
376
- }
377
- const newId = idxNewId !== -1 ? rest[idxNewId + 1] : `${sourceId}-copy`;
378
- console.log(`Creating new instance ${newId} from ${sourceId}/${backupFile}...`);
379
- try {
380
- const result = await apiCall("/instances/create-from-backup", {
381
- method: "POST",
382
- body: { source_id: sourceId, backup_file: backupFile, new_id: newId, new_name: newId },
383
- });
384
- if (result.ok) {
385
- console.log(`Instance created: ${result.instance_id}`);
386
- if (result.warnings?.length)
387
- result.warnings.forEach((w) => console.log(` Warning: ${w}`));
388
- }
389
- else {
390
- console.error(`Create from backup failed: ${result.detail || "unknown error"}`);
391
- if (result.warnings?.length)
392
- result.warnings.forEach((w) => console.log(` Warning: ${w}`));
393
- process.exit(1);
394
- }
395
- }
396
- catch (e) {
397
- console.error(`Restore --new failed: ${e.message}`);
398
- process.exit(1);
399
- }
400
- }
401
- else {
402
- // restore --new <file> [--id <new-id>]
403
- const file = rest.find((a) => !a.startsWith("--") && a !== "restore");
404
- if (!file) {
405
- console.error("Usage: jishushell restore --new <file> [--id <new-id>]");
406
- process.exit(1);
407
- }
408
- console.log("Note: restore --new from local file requires import flow.");
409
- console.log(`Use: jishushell import ${file}`);
410
- }
411
- process.exit(0);
412
- }
413
- const id = rest[0];
414
- if (!id) {
415
- console.error("Usage: jishushell restore <id> <--latest|--pick|file>");
416
- process.exit(1);
417
- }
418
- let file = "";
419
- if (rest.includes("--latest") || rest.includes("--pick")) {
420
- let backups;
421
- try {
422
- backups = await apiCall(`/instances/${id}/backups`);
423
- }
424
- catch (e) {
425
- console.error(`Restore failed: ${e.message}`);
426
- process.exit(1);
427
- }
428
- if (!backups.length) {
429
- console.error("No backups found.");
430
- process.exit(1);
431
- }
432
- if (rest.includes("--latest")) {
433
- file = backups[0].filename;
434
- }
435
- else {
436
- console.log("Available backups:");
437
- backups.forEach((b, i) => {
438
- console.log(` ${i + 1}. ${b.type} ${new Date(b.created_at).toLocaleString()} (${(b.size / 1024 / 1024).toFixed(1)}MB)`);
439
- });
440
- const readline = await import("readline");
441
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
442
- const answer = await new Promise(r => rl.question("Select number: ", r));
443
- rl.close();
444
- const idx = parseInt(answer, 10) - 1;
445
- if (idx < 0 || idx >= backups.length) {
446
- console.error("Invalid selection.");
447
- process.exit(1);
448
- }
449
- file = backups[idx].filename;
450
- }
451
- }
452
- else {
453
- file = rest[1];
454
- }
455
- if (!file) {
456
- console.error("No backup file specified.");
457
- process.exit(1);
458
- }
459
- try {
460
- console.log(`Restoring ${id} from ${file}...`);
461
- const result = await apiCall(`/instances/${id}/restore/${encodeURIComponent(file)}`, { method: "POST" });
462
- if (result.job_id) {
463
- const job = await waitForJob(result.job_id);
464
- const r = job.result || {};
465
- if (r.ok) {
466
- console.log(`\nRestore complete.`);
467
- if (r.api_key_status === "lost") {
468
- console.log("Warning: archive was built on a different platform/arch; verify API Key configuration still matches this host.");
469
- }
470
- if (r.warnings?.length)
471
- r.warnings.forEach((w) => console.log(` Warning: ${w}`));
472
- }
473
- else {
474
- console.error(`\nRestore failed.${r.rolled_back ? " Auto-rollback completed." : ""}`);
475
- if (r.warnings?.length)
476
- r.warnings.forEach((w) => console.log(` ${w}`));
477
- }
478
- }
479
- }
480
- catch (e) {
481
- console.error(`Restore failed: ${e.message}`);
482
- process.exit(1);
483
- }
484
- process.exit(0);
485
- // ── export ─────────────────────────────────────────────────────────────────
486
- }
487
- else if (subcommand === "export") {
488
- const id = rest[0];
489
- if (!id) {
490
- console.error("Usage: jishushell export <id> [--with-sessions]");
491
- process.exit(1);
492
- }
493
- const withSessions = rest.includes("--with-sessions");
494
- try {
495
- console.log(`Exporting ${id}...`);
496
- const result = await apiCall(`/instances/${id}/export`, {
497
- method: "POST",
498
- body: { include_sessions: withSessions },
499
- });
500
- if (result.job_id) {
501
- const job = await waitForJob(result.job_id);
502
- const r = job.result || {};
503
- console.log(`\nExport complete: ${r.filename}`);
504
- console.log(`Download: http://127.0.0.1:${process.env.JISHUSHELL_PORT || "8090"}/api/instances/${id}/export/download/${encodeURIComponent(r.filename)}`);
505
- if (r.warnings?.length)
506
- r.warnings.forEach((w) => console.log(` Warning: ${w}`));
507
- }
508
- }
509
- catch (e) {
510
- console.error(`Export failed: ${e.message}`);
511
- process.exit(1);
512
- }
513
- process.exit(0);
514
- // ── import ─────────────────────────────────────────────────────────────────
515
- }
516
- else if (subcommand === "import") {
517
- const file = rest[0];
518
- if (!file) {
519
- console.error("Usage: jishushell import <file> [--id <id>] [--name <name>]");
520
- process.exit(1);
521
- }
522
- const idIdx = rest.indexOf("--id");
523
- const nameIdx = rest.indexOf("--name");
524
- const customId = idIdx !== -1 ? rest[idIdx + 1] : undefined;
525
- const customName = nameIdx !== -1 ? rest[nameIdx + 1] : undefined;
526
- try {
527
- const { readFileSync } = await import("fs");
528
- const { basename } = await import("path");
529
- const port = process.env.JISHUSHELL_PORT || "8090";
530
- console.log("Uploading file...");
531
- const fileData = readFileSync(file);
532
- const boundary = "----JishuShellCLI" + Date.now();
533
- const fileName = basename(file);
534
- const body = Buffer.concat([
535
- Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${fileName}"\r\nContent-Type: application/gzip\r\n\r\n`),
536
- fileData,
537
- Buffer.from(`\r\n--${boundary}--\r\n`),
538
- ]);
539
- const uploadRes = await fetch(`http://127.0.0.1:${port}/api/instances/import/upload`, {
540
- method: "POST",
541
- headers: { "Content-Type": `multipart/form-data; boundary=${boundary}` },
542
- body,
543
- });
544
- if (!uploadRes.ok) {
545
- const err = await uploadRes.json().catch(() => ({ detail: uploadRes.statusText }));
546
- throw new Error(err.detail || `Upload failed: ${uploadRes.status}`);
547
- }
548
- const { temp_id } = await uploadRes.json();
549
- console.log("Previewing...");
550
- const preview = await apiCall("/instances/import/preview", { method: "POST", body: { temp_id } });
551
- console.log(` Name: ${preview.manifest?.name || "unknown"}`);
552
- console.log(` Type: ${preview.manifest?.type || "unknown"}`);
553
- console.log(` Size: ${((preview.estimated_size || 0) / 1024 / 1024).toFixed(1)}MB`);
554
- if (preview.warnings?.length) {
555
- for (const w of preview.warnings)
556
- console.log(` Warning: ${w}`);
557
- }
558
- const instanceId = customId || preview.manifest?.name?.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 63) || `import-${Date.now()}`;
559
- const instanceName = customName || preview.manifest?.name || instanceId;
560
- console.log(`Creating instance ${instanceId}...`);
561
- const result = await apiCall("/instances/import", {
562
- method: "POST",
563
- body: { temp_id, id: instanceId, name: instanceName },
564
- });
565
- if (result.ok) {
566
- console.log(`Instance created: ${result.instance_id}`);
567
- if (result.warnings?.length) {
568
- for (const w of result.warnings)
569
- console.log(` Warning: ${w}`);
570
- }
571
- }
572
- else {
573
- console.error(`Import failed: ${result.detail || "unknown error"}`);
574
- if (result.warnings?.length) {
575
- for (const w of result.warnings)
576
- console.log(` Warning: ${w}`);
577
- }
578
- process.exit(1);
579
- }
580
- }
581
- catch (e) {
582
- console.error(`Import failed: ${e.message}`);
583
- process.exit(1);
584
- }
585
- process.exit(0);
586
- // ── clone ──────────────────────────────────────────────────────────────────
587
- }
588
- else if (subcommand === "clone") {
589
- const src = rest[0];
590
- const newId = rest[1];
591
- if (!src || !newId) {
592
- console.error("Usage: jishushell clone <src-id> <new-id> [--with-sessions] [--no-memory]");
593
- process.exit(1);
594
- }
595
- const withSessions = rest.includes("--with-sessions");
596
- const noMemory = rest.includes("--no-memory");
597
- try {
598
- console.log(`Cloning ${src} to ${newId}...`);
599
- const result = await apiCall("/instances", {
600
- method: "POST",
601
- body: {
602
- id: newId,
603
- name: newId,
604
- clone_from: src,
605
- clone_options: {
606
- include_sessions: withSessions,
607
- include_memory: !noMemory,
608
- },
609
- },
610
- });
611
- console.log(`Clone created: ${result.id || newId}`);
612
- }
613
- catch (e) {
614
- console.error(`Clone failed: ${e.message}`);
615
- process.exit(1);
616
- }
617
- process.exit(0);
618
- // ── backups ────────────────────────────────────────────────────────────────
619
- }
620
- else if (subcommand === "backups") {
621
- const sub = rest[0];
622
- if (sub === "list") {
623
- const id = rest[1];
624
- try {
625
- if (id) {
626
- const backups = await apiCall(`/instances/${id}/backups`);
627
- if (!backups.length) {
628
- console.log("No backups.");
629
- }
630
- else {
631
- backups.forEach((b, i) => {
632
- console.log(` ${i + 1}. [${b.type}] ${new Date(b.created_at).toLocaleString()} ${(b.size / 1024 / 1024).toFixed(1)}MB ${b.filename}`);
633
- });
634
- }
635
- }
636
- else {
637
- const orphans = await apiCall("/backups/orphaned");
638
- if (orphans.length) {
639
- console.log("Orphaned backups:");
640
- for (const o of orphans) {
641
- console.log(` ${o.instance_id}: ${o.backups?.length || 0} files`);
642
- }
643
- }
644
- else {
645
- console.log("No orphaned backups.");
646
- }
647
- }
648
- }
649
- catch (e) {
650
- console.error(`backups list failed: ${e.message}`);
651
- process.exit(1);
652
- }
653
- }
654
- else if (sub === "clean") {
655
- try {
656
- const orphans = await apiCall("/backups/orphaned");
657
- if (!orphans.length) {
658
- console.log("No orphaned backups to clean.");
659
- process.exit(0);
660
- }
661
- const force = rest.includes("--force");
662
- console.log(`Found ${orphans.length} orphaned backup(s):`);
663
- for (const o of orphans) {
664
- console.log(` ${o.instance_id}: ${o.backups?.length || 0} files`);
665
- }
666
- if (!force) {
667
- const readline = await import("readline");
668
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
669
- const answer = await new Promise(r => rl.question("Delete all orphaned backups? (y/N): ", r));
670
- rl.close();
671
- if (answer.toLowerCase() !== "y") {
672
- console.log("Cancelled.");
673
- process.exit(0);
674
- }
675
- }
676
- for (const o of orphans) {
677
- for (const b of o.backups || []) {
678
- await apiCall(`/backups/orphaned/${encodeURIComponent(o.instance_id)}/${encodeURIComponent(b.filename)}`, { method: "DELETE" });
679
- }
680
- console.log(` Cleaned: ${o.instance_id}`);
681
- }
682
- console.log("Done.");
683
- }
684
- catch (e) {
685
- console.error(`backups clean failed: ${e.message}`);
686
- process.exit(1);
687
- }
688
- }
689
- else {
690
- console.error("Usage: jishushell backups list [id]\n jishushell backups clean [--force]");
691
- process.exit(1);
692
- }
693
- process.exit(0);
694
- // ── auto-backup ────────────────────────────────────────────────────────────
695
- }
696
- else if (subcommand === "auto-backup") {
697
- const id = rest[0];
698
- const action = rest[1];
699
- if (!id || !action) {
700
- console.error("Usage: jishushell auto-backup <id> enable|disable|status");
701
- process.exit(1);
702
- }
57
+ let handled = false;
58
+ for (const load of CLI_MODULE_LOADERS) {
703
59
  try {
704
- if (action === "status") {
705
- const config = await apiCall(`/instances/${id}/auto-backup`);
706
- console.log(JSON.stringify(config, null, 2));
707
- }
708
- else if (action === "enable") {
709
- const interval = parseInt(rest[rest.indexOf("--interval") + 1], 10) || 24;
710
- const keep = parseInt(rest[rest.indexOf("--keep") + 1], 10) || 7;
711
- await apiCall(`/instances/${id}/auto-backup`, { method: "PUT", body: { enabled: true, interval_hours: interval, keep_count: keep } });
712
- console.log(`Auto-backup enabled for ${id} (every ${interval}h, keep ${keep})`);
713
- }
714
- else if (action === "disable") {
715
- await apiCall(`/instances/${id}/auto-backup`, { method: "PUT", body: { enabled: false } });
716
- console.log(`Auto-backup disabled for ${id}`);
717
- }
718
- else {
719
- console.error(`Unknown action: ${action}. Use enable|disable|status`);
720
- process.exit(1);
60
+ const module = await load();
61
+ if (await module.dispatch(argv)) {
62
+ handled = true;
63
+ break;
721
64
  }
722
65
  }
723
- catch (e) {
724
- console.error(`auto-backup failed: ${e.message}`);
725
- process.exit(1);
66
+ catch (err) {
67
+ cliError(err);
726
68
  }
727
- process.exit(0);
728
- // ── help ───────────────────────────────────────────────────────────────────
729
69
  }
730
- else if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
731
- console.log(`
732
- JishuShell — OpenClaw 实例管理面板
733
-
734
- Usage:
735
- jishushell 前台启动面板服务器
736
- jishushell <command> [options]
737
-
738
- Commands:
739
- status 显示各模块运行状态(面板、Docker、Nomad 及所有 Job)
740
- serve [--port N] [--host H] 前台启动面板服务器(供 systemd/launchd 使用)
741
- start [--port N] 在后台启动面板
742
- stop [--jobs] 停止面板;加 --jobs 同时停止所有 Nomad Job
743
- restart [--port N] 重启面板
744
- doctor 检查所有组件健康状态
745
- doctor --fix 检查并尝试自动修复问题
746
- onboard [--force] 首次引导:设置管理员密码
747
- reset [--yes] 重置配置与所有 Nomad Job(保留实例数据目录)
748
- uninstall [--yes] 停止服务、删除 ~/.jishushell 并卸载 npm 包
749
- update-check 检查是否有新版本(0=有新版本,1=已是最新)
750
- update 安装最新版本并重启面板
751
- install [options] 安装依赖并配置 JishuShell
752
- llm list 列出已配置的 LLM provider
753
- llm add <name> <url> <key> 新增 LLM provider
754
- llm update <name> <url> <key> 更新 LLM provider
755
- llm delete <name> 删除 LLM provider
756
- llm set-default <name> 设置默认 LLM provider
757
- llm help LLM 子命令帮助
758
- job list 列出所有实例及运行状态
759
- job status <id> 查看实例详细状态
760
- job start/stop/restart <id> 启停实例
761
- job logs <id> 查看实例日志
762
- job exec <id> -- <cmd> 在容器内执行命令
763
- pairing list 列出等待审批的 DM 配对请求
764
- pairing approve <ch> <code> 审批飞书/Lark 配对码(在容器内执行)
765
- backup <id> [--with-sessions] [--only-config] 备份实例(加 --verify 校验完整性)
766
- backup verify <id> <filename> 校验备份文件完整性
767
- restore <id> <--latest|--pick|file> 从备份文件还原实例
768
- restore --new --from <src-id> --latest|--pick [--id <new-id>] 从备份创建新实例
769
- export <id> [--with-sessions] 导出实例为可迁移归档包
770
- import <file> [--id <id>] [--name <name>] 导入归档包为新实例
771
- clone <src-id> <new-id> [--with-sessions] [--no-memory] 克隆实例
772
- backups list [id] 列出备份(不指定 id 则列孤立备份)
773
- backups clean [--force] 清理孤立备份文件
774
- auto-backup <id> enable|disable|status 管理定时自动备份
775
- help 显示此帮助信息
776
-
777
- Server options:
778
- --port <port> 监听端口(默认 8090 或 panel.json 中的值)
779
- --host <host> 监听地址(默认 0.0.0.0)
780
-
781
- Install options:
782
- --port <port> 面板端口(默认 8090)
783
- --yes, -y 跳过所有交互提示(非交互模式)
784
- --no-service 跳过 systemd/launchd 注册
785
- --skip-docker 跳过 Docker 安装
786
- --skip-nomad 跳过 Nomad 安装
787
- --skip-openclaw 跳过 OpenClaw 安装
788
- --force, -f 即使已配置也强制重新安装
789
- --dry-run, -n 预演,不实际执行任何变更
790
-
791
- Examples:
792
- jishushell status # 查看当前所有组件状态
793
- jishushell start # 后台启动面板
794
- jishushell stop --jobs # 停止面板及所有实例 Job
795
- jishushell restart # 重启面板
796
- jishushell doctor --fix # 检查并自动修复
797
- jishushell reset --yes # 非交互式重置(CI 场景)
798
- `);
799
- process.exit(0);
800
- // ── unknown command ────────────────────────────────────────────────────────
70
+ if (handled) {
71
+ // Let long-running commands like `serve` keep the event loop alive naturally.
801
72
  }
802
73
  else {
803
- console.error(`Unknown command: ${subcommand}\nRun 'jishushell help' for usage.`);
74
+ console.error(`Unknown command: ${subcommand}`);
75
+ await printTopLevelHelp();
804
76
  process.exit(1);
805
77
  }
806
78
  //# sourceMappingURL=cli.js.map