jishushell 0.4.10 → 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.
Files changed (100) hide show
  1. package/INSTALL-NOTICE +10 -12
  2. package/dist/cli/app.d.ts +3 -0
  3. package/dist/cli/app.js +156 -0
  4. package/dist/cli/app.js.map +1 -0
  5. package/dist/{doctor.d.ts → cli/doctor.d.ts} +6 -1
  6. package/dist/{doctor.js → cli/doctor.js} +343 -14
  7. package/dist/cli/doctor.js.map +1 -0
  8. package/dist/cli/helpers.d.ts +4 -0
  9. package/dist/cli/helpers.js +32 -0
  10. package/dist/cli/helpers.js.map +1 -0
  11. package/dist/cli/job.d.ts +3 -0
  12. package/dist/cli/job.js +260 -0
  13. package/dist/cli/job.js.map +1 -0
  14. package/dist/cli/llm.d.ts +24 -0
  15. package/dist/cli/llm.js +593 -0
  16. package/dist/cli/llm.js.map +1 -0
  17. package/dist/cli/openclaw.d.ts +12 -0
  18. package/dist/cli/openclaw.js +156 -0
  19. package/dist/cli/openclaw.js.map +1 -0
  20. package/dist/cli/panel.d.ts +25 -0
  21. package/dist/cli/panel.js +734 -0
  22. package/dist/cli/panel.js.map +1 -0
  23. package/dist/cli.js +67 -326
  24. package/dist/cli.js.map +1 -1
  25. package/dist/config.d.ts +1 -0
  26. package/dist/config.js +11 -4
  27. package/dist/config.js.map +1 -1
  28. package/dist/control.d.ts +13 -41
  29. package/dist/control.js +12 -1355
  30. package/dist/control.js.map +1 -1
  31. package/dist/routes/apps.d.ts +3 -0
  32. package/dist/routes/apps.js +99 -0
  33. package/dist/routes/apps.js.map +1 -0
  34. package/dist/routes/instances.js +12 -6
  35. package/dist/routes/instances.js.map +1 -1
  36. package/dist/routes/llm.d.ts +15 -0
  37. package/dist/routes/llm.js +246 -0
  38. package/dist/routes/llm.js.map +1 -0
  39. package/dist/routes/setup.js +29 -2
  40. package/dist/routes/setup.js.map +1 -1
  41. package/dist/routes/system.js +31 -6
  42. package/dist/routes/system.js.map +1 -1
  43. package/dist/server.js +40 -4
  44. package/dist/server.js.map +1 -1
  45. package/dist/services/app-compiler.d.ts +15 -0
  46. package/dist/services/app-compiler.js +169 -0
  47. package/dist/services/app-compiler.js.map +1 -0
  48. package/dist/services/app-manager.d.ts +17 -0
  49. package/dist/services/app-manager.js +168 -0
  50. package/dist/services/app-manager.js.map +1 -0
  51. package/dist/services/instance-manager.d.ts +51 -3
  52. package/dist/services/instance-manager.js +233 -30
  53. package/dist/services/instance-manager.js.map +1 -1
  54. package/dist/services/job-manager.d.ts +22 -0
  55. package/dist/services/job-manager.js +102 -0
  56. package/dist/services/job-manager.js.map +1 -0
  57. package/dist/services/llm-proxy/adapters.js +5 -1
  58. package/dist/services/llm-proxy/adapters.js.map +1 -1
  59. package/dist/services/llm-proxy/index.d.ts +30 -0
  60. package/dist/services/llm-proxy/index.js +71 -1
  61. package/dist/services/llm-proxy/index.js.map +1 -1
  62. package/dist/services/llm-proxy/ssrf.js +1 -1
  63. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  64. package/dist/services/nomad-manager.js +192 -29
  65. package/dist/services/nomad-manager.js.map +1 -1
  66. package/dist/services/panel-manager.d.ts +40 -0
  67. package/dist/services/panel-manager.js +346 -0
  68. package/dist/services/panel-manager.js.map +1 -0
  69. package/dist/services/process-manager.js +20 -7
  70. package/dist/services/process-manager.js.map +1 -1
  71. package/dist/services/setup-manager.js +316 -31
  72. package/dist/services/setup-manager.js.map +1 -1
  73. package/dist/services/update-manager.d.ts +47 -0
  74. package/dist/services/update-manager.js +305 -0
  75. package/dist/services/update-manager.js.map +1 -0
  76. package/dist/types.d.ts +62 -0
  77. package/install/jishu-install.sh +279 -37
  78. package/install/post-install.sh +64 -5
  79. package/package.json +6 -2
  80. package/public/assets/Dashboard-CQsp1Mr9.js +1 -0
  81. package/public/assets/InitPassword-BEC8SE4A.js +1 -0
  82. package/public/assets/InstanceDetail-B5wTgNEg.js +17 -0
  83. package/public/assets/{Login-CUoEZOWR.js → Login-D1Bt-Lyk.js} +1 -1
  84. package/public/assets/NewInstance-GQzm3K9D.js +1 -0
  85. package/public/assets/Settings-ByjGlqhP.js +1 -0
  86. package/public/assets/Setup-cMF21Y-8.js +1 -0
  87. package/public/assets/index-B6qQP4mH.css +1 -0
  88. package/public/assets/index-BuTQtuNy.js +16 -0
  89. package/public/assets/{providers-lBSOjUWy.js → providers-V-vwrExZ.js} +1 -1
  90. package/public/index.html +2 -2
  91. package/dist/doctor.js.map +0 -1
  92. package/install/jishu-install-china.sh +0 -3092
  93. package/public/assets/Dashboard-DhsrzJ4F.js +0 -1
  94. package/public/assets/InitPassword-BjubiVdd.js +0 -1
  95. package/public/assets/InstanceDetail-DMcywsof.js +0 -17
  96. package/public/assets/NewInstance-Bk0G4EiJ.js +0 -1
  97. package/public/assets/Settings-D5tHL_h5.js +0 -1
  98. package/public/assets/Setup-4t6E3Rut.js +0 -1
  99. package/public/assets/index-BJ47MWpF.css +0 -1
  100. package/public/assets/index-DbX85irc.js +0 -16
