jishushell 0.0.1 → 0.4.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.
Files changed (136) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +36 -0
  3. package/THIRD-PARTY-NOTICES +387 -0
  4. package/dist/auth.d.ts +6 -0
  5. package/dist/auth.js +88 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +290 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config.d.ts +24 -0
  11. package/dist/config.js +226 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/constants.d.ts +3 -0
  14. package/dist/constants.js +15 -0
  15. package/dist/constants.js.map +1 -0
  16. package/dist/control.d.ts +44 -0
  17. package/dist/control.js +1359 -0
  18. package/dist/control.js.map +1 -0
  19. package/dist/crypto-shim.d.ts +1 -0
  20. package/dist/crypto-shim.js +2 -0
  21. package/dist/crypto-shim.js.map +1 -0
  22. package/dist/doctor.d.ts +46 -0
  23. package/dist/doctor.js +937 -0
  24. package/dist/doctor.js.map +1 -0
  25. package/dist/install.d.ts +27 -0
  26. package/dist/install.js +570 -0
  27. package/dist/install.js.map +1 -0
  28. package/dist/routes/auth.d.ts +4 -0
  29. package/dist/routes/auth.js +151 -0
  30. package/dist/routes/auth.js.map +1 -0
  31. package/dist/routes/instances.d.ts +2 -0
  32. package/dist/routes/instances.js +1303 -0
  33. package/dist/routes/instances.js.map +1 -0
  34. package/dist/routes/setup.d.ts +2 -0
  35. package/dist/routes/setup.js +139 -0
  36. package/dist/routes/setup.js.map +1 -0
  37. package/dist/routes/system.d.ts +2 -0
  38. package/dist/routes/system.js +102 -0
  39. package/dist/routes/system.js.map +1 -0
  40. package/dist/server.d.ts +6 -0
  41. package/dist/server.js +392 -0
  42. package/dist/server.js.map +1 -0
  43. package/dist/services/instance-manager.d.ts +67 -0
  44. package/dist/services/instance-manager.js +1319 -0
  45. package/dist/services/instance-manager.js.map +1 -0
  46. package/dist/services/llm-proxy/adapters.d.ts +3 -0
  47. package/dist/services/llm-proxy/adapters.js +309 -0
  48. package/dist/services/llm-proxy/adapters.js.map +1 -0
  49. package/dist/services/llm-proxy/circuit-breaker.d.ts +9 -0
  50. package/dist/services/llm-proxy/circuit-breaker.js +73 -0
  51. package/dist/services/llm-proxy/circuit-breaker.js.map +1 -0
  52. package/dist/services/llm-proxy/encryption.d.ts +6 -0
  53. package/dist/services/llm-proxy/encryption.js +61 -0
  54. package/dist/services/llm-proxy/encryption.js.map +1 -0
  55. package/dist/services/llm-proxy/index.d.ts +24 -0
  56. package/dist/services/llm-proxy/index.js +708 -0
  57. package/dist/services/llm-proxy/index.js.map +1 -0
  58. package/dist/services/llm-proxy/rate-limiter.d.ts +1 -0
  59. package/dist/services/llm-proxy/rate-limiter.js +39 -0
  60. package/dist/services/llm-proxy/rate-limiter.js.map +1 -0
  61. package/dist/services/llm-proxy/sse.d.ts +10 -0
  62. package/dist/services/llm-proxy/sse.js +378 -0
  63. package/dist/services/llm-proxy/sse.js.map +1 -0
  64. package/dist/services/llm-proxy/ssrf.d.ts +16 -0
  65. package/dist/services/llm-proxy/ssrf.js +185 -0
  66. package/dist/services/llm-proxy/ssrf.js.map +1 -0
  67. package/dist/services/llm-proxy/types.d.ts +52 -0
  68. package/dist/services/llm-proxy/types.js +2 -0
  69. package/dist/services/llm-proxy/types.js.map +1 -0
  70. package/dist/services/llm-proxy/usage.d.ts +12 -0
  71. package/dist/services/llm-proxy/usage.js +108 -0
  72. package/dist/services/llm-proxy/usage.js.map +1 -0
  73. package/dist/services/nomad-manager.d.ts +22 -0
  74. package/dist/services/nomad-manager.js +828 -0
  75. package/dist/services/nomad-manager.js.map +1 -0
  76. package/dist/services/plugin-installer.d.ts +22 -0
  77. package/dist/services/plugin-installer.js +102 -0
  78. package/dist/services/plugin-installer.js.map +1 -0
  79. package/dist/services/process-manager.d.ts +25 -0
  80. package/dist/services/process-manager.js +531 -0
  81. package/dist/services/process-manager.js.map +1 -0
  82. package/dist/services/setup-manager.d.ts +93 -0
  83. package/dist/services/setup-manager.js +1922 -0
  84. package/dist/services/setup-manager.js.map +1 -0
  85. package/dist/services/system-monitor.d.ts +1 -0
  86. package/dist/services/system-monitor.js +79 -0
  87. package/dist/services/system-monitor.js.map +1 -0
  88. package/dist/services/telemetry/activation.d.ts +12 -0
  89. package/dist/services/telemetry/activation.js +75 -0
  90. package/dist/services/telemetry/activation.js.map +1 -0
  91. package/dist/services/telemetry/client.d.ts +21 -0
  92. package/dist/services/telemetry/client.js +47 -0
  93. package/dist/services/telemetry/client.js.map +1 -0
  94. package/dist/services/telemetry/device-fingerprint.d.ts +18 -0
  95. package/dist/services/telemetry/device-fingerprint.js +123 -0
  96. package/dist/services/telemetry/device-fingerprint.js.map +1 -0
  97. package/dist/services/telemetry/heartbeat.d.ts +13 -0
  98. package/dist/services/telemetry/heartbeat.js +81 -0
  99. package/dist/services/telemetry/heartbeat.js.map +1 -0
  100. package/dist/services/telemetry/index.d.ts +3 -0
  101. package/dist/services/telemetry/index.js +4 -0
  102. package/dist/services/telemetry/index.js.map +1 -0
  103. package/dist/types.d.ts +51 -0
  104. package/dist/types.js +2 -0
  105. package/dist/types.js.map +1 -0
  106. package/dist/utils/safe-json.d.ts +2 -0
  107. package/dist/utils/safe-json.js +80 -0
  108. package/dist/utils/safe-json.js.map +1 -0
  109. package/dist/utils/ttl-cache.d.ts +29 -0
  110. package/dist/utils/ttl-cache.js +77 -0
  111. package/dist/utils/ttl-cache.js.map +1 -0
  112. package/install/jishu-install.sh +2920 -0
  113. package/install/jishu-uninstall.sh +811 -0
  114. package/install/post-install.sh +110 -0
  115. package/install/post-uninstall.sh +46 -0
  116. package/package.json +57 -8
  117. package/public/assets/Dashboard-CAOQDYDR.js +1 -0
  118. package/public/assets/InitPassword-CkehIkJG.js +1 -0
  119. package/public/assets/InstanceDetail-CzW2S95J.js +14 -0
  120. package/public/assets/Login-RkjzTNWg.js +1 -0
  121. package/public/assets/NewInstance-DdbErdjA.js +1 -0
  122. package/public/assets/Settings-BUD7zwv9.js +1 -0
  123. package/public/assets/Setup-RRTIERGG.js +1 -0
  124. package/public/assets/index-77Ug7feY.css +1 -0
  125. package/public/assets/index-DfRnVUQR.js +16 -0
  126. package/public/assets/providers-lBSOjUWy.js +1 -0
  127. package/public/assets/usePolling-CqQ8hrNc.js +1 -0
  128. package/public/assets/vendor-i18n-Bvxxh8Di.js +9 -0
  129. package/public/assets/vendor-react-DONn7uBV.js +59 -0
  130. package/public/index.html +15 -0
  131. package/scripts/build-image.sh +55 -0
  132. package/scripts/run.sh +310 -0
  133. package/scripts/setup-pi.sh +80 -0
  134. package/scripts/start-feishu1.js +46 -0
  135. package/index.js +0 -0
  136. package/jishushell-0.0.1.tgz +0 -0
