jishushell 0.4.2 → 0.4.17
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/Dockerfile.openclaw-slim +58 -0
- package/INSTALL-NOTICE +45 -0
- package/dist/auth.js +3 -3
- package/dist/auth.js.map +1 -1
- package/dist/cli/app.d.ts +3 -0
- package/dist/cli/app.js +156 -0
- package/dist/cli/app.js.map +1 -0
- package/dist/{doctor.d.ts → cli/doctor.d.ts} +6 -1
- package/dist/{doctor.js → cli/doctor.js} +389 -27
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/helpers.d.ts +4 -0
- package/dist/cli/helpers.js +32 -0
- package/dist/cli/helpers.js.map +1 -0
- package/dist/cli/job.d.ts +3 -0
- package/dist/cli/job.js +260 -0
- package/dist/cli/job.js.map +1 -0
- package/dist/cli/llm.d.ts +24 -0
- package/dist/cli/llm.js +593 -0
- package/dist/cli/llm.js.map +1 -0
- package/dist/cli/openclaw.d.ts +12 -0
- package/dist/cli/openclaw.js +156 -0
- package/dist/cli/openclaw.js.map +1 -0
- package/dist/cli/panel.d.ts +25 -0
- package/dist/cli/panel.js +734 -0
- package/dist/cli/panel.js.map +1 -0
- package/dist/cli.js +476 -219
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +22 -4
- package/dist/config.js +96 -55
- package/dist/config.js.map +1 -1
- package/dist/control.d.ts +13 -41
- package/dist/control.js +12 -1355
- package/dist/control.js.map +1 -1
- package/dist/install.d.ts +1 -1
- package/dist/install.js +15 -29
- package/dist/install.js.map +1 -1
- package/dist/routes/apps.d.ts +3 -0
- package/dist/routes/apps.js +99 -0
- package/dist/routes/apps.js.map +1 -0
- package/dist/routes/backup.d.ts +2 -0
- package/dist/routes/backup.js +370 -0
- package/dist/routes/backup.js.map +1 -0
- package/dist/routes/instances.d.ts +1 -0
- package/dist/routes/instances.js +61 -15
- package/dist/routes/instances.js.map +1 -1
- package/dist/routes/llm.d.ts +15 -0
- package/dist/routes/llm.js +246 -0
- package/dist/routes/llm.js.map +1 -0
- package/dist/routes/setup.js +32 -7
- package/dist/routes/setup.js.map +1 -1
- package/dist/routes/system.js +31 -6
- package/dist/routes/system.js.map +1 -1
- package/dist/server.js +69 -5
- package/dist/server.js.map +1 -1
- package/dist/services/app-compiler.d.ts +15 -0
- package/dist/services/app-compiler.js +169 -0
- package/dist/services/app-compiler.js.map +1 -0
- package/dist/services/app-manager.d.ts +17 -0
- package/dist/services/app-manager.js +168 -0
- package/dist/services/app-manager.js.map +1 -0
- package/dist/services/backup-manager.d.ts +253 -0
- package/dist/services/backup-manager.js +2014 -0
- package/dist/services/backup-manager.js.map +1 -0
- package/dist/services/backup-verify.d.ts +26 -0
- package/dist/services/backup-verify.js +240 -0
- package/dist/services/backup-verify.js.map +1 -0
- package/dist/services/instance-manager.d.ts +73 -5
- package/dist/services/instance-manager.js +446 -74
- package/dist/services/instance-manager.js.map +1 -1
- package/dist/services/job-manager.d.ts +22 -0
- package/dist/services/job-manager.js +102 -0
- package/dist/services/job-manager.js.map +1 -0
- package/dist/services/llm-proxy/adapters.js +5 -1
- package/dist/services/llm-proxy/adapters.js.map +1 -1
- package/dist/services/llm-proxy/index.d.ts +30 -0
- package/dist/services/llm-proxy/index.js +71 -1
- package/dist/services/llm-proxy/index.js.map +1 -1
- package/dist/services/llm-proxy/ssrf.js +1 -1
- package/dist/services/llm-proxy/ssrf.js.map +1 -1
- package/dist/services/nomad-manager.js +263 -159
- package/dist/services/nomad-manager.js.map +1 -1
- package/dist/services/panel-manager.d.ts +40 -0
- package/dist/services/panel-manager.js +346 -0
- package/dist/services/panel-manager.js.map +1 -0
- package/dist/services/process-manager.js +24 -10
- package/dist/services/process-manager.js.map +1 -1
- package/dist/services/setup-manager.d.ts +4 -2
- package/dist/services/setup-manager.js +578 -154
- package/dist/services/setup-manager.js.map +1 -1
- package/dist/services/telemetry/activation.js +10 -7
- package/dist/services/telemetry/activation.js.map +1 -1
- package/dist/services/telemetry/client.js +7 -18
- package/dist/services/telemetry/client.js.map +1 -1
- package/dist/services/telemetry/heartbeat.js +12 -6
- package/dist/services/telemetry/heartbeat.js.map +1 -1
- package/dist/services/update-manager.d.ts +47 -0
- package/dist/services/update-manager.js +305 -0
- package/dist/services/update-manager.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/utils/fs.d.ts +85 -0
- package/dist/utils/fs.js +111 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/safe-json.d.ts +2 -0
- package/dist/utils/safe-json.js +22 -16
- package/dist/utils/safe-json.js.map +1 -1
- package/install/jishu-install.sh +582 -138
- package/install/jishu-uninstall.sh +276 -391
- package/install/post-install.sh +85 -3
- package/openclaw-entry.sh +15 -0
- package/package.json +12 -5
- package/public/assets/Dashboard-CQsp1Mr9.js +1 -0
- package/public/assets/InitPassword-BEC8SE4A.js +1 -0
- package/public/assets/InstanceDetail-B5wTgNEg.js +17 -0
- package/public/assets/{Login-RkjzTNWg.js → Login-D1Bt-Lyk.js} +1 -1
- package/public/assets/NewInstance-GQzm3K9D.js +1 -0
- package/public/assets/Settings-ByjGlqhP.js +1 -0
- package/public/assets/Setup-cMF21Y-8.js +1 -0
- package/public/assets/index-B6qQP4mH.css +1 -0
- package/public/assets/index-BuTQtuNy.js +16 -0
- package/public/assets/logo-black-theme-DywLAtFy.png +0 -0
- package/public/assets/logo-white-theme-DXffFAWw.png +0 -0
- package/public/assets/{providers-lBSOjUWy.js → providers-V-vwrExZ.js} +1 -1
- package/public/assets/{usePolling-CqQ8hrNc.js → usePolling-CK0DfI4h.js} +1 -1
- package/public/assets/{vendor-i18n-Bvxxh8Di.js → vendor-i18n-CfW0RvgE.js} +1 -1
- package/public/assets/vendor-react-B1-3Yrt-.js +59 -0
- package/public/index.html +4 -4
- package/dist/doctor.js.map +0 -1
- package/public/assets/Dashboard-CAOQDYDR.js +0 -1
- package/public/assets/InitPassword-CkehIkJG.js +0 -1
- package/public/assets/InstanceDetail-CzW2S95J.js +0 -14
- package/public/assets/NewInstance-DdbErdjA.js +0 -1
- package/public/assets/Settings-BUD7zwv9.js +0 -1
- package/public/assets/Setup-RRTIERGG.js +0 -1
- package/public/assets/index-77Ug7feY.css +0 -1
- package/public/assets/index-DfRnVUQR.js +0 -16
- package/public/assets/vendor-react-DONn7uBV.js +0 -59
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/cli/panel.ts
|
|
3
|
+
* 面板管理统一模块 — 整合 start/stop/restart/status/serve/onboard/reset/update/config/install/uninstall
|
|
4
|
+
*
|
|
5
|
+
* 依赖层次:
|
|
6
|
+
* cli/panel.ts → services/panel-manager.ts (纯逻辑,无 I/O)
|
|
7
|
+
* cli/panel.ts → services/update-manager.ts (纯逻辑,无 I/O)
|
|
8
|
+
* cli/panel.ts → services/job-manager.ts (纯逻辑,无 I/O)
|
|
9
|
+
* 所有终端输出/格式化均在本文件完成。
|
|
10
|
+
*/
|
|
11
|
+
import { execSync, execFileSync } from "child_process";
|
|
12
|
+
import { existsSync, unlinkSync } from "fs";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { networkInterfaces } from "os";
|
|
15
|
+
import { parsePort, hasFlag } from "./helpers.js";
|
|
16
|
+
import { detectRunMode, getPanelPort, isPortListening, readAlivePid, systemdStatus, startPanel, stopPanel, restartPanel, } from "../services/panel-manager.js";
|
|
17
|
+
import { checkUpdate, upgradePackage, scheduleRestart, getInstalledGlobalVersion, isVersionAtLeast, markUpdateRestarting, markUpdateFailed, } from "../services/update-manager.js";
|
|
18
|
+
import { getSvc, listInstanceIds, readInstanceMeta } from "../services/job-manager.js";
|
|
19
|
+
import { deleteInstance } from "../services/instance-manager.js";
|
|
20
|
+
import { JISHUSHELL_HOME, getPanelConfig, getNomadToken, getNomadAddr, savePanelConfig } from "../config.js";
|
|
21
|
+
import { loadNomadToken } from "../services/setup-manager.js";
|
|
22
|
+
import { isInitialized, initPassword } from "../auth.js";
|
|
23
|
+
const DEFAULT_PORT = 8090;
|
|
24
|
+
// ── ANSI colours (CLI-only, stripped when not a TTY) ──────────────────────
|
|
25
|
+
const isTTY = process.stdout.isTTY ?? false;
|
|
26
|
+
const c = {
|
|
27
|
+
bold: (s) => isTTY ? `\x1b[1m${s}\x1b[0m` : s,
|
|
28
|
+
green: (s) => isTTY ? `\x1b[32m${s}\x1b[0m` : s,
|
|
29
|
+
yellow: (s) => isTTY ? `\x1b[33m${s}\x1b[0m` : s,
|
|
30
|
+
red: (s) => isTTY ? `\x1b[31m${s}\x1b[0m` : s,
|
|
31
|
+
cyan: (s) => isTTY ? `\x1b[36m${s}\x1b[0m` : s,
|
|
32
|
+
dim: (s) => isTTY ? `\x1b[2m${s}\x1b[0m` : s,
|
|
33
|
+
};
|
|
34
|
+
function log(msg) { process.stdout.write(msg + "\n"); }
|
|
35
|
+
// ── 面板进程生命周期 ─────────────────────────────────────────────────────────
|
|
36
|
+
export async function runStart(rest) {
|
|
37
|
+
const result = await startPanel(parsePort(rest));
|
|
38
|
+
if (result.alreadyRunning) {
|
|
39
|
+
log(c.yellow(`! JishuShell is already running on port ${result.port}`));
|
|
40
|
+
log(c.dim(` Run ${c.bold("jishushell stop")} first, or ${c.bold("jishushell restart")}`));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
log(c.dim(` run mode: ${result.mode} | port: ${result.port}`));
|
|
44
|
+
log(c.dim(" Waiting for panel to start…"));
|
|
45
|
+
if (result.started) {
|
|
46
|
+
log(c.green(`✓ JishuShell started → http://localhost:${result.port}`));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
log(c.yellow(`! Port ${result.port} still not listening after 10s.`));
|
|
50
|
+
if (result.logFile) {
|
|
51
|
+
log(c.dim(` Check logs: ${result.logFile}`));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
log(c.dim(" Check logs: journalctl -u jishushell -n 50"));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export async function runStop(rest) {
|
|
59
|
+
const withJobs = hasFlag(rest, "--jobs", "-j");
|
|
60
|
+
if (withJobs) {
|
|
61
|
+
loadNomadToken();
|
|
62
|
+
const instanceIds = listInstanceIds();
|
|
63
|
+
if (instanceIds.length > 0) {
|
|
64
|
+
log(c.dim(` Stopping ${instanceIds.length} Nomad job(s)…`));
|
|
65
|
+
const svc = await getSvc();
|
|
66
|
+
await Promise.allSettled(instanceIds.map(async (id) => {
|
|
67
|
+
const meta = readInstanceMeta(id);
|
|
68
|
+
const name = meta?.name || id;
|
|
69
|
+
const result = await svc.stopInstance(id);
|
|
70
|
+
if (result.ok) {
|
|
71
|
+
log(` ${c.green("✓")} stopped job: ${name}`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
log(` ${c.dim("–")} ${result.error || "already stopped"}: ${name}`);
|
|
75
|
+
}
|
|
76
|
+
}));
|
|
77
|
+
log("");
|
|
78
|
+
}
|
|
79
|
+
log(c.dim(" Stopping JishuShell panel…"));
|
|
80
|
+
}
|
|
81
|
+
const result = await stopPanel();
|
|
82
|
+
log(c.dim(` run mode: ${result.mode}`));
|
|
83
|
+
log(c.green("✓ JishuShell stopped"));
|
|
84
|
+
}
|
|
85
|
+
export async function runRestart(rest) {
|
|
86
|
+
const result = await restartPanel(parsePort(rest));
|
|
87
|
+
log(c.dim(` run mode: ${result.mode} | port: ${result.port}`));
|
|
88
|
+
if (result.started) {
|
|
89
|
+
log(c.green(`✓ JishuShell restarted → http://localhost:${result.port}`));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
log(c.yellow("! Panel did not come back up within 10s."));
|
|
93
|
+
if (result.logFile) {
|
|
94
|
+
log(c.dim(` Check logs: ${result.logFile}`));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
log(c.dim(" Check: journalctl -u jishushell -n 50"));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// ── status ────────────────────────────────────────────────────────────────
|
|
102
|
+
export async function runStatus() {
|
|
103
|
+
loadNomadToken();
|
|
104
|
+
const port = getPanelPort();
|
|
105
|
+
const mode = detectRunMode();
|
|
106
|
+
const nomadAddr = getNomadAddr();
|
|
107
|
+
log("");
|
|
108
|
+
log(c.bold(c.cyan(" JishuShell Status")));
|
|
109
|
+
log(c.dim(" ─────────────────────────────────────────────────────"));
|
|
110
|
+
log("");
|
|
111
|
+
// ── 1. Panel ──────────────────────────────────────────────────────────
|
|
112
|
+
log(` ${c.bold("Panel (CLI)")}`);
|
|
113
|
+
const panelListening = isPortListening(port);
|
|
114
|
+
const pid = readAlivePid();
|
|
115
|
+
if (panelListening) {
|
|
116
|
+
log(` ${c.green("●")} ${c.green("running")} → port :${port}`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
log(` ${c.red("○")} ${c.red("stopped")}`);
|
|
120
|
+
}
|
|
121
|
+
log(` ${c.dim("run mode:")} ${mode}`);
|
|
122
|
+
if (mode === "systemd") {
|
|
123
|
+
const svcStatus = systemdStatus();
|
|
124
|
+
log(` ${c.dim("systemd:")} ${svcStatus === "active" ? c.green(svcStatus) : c.yellow(svcStatus)}`);
|
|
125
|
+
}
|
|
126
|
+
else if (mode === "process" && pid !== null) {
|
|
127
|
+
log(` ${c.dim("pid:")} ${pid}`);
|
|
128
|
+
}
|
|
129
|
+
log(` ${c.dim("url:")} http://localhost:${port}`);
|
|
130
|
+
log("");
|
|
131
|
+
// ── 2. Docker ─────────────────────────────────────────────────────────
|
|
132
|
+
log(` ${c.bold("Docker")}`);
|
|
133
|
+
let dockerOk = false;
|
|
134
|
+
let dockerDetail = "daemon unreachable";
|
|
135
|
+
try {
|
|
136
|
+
execFileSync("docker", ["info"], { stdio: "ignore", timeout: 5000 });
|
|
137
|
+
const ver = execSync("docker --version 2>/dev/null", { encoding: "utf-8", timeout: 3000 }).trim();
|
|
138
|
+
dockerOk = true;
|
|
139
|
+
dockerDetail = ver.replace("Docker version ", "").split(",")[0];
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
try {
|
|
143
|
+
execFileSync("sudo", ["-n", "docker", "info"], { stdio: "ignore", timeout: 5000 });
|
|
144
|
+
dockerOk = true;
|
|
145
|
+
dockerDetail = "running (via sudo — re-login to activate docker group)";
|
|
146
|
+
}
|
|
147
|
+
catch { }
|
|
148
|
+
}
|
|
149
|
+
if (dockerOk) {
|
|
150
|
+
log(` ${c.green("●")} ${c.green("running")} ${c.dim(dockerDetail)}`);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
log(` ${c.red("○")} ${c.red("stopped")} ${c.dim(dockerDetail)}`);
|
|
154
|
+
}
|
|
155
|
+
log("");
|
|
156
|
+
// ── 3. Nomad ──────────────────────────────────────────────────────────
|
|
157
|
+
log(` ${c.bold("Nomad")}`);
|
|
158
|
+
const token = getNomadToken();
|
|
159
|
+
const nomadHeaders = token ? { "X-Nomad-Token": token } : {};
|
|
160
|
+
let nomadAgent = null;
|
|
161
|
+
try {
|
|
162
|
+
const res = await fetch(`${nomadAddr}/v1/agent/self`, {
|
|
163
|
+
headers: nomadHeaders, signal: AbortSignal.timeout(5000),
|
|
164
|
+
});
|
|
165
|
+
if (res.ok)
|
|
166
|
+
nomadAgent = await res.json();
|
|
167
|
+
}
|
|
168
|
+
catch { /* unreachable */ }
|
|
169
|
+
const nomadRunning = nomadAgent !== null;
|
|
170
|
+
if (nomadRunning) {
|
|
171
|
+
const version = nomadAgent?.config?.Version?.Version || nomadAgent?.member?.Tags?.version || "";
|
|
172
|
+
log(` ${c.green("●")} ${c.green("running")} → ${nomadAddr}${version ? " " + c.dim("v" + version) : ""}`);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
log(` ${c.red("○")} ${c.red("stopped")} ${c.dim("(" + nomadAddr + " unreachable)")}`);
|
|
176
|
+
}
|
|
177
|
+
log("");
|
|
178
|
+
// ── 4. Jobs (instances) ───────────────────────────────────────────────
|
|
179
|
+
const instanceIds = listInstanceIds();
|
|
180
|
+
log(` ${c.bold("Nomad Jobs")} ${c.dim("(" + instanceIds.length + " instance" + (instanceIds.length !== 1 ? "s" : "") + ")")}`);
|
|
181
|
+
if (instanceIds.length === 0) {
|
|
182
|
+
log(` ${c.dim("(no instances created)")}`);
|
|
183
|
+
}
|
|
184
|
+
else if (!nomadRunning) {
|
|
185
|
+
log(` ${c.dim("(Nomad not reachable — cannot query job status)")}`);
|
|
186
|
+
for (const id of instanceIds) {
|
|
187
|
+
const meta = readInstanceMeta(id);
|
|
188
|
+
log(` ${c.dim("??")} ${(meta?.name || id).padEnd(20)} ${c.dim(id)}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
const svc = await getSvc();
|
|
193
|
+
for (const id of instanceIds) {
|
|
194
|
+
const meta = readInstanceMeta(id);
|
|
195
|
+
let statusStr = "unknown";
|
|
196
|
+
let statusColor = c.dim;
|
|
197
|
+
try {
|
|
198
|
+
const st = await svc.getStatus(id);
|
|
199
|
+
statusStr = st.status;
|
|
200
|
+
if (st.status === "running")
|
|
201
|
+
statusColor = c.green;
|
|
202
|
+
else if (st.status === "pending")
|
|
203
|
+
statusColor = c.yellow;
|
|
204
|
+
else if (st.status === "failed")
|
|
205
|
+
statusColor = c.red;
|
|
206
|
+
}
|
|
207
|
+
catch { /* keep unknown */ }
|
|
208
|
+
const runtime = meta?.runtime;
|
|
209
|
+
let portInfo = "";
|
|
210
|
+
if (runtime) {
|
|
211
|
+
const envPort = runtime.env?.OPENCLAW_GATEWAY_PORT;
|
|
212
|
+
const args = runtime.args || [];
|
|
213
|
+
const argsPort = (() => {
|
|
214
|
+
for (let i = 0; i < args.length - 1; i++) {
|
|
215
|
+
if (args[i] === "--port")
|
|
216
|
+
return args[i + 1];
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
})();
|
|
220
|
+
const gp = envPort || argsPort;
|
|
221
|
+
if (gp)
|
|
222
|
+
portInfo = c.dim(` gateway :${gp}`);
|
|
223
|
+
}
|
|
224
|
+
const dot = statusStr === "running" ? c.green("●")
|
|
225
|
+
: statusStr === "pending" ? c.yellow("●")
|
|
226
|
+
: statusStr === "failed" ? c.red("●")
|
|
227
|
+
: c.dim("○");
|
|
228
|
+
log(` ${dot} ${(meta?.name || id).padEnd(20)} ${statusColor(statusStr.padEnd(14))}${portInfo}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
log("");
|
|
232
|
+
}
|
|
233
|
+
// ── onboard ───────────────────────────────────────────────────────────────
|
|
234
|
+
function getLocalIP() {
|
|
235
|
+
try {
|
|
236
|
+
for (const list of Object.values(networkInterfaces())) {
|
|
237
|
+
for (const iface of list ?? []) {
|
|
238
|
+
if (!iface.internal && iface.family === "IPv4")
|
|
239
|
+
return iface.address;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch { }
|
|
244
|
+
return "127.0.0.1";
|
|
245
|
+
}
|
|
246
|
+
function readPassword(prompt) {
|
|
247
|
+
return new Promise((resolve, reject) => {
|
|
248
|
+
process.stdout.write(prompt);
|
|
249
|
+
if (process.stdin.isTTY) {
|
|
250
|
+
process.stdin.setRawMode(true);
|
|
251
|
+
process.stdin.resume();
|
|
252
|
+
let input = "";
|
|
253
|
+
const onData = (buf) => {
|
|
254
|
+
const char = buf.toString("utf-8");
|
|
255
|
+
if (char === "\r" || char === "\n") {
|
|
256
|
+
process.stdin.setRawMode(false);
|
|
257
|
+
process.stdin.pause();
|
|
258
|
+
process.stdin.off("data", onData);
|
|
259
|
+
process.stdout.write("\n");
|
|
260
|
+
resolve(input);
|
|
261
|
+
}
|
|
262
|
+
else if (char === "\u0003") {
|
|
263
|
+
process.stdin.setRawMode(false);
|
|
264
|
+
process.stdout.write("\n");
|
|
265
|
+
reject(new Error("Interrupted"));
|
|
266
|
+
}
|
|
267
|
+
else if (char === "\u007f" || char === "\b") {
|
|
268
|
+
if (input.length > 0)
|
|
269
|
+
input = input.slice(0, -1);
|
|
270
|
+
}
|
|
271
|
+
else if (char >= " ") {
|
|
272
|
+
input += char;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
process.stdin.on("data", onData);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
let input = "";
|
|
279
|
+
process.stdin.resume();
|
|
280
|
+
process.stdin.setEncoding("utf-8");
|
|
281
|
+
const onData = (chunk) => {
|
|
282
|
+
const nl = chunk.indexOf("\n");
|
|
283
|
+
if (nl >= 0) {
|
|
284
|
+
input += chunk.slice(0, nl);
|
|
285
|
+
process.stdin.off("data", onData);
|
|
286
|
+
process.stdin.pause();
|
|
287
|
+
resolve(input.replace(/\r$/, "").trim());
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
input += chunk;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
process.stdin.on("data", onData);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
export async function runOnboard(rest) {
|
|
298
|
+
const force = hasFlag(rest, "--force", "-f");
|
|
299
|
+
log("");
|
|
300
|
+
log(c.bold(c.cyan(" JishuShell Onboard")));
|
|
301
|
+
log(c.dim(" ─────────────────────────────────────────"));
|
|
302
|
+
log("");
|
|
303
|
+
const cfg = getPanelConfig();
|
|
304
|
+
if (!cfg.service_manager) {
|
|
305
|
+
log(c.red(" ✗ JishuShell is not installed yet."));
|
|
306
|
+
log(c.dim(" Run ") + c.bold("jishushell install") + c.dim(" first."));
|
|
307
|
+
log("");
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
const port = getPanelPort();
|
|
311
|
+
if (!isPortListening(port)) {
|
|
312
|
+
log(c.yellow(` ! Panel is not running on port ${port}.`));
|
|
313
|
+
log(c.dim(" Start it with: ") + c.bold("jishushell start"));
|
|
314
|
+
log("");
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
const initialized = isInitialized();
|
|
318
|
+
if (initialized && !force) {
|
|
319
|
+
const ip = getLocalIP();
|
|
320
|
+
log(c.green(" ✓ JishuShell is already onboarded."));
|
|
321
|
+
log("");
|
|
322
|
+
log(` Panel: ${c.bold(c.cyan(`http://${ip}:${port}`))}`);
|
|
323
|
+
log(` Local: ${c.bold(c.cyan(`http://localhost:${port}`))}`);
|
|
324
|
+
log("");
|
|
325
|
+
log(c.dim(" Tip: ") + c.bold("jishushell onboard --force") + c.dim(" resets the admin password."));
|
|
326
|
+
log("");
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (initialized) {
|
|
330
|
+
log(c.yellow(" ! Resetting admin password (--force)."));
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
log(" " + c.bold("Welcome!") + " Set your admin password to start using JishuShell.");
|
|
334
|
+
}
|
|
335
|
+
log("");
|
|
336
|
+
let password = "";
|
|
337
|
+
for (;;) {
|
|
338
|
+
try {
|
|
339
|
+
password = await readPassword(" Password (≥8 chars): ");
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
log(c.yellow(" Interrupted."));
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
if (password.length < 8) {
|
|
346
|
+
log(c.red(" ✗ Minimum 8 characters. Try again."));
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
let confirm = "";
|
|
350
|
+
try {
|
|
351
|
+
confirm = await readPassword(" Confirm password: ");
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
log(c.yellow(" Interrupted."));
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
if (password !== confirm) {
|
|
358
|
+
log(c.red(" ✗ Passwords do not match. Try again."));
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
await initPassword(password);
|
|
364
|
+
const ip = getLocalIP();
|
|
365
|
+
log("");
|
|
366
|
+
log(c.bold(c.green(" ✓ Onboarding complete!")));
|
|
367
|
+
log("");
|
|
368
|
+
log(` Open your browser: ${c.bold(c.cyan(`http://${ip}:${port}`))}`);
|
|
369
|
+
log(` Or locally: ${c.bold(c.cyan(`http://localhost:${port}`))}`);
|
|
370
|
+
log("");
|
|
371
|
+
log(c.dim(" Log in with your password, then configure your LLM provider in Settings."));
|
|
372
|
+
log("");
|
|
373
|
+
}
|
|
374
|
+
// ── reset ─────────────────────────────────────────────────────────────────
|
|
375
|
+
export async function runReset(rest) {
|
|
376
|
+
const yes = hasFlag(rest, "--yes", "-y");
|
|
377
|
+
loadNomadToken();
|
|
378
|
+
log("");
|
|
379
|
+
log(c.bold(c.yellow(" ⚠ JishuShell Reset")));
|
|
380
|
+
log(c.dim(" ─────────────────────────────────────────────────────"));
|
|
381
|
+
log("");
|
|
382
|
+
log(" This will:");
|
|
383
|
+
log(` ${c.dim("•")} Stop all instances and purge them from the scheduler`);
|
|
384
|
+
log(` ${c.dim("•")} Delete all instance directories (~/.jishushell/instances/)`);
|
|
385
|
+
log(` ${c.dim("•")} Stop the JishuShell panel`);
|
|
386
|
+
log(` ${c.dim("•")} Reset auth credentials and LLM provider config`);
|
|
387
|
+
log(` ${c.dim("•")} Preserve infrastructure config (Docker image, service manager, port)`);
|
|
388
|
+
log("");
|
|
389
|
+
log(` ${c.bold(c.yellow("Custom openclaw_home paths outside ~/.jishushell are NOT removed."))}`);
|
|
390
|
+
log(` ${c.dim("After reset: restart the panel and open")} ${c.bold(`http://localhost:${getPanelPort()}`)} ${c.dim("to reconfigure.")}`);
|
|
391
|
+
log("");
|
|
392
|
+
if (!yes) {
|
|
393
|
+
if (!process.stdin.isTTY) {
|
|
394
|
+
log(c.red(" ✗ Reset requires interactive confirmation. Pass --yes to skip."));
|
|
395
|
+
log("");
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
const answer = await new Promise((resolve) => {
|
|
399
|
+
process.stdout.write(` Type ${c.bold("RESET")} to continue, or press Enter to cancel: `);
|
|
400
|
+
let buf = "";
|
|
401
|
+
const onData = (chunk) => {
|
|
402
|
+
const str = chunk.toString("utf-8");
|
|
403
|
+
const nl = str.indexOf("\n");
|
|
404
|
+
if (nl >= 0) {
|
|
405
|
+
buf += str.slice(0, nl);
|
|
406
|
+
process.stdin.off("data", onData);
|
|
407
|
+
process.stdin.pause();
|
|
408
|
+
process.stdout.write("\n");
|
|
409
|
+
resolve(buf.trim());
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
buf += str;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
process.stdin.resume();
|
|
416
|
+
process.stdin.on("data", onData);
|
|
417
|
+
});
|
|
418
|
+
if (answer !== "RESET") {
|
|
419
|
+
log(c.dim(" Cancelled."));
|
|
420
|
+
log("");
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
log("");
|
|
425
|
+
// 1. Purge jobs and delete instance directories
|
|
426
|
+
const instanceIds = listInstanceIds();
|
|
427
|
+
if (instanceIds.length > 0) {
|
|
428
|
+
log(c.dim(` Removing ${instanceIds.length} instance(s)…`));
|
|
429
|
+
const svc = await getSvc();
|
|
430
|
+
for (const id of instanceIds) {
|
|
431
|
+
const meta = readInstanceMeta(id);
|
|
432
|
+
const name = meta?.name || id;
|
|
433
|
+
// Stop from scheduler (best-effort — may already be gone if Nomad is down)
|
|
434
|
+
await svc.stopInstance(id, true).catch(() => { });
|
|
435
|
+
// Delete local directory
|
|
436
|
+
const del = await deleteInstance(id);
|
|
437
|
+
if (del.ok) {
|
|
438
|
+
log(` ${c.green("✓")} removed: ${name}`);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
log(` ${c.yellow("!")} could not fully remove ${name}`);
|
|
442
|
+
}
|
|
443
|
+
if (del.warnings?.length) {
|
|
444
|
+
for (const w of del.warnings)
|
|
445
|
+
log(` ${c.dim(w)}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// 2. Stop panel
|
|
450
|
+
const port = getPanelPort();
|
|
451
|
+
if (isPortListening(port)) {
|
|
452
|
+
log(c.dim(" Stopping panel…"));
|
|
453
|
+
try {
|
|
454
|
+
await stopPanel();
|
|
455
|
+
}
|
|
456
|
+
catch { /* best-effort */ }
|
|
457
|
+
}
|
|
458
|
+
// 3. Selectively clear panel.json: preserve infrastructure settings
|
|
459
|
+
// (service_manager, nomad_driver, openclaw_image, nomad_token, panel_port,
|
|
460
|
+
// npm_registry) so the user does NOT need to reinstall Docker/Nomad/OpenClaw.
|
|
461
|
+
// Only user-owned fields (default_provider and related LLM config) are cleared.
|
|
462
|
+
const PANEL_CONFIG = join(JISHUSHELL_HOME, "panel.json");
|
|
463
|
+
const INFRA_KEYS = new Set([
|
|
464
|
+
"service_manager", "nomad_driver", "openclaw_image",
|
|
465
|
+
"nomad_token", "panel_port", "npm_registry",
|
|
466
|
+
]);
|
|
467
|
+
try {
|
|
468
|
+
const cfg = getPanelConfig();
|
|
469
|
+
const preserved = {};
|
|
470
|
+
for (const key of Object.keys(cfg)) {
|
|
471
|
+
if (INFRA_KEYS.has(key))
|
|
472
|
+
preserved[key] = cfg[key];
|
|
473
|
+
}
|
|
474
|
+
savePanelConfig(preserved);
|
|
475
|
+
log(` ${c.green("✓")} reset panel config (infrastructure settings preserved)`);
|
|
476
|
+
// Remove panel.json backups (stale, not needed after a clean reset)
|
|
477
|
+
for (const f of [`${PANEL_CONFIG}.bak`, `${PANEL_CONFIG}.bak.1`, `${PANEL_CONFIG}.bak.2`]) {
|
|
478
|
+
try {
|
|
479
|
+
if (existsSync(f)) {
|
|
480
|
+
unlinkSync(f);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
catch { /* best-effort */ }
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
catch (e) {
|
|
487
|
+
log(` ${c.yellow("!")} could not reset panel config: ${e.message}`);
|
|
488
|
+
}
|
|
489
|
+
// 4. Delete auth and secret files
|
|
490
|
+
const AUTH_FILE = join(JISHUSHELL_HOME, "auth.json");
|
|
491
|
+
const JWT_FILE = join(JISHUSHELL_HOME, "jwt-secret");
|
|
492
|
+
const ENCRYPT_KEY = join(JISHUSHELL_HOME, "encryption-key");
|
|
493
|
+
const filesToRemove = [
|
|
494
|
+
AUTH_FILE,
|
|
495
|
+
`${AUTH_FILE}.bak`, `${AUTH_FILE}.bak.1`, `${AUTH_FILE}.bak.2`,
|
|
496
|
+
JWT_FILE,
|
|
497
|
+
ENCRYPT_KEY,
|
|
498
|
+
];
|
|
499
|
+
for (const f of filesToRemove) {
|
|
500
|
+
try {
|
|
501
|
+
if (existsSync(f)) {
|
|
502
|
+
unlinkSync(f);
|
|
503
|
+
log(` ${c.green("✓")} removed: ${f}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
catch (e) {
|
|
507
|
+
log(` ${c.yellow("!")} could not remove ${f}: ${e.message}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
log("");
|
|
511
|
+
log(c.bold(c.green(" ✓ Reset complete.")));
|
|
512
|
+
log("");
|
|
513
|
+
log(` To reconfigure: restart the panel, then open ${c.bold(`http://localhost:${port}`)}`);
|
|
514
|
+
log("");
|
|
515
|
+
}
|
|
516
|
+
// ── update ────────────────────────────────────────────────────────────────
|
|
517
|
+
export async function runUpdateCheck() {
|
|
518
|
+
const info = await checkUpdate();
|
|
519
|
+
console.log(JSON.stringify(info));
|
|
520
|
+
process.exit(info.hasUpdate ? 0 : 1);
|
|
521
|
+
}
|
|
522
|
+
export async function runUpdate() {
|
|
523
|
+
log("");
|
|
524
|
+
log(c.bold(" Updating JishuShell…"));
|
|
525
|
+
const { currentVersion, latestVersion } = await checkUpdate();
|
|
526
|
+
const expectedVersion = process.env.JISHUSHELL_UPDATE_TARGET_VERSION || latestVersion;
|
|
527
|
+
const trackedCurrentVersion = process.env.JISHUSHELL_UPDATE_CURRENT_VERSION || currentVersion;
|
|
528
|
+
const trackedWebUpdate = process.env.JISHUSHELL_WEB_UPDATE === "1";
|
|
529
|
+
log(c.dim(` current: ${currentVersion} → latest: ${expectedVersion}`));
|
|
530
|
+
log(c.dim(" Running: npm install -g jishushell …"));
|
|
531
|
+
try {
|
|
532
|
+
const { output } = await upgradePackage();
|
|
533
|
+
if (output)
|
|
534
|
+
log(c.dim(output));
|
|
535
|
+
const installedVersion = getInstalledGlobalVersion();
|
|
536
|
+
if (!isVersionAtLeast(installedVersion, expectedVersion)) {
|
|
537
|
+
throw new Error(`npm -g version is still ${installedVersion}, expected at least ${expectedVersion}`);
|
|
538
|
+
}
|
|
539
|
+
if (trackedWebUpdate) {
|
|
540
|
+
markUpdateRestarting(expectedVersion, trackedCurrentVersion, installedVersion);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
catch (err) {
|
|
544
|
+
if (trackedWebUpdate) {
|
|
545
|
+
markUpdateFailed(err.message || "Update failed", trackedCurrentVersion);
|
|
546
|
+
}
|
|
547
|
+
log(c.red(` ✗ npm install failed: ${err.message}`));
|
|
548
|
+
throw err;
|
|
549
|
+
}
|
|
550
|
+
log(c.green(" ✓ Package updated."));
|
|
551
|
+
log(c.dim(" Scheduling panel restart in 2 s…"));
|
|
552
|
+
scheduleRestart(2000);
|
|
553
|
+
log(c.green(" ✓ Restart scheduled — panel will reconnect shortly."));
|
|
554
|
+
}
|
|
555
|
+
// ── serve:前台服务器(systemd/launchd ExecStart)────────────────────────────
|
|
556
|
+
export async function runServe(rest) {
|
|
557
|
+
const { createServer } = await import("../server.js");
|
|
558
|
+
const { getPanelConfig } = await import("../config.js");
|
|
559
|
+
// Priority: --port flag > panel.json panel_port > 8090
|
|
560
|
+
const config = getPanelConfig();
|
|
561
|
+
let port = Number(config.panel_port) || DEFAULT_PORT;
|
|
562
|
+
let host = "0.0.0.0";
|
|
563
|
+
for (let i = 0; i < rest.length; i++) {
|
|
564
|
+
let portVal;
|
|
565
|
+
if (rest[i] === "--port" && rest[i + 1]) {
|
|
566
|
+
portVal = rest[++i];
|
|
567
|
+
}
|
|
568
|
+
else if (rest[i].startsWith("--port=")) {
|
|
569
|
+
portVal = rest[i].slice("--port=".length);
|
|
570
|
+
}
|
|
571
|
+
else if (rest[i] === "--host" && rest[i + 1]) {
|
|
572
|
+
host = rest[++i];
|
|
573
|
+
}
|
|
574
|
+
else if (rest[i].startsWith("--host=")) {
|
|
575
|
+
host = rest[i].slice("--host=".length);
|
|
576
|
+
}
|
|
577
|
+
if (portVal !== undefined) {
|
|
578
|
+
const parsed = parseInt(portVal, 10);
|
|
579
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 65535) {
|
|
580
|
+
console.error(`Invalid port: ${portVal}. Must be 0-65535.`);
|
|
581
|
+
process.exit(1);
|
|
582
|
+
}
|
|
583
|
+
port = parsed;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
await createServer({ port, host });
|
|
587
|
+
}
|
|
588
|
+
// ── config:panel.json 读写 ──────────────────────────────────────────────────
|
|
589
|
+
export async function runConfig(rest) {
|
|
590
|
+
const cfgCmd = rest[0];
|
|
591
|
+
if (cfgCmd === "get") {
|
|
592
|
+
const cfg = getPanelConfig();
|
|
593
|
+
const key = rest[1];
|
|
594
|
+
if (key) {
|
|
595
|
+
const val = cfg[key];
|
|
596
|
+
if (val === undefined) {
|
|
597
|
+
process.stdout.write(c.dim(" (not set)") + "\n");
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
console.log(typeof val === "object" ? JSON.stringify(val, null, 2) : String(val));
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
console.log(JSON.stringify(cfg, null, 2));
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
else if (cfgCmd === "set" && rest[1] && rest[2]) {
|
|
608
|
+
const ALLOWED_KEYS = ["service_manager", "nomad_driver", "openclaw_image", "panel_port", "npm_registry"];
|
|
609
|
+
const key = rest[1];
|
|
610
|
+
const value = rest[2];
|
|
611
|
+
if (!ALLOWED_KEYS.includes(key)) {
|
|
612
|
+
log(c.red(` ✗ Unknown config key: ${key}`));
|
|
613
|
+
log(c.dim(` Allowed: ${ALLOWED_KEYS.join(", ")}`));
|
|
614
|
+
process.exitCode = 1;
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
const cfg = getPanelConfig();
|
|
618
|
+
const numVal = parseInt(value, 10);
|
|
619
|
+
cfg[key] = !isNaN(numVal) && String(numVal) === value ? numVal : value;
|
|
620
|
+
savePanelConfig(cfg);
|
|
621
|
+
log(c.green(` ✓ ${key} = ${value}`));
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
console.log(`
|
|
625
|
+
Usage: jishushell config <command>
|
|
626
|
+
|
|
627
|
+
Commands:
|
|
628
|
+
get [key] 查看配置(不指定 key 则显示全部)
|
|
629
|
+
set <key> <value> 修改配置项
|
|
630
|
+
|
|
631
|
+
Allowed keys:
|
|
632
|
+
service_manager 服务管理器类型(nomad / process)
|
|
633
|
+
nomad_driver Nomad driver(docker)
|
|
634
|
+
openclaw_image OpenClaw Docker 镜像
|
|
635
|
+
panel_port 面板端口
|
|
636
|
+
npm_registry npm 镜像源
|
|
637
|
+
|
|
638
|
+
Examples:
|
|
639
|
+
jishushell config get
|
|
640
|
+
jishushell config get panel_port
|
|
641
|
+
jishushell config set panel_port 8090
|
|
642
|
+
`);
|
|
643
|
+
if (cfgCmd)
|
|
644
|
+
process.exit(1);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
// ── install / uninstall ──────────────────────────────────────────────────────
|
|
648
|
+
export function brief() {
|
|
649
|
+
return " serve [--port N] 前台启动面板服务器(默认端口 8090)\n start|stop|restart|status|config|onboard 面板生命周期与配置";
|
|
650
|
+
}
|
|
651
|
+
export async function runInstallCmd(rest) {
|
|
652
|
+
const { runInstall, parseInstallArgs } = await import("../install.js");
|
|
653
|
+
const opts = parseInstallArgs(rest);
|
|
654
|
+
await runInstall(opts);
|
|
655
|
+
}
|
|
656
|
+
export async function runUninstall(rest) {
|
|
657
|
+
const yes = hasFlag(rest, "--yes", "-y");
|
|
658
|
+
const { spawnSync } = await import("child_process");
|
|
659
|
+
const uninstallSh = join(JISHUSHELL_HOME, "install", "post-uninstall.sh");
|
|
660
|
+
if (!existsSync(uninstallSh)) {
|
|
661
|
+
console.error(`Uninstall script not found: ${uninstallSh}`);
|
|
662
|
+
console.error("Please run: sudo bash ~/.jishushell/install/jishu-uninstall.sh --data --yes");
|
|
663
|
+
process.exit(1);
|
|
664
|
+
}
|
|
665
|
+
if (!yes) {
|
|
666
|
+
const { createInterface } = await import("readline");
|
|
667
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
668
|
+
await new Promise((resolve) => {
|
|
669
|
+
rl.question("This will stop all services, remove auto-start and delete ~/.jishushell. Continue? [y/N] ", (ans) => {
|
|
670
|
+
rl.close();
|
|
671
|
+
if (ans.toLowerCase() !== "y") {
|
|
672
|
+
console.log("Cancelled.");
|
|
673
|
+
process.exit(0);
|
|
674
|
+
}
|
|
675
|
+
resolve();
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
const cleanResult = spawnSync("bash", [uninstallSh], { stdio: "inherit" });
|
|
680
|
+
const npmResult = spawnSync("npm", ["uninstall", "-g", "jishushell"], { stdio: "inherit" });
|
|
681
|
+
process.exit((cleanResult.status ?? 0) || (npmResult.status ?? 0));
|
|
682
|
+
}
|
|
683
|
+
// ── 统一入口 ──────────────────────────────────────────────────────────────────
|
|
684
|
+
export function printHelp() {
|
|
685
|
+
console.log(`
|
|
686
|
+
Usage: jishushell <command> [options]
|
|
687
|
+
|
|
688
|
+
Panel commands:
|
|
689
|
+
start [--port N] 启动面板
|
|
690
|
+
stop [--jobs] 停止面板(--jobs 同时停止所有实例)
|
|
691
|
+
restart [--port N] 重启面板
|
|
692
|
+
status 面板状态总览
|
|
693
|
+
serve [--port N] [--host H] 前台运行(适合 Docker 等场景)
|
|
694
|
+
onboard [--pass P] 初始化密码向导
|
|
695
|
+
reset 重置面板数据
|
|
696
|
+
config [key] [value] 查看或修改配置项
|
|
697
|
+
update-check 检查新版本
|
|
698
|
+
update 升级到最新版本
|
|
699
|
+
install [options] 安装服务(systemd / Docker / 直接运行)
|
|
700
|
+
uninstall [--yes] 卸载面板及所有数据
|
|
701
|
+
|
|
702
|
+
Run 'jishushell <command> help' for details.
|
|
703
|
+
`);
|
|
704
|
+
}
|
|
705
|
+
export async function run(rest) {
|
|
706
|
+
const cmd = rest[0];
|
|
707
|
+
const args = rest.slice(1);
|
|
708
|
+
switch (cmd) {
|
|
709
|
+
case "start": return runStart(args);
|
|
710
|
+
case "stop": return runStop(args);
|
|
711
|
+
case "restart": return runRestart(args);
|
|
712
|
+
case "status": return runStatus();
|
|
713
|
+
case "serve": return runServe(args);
|
|
714
|
+
case "onboard": return runOnboard(args);
|
|
715
|
+
case "reset": return runReset(args);
|
|
716
|
+
case "update-check": return runUpdateCheck();
|
|
717
|
+
case "update": return runUpdate();
|
|
718
|
+
case "config": return runConfig(args);
|
|
719
|
+
case "install": return runInstallCmd(args);
|
|
720
|
+
case "uninstall": return runUninstall(args);
|
|
721
|
+
case "help":
|
|
722
|
+
case "--help":
|
|
723
|
+
case "-h":
|
|
724
|
+
printHelp();
|
|
725
|
+
return;
|
|
726
|
+
default:
|
|
727
|
+
printHelp();
|
|
728
|
+
if (cmd) {
|
|
729
|
+
console.error(`Unknown panel command: ${cmd}`);
|
|
730
|
+
process.exit(1);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
//# sourceMappingURL=panel.js.map
|