package/INSTALL-NOTICE CHANGED
@@ -32,16 +32,14 @@ software bundled with or installed by this product.
32
32
  https://github.com/abiosoft/colima/blob/main/LICENSE
33
33
  Author : Abiola Ibrahim
34
34
 
35
- Nomad v1.11.3+ (>= 1.7.0)
36
- URL : https://github.com/hashicorp/nomad
37
- License : Business Source License 1.1 (BSL 1.1)
38
- https://github.com/hashicorp/nomad/blob/main/LICENSE
39
- Licensor: International Business Machines Corporation (IBM)
40
- Work : Nomad Version 1.7.0 or later. (c) 2024 IBM Corp.
41
-
42
- You, as the operator deploying this software, are the end user of
43
- Nomad and are solely responsible for ensuring your use complies with
44
- the BSL 1.1 terms, including any restrictions on competitive or
45
- commercial use. For licensing details and FAQ, see:
46
- https://www.hashicorp.com/license-faq
35
+ Nomad v1.6.5 (last MPL 2.0 release in the 1.6.x line)
36
+ URL : https://github.com/hashicorp/nomad/tree/v1.6.5
37
+ License : Mozilla Public License 2.0
38
+ https://github.com/hashicorp/nomad/blob/v1.6.5/LICENSE
39
+ Author : HashiCorp, Inc.
40
+
41
+ JishuShell pins Nomad to v1.6.5, the final release in the 1.6.x series
42
+ distributed under MPL 2.0. Nomad 1.6.6 and later (including 1.7.0+) are
43
+ licensed under the Business Source License 1.1 (BSL 1.1) and are not
44
+ used by JishuShell.
47
45
 