package/dist/auth.js ADDED
@@ -0,0 +1,88 @@
1
+ import { AUTH_FILE, getJwtSecret, JWT_ALGORITHM, JWT_EXPIRE_HOURS } from "./config.js";
2
+ import { safeReadJson, safeWriteJson } from "./utils/safe-json.js";
3
+ import bcrypt from "bcryptjs";
4
+ import jwt from "jsonwebtoken";
5
+ const AUTH_CACHE_TTL = 60_000;
6
+ let _authCache = null;
7
+ // In-process mutex: prevents two concurrent /api/auth/init calls from both
8
+ // seeing isInitialized()=false and then racing to write the password file.
9
+ let _initInProgress = false;
10
+ function invalidateAuthCache() { _authCache = null; }
11
+ function readAuthData() {
12
+ return safeReadJson(AUTH_FILE, "auth");
13
+ }
14
+ function writeAuthData(data) {
15
+ safeWriteJson(AUTH_FILE, data, true);
16
+ }
17
+ export function isInitialized() {
18
+ return readAuthData() !== null;
19
+ }
20
+ export async function initPassword(password) {
21
+ // CAS: first caller wins; subsequent concurrent calls see _initInProgress=true
22
+ // and return false before even touching the file.
23
+ if (_initInProgress || readAuthData() !== null)
24
+ return false;
25
+ _initInProgress = true;
26
+ try {
27
+ // Double-check after the flag is set — in case two calls raced to the check above.
28
+ if (readAuthData() !== null)
29
+ return false;
30
+ const hashed = await bcrypt.hash(password, 10);
31
+ const version = Date.now();
32
+ writeAuthData({ password_hash: hashed, password_version: version });
33
+ invalidateAuthCache();
34
+ return true;
35
+ }
36
+ finally {
37
+ _initInProgress = false;
38
+ }
39
+ }
40
+ export async function verifyPassword(password) {
41
+ const data = readAuthData();
42
+ if (!data)
43
+ return false;
44
+ return bcrypt.compare(password, data.password_hash);
45
+ }
46
+ export async function changePassword(oldPassword, newPassword) {
47
+ if (!await verifyPassword(oldPassword))
48
+ return false;
49
+ const hashed = await bcrypt.hash(newPassword, 10);
50
+ let currentVersion = Date.now();
51
+ const data = readAuthData();
52
+ if (data) {
53
+ const prev = data.password_version || 0;
54
+ if (currentVersion <= prev)
55
+ currentVersion = prev + 1;
56
+ }
57
+ writeAuthData({ password_hash: hashed, password_version: currentVersion });
58
+ invalidateAuthCache();
59
+ return true;
60
+ }
61
+ export function createToken() {
62
+ const data = readAuthData();
63
+ const passwordVersion = data?.password_version || 1;
64
+ return jwt.sign({ sub: "admin", pv: passwordVersion }, getJwtSecret(), {
65
+ algorithm: JWT_ALGORITHM,
66
+ expiresIn: `${JWT_EXPIRE_HOURS}h`,
67
+ });
68
+ }
69
+ export function verifyToken(token) {
70
+ if (!token)
71
+ return false;
72
+ try {
73
+ const decoded = jwt.verify(token, getJwtSecret(), { algorithms: [JWT_ALGORITHM] });
74
+ if (_authCache && Date.now() - _authCache.ts < AUTH_CACHE_TTL) {
75
+ return decoded.pv === _authCache.passwordVersion;
76
+ }
77
+ const data = readAuthData();
78
+ if (!data)
79
+ return false;
80
+ const currentVersion = data.password_version || 1;
81
+ _authCache = { passwordVersion: currentVersion, ts: Date.now() };
82
+ return decoded.pv === currentVersion;
83
+ }
84
+ catch {
85
+ return false;
86
+ }
87
+ }
88
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env node
2
+ if (process.platform === "darwin") {
3
+ const extra = ["/usr/local/bin", "/opt/homebrew/bin", "/opt/homebrew/sbin", "/Applications/Docker.app/Contents/Resources/bin"];
4
+ const current = process.env.PATH ?? "";
5
+ const missing = extra.filter(p => !current.split(":").includes(p));
6
+ if (missing.length > 0)
7
+ process.env.PATH = [...missing, current].join(":");
8
+ }
9
+ import { existsSync } from "fs";
10
+ import { join } from "path";
11
+ import { homedir } from "os";
12
+ import { exec } from "child_process";
13
+ import { createServer } from "./server.js";
14
+ import { runInstall, parseInstallArgs } from "./install.js";
15
+ import { startPanel, stopPanel, stopWithJobs, restartPanel, runDoctor, runOnboard, showStatus, resetAll, checkUpdate, runUpdate } from "./control.js";
16
+ const JISHUSHELL_HOME = process.env.JISHUSHELL_HOME || join(homedir(), ".jishushell");
17
+ const NOMAD_BIN = join(JISHUSHELL_HOME, "bin", "nomad");
18
+ const PANEL_PORT = 8090;
19
+ function openBrowser(url) {
20
+ const platform = process.platform;
21
+ const cmd = platform === "darwin" ? `open "${url}"`
22
+ : platform === "win32" ? `start "" "${url}"`
23
+ : `xdg-open "${url}"`;
24
+ exec(cmd, () => { });
25
+ }
26
+ const [subcommand, ...rest] = process.argv.slice(2);
27
+ // ── Helper to parse --port from remaining args ─────────────────────────────
28
+ function parsePort(args) {
29
+ for (let i = 0; i < args.length; i++) {
30
+ if (args[i] === "--port" && args[i + 1]) {
31
+ const p = parseInt(args[i + 1], 10);
32
+ if (!isNaN(p) && p > 0 && p < 65536)
33
+ return p;
34
+ }
35
+ }
36
+ return undefined;
37
+ }
38
+ // ── install subcommand ─────────────────────────────────────────────────────
39
+ if (subcommand === "install") {
40
+ const opts = parseInstallArgs(rest);
41
+ runInstall(opts).catch((err) => {
42
+ console.error("Installation failed:", err);
43
+ process.exit(1);
44
+ });
45
+ // runInstall is async and calls process.exit() itself
46
+ // ── start ──────────────────────────────────────────────────────────────────
47
+ }
48
+ else if (subcommand === "start") {
49
+ startPanel(parsePort(rest)).then(() => process.exit(0)).catch((err) => {
50
+ console.error("start failed:", err.message);
51
+ process.exit(1);
52
+ });
53
+ // ── stop ───────────────────────────────────────────────────────────────────
54
+ }
55
+ else if (subcommand === "stop") {
56
+ // --jobs: also stop (but not purge) all Nomad jobs before stopping the panel
57
+ const withJobs = rest.includes("--jobs") || rest.includes("-j");
58
+ const stopFn = withJobs ? stopWithJobs : stopPanel;
59
+ stopFn().then(() => process.exit(0)).catch((err) => {
60
+ console.error("stop failed:", err.message);
61
+ process.exit(1);
62
+ });
63
+ // ── restart ────────────────────────────────────────────────────────────────
64
+ }
65
+ else if (subcommand === "restart") {
66
+ restartPanel(parsePort(rest)).then(() => process.exit(0)).catch((err) => {
67
+ console.error("restart failed:", err.message);
68
+ process.exit(1);
69
+ });
70
+ // ── status ─────────────────────────────────────────────────────────────────
71
+ }
72
+ else if (subcommand === "status") {
73
+ showStatus().then(() => process.exit(0)).catch((err) => {
74
+ console.error("status failed:", err.message);
75
+ process.exit(1);
76
+ });
77
+ // ── doctor ─────────────────────────────────────────────────────────────────
78
+ }
79
+ else if (subcommand === "doctor") {
80
+ const fix = rest.includes("--fix") || rest.includes("-f");
81
+ runDoctor({ fix }).then((ok) => process.exit(ok ? 0 : 1)).catch((err) => {
82
+ console.error("doctor failed:", err.message);
83
+ process.exit(1);
84
+ });
85
+ // ── onboard ────────────────────────────────────────────────────────────────
86
+ }
87
+ else if (subcommand === "onboard") {
88
+ const force = rest.includes("--force") || rest.includes("-f");
89
+ runOnboard({ force }).then(() => process.exit(0)).catch((err) => {
90
+ console.error("onboard failed:", err.message);
91
+ process.exit(1);
92
+ });
93
+ // ── reset ──────────────────────────────────────────────────────────────────
94
+ }
95
+ else if (subcommand === "reset") {
96
+ const yes = rest.includes("--yes") || rest.includes("-y");
97
+ resetAll({ yes }).then(() => process.exit(0)).catch((err) => {
98
+ console.error("reset failed:", err.message);
99
+ process.exit(1);
100
+ });
101
+ // ── update-check ───────────────────────────────────────────────────────────
102
+ }
103
+ else if (subcommand === "update-check") {
104
+ checkUpdate().then((info) => {
105
+ console.log(JSON.stringify(info));
106
+ process.exit(info.hasUpdate ? 0 : 1);
107
+ }).catch((err) => {
108
+ console.error("update-check failed:", err.message);
109
+ process.exit(2);
110
+ });
111
+ // ── update-run ─────────────────────────────────────────────────────────────
112
+ }
113
+ else if (subcommand === "update") {
114
+ runUpdate().then(() => process.exit(0)).catch((err) => {
115
+ console.error("update-run failed:", err.message);
116
+ process.exit(1);
117
+ });
118
+ // ── pairing: approve / list DM pairing requests inside running containers ──
119
+ }
120
+ else if (subcommand === "pairing") {
121
+ const action = rest[0];
122
+ // Parse --instance <id> flag
123
+ let instanceId;
124
+ const instIdx = rest.indexOf("--instance");
125
+ if (instIdx !== -1 && rest[instIdx + 1])
126
+ instanceId = rest[instIdx + 1];
127
+ if (action === "list") {
128
+ const { runPairingList } = await import("./control.js");
129
+ runPairingList({ instanceId }).then((ok) => process.exit(ok ? 0 : 1)).catch((err) => {
130
+ console.error("pairing list failed:", err.message);
131
+ process.exit(1);
132
+ });
133
+ }
134
+ else if (action === "approve") {
135
+ // args after "approve": [channel, code] or [code] (single-arg form)
136
+ const actionArgs = rest.slice(1).filter((a) => !a.startsWith("--") && a !== instanceId);
137
+ let channel;
138
+ let code;
139
+ if (actionArgs.length >= 2) {
140
+ [channel, code] = actionArgs;
141
+ }
142
+ else if (actionArgs.length === 1) {
143
+ channel = "feishu";
144
+ code = actionArgs[0];
145
+ }
146
+ else {
147
+ console.error("Usage: jishushell pairing approve <channel> <code> [--instance <id>]");
148
+ process.exit(1);
149
+ }
150
+ const notify = rest.includes("--notify");
151
+ const { runPairingApprove } = await import("./control.js");
152
+ runPairingApprove({ instanceId, channel, code, notify }).then((ok) => process.exit(ok ? 0 : 1)).catch((err) => {
153
+ console.error("pairing approve failed:", err.message);
154
+ process.exit(1);
155
+ });
156
+ }
157
+ else {
158
+ console.log(`
159
+ Usage: jishushell pairing <command> [options]
160
+
161
+ Commands:
162
+ list 列出等待审批的配对请求
163
+ approve <channel> <code> 审批配对码(例: feishu LVU7PNYK)
164
+ approve <code> 默认 channel=feishu 简写形式
165
+
166
+ Options:
167
+ --instance <id> 指定实例 ID(只有一个实例时可省略)
168
+ --notify 审批后通知申请方
169
+
170
+ Examples:
171
+ jishushell pairing list
172
+ jishushell pairing approve feishu LVU7PNYK
173
+ jishushell pairing approve LVU7PNYK
174
+ jishushell pairing approve feishu LVU7PNYK --instance my-instance
175
+ `);
176
+ process.exit(1);
177
+ }
178
+ // ── serve: foreground server (used by systemd/launchd ExecStart) ──────────
179
+ }
180
+ else if (subcommand === "serve") {
181
+ const args = rest;
182
+ let port = PANEL_PORT;
183
+ let host = "0.0.0.0";
184
+ for (let i = 0; i < args.length; i++) {
185
+ if (args[i] === "--port" && args[i + 1]) {
186
+ const parsed = parseInt(args[i + 1], 10);
187
+ if (isNaN(parsed) || parsed < 1 || parsed > 65535) {
188
+ console.error(`Invalid port: ${args[i + 1]}. Must be 1-65535.`);
189
+ process.exit(1);
190
+ }
191
+ port = parsed;
192
+ i++;
193
+ }
194
+ else if (args[i] === "--host" && args[i + 1]) {
195
+ host = args[i + 1];
196
+ i++;
197
+ }
198
+ }
199
+ createServer({ port, host }).catch((err) => {
200
+ console.error("serve failed:", err.message);
201
+ process.exit(1);
202
+ });
203
+ // ── uninstall ─────────────────────────────────────────────────────────────
204
+ }
205
+ else if (subcommand === "uninstall") {
206
+ const yes = rest.includes("--yes") || rest.includes("-y");
207
+ const { spawnSync } = await import("child_process");
208
+ const uninstallSh = join(JISHUSHELL_HOME, "install", "post-uninstall.sh");
209
+ if (!existsSync(uninstallSh)) {
210
+ console.error(`Uninstall script not found: ${uninstallSh}`);
211
+ console.error("Please run: sudo bash ~/.jishushell/install/jishu-uninstall.sh --data --yes");
212
+ process.exit(1);
213
+ }
214
+ if (!yes) {
215
+ const { createInterface } = await import("readline");
216
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
217
+ await new Promise((resolve) => {
218
+ rl.question("This will stop all services, remove auto-start and delete ~/.jishushell. Continue? [y/N] ", (ans) => {
219
+ rl.close();
220
+ if (ans.toLowerCase() !== "y") {
221
+ console.log("Cancelled.");
222
+ process.exit(0);
223
+ }
224
+ resolve();
225
+ });
226
+ });
227
+ }
228
+ // Step 1: stop services and remove ~/.jishushell via the backed-up script
229
+ const cleanResult = spawnSync("bash", [uninstallSh], { stdio: "inherit" });
230
+ // Step 2: remove the npm global package (now ~/.jishushell is already gone)
231
+ const npmResult = spawnSync("npm", ["uninstall", "-g", "jishushell"], { stdio: "inherit" });
232
+ process.exit((cleanResult.status ?? 0) || (npmResult.status ?? 0));
233
+ // ── help ───────────────────────────────────────────────────────────────────
234
+ }
235
+ else if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
236
+ console.log(`
237
+ JishuShell — OpenClaw 实例管理面板
238
+
239
+ Usage:
240
+ jishushell 前台启动面板服务器
241
+ jishushell <command> [options]
242
+
243
+ Commands:
244
+ status 显示各模块运行状态(面板、Docker、Nomad 及所有 Job)
245
+ serve [--port N] [--host H] 前台启动面板服务器(供 systemd/launchd 使用)
246
+ start [--port N] 在后台启动面板
247
+ stop [--jobs] 停止面板;加 --jobs 同时停止所有 Nomad Job
248
+ restart [--port N] 重启面板
249
+ doctor 检查所有组件健康状态
250
+ doctor --fix 检查并尝试自动修复问题
251
+ onboard [--force] 首次引导:设置管理员密码
252
+ reset [--yes] 重置配置与所有 Nomad Job(保留实例数据目录)
253
+ uninstall [--yes] 停止服务、删除 ~/.jishushell 并卸载 npm 包
254
+ update-check 检查是否有新版本(0=有新版本,1=已是最新)
255
+ update 安装最新版本并重启面板
256
+ install [options] 安装依赖并配置 JishuShell
257
+ pairing list 列出等待审批的 DM 配对请求
258
+ pairing approve <ch> <code> 审批飞书/Lark 配对码(在容器内执行)
259
+ help 显示此帮助信息
260
+
261
+ Server options:
262
+ --port <port> 监听端口(默认 8090 或 panel.json 中的值)
263
+ --host <host> 监听地址(默认 0.0.0.0)
264
+
265
+ Install options:
266
+ --port <port> 面板端口(默认 8090)
267
+ --yes, -y 跳过所有交互提示(非交互模式)
268
+ --no-service 跳过 systemd/launchd 注册
269
+ --skip-docker 跳过 Docker 安装
270
+ --skip-nomad 跳过 Nomad 安装
271
+ --skip-openclaw 跳过 OpenClaw 安装
272
+ --force, -f 即使已配置也强制重新安装
273
+ --dry-run, -n 预演,不实际执行任何变更
274
+
275
+ Examples:
276
+ jishushell status # 查看当前所有组件状态
277
+ jishushell start # 后台启动面板
278
+ jishushell stop --jobs # 停止面板及所有实例 Job
279
+ jishushell restart # 重启面板
280
+ jishushell doctor --fix # 检查并自动修复
281
+ jishushell reset --yes # 非交互式重置(CI 场景)
282
+ `);
283
+ process.exit(0);
284
+ // ── unknown command ────────────────────────────────────────────────────────
285
+ }
286
+ else {
287
+ console.error(`Unknown command: ${subcommand}\nRun 'jishushell help' for usage.`);
288
+ process.exit(1);
289
+ }
290
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export declare const JISHUSHELL_HOME: string;
2
+ export declare const INSTANCES_DIR: string;
3
+ export declare const AUTH_FILE: string;
4
+ export declare const PANEL_CONFIG_FILE: string;
5
+ export declare function getJwtSecret(): string;
6
+ export declare const JWT_ALGORITHM: "HS256";
7
+ export declare const JWT_EXPIRE_HOURS = 24;
8
+ export declare const OPENCLAW_BIN: string;
9
+ export declare const DEFAULT_OPENCLAW_DOCKER_IMAGE = "jishushell-openclaw:local";
10
+ export declare const CUSTOM_IMAGE_PREFIX = "jishushell-openclaw";
11
+ export declare function getOpenclawDockerImage(): string;
12
+ /** Check whether the image is an official remote image (ghcr.io/openclaw/openclaw). */
13
+ export declare function isOfficialImage(image?: string): boolean;
14
+ export declare function setOpenclawDockerImage(tag: string): void;
15
+ export declare function ensureDirs(): void;
16
+ export declare function getPanelConfig(): Record<string, any>;
17
+ export declare function savePanelConfig(config: Record<string, any>): Record<string, any>;
18
+ export declare function ensurePanelConfig(): void;
19
+ export declare function getServiceManagerType(): string;
20
+ export declare function getNomadDriver(): string;
21
+ export declare function getNomadToken(): string;
22
+ export declare function getDeviceId(): string;
23
+ export declare function getAesKey(): Buffer;
24
+ export declare function getNomadAddr(): string;
package/dist/config.js ADDED
@@ -0,0 +1,226 @@
1
+ import { execFileSync } from "child_process";
2
+ import { randomBytes } from "crypto";
3
+ import { chmodSync, closeSync, existsSync, fdatasyncSync, mkdirSync, openSync, readFileSync, writeFileSync } from "fs";
4
+ import { generateFingerprint } from "./services/telemetry/device-fingerprint.js";
5
+ import { homedir } from "os";
6
+ import { dirname, join, resolve } from "path";
7
+ import { safeReadJson, safeWriteJson } from "./utils/safe-json.js";
8
+ import { TtlCache } from "./utils/ttl-cache.js";
9
+ export const JISHUSHELL_HOME = resolve(process.env.JISHUSHELL_HOME || join(homedir(), ".jishushell"));
10
+ export const INSTANCES_DIR = join(JISHUSHELL_HOME, "instances");
11
+ export const AUTH_FILE = join(JISHUSHELL_HOME, "auth.json");
12
+ export const PANEL_CONFIG_FILE = join(JISHUSHELL_HOME, "panel.json");
13
+ const JWT_SECRET_FILE = join(JISHUSHELL_HOME, "jwt-secret");
14
+ let _jwtSecret = null;
15
+ export function getJwtSecret() {
16
+ if (_jwtSecret)
17
+ return _jwtSecret;
18
+ // 1. Environment variable takes precedence
19
+ if (process.env.JISHUSHELL_JWT_SECRET) {
20
+ if (process.env.JISHUSHELL_JWT_SECRET.length < 32) {
21
+ throw new Error("JISHUSHELL_JWT_SECRET must be at least 32 characters for security");
22
+ }
23
+ _jwtSecret = process.env.JISHUSHELL_JWT_SECRET;
24
+ return _jwtSecret;
25
+ }
26
+ // 2. Read persisted secret
27
+ if (existsSync(JWT_SECRET_FILE)) {
28
+ const stored = readFileSync(JWT_SECRET_FILE, "utf-8").trim();
29
+ if (stored) {
30
+ _jwtSecret = stored;
31
+ return _jwtSecret;
32
+ }
33
+ }
34
+ // 3. Auto-generate and persist (fsync to protect against RPi power loss)
35
+ _jwtSecret = randomBytes(48).toString("base64url");
36
+ mkdirSync(dirname(JWT_SECRET_FILE), { recursive: true, mode: 0o700 });
37
+ writeFileSync(JWT_SECRET_FILE, _jwtSecret, { mode: 0o600 });
38
+ try {
39
+ const fd = openSync(JWT_SECRET_FILE, "r");
40
+ fdatasyncSync(fd);
41
+ closeSync(fd);
42
+ }
43
+ catch { }
44
+ return _jwtSecret;
45
+ }
46
+ export const JWT_ALGORITHM = "HS256";
47
+ export const JWT_EXPIRE_HOURS = 24;
48
+ export const OPENCLAW_BIN = process.env.OPENCLAW_BIN || "openclaw-gateway";
49
+ // Default Docker image: locally built from npm package + Python.
50
+ export const DEFAULT_OPENCLAW_DOCKER_IMAGE = "jishushell-openclaw:local";
51
+ const OFFICIAL_IMAGE_PREFIX = "ghcr.io/openclaw/openclaw";
52
+ // Custom image built locally (npm install openclaw + Python)
53
+ export const CUSTOM_IMAGE_PREFIX = "jishushell-openclaw";
54
+ export function getOpenclawDockerImage() {
55
+ if (process.env.OPENCLAW_DOCKER_IMAGE)
56
+ return process.env.OPENCLAW_DOCKER_IMAGE;
57
+ const stored = getPanelConfig().openclaw_image;
58
+ if (typeof stored === "string" && stored.trim())
59
+ return stored;
60
+ // panel.json has no image — try scanning local Docker daemon
61
+ try {
62
+ const lines = execFileSync("docker", ["images", "--format", "{{.Repository}}:{{.Tag}}"], { encoding: "utf8", timeout: 5000 }).split("\n");
63
+ const found = lines.find((l) => l.startsWith(CUSTOM_IMAGE_PREFIX + ":"))
64
+ || lines.find((l) => l.startsWith(OFFICIAL_IMAGE_PREFIX + ":"));
65
+ if (found) {
66
+ // Persist so we don't scan every call
67
+ setOpenclawDockerImage(found);
68
+ return found;
69
+ }
70
+ }
71
+ catch { }
72
+ return DEFAULT_OPENCLAW_DOCKER_IMAGE;
73
+ }
74
+ /** Check whether the image is an official remote image (ghcr.io/openclaw/openclaw). */
75
+ export function isOfficialImage(image) {
76
+ const img = image || getOpenclawDockerImage();
77
+ return img.startsWith(OFFICIAL_IMAGE_PREFIX + ":") || img === OFFICIAL_IMAGE_PREFIX;
78
+ }
79
+ export function setOpenclawDockerImage(tag) {
80
+ const config = getPanelConfig();
81
+ config.openclaw_image = tag;
82
+ savePanelConfig(config);
83
+ }
84
+ export function ensureDirs() {
85
+ for (const dir of [JISHUSHELL_HOME, INSTANCES_DIR]) {
86
+ mkdirSync(dir, { recursive: true, mode: 0o775 });
87
+ try {
88
+ chmodSync(dir, 0o775);
89
+ }
90
+ catch { /* ignore if not owner */ }
91
+ }
92
+ }
93
+ const _panelConfigCache = new TtlCache(5_000);
94
+ export function getPanelConfig() {
95
+ return structuredClone(_panelConfigCache.get(() => {
96
+ return safeReadJson(PANEL_CONFIG_FILE, "config") || {};
97
+ }));
98
+ }
99
+ export function savePanelConfig(config) {
100
+ safeWriteJson(PANEL_CONFIG_FILE, config, true);
101
+ _panelConfigCache.invalidate();
102
+ return config;
103
+ }
104
+ export function ensurePanelConfig() {
105
+ const defaults = {
106
+ service_manager: "nomad",
107
+ nomad_driver: "docker",
108
+ };
109
+ if (!existsSync(PANEL_CONFIG_FILE)) {
110
+ safeWriteJson(PANEL_CONFIG_FILE, JSON.stringify(defaults, null, 2), true);
111
+ _panelConfigCache.invalidate();
112
+ }
113
+ const cfg = getPanelConfig();
114
+ if (!cfg.openclaw_image) {
115
+ try {
116
+ const lines = execFileSync("docker", ["images", "--format", "{{.Repository}}:{{.Tag}}"], { encoding: "utf8", timeout: 5000 }).split("\n");
117
+ // Prefer custom image (with Python), then official, then legacy
118
+ const custom = lines.find((l) => l.startsWith(CUSTOM_IMAGE_PREFIX + ":"));
119
+ const official = lines.find((l) => l.startsWith(OFFICIAL_IMAGE_PREFIX + ":"));
120
+ const legacy = lines.find((l) => l.startsWith("openclaw:"));
121
+ const raw = custom || official || legacy || "";
122
+ if (raw) {
123
+ cfg.openclaw_image = raw;
124
+ savePanelConfig({ ...defaults, ...cfg });
125
+ }
126
+ }
127
+ catch {
128
+ }
129
+ }
130
+ }
131
+ export function getServiceManagerType() {
132
+ return getPanelConfig().service_manager || "nomad";
133
+ }
134
+ export function getNomadDriver() {
135
+ const driver = getPanelConfig().nomad_driver || "docker";
136
+ if (driver === "raw_exec") {
137
+ console.warn("[config] raw_exec driver is disabled for security reasons, using docker instead");
138
+ return "docker";
139
+ }
140
+ return driver;
141
+ }
142
+ export function getNomadToken() {
143
+ return process.env.NOMAD_TOKEN || getPanelConfig().nomad_token || "";
144
+ }
145
+ const DEVICE_ID_FILE = join(JISHUSHELL_HOME, "device-id");
146
+ let _deviceId = null;
147
+ export function getDeviceId() {
148
+ if (_deviceId)
149
+ return _deviceId;
150
+ if (existsSync(DEVICE_ID_FILE)) {
151
+ const stored = readFileSync(DEVICE_ID_FILE, "utf-8").trim();
152
+ if (stored) {
153
+ _deviceId = stored;
154
+ return _deviceId;
155
+ }
156
+ }
157
+ _deviceId = generateFingerprint();
158
+ mkdirSync(dirname(DEVICE_ID_FILE), { recursive: true, mode: 0o700 });
159
+ writeFileSync(DEVICE_ID_FILE, _deviceId, { mode: 0o600 });
160
+ try {
161
+ const fd = openSync(DEVICE_ID_FILE, "r");
162
+ fdatasyncSync(fd);
163
+ closeSync(fd);
164
+ }
165
+ catch { }
166
+ return _deviceId;
167
+ }
168
+ const ENCRYPTION_KEY_FILE = join(JISHUSHELL_HOME, "encryption-key");
169
+ let _aesKey = null;
170
+ export function getAesKey() {
171
+ if (_aesKey)
172
+ return _aesKey;
173
+ // 1. Environment variable (hex string, 64 chars = 32 bytes)
174
+ const envKey = process.env.JISHUSHELL_AES_KEY;
175
+ if (envKey) {
176
+ const buf = Buffer.from(envKey, "hex");
177
+ if (buf.length !== 32)
178
+ throw new Error("JISHUSHELL_AES_KEY must be 64 hex chars (32 bytes)");
179
+ _aesKey = buf;
180
+ return _aesKey;
181
+ }
182
+ // 2. File-based key
183
+ if (existsSync(ENCRYPTION_KEY_FILE)) {
184
+ const buf = readFileSync(ENCRYPTION_KEY_FILE);
185
+ if (buf.length !== 32) {
186
+ throw new Error(`encryption-key file has invalid length ${buf.length}, expected 32 bytes. Fix or delete the file to regenerate.`);
187
+ }
188
+ _aesKey = buf;
189
+ return _aesKey;
190
+ }
191
+ // 3. Auto-generate and persist (fsync to protect against RPi power loss)
192
+ _aesKey = randomBytes(32);
193
+ mkdirSync(dirname(ENCRYPTION_KEY_FILE), { recursive: true, mode: 0o700 });
194
+ writeFileSync(ENCRYPTION_KEY_FILE, _aesKey, { mode: 0o600 });
195
+ try {
196
+ const fd = openSync(ENCRYPTION_KEY_FILE, "r");
197
+ fdatasyncSync(fd);
198
+ closeSync(fd);
199
+ }
200
+ catch { }
201
+ return _aesKey;
202
+ }
203
+ export function getNomadAddr() {
204
+ const addr = process.env.NOMAD_ADDR || "http://127.0.0.1:4646";
205
+ // When the user explicitly sets a remote NOMAD_ADDR, enforce HTTPS so that
206
+ // the ACL token carried in X-Nomad-Token is never transmitted in cleartext.
207
+ if (process.env.NOMAD_ADDR) {
208
+ let url;
209
+ try {
210
+ url = new URL(addr);
211
+ }
212
+ catch {
213
+ throw new Error(`NOMAD_ADDR "${addr}" is not a valid URL`);
214
+ }
215
+ const isLoopback = url.hostname === "127.0.0.1" ||
216
+ url.hostname === "::1" ||
217
+ url.hostname.toLowerCase() === "localhost";
218
+ if (!isLoopback && url.protocol !== "https:") {
219
+ throw new Error(`NOMAD_ADDR "${addr}" points to a remote host over plain HTTP. ` +
220
+ `The Nomad ACL token would be transmitted in cleartext. ` +
221
+ `Use HTTPS instead, e.g. NOMAD_ADDR=https://${url.host}`);
222
+ }
223
+ }
224
+ return addr;
225
+ }
226
+ //# sourceMappingURL=config.js.map