@@ -0,0 +1,3 @@
1
+ export declare function run(rest: string[]): Promise<void>;
2
+ export declare function brief(): string;
3
+ export declare function printHelp(): void;
@@ -0,0 +1,156 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ // ── ANSI colour helpers ───────────────────────────────────────────────────
3
+ const isTTY = process.stdout.isTTY ?? false;
4
+ const c = {
5
+ bold: (s) => isTTY ? `\x1b[1m${s}\x1b[0m` : s,
6
+ green: (s) => isTTY ? `\x1b[32m${s}\x1b[0m` : s,
7
+ red: (s) => isTTY ? `\x1b[31m${s}\x1b[0m` : s,
8
+ cyan: (s) => isTTY ? `\x1b[36m${s}\x1b[0m` : s,
9
+ dim: (s) => isTTY ? `\x1b[2m${s}\x1b[0m` : s,
10
+ };
11
+ function log(msg) { process.stdout.write(msg + "\n"); }
12
+ // ── Command implementations ───────────────────────────────────────────────
13
+ async function cmdList(args) {
14
+ const { listApps } = await import("../services/app-manager.js");
15
+ const apps = listApps();
16
+ if (args.includes("--json")) {
17
+ console.log(JSON.stringify(apps.map(a => ({
18
+ id: a.spec.id, name: a.spec.name, version: a.spec.version,
19
+ task_count: a.spec.tasks.length, installed_at: a.manifest.installed_at,
20
+ })), null, 2));
21
+ return;
22
+ }
23
+ log("");
24
+ log(c.bold(" Installed Apps"));
25
+ log(c.dim(" ─────────────────────────────────────────────────────"));
26
+ if (apps.length === 0) {
27
+ log(c.dim(" (no apps installed)"));
28
+ }
29
+ else {
30
+ for (const a of apps) {
31
+ log(` ${c.cyan(a.spec.id.padEnd(30))} ${(a.spec.version || "").padEnd(10)} ${c.dim(a.spec.name || "")}`);
32
+ }
33
+ }
34
+ log("");
35
+ }
36
+ async function cmdShow(id) {
37
+ const { getApp } = await import("../services/app-manager.js");
38
+ const app = getApp(id);
39
+ if (!app) {
40
+ log(c.red(` ✗ App "${id}" not found.`));
41
+ process.exitCode = 1;
42
+ return;
43
+ }
44
+ console.log(JSON.stringify(app, null, 2));
45
+ }
46
+ async function cmdInstall(yamlPath) {
47
+ if (!existsSync(yamlPath)) {
48
+ log(c.red(` ✗ File not found: ${yamlPath}`));
49
+ process.exitCode = 1;
50
+ return;
51
+ }
52
+ const yamlText = readFileSync(yamlPath, "utf-8");
53
+ const { installApp } = await import("../services/app-manager.js");
54
+ const result = await installApp(yamlText);
55
+ log(c.green(` ✓ Installed app: ${result.spec.id} (${result.spec.name || ""} v${result.spec.version || "?"})`));
56
+ }
57
+ async function cmdUninstall(id) {
58
+ const { uninstallApp, getApp } = await import("../services/app-manager.js");
59
+ if (!getApp(id)) {
60
+ log(c.red(` ✗ App "${id}" not found.`));
61
+ process.exitCode = 1;
62
+ return;
63
+ }
64
+ uninstallApp(id);
65
+ log(c.green(` ✓ Uninstalled app: ${id}`));
66
+ }
67
+ async function cmdCreateInstance(appId, instanceId, name) {
68
+ const { getApp, resolveRequires } = await import("../services/app-manager.js");
69
+ const { createInstance } = await import("../services/instance-manager.js");
70
+ const appData = getApp(appId);
71
+ if (!appData) {
72
+ log(c.red(` ✗ App "${appId}" not found.`));
73
+ process.exitCode = 1;
74
+ return;
75
+ }
76
+ let resolvedEnv = {};
77
+ try {
78
+ resolvedEnv = resolveRequires(appData.spec);
79
+ }
80
+ catch (e) {
81
+ log(c.red(` ✗ ${e.message}`));
82
+ process.exitCode = 1;
83
+ return;
84
+ }
85
+ const specWithEnv = Object.keys(resolvedEnv).length > 0
86
+ ? {
87
+ ...appData.spec,
88
+ tasks: appData.spec.tasks.map((t) => t.role === "service" ? { ...t, env: { ...t.env, ...resolvedEnv } } : t),
89
+ }
90
+ : appData.spec;
91
+ await createInstance(instanceId, name, "", undefined, undefined, specWithEnv);
92
+ log(c.green(` ✓ Created instance "${instanceId}" from app "${appId}"`));
93
+ }
94
+ // ── Entry point ───────────────────────────────────────────────────────────
95
+ export async function run(rest) {
96
+ const appCmd = rest[0];
97
+ try {
98
+ if (appCmd === "list") {
99
+ await cmdList(rest.slice(1));
100
+ }
101
+ else if (appCmd === "show" && rest[1]) {
102
+ await cmdShow(rest[1]);
103
+ }
104
+ else if (appCmd === "install" && rest[1]) {
105
+ await cmdInstall(rest[1]);
106
+ }
107
+ else if (appCmd === "uninstall" && rest[1]) {
108
+ await cmdUninstall(rest[1]);
109
+ }
110
+ else if (appCmd === "create-instance" && rest[1]) {
111
+ const appId = rest[1];
112
+ const instanceId = rest[2];
113
+ const name = rest[3] || instanceId;
114
+ if (!instanceId) {
115
+ console.error("Usage: jishushell app create-instance <app-id> <instance-id> [name]");
116
+ process.exit(1);
117
+ }
118
+ await cmdCreateInstance(appId, instanceId, name);
119
+ }
120
+ else if (appCmd === "help" || appCmd === "--help" || appCmd === "-h") {
121
+ printHelp();
122
+ }
123
+ else {
124
+ printHelp();
125
+ if (appCmd)
126
+ process.exit(1);
127
+ }
128
+ }
129
+ catch (err) {
130
+ log(c.red(` ✗ ${err.message}`));
131
+ process.exitCode = 1;
132
+ }
133
+ }
134
+ export function brief() {
135
+ return " app <install|list|show|uninstall|create-instance> App Spec 管理";
136
+ }
137
+ export function printHelp() {
138
+ console.log(`
139
+ Usage: jishushell app <command> [options]
140
+
141
+ Commands:
142
+ list [--json] 列出已安装的 App
143
+ show <app-id> 查看 App 详情(JSON)
144
+ install <yaml-file> 从 YAML 文件安装 App
145
+ uninstall <app-id> 卸载 App
146
+ create-instance <app-id> <inst-id> [name] 从 App 创建实例
147
+ help 显示此帮助
148
+
149
+ Examples:
150
+ jishushell app install ./my-app.yaml
151
+ jishushell app create-instance my-app inst1 "My Instance"
152
+ jishushell app list
153
+ jishushell app show my-app
154
+ `);
155
+ }
156
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/cli/app.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  * jishushell doctor --fix 检查并尝试自动修复
7
7
  *
8
8
  * Nomad 检查依据 install/jishu-install.sh 及 src/services/setup-manager.ts:
9
- * - 二进制路径: ~/.jishushell/bin/nomad (v1.11.3)
9
+ * - 二进制路径: ~/.jishushell/bin/nomad (v1.6.5)
10
10
  * - 配置文件: ~/.jishushell/nomad/nomad.hcl
11
11
  * - 数据目录: ~/.jishushell/nomad/data/
12
12
  * - Alloc 目录: ~/.jishushell/nomad/data/alloc/
@@ -38,9 +38,14 @@ export interface DoctorCheck {
38
38
  }
39
39
  export interface DoctorOptions {
40
40
  fix?: boolean;
41
+ ai?: boolean;
41
42
  }
42
43
  export declare const ALL_CHECKS: DoctorCheck[];
43
44
  /**
44
45
  * 运行所有诊断检查,返回是否全部 error 级别检查通过。
45
46
  */
46
47
  export declare function runDoctorChecks(opts?: DoctorOptions): Promise<boolean>;
48
+ export declare function brief(): string;
49
+ export declare function printHelp(): void;
50
+ export declare function runAiDiagnosis(): Promise<void>;
51
+ export declare function run(rest: string[]): Promise<void>;
@@ -6,7 +6,7 @@
6
6
  * jishushell doctor --fix 检查并尝试自动修复
7
7
  *
8
8
  * Nomad 检查依据 install/jishu-install.sh 及 src/services/setup-manager.ts:
9
- * - 二进制路径: ~/.jishushell/bin/nomad (v1.11.3)
9
+ * - 二进制路径: ~/.jishushell/bin/nomad (v1.6.5)
10
10
  * - 配置文件: ~/.jishushell/nomad/nomad.hcl
11
11
  * - 数据目录: ~/.jishushell/nomad/data/
12
12
  * - Alloc 目录: ~/.jishushell/nomad/data/alloc/
@@ -21,9 +21,10 @@ import { existsSync, readFileSync, unlinkSync, } from "fs";
21
21
  import * as http from "http";
22
22
  import { homedir } from "os";
23
23
  import { join } from "path";
24
- import { AUTH_FILE, INSTANCES_DIR, JISHUSHELL_HOME, PANEL_CONFIG_FILE, ensureDirs, getNomadToken, getPanelConfig, } from "./config.js";
25
- import { loadNomadToken } from "./services/setup-manager.js";
26
- import { ensureDirContainer, writeConfigFile, writeSecretFile } from "./utils/fs.js";
24
+ import { AUTH_FILE, DEFAULT_OPENCLAW_DOCKER_IMAGE, INSTANCES_DIR, JISHUSHELL_HOME, PANEL_CONFIG_FILE, ensureDirs, getNomadToken, getPanelConfig, } from "../config.js";
25
+ import { loadNomadToken } from "../services/setup-manager.js";
26
+ import { ensureDirContainer, writeConfigFile, writeSecretFile } from "../utils/fs.js";
27
+ import { LOG_FILE as PANEL_LOG_FILE } from "../services/panel-manager.js";
27
28
  // ── ANSI 颜色(非 TTY 时自动禁用)──────────────────────────────────────────
28
29
  const isTTY = process.stdout.isTTY ?? false;
29
30
  const c = {
@@ -103,7 +104,7 @@ const NOMAD_DATA_DIR = join(NOMAD_DIR, "data");
103
104
  const NOMAD_ALLOC_DIR = join(NOMAD_DIR, "data", "alloc");
104
105
  const NOMAD_LOG = join(NOMAD_DIR, "nomad.log");
105
106
  const NOMAD_SERVICE = "/etc/systemd/system/nomad.service";
106
- const NOMAD_MIN_VER = "1.11.3";
107
+ const NOMAD_MIN_VER = "1.6.5";
107
108
  function nomadEnabled() {
108
109
  return getPanelConfig().service_manager === "nomad";
109
110
  }
@@ -488,7 +489,7 @@ const checkSystemdEnabled = {
488
489
  * 检查 Nomad 二进制(~/.jishushell/bin/nomad)
489
490
  * 依据: install/jishu-install.sh _install_nomad_binary()
490
491
  * 安装路径: JISHUSHELL_BIN_DIR=${JISHUSHELL_HOME}/bin/nomad
491
- * 要求版本: NOMAD_VERSION=1.11.3
492
+ * 要求版本: NOMAD_VERSION=1.6.5
492
493
  */
493
494
  const checkNomadBin = {
494
495
  id: "nomad-bin", label: "Nomad 二进制", category: "Nomad", severity: "error",
@@ -780,25 +781,43 @@ const checkNomadJobs = {
780
781
  // 类别: AI 组件
781
782
  // ════════════════════════════════════════════════════════════════════════════
782
783
  const checkOpenclawPkg = {
783
- id: "openclaw-pkg", label: "OpenClaw ", category: "AI 组件", severity: "warning",
784
+ id: "openclaw-pkg", label: "OpenClaw 运行时", category: "AI 组件", severity: "warning",
784
785
  async check() {
785
786
  const oclawBin = join(JISHUSHELL_HOME, "packages/openclaw/bin/openclaw");
786
- const oclawPkg = join(JISHUSHELL_HOME, "packages/openclaw/lib/node_modules/openclaw/package.json");
787
- if (!existsSync(oclawBin))
788
- return { ok: false, detail: "OpenClaw 未安装", hint: "jishushell install --skip-nomad --skip-docker", fixable: false };
787
+ const oclawPkgJson = join(JISHUSHELL_HOME, "packages/openclaw/lib/node_modules/openclaw/package.json");
788
+ const localBinOk = existsSync(oclawBin);
789
+ // Docker 镜像也是合法的运行时(与 setup-manager 逻辑一致)
790
+ const imageTag = getPanelConfig().openclaw_image || DEFAULT_OPENCLAW_DOCKER_IMAGE;
791
+ let dockerImageOk = false;
789
792
  try {
790
- const pkg = JSON.parse(readFileSync(oclawPkg, "utf-8"));
791
- return { ok: true, detail: `v${pkg.version}` };
793
+ execFileSync("docker", ["image", "inspect", imageTag], { stdio: "ignore", timeout: 5000 });
794
+ dockerImageOk = true;
792
795
  }
793
796
  catch {
794
- return { ok: true, detail: "已安装(版本信息不可读)" };
797
+ try {
798
+ execFileSync("sudo", ["-n", "docker", "image", "inspect", imageTag], { stdio: "ignore", timeout: 5000 });
799
+ dockerImageOk = true;
800
+ }
801
+ catch { }
802
+ }
803
+ if (localBinOk) {
804
+ let ver = "已安装";
805
+ try {
806
+ ver = `v${JSON.parse(readFileSync(oclawPkgJson, "utf-8")).version}`;
807
+ }
808
+ catch { }
809
+ return { ok: true, detail: `本地包 ${ver}${dockerImageOk ? ",Docker 镜像已就绪" : ""}` };
810
+ }
811
+ if (dockerImageOk) {
812
+ return { ok: true, detail: `Docker 镜像已就绪(${imageTag})` };
795
813
  }
814
+ return { ok: false, detail: "未安装(本地包与 Docker 镜像均缺失)", hint: "jishushell install", fixable: false };
796
815
  },
797
816
  };
798
817
  const checkOpenclawImage = {
799
818
  id: "openclaw-image", label: "OpenClaw Docker 镜像", category: "AI 组件", severity: "warning",
800
819
  async check() {
801
- const imageTag = getPanelConfig().openclaw_image || "openclaw:v1.0";
820
+ const imageTag = getPanelConfig().openclaw_image || DEFAULT_OPENCLAW_DOCKER_IMAGE;
802
821
  let dockerAccessible = false;
803
822
  try {
804
823
  execFileSync("docker", ["info"], { stdio: "ignore", timeout: 4000 });
@@ -967,4 +986,314 @@ export async function runDoctorChecks(opts = {}) {
967
986
  log("");
968
987
  return errors.length === 0;
969
988
  }
989
+ // ── CLI entry ──────────────────────────────────────────────────────────────
990
+ export function brief() {
991
+ return " doctor [--fix] [--ai] 环境诊断、自动修复与 AI 分析";
992
+ }
993
+ export function printHelp() {
994
+ console.log(`
995
+ Usage: jishushell doctor [options]
996
+
997
+ Options:
998
+ --fix 尝试自动修复发现的问题
999
+ --ai 将诊断结果发给 LLM 进行智能分析(需配置 LLM provider)
1000
+ help 显示此帮助
1001
+ `);
1002
+ }
1003
+ // ════════════════════════════════════════════════════════════════════════════
1004
+ // AI 诊断
1005
+ // ════════════════════════════════════════════════════════════════════════════
1006
+ /** Strip ANSI escape codes from a string. */
1007
+ function stripAnsi(s) {
1008
+ // eslint-disable-next-line no-control-regex
1009
+ return s.replace(/\x1b\[[0-9;]*m/g, "");
1010
+ }
1011
+ /** Silently run all checks and return a plain-text report (no ANSI). */
1012
+ async function collectDoctorReport() {
1013
+ const categories = [...new Set(ALL_CHECKS.map((ch) => ch.category))];
1014
+ const lines = ["=== JishuShell Doctor Report ===", ""];
1015
+ for (const category of categories) {
1016
+ lines.push(`[${category}]`);
1017
+ for (const check of ALL_CHECKS.filter((ch) => ch.category === category)) {
1018
+ const result = await check.check();
1019
+ const status = result.ok ? "OK" : check.severity === "error" ? "ERROR" : "WARN";
1020
+ lines.push(` ${status} ${check.label}: ${result.detail}`);
1021
+ if (!result.ok && result.hint)
1022
+ lines.push(` hint: ${result.hint}`);
1023
+ }
1024
+ lines.push("");
1025
+ }
1026
+ return lines.join("\n");
1027
+ }
1028
+ /** Read the tail of a log file (plain text, no ANSI). */
1029
+ function tailFile(path, lines = 80) {
1030
+ if (!existsSync(path))
1031
+ return "";
1032
+ try {
1033
+ const content = readFileSync(path, "utf-8");
1034
+ const tail = content.split("\n").slice(-lines).join("\n");
1035
+ return stripAnsi(tail);
1036
+ }
1037
+ catch {
1038
+ return "";
1039
+ }
1040
+ }
1041
+ /** Collect journalctl output for a service (best-effort). */
1042
+ function journalTail(service, lines = 60) {
1043
+ try {
1044
+ return execSync(`journalctl -u ${service} -n ${lines} --no-pager 2>/dev/null`, {
1045
+ encoding: "utf-8", timeout: 5000,
1046
+ }).trim();
1047
+ }
1048
+ catch {
1049
+ return "";
1050
+ }
1051
+ }
1052
+ /** Collect instance status + recent logs for all local instances. */
1053
+ async function collectInstanceContext() {
1054
+ const { listInstanceIds, readInstanceMeta, getSvc } = await import("../services/job-manager.js");
1055
+ loadNomadToken();
1056
+ const ids = listInstanceIds();
1057
+ if (ids.length === 0)
1058
+ return "No instances.";
1059
+ const sections = [];
1060
+ let svc = null;
1061
+ try {
1062
+ svc = await getSvc();
1063
+ }
1064
+ catch { /* Nomad not available */ }
1065
+ for (const id of ids) {
1066
+ const meta = readInstanceMeta(id);
1067
+ const name = meta?.name || id;
1068
+ const header = `--- Instance: ${name} (${id}) ---`;
1069
+ const lines = [header];
1070
+ // Status
1071
+ if (svc) {
1072
+ try {
1073
+ const st = await svc.getStatus(id);
1074
+ lines.push(`status: ${st.status}`);
1075
+ }
1076
+ catch (e) {
1077
+ lines.push(`status: unavailable (${e.message})`);
1078
+ }
1079
+ // Logs (stderr first, then stdout)
1080
+ for (const logType of ["stderr", "stdout"]) {
1081
+ try {
1082
+ const logLines = await svc.getLogs(id, 60, logType);
1083
+ if (logLines.length > 0) {
1084
+ lines.push(`\n[${logType} tail]`);
1085
+ lines.push(...logLines.slice(-60));
1086
+ }
1087
+ }
1088
+ catch { /* skip */ }
1089
+ }
1090
+ }
1091
+ else {
1092
+ lines.push("status: Nomad unavailable");
1093
+ }
1094
+ sections.push(lines.join("\n"));
1095
+ }
1096
+ return sections.join("\n\n");
1097
+ }
1098
+ /** Build the full context string to send to the LLM. */
1099
+ async function buildAiContext() {
1100
+ const parts = [];
1101
+ // 1. Doctor checks
1102
+ log(c.dim(" ✦ 收集诊断检查结果…"));
1103
+ parts.push(await collectDoctorReport());
1104
+ // 2. Panel log
1105
+ log(c.dim(" ✦ 收集面板日志…"));
1106
+ const panelLog = tailFile(PANEL_LOG_FILE, 80);
1107
+ if (panelLog) {
1108
+ parts.push(`=== Panel Log (${PANEL_LOG_FILE}) ===\n${panelLog}`);
1109
+ }
1110
+ // 3. Panel journalctl
1111
+ const jLogs = journalTail("jishushell", 60);
1112
+ if (jLogs) {
1113
+ parts.push(`=== journalctl jishushell (last 60 lines) ===\n${jLogs}`);
1114
+ }
1115
+ // 4. Nomad log
1116
+ const nomadLog = join(JISHUSHELL_HOME, "nomad", "nomad.log");
1117
+ const nLog = tailFile(nomadLog, 60);
1118
+ if (nLog) {
1119
+ parts.push(`=== Nomad Log (${nomadLog}) ===\n${nLog}`);
1120
+ }
1121
+ // 5. Instance context
1122
+ log(c.dim(" ✦ 收集实例状态与日志…"));
1123
+ try {
1124
+ const instCtx = await collectInstanceContext();
1125
+ parts.push(`=== Instance Status & Logs ===\n${instCtx}`);
1126
+ }
1127
+ catch (e) {
1128
+ parts.push(`=== Instance Status & Logs ===\nError collecting: ${e.message}`);
1129
+ }
1130
+ return parts.join("\n\n");
1131
+ }
1132
+ /** Validate that a usable LLM provider exists and return it (or null). */
1133
+ async function getDefaultProvider() {
1134
+ const { decryptApiKey } = await import("../services/llm-proxy/encryption.js");
1135
+ const config = getPanelConfig();
1136
+ const providers = Array.isArray(config.providers) ? config.providers : [];
1137
+ const dp = config.default_provider;
1138
+ if (providers.length === 0 && dp && !dp.skipped) {
1139
+ return {
1140
+ id: dp.providerId || "default",
1141
+ baseUrl: dp.baseUrl || "",
1142
+ api: dp.api || "openai",
1143
+ apiKey: dp.apiKey ? (() => { try {
1144
+ return decryptApiKey(dp.apiKey);
1145
+ }
1146
+ catch {
1147
+ return "";
1148
+ } })() : undefined,
1149
+ authHeader: dp.authHeader,
1150
+ models: dp.models,
1151
+ selectedModelId: dp.selectedModelId,
1152
+ };
1153
+ }
1154
+ const provider = providers.find((p) => p.isDefault) || providers[0];
1155
+ if (!provider)
1156
+ return null;
1157
+ return {
1158
+ id: provider.id,
1159
+ baseUrl: provider.baseUrl,
1160
+ api: provider.api || "openai",
1161
+ apiKey: provider.apiKey ? (() => { try {
1162
+ return decryptApiKey(provider.apiKey);
1163
+ }
1164
+ catch {
1165
+ return "";
1166
+ } })() : undefined,
1167
+ authHeader: provider.authHeader,
1168
+ models: provider.models,
1169
+ selectedModelId: provider.selectedModelId,
1170
+ };
1171
+ }
1172
+ const AI_SYSTEM_PROMPT = `你是 JishuShell 的智能运维助手。
1173
+ JishuShell 是一个用于管理 OpenClaw(AI Agent 运行时)实例的面板,运行在 Linux/ARM 设备上,依赖 Docker 和 Nomad 进行容器编排。
1174
+
1175
+ 用户将提供系统诊断报告、日志和实例状态。
1176
+ 你的任务:
1177
+ 1. 识别所有异常项(ERROR/WARN)并解释可能的根本原因
1178
+ 2. 按优先级给出具体的修复建议(优先 jishushell 命令,其次 shell 命令)
1179
+ 3. 如果日志中有明显错误,指出具体错误行和含义
1180
+ 4. 用中文回复,保持简洁,使用 Markdown 格式`;
1181
+ export async function runAiDiagnosis() {
1182
+ log("");
1183
+ log(c.bold(c.cyan(" JishuShell Doctor — AI 模式")));
1184
+ log(c.dim(" ────────────────────────────────────────────────"));
1185
+ log("");
1186
+ // 1. Check that a provider exists
1187
+ let provider;
1188
+ try {
1189
+ provider = await getDefaultProvider();
1190
+ }
1191
+ catch (e) {
1192
+ log(c.red(" ✗ 无法加载 LLM 配置: " + e.message));
1193
+ log(c.dim(" 请先运行: jishushell llm add <id> --url <url> --key <key>"));
1194
+ log("");
1195
+ process.exit(1);
1196
+ }
1197
+ if (!provider) {
1198
+ log(c.red(" ✗ 未配置任何 LLM provider,无法使用 --ai 模式"));
1199
+ log("");
1200
+ log(c.dim(" 请先配置供应商,然后重新运行:"));
1201
+ log(c.dim(" jishushell llm add <id> --url <url> --key <key>"));
1202
+ log(c.dim(" 或在 Web 界面「设置 → 模型供应商」中添加"));
1203
+ log("");
1204
+ process.exit(1);
1205
+ }
1206
+ const model = provider.selectedModelId
1207
+ || (Array.isArray(provider.models) && provider.models[0]?.id)
1208
+ || undefined;
1209
+ if (!model) {
1210
+ log(c.yellow(` ! Provider '${provider.id}' 未配置默认模型`));
1211
+ log(c.dim(` 请运行: jishushell llm update ${provider.id} --model <model-id>`));
1212
+ log("");
1213
+ process.exit(1);
1214
+ }
1215
+ log(c.dim(` 使用 provider: ${provider.id} 模型: ${model}`));
1216
+ log("");
1217
+ // 2. Collect context
1218
+ log(c.bold(" ▸ 收集系统信息…"));
1219
+ const context = await buildAiContext();
1220
+ log("");
1221
+ // 3. Call LLM
1222
+ log(c.bold(" ▸ 发送给 AI 分析(请稍候…)"));
1223
+ log("");
1224
+ const apiKey = provider.apiKey || "";
1225
+ const baseUrl = provider.baseUrl.replace(/\/$/, "");
1226
+ const authHeader = (typeof provider.authHeader === "string" && provider.authHeader)
1227
+ ? provider.authHeader : "Authorization";
1228
+ const userMessage = `以下是 JishuShell 的系统诊断信息,请分析问题并给出修复建议:\n\n${context}`;
1229
+ const body = {
1230
+ model,
1231
+ messages: [
1232
+ { role: "system", content: AI_SYSTEM_PROMPT },
1233
+ { role: "user", content: userMessage },
1234
+ ],
1235
+ stream: false,
1236
+ };
1237
+ const endpoints = [`${baseUrl}/v1/chat/completions`, `${baseUrl}/chat/completions`];
1238
+ let lastErr = "";
1239
+ for (const url of endpoints) {
1240
+ try {
1241
+ const resp = await fetch(url, {
1242
+ method: "POST",
1243
+ headers: {
1244
+ "Content-Type": "application/json",
1245
+ [authHeader]: `Bearer ${apiKey}`,
1246
+ },
1247
+ body: JSON.stringify(body),
1248
+ signal: AbortSignal.timeout(120_000),
1249
+ });
1250
+ if (resp.ok) {
1251
+ const data = await resp.json();
1252
+ const content = data?.choices?.[0]?.message?.content ?? "";
1253
+ const usage = data?.usage;
1254
+ log(c.bold(c.cyan(" ═══════════════════════════════════════════════")));
1255
+ log(c.bold(" AI 诊断结果"));
1256
+ log(c.bold(c.cyan(" ═══════════════════════════════════════════════")));
1257
+ log("");
1258
+ // Print result with light indentation
1259
+ for (const line of content.split("\n")) {
1260
+ log(" " + line);
1261
+ }
1262
+ log("");
1263
+ log(c.bold(c.cyan(" ═══════════════════════════════════════════════")));
1264
+ if (usage) {
1265
+ log(c.dim(` [${provider.id} / ${model} tokens: ${usage.total_tokens ?? "?"}]`));
1266
+ }
1267
+ log("");
1268
+ return;
1269
+ }
1270
+ else {
1271
+ const text = await resp.text().catch(() => "");
1272
+ lastErr = `HTTP ${resp.status}: ${text.slice(0, 300)}`;
1273
+ }
1274
+ }
1275
+ catch (e) {
1276
+ lastErr = e.message;
1277
+ }
1278
+ }
1279
+ log(c.red(` ✗ AI 请求失败 (${provider.id}): ${lastErr}`));
1280
+ log(c.dim(" 请检查 LLM provider 配置是否正确,或使用 --fix 尝试自动修复。"));
1281
+ log("");
1282
+ process.exit(1);
1283
+ }
1284
+ export async function run(rest) {
1285
+ if (rest.includes("help") || rest.includes("--help") || rest.includes("-h")) {
1286
+ printHelp();
1287
+ return;
1288
+ }
1289
+ if (rest.includes("--ai")) {
1290
+ // Run standard doctor first (to stdout), then AI analysis
1291
+ await runDoctorChecks({ fix: false });
1292
+ await runAiDiagnosis();
1293
+ return;
1294
+ }
1295
+ const fix = rest.includes("--fix") || rest.includes("-f");
1296
+ const ok = await runDoctorChecks({ fix });
1297
+ process.exit(ok ? 0 : 1);
1298
+ }
970
1299
  //# sourceMappingURL=doctor.js